From cf1ddf0b9d55b8ca8544b0ae25e8fa08738b2049 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Sat, 10 Jul 2010 04:03:20 +0200 Subject: Import OpenPGM into the tree, and provide a rudimentary build script. OpenPGM will serve as the network layer for Multicast File Transfer. --- 3rdparty/CMakeLists.txt | 160 +++++++++++++++++++++++++++++++++++ 3rdparty/libpgm-5.0.63alpha1.tar.bz2 | Bin 0 -> 358887 bytes CMakeLists.txt | 10 +++ 3 files changed, 170 insertions(+) create mode 100644 3rdparty/CMakeLists.txt create mode 100644 3rdparty/libpgm-5.0.63alpha1.tar.bz2 diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt new file mode 100644 index 0000000..84f366b --- /dev/null +++ b/3rdparty/CMakeLists.txt @@ -0,0 +1,160 @@ +################################################################################ +# Build OpenPGM +################################################################################ + +# We need GLib +INCLUDE(${CMAKE_ROOT}/Modules/FindPkgConfig.cmake) + +PKG_CHECK_MODULES(GLIB glib-2.0>=2.10) +IF(NOT GLIB_FOUND) + MESSAGE(FATAL_ERROR "You don't seem to have GLib2 installed.") +ELSE(NOT GLIB_FOUND) + MESSAGE("-- GLib2 found. Libraries: ${GLIB_LIBRARIES}, CFLAGS: ${GLIB_CFLAGS}") +ENDIF(NOT GLIB_FOUND) + +# Set up build +SET(pgm_VERSION + 5.0.63alpha1 +) + +# OpenPGM will be built in the binary tree +SET(pgm + ${CMAKE_CURRENT_BINARY_DIR}/libpgm-${pgm_VERSION}/openpgm/pgm +) + +# This has been adapted from SConscript.libpgm +SET(pgm_SRCS + ${pgm}/thread.c + ${pgm}/mem.c + ${pgm}/string.c + ${pgm}/list.c + ${pgm}/slist.c + ${pgm}/queue.c + ${pgm}/hashtable.c + ${pgm}/messages.c + ${pgm}/error.c + ${pgm}/math.c + ${pgm}/packet_parse.c + ${pgm}/packet_test.c + ${pgm}/sockaddr.c + ${pgm}/time.c + ${pgm}/if.c + ${pgm}/getifaddrs.c + ${pgm}/getnodeaddr.c + ${pgm}/indextoaddr.c + ${pgm}/indextoname.c + ${pgm}/nametoindex.c + ${pgm}/inet_network.c + ${pgm}/md5.c + ${pgm}/rand.c + ${pgm}/gsi.c + ${pgm}/tsi.c + ${pgm}/txw.c + ${pgm}/rxw.c + ${pgm}/skbuff.c + ${pgm}/socket.c + ${pgm}/source.c + ${pgm}/receiver.c + ${pgm}/recv.c + ${pgm}/engine.c + ${pgm}/timer.c + ${pgm}/net.c + ${pgm}/rate_control.c + ${pgm}/checksum.c + ${pgm}/reed_solomon.c + ${pgm}/wsastrerror.c + ${pgm}/histogram.c +) + +SET(pgm_GENERATED + ${CMAKE_CURRENT_BINARY_DIR}/version.c + ${CMAKE_CURRENT_BINARY_DIR}/galois_tables.c +) + +# We need to generate galois_tables.c ... +ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/galois_tables.c + COMMAND perl ${pgm}/galois_generator.pl > ${CMAKE_CURRENT_BINARY_DIR}/galois_tables.c + DEPENDS ${pgm}/galois_generator.pl +) + +# ... and version.c ... +ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version.c + COMMAND python ${pgm}/version_generator.py > ${CMAKE_CURRENT_BINARY_DIR}/version.c + DEPENDS ${pgm}/version_generator.py +) + +# ... and we need to unpack the tree. +ADD_CUSTOM_COMMAND( + OUTPUT + ${pgm_SRCS} + ${pgm}/galois_generator.pl + ${pgm}/version_generator.py + COMMAND bzip2 -dc ${CMAKE_CURRENT_SOURCE_DIR}/libpgm-${pgm_VERSION}.tar.bz2 | tar -C ${CMAKE_CURRENT_BINARY_DIR} -x +) + +INCLUDE_DIRECTORIES( + ${pgm}/include + ${GLIB_INCLUDES_DIRS} +) + +ADD_LIBRARY( + pgm + STATIC + ${pgm_SRCS} + ${pgm_GENERATED} +) + +LINK_DIRECTORIES( + ${GLIB_LIBRARY_DIRS} +) + +ADD_DEFINITIONS( + ${GLIB_CFLAGS} +) + +IF(UNIX) + IF(CMAKE_COMPILER_IS_GNUCC) + # The scripts are fine for Linux/GCC, other platforms may or may + # not work. + ADD_DEFINITIONS( + -std=gnu99 + -D_XOPEN_SOURCE=600 + -D_BSD_SOURCE + -D_REENTRANT + -DCONFIG_HAVE_GETPROTOBYNAME_R2 + -DCONFIG_HAVE_ISO_VARARGS + -DCONFIG_HAVE_ALLOCA_H + -DCONFIG_16BIT_CHECKSUM + -DCONFIG_HAVE_PROC + -DCONFIG_HAVE_BACKTRACE + -DCONFIG_HAVE_PSELECT + -DCONFIG_HAVE_RTC + -DCONFIG_HAVE_TSC + -DCONFIG_HAVE_HPET + -DCONFIG_HAVE_POLL + -DCONFIG_HAVE_EPOLL + -DCONFIG_HAVE_GETIFADDRS + -DCONFIG_HAVE_IFR_NETMASK + -DCONFIG_HAVE_MCAST_JOIN + -DCONFIG_HAVE_IP_MREQN + -DCONFIG_HAVE_SPRINTF_GROUPING + -DCONFIG_HAVE_VASPRINTF + -DCONFIG_HAVE_DSO_VISIBILITY + -DCONFIG_BIND_INADDR_ANY + -DCONFIG_GALOIS_MUL_LUT + -DCONFIG_HAVE_GETOPT + ) + + TARGET_LINK_LIBRARIES(pgm + m rt + ${GLIB_LIBRARIES}) + + SET(_SYSTEM_SPECIFICS_SET 1) + ENDIF(CMAKE_COMPILER_IS_GNUCC) +ENDIF(UNIX) + +# Complain if this is NOT Linux/GCC. +IF(NOT _SYSTEM_SPECIFICS_SET) + MESSAGE(FATAL_ERROR "Can only build libpgm on Unix with gcc.") +ENDIF(NOT _SYSTEM_SPECIFICS_SET) + diff --git a/3rdparty/libpgm-5.0.63alpha1.tar.bz2 b/3rdparty/libpgm-5.0.63alpha1.tar.bz2 new file mode 100644 index 0000000..ab4903c Binary files /dev/null and b/3rdparty/libpgm-5.0.63alpha1.tar.bz2 differ diff --git a/CMakeLists.txt b/CMakeLists.txt index 2153497..5e63636 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,12 @@ INCLUDE_DIRECTORIES( ${CMAKE_BINARY_DIR} ) +################################################################################ +# Build third-party libraries +################################################################################ + +ADD_SUBDIRECTORY(3rdparty) + ################################################################################ # Variables ################################################################################ @@ -299,23 +305,27 @@ TARGET_LINK_LIBRARIES( pvsmgr ${QT_LIBRARIES} ${VNC_LIBRARIES} ${X11_LIBRARIES} + pgm ) TARGET_LINK_LIBRARIES( pvsmgrtouch ${QT_LIBRARIES} ${VNC_LIBRARIES} ${X11_LIBRARIES} + pgm ) TARGET_LINK_LIBRARIES( pvs ${QT_LIBRARIES} ${VNC_LIBRARIES} ${X11_LIBRARIES} + pgm ) TARGET_LINK_LIBRARIES( pvsgui ${QT_LIBRARIES} ${VNC_LIBRARIES} + pgm ) SET_PROPERTY(TARGET pvsmgrtouch PROPERTY COMPILE_DEFINITIONS MAINWINDOW_USE_TOUCHGUI) -- cgit v1.2.3-55-g7522 From 6940ab33b5009b79c3141dde7f98ea78a2662449 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Mon, 12 Jul 2010 04:15:13 +0200 Subject: Implement multicast transfer protocol. --- 3rdparty/CMakeLists.txt | 110 ++-- CMakeLists.txt | 10 +- OpenPGMConfig.cmake | 53 ++ src/net/mcast/CMakeLists.txt | 65 +++ src/net/mcast/McastConfiguration.cpp | 9 + src/net/mcast/McastConfiguration.h | 159 ++++++ src/net/mcast/McastConstants.h | 33 ++ src/net/mcast/McastPGMSocket.cpp | 601 +++++++++++++++++++++ src/net/mcast/McastPGMSocket.h | 74 +++ src/net/mcast/McastReceiver.cpp | 138 +++++ src/net/mcast/McastReceiver.h | 76 +++ src/net/mcast/McastSender.cpp | 96 ++++ src/net/mcast/McastSender.h | 68 +++ src/net/mcast/trial_programs/CMakeLists.txt | 38 ++ .../mcast/trial_programs/McastConfigArgParser.cpp | 151 ++++++ .../mcast/trial_programs/McastConfigArgParser.h | 26 + src/net/mcast/trial_programs/mcastreceive.cpp | 150 +++++ src/net/mcast/trial_programs/mcastreceive.h | 44 ++ src/net/mcast/trial_programs/mcastsend.cpp | 123 +++++ src/net/mcast/trial_programs/mcastsend.h | 42 ++ 20 files changed, 2008 insertions(+), 58 deletions(-) create mode 100644 OpenPGMConfig.cmake create mode 100644 src/net/mcast/CMakeLists.txt create mode 100644 src/net/mcast/McastConfiguration.cpp create mode 100644 src/net/mcast/McastConfiguration.h create mode 100644 src/net/mcast/McastConstants.h create mode 100644 src/net/mcast/McastPGMSocket.cpp create mode 100644 src/net/mcast/McastPGMSocket.h create mode 100644 src/net/mcast/McastReceiver.cpp create mode 100644 src/net/mcast/McastReceiver.h create mode 100644 src/net/mcast/McastSender.cpp create mode 100644 src/net/mcast/McastSender.h create mode 100644 src/net/mcast/trial_programs/CMakeLists.txt create mode 100644 src/net/mcast/trial_programs/McastConfigArgParser.cpp create mode 100644 src/net/mcast/trial_programs/McastConfigArgParser.h create mode 100644 src/net/mcast/trial_programs/mcastreceive.cpp create mode 100644 src/net/mcast/trial_programs/mcastreceive.h create mode 100644 src/net/mcast/trial_programs/mcastsend.cpp create mode 100644 src/net/mcast/trial_programs/mcastsend.h diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 84f366b..8a4cea0 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -2,15 +2,9 @@ # Build OpenPGM ################################################################################ -# We need GLib -INCLUDE(${CMAKE_ROOT}/Modules/FindPkgConfig.cmake) +INCLUDE(../OpenPGMConfig.cmake) -PKG_CHECK_MODULES(GLIB glib-2.0>=2.10) -IF(NOT GLIB_FOUND) - MESSAGE(FATAL_ERROR "You don't seem to have GLib2 installed.") -ELSE(NOT GLIB_FOUND) - MESSAGE("-- GLib2 found. Libraries: ${GLIB_LIBRARIES}, CFLAGS: ${GLIB_CFLAGS}") -ENDIF(NOT GLIB_FOUND) +ADD_DEFINITIONS(${LIBPGM_CFLAGS}) # Set up build SET(pgm_VERSION @@ -19,7 +13,7 @@ SET(pgm_VERSION # OpenPGM will be built in the binary tree SET(pgm - ${CMAKE_CURRENT_BINARY_DIR}/libpgm-${pgm_VERSION}/openpgm/pgm + ${CMAKE_CURRENT_BINARY_DIR}/libpgm-src/openpgm/pgm ) # This has been adapted from SConscript.libpgm @@ -66,6 +60,35 @@ SET(pgm_SRCS ${pgm}/histogram.c ) +SET(pgm_HDRS + ${pgm}/include/pgm/atomic.h + ${pgm}/include/pgm/backtrace.h + ${pgm}/include/pgm/engine.h + ${pgm}/include/pgm/error.h + ${pgm}/include/pgm/gsi.h + ${pgm}/include/pgm/http.h + ${pgm}/include/pgm/if.h + ${pgm}/include/pgm/list.h + ${pgm}/include/pgm/log.h + ${pgm}/include/pgm/macros.h + ${pgm}/include/pgm/mem.h + ${pgm}/include/pgm/messages.h + ${pgm}/include/pgm/msgv.h + ${pgm}/include/pgm/packet.h + ${pgm}/include/pgm/pgm.h + ${pgm}/include/pgm/signal.h + ${pgm}/include/pgm/skbuff.h + ${pgm}/include/pgm/snmp.h + ${pgm}/include/pgm/socket.h + ${pgm}/include/pgm/time.h + ${pgm}/include/pgm/tsi.h + ${pgm}/include/pgm/types.h + ${pgm}/include/pgm/version.h + ${pgm}/include/pgm/winint.h + ${pgm}/include/pgm/wininttypes.h +) + + SET(pgm_GENERATED ${CMAKE_CURRENT_BINARY_DIR}/version.c ${CMAKE_CURRENT_BINARY_DIR}/galois_tables.c @@ -86,10 +109,24 @@ ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version.c # ... and we need to unpack the tree. ADD_CUSTOM_COMMAND( OUTPUT - ${pgm_SRCS} + ${pgm_SRCS} + ${pgm_HDRS} ${pgm}/galois_generator.pl ${pgm}/version_generator.py - COMMAND bzip2 -dc ${CMAKE_CURRENT_SOURCE_DIR}/libpgm-${pgm_VERSION}.tar.bz2 | tar -C ${CMAKE_CURRENT_BINARY_DIR} -x + DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/libpgm-${pgm_VERSION}.tar.bz2" + COMMAND rm -rf "${CMAKE_CURRENT_BINARY_DIR}/libpgm-src" + COMMAND bzip2 -dc "${CMAKE_CURRENT_SOURCE_DIR}/libpgm-${pgm_VERSION}.tar.bz2" | tar -C "${CMAKE_CURRENT_BINARY_DIR}" -x + COMMAND mv "${CMAKE_CURRENT_BINARY_DIR}/libpgm-${pgm_VERSION}" "${CMAKE_CURRENT_BINARY_DIR}/libpgm-src" +) + +ADD_CUSTOM_COMMAND(TARGET clean + COMMAND rm -rf "${CMAKE_CURRENT_BINARY_DIR}/libpgm-src" +) + +ADD_CUSTOM_TARGET( + unpack_libpgm + DEPENDS ${pgm_SRCS} ${pgm_HDRS} ) INCLUDE_DIRECTORIES( @@ -101,9 +138,14 @@ ADD_LIBRARY( pgm STATIC ${pgm_SRCS} + ${pgm_HDRS} ${pgm_GENERATED} ) +TARGET_LINK_LIBRARIES(pgm + ${LIBPGM_LIBRARIES} +) + LINK_DIRECTORIES( ${GLIB_LIBRARY_DIRS} ) @@ -112,49 +154,3 @@ ADD_DEFINITIONS( ${GLIB_CFLAGS} ) -IF(UNIX) - IF(CMAKE_COMPILER_IS_GNUCC) - # The scripts are fine for Linux/GCC, other platforms may or may - # not work. - ADD_DEFINITIONS( - -std=gnu99 - -D_XOPEN_SOURCE=600 - -D_BSD_SOURCE - -D_REENTRANT - -DCONFIG_HAVE_GETPROTOBYNAME_R2 - -DCONFIG_HAVE_ISO_VARARGS - -DCONFIG_HAVE_ALLOCA_H - -DCONFIG_16BIT_CHECKSUM - -DCONFIG_HAVE_PROC - -DCONFIG_HAVE_BACKTRACE - -DCONFIG_HAVE_PSELECT - -DCONFIG_HAVE_RTC - -DCONFIG_HAVE_TSC - -DCONFIG_HAVE_HPET - -DCONFIG_HAVE_POLL - -DCONFIG_HAVE_EPOLL - -DCONFIG_HAVE_GETIFADDRS - -DCONFIG_HAVE_IFR_NETMASK - -DCONFIG_HAVE_MCAST_JOIN - -DCONFIG_HAVE_IP_MREQN - -DCONFIG_HAVE_SPRINTF_GROUPING - -DCONFIG_HAVE_VASPRINTF - -DCONFIG_HAVE_DSO_VISIBILITY - -DCONFIG_BIND_INADDR_ANY - -DCONFIG_GALOIS_MUL_LUT - -DCONFIG_HAVE_GETOPT - ) - - TARGET_LINK_LIBRARIES(pgm - m rt - ${GLIB_LIBRARIES}) - - SET(_SYSTEM_SPECIFICS_SET 1) - ENDIF(CMAKE_COMPILER_IS_GNUCC) -ENDIF(UNIX) - -# Complain if this is NOT Linux/GCC. -IF(NOT _SYSTEM_SPECIFICS_SET) - MESSAGE(FATAL_ERROR "Can only build libpgm on Unix with gcc.") -ENDIF(NOT _SYSTEM_SPECIFICS_SET) - diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e63636..8eb1961 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,8 @@ CMAKE_MINIMUM_REQUIRED( VERSION 2.6.2 ) # set compiler optimizations for debug and release SET(CMAKE_BUILD_TYPE Debug) +SET(CMAKE_C_FLAGS_DEBUG "-O0 -g -Wall") +SET(CMAKE_C_FLAGS_RELEASE "-O3 -march=native") SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -Wall") SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -march=native") # -DQT_NO_DEBUG_OUTPUT") @@ -37,6 +39,12 @@ INCLUDE_DIRECTORIES( ADD_SUBDIRECTORY(3rdparty) +################################################################################ +# Common multicast transfer module +################################################################################ + +ADD_SUBDIRECTORY(src/net/mcast) + ################################################################################ # Variables ################################################################################ @@ -338,7 +346,7 @@ INSTALL( PROGRAMS misc/pvs-vncsrv DESTINATION bin) INSTALL( FILES ${CMAKE_BINARY_DIR}/org.openslx.pvs.service DESTINATION share/dbus-1/services ) # add package target to our makefile -SET( CPACK_GENERATOR "DEB" ) +SET( CPACK_GENERATOR "DEB;RPM" ) SET( CPACK_SET_DESTDIR "ON" ) SET( CPACK_PACKAGE_NAME "pvs" ) SET( CPACK_PACKAGE_VERSION_MAJOR "2" ) diff --git a/OpenPGMConfig.cmake b/OpenPGMConfig.cmake new file mode 100644 index 0000000..074e65b --- /dev/null +++ b/OpenPGMConfig.cmake @@ -0,0 +1,53 @@ +INCLUDE(${CMAKE_ROOT}/Modules/FindPkgConfig.cmake) + +PKG_CHECK_MODULES(GLIB glib-2.0>=2.10) +IF(NOT GLIB_FOUND) + MESSAGE(FATAL_ERROR "You don't seem to have GLib2 installed.") +ENDIF(NOT GLIB_FOUND) + +IF(UNIX) + IF(CMAKE_COMPILER_IS_GNUCC) + # The scripts are fine for Linux/GCC, other platforms may or may + # not work. + SET(LIBPGM_CFLAGS + -std=gnu99 + -D_XOPEN_SOURCE=600 + -D_BSD_SOURCE + -D_REENTRANT + -DCONFIG_HAVE_GETPROTOBYNAME_R2 + -DCONFIG_HAVE_ISO_VARARGS + -DCONFIG_HAVE_ALLOCA_H + -DCONFIG_16BIT_CHECKSUM + -DCONFIG_HAVE_PROC + -DCONFIG_HAVE_BACKTRACE + -DCONFIG_HAVE_PSELECT + -DCONFIG_HAVE_RTC + -DCONFIG_HAVE_TSC + -DCONFIG_HAVE_HPET + -DCONFIG_HAVE_POLL + -DCONFIG_HAVE_EPOLL + -DCONFIG_HAVE_GETIFADDRS + -DCONFIG_HAVE_IFR_NETMASK + -DCONFIG_HAVE_MCAST_JOIN + -DCONFIG_HAVE_IP_MREQN + -DCONFIG_HAVE_SPRINTF_GROUPING + -DCONFIG_HAVE_VASPRINTF + -DCONFIG_HAVE_DSO_VISIBILITY + -DCONFIG_BIND_INADDR_ANY + -DCONFIG_GALOIS_MUL_LUT + -DCONFIG_HAVE_GETOPT + ) + + SET(LIBPGM_LIBRARIES + m rt + ${GLIB_LIBRARIES}) + + SET(_SYSTEM_SPECIFICS_SET 1) + ENDIF(CMAKE_COMPILER_IS_GNUCC) +ENDIF(UNIX) + +# Complain if this is NOT Linux/GCC. +IF(NOT _SYSTEM_SPECIFICS_SET) + MESSAGE(FATAL_ERROR "Can only build libpgm on Unix with gcc.") +ENDIF(NOT _SYSTEM_SPECIFICS_SET) + diff --git a/src/net/mcast/CMakeLists.txt b/src/net/mcast/CMakeLists.txt new file mode 100644 index 0000000..e418c64 --- /dev/null +++ b/src/net/mcast/CMakeLists.txt @@ -0,0 +1,65 @@ +INCLUDE(../../../OpenPGMConfig.cmake) + +ADD_DEFINITIONS( + ${LIBPGM_CFLAGS} + -D__STDC_CONSTANT_MACROS + -D__STDC_LIMIT_MACROS +) + +# OpenPGM uses the C99 restrict keyword which g++ does not recognize: +IF(CMAKE_COMPILER_IS_GNUCXX) + ADD_DEFINITIONS(-Drestrict=__restrict__) +ENDIF(CMAKE_COMPILER_IS_GNUCXX) + +INCLUDE(${QT_USE_FILE}) + +SET(pvsmcast_MOC_HDRS + McastConfiguration.h + McastPGMSocket.h + McastReceiver.h + McastSender.h +) + +SET(pvsmcast_HDRS + McastConfiguration.h + McastPGMSocket.h + McastReceiver.h + McastSender.h +) + +SET(pvsmcast_SRCS + McastConfiguration.cpp + McastPGMSocket.cpp + McastReceiver.cpp + McastSender.cpp +) + +INCLUDE_DIRECTORIES( + ${CMAKE_BINARY_DIR}/3rdparty/libpgm-src/openpgm/pgm/include +) + +QT4_WRAP_CPP( + pvsmcast_MOC_SRCS + ${pvsmcast_MOC_HDRS} +) + +SET_SOURCE_FILES_PROPERTIES(${pvsmcast_SRCS} ${pvsmcast_MOC_SRCS} + PROPERTIES + OBJECT_DEPENDS "3rdparty/libpgm.a" # Make sure libpgm gets unpacked before building C++ files +) + +ADD_LIBRARY( + pvsmcast + STATIC + ${pvsmcast_HDRS} + ${pvsmcast_SRCS} + ${pvsmcast_MOC_SRCS} +) + +TARGET_LINK_LIBRARIES( + pvsmcast + pgm + ${QT_LIBRARIES} +) + +ADD_SUBDIRECTORY(trial_programs) diff --git a/src/net/mcast/McastConfiguration.cpp b/src/net/mcast/McastConfiguration.cpp new file mode 100644 index 0000000..1a1c0a8 --- /dev/null +++ b/src/net/mcast/McastConfiguration.cpp @@ -0,0 +1,9 @@ +/* + * McastConfiguration.cpp + * + * Created on: Jul 10, 2010 + * Author: brs + */ + +#include "McastConfiguration.h" + diff --git a/src/net/mcast/McastConfiguration.h b/src/net/mcast/McastConfiguration.h new file mode 100644 index 0000000..a609ce1 --- /dev/null +++ b/src/net/mcast/McastConfiguration.h @@ -0,0 +1,159 @@ +/* +# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# src/net/mcast/McastConfiguration.h +# - hold Multicast protocol configuration data +# ----------------------------------------------------------------------------- +*/ + +#ifndef MCASTCONFIGURATION_H_ +#define MCASTCONFIGURATION_H_ + +#include +#include +#include + +#include "McastConstants.h" + +class McastConfiguration: public QObject +{ +Q_OBJECT +public: + McastConfiguration(QObject* parent = 0) : + QObject(parent), + _multicastAddress(DEFAULT_MULTICAST_ADDRESS), + _multicastRate(DEFAULT_MULTICAST_RATE), + _multicastSPort(DEFAULT_MULTICAST_SPORT), + _multicastDPort(DEFAULT_MULTICAST_DPORT), + _multicastWinSize(DEFAULT_MULTICAST_WSIZ), + _multicastMTU(DEFAULT_MULTICAST_MTU), + _multicastUDPPort(DEFAULT_MULTICAST_UDPPORT), + _multicastUseUDP(false) + { + } + + McastConfiguration(McastConfiguration const& other) : + QObject(), + _multicastAddress(other._multicastAddress), + _multicastRate(other._multicastRate), + _multicastSPort(other._multicastSPort), + _multicastDPort(other._multicastDPort), + _multicastWinSize(other._multicastWinSize), + _multicastMTU(other._multicastMTU), + _multicastUDPPort(other._multicastUDPPort), + _multicastUseUDP(other._multicastUseUDP) + { + } + + virtual ~McastConfiguration() + { + } + + QString multicastAddress() const + { + return _multicastAddress; + } + McastConfiguration* multicastAddress(QString const& address) + { + _multicastAddress = address; + return this; + } + + quint16 multicastSPort() const + { + return _multicastSPort; + } + McastConfiguration* multicastSPort(quint16 port) + { + _multicastSPort = port; + return this; + } + + quint16 multicastDPort() const + { + return _multicastDPort; + } + McastConfiguration* multicastDPort(quint16 port) + { + _multicastDPort = port; + return this; + } + + quint32 multicastRate() const + { + return _multicastRate; + } + McastConfiguration* multicastRate(quint32 rate) + { + _multicastRate = rate; + return this; + } + + quint16 multicastWinSize() const + { + return _multicastWinSize; + } + McastConfiguration* multicastWinSize(quint16 size) + { + _multicastWinSize = size; + return this; + } + + quint16 multicastMTU() const + { + return _multicastMTU; + } + McastConfiguration* multicastMTU(quint16 mtu) + { + _multicastMTU = mtu; + return this; + } + + bool multicastUseUDP() const + { + return _multicastUseUDP; + } + McastConfiguration* multicastUseUDP(bool useUDP) + { + _multicastUseUDP = useUDP; + return this; + } + + bool multicastUDPPort() const + { + return _multicastUDPPort; + } + McastConfiguration* multicastUDPPort(quint16 port) + { + _multicastUDPPort = port; + return this; + } + + void commit() + { + emit changed(); + } + +signals: + void changed(); + +private: + QString _multicastAddress; + quint32 _multicastRate; + quint16 _multicastSPort; + quint16 _multicastDPort; + quint16 _multicastWinSize; + quint16 _multicastMTU; + quint16 _multicastUDPPort; + bool _multicastUseUDP; +}; + +#endif /* MCASTCONFIGURATION_H_ */ diff --git a/src/net/mcast/McastConstants.h b/src/net/mcast/McastConstants.h new file mode 100644 index 0000000..712a0d5 --- /dev/null +++ b/src/net/mcast/McastConstants.h @@ -0,0 +1,33 @@ +/* +# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# src/net/mcast/McastMagic.h +# - Specify the magic numbers for the McastFT protocol +# ----------------------------------------------------------------------------- +*/ + +#ifndef MCASTMAGIC_H_ +#define MCASTMAGIC_H_ + +#include + +#define MCASTFT_MAGIC UINT64_C(0x6d60ad83825fb7f9) +#define DEFAULT_MULTICAST_ADDRESS ";239.255.220.207" +#define DEFAULT_MULTICAST_SPORT 6964 +#define DEFAULT_MULTICAST_DPORT 6965 +#define DEFAULT_MULTICAST_UDPPORT 6966 +#define DEFAULT_MULTICAST_RATE (100*1024) +#define DEFAULT_MULTICAST_WSIZ 3000 +#define DEFAULT_MULTICAST_MTU 1400 +#define DEFAULT_MULTICAST_APDU 1200 +#define DEFAULT_MULTICAST_CHUNK 1024 + +#endif /* MCASTMAGIC_H_ */ diff --git a/src/net/mcast/McastPGMSocket.cpp b/src/net/mcast/McastPGMSocket.cpp new file mode 100644 index 0000000..0d6b694 --- /dev/null +++ b/src/net/mcast/McastPGMSocket.cpp @@ -0,0 +1,601 @@ +/* +# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# src/net/mcast/McastPGMSocket.cpp +# - wrap OpenPGM Sockets in a nicer interface -- implementation +# ----------------------------------------------------------------------------- +*/ + +#include + +#include +#include +#include +#include +#include + +#include +// #include +// #define SIZE_MAX UINT64_MAX +// #include +// pgm redefined bool to int. Undo that. +#undef bool + +#include + +#include "McastPGMSocket.h" + +class McastPGMSocket_priv +{ +public: + McastPGMSocket_priv() : + socket(0), + recv_notif(0), + repair_notif(0), + pending_notif(0), + send_notif(0) + { + } + ~McastPGMSocket_priv() + { + if (socket) + pgm_close(socket, 0); + if (recv_notif) + delete recv_notif; + if (repair_notif) + delete repair_notif; + if (pending_notif) + delete pending_notif; + if (send_notif) + delete send_notif; + } + + pgm_sock_t* socket; + McastPGMSocket::Direction direction; + QSocketNotifier* recv_notif; + QSocketNotifier* repair_notif; + QSocketNotifier* pending_notif; + QSocketNotifier* send_notif; + + QSocketNotifier* notifier_for(int fd) { + if (recv_notif && (fd == recv_notif->socket())) + { + return recv_notif; + } + else if (repair_notif && (fd == repair_notif->socket())) + { + return repair_notif; + } + else if (pending_notif && (fd == pending_notif->socket())) + { + return pending_notif; + } + return 0; + } +}; + +static void _ensurePGMInited() +{ + if (!pgm_supported()) + { + pgm_error_t* err; + int good = pgm_init(&err); + if (!good) + { + qCritical() << "Could not init OpenPGM library: PGM Error: " << (err->message ? err->message : "(null)"); + std::exit(1); + } + } +} + +McastPGMSocket::McastPGMSocket(QObject* parent) : + QObject(parent), + _priv(new McastPGMSocket_priv), + _finished(false), + _nakTimeout(new QTimer()), + _dataTimeout(new QTimer()), + _sendTimeout(new QTimer()) +{ + _ensurePGMInited(); + + _nakTimeout->setSingleShot(true); + _dataTimeout->setSingleShot(true); + _sendTimeout->setSingleShot(true); + + connect(_nakTimeout, SIGNAL(timeout()), this, SLOT(handleNakTimeout())); + connect(_dataTimeout, SIGNAL(timeout()), this, SLOT(handleDataTimeout())); + connect(_sendTimeout, SIGNAL(timeout()), this, SLOT(canSend())); +} + +McastPGMSocket::~McastPGMSocket() +{ + delete _priv; + delete _nakTimeout; + delete _dataTimeout; + delete _sendTimeout; +} + +bool McastPGMSocket::open(McastConfiguration const* config, Direction direction) +{ + _priv->direction = direction; + + pgm_error_t* err = 0; + int good; + + pgm_addrinfo_t* addrinfo; + // parse the address string + good = pgm_getaddrinfo(config->multicastAddress().toLatin1().constData(), + 0, &addrinfo, &err); + if (!good) + { + qCritical() << "Could not parse address info: PGM Error: " + << err->message; + } + + sa_family_t family = addrinfo->ai_send_addrs[0].gsr_group.ss_family; + + good + = pgm_socket(&_priv->socket, family, SOCK_SEQPACKET, IPPROTO_PGM, &err); + if (!good) + { + qCritical() << "Could not open socket: PGM Error: " << err->message; + pgm_error_free(err); + return false; + } + + unsigned const ambient_spm = 4096 * 1000; // every four seconds (approx.) + + // set parameters + if (direction == PSOCK_WRITE) + { + // write-only socket + const int send_only = 1, + spm_heartbeat[] = + { 16 * 1000, 16 * 1000, 16 * 1000, 16 * 1000, 32 * 1000, 64 * 1000, 128 + * 1000, 256 * 1000, 512 * 1000, 1024 * 1000, 2048 * 1000, 4096 + * 1000 }, + max_rate = config->multicastRate(), + max_window = config->multicastWinSize(); + // const int max_window_sqns = 3000; + + pgm_setsockopt(_priv->socket, PGM_SEND_ONLY, &send_only, + sizeof(send_only)); + + // SPM messages + pgm_setsockopt(_priv->socket, PGM_AMBIENT_SPM, &ambient_spm, sizeof(ambient_spm)); + pgm_setsockopt(_priv->socket, PGM_HEARTBEAT_SPM, &spm_heartbeat, sizeof(spm_heartbeat)); + + // Transmit window + pgm_setsockopt(_priv->socket, PGM_TXW_MAX_RTE, &max_rate, sizeof(max_rate)); + // pgm_setsockopt(_priv->socket, PGM_TXW_SECS, &max_window, sizeof(max_window)); + pgm_setsockopt(_priv->socket, PGM_TXW_SQNS, &max_window, sizeof(max_window)); + } + else + { + // readonly + const int recv_only = 1, + passive = 0, + max_window = config->multicastWinSize(), + max_winsqns = 0, + peer_expiry = ambient_spm * 5, + spmr_expiry = 250 * 1000, + nak_bo_ivl = 500 * 1000, + nak_rpt_ivl = 500 * 1000, + nak_rdata_ivl = 2000 * 1000, + nak_data_retries = 50, + nak_ncf_retries = 50; + pgm_setsockopt(_priv->socket, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + pgm_setsockopt(_priv->socket, PGM_PASSIVE, &passive, sizeof(passive)); + // pgm_setsockopt(_priv->socket, PGM_RXW_SECS, &max_window, sizeof(max_window)); + pgm_setsockopt(_priv->socket, PGM_RXW_SQNS, &max_window, sizeof(max_window)); + pgm_setsockopt(_priv->socket, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + pgm_setsockopt(_priv->socket, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + pgm_setsockopt(_priv->socket, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + pgm_setsockopt(_priv->socket, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + pgm_setsockopt(_priv->socket, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + pgm_setsockopt(_priv->socket, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + pgm_setsockopt(_priv->socket, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + } + + // MTU + int const mtu = config->multicastMTU(); + pgm_setsockopt(_priv->socket, PGM_MTU, &mtu, sizeof(mtu)); + + pgm_sockaddr_t addr; + addr.sa_addr.sport = config->multicastSPort(); + addr.sa_port = config->multicastDPort(); + good = pgm_gsi_create_from_hostname(&addr.sa_addr.gsi, &err); + if (!good) + { + qCritical() << "Could not generate a GSI: PGM Error: " << err->message; + pgm_error_free(err); + return false; + } + + good = pgm_bind3(_priv->socket, &addr, sizeof(addr), (struct group_req*)&addrinfo->ai_send_addrs[0], sizeof(struct group_req), (struct group_req*)&addrinfo->ai_recv_addrs[0], sizeof(struct group_req), &err); + if (!good) + { + qCritical() << "Could not bind socket: PGM Error: " << err->message; + pgm_error_free(err); + return false; + } + + // qDebug() << "Max APDU is " << _priv->socket->max_apdu; + // qDebug() << "Max TPDU is " << _priv->socket->max_tpdu; + // qDebug() << "Max TSDU Fragment is " << _priv->socket->max_tsdu_fragment; + // qDebug() << "TXW_SQNS is " << _priv->socket->txw_sqns; + + // join the group + for (unsigned i = 0; i < addrinfo->ai_recv_addrs_len; i++) + { + pgm_setsockopt(_priv->socket, PGM_JOIN_GROUP, + &addrinfo->ai_recv_addrs[i], sizeof(struct group_req)); + } + + // set send address + pgm_setsockopt(_priv->socket, PGM_SEND_GROUP, &addrinfo->ai_send_addrs[0], + sizeof(struct group_req)); + + // IP parameters + const int nonblocking = 1, multicast_loop = 0, multicast_hops = 16; + pgm_setsockopt(_priv->socket, PGM_MULTICAST_LOOP, &multicast_loop, + sizeof(multicast_loop)); + pgm_setsockopt(_priv->socket, PGM_MULTICAST_HOPS, &multicast_hops, + sizeof(multicast_hops)); + pgm_setsockopt(_priv->socket, PGM_NOBLOCK, &nonblocking, + sizeof(nonblocking)); + + good = pgm_connect(_priv->socket, &err); + if (!good) + { + qCritical() << "Could not connect socket: PGM Error: " << err->message; + pgm_error_free(err); + return false; + } + + setupNotifiers(); + + pgm_freeaddrinfo(addrinfo); + + return true; +} + +void McastPGMSocket::setupNotifiers() +{ + int recv_sock, repair_sock, pending_sock; + char const* slotname = (_priv->direction == PSOCK_WRITE) ? SLOT(handleNak(int)) : SLOT(handleData(int)); + + pgm_getsockopt(_priv->socket, PGM_RECV_SOCK, &recv_sock, sizeof(recv_sock)); + _priv->recv_notif = new QSocketNotifier(recv_sock, QSocketNotifier::Read, + this); + connect(_priv->recv_notif, SIGNAL(activated(int)), this, slotname); + + pgm_getsockopt(_priv->socket, PGM_REPAIR_SOCK, &repair_sock, sizeof(repair_sock)); + _priv->repair_notif = new QSocketNotifier(repair_sock, + QSocketNotifier::Read, this); + connect(_priv->repair_notif, SIGNAL(activated(int)), this, slotname); + + pgm_getsockopt(_priv->socket, PGM_PENDING_SOCK, &pending_sock, sizeof(pending_sock)); + _priv->pending_notif = new QSocketNotifier(pending_sock, + QSocketNotifier::Read, this); + connect(_priv->pending_notif, SIGNAL(activated(int)), this, slotname); + + if(_priv->direction == PSOCK_WRITE) + { + struct pollfd pfd; + int nfds = 1; + pgm_poll_info(_priv->socket, &pfd, &nfds, POLLOUT); + _priv->send_notif = new QSocketNotifier(pfd.fd, QSocketNotifier::Write, this); + connect(_priv->send_notif, SIGNAL(activated(int)), this, SLOT(canSend())); + } +} + +void McastPGMSocket::handleNak(int fd) +{ + qDebug() << "handleNak(int)"; + + QSocketNotifier* notif = _priv->notifier_for(fd); + notif->setEnabled(false); + + handleNak(); + + notif->setEnabled(true); +} + +void McastPGMSocket::handleNak() +{ + if (_finished) + return; + + qDebug() << "handleNak()"; + + QTimer::singleShot(1000, this, SLOT(handleNakTimeout())); + + // to handle NAKs in OpenPGM, we need to pgm_recv: + char buf[4096]; + pgm_error_t* err = 0; + + int status; + // while we don't block: + do + { + status = pgm_recv(_priv->socket, buf, sizeof(buf), MSG_DONTWAIT, 0, &err); + + if(status == PGM_IO_STATUS_TIMER_PENDING) + { + struct timeval tv; + pgm_getsockopt(_priv->socket, PGM_TIME_REMAIN, &tv, sizeof(tv)); + const int msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + qDebug() << " timer pending: " << msecs << "ms"; + _nakTimeout->start(msecs); + break; + } + else if(status == PGM_IO_STATUS_RATE_LIMITED) + { + struct timeval tv; + pgm_getsockopt(_priv->socket, PGM_RATE_REMAIN, &tv, sizeof(tv)); + const int msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + qDebug() << " rate limited: " << msecs << "ms"; + _nakTimeout->start(msecs); + break; + } + else if(status == PGM_IO_STATUS_WOULD_BLOCK) + { + qDebug() << " wouldblock"; + break; + } + else + { + if(err) + { + qCritical() << "Could not handle NAKs: PGM Error: " << err->message; + pgm_error_free(err); + err = 0; + } + } + } + while (true); +} + +void McastPGMSocket::handleNakTimeout() +{ + qDebug() << "handleNakTimeout()"; + + handleNak(); +} + +void McastPGMSocket::handleData(int fd) +{ + // need to guard against destruction in finish() via signals/slots + QPointer notif(_priv->notifier_for(fd)); + notif->setEnabled(false); + + handleData(); + + if (notif) + notif->setEnabled(true); +} + +void McastPGMSocket::handleData() +{ + qDebug() << "handleData()"; + + if (_finished) { + qDebug() << " finished!"; + return; + } + + int status; + do + { + char buf[4096]; + size_t size; + pgm_error_t* err; + + status = pgm_recv(_priv->socket, buf, sizeof(buf), MSG_DONTWAIT, &size, &err); + + if (status == PGM_IO_STATUS_NORMAL) + { + qDebug() << " normally received"; + if(size > 0) + { + QByteArray bytes(buf, size); + emit receivedPacket(bytes); + } + } + else if (status == PGM_IO_STATUS_WOULD_BLOCK) + { + qDebug() << " would block"; + // nothing more to do this time + break; + } + else if (status == PGM_IO_STATUS_TIMER_PENDING) + { + struct timeval tv; + pgm_getsockopt(_priv->socket, PGM_TIME_REMAIN, &tv, sizeof(tv)); + const int msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + qDebug() << " timer pending: " << msecs << "ms"; + _dataTimeout->start(msecs); + break; + } + else if (status == PGM_IO_STATUS_RATE_LIMITED) + { + struct timeval tv; + pgm_getsockopt(_priv->socket, PGM_RATE_REMAIN, &tv, sizeof(tv)); + const int msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + qDebug() << " rate limit pending: " << msecs << "ms"; + _dataTimeout->start(msecs); + break; + } + else if (status == PGM_IO_STATUS_RESET) + { + qDebug() << " connection reset"; + emit connectionReset(); + qCritical() << "Connection Reset: PGM Error: " << (err ? err->message : "(null)"); + break; + } + else if (status == PGM_IO_STATUS_FIN) + { + qDebug() << " connection finished"; + emit connectionFinished(); + break; + } + else + { + if(err) + { + qCritical() << "Could not read packet: PGM Error: " << (err ? err->message: "(null)"); + break; + } + } + + // the socket might have been closed from under us + if (!_priv->socket) + break; + } + while (true); +} + +void McastPGMSocket::handleDataTimeout() +{ + qDebug() << "handleDataTimeout()"; + + handleData(); +} + +void McastPGMSocket::canSend() +{ + if (_finished) + return; + + qDebug() << "canSend()"; + + if (_priv->send_notif) + { + _priv->send_notif->setEnabled(false); + } + + bool reenable = true; + + while (!_q.isEmpty()) + { + int status; + QByteArray const nextPacket(_q.head()); + status = pgm_send(_priv->socket, nextPacket.constData(), nextPacket.size(), 0); + if (status == PGM_IO_STATUS_ERROR || status == PGM_IO_STATUS_RESET) + { + qCritical() << "Could not send packet: PGM Error."; + continue; + } + else if (status == PGM_IO_STATUS_WOULD_BLOCK) + { + qDebug() << " would block"; + break; + } + else if (status == PGM_IO_STATUS_RATE_LIMITED) + { + struct timeval tv; + pgm_getsockopt(_priv->socket, PGM_RATE_REMAIN, &tv, sizeof(tv)); + const int msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + qDebug() << " rate limited:" << msecs << "ms"; + _sendTimeout->start((msecs > 0) ? msecs : 1); + reenable = false; + break; + } + else if (status == PGM_IO_STATUS_NORMAL) + { + qDebug() << " sent"; + _q.dequeue(); + continue; + } + else + { + qCritical() << "Unhandled condition in McastPGMSocket::canSend()"; + } + } + + if (_priv->send_notif && reenable) + { + emit readyToSend(); + + qDebug() << " reenable notifier"; + _priv->send_notif->setEnabled(true); + } +} + +void McastPGMSocket::sendPacket(QByteArray const& bytes) +{ + if(_q.isEmpty()) + { + int status = pgm_send(_priv->socket, bytes.constData(), bytes.size(), 0); + + if (status == PGM_IO_STATUS_ERROR || status == PGM_IO_STATUS_RESET) + { + qCritical() << "Could not send packet: PGM Error."; + return; + } + else if (status == PGM_IO_STATUS_WOULD_BLOCK) + { + _q.enqueue(bytes); + } + else if (status == PGM_IO_STATUS_RATE_LIMITED) + { + _q.enqueue(bytes); + struct timeval tv; + pgm_getsockopt(_priv->socket, PGM_RATE_REMAIN, &tv, sizeof(tv)); + _dataTimeout->start((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + } + else if (status == PGM_IO_STATUS_NORMAL) + { + return; + } + else + { + qCritical() << "Unhandled condition in McastPGMSocket::sendPacket()"; + } + } else { + _q.enqueue(bytes); + } +} + +void McastPGMSocket::finish() +{ + if(_priv->pending_notif) + { + delete _priv->pending_notif; + _priv->pending_notif = 0; + } + if(_priv->recv_notif) + { + delete _priv->recv_notif; + _priv->recv_notif = 0; + } + if(_priv->repair_notif) + { + delete _priv->repair_notif; + _priv->repair_notif = 0; + } + if(_priv->send_notif) + { + delete _priv->send_notif; + _priv->send_notif = 0; + } + + pgm_close(_priv->socket, 1); + _priv->socket = 0; + + _finished = true; +} + +bool McastPGMSocket::finished() const +{ + return _finished; +} diff --git a/src/net/mcast/McastPGMSocket.h b/src/net/mcast/McastPGMSocket.h new file mode 100644 index 0000000..b0007a7 --- /dev/null +++ b/src/net/mcast/McastPGMSocket.h @@ -0,0 +1,74 @@ +/* +# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# src/net/mcast/McastPGMSocket.h +# - wrap OpenPGM Sockets in a nicer interface -- interface +# ----------------------------------------------------------------------------- +*/ + +#ifndef MCASTPGMSOCKET_H_ +#define MCASTPGMSOCKET_H_ + +#include +#include +#include + +#include + +class McastPGMSocket_priv; +class QTimer; + +class McastPGMSocket : public QObject +{ + Q_OBJECT +public: + enum Direction { + PSOCK_READ, + PSOCK_WRITE + }; + + McastPGMSocket(QObject* parent = 0); + virtual ~McastPGMSocket(); + + bool open(McastConfiguration const* config, Direction direction); + bool finished() const; + +signals: + void readyToSend(); + void receivedPacket(QByteArray const& bytes); + void connectionReset(); + void connectionFinished(); + +public slots: + void sendPacket(QByteArray const& bytes); + void finish(); + +private slots: + void handleNak(int fd); + void handleData(int fd); + void handleNak(); + void handleData(); + void handleNakTimeout(); + void handleDataTimeout(); + void canSend(); + +private: + McastPGMSocket_priv* _priv; + QQueue _q; + bool _finished; + QTimer* _nakTimeout; + QTimer* _dataTimeout; + QTimer* _sendTimeout; + + void setupNotifiers(); +}; + +#endif /* MCASTPGMSOCKET_H_ */ diff --git a/src/net/mcast/McastReceiver.cpp b/src/net/mcast/McastReceiver.cpp new file mode 100644 index 0000000..7480ac2 --- /dev/null +++ b/src/net/mcast/McastReceiver.cpp @@ -0,0 +1,138 @@ +/* +# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# src/net/mcast/McastReceiver.h +# - implement the receiver-side multicast file transfer protocol -- implementation +# ----------------------------------------------------------------------------- +*/ + +#include +#include +#include + +#include +// OpenPGM #defines bool. This is bad in C++. +#undef bool + +#include "McastConstants.h" +#include "McastReceiver.h" + +McastReceiver::McastReceiver(QIODevice* iodev, McastConfiguration* config, QObject* parent) : + QObject(parent), + _config(config ? new McastConfiguration(*config) : new McastConfiguration()), + _socket(0), + _curoffs(0), + _closed(false), + _hash(QCryptographicHash::Md5), + _iodev(iodev) +{ + _config->setParent(this); +} + +McastReceiver::~McastReceiver() +{ + if (_config) + delete _config; +} + +void McastReceiver::start() +{ + _socket = new McastPGMSocket(this); + connect(_socket, SIGNAL(receivedPacket(QByteArray)), this, SLOT(receivedPacket(QByteArray))); + connect(_socket, SIGNAL(connectionReset()), this, SLOT(connectionReset())); + // connect(_socket, SIGNAL(connectionFinished()), this, SLOT(connectionFinished())); + _socket->open(_config, McastPGMSocket::PSOCK_READ); +} + +void McastReceiver::receivedPacket(QByteArray const& bytes) +{ + if(_closed) + return; + + quint16 checksum_should = qChecksum(bytes.constData(), bytes.size() - 2); + + QDataStream strm(bytes); + strm.setByteOrder(QDataStream::BigEndian); + + // read the packet + quint64 magic; + quint64 offset; + quint16 checksum; + + + strm >> magic; + if(magic != MCASTFT_MAGIC) + { + qCritical() << "Received packet whose magic number does not match. Ignoring."; + return; + } + + strm >> offset; + qDebug() << " Received packet for offset" << offset; + + if (offset == UINT64_C(0xffffffffffffffff)) + { + // this is the end of the data stream. + QByteArray md5; + strm >> md5; + + quint16 fchecksum; + strm >> fchecksum; + + // compare the hash value + if ((fchecksum != checksum_should) || (md5 != _hash.result())) + { + _close(RES_MD5_MISMATCH); + } + else + { + _close(RES_OK); + } + + return; + } + else if (offset != _curoffs) + { + qCritical() << "Packet loss or double delivery. PGM should have prevented this. Bailing out."; + _close(RES_OFFSET_MISMATCH); + return; + } + + QByteArray contents; + strm >> contents; + _curoffs += contents.size(); + + strm >> checksum; + if(checksum != checksum_should) + { + qCritical() << "Checksum does not match. Bailing out."; + _close(RES_CHECKSUM_MISMATCH); + return; + } + + _hash.addData(contents); + + _iodev->write(contents); +} + +void McastReceiver::connectionReset() +{ + _close(RES_CONNECTION_RESET); +} + +void McastReceiver::_close(Result result) +{ + _iodev->close(); + _socket->finish(); + + _closed = true; + emit finished(result); +} diff --git a/src/net/mcast/McastReceiver.h b/src/net/mcast/McastReceiver.h new file mode 100644 index 0000000..38e1219 --- /dev/null +++ b/src/net/mcast/McastReceiver.h @@ -0,0 +1,76 @@ +/* +# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# src/net/mcast/McastReceiver.h +# - implement the receiver-side multicast file transfer protocol -- interface +# ----------------------------------------------------------------------------- +*/ + +#ifndef MCASTRECEIVER_H_ +#define MCASTRECEIVER_H_ + +#include +#include +#include +#include +#include + +#include +#include + +class McastReceiver : public QObject +{ + Q_OBJECT +public: + enum Result { + RES_OK, + RES_ABORTED, + RES_OFFSET_MISMATCH, + RES_CHECKSUM_MISMATCH, + RES_MD5_MISMATCH, + RES_CONNECTION_RESET + }; + + McastReceiver(QIODevice* iodev, McastConfiguration* config = 0, QObject* parent = 0); + virtual ~McastReceiver(); + + McastConfiguration* config() + { + return _config; + } + + static inline bool is_error(Result result) + { + return result != RES_OK; + } + +signals: + void finished(int result); + +public slots: + void start(); + +private: + McastConfiguration* _config; + McastPGMSocket* _socket; + quint64 _curoffs; + bool _closed; + QCryptographicHash _hash; + QIODevice* _iodev; + +private slots: + void receivedPacket(QByteArray const& bytes); + void connectionReset(); + + void _close(Result result); +}; + +#endif /* MCASTRECEIVER_H_ */ diff --git a/src/net/mcast/McastSender.cpp b/src/net/mcast/McastSender.cpp new file mode 100644 index 0000000..24a629c --- /dev/null +++ b/src/net/mcast/McastSender.cpp @@ -0,0 +1,96 @@ +/* +# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# src/net/mcast/McastReceiver.h +# - implement the receiver-side multicast file transfer protocol -- implementation +# ----------------------------------------------------------------------------- +*/ + +#include "McastSender.h" +#include "McastConstants.h" + +#include + +#include +// OpenPGM #defines bool. This is bad in C++. +#undef bool + +McastSender::McastSender(QIODevice* iodev, McastConfiguration const* config, QObject* parent) : + QObject(parent), + _config(config ? new McastConfiguration(*config) : new McastConfiguration()), + _socket(0), + _iodev(iodev), + _curoffs(0), + _hash(QCryptographicHash::Md5), + _finished(false) +{ +} + +McastSender::~McastSender() +{ + delete _config; +} + +void McastSender::start() +{ + _socket = new McastPGMSocket(this); + connect(_socket, SIGNAL(readyToSend()), this, SLOT(readyToSend())); + _socket->open(_config, McastPGMSocket::PSOCK_WRITE); +} + +void McastSender::readyToSend() +{ + if(_finished) + return; + + if(_iodev->atEnd()) + { + QByteArray fpdu; + QDataStream strm(&fpdu, QIODevice::WriteOnly); + strm.setByteOrder(QDataStream::BigEndian); + + strm << (quint64)MCASTFT_MAGIC << (quint64)UINT64_C(0xffffffffffffffff) << _hash.result(); + strm << qChecksum(fpdu.constData(), fpdu.size()); + + _socket->sendPacket(fpdu); + // _socket->finish(); + + _finished = true; + + emit finished(); + return; + } + + QByteArray barr(DEFAULT_MULTICAST_APDU, '\0'); + qint64 len_read; + len_read = _iodev->read(barr.data(), barr.capacity()); + barr.resize((int)len_read); + + _hash.addData(barr); + + QByteArray pdu; + QDataStream strm(&pdu, QIODevice::WriteOnly); + strm.setByteOrder(QDataStream::BigEndian); + + strm << (quint64)MCASTFT_MAGIC << _curoffs; + strm << barr; + quint16 checksum = qChecksum(pdu.constData(), pdu.size()); + strm << checksum; + + _curoffs += len_read; + + _socket->sendPacket(pdu); +} + +void McastSender::close() +{ + _socket->finish(); +} diff --git a/src/net/mcast/McastSender.h b/src/net/mcast/McastSender.h new file mode 100644 index 0000000..e713886 --- /dev/null +++ b/src/net/mcast/McastSender.h @@ -0,0 +1,68 @@ +/* +# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# src/net/mcast/McastReceiver.h +# - implement the sender-side multicast file transfer protocol -- interface +# ----------------------------------------------------------------------------- +*/ + +#ifndef MCASTSENDER_H_ +#define MCASTSENDER_H_ + +#include +#include +#include + +#include "McastConfiguration.h" +#include "McastPGMSocket.h" + +class McastSender : public QObject +{ + Q_OBJECT +public: + McastSender(QIODevice* iodev = 0, McastConfiguration const* config = 0, QObject* parent = 0); + virtual ~McastSender(); + + McastConfiguration* config() + { + return _config; + } + + QIODevice* iodevice() const + { + return _iodev; + } + + void setIODevice(QIODevice* iodevice) + { + _iodev = iodevice; + } + +signals: + void finished(); + +public slots: + void start(); + void close(); + +private slots: + void readyToSend(); + +private: + McastConfiguration* _config; + McastPGMSocket* _socket; + QIODevice* _iodev; + quint64 _curoffs; + QCryptographicHash _hash; + bool _finished; +}; + +#endif /* MCASTSENDER_H_ */ diff --git a/src/net/mcast/trial_programs/CMakeLists.txt b/src/net/mcast/trial_programs/CMakeLists.txt new file mode 100644 index 0000000..d0f68fa --- /dev/null +++ b/src/net/mcast/trial_programs/CMakeLists.txt @@ -0,0 +1,38 @@ +INCLUDE(${QT_USE_FILE}) + +QT4_WRAP_CPP( + mcastsend_MOC + mcastsend.h +) + +QT4_WRAP_CPP( + mcastreceive_MOC + mcastreceive.h +) + +SET(argparser_SRC + McastConfigArgParser.h + McastConfigArgParser.cpp +) + +ADD_EXECUTABLE(mcastsend + mcastsend.cpp + mcastsend.h + ${argparser_SRC} + ${mcastsend_MOC} +) + +ADD_EXECUTABLE(mcastreceive + mcastreceive.cpp + mcastreceive.h + ${argparser_SRC} + ${mcastreceive_MOC} +) + +TARGET_LINK_LIBRARIES(mcastsend + pvsmcast +) + +TARGET_LINK_LIBRARIES(mcastreceive + pvsmcast +) \ No newline at end of file diff --git a/src/net/mcast/trial_programs/McastConfigArgParser.cpp b/src/net/mcast/trial_programs/McastConfigArgParser.cpp new file mode 100644 index 0000000..8849544 --- /dev/null +++ b/src/net/mcast/trial_programs/McastConfigArgParser.cpp @@ -0,0 +1,151 @@ +/* +# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# src/net/mcast/trial_programs/McastConfigArgParser.cpp +# - Parse common Multicast Configuration CLI arguments +# ----------------------------------------------------------------------------- +*/ + +#include + +#include + +#include "McastConfigArgParser.h" + +using namespace std; + +bool parseMcastConfigArg(QStringList::iterator& i, QStringList::iterator const& end, McastConfiguration* config) +{ + QString arg = *i; + + if (arg == "-addr") + { + i++; + if(i == end) + { + cerr << "Option " << arg.toLatin1().constData() << " is missing argument" << endl; + return false; + } + config->multicastAddress(*i); + } + else if (arg == "-dport") + { + i++; + if(i == end) + { + cerr << "Option " << arg.toLatin1().constData() << " is missing argument" << endl; + return false; + } + bool ok; + quint16 dport = (quint16)i->toInt(&ok); + if (!ok) + { + cerr << "Error: dport is not an integer" << endl; + return false; + } + config->multicastDPort(dport); + } + else if (arg == "-sport") + { + i++; + if(i == end) + { + cerr << "Option " << arg.toLatin1().constData() << " is missing argument" << endl; + return false; + } + bool ok; + quint16 sport = (quint16)i->toInt(&ok); + if (!ok) + { + cerr << "Error: sport is not an integer" << endl; + return false; + } + config->multicastSPort(sport); + } + else if (arg == "-mtu") + { + i++; + if(i == end) + { + cerr << "Option " << arg.toLatin1().constData() << " is missing argument" << endl; + return false; + } + bool ok; + quint16 mtu = (quint16)i->toInt(&ok); + if (!ok) + { + cerr << "Error: MTU is not an integer" << endl; + return false; + } + config->multicastMTU(mtu); + } + else if (arg == "-rate") + { + i++; + if(i == end) + { + cerr << "Option " << arg.toLatin1().constData() << " is missing argument" << endl; + return false; + } + bool ok; + quint32 rate = i->toInt(&ok); + if (!ok) + { + cerr << "Error: Rate is not an integer" << endl; + return false; + } + config->multicastRate(rate); + } + else if (arg == "-winsize") + { + i++; + if(i == end) + { + cerr << "Option " << arg.toLatin1().constData() << " is missing argument" << endl; + return false; + } + bool ok; + quint16 winsize = (quint16)i->toInt(&ok); + if (!ok) + { + cerr << "Error: Winsize is not an integer" << endl; + return false; + } + config->multicastWinSize(winsize); + } + else if (arg == "-udp") + { + config->multicastUseUDP(true); + } + else if (arg == "-udp-port") + { + i++; + if(i == end) + { + cerr << "Option " << arg.toLatin1().constData() << " is missing argument" << endl; + return false; + } + bool ok; + quint16 udpport = (quint16)i->toInt(&ok); + if (!ok) + { + cerr << "Error: UDP-Port is not an integer" << endl; + return false; + } + config->multicastUDPPort(udpport); + } + else + { + return false; + } + + return true; +} diff --git a/src/net/mcast/trial_programs/McastConfigArgParser.h b/src/net/mcast/trial_programs/McastConfigArgParser.h new file mode 100644 index 0000000..4fb18a7 --- /dev/null +++ b/src/net/mcast/trial_programs/McastConfigArgParser.h @@ -0,0 +1,26 @@ +/* +# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# src/net/mcast/trial_programs/McastConfigArgParser.h +# - Parse common Multicast Configuration CLI arguments +# ----------------------------------------------------------------------------- +*/ + +#ifndef MCASTCONFIGARGPARSER_H_ +#define MCASTCONFIGARGPARSER_H_ + +#include + +#include "../McastConfiguration.h" + +bool parseMcastConfigArg(QStringList::iterator& i, QStringList::iterator const& end, McastConfiguration* config); + +#endif /* MCASTCONFIGARGPARSER_H_ */ diff --git a/src/net/mcast/trial_programs/mcastreceive.cpp b/src/net/mcast/trial_programs/mcastreceive.cpp new file mode 100644 index 0000000..48a0f10 --- /dev/null +++ b/src/net/mcast/trial_programs/mcastreceive.cpp @@ -0,0 +1,150 @@ +/* +# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# src/net/mcast/trial_programs/mcastsend.cpp +# - Receive a file via the PVS Mcast protocol +# ----------------------------------------------------------------------------- +*/ + +#include + +#include +#include +#include +#include + +#include "mcastreceive.h" +#include "McastConfigArgParser.h" +#include "../McastConfiguration.h" +#include "../McastReceiver.h" + +using namespace std; + +int main(int argc, char** argv) +{ + QCoreApplication app(argc, argv); + McastReceive me; + + QTimer::singleShot(0, &me, SLOT(run())); + + return app.exec(); +} + +void McastReceive::run() +{ + QStringList args = QCoreApplication::arguments(); + QStringList::iterator i = args.begin(); + QStringList::iterator const end = args.end(); + + QString filename(""); + + McastConfiguration config; + + ++i; + while (i != end) + { + QString arg = *i; + + cerr << "Arg: " << arg.toLatin1().constData() << endl; + + if (arg == "-file") + { + ++i; + if (i == end) + { + cerr << "Option " << arg.toLatin1().constData() << " is missing its argument" << endl; + QCoreApplication::exit(1); + return; + } + filename = *i; + } + else if (arg == "-help") + { + cerr << "Options:" << endl << endl + << " -file Receive to file FILE" << endl + << " -addr Use ADDR as address specification" << endl + << " -dport Send to port PORT" << endl + << " -sport Send from port PORT" << endl + << " -mtu Set MTU to BYTES" << endl + << " -rate Send BYTES per second" << endl + << " -winsize Set Window Size to SECONDS" << endl + << " -udp Use UDP encapsulation" << endl + << " -udp-port PORT Use UDP port PORT" << endl; + QCoreApplication::quit(); + return; + } + else + { + if (!parseMcastConfigArg(i, end, &config)) + { + cerr << "Unknown argument: " << arg.toLatin1().constData() << endl; + QCoreApplication::exit(1); + return; + } + } + + ++i; + } + + if (filename == "") + { + cerr << "No Filename given" << endl; + QCoreApplication::exit(1); + return; + } + + _target = new QFile(filename, this); + _target->open(QIODevice::WriteOnly); + + McastReceiver* recv = new McastReceiver(_target, &config, this); + + connect(recv, SIGNAL(finished(int)), this, SLOT(finished(int))); + + QTimer::singleShot(0, recv, SLOT(start())); +} + +void McastReceive::finished(int state) +{ + cerr << "finished: "; + + switch(state) + { + case McastReceiver::RES_OK: + cerr << "OK." << endl; + break; + case McastReceiver::RES_ABORTED: + cerr << "Aborted." << endl; + goto failed; + case McastReceiver::RES_CHECKSUM_MISMATCH: + cerr << "Checksum mismatch." << endl; + goto failed; + case McastReceiver::RES_CONNECTION_RESET: + cerr << "Connection reset." << endl; + goto failed; + case McastReceiver::RES_MD5_MISMATCH: + cerr << "MD5 mismatch." << endl; + goto failed; + case McastReceiver::RES_OFFSET_MISMATCH: + cerr << "Offset mismatch. Undetected packet loss?" << endl; + goto failed; + default: + cerr << "Unknown error code!" << endl; + goto failed; + } + + QCoreApplication::quit(); + return; +failed: + cerr << "Deleting file." << endl; + _target->remove(); + QCoreApplication::exit(1); + return; +} diff --git a/src/net/mcast/trial_programs/mcastreceive.h b/src/net/mcast/trial_programs/mcastreceive.h new file mode 100644 index 0000000..3e72d4c --- /dev/null +++ b/src/net/mcast/trial_programs/mcastreceive.h @@ -0,0 +1,44 @@ +/* +# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# src/net/mcast/trial_programs/mcastsend.cpp +# - Receive a file via the PVS Mcast protocol +# ----------------------------------------------------------------------------- +*/ + +#ifndef MCASTRECEIVE_H_ +#define MCASTRECEIVE_H_ + +#include + +class QFile; +class McastReceiver; + +class McastReceive : public QObject +{ + Q_OBJECT +public: + McastReceive() : + QObject(), + _receiver(0) + { + } + +public slots: + void run(); + void finished(int state); + +private: + McastReceiver* _receiver; + QFile* _target; +}; + +#endif /* MCASTRECEIVE_H_ */ diff --git a/src/net/mcast/trial_programs/mcastsend.cpp b/src/net/mcast/trial_programs/mcastsend.cpp new file mode 100644 index 0000000..da8ecf4 --- /dev/null +++ b/src/net/mcast/trial_programs/mcastsend.cpp @@ -0,0 +1,123 @@ +/* +# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# src/net/mcast/trial_programs/mcastsend.cpp +# - Send a file via the PVS Mcast protocol +# ----------------------------------------------------------------------------- +*/ + +#include + +#include +#include +#include +#include + +#include +#include "mcastsend.h" +#include "McastConfigArgParser.h" +#include "../McastConstants.h" +#include "../McastConfiguration.h" + +using namespace std; + +int +main(int argc, char**argv) +{ + QCoreApplication app(argc, argv); + McastSend me; + + QTimer::singleShot(0, &me, SLOT(run())); + + return app.exec(); +} + +void McastSend::run() +{ + QStringList args = QCoreApplication::arguments(); + QStringList::iterator i = args.begin(); + QStringList::iterator const end = args.end(); + QString filename(""); + McastConfiguration config; + + ++i; + while(i != end) + { + // parse command line arguments + + QString arg = *i; + + cerr << "Arg: " << arg.toLatin1().constData() << endl; + + if (arg == "-file") + { + i++; + if(i == end) + { + cerr << "Option " << arg.toLatin1().constData() << " is missing argument" << endl; + QCoreApplication::exit(1); + return; + } + filename = *i; + } + else if (arg == "-help") + { + cerr << "Options:" << endl << endl + << " -file Send FILE to the listeners" << endl + << " -addr Use ADDR as address specification" << endl + << " -dport Send to port PORT" << endl + << " -sport Send from port PORT" << endl + << " -mtu Set MTU to BYTES" << endl + << " -rate Send BYTES per second" << endl + << " -winsize Set Window Size to SECONDS" << endl + << " -udp Use UDP encapsulation" << endl + << " -udp-port PORT Use UDP port PORT" << endl; + QCoreApplication::quit(); + return; + } + else + { + if (!parseMcastConfigArg(i, end, &config)) + { + cerr << "Unknown command line argument: " << arg.toLatin1().constData() << endl; + QCoreApplication::exit(1); + return; + } + } + + ++i; + } + + if(filename == "") + { + cerr << "No filename given" << endl; + QCoreApplication::exit(1); + return; + } + + // now, do it. + QFile* file = new QFile(filename); + file->open(QIODevice::ReadOnly); + + McastSender* sender = new McastSender(file, &config, this); + file->setParent(sender); + + connect(sender, SIGNAL(finished()), this, SLOT(finished())); + + QTimer::singleShot(0, sender, SLOT(start())); +} + +void McastSend::finished() +{ + cerr << "finished." << endl; + // QTimer::singleShot(30000, QCoreApplication::instance(), SLOT(quit())); + // QCoreApplication::quit(); +} diff --git a/src/net/mcast/trial_programs/mcastsend.h b/src/net/mcast/trial_programs/mcastsend.h new file mode 100644 index 0000000..ae15eb4 --- /dev/null +++ b/src/net/mcast/trial_programs/mcastsend.h @@ -0,0 +1,42 @@ +/* +# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# src/net/mcast/trial_programs/mcastsend.cpp +# - Send a file via the PVS Mcast protocol +# ----------------------------------------------------------------------------- +*/ + +#ifndef MCASTSEND_H_ +#define MCASTSEND_H_ + +#include + +#include "../McastSender.h" + +class McastSend : public QObject +{ + Q_OBJECT +public: + McastSend() : + QObject(), + _sender(0) + { + } + +public slots: + void run(); + void finished(); + +private: + McastSender* _sender; +}; + +#endif /* MCASTSEND_H_ */ -- cgit v1.2.3-55-g7522 From b4059aff0fbc54ba67fae57c743f5900e7b73f92 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 14 Jul 2010 16:10:00 +0200 Subject: Make UDP encapsulation correctly configurable. --- src/net/mcast/McastConfiguration.h | 26 +++++++++++++++------- src/net/mcast/McastConstants.h | 1 + src/net/mcast/McastPGMSocket.cpp | 21 +++++++++++++++-- .../mcast/trial_programs/McastConfigArgParser.cpp | 6 ++++- 4 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/net/mcast/McastConfiguration.h b/src/net/mcast/McastConfiguration.h index a609ce1..c32ac4f 100644 --- a/src/net/mcast/McastConfiguration.h +++ b/src/net/mcast/McastConfiguration.h @@ -35,8 +35,8 @@ public: _multicastDPort(DEFAULT_MULTICAST_DPORT), _multicastWinSize(DEFAULT_MULTICAST_WSIZ), _multicastMTU(DEFAULT_MULTICAST_MTU), - _multicastUDPPort(DEFAULT_MULTICAST_UDPPORT), - _multicastUseUDP(false) + _multicastUDPPortBase(DEFAULT_MULTICAST_UDPPORT), + _multicastUseUDP(DEFAULT_MULTICAST_USEUDP) { } @@ -48,7 +48,7 @@ public: _multicastDPort(other._multicastDPort), _multicastWinSize(other._multicastWinSize), _multicastMTU(other._multicastMTU), - _multicastUDPPort(other._multicastUDPPort), + _multicastUDPPortBase(other._multicastUDPPortBase), _multicastUseUDP(other._multicastUseUDP) { } @@ -127,16 +127,26 @@ public: return this; } - bool multicastUDPPort() const + quint16 multicastUDPPortBase() const { - return _multicastUDPPort; + return _multicastUDPPortBase; } - McastConfiguration* multicastUDPPort(quint16 port) + McastConfiguration* multicastUDPPortBase(quint16 port) { - _multicastUDPPort = port; + _multicastUDPPortBase = port; return this; } + quint16 multicastUDPUPort() const + { + return _multicastUDPPortBase; + } + + quint16 multicastUDPMPort() const + { + return _multicastUDPPortBase + 1; + } + void commit() { emit changed(); @@ -152,7 +162,7 @@ private: quint16 _multicastDPort; quint16 _multicastWinSize; quint16 _multicastMTU; - quint16 _multicastUDPPort; + quint16 _multicastUDPPortBase; bool _multicastUseUDP; }; diff --git a/src/net/mcast/McastConstants.h b/src/net/mcast/McastConstants.h index 712a0d5..9913457 100644 --- a/src/net/mcast/McastConstants.h +++ b/src/net/mcast/McastConstants.h @@ -23,6 +23,7 @@ #define DEFAULT_MULTICAST_ADDRESS ";239.255.220.207" #define DEFAULT_MULTICAST_SPORT 6964 #define DEFAULT_MULTICAST_DPORT 6965 +#define DEFAULT_MULTICAST_USEUDP true #define DEFAULT_MULTICAST_UDPPORT 6966 #define DEFAULT_MULTICAST_RATE (100*1024) #define DEFAULT_MULTICAST_WSIZ 3000 diff --git a/src/net/mcast/McastPGMSocket.cpp b/src/net/mcast/McastPGMSocket.cpp index 0d6b694..41764d5 100644 --- a/src/net/mcast/McastPGMSocket.cpp +++ b/src/net/mcast/McastPGMSocket.cpp @@ -142,8 +142,15 @@ bool McastPGMSocket::open(McastConfiguration const* config, Direction direction) sa_family_t family = addrinfo->ai_send_addrs[0].gsr_group.ss_family; - good - = pgm_socket(&_priv->socket, family, SOCK_SEQPACKET, IPPROTO_PGM, &err); + if(config->multicastUseUDP()) + { + good = pgm_socket(&_priv->socket, family, SOCK_SEQPACKET, IPPROTO_UDP, &err); + } + else + { + good = pgm_socket(&_priv->socket, family, SOCK_SEQPACKET, IPPROTO_PGM, &err); + } + if (!good) { qCritical() << "Could not open socket: PGM Error: " << err->message; @@ -209,6 +216,16 @@ bool McastPGMSocket::open(McastConfiguration const* config, Direction direction) int const mtu = config->multicastMTU(); pgm_setsockopt(_priv->socket, PGM_MTU, &mtu, sizeof(mtu)); + // UDP Encapsulation + if(config->multicastUseUDP()) + { + const quint16 uport = config->multicastUDPUPort(); + const quint16 mport = config->multicastUDPMPort(); + + pgm_setsockopt(_priv->socket, PGM_UDP_ENCAP_MCAST_PORT, &mport, sizeof(mport)); + pgm_setsockopt(_priv->socket, PGM_UDP_ENCAP_UCAST_PORT, &uport, sizeof(uport)); + } + pgm_sockaddr_t addr; addr.sa_addr.sport = config->multicastSPort(); addr.sa_port = config->multicastDPort(); diff --git a/src/net/mcast/trial_programs/McastConfigArgParser.cpp b/src/net/mcast/trial_programs/McastConfigArgParser.cpp index 8849544..efea0b5 100644 --- a/src/net/mcast/trial_programs/McastConfigArgParser.cpp +++ b/src/net/mcast/trial_programs/McastConfigArgParser.cpp @@ -125,6 +125,10 @@ bool parseMcastConfigArg(QStringList::iterator& i, QStringList::iterator const& { config->multicastUseUDP(true); } + else if (arg == "-no-udp") + { + config->multicastUseUDP(false); + } else if (arg == "-udp-port") { i++; @@ -140,7 +144,7 @@ bool parseMcastConfigArg(QStringList::iterator& i, QStringList::iterator const& cerr << "Error: UDP-Port is not an integer" << endl; return false; } - config->multicastUDPPort(udpport); + config->multicastUDPPortBase(udpport); } else { -- cgit v1.2.3-55-g7522 From 4bedfeeaa534e5e59b7fad745c5be0d6b7d587df Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 14 Jul 2010 16:13:57 +0200 Subject: Make interface configurable. --- src/net/mcast/McastConfiguration.h | 13 +++++++++++++ src/net/mcast/McastConstants.h | 3 ++- src/net/mcast/McastPGMSocket.cpp | 2 +- src/net/mcast/trial_programs/McastConfigArgParser.cpp | 10 ++++++++++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/net/mcast/McastConfiguration.h b/src/net/mcast/McastConfiguration.h index c32ac4f..4e0e2ad 100644 --- a/src/net/mcast/McastConfiguration.h +++ b/src/net/mcast/McastConfiguration.h @@ -29,6 +29,7 @@ Q_OBJECT public: McastConfiguration(QObject* parent = 0) : QObject(parent), + _multicastInterface(DEFAULT_MULTICAST_INTERFACE), _multicastAddress(DEFAULT_MULTICAST_ADDRESS), _multicastRate(DEFAULT_MULTICAST_RATE), _multicastSPort(DEFAULT_MULTICAST_SPORT), @@ -41,6 +42,7 @@ public: } McastConfiguration(McastConfiguration const& other) : + _multicastInterface(other._multicastInterface), QObject(), _multicastAddress(other._multicastAddress), _multicastRate(other._multicastRate), @@ -137,6 +139,16 @@ public: return this; } + QString multicastInterface() const + { + return _multicastInterface; + } + McastConfiguration* multicastInterface(QString const& interface) + { + _multicastInterface = interface; + return this; + } + quint16 multicastUDPUPort() const { return _multicastUDPPortBase; @@ -156,6 +168,7 @@ signals: void changed(); private: + QString _multicastInterface; QString _multicastAddress; quint32 _multicastRate; quint16 _multicastSPort; diff --git a/src/net/mcast/McastConstants.h b/src/net/mcast/McastConstants.h index 9913457..b4c71a5 100644 --- a/src/net/mcast/McastConstants.h +++ b/src/net/mcast/McastConstants.h @@ -20,7 +20,8 @@ #include #define MCASTFT_MAGIC UINT64_C(0x6d60ad83825fb7f9) -#define DEFAULT_MULTICAST_ADDRESS ";239.255.220.207" +#define DEFAULT_MULTICAST_INTERFACE "" +#define DEFAULT_MULTICAST_ADDRESS "239.255.220.207" #define DEFAULT_MULTICAST_SPORT 6964 #define DEFAULT_MULTICAST_DPORT 6965 #define DEFAULT_MULTICAST_USEUDP true diff --git a/src/net/mcast/McastPGMSocket.cpp b/src/net/mcast/McastPGMSocket.cpp index 41764d5..ebf6ae7 100644 --- a/src/net/mcast/McastPGMSocket.cpp +++ b/src/net/mcast/McastPGMSocket.cpp @@ -132,7 +132,7 @@ bool McastPGMSocket::open(McastConfiguration const* config, Direction direction) pgm_addrinfo_t* addrinfo; // parse the address string - good = pgm_getaddrinfo(config->multicastAddress().toLatin1().constData(), + good = pgm_getaddrinfo((config->multicastInterface() + ";" + config->multicastAddress()).toLatin1().constData(), 0, &addrinfo, &err); if (!good) { diff --git a/src/net/mcast/trial_programs/McastConfigArgParser.cpp b/src/net/mcast/trial_programs/McastConfigArgParser.cpp index efea0b5..881f728 100644 --- a/src/net/mcast/trial_programs/McastConfigArgParser.cpp +++ b/src/net/mcast/trial_programs/McastConfigArgParser.cpp @@ -146,6 +146,16 @@ bool parseMcastConfigArg(QStringList::iterator& i, QStringList::iterator const& } config->multicastUDPPortBase(udpport); } + else if (arg == "-intf") + { + i++; + if (i == end) + { + cerr << "Option " << arg.toLatin1().constData() << "is missing argument" << endl; + return false; + } + config->multicastInterface(*i); + } else { return false; -- cgit v1.2.3-55-g7522 From 940efab52792bce03a9fc8734eed2d2d3649fc1b Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 14 Jul 2010 16:14:29 +0200 Subject: Change to shorter NAK intervals for performance. --- src/net/mcast/McastPGMSocket.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/net/mcast/McastPGMSocket.cpp b/src/net/mcast/McastPGMSocket.cpp index ebf6ae7..ba51444 100644 --- a/src/net/mcast/McastPGMSocket.cpp +++ b/src/net/mcast/McastPGMSocket.cpp @@ -194,9 +194,9 @@ bool McastPGMSocket::open(McastConfiguration const* config, Direction direction) max_winsqns = 0, peer_expiry = ambient_spm * 5, spmr_expiry = 250 * 1000, - nak_bo_ivl = 500 * 1000, - nak_rpt_ivl = 500 * 1000, - nak_rdata_ivl = 2000 * 1000, + nak_bo_ivl = 100 * 1000, + nak_rpt_ivl = 100 * 1000, + nak_rdata_ivl = 200 * 1000, nak_data_retries = 50, nak_ncf_retries = 50; pgm_setsockopt(_priv->socket, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); -- cgit v1.2.3-55-g7522 From 94b88e75b9ebeaf9abb2adef130fdf971884e7b4 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Thu, 15 Jul 2010 01:12:17 +0200 Subject: * Upgrade OpenPGM to current trunk * Implement wait-for-shutdown for McastPGMSocket * Work around bug in UDP encapsulation --- 3rdparty/CMakeLists.txt | 19 +- 3rdparty/libpgm-5.0.63alpha1.tar.bz2 | Bin 358887 -> 0 bytes .../doc/draft-ietf-rmt-bb-pgmcc-03.txt | 1226 ++++ 3rdparty/openpgm-svn-r1085/doc/rfc3208.txt | 6219 ++++++++++++++++++++ 3rdparty/openpgm-svn-r1085/pgm/COPYING | 504 ++ 3rdparty/openpgm-svn-r1085/pgm/INSTALL | 4 + 3rdparty/openpgm-svn-r1085/pgm/LICENSE | 19 + 3rdparty/openpgm-svn-r1085/pgm/README | 7 + 3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgm | 170 + 3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgm89 | 80 + 3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmex | 18 + .../openpgm-svn-r1085/pgm/SConscript.libpgmhttp | 53 + .../openpgm-svn-r1085/pgm/SConscript.libpgmsnmp | 35 + 3rdparty/openpgm-svn-r1085/pgm/SConstruct | 326 + 3rdparty/openpgm-svn-r1085/pgm/SConstruct.097 | 321 + .../openpgm-svn-r1085/pgm/SConstruct.097.intelc | 331 ++ .../openpgm-svn-r1085/pgm/SConstruct.097.mingw64 | 325 + .../openpgm-svn-r1085/pgm/SConstruct.097.sunstudio | 312 + 3rdparty/openpgm-svn-r1085/pgm/SConstruct.Debian4 | 281 + .../openpgm-svn-r1085/pgm/SConstruct.FreeBSD80 | 337 ++ .../openpgm-svn-r1085/pgm/SConstruct.OpenSolaris | 310 + 3rdparty/openpgm-svn-r1085/pgm/SConstruct.RHEL4 | 279 + .../openpgm-svn-r1085/pgm/SConstruct.Solaris.gcc64 | 324 + .../pgm/SConstruct.Solaris.sungcc | 321 + .../pgm/SConstruct.Solaris.sunstudio | 306 + 3rdparty/openpgm-svn-r1085/pgm/SConstruct.clang | 336 ++ 3rdparty/openpgm-svn-r1085/pgm/SConstruct.mingw | 330 ++ .../openpgm-svn-r1085/pgm/SConstruct.mingw-wine | 339 ++ 3rdparty/openpgm-svn-r1085/pgm/atomic_unittest.c | 166 + 3rdparty/openpgm-svn-r1085/pgm/backtrace.c | 69 + 3rdparty/openpgm-svn-r1085/pgm/checksum.c | 941 +++ 3rdparty/openpgm-svn-r1085/pgm/checksum_perftest.c | 807 +++ 3rdparty/openpgm-svn-r1085/pgm/checksum_unittest.c | 278 + 3rdparty/openpgm-svn-r1085/pgm/crossmingw.py | 144 + 3rdparty/openpgm-svn-r1085/pgm/crossmingw64.py | 140 + 3rdparty/openpgm-svn-r1085/pgm/engine.c | 277 + 3rdparty/openpgm-svn-r1085/pgm/engine_unittest.c | 232 + 3rdparty/openpgm-svn-r1085/pgm/error.c | 518 ++ 3rdparty/openpgm-svn-r1085/pgm/error_unittest.c | 292 + 3rdparty/openpgm-svn-r1085/pgm/examples/SConscript | 88 + .../openpgm-svn-r1085/pgm/examples/SConscript89 | 41 + 3rdparty/openpgm-svn-r1085/pgm/examples/async.c | 441 ++ 3rdparty/openpgm-svn-r1085/pgm/examples/async.h | 82 + .../openpgm-svn-r1085/pgm/examples/blocksyncrecv.c | 350 ++ 3rdparty/openpgm-svn-r1085/pgm/examples/daytime.c | 546 ++ .../pgm/examples/enonblocksyncrecv.c | 382 ++ .../pgm/examples/enonblocksyncrecvmsg.c | 382 ++ .../pgm/examples/enonblocksyncrecvmsgv.c | 397 ++ 3rdparty/openpgm-svn-r1085/pgm/examples/getopt.c | 110 + 3rdparty/openpgm-svn-r1085/pgm/examples/getopt.h | 62 + 3rdparty/openpgm-svn-r1085/pgm/examples/pgmdump.c | 279 + 3rdparty/openpgm-svn-r1085/pgm/examples/pgmping.cc | 1059 ++++ 3rdparty/openpgm-svn-r1085/pgm/examples/pgmrecv.c | 649 ++ 3rdparty/openpgm-svn-r1085/pgm/examples/pgmsend.c | 305 + 3rdparty/openpgm-svn-r1085/pgm/examples/pgmtop.c | 1031 ++++ 3rdparty/openpgm-svn-r1085/pgm/examples/ping.proto | 47 + .../pgm/examples/pnonblocksyncrecv.c | 385 ++ .../openpgm-svn-r1085/pgm/examples/purinrecv.c | 474 ++ .../openpgm-svn-r1085/pgm/examples/purinrecvcc.cc | 434 ++ .../openpgm-svn-r1085/pgm/examples/purinsend.c | 280 + .../openpgm-svn-r1085/pgm/examples/purinsendcc.cc | 269 + .../openpgm-svn-r1085/pgm/examples/shortcakerecv.c | 430 ++ .../pgm/examples/snonblocksyncrecv.c | 435 ++ 3rdparty/openpgm-svn-r1085/pgm/fec-block.txt | 66 + 3rdparty/openpgm-svn-r1085/pgm/fec.txt | 77 + 3rdparty/openpgm-svn-r1085/pgm/galois_generator.pl | 139 + 3rdparty/openpgm-svn-r1085/pgm/gcov-parse.pl | 41 + 3rdparty/openpgm-svn-r1085/pgm/gcov-seed.sh | 33 + 3rdparty/openpgm-svn-r1085/pgm/gcov.sh | 29 + 3rdparty/openpgm-svn-r1085/pgm/getifaddrs.c | 840 +++ .../openpgm-svn-r1085/pgm/getifaddrs_unittest.c | 262 + 3rdparty/openpgm-svn-r1085/pgm/getnodeaddr.c | 196 + .../openpgm-svn-r1085/pgm/getnodeaddr_unittest.c | 573 ++ 3rdparty/openpgm-svn-r1085/pgm/gsi.c | 227 + 3rdparty/openpgm-svn-r1085/pgm/gsi_unittest.c | 350 ++ 3rdparty/openpgm-svn-r1085/pgm/hashtable.c | 327 + 3rdparty/openpgm-svn-r1085/pgm/histogram.c | 414 ++ 3rdparty/openpgm-svn-r1085/pgm/htdocs/404.html | 11 + 3rdparty/openpgm-svn-r1085/pgm/htdocs/base.css | 136 + .../pgm/htdocs/convert_to_macro.pl | 20 + 3rdparty/openpgm-svn-r1085/pgm/htdocs/robots.txt | 2 + .../pgm/htdocs/xhtml10_strict.doctype | 3 + 3rdparty/openpgm-svn-r1085/pgm/http.c | 1718 ++++++ 3rdparty/openpgm-svn-r1085/pgm/http_unittest.c | 186 + 3rdparty/openpgm-svn-r1085/pgm/if.c | 1595 +++++ 3rdparty/openpgm-svn-r1085/pgm/if_unittest.c | 1495 +++++ .../openpgm-svn-r1085/pgm/include/impl/checksum.h | 75 + .../openpgm-svn-r1085/pgm/include/impl/engine.h | 43 + .../openpgm-svn-r1085/pgm/include/impl/features.h | 42 + .../openpgm-svn-r1085/pgm/include/impl/fixed.h | 140 + .../openpgm-svn-r1085/pgm/include/impl/framework.h | 76 + .../openpgm-svn-r1085/pgm/include/impl/galois.h | 138 + .../pgm/include/impl/getifaddrs.h | 73 + .../pgm/include/impl/getnodeaddr.h | 41 + .../openpgm-svn-r1085/pgm/include/impl/hashtable.h | 58 + .../openpgm-svn-r1085/pgm/include/impl/histogram.h | 129 + 3rdparty/openpgm-svn-r1085/pgm/include/impl/i18n.h | 32 + .../pgm/include/impl/indextoaddr.h | 41 + .../pgm/include/impl/indextoname.h | 37 + .../pgm/include/impl/inet_network.h | 41 + 3rdparty/openpgm-svn-r1085/pgm/include/impl/ip.h | 150 + 3rdparty/openpgm-svn-r1085/pgm/include/impl/list.h | 43 + 3rdparty/openpgm-svn-r1085/pgm/include/impl/math.h | 75 + 3rdparty/openpgm-svn-r1085/pgm/include/impl/md5.h | 61 + 3rdparty/openpgm-svn-r1085/pgm/include/impl/mem.h | 34 + .../openpgm-svn-r1085/pgm/include/impl/messages.h | 352 ++ .../pgm/include/impl/nametoindex.h | 40 + 3rdparty/openpgm-svn-r1085/pgm/include/impl/net.h | 38 + .../openpgm-svn-r1085/pgm/include/impl/notify.h | 298 + .../pgm/include/impl/packet_parse.h | 45 + .../pgm/include/impl/packet_test.h | 40 + .../openpgm-svn-r1085/pgm/include/impl/pgmMIB.h | 28 + .../pgm/include/impl/pgmMIB_columns.h | 372 ++ .../pgm/include/impl/pgmMIB_enums.h | 64 + .../openpgm-svn-r1085/pgm/include/impl/processor.h | 61 + .../openpgm-svn-r1085/pgm/include/impl/queue.h | 51 + 3rdparty/openpgm-svn-r1085/pgm/include/impl/rand.h | 50 + .../pgm/include/impl/rate_control.h | 54 + .../openpgm-svn-r1085/pgm/include/impl/receiver.h | 142 + .../pgm/include/impl/reed_solomon.h | 51 + 3rdparty/openpgm-svn-r1085/pgm/include/impl/rxw.h | 220 + .../openpgm-svn-r1085/pgm/include/impl/slist.h | 52 + 3rdparty/openpgm-svn-r1085/pgm/include/impl/sn.h | 186 + .../openpgm-svn-r1085/pgm/include/impl/sockaddr.h | 105 + .../openpgm-svn-r1085/pgm/include/impl/socket.h | 182 + .../openpgm-svn-r1085/pgm/include/impl/source.h | 75 + .../openpgm-svn-r1085/pgm/include/impl/sqn_list.h | 38 + .../openpgm-svn-r1085/pgm/include/impl/string.h | 59 + .../openpgm-svn-r1085/pgm/include/impl/thread.h | 210 + 3rdparty/openpgm-svn-r1085/pgm/include/impl/time.h | 51 + .../openpgm-svn-r1085/pgm/include/impl/timer.h | 58 + 3rdparty/openpgm-svn-r1085/pgm/include/impl/tsi.h | 39 + 3rdparty/openpgm-svn-r1085/pgm/include/impl/txw.h | 204 + .../pgm/include/impl/wsastrerror.h | 37 + .../openpgm-svn-r1085/pgm/include/pgm/atomic.h | 140 + .../openpgm-svn-r1085/pgm/include/pgm/backtrace.h | 33 + .../openpgm-svn-r1085/pgm/include/pgm/engine.h | 37 + 3rdparty/openpgm-svn-r1085/pgm/include/pgm/error.h | 109 + 3rdparty/openpgm-svn-r1085/pgm/include/pgm/gsi.h | 49 + 3rdparty/openpgm-svn-r1085/pgm/include/pgm/http.h | 37 + 3rdparty/openpgm-svn-r1085/pgm/include/pgm/if.h | 33 + .../openpgm-svn-r1085/pgm/include/pgm/ip/pgm.hh | 100 + .../pgm/include/pgm/ip/pgm_endpoint.hh | 171 + 3rdparty/openpgm-svn-r1085/pgm/include/pgm/list.h | 40 + 3rdparty/openpgm-svn-r1085/pgm/include/pgm/log.h | 33 + .../openpgm-svn-r1085/pgm/include/pgm/macros.h | 171 + 3rdparty/openpgm-svn-r1085/pgm/include/pgm/mem.h | 60 + .../openpgm-svn-r1085/pgm/include/pgm/messages.h | 66 + 3rdparty/openpgm-svn-r1085/pgm/include/pgm/msgv.h | 54 + .../openpgm-svn-r1085/pgm/include/pgm/packet.h | 472 ++ 3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm.h | 42 + 3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm.hh | 33 + .../pgm/include/pgm/pgm_socket.hh | 157 + .../openpgm-svn-r1085/pgm/include/pgm/signal.h | 36 + .../openpgm-svn-r1085/pgm/include/pgm/skbuff.h | 243 + 3rdparty/openpgm-svn-r1085/pgm/include/pgm/snmp.h | 35 + .../openpgm-svn-r1085/pgm/include/pgm/socket.h | 170 + 3rdparty/openpgm-svn-r1085/pgm/include/pgm/time.h | 54 + 3rdparty/openpgm-svn-r1085/pgm/include/pgm/tsi.h | 47 + 3rdparty/openpgm-svn-r1085/pgm/include/pgm/types.h | 56 + .../openpgm-svn-r1085/pgm/include/pgm/version.h | 41 + .../openpgm-svn-r1085/pgm/include/pgm/winint.h | 198 + .../pgm/include/pgm/wininttypes.h | 254 + 3rdparty/openpgm-svn-r1085/pgm/indextoaddr.c | 98 + .../openpgm-svn-r1085/pgm/indextoaddr_unittest.c | 302 + 3rdparty/openpgm-svn-r1085/pgm/indextoname.c | 52 + 3rdparty/openpgm-svn-r1085/pgm/inet_network.c | 237 + .../openpgm-svn-r1085/pgm/inet_network_unittest.c | 203 + 3rdparty/openpgm-svn-r1085/pgm/ip_unittest.c | 362 ++ 3rdparty/openpgm-svn-r1085/pgm/list.c | 146 + 3rdparty/openpgm-svn-r1085/pgm/log.c | 151 + 3rdparty/openpgm-svn-r1085/pgm/math.c | 75 + 3rdparty/openpgm-svn-r1085/pgm/md5.c | 368 ++ 3rdparty/openpgm-svn-r1085/pgm/md5_unittest.c | 189 + 3rdparty/openpgm-svn-r1085/pgm/mem.c | 249 + 3rdparty/openpgm-svn-r1085/pgm/memcheck | 13 + 3rdparty/openpgm-svn-r1085/pgm/messages.c | 173 + .../pgm/mibs/PGM-MIB-petrova-01.txt | 5459 +++++++++++++++++ 3rdparty/openpgm-svn-r1085/pgm/mld-semantics.txt | 52 + 3rdparty/openpgm-svn-r1085/pgm/msfec.txt | 33 + 3rdparty/openpgm-svn-r1085/pgm/nametoindex.c | 249 + 3rdparty/openpgm-svn-r1085/pgm/net-snmp.txt | 34 + 3rdparty/openpgm-svn-r1085/pgm/net.c | 175 + 3rdparty/openpgm-svn-r1085/pgm/net_unittest.c | 375 ++ 3rdparty/openpgm-svn-r1085/pgm/options.txt | 158 + 3rdparty/openpgm-svn-r1085/pgm/packet_parse.c | 615 ++ .../openpgm-svn-r1085/pgm/packet_parse_unittest.c | 382 ++ 3rdparty/openpgm-svn-r1085/pgm/packet_test.c | 1158 ++++ .../openpgm-svn-r1085/pgm/packet_test_unittest.c | 169 + 3rdparty/openpgm-svn-r1085/pgm/pgmMIB.c | 3212 ++++++++++ 3rdparty/openpgm-svn-r1085/pgm/pgmMIB_unittest.c | 257 + 3rdparty/openpgm-svn-r1085/pgm/plan.txt | 238 + 3rdparty/openpgm-svn-r1085/pgm/queue.c | 110 + 3rdparty/openpgm-svn-r1085/pgm/rand.c | 137 + 3rdparty/openpgm-svn-r1085/pgm/rate_control.c | 158 + .../openpgm-svn-r1085/pgm/rate_control_unittest.c | 241 + 3rdparty/openpgm-svn-r1085/pgm/receiver.c | 2268 +++++++ 3rdparty/openpgm-svn-r1085/pgm/receiver_unittest.c | 857 +++ 3rdparty/openpgm-svn-r1085/pgm/recv.c | 1059 ++++ 3rdparty/openpgm-svn-r1085/pgm/recv_unittest.c | 1600 +++++ 3rdparty/openpgm-svn-r1085/pgm/reed_solomon.c | 576 ++ .../openpgm-svn-r1085/pgm/reed_solomon_unittest.c | 305 + 3rdparty/openpgm-svn-r1085/pgm/rxw.c | 2229 +++++++ 3rdparty/openpgm-svn-r1085/pgm/rxw_unittest.c | 1844 ++++++ 3rdparty/openpgm-svn-r1085/pgm/signal.c | 176 + 3rdparty/openpgm-svn-r1085/pgm/signal_unittest.c | 115 + 3rdparty/openpgm-svn-r1085/pgm/skbuff.c | 115 + 3rdparty/openpgm-svn-r1085/pgm/slist.c | 166 + 3rdparty/openpgm-svn-r1085/pgm/snmp.c | 222 + 3rdparty/openpgm-svn-r1085/pgm/snmp_unittest.c | 184 + 3rdparty/openpgm-svn-r1085/pgm/sockaddr.c | 1193 ++++ 3rdparty/openpgm-svn-r1085/pgm/socket.c | 2046 +++++++ 3rdparty/openpgm-svn-r1085/pgm/socket_unittest.c | 1186 ++++ 3rdparty/openpgm-svn-r1085/pgm/source.c | 2339 ++++++++ 3rdparty/openpgm-svn-r1085/pgm/source_unittest.c | 1216 ++++ 3rdparty/openpgm-svn-r1085/pgm/string.c | 486 ++ 3rdparty/openpgm-svn-r1085/pgm/test/PGM/Test.pm | 394 ++ 3rdparty/openpgm-svn-r1085/pgm/test/SConscript | 15 + 3rdparty/openpgm-svn-r1085/pgm/test/ambient_spm.pl | 86 + 3rdparty/openpgm-svn-r1085/pgm/test/apdu.pl | 56 + 3rdparty/openpgm-svn-r1085/pgm/test/apdu_parity.pl | 59 + 3rdparty/openpgm-svn-r1085/pgm/test/app.c | 904 +++ 3rdparty/openpgm-svn-r1085/pgm/test/async.c | 572 ++ 3rdparty/openpgm-svn-r1085/pgm/test/async.h | 76 + 3rdparty/openpgm-svn-r1085/pgm/test/dump-json.c | 1292 ++++ 3rdparty/openpgm-svn-r1085/pgm/test/dump-json.h | 33 + .../openpgm-svn-r1085/pgm/test/heartbeat_spm.pl | 57 + 3rdparty/openpgm-svn-r1085/pgm/test/monitor.c | 349 ++ 3rdparty/openpgm-svn-r1085/pgm/test/nak.pl | 66 + .../openpgm-svn-r1085/pgm/test/nak_cancellation.pl | 161 + 3rdparty/openpgm-svn-r1085/pgm/test/nak_list.pl | 70 + 3rdparty/openpgm-svn-r1085/pgm/test/nak_parity.pl | 75 + 3rdparty/openpgm-svn-r1085/pgm/test/nak_repeat.pl | 69 + 3rdparty/openpgm-svn-r1085/pgm/test/ncf.pl | 66 + .../openpgm-svn-r1085/pgm/test/ncf_cancellation.pl | 147 + 3rdparty/openpgm-svn-r1085/pgm/test/ncf_list.pl | 72 + .../openpgm-svn-r1085/pgm/test/ncf_suppression.pl | 101 + 3rdparty/openpgm-svn-r1085/pgm/test/odata.pl | 44 + .../openpgm-svn-r1085/pgm/test/odata_completion.pl | 87 + 3rdparty/openpgm-svn-r1085/pgm/test/odata_jump.pl | 71 + .../pgm/test/odata_jump_parity.pl | 83 + .../openpgm-svn-r1085/pgm/test/odata_number.pl | 59 + 3rdparty/openpgm-svn-r1085/pgm/test/odata_rate.pl | 68 + .../openpgm-svn-r1085/pgm/test/odata_reception.pl | 59 + .../openpgm-svn-r1085/pgm/test/on-demand_spm.pl | 49 + .../openpgm-svn-r1085/pgm/test/outofwindow_ncf.pl | 103 + .../openpgm-svn-r1085/pgm/test/rdata_completion.pl | 87 + .../pgm/test/rdata_completion_parity.pl | 97 + .../pgm/test/rdata_completion_parity_var_pktlen.pl | 97 + 3rdparty/openpgm-svn-r1085/pgm/test/rdata_jump.pl | 71 + .../openpgm-svn-r1085/pgm/test/rdata_reception.pl | 59 + 3rdparty/openpgm-svn-r1085/pgm/test/sim.c | 1924 ++++++ 3rdparty/openpgm-svn-r1085/pgm/test/spm.pl | 42 + 3rdparty/openpgm-svn-r1085/pgm/test/spm_jump.pl | 60 + 3rdparty/openpgm-svn-r1085/pgm/test/spm_jump2.pl | 59 + .../openpgm-svn-r1085/pgm/test/spm_reception.pl | 58 + 3rdparty/openpgm-svn-r1085/pgm/test/spmr.pl | 73 + .../openpgm-svn-r1085/pgm/test/spmr_after_spm.pl | 78 + .../openpgm-svn-r1085/pgm/test/spmr_from_odata.pl | 53 + .../openpgm-svn-r1085/pgm/test/spmr_suppression.pl | 58 + .../openpgm-svn-r1085/pgm/test/sudoers.example | 26 + 3rdparty/openpgm-svn-r1085/pgm/test/test.conf.pl | 27 + 3rdparty/openpgm-svn-r1085/pgm/thread.c | 457 ++ 3rdparty/openpgm-svn-r1085/pgm/time.c | 770 +++ 3rdparty/openpgm-svn-r1085/pgm/time_unittest.c | 188 + 3rdparty/openpgm-svn-r1085/pgm/timer.c | 223 + 3rdparty/openpgm-svn-r1085/pgm/timer_unittest.c | 355 ++ .../pgm/token and leaky bucket.txt | 12 + 3rdparty/openpgm-svn-r1085/pgm/tsi.c | 119 + 3rdparty/openpgm-svn-r1085/pgm/tsi_unittest.c | 185 + 3rdparty/openpgm-svn-r1085/pgm/txw.c | 763 +++ 3rdparty/openpgm-svn-r1085/pgm/txw_unittest.c | 743 +++ 3rdparty/openpgm-svn-r1085/pgm/valgrind.supp | 147 + .../openpgm-svn-r1085/pgm/version_generator.py | 52 + .../pgm/win/mingw32-runtime_3.13-1openpgm3.diff | 136 + .../pgm/win/mingw32-runtime_3.15.2-0openpgm1.diff | 135 + ...mingw-w64-bin_x86-64-linux_4.4.1-1openpgm1.diff | 53 + 3rdparty/openpgm-svn-r1085/pgm/wsastrerror.c | 372 ++ OpenPGMConfig.cmake | 10 + src/net/mcast/CMakeLists.txt | 4 - src/net/mcast/McastConfiguration.h | 2 +- src/net/mcast/McastConstants.h | 3 +- src/net/mcast/McastPGMSocket.cpp | 323 +- src/net/mcast/McastPGMSocket.h | 5 + src/net/mcast/McastSender.cpp | 44 +- src/net/mcast/McastSender.h | 1 + src/net/mcast/trial_programs/mcastsend.cpp | 3 +- 287 files changed, 91322 insertions(+), 203 deletions(-) delete mode 100644 3rdparty/libpgm-5.0.63alpha1.tar.bz2 create mode 100644 3rdparty/openpgm-svn-r1085/doc/draft-ietf-rmt-bb-pgmcc-03.txt create mode 100644 3rdparty/openpgm-svn-r1085/doc/rfc3208.txt create mode 100644 3rdparty/openpgm-svn-r1085/pgm/COPYING create mode 100644 3rdparty/openpgm-svn-r1085/pgm/INSTALL create mode 100644 3rdparty/openpgm-svn-r1085/pgm/LICENSE create mode 100644 3rdparty/openpgm-svn-r1085/pgm/README create mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgm create mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgm89 create mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmex create mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmhttp create mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmsnmp create mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct create mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.097 create mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.intelc create mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.mingw64 create mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.sunstudio create mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.Debian4 create mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.FreeBSD80 create mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.OpenSolaris create mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.RHEL4 create mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.gcc64 create mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.sungcc create mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.sunstudio create mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.clang create mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.mingw create mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.mingw-wine create mode 100644 3rdparty/openpgm-svn-r1085/pgm/atomic_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/backtrace.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/checksum.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/checksum_perftest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/checksum_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/crossmingw.py create mode 100644 3rdparty/openpgm-svn-r1085/pgm/crossmingw64.py create mode 100644 3rdparty/openpgm-svn-r1085/pgm/engine.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/engine_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/error.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/error_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/SConscript create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/SConscript89 create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/async.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/async.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/blocksyncrecv.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/daytime.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecv.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecvmsg.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecvmsgv.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/getopt.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/getopt.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/pgmdump.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/pgmping.cc create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/pgmrecv.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/pgmsend.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/pgmtop.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/ping.proto create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/pnonblocksyncrecv.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/purinrecv.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/purinrecvcc.cc create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/purinsend.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/purinsendcc.cc create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/shortcakerecv.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/snonblocksyncrecv.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/fec-block.txt create mode 100644 3rdparty/openpgm-svn-r1085/pgm/fec.txt create mode 100755 3rdparty/openpgm-svn-r1085/pgm/galois_generator.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/gcov-parse.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/gcov-seed.sh create mode 100755 3rdparty/openpgm-svn-r1085/pgm/gcov.sh create mode 100644 3rdparty/openpgm-svn-r1085/pgm/getifaddrs.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/getifaddrs_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/getnodeaddr.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/getnodeaddr_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/gsi.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/gsi_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/hashtable.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/histogram.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/htdocs/404.html create mode 100644 3rdparty/openpgm-svn-r1085/pgm/htdocs/base.css create mode 100755 3rdparty/openpgm-svn-r1085/pgm/htdocs/convert_to_macro.pl create mode 100644 3rdparty/openpgm-svn-r1085/pgm/htdocs/robots.txt create mode 100644 3rdparty/openpgm-svn-r1085/pgm/htdocs/xhtml10_strict.doctype create mode 100644 3rdparty/openpgm-svn-r1085/pgm/http.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/http_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/if.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/if_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/checksum.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/engine.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/features.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/fixed.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/framework.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/galois.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/getifaddrs.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/getnodeaddr.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/hashtable.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/histogram.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/i18n.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/indextoaddr.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/indextoname.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/inet_network.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/ip.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/list.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/math.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/md5.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/mem.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/messages.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/nametoindex.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/net.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/notify.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/packet_parse.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/packet_test.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB_columns.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB_enums.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/processor.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/queue.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/rand.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/rate_control.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/receiver.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/reed_solomon.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/rxw.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/slist.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/sn.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/sockaddr.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/socket.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/source.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/sqn_list.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/string.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/thread.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/time.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/timer.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/tsi.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/txw.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/wsastrerror.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/atomic.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/backtrace.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/engine.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/error.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/gsi.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/http.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/if.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/ip/pgm.hh create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/ip/pgm_endpoint.hh create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/list.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/log.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/macros.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/mem.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/messages.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/msgv.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/packet.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm.hh create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm_socket.hh create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/signal.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/skbuff.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/snmp.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/socket.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/time.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/tsi.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/types.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/version.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/winint.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/wininttypes.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/indextoaddr.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/indextoaddr_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/indextoname.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/inet_network.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/inet_network_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/ip_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/list.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/log.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/math.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/md5.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/md5_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/mem.c create mode 100755 3rdparty/openpgm-svn-r1085/pgm/memcheck create mode 100644 3rdparty/openpgm-svn-r1085/pgm/messages.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/mibs/PGM-MIB-petrova-01.txt create mode 100644 3rdparty/openpgm-svn-r1085/pgm/mld-semantics.txt create mode 100644 3rdparty/openpgm-svn-r1085/pgm/msfec.txt create mode 100644 3rdparty/openpgm-svn-r1085/pgm/nametoindex.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/net-snmp.txt create mode 100644 3rdparty/openpgm-svn-r1085/pgm/net.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/net_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/options.txt create mode 100644 3rdparty/openpgm-svn-r1085/pgm/packet_parse.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/packet_parse_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/packet_test.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/packet_test_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/pgmMIB.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/pgmMIB_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/plan.txt create mode 100644 3rdparty/openpgm-svn-r1085/pgm/queue.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/rand.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/rate_control.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/rate_control_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/receiver.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/receiver_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/recv.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/recv_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/reed_solomon.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/reed_solomon_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/rxw.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/rxw_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/signal.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/signal_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/skbuff.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/slist.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/snmp.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/snmp_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/sockaddr.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/socket.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/socket_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/source.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/source_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/string.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/test/PGM/Test.pm create mode 100644 3rdparty/openpgm-svn-r1085/pgm/test/SConscript create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/ambient_spm.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/apdu.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/apdu_parity.pl create mode 100644 3rdparty/openpgm-svn-r1085/pgm/test/app.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/test/async.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/test/async.h create mode 100644 3rdparty/openpgm-svn-r1085/pgm/test/dump-json.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/test/dump-json.h create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/heartbeat_spm.pl create mode 100644 3rdparty/openpgm-svn-r1085/pgm/test/monitor.c create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/nak.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/nak_cancellation.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/nak_list.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/nak_parity.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/nak_repeat.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/ncf.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/ncf_cancellation.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/ncf_list.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/ncf_suppression.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/odata.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/odata_completion.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/odata_jump.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/odata_jump_parity.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/odata_number.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/odata_rate.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/odata_reception.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/on-demand_spm.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/outofwindow_ncf.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion_parity.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion_parity_var_pktlen.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/rdata_jump.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/rdata_reception.pl create mode 100644 3rdparty/openpgm-svn-r1085/pgm/test/sim.c create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/spm.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/spm_jump.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/spm_jump2.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/spm_reception.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/spmr.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/spmr_after_spm.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/spmr_from_odata.pl create mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/spmr_suppression.pl create mode 100644 3rdparty/openpgm-svn-r1085/pgm/test/sudoers.example create mode 100644 3rdparty/openpgm-svn-r1085/pgm/test/test.conf.pl create mode 100644 3rdparty/openpgm-svn-r1085/pgm/thread.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/time.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/time_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/timer.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/timer_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/token and leaky bucket.txt create mode 100644 3rdparty/openpgm-svn-r1085/pgm/tsi.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/tsi_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/txw.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/txw_unittest.c create mode 100644 3rdparty/openpgm-svn-r1085/pgm/valgrind.supp create mode 100755 3rdparty/openpgm-svn-r1085/pgm/version_generator.py create mode 100644 3rdparty/openpgm-svn-r1085/pgm/win/mingw32-runtime_3.13-1openpgm3.diff create mode 100644 3rdparty/openpgm-svn-r1085/pgm/win/mingw32-runtime_3.15.2-0openpgm1.diff create mode 100644 3rdparty/openpgm-svn-r1085/pgm/win64/mingw-w64-bin_x86-64-linux_4.4.1-1openpgm1.diff create mode 100644 3rdparty/openpgm-svn-r1085/pgm/wsastrerror.c diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 8a4cea0..e7676b6 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -6,14 +6,9 @@ INCLUDE(../OpenPGMConfig.cmake) ADD_DEFINITIONS(${LIBPGM_CFLAGS}) -# Set up build -SET(pgm_VERSION - 5.0.63alpha1 -) - # OpenPGM will be built in the binary tree SET(pgm - ${CMAKE_CURRENT_BINARY_DIR}/libpgm-src/openpgm/pgm + "${CMAKE_CURRENT_SOURCE_DIR}/openpgm-${pgm_VERSION}/pgm" ) # This has been adapted from SConscript.libpgm @@ -106,6 +101,7 @@ ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version.c DEPENDS ${pgm}/version_generator.py ) +IF(comment_this_out_we_dont_need_it) # ... and we need to unpack the tree. ADD_CUSTOM_COMMAND( OUTPUT @@ -115,9 +111,11 @@ ADD_CUSTOM_COMMAND( ${pgm}/version_generator.py DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/libpgm-${pgm_VERSION}.tar.bz2" + "${CMAKE_CURRENT_SOURCE_DIR}/01-libpgm-fix-switch-fallthrough.patch" COMMAND rm -rf "${CMAKE_CURRENT_BINARY_DIR}/libpgm-src" COMMAND bzip2 -dc "${CMAKE_CURRENT_SOURCE_DIR}/libpgm-${pgm_VERSION}.tar.bz2" | tar -C "${CMAKE_CURRENT_BINARY_DIR}" -x - COMMAND mv "${CMAKE_CURRENT_BINARY_DIR}/libpgm-${pgm_VERSION}" "${CMAKE_CURRENT_BINARY_DIR}/libpgm-src" + COMMAND mv "${CMAKE_CURRENT_BINARY_DIR}/libpgm-${pgm_VERSION}" "${CMAKE_CURRENT_BINARY_DIR}/libpgm-src" + COMMAND cd ${pgm} && patch -p1 < "${CMAKE_CURRENT_SOURCE_DIR}/01-libpgm-fix-switch-fallthrough.patch" ) ADD_CUSTOM_COMMAND(TARGET clean @@ -128,11 +126,7 @@ ADD_CUSTOM_TARGET( unpack_libpgm DEPENDS ${pgm_SRCS} ${pgm_HDRS} ) - -INCLUDE_DIRECTORIES( - ${pgm}/include - ${GLIB_INCLUDES_DIRS} -) +ENDIF(comment_this_out_we_dont_need_it) ADD_LIBRARY( pgm @@ -153,4 +147,3 @@ LINK_DIRECTORIES( ADD_DEFINITIONS( ${GLIB_CFLAGS} ) - diff --git a/3rdparty/libpgm-5.0.63alpha1.tar.bz2 b/3rdparty/libpgm-5.0.63alpha1.tar.bz2 deleted file mode 100644 index ab4903c..0000000 Binary files a/3rdparty/libpgm-5.0.63alpha1.tar.bz2 and /dev/null differ diff --git a/3rdparty/openpgm-svn-r1085/doc/draft-ietf-rmt-bb-pgmcc-03.txt b/3rdparty/openpgm-svn-r1085/doc/draft-ietf-rmt-bb-pgmcc-03.txt new file mode 100644 index 0000000..6f1869c --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/doc/draft-ietf-rmt-bb-pgmcc-03.txt @@ -0,0 +1,1226 @@ + +Internet Engineering Task Force RMT WG +INTERNET-DRAFT Luigi Rizzo/U. Pisa +draft-ietf-rmt-bb-pgmcc-03.txt Gianluca Iannaccone/Intel + Lorenzo Vicisano/Cisco + Mark Handley/UCL + 12 July 2004 + Expires: January 2005 + + + PGMCC single rate multicast congestion control: + Protocol Specification + + + +Status of this Document + +This document is an Internet-Draft and is in full conformance with all +provisions of Section 10 of RFC2026. + +Internet-Drafts are working documents of the Internet Engineering Task +Force (IETF), its areas, and its working groups. Note that other groups +may also distribute working documents as Internet-Drafts. + +Internet-Drafts are valid for a maximum of six months and may be +updated, replaced, or obsoleted by other documents at any time. It is +inappropriate to use Internet-Drafts as reference material or to cite +them other than as a "work in progress". + +The list of current Internet-Drafts can be accessed at +http://www.ietf.org/ietf/1id-abstracts.txt + +To view the list Internet-Draft Shadow Directories, see +http://www.ietf.org/shadow.html. + +This document is a product of the IETF RMT WG. Comments should be +addressed to the authors, or the WG's mailing list at rmt@lbl.gov. + + + Abstract + + + This document describes PGMCC, a single rate multicast + congestion control scheme which is TCP-friendly and achieves + scalability, stability and fast response to variations in + network conditions. PGMCC is suitable for both non-reliable + + + +Rizzo/Iannaccone/Vicisano/Handley [Page 1] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + + and reliable data transfers. It is mainly designed for NAK- + based multicast protocols, and uses a window-based, TCP-like + control loop using positive ACKs between one representative of + the receiver group (the ACKER) and the sender. The ACKER is + selected dynamically and may change over time. + + PGMCC is made of two components: a window-based control loop, + which closely mimics TCP behavior, and a fast and low-overhead + procedure to select (and track changes of) the ACKER. The + scheme is robust to measurement errors, and supports fast + response to changes in the receiver set and/or network + conditions. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Rizzo/Iannaccone/Vicisano/Handley [Page 2] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + + Table of Contents + + + 1. Introduction. . . . . . . . . . . . . . . . . . . . . . 4 + 1.1. Terminology. . . . . . . . . . . . . . . . . . . . . 4 + 2. Protocol Overview . . . . . . . . . . . . . . . . . . . 4 + 2.1. Packet Contents. . . . . . . . . . . . . . . . . . . 6 + 2.1.1. Data Packets. . . . . . . . . . . . . . . . . . . 6 + 2.1.2. Feedback Packets. . . . . . . . . . . . . . . . . 7 + 2.1.3. Field sizes and formats . . . . . . . . . . . . . 8 + 2.2. Window-based controller. . . . . . . . . . . . . . . 9 + 2.3. Acker Selection. . . . . . . . . . . . . . . . . . . 11 + 2.3.1. Initial Acker election. . . . . . . . . . . . . . 11 + 2.3.2. Acker dropouts. . . . . . . . . . . . . . . . . . 12 + 2.4. TCP Throughput Equation. . . . . . . . . . . . . . . 12 + 2.5. RTT measurement. . . . . . . . . . . . . . . . . . . 13 + 2.5.1. Explicit Timestamp. . . . . . . . . . . . . . . . 13 + 2.5.2. Implicit timestamp. . . . . . . . . . . . . . . . 13 + 2.5.3. Sequence numbers. . . . . . . . . . . . . . . . . 14 + 2.5.4. Recommendations . . . . . . . . . . . . . . . . . 15 + 2.6. Loss rate measurement. . . . . . . . . . . . . . . . 15 + 2.7. Timeouts . . . . . . . . . . . . . . . . . . . . . . 16 + 2.8. Interaction with feedback suppression + schemes . . . . . . . . . . . . . . . . . . . . . . . . . 16 + 2.9. Interaction with ECN . . . . . . . . . . . . . . . . 17 + 3. Procedures - Sender . . . . . . . . . . . . . . . . . . 17 + 4. Procedures -- Receiver. . . . . . . . . . . . . . . . . 18 + 5. Security Considerations . . . . . . . . . . . . . . . . 19 + 6. Authors' Addresses. . . . . . . . . . . . . . . . . . . 20 + 7. Acknowledgments . . . . . . . . . . . . . . . . . . . . 20 + 8. Full Copyright Statement. . . . . . . . . . . . . . . . 21 + + + + + + + + + + + + + + + + + + + + +Rizzo/Iannaccone/Vicisano/Handley [Page 3] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +1. Introduction + +This document describes PGMCC, a single rate multicast congestion +control scheme which is TCP-friendly and achieves scalability, stability +and fast response to variations in network conditions. + +PGMCC is designed for multicast sessions with one sender and one or more +receivers, and is a good match for transport protocols using negative +acknowledgements (NAKs) to collect feedback from the receivers. The +congestion control scheme implemented by PGMCC closely mimics the +congestion-control behavior of TCP, as it uses a window-based control +loop which is run between the sender and a selected receiver called the +ACKER. The role of the ACKER is to provide timely feedback in the same +way as a TCP receiver; additionally, the ACKER is selected dynamically +among the receivers as the one which would experience the lowest +throughput if separate TCP sessions were run between the sender and each +of the receivers. + +Scalability in PGMCC comes from the use of negative acknowledgements +(NAKs) for collecting feedback from receivers other than the ACKER. As +a consequence, the usual techniques for NAK suppression and aggregation +can be used to reduce the amount of feedback to the source and improve +the scalability of the scheme. + +PGMCC is designed to completely decouple congestion control from data +integrity. As a consequence, the scheme can work with both reliable data +transfer and unreliable communication protocols such as those used for +video or audio streaming. + +While designed with multicast in mind, PGMCC can be equally used as a +replacement for TCP for unicast sessions which require a lower degree of +reliability than what TCP offers. + + +1.1. Terminology + +In this document, the key words "MUST", "MUST NOT", "REQUIRED", "SHALL", +"SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" are to be interpreted as described in RFC 2119 and indicate +requirement levels for compliant PGMCC implementations. + + +2. Protocol Overview + +PGMCC is based on two separate but complementary mechanisms: + + o A window-based control loop which closely emulates TCP congestion + control. + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2. [Page 4] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + + The window-based control loop is simply an adaptation of the TCP + congestion control scheme to transport protocols where missing + (because of network errors or congestion) data packets are not + necessarily retransmitted, and so the congestion control scheme + cannot rely on cumulative acknowledgements. In PGMCC, the + ``congestion window'' is simulated using a token-based scheme which + permits congestion control to be decoupled from retransmission + state. One of the receivers in the group operates as the ACKER, i.e. + the node in charge of sending positive acknowledgements back to the + source and thus controlling the rate of the transfer. + + + o A procedure to select the ACKER. + The purpose of this procedure is to make sure that, in presence of + multiple receivers, the ACKER is dynamically selected to be the + receiver which would have the lowest throughput if separate TCP + sessions were run between the sender and each receiver. + For the acker selection mechanism, PGMCC uses a throughput equation + to determine the expected throughput for a given receiver as a + function of the loss rate and round-trip time. Unlike other schemes + [2], the TCP throughput equation is not used to determine the actual + sending rate, which is completely controlled by the window-based + control loop. + + +In principle, PGMCC's congestion control mechanism works as follows: + + + o Receivers measure the loss rate and feed this information back to + the sender, either in ACK or NAK messages. + + + o The sender also uses these feedback messages to measure the round- + trip time (RTT) to each receiver. + + + o The loss rate and RTT are then fed into PGMCC's throughput equation, + to determine the expected throughput to that receiver. + + + o The sender then selects as the acker the receiver with the lowest + expected throughput, as computed by the equation. + +The dynamics of the acker selection mechanism are sensitive to how the +measurements are performed and applied. In the rest of this document we +suggest specific mechanisms to perform and apply these measurements. +Other mechanisms are possible, but it is important to understand how the +interactions between mechanisms affect the dynamics of PGMCC. + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2. [Page 5] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +2.1. Packet Contents + +Before specifying the sender and receiver functionality, we describe the +information required by PGMCC to perform its tasks. This information is +carried in the data packets sent by the sender, and in the feedback +packets sent by the receiver. As PGMCC will be used along with some +transport protocol, the actual data and feedback packets will contain +further information for use by the protocol itself. For this reason, we +do not specify packet formats, as these depend on the details of the +transport protocol used. + +Note that the requirements of the transport protocol in terms of packet +generation may differ from those of PGMCC. As an example, most NAK-based +reliable multicast protocols do not use positive acknowledgements, but +PGMCC requires ACKs for clocking out data packets; unreliable transport +protocols might have no interest in generating NAKs for data integrity +purposes, yet PGMCC depends on NAKs reaching the data sender in order to +elect the ACKER. + + +Implementors may decide to insert PGMCC-related information in already +existing protocol packets whenever possible, but in cases such as the +ones described in the previous paragraph, it might be necessary to +define and generate new packets exclusively for congestion control +purposes. As an example, in a prototype implementation of PGMCC on top +of the PGM protocol [7], some of the information used by PGMCC is +already present in the original protocol packets, and PGMCC-specific +information is carried as PGM options in ODATA and NAK packets. However, +a new packet type has been defined for ACKs, which are generated +according to the rules defined in this document. + + +2.1.1. Data Packets + +Each data packet sent by the data sender contains the following +information: + + + o A SEQUENCE NUMBER. This number is incremented by one for each data + packet transmitted. The field must be sufficiently large that it + does not wrap causing two different packets with the same sequence + number to be in the receiver's recent packet history at the same + time. + + + o A TIMESTAMP (or equivalent information, see Section 2.5) indicating + when the packet with this sequence number has been sent. There is + no requirement for synchronized clocks between the sender and the + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2.1.1. [Page 6] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + + receivers. The timestamp is used to measure network round-trip + times, so needs sufficient resolution for this task. A resolution + of 1ms would be adequate. + + + o The ACKER IDENTITY, i.e. the identity of the receiver in charge of + sending an acknowledgement for this data packet. The ACKER is + elected as a result of the process described in Section 2.3. + A special value is used to indicate that no ACKER is designated for + this packet -- this can happen at the beginning of a session or when + the current ACKER leaves the group. Receivers interpret this value + as a request to elect a new acker. + + +2.1.2. Feedback Packets + +There are two types of feedback packets used by PGMCC: ACK packets and +NAK packets. +ACK packets are generated by the current ACKER, and are used to detect +loss or successful delivery of packets, and to regulate the throughput +accordingly. ACK packets also contain information used to determine the +TCP-equivalent throughput for the ACKER. +NAK packets are sent by any receiver who experiences loss. They contain +information used to determine the TCP-equivalent throughput for that +receiver. In an actual protocol instantiation (such as PGM [7]), NAK +packets might also be used by the protocol to request the retransmission +of specific packets, and indicate the identity of the packet being +requested. + +Both ACK and NAK packets are sent by data receivers, and contain the +following information: + + + o The TIMESTAMP (or equivalent information) derived from the most + recently received data packet according to one of the techniques + described in Section 2.5. + This value is used by the sender to measure the RTT to the receiver + who generated this feedback packet. + + + o ``p'', the receiver's current estimate of the LOSS RATE. The loss + rate is measured by receivers as described in Section 2.6 + +In addition to the above, ACK packets (sent by the acker designated in +the corresponding data packets) must also contain the following +information: + + + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2.1.2. [Page 7] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + + o RX_MAX, the highest sequence number among received data packets + (taking care to deal with sequence number wrapping correctly). + + o ACK_BITMAP, a bitmap indicating the receive status of the latest N + (typically N=32) data packets with sequence numbers RX_MAX-(N-1) to + RX_MAX. + + +This information is used by the sender to record which packets have been +received or lost, and manipulate the transmit window accordingly. Note +that each ACK packet contains information about multiple packets, and +this increases the robustness of the scheme to loss of ACK packets. +This is necessary because ACKs are not sent reliably (unlike TCP's ACKs, +which are cumulative). + + +2.1.3. Field sizes and formats + +The following sizes and formats are suggested for the various fields +used by PGMCC and transmitted over the network: + + + o SEQUENCE NUMBERS + 32 bit, unsigned, network order. + + + o TIMESTAMPS + 32 bit, unsigned, network order. A resolution of 1ms or better is + desirable. + + + o ACKER IDENTITY + Same size and format of a network layer address (e.g. 32 bit for + IPv4). Note though that using an IP address for the Acker Identify + will cause problems with NAT traversal. Transport protocol + designers might examine the SSRC mechanism used by RTP [6] as an + alternative form of node identifier that could be used as Acker + Identity. + + + o LOSS RATE (``p'') + 16-bit unsigned integer, in network format, with 0 indicating no + loss and 2^16-1 indicating 100% loss. + + + o ACK BITMAP + 32-bit, in network format, with least significant bit indicating + receive status of packet RX_MAX. + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2.1.3. [Page 8] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +2.2. Window-based controller + +In a window-based congestion control scheme such as TCP, the +``congestion window'' represents, among other things, the maximum amount +of packets in flight at any time, which in turn controls the throughput +of the session. The sender keeps track of the actual number of packets +in flight, basing on its transmissions and the reception of +acknowledgements. + +The sender may dynamically change the size of the window, according to +the congestion control scheme being used. In TCP, and PGMCC, an +``Additive Increase Multiplicative Decrease'' (AIMD) scheme is used: in +absence of loss, the window is increased by some fixed amount (typically +one packet) per round trip time (RTT), whereas upon loss the window is +reduced to a fraction of its original value (typically halved) in each +RTT in which a loss event is experienced. + +In PGMCC the window is managed using a token-based mechanism, controlled +by two variables: + + o A ``Window Size'', W, which describes the current window size in + packets. + + o A ``Token Count'', T, which indicates the number of packets that can + be transmitted. T is bounded above by W. It is decremented every + time a packet is transmitted, and incremented every time an ACK is + received, according to the rules below. + +Note that these two variables need to hold non-integer data. Typically +a fixed point representation with at least 16 bits for both integer and +fractional parts would be acceptable for implementation purposes. + +The information contained in each ACK is used to determine how many new +packets are acknowledged by that ACK, and whether there are +unacknowledged packets which were not reported in previous ACKs. The +sender also schedules a timeout to react in case no ACKs are received. + +The sender behaves as follows: + + + o INITIALIZATION + At startup, or after a timeout, both W and T are set to 1. + + + o ACK RECEPTION, NO LOSS DETECTED + If the incoming ACK reports new acknowledged packets, and no loss + (as defined in the next paragraph) is detected, then the window is + inflated by one packet per RTT. + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2.2. [Page 9] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + + NOTE: during the slow-start phase, TCP opens the window + exponentially up to the SSTHRESH value, which is computed by TCP + according to the dynamics of the session and updated upon losses. + + We do recommend that PGMCC uses a similar strategy, but using a + fixed, small value for SSTHRESH (e.g. 4 packets). In fact, due to + the dynamicity of the ACKER, which might change on every single + packet, it is hard to compute a reliable estimate of the SSTHRESH + without keeping state for multiple receivers, and the benefits are + small in any event. + + In summary, the reaction to ACK reception on no loss modifies T and + W as follows (here, N is the number of new packets acknowledged by + the incoming ACK): + + if (W < SSTHRESH) then + D = min(N, SSTHRESH - W) // use the first D acks for + exp.opening + N = N - D // and the remaining ones for + linear opening + T = T + 2*D + W = W + D + endif + // do linear window opening with the remaining acks + T = T + N * ( 1 + 1/W ) + W = W + N/W + + + o PACKET TRANSMISSION + One token is consumed for each packet transmitted: + + T = T - 1 + + + o ACK RECEPTION, LOSS DETECTED + If the incoming ACK reports an unacknowledged data packet which is + followed by at least 3 acknowledged data packets, then the packet is + assumed to be lost and PGMCC reacts by halving the window, in the + same way as TCP after 3 duplicate acknowledgements. This is + achieved by modifying T and W as follows: + + T = T - W/2 , W = W/2 + + to simulate the multiplicative decrease. + Additionally, all window manipulation is suspended for the + subsequent RTT. This is achieved by recording the current transmit + sequence number, and canceling any further manipulation of the + window until feedback is received for the next transmitted packet, + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2.2. [Page 10] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + + or until a timeout occurs. + + + + +2.3. Acker Selection + +The ACKER selection process in PGMCC aims at locating the receiver which +would have the lowest throughput if each receiver were using a separate +TCP connection to transfer data. + +Because the steady-state throughput of a TCP connection can be +characterized in a reasonably accurate way in terms of its loss rate and +round trip time [3], the throughput for each receiver can be estimated +by using these two parameters. + +Whenever an ACK or NAK packet from any of the receivers reaches it, the +sender is able to compute the expected throughput T_i for that receiver +by using the equation shown in Section 2.4, with the round trip time RTT +and loss rate p and measured as described in Sections 2.5 and 2.6, +respectively. At any given time, the sender stores the expected +throughput for the current ACKER, T_acker. This value is updated every +time an ACK or NAK from the current ACKER is received (note that, after +a new ACKER is selected, the sender will typically receive ACKs from the +old ACKER for one RTT, and the feedback from different ACKERs might be +interleaved if the paths leading to them have different round trip +times). + +Whenever an ACK or NAK is received from another node i (a previous ACKER +or some other receiver), the expected throughput T_i for that node is +computed, and compared with T_acker. Node i is selected as the new +acker if + + T_i < C * T_acker + +where the constant C between 0 and 1 provides some hysteresis and avoids +too frequent oscillations in the choice of the ACKER. A suggested value +for C is 0.75. + +Note that, from an implementation point of view (see Section 2.4), it is +more convenient to compute T_i ^(-2), so the above equation must be +modified accordingly. + + +2.3.1. Initial Acker election + +Upon reception of a data packet reporting that no acker is currently +selected, receivers generate a dummy NAK report which is used to elect + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2.3.1. [Page 11] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +the initial acker. The NAK is sent with the usual feedback suppression +mechanism dictated by the transport protocol (possibly with shorter time +constants) to avoid feedback implosion, and the sender will select the +source of the first incoming NAK as the new ACKER. + + +2.3.2. Acker dropouts + + +If the ACKER decides to disconnect from the session, it can cause the +session to stop. To avoid this, it is recommended that an ACKER deciding +to leave the session informs the sender by sending an ACK packet (or a +duplicate) carrying an "ACKER_LEAVING" option. The reception of this +packet by the sender will in turn trigger an initial acker election +phase. + + + +2.4. TCP Throughput Equation + +Any realistic equation of TCP throughput as a function of loss event +rate and RTT should be suitable for use in PGMCC. Unlike other schemes +[2] where the throughput equation directly controls the transmit rate, +in PGMCC the equation is used only for acker selection purposes, and the +throughput values are only compared among themselves. As a consequence, +we can use the following equation, derived from the one presented in [3] +by setting RTO = 4 * RTT (as it is common practice): + + M = 1/T = RTT_i * sqrt(p) * (1 + 9*p * (1 + 32*(p)^2)) + + +where + + M = 1/T is proportional to the inverse of the throughput for the + receiver under consideration; + + RTT is the round trip time for the receiver under consideration; + + p is the loss rate for the receiver under consideration, between 0 + and 1.0; + +and multiplying constants are omitted. + +The above equation is accurate on a wide range of loss rates, and also +covers situations where retransmission timeouts have a significant +impact on the throughput of the protocol. + +Note that when p=0, the equation yields 1/T = M = 0. This does not + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2.4. [Page 12] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +constitute a problem as we can still compare the M values computed for +different receivers to determine the acker. Also note that it is easier +to compute M^2 instead of M, because the former does not require the use +of sqrt(). + +In future, different throughput equations may be substituted for this +equation. The requirement is that the throughput equation be a +reasonable approximation of the sending rate of TCP for conformant TCP +congestion control. + +The parameters p and RTT need to be measured or calculated by a PGMCC +implementation. The measurement of RTT is specified in Section 2.5; the +measurement of p is specified in Section 2.6. + +2.5. RTT measurement + +In PGMCC, the RTT is measured by the sender making use of the timestamp +(or equivalent information) echoed back by each receiver in feedback +messages. Three procedures are possible to measure the RTT, as follows. +In no case is it required to have clock synchronization between sender +and receivers. + + +2.5.1. Explicit Timestamp + +This first technique relies on the transmission of a timestamp TS_j with +each data packet j. +The receiver will record the most recently received timestamp, and will +echo it back to the source when generating an ACK or a NAK. If the +feedback is delayed, the time elapsed between the reception of the +timestamp and the generation of the feedback should be added to the +echoed timestamp. +The sender computes the RTT by subtracting the received timestamp from +the current value of the clock. + +The resolution of the timestamp value should be good enough for +reasonable precision measurement of typical network round trip times. If +receivers need to apply correction for delayed feedback, it is necessary +that receivers know the resolution of the timestamp clock. A suggested +value is 1ms. + + +2.5.2. Implicit timestamp + +With this technique, the sender will record a timestamp TS_j for each +transmitted data packet j, but the timestamp will not be transmitted +with the packet itself. +The receiver will record the most recently received sequence number, and + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2.5.2. [Page 13] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +will echo it back to the source when generating an ACK or a NAK. +The sender computes the RTT by looking up the timestamp associated with +the sequence number received in the feedback packet, and subtracting it +from the current clock value. + +If the feedback from the receiver is delayed, as it is commonly the case +for NAKs, the receiver can compute, and send back to the source, a +correction term corresponding to the time elapsed between the reception +of the timestamp and the generation of the feedback. The correction term +will then be subtracted by the sender in order to obtain the correct +estimate of the RTT. + +This RTT measurement technique is equivalent to the previous one, but it +saves some space in data packets as the timestamp does not need to be +sent explicitly. Feedback packets might become larger if the correction +value is transmitted explicitly; but in many cases, the sequence number +will already be present for other reasons (e.g. ACK packets), and +wherever space is a concern the sequence number and the correction term +can be packed in a single 32-bit word without loss of precision. + + +2.5.3. Sequence numbers + +This technique is the least precise, but it does not rely on the +presence of a high resolution clock on the nodes. +The sender will not compute any timestamp, and just send data packets +with their sequence number j. +The receiver will record the most recently received sequence number, and +will echo it back to the source when generating an ACK or a NAK. +The sender computes the RTT as the difference between the most recently +sent sequence number and the sequence number received from the ACK or +NAK packet. + +Note that in this case the RTT is not measured in seconds, but in +"sequence numbers", which are monotonically, but not uniformly, +increasing with time. The two measurements are equivalent if the sender +transmits at a constant rate. When the data rate changes over time (as +it is normally happens, given that PGMCC controls the actual data rate), +then the "measured" RTT values grow with the actual transmit rate. This +can influence the correctness of the results when comparing two +measurement done over different and only partially overlapping time (and +sequence number) intervals where the transmit rate incurs a significant +change. + + + + + + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2.5.3. [Page 14] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +2.5.4. Recommendations + +Whenever possible, the measurement of the RTT should be carried out +using either explicit or implicit timestamps, and by keeping track of +the "correction term" (the delay between data reception and feedback +generation). + +If the receiver does not have a clock with a suitable resolution, the +correction term might not be present (or be inaccurate). In this case +the timestamps received by the sender on NAK packets might be in error, +in the worst case, by as much as the packet interarrival time. This +error will normally not be present on ACK packets, which are sent +immediately. A suitable correction should be applied by the sender in +order to avoid systematic errors. + +The measurement based on sequence numbers is less accurate, but also +less sensitive to errors due to the lack of the correction term. In +fact, the measurement error induced by the lack of the correction term +can be at most one unit. This suggests that, when the correction term +is not available, measurements based on sequence numbers should be +favoured. Simulations have shown that the acker selection mechanism +performs moderately better when the RTT measurement is based on +timestamps, but performance is reasonably good also with measurements +based on sequence numbers. + + + +2.6. Loss rate measurement + + +The loss measurement in PGMCC is entirely performed by receivers. The +measurement results do not directly influence the transmit rate, but are +only used for comparison purposes. As a consequence, the scheme is +reasonably robust to different measurement techniques, as long as they +are not influenced too strongly by single loss events. + +The main method suggested for loss measurement is Exponentially Weighted +Moving Average (EWMA), which is formally equivalent to a single-pole +digital low pass filter applied to a binary signal x_i, where x_i = 1 if +packet i is lost, x_i = 0 if packet i is successfully received. + +The loss rate p_i upon reception or detection of loss of packet i is +computed as + + + p_i = c_p * p_{i-1} + (1 - c_p ) * p_i + where the constant c_p between 0 and 1 is related to the bandpass of +the filter. Experiments have shown good performance with c = 500/65536, + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2.6. [Page 15] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +and computations performed with fixed point arithmetic and 16 fractional +digits. + +As an alternative to EWMA, the technique used in TFRC [2] can be +adopted. Simulations have shown a moderate improvement in the acker +selection mechanism by measuring loss using the TFRC loss estimator, +which is however slightly more expensive to compute than the EWMA loss +estimator in presence of packet reordering. + + +2.7. Timeouts + + +When a packet is transmitted, the sender schedules a timeout to prevent +stalls upon loss of ACKs or disconnection of the ACKER. In TCP, which +has a similar problem, the timeout value is computed by accumulating +statistics (SRTT and RTTVAR) on RTT samples, starting from a default +initial value (3s) when no RTT samples are available. + +PGMCC can use a similar scheme to compute the timeouts, remembering that +upon ACKER changes (which may be very frequent), the computation of SRTT +and RTTVAR must be restarted from the beginning, unless the sender +decides to keep state for at least a small number of recent ackers. + +Because the ACKER can leave the group without notifying the sender, +after a number of successive timeouts the sender MUST force the election +of a new ACKER. We recommend this new election to be performed after +two successive timeouts. + + +2.8. Interaction with feedback suppression schemes + + +Several schemes are used by NAK-based multicast protocols to reduce the +amount of feedback directed toward the source and make the protocol +scale with large populations of receivers. Such schemes typically rely +on randomly delaying NAK generation, and suppressing pending NAKs when +an equivalent NAK or a retransmission is heard; or, intermediate nodes +such as routers can implement some form of feedback aggregation and +filtering. + +Such schemes might prevent NAKs from potential ACKER candidates from +reaching the source. This filtering might impact the speed at which +PGMCC selects the correct ACKER, though initial experience from +simulations seem to suggest that PGMCC behavior is not severely affected +by NAK suppression schemes. + + + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2.8. [Page 16] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +2.9. Interaction with ECN + + +PGMCC can use ECN notifications in much the same way as actual losses, +and use such notifications to control the throughput of the session. + +At the receiver, ECN-marked data packets can be considered as lost +packets for the purpose of loss rate computation and ACK/NAK generation. +If the ACKER sends an ACK for ECN-marked packets, that ACK MUST report +that the packet being acknowledged that was ECN marked. Similarly the +ACKER must indicate in the ACK packet's received packets bitmap that the +packet was ECN-marked, or that the packet was lost. + +We note that to support use of the ECN nonce, the ACK packet's received +packets bitmap would require two bits per packet being reported. + + +3. Procedures - Sender + +The following pseudo-code specifies the complete behavior of the sender +in PGMCC. + + +initialization: + T = 1 ; W = 1 ; /* initialize window and number of tokens */ + RETRY = 0 ; /* number of consecutive timeouts so far */ + < initialize p, RTT for acker to default values > + ACKER = NO_ACKER; /* no acker is known */ + < initialize sequence numbers > + QUEUED = 0; /* packets waiting to be transmitted */ + +on transmission request: + send_packet() ; + +on timeout expiration : + T = 1 ; W = 1 ; /* initialize window and number of tokens */ + if (RETRY < RETRY_MAX) + RETRY = RETRY + 1 + else + ACKER = NO_ACKER ; /* old acker is not valid anymore */ + send_packet() ; + + + + + + + + + + +Rizzo/Iannaccone/Vicisano/Handley Section 3. [Page 17] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +on ACK/NAK reception from receiver I : + < compute p and RTT for source of this ACK, see Sec. 2.5 and 2.6 > + RETRY = 0 ; + if (ACKER == NO_ACKER) { /* select current as acker is no other known */ + ACKER = I ; + T = T + 1 ; + } + if (ACKER != I) + < select acker according to Sec. 2.3 > ; + else { + + if (packet_type == ACK) { + < update_window according to Sec.2.2 > + send_packet ; + if (ack_pending) + update_timeout ; + } +} + +send_packet() { + if (QUEUED > 0 && T >= 1) { + < transmit one packet > + T = T - 1 ; + QUEUED = QUEUED - 1 ; + } + if ( ) + +} + + + +4. Procedures -- Receiver + +The following pseudo-code specifies the complete behavior of the +receiver in PGMCC. + +A receiver only transmits an ACK packet when it receives a data packet +for which the receiver is designated as the ACKER by the data packet +itself. A receiver can transmit a NAK packet after it has detected that +a data packet is missing and a suitable delay has passed, as dictated by +the feedback suppression rules of the protocol in use. + +The data packet contains acknowledgement status about the most recent 32 +sequence numbers known to the receiver. + + + + + + + +Rizzo/Iannaccone/Vicisano/Handley Section 4. [Page 18] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +on initialization/session setup: + < initialize state variables and ACK bitmap > + +on DATA packet reception: + < update p measurement according to Sec.2.6 > + < record timestamp and packet reception time > + if (ACKER == this_node) { + < send an immediate ACK > + } + if ( ) + < schedule a timeout for NAK transmission > + +on NAK reception: + < suppress any pending NAK transmission for the sequence + number indicated in the NAK > + +on timeout: + if ( < there are missing and unacknowledged packets > ) { + < send a NAK for one or more of the missing packets > + < mark such packets as acknowledged > + if ( ) + < schedule a timeout for NAK transmission > + } + + +5. Security Considerations + +PGMCC is not a transport protocol in its own right, but a congestion +control mechanism that is intended to be used in conjunction with a +transport protocol. Therefore security primarily needs to be considered +in the context of a specific transport protocol and its authentication +mechanisms. + +Congestion control mechanisms can potentially be exploited to create +denial of service. This may occur through spoofed feedback. Thus any +transport protocol that uses PGMCC should take care to ensure that +feedback is only accepted from the receiver of the data. The precise +mechanism to achieve this will however depend on the transport protocol +itself. + +In addition, congestion control mechanisms may potentially be +manipulated by a greedy receiver that wishes to receive more than its +fair share of network bandwidth. A receiver might do this by first +reporting inflated loss and RTT samples, in order to get selected as the +ACKER, and then generating ACK at the desired rate (including possibly +claiming to have received packets that in fact were lost due to +congestion). Possible defenses against such a receiver could be based +on the sender verifying the correctness of the loss and RTT samples + + + +Rizzo/Iannaccone/Vicisano/Handley Section 5. [Page 19] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +supplied by the receiver. A PGMCC sender SHOULD compare the receiver +reports on loss rate and RTT with the information derived directly from +the incoming stream of ACKs. In case of discrepancy of the reports, a +PGMCC sender SHOULD mark the current acker as ineligible and initiate a +new acker election. The decision on how large that discrepancy should be +before initiating a new acker election is left to the implementation. + +Also, the sender MAY include some form of nonce that the receiver must +feed back to the sender to prove receipt. However, the details of such a +nonce would depend on the transport protocol, and in particular on +whether the transport protocol is reliable or unreliable. + + +6. Authors' Addresses + + Luigi Rizzo + luigi@iet.unipi.it + Dip. Ing. dell'Informazione, + Univ. di Pisa + via Diotisalvi 2, 56122 Pisa, Italy + + Gianluca Iannaccone + gianluca.iannaccone@intel.com + Intel Research + 15 JJ Thomson Avenue, Cambridge CB3 0FD, UK + + Lorenzo Vicisano + lorenzo@cisco.com + cisco Systems, Inc. + 170 West Tasman Dr., + San Jose, CA, USA, 95134 + + Mark Handley + m.handley@cs.ucl.ac.uk + University College London, + Gower Street, London WC1E 6BT, UK + + +7. Acknowledgments + +We would like to acknowledge feedback and discussions on equation-based +congestion control with a wide range of people, including members of the +Reliable Multicast Research Group, the Reliable Multicast Transport +Working Group, and the End-to-End Research Group. + + + + + + + +Rizzo/Iannaccone/Vicisano/Handley Section 7. [Page 20] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +[1] Bradner, S., Key words for use in RFCs to Indicate Requirement +Levels (IETF RFC 2119) http://www.rfc-editor.org/rfc/rfc2119.txt + +[2] Floyd, S., Handley, M., Padhye, J., Widmer, J., "Equation-Based +Congestion Control for Unicast Applications", ACM SIGCOMM 2000, +Stockholm, Aug. 2000 + +[3] Padhye, J. and Firoiu, V. and Towsley, D. and Kurose, J., "Modeling +TCP Throughput: A Simple Model and its Empirical Validation", Proc ACM +SIGCOMM 1998. + +[4] Mankin, A., Romanow, A., Brander, S., Paxson, V., "IETF Criteria for +Evaluating Reliable Multicast Transport and Application Protocols," +RFC2357, June 1998. + +[5] Rizzo, L., "pgmcc: a TCP-friendly single-rate multicast congestion +control scheme", ACM SIGCOMM 2000, Stockholm, Aug.2000 + +[6] Schulzrinne, H., Casner, S., Frederick, R., Jacobson, V., "RTP: A +Transport Protocol for Real-Time Applications", RFC 1889, Jan 1996. + +[7] Speakman, T., Crowcroft, J., Gemmell, J., Farinacci, D. , Lin, S., +Leshchiner, D., Luby, M., Montgomery, T. , Rizzo, L., Tweedly, A., +Bhaskar, N., Edmonstone, R., Sumanasekera, R., Vicisano, L., PGM +Reliable Transport Protocol Specification, RFC 3208, December 2001. +rfc3208.txt also available at ftp://ftp.rfc-editor.org/in- +notes/rfc3208.txt + + + + +8. Full Copyright Statement + +Copyright (C) The Internet Society (2000). All Rights Reserved. + +This document and translations of it may be copied and furnished to +others, and derivative works that comment on or otherwise explain it or +assist in its implementation may be prepared, copied, published and +distributed, in whole or in part, without restriction of any kind, +provided that the above copyright notice and this paragraph are included +on all such copies and derivative works. However, this document itself +may not be modified in any way, such as by removing the copyright notice +or references to the Internet Society or other Internet organizations, +except as needed for the purpose of developing Internet standards in +which case the procedures for copyrights defined in the Internet +languages other than English. + + + + + +Rizzo/Iannaccone/Vicisano/Handley Section 8. [Page 21] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +The limited permissions granted above are perpetual and will not be +revoked by the Internet Society or its successors or assigns. + +This document and the information contained herein is provided on an "AS +IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK +FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT +INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR +FITNESS FOR A PARTICULAR PURPOSE." + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Rizzo/Iannaccone/Vicisano/Handley Section 8. [Page 22] diff --git a/3rdparty/openpgm-svn-r1085/doc/rfc3208.txt b/3rdparty/openpgm-svn-r1085/doc/rfc3208.txt new file mode 100644 index 0000000..fb82c26 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/doc/rfc3208.txt @@ -0,0 +1,6219 @@ + + + + + + +Network Working Group T. Speakman +Request for Comments: 3208 Cisco Systems +Category: Experimental J. Crowcroft + UCL + J. Gemmell + Microsoft + D. Farinacci + Procket Networks + S. Lin + Juniper Networks + D. Leshchiner + TIBCO Software + M. Luby + Digital Fountain + T. Montgomery + Talarian Corporation + L. Rizzo + University of Pisa + A. Tweedly + N. Bhaskar + R. Edmonstone + R. Sumanasekera + L. Vicisano + Cisco Systems + December 2001 + + + PGM Reliable Transport Protocol Specification + +Status of this Memo + + This memo defines an Experimental Protocol for the Internet + community. It does not specify an Internet standard of any kind. + Discussion and suggestions for improvement are requested. + Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2001). All Rights Reserved. + +Abstract + + Pragmatic General Multicast (PGM) is a reliable multicast transport + protocol for applications that require ordered or unordered, + duplicate-free, multicast data delivery from multiple sources to + multiple receivers. PGM guarantees that a receiver in the group + either receives all data packets from transmissions and repairs, or + is able to detect unrecoverable data packet loss. PGM is + + + +Speakman, et. al. Experimental [Page 1] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + specifically intended as a workable solution for multicast + applications with basic reliability requirements. Its central design + goal is simplicity of operation with due regard for scalability and + network efficiency. + +Table of Contents + + 1. Introduction and Overview .................................. 3 + 2. Architectural Description .................................. 9 + 3. Terms and Concepts ......................................... 12 + 4. Procedures - General ....................................... 18 + 5. Procedures - Sources ....................................... 19 + 6. Procedures - Receivers ..................................... 22 + 7. Procedures - Network Elements .............................. 27 + 8. Packet Formats ............................................. 31 + 9. Options .................................................... 40 + 10. Security Considerations .................................... 56 + 11. Appendix A - Forward Error Correction ...................... 58 + 12. Appendix B - Support for Congestion Control ................ 72 + 13. Appendix C - SPM Requests .................................. 79 + 14. Appendix D - Poll Mechanism ................................ 82 + 15. Appendix E - Implosion Prevention .......................... 92 + 16. Appendix F - Transmit Window Example ....................... 98 + 17 Appendix G - Applicability Statement ....................... 103 + 18. Abbreviations .............................................. 105 + 19. Acknowledgments ............................................ 106 + 20. References ................................................. 106 + 21. Authors' Addresses.......................................... 108 + 22. Full Copyright Statement ................................... 111 + +Nota Bene: + + The publication of this specification is intended to freeze the + definition of PGM in the interest of fostering both ongoing and + prospective experimentation with the protocol. The intent of that + experimentation is to provide experience with the implementation and + deployment of a reliable multicast protocol of this class so as to be + able to feed that experience back into the longer-term + standardization process underway in the Reliable Multicast Transport + Working Group of the IETF. Appendix G provides more specific detail + on the scope and status of some of this experimentation. Reports of + experiments include [16-23]. Additional results and new + experimentation are encouraged. + + + + + + + + +Speakman, et. al. Experimental [Page 2] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +1. Introduction and Overview + + A variety of reliable protocols have been proposed for multicast data + delivery, each with an emphasis on particular types of applications, + network characteristics, or definitions of reliability ([1], [2], + [3], [4]). In this tradition, Pragmatic General Multicast (PGM) is a + reliable transport protocol for applications that require ordered or + unordered, duplicate-free, multicast data delivery from multiple + sources to multiple receivers. + + PGM is specifically intended as a workable solution for multicast + applications with basic reliability requirements rather than as a + comprehensive solution for multicast applications with sophisticated + ordering, agreement, and robustness requirements. Its central design + goal is simplicity of operation with due regard for scalability and + network efficiency. + + PGM has no notion of group membership. It simply provides reliable + multicast data delivery within a transmit window advanced by a source + according to a purely local strategy. Reliable delivery is provided + within a source's transmit window from the time a receiver joins the + group until it departs. PGM guarantees that a receiver in the group + either receives all data packets from transmissions and repairs, or + is able to detect unrecoverable data packet loss. PGM supports any + number of sources within a multicast group, each fully identified by + a globally unique Transport Session Identifier (TSI), but since these + sources/sessions operate entirely independently of each other, this + specification is phrased in terms of a single source and extends + without modification to multiple sources. + + More specifically, PGM is not intended for use with applications that + depend either upon acknowledged delivery to a known group of + recipients, or upon total ordering amongst multiple sources. + + Rather, PGM is best suited to those applications in which members may + join and leave at any time, and that are either insensitive to + unrecoverable data packet loss or are prepared to resort to + application recovery in the event. Through its optional extensions, + PGM provides specific mechanisms to support applications as disparate + as stock and news updates, data conferencing, low-delay real-time + video transfer, and bulk data transfer. + + In the following text, transport-layer originators of PGM data + packets are referred to as sources, transport-layer consumers of PGM + data packets are referred to as receivers, and network-layer entities + in the intervening network are referred to as network elements. + + + + + +Speakman, et. al. Experimental [Page 3] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Unless otherwise specified, the term "repair" will be used to + indicate both the actual retransmission of a copy of a missing packet + or the transmission of an FEC repair packet. + +Terminology + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in RFC 2119 [14] and + indicate requirement levels for compliant PGM implementations. + +1.1. Summary of Operation + + PGM runs over a datagram multicast protocol such as IP multicast [5]. + In the normal course of data transfer, a source multicasts sequenced + data packets (ODATA), and receivers unicast selective negative + acknowledgments (NAKs) for data packets detected to be missing from + the expected sequence. Network elements forward NAKs PGM-hop-by- + PGM-hop to the source, and confirm each hop by multicasting a NAK + confirmation (NCF) in response on the interface on which the NAK was + received. Repairs (RDATA) may be provided either by the source + itself or by a Designated Local Repairer (DLR) in response to a NAK. + + Since NAKs provide the sole mechanism for reliability, PGM is + particularly sensitive to their loss. To minimize NAK loss, PGM + defines a network-layer hop-by-hop procedure for reliable NAK + forwarding. + + Upon detection of a missing data packet, a receiver repeatedly + unicasts a NAK to the last-hop PGM network element on the + distribution tree from the source. A receiver repeats this NAK until + it receives a NAK confirmation (NCF) multicast to the group from that + PGM network element. That network element responds with an NCF to + the first occurrence of the NAK and any further retransmissions of + that same NAK from any receiver. In turn, the network element + repeatedly forwards the NAK to the upstream PGM network element on + the reverse of the distribution path from the source of the original + data packet until it also receives an NCF from that network element. + Finally, the source itself receives and confirms the NAK by + multicasting an NCF to the group. + + While NCFs are multicast to the group, they are not propagated by PGM + network elements since they act as hop-by-hop confirmations. + + + + + + + + +Speakman, et. al. Experimental [Page 4] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + To avoid NAK implosion, PGM specifies procedures for subnet-based NAK + suppression amongst receivers and NAK elimination within network + elements. The usual result is the propagation of just one copy of a + given NAK along the reverse of the distribution path from any network + with directly connected receivers to a source. + + The net effect is that unicast NAKs return from a receiver to a + source on the reverse of the path on which ODATA was forwarded, that + is, on the reverse of the distribution tree from the source. More + specifically, they return through exactly the same sequence of PGM + network elements through which ODATA was forwarded, but in reverse. + The reasons for handling NAKs this way will become clear in the + discussion of constraining repairs, but first it's necessary to + describe the mechanisms for establishing the requisite source path + state in PGM network elements. + + To establish source path state in PGM network elements, the basic + data transfer operation is augmented by Source Path Messages (SPMs) + from a source, periodically interleaved with ODATA. SPMs function + primarily to establish source path state for a given TSI in all PGM + network elements on the distribution tree from the source. PGM + network elements use this information to address returning unicast + NAKs directly to the upstream PGM network element toward the source, + and thereby insure that NAKs return from a receiver to a source on + the reverse of the distribution path for the TSI. + + SPMs are sent by a source at a rate that serves to maintain up-to- + date PGM neighbor information. In addition, SPMs complement the role + of DATA packets in provoking further NAKs from receivers, and + maintaining receive window state in the receivers. + + As a further efficiency, PGM specifies procedures for the constraint + of repairs by network elements so that they reach only those network + segments containing group members that did not receive the original + transmission. As NAKs traverse the reverse of the ODATA path + (upward), they establish repair state in the network elements which + is used in turn to constrain the (downward) forwarding of the + corresponding RDATA. + + Besides procedures for the source to provide repairs, PGM also + specifies options and procedures that permit designated local + repairers (DLRs) to announce their availability and to redirect + repair requests (NAKs) to themselves rather than to the original + source. In addition to these conventional procedures for loss + recovery through selective ARQ, Appendix A specifies Forward Error + Correction (FEC) procedures for sources to provide and receivers to + request general error correcting parity packets rather than selective + retransmissions. + + + +Speakman, et. al. Experimental [Page 5] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Finally, since PGM operates without regular return traffic from + receivers, conventional feedback mechanisms for transport flow and + congestion control cannot be applied. Appendix B specifies a TCP- + friendly, NE-based solution for PGM congestion control, and cites a + reference to a TCP-friendly, end-to-end solution for PGM congestion + control. + + In its basic operation, PGM relies on a purely rate-limited + transmission strategy in the source to bound the bandwidth consumed + by PGM transport sessions and to define the transmit window + maintained by the source. + + PGM defines four basic packet types: three that flow downstream + (SPMs, DATA, NCFs), and one that flows upstream (NAKs). + +1.2. Design Goals and Constraints + + PGM has been designed to serve that broad range of multicast + applications that have relatively simple reliability requirements, + and to do so in a way that realizes the much advertised but often + unrealized network efficiencies of multicast data transfer. The + usual impediments to realizing these efficiencies are the implosion + of negative and positive acknowledgments from receivers to sources, + repair latency from the source, and the propagation of repairs to + disinterested receivers. + +1.2.1. Reliability. + + Reliable data delivery across an unreliable network is conventionally + achieved through an end-to-end protocol in which a source (implicitly + or explicitly) solicits receipt confirmation from a receiver, and the + receiver responds positively or negatively. While the frequency of + negative acknowledgments is a function of the reliability of the + network and the receiver's resources (and so, potentially quite low), + the frequency of positive acknowledgments is fixed at at least the + rate at which the transmit window is advanced, and usually more + often. + + Negative acknowledgments primarily determine repairs and reliability. + Positive acknowledgments primarily determine transmit buffer + management. + + When these principles are extended without modification to multicast + protocols, the result, at least for positive acknowledgments, is a + burden of positive acknowledgments transmitted to the source that + quickly threatens to overwhelm it as the number of receivers grows. + More succinctly, ACK implosion keeps ACK-based reliable multicast + protocols from scaling well. + + + +Speakman, et. al. Experimental [Page 6] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + One of the goals of PGM is to get as strong a definition of + reliability as possible from as simple a protocol as possible. ACK + implosion can be addressed in a variety of effective but complicated + ways, most of which require re-transmit capability from other than + the original source. + + An alternative is to dispense with positive acknowledgments + altogether, and to resort to other strategies for buffer management + while retaining negative acknowledgments for repairs and reliability. + The approach taken in PGM is to retain negative acknowledgments, but + to dispense with positive acknowledgments and resort instead to + timeouts at the source to manage transmit resources. + + The definition of reliability with PGM is a direct consequence of + this design decision. PGM guarantees that a receiver either receives + all data packets from transmissions and repairs, or is able to detect + unrecoverable data packet loss. + + PGM includes strategies for repeatedly provoking NAKs from receivers, + and for adding reliability to the NAKs themselves. By reinforcing + the NAK mechanism, PGM minimizes the probability that a receiver will + detect a missing data packet so late that the packet is unavailable + for repair either from the source or from a designated local repairer + (DLR). Without ACKs and knowledge of group membership, however, PGM + cannot eliminate this possibility. + +1.2.2. Group Membership + + A second consequence of eliminating ACKs is that knowledge of group + membership is neither required nor provided by the protocol. + Although a source may receive some PGM packets (NAKs for instance) + from some receivers, the identity of the receivers does not figure in + the processing of those packets. Group membership MAY change during + the course of a PGM transport session without the knowledge of or + consequence to the source or the remaining receivers. + +1.2.3. Efficiency + + While PGM avoids the implosion of positive acknowledgments simply by + dispensing with ACKs, the implosion of negative acknowledgments is + addressed directly. + + Receivers observe a random back-off prior to generating a NAK during + which interval the NAK is suppressed (i.e. it is not sent, but the + receiver acts as if it had sent it) by the receiver upon receipt of a + matching NCF. In addition, PGM network elements eliminate duplicate + NAKs received on different interfaces on the same network element. + + + + +Speakman, et. al. Experimental [Page 7] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + The combination of these two strategies usually results in the source + receiving just a single NAK for any given lost data packet. + + Whether a repair is provided from a DLR or the original source, it is + important to constrain that repair to only those network segments + containing members that negatively acknowledged the original + transmission rather than propagating it throughout the group. PGM + specifies procedures for network elements to use the pattern of NAKs + to define a sub-tree within the group upon which to forward the + corresponding repair so that it reaches only those receivers that + missed it in the first place. + +1.2.4. Simplicity + + PGM is designed to achieve the greatest improvement in reliability + (as compared to the usual UDP) with the least complexity. As a + result, PGM does NOT address conference control, global ordering + amongst multiple sources in the group, nor recovery from network + partitions. + +1.2.5. Operability + + PGM is designed to function, albeit with less efficiency, even when + some or all of the network elements in the multicast tree have no + knowledge of PGM. To that end, all PGM data packets can be + conventionally multicast routed by non-PGM network elements with no + loss of functionality, but with some inefficiency in the propagation + of RDATA and NCFs. + + In addition, since NAKs are unicast to the last-hop PGM network + element and NCFs are multicast to the group, NAK/NCF operation is + also consistent across non-PGM network elements. Note that for NAK + suppression to be most effective, receivers should always have a PGM + network element as a first hop network element between themselves and + every path to every PGM source. If receivers are several hops + removed from the first PGM network element, the efficacy of NAK + suppression may degrade. + +1.3. Options + + In addition to the basic data transfer operation described above, PGM + specifies several end-to-end options to address specific application + requirements. PGM specifies options to support fragmentation, late + joining, redirection, Forward Error Correction (FEC), reachability, + and session synchronization/termination/reset. Options MAY be + appended to PGM data packet headers only by their original + transmitters. While they MAY be interpreted by network elements, + options are neither added nor removed by network elements. + + + +Speakman, et. al. Experimental [Page 8] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + All options are receiver-significant (i.e., they must be interpreted + by receivers). Some options are also network-significant (i.e., they + must be interpreted by network elements). + + Fragmentation MAY be used in conjunction with data packets to allow a + transport-layer entity at the source to break up application-layer + data packets into multiple PGM data packets to conform with the + maximum transmission unit (MTU) supported by the network layer. + + Late joining allows a source to indicate whether or not receivers may + request all available repairs when they initially join a particular + transport session. + + Redirection MAY be used in conjunction with Poll Responses to allow a + DLR to respond to normal NCFs or POLLs with a redirecting POLR + advertising its own address as an alternative re-transmitter to the + original source. + + FEC techniques MAY be applied by receivers to use source-provided + parity packets rather than selective retransmissions to effect loss + recovery. + +2. Architectural Description + + As an end-to-end transport protocol, PGM specifies packet formats and + procedures for sources to transmit and for receivers to receive data. + To enhance the efficiency of this data transfer, PGM also specifies + packet formats and procedures for network elements to improve the + reliability of NAKs and to constrain the propagation of repairs. The + division of these functions is described in this section and expanded + in detail in the next section. + +2.1. Source Functions + + Data Transmission + + Sources multicast ODATA packets to the group within the + transmit window at a given transmit rate. + + Source Path State + + Sources multicast SPMs to the group, interleaved with ODATA if + present, to establish source path state in PGM network + elements. + + + + + + + +Speakman, et. al. Experimental [Page 9] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + NAK Reliability + + Sources multicast NCFs to the group in response to any NAKs + they receive. + + Repairs + + Sources multicast RDATA packets to the group in response to + NAKs received for data packets within the transmit window. + + Transmit Window Advance + + Sources MAY advance the trailing edge of the window according + to one of a number of strategies. Implementations MAY support + automatic adjustments such as keeping the window at a fixed + size in bytes, a fixed number of packets or a fixed real time + duration. In addition, they MAY optionally delay window + advancement based on NAK-silence for a certain period. Some + possible strategies are outlined later in this document. + +2.2. Receiver Functions + + Source Path State + + Receivers use SPMs to determine the last-hop PGM network + element for a given TSI to which to direct their NAKs. + + Data Reception + + Receivers receive ODATA within the transmit window and + eliminate any duplicates. + + Repair Requests + + Receivers unicast NAKs to the last-hop PGM network element (and + MAY optionally multicast a NAK with TTL of 1 to the local + group) for data packets within the receive window detected to + be missing from the expected sequence. A receiver MUST + repeatedly transmit a given NAK until it receives a matching + NCF. + + NAK Suppression + + Receivers suppress NAKs for which a matching NCF or NAK is + received during the NAK transmit back-off interval. + + + + + + +Speakman, et. al. Experimental [Page 10] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Receive Window Advance + + Receivers immediately advance their receive windows upon + receipt of any PGM data packet or SPM within the transmit + window that advances the receive window. + +2.3. Network Element Functions + + Network elements forward ODATA without intervention. + + Source Path State + + Network elements intercept SPMs and use them to establish + source path state for the corresponding TSI before multicast + forwarding them in the usual way. + + NAK Reliability + + Network elements multicast NCFs to the group in response to any + NAK they receive. For each NAK received, network elements + create repair state recording the transport session identifier, + the sequence number of the NAK, and the input interface on + which the NAK was received. + + Constrained NAK Forwarding + + Network elements repeatedly unicast forward only the first copy + of any NAK they receive to the upstream PGM network element on + the distribution path for the TSI until they receive an NCF in + response. In addition, they MAY optionally multicast this NAK + upstream with TTL of 1. + + Nota Bene: Once confirmed by an NCF, network elements discard NAK + packets; NAKs are NOT retained in network elements beyond this + forwarding operation, but state about the reception of them is + stored. + + NAK Elimination + + Network elements discard exact duplicates of any NAK for which + they already have repair state (i.e., that has been forwarded + either by themselves or a neighboring PGM network element), and + respond with a matching NCF. + + + + + + + + +Speakman, et. al. Experimental [Page 11] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Constrained RDATA Forwarding + + Network elements use NAKs to maintain repair state consisting + of a list of interfaces upon which a given NAK was received, + and they forward the corresponding RDATA only on these + interfaces. + + NAK Anticipation + + If a network element hears an upstream NCF (i.e., on the + upstream interface for the distribution tree for the TSI), it + establishes repair state without outgoing interfaces in + anticipation of responding to and eliminating duplicates of the + NAK that may arrive from downstream. + +3. Terms and Concepts + + Before proceeding from the preceding overview to the detail in the + subsequent Procedures, this section presents some concepts and + definitions that make that detail more intelligible. + +3.1. Transport Session Identifiers + + Every PGM packet is identified by a: + + TSI transport session identifier + + TSIs MUST be globally unique, and only one source at a time may act + as the source for a transport session. (Note that repairers do not + change the TSI in any RDATA they transmit). TSIs are composed of the + concatenation of a globally unique source identifier (GSI) and a + source-assigned data-source port. + + Since all PGM packets originated by receivers are in response to PGM + packets originated by a source, receivers simply echo the TSI heard + from the source in any corresponding packets they originate. + + Since all PGM packets originated by network elements are in response + to PGM packets originated by a receiver, network elements simply echo + the TSI heard from the receiver in any corresponding packets they + originate. + +3.2. Sequence Numbers + + PGM uses a circular sequence number space from 0 through ((2**32) - + 1) to identify and order ODATA packets. Sources MUST number ODATA + packets in unit increments in the order in which the corresponding + application data is submitted for transmission. Within a transmit or + + + +Speakman, et. al. Experimental [Page 12] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + receive window (defined below), a sequence number x is "less" or + "older" than sequence number y if it numbers an ODATA packet + preceding ODATA packet y, and a sequence number y is "greater" or + "more recent" than sequence number x if it numbers an ODATA packet + subsequent to ODATA packet x. + +3.3. Transmit Window + + The description of the operation of PGM rests fundamentally on the + definition of the source-maintained transmit window. This definition + in turn is derived directly from the amount of transmitted data (in + seconds) a source retains for repair (TXW_SECS), and the maximum + transmit rate (in bytes/second) maintained by a source to regulate + its bandwidth utilization (TXW_MAX_RTE). + + In terms of sequence numbers, the transmit window is the range of + sequence numbers consumed by the source for sequentially numbering + and transmitting the most recent TXW_SECS of ODATA packets. The + trailing (or left) edge of the transmit window (TXW_TRAIL) is defined + as the sequence number of the oldest data packet available for repair + from a source. The leading (or right) edge of the transmit window + (TXW_LEAD) is defined as the sequence number of the most recent data + packet a source has transmitted. + + The size of the transmit window in sequence numbers (TXW_SQNS) (i.e., + the difference between the leading and trailing edges plus one) MUST + be no greater than half the PGM sequence number space less one. + + When TXW_TRAIL is equal to TXW_LEAD, the transmit window size is one. + When TXW_TRAIL is equal to TXW_LEAD plus one, the transmit window + size is empty. + +3.4. Receive Window + + The receive window at the receivers is determined entirely by PGM + packets from the source. That is, a receiver simply obeys what the + source tells it in terms of window state and advancement. + + For a given transport session identified by a TSI, a receiver + maintains: + + RXW_TRAIL the sequence number defining the trailing edge of the + receive window, the sequence number (known from data + packets and SPMs) of the oldest data packet available + for repair from the source + + + + + + +Speakman, et. al. Experimental [Page 13] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + RXW_LEAD the sequence number defining the leading edge of the + receive window, the greatest sequence number of any + received data packet within the transmit window + + The receive window is the range of sequence numbers a receiver is + expected to use to identify receivable ODATA. + + A data packet is described as being "in" the receive window if its + sequence number is in the receive window. + + The receive window is advanced by the receiver when it receives an + SPM or ODATA packet within the transmit window that increments + RXW_TRAIL. Receivers also advance their receive windows upon receipt + of any PGM data packet within the receive window that advances the + receive window. + +3.5. Source Path State + + To establish the repair state required to constrain RDATA, it's + essential that NAKs return from a receiver to a source on the reverse + of the distribution tree from the source. That is, they must return + through the same sequence of PGM network elements through which the + ODATA was forwarded, but in reverse. There are two reasons for this, + the less obvious one being by far the more important. + + The first and obvious reason is that RDATA is forwarded on the same + path as ODATA and so repair state must be established on this path if + it is to constrain the propagation of RDATA. + + The second and less obvious reason is that in the absence of repair + state, PGM network elements do NOT forward RDATA, so the default + behavior is to discard repairs. If repair state is not properly + established for interfaces on which ODATA went missing, then + receivers on those interfaces will continue to NAK for lost data and + ultimately experience unrecoverable data loss. + + The principle function of SPMs is to provide the source path state + required for PGM network elements to forward NAKs from one PGM + network element to the next on the reverse of the distribution tree + for the TSI, establishing repair state each step of the way. This + source path state is simply the address of the upstream PGM network + element on the reverse of the distribution tree for the TSI. That + upstream PGM network element may be more than one subnet hop away. + SPMs establish the identity of the upstream PGM network element on + the distribution tree for each TSI in each group in each PGM network + element, a sort of virtual PGM topology. So although NAKs are + unicast addressed, they are NOT unicast routed by PGM network + elements in the conventional sense. Instead PGM network elements use + + + +Speakman, et. al. Experimental [Page 14] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + the source path state established by SPMs to direct NAKs PGM-hop-by- + PGM-hop toward the source. The idea is to constrain NAKs to the pure + PGM topology spanning the more heterogeneous underlying topology of + both PGM and non-PGM network elements. + + The result is repair state in every PGM network element between the + receiver and the source so that the corresponding RDATA is never + discarded by a PGM network element for lack of repair state. + + SPMs also maintain transmit window state in receivers by advertising + the trailing and leading edges of the transmit window (SPM_TRAIL and + SPM_LEAD). In the absence of data, SPMs MAY be used to close the + transmit window in time by advancing the transmit window until + SPM_TRAIL is equal to SPM_LEAD plus one. + +3.6. Packet Contents + + This section just provides enough short-hand to make the Procedures + intelligible. For the full details of packet contents, please refer + to Packet Formats below. + +3.6.1. Source Path Messages + +3.6.1.1. SPMs + + SPMs are transmitted by sources to establish source-path state in PGM + network elements, and to provide transmit-window state in receivers. + + SPMs are multicast to the group and contain: + + SPM_TSI the source-assigned TSI for the session to which the + SPM corresponds + + SPM_SQN a sequence number assigned sequentially by the source + in unit increments and scoped by SPM_TSI + + Nota Bene: this is an entirely separate sequence than is used to + number ODATA and RDATA. + + SPM_TRAIL the sequence number defining the trailing edge of the + source's transmit window (TXW_TRAIL) + + SPM_LEAD the sequence number defining the leading edge of the + source's transmit window (TXW_LEAD) + + SPM_PATH the network-layer address (NLA) of the interface on + the PGM network element on which the SPM is forwarded + + + + +Speakman, et. al. Experimental [Page 15] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +3.6.2. Data Packets + +3.6.2.1. ODATA - Original Data + + ODATA packets are transmitted by sources to send application data to + receivers. + + ODATA packets are multicast to the group and contain: + + OD_TSI the globally unique source-assigned TSI + + OD_TRAIL the sequence number defining the trailing edge of the + source's transmit window (TXW_TRAIL) + + OD_TRAIL makes the protocol more robust in the face of + lost SPMs. By including the trailing edge of the + transmit window on every data packet, receivers that + have missed any SPMs that advanced the transmit window + can still detect the case, recover the application, + and potentially re-synchronize to the transport + session. + + OD_SQN a sequence number assigned sequentially by the source + in unit increments and scoped by OD_TSI + +3.6.2.2. RDATA - Repair Data + + RDATA packets are repair packets transmitted by sources or DLRs in + response to NAKs. + + RDATA packets are multicast to the group and contain: + + RD_TSI OD_TSI of the ODATA packet for which this is a repair + + RD_TRAIL the sequence number defining the trailing edge of the + source's transmit window (TXW_TRAIL). This is updated + to the most current value when the repair is sent, so + it is not necessarily the same as OD_TRAIL of the + ODATA packet for which this is a repair + + RD_SQN OD_SQN of the ODATA packet for which this is a repair + +3.6.3. Negative Acknowledgments + +3.6.3.1. NAKs - Negative Acknowledgments + + NAKs are transmitted by receivers to request repairs for missing data + packets. + + + +Speakman, et. al. Experimental [Page 16] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + NAKs are unicast (PGM-hop-by-PGM-hop) to the source and contain: + + NAK_TSI OD_TSI of the ODATA packet for which a repair is + requested + + NAK_SQN OD_SQN of the ODATA packet for which a repair is + requested + + NAK_SRC the unicast NLA of the original source of the missing + ODATA. + + NAK_GRP the multicast group NLA + +3.6.3.2. NNAKs - Null Negative Acknowledgments + + NNAKs are transmitted by a DLR that receives NAKs redirected to it by + either receivers or network elements to provide flow-control feed- + back to a source. + + NNAKs are unicast (PGM-hop-by-PGM-hop) to the source and contain: + + NNAK_TSI NAK_TSI of the corresponding re-directed NAK. + + NNAK_SQN NAK_SQN of the corresponding re-directed NAK. + + NNAK_SRC NAK_SRC of the corresponding re-directed NAK. + + NNAK_GRP NAK_GRP of the corresponding re-directed NAK. + +3.6.4. Negative Acknowledgment Confirmations + +3.6.4.1. NCFs - NAK confirmations + + NCFs are transmitted by network elements and sources in response to + NAKs. + + NCFs are multicast to the group and contain: + + NCF_TSI NAK_TSI of the NAK being confirmed + + NCF_SQN NAK_SQN of the NAK being confirmed + + NCF_SRC NAK_SRC of the NAK being confirmed + + NCF_GRP NAK_GRP of the NAK being confirmed + + + + + + +Speakman, et. al. Experimental [Page 17] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +3.6.5. Option Encodings + + OPT_LENGTH 0x00 - Option's Length + + OPT_FRAGMENT 0x01 - Fragmentation + + OPT_NAK_LIST 0x02 - List of NAK entries + + OPT_JOIN 0x03 - Late Joining + + OPT_REDIRECT 0x07 - Redirect + + OPT_SYN 0x0D - Synchronization + + OPT_FIN 0x0E - Session Fin receivers, conventional + feedbackish + + OPT_RST 0x0F - Session Reset + + OPT_PARITY_PRM 0x08 - Forward Error Correction Parameters + + OPT_PARITY_GRP 0x09 - Forward Error Correction Group Number + + OPT_CURR_TGSIZE 0x0A - Forward Error Correction Group Size + + OPT_CR 0x10 - Congestion Report + + OPT_CRQST 0x11 - Congestion Report Request + + OPT_NAK_BO_IVL 0x04 - NAK Back-Off Interval + + OPT_NAK_BO_RNG 0x05 - NAK Back-Off Range + + OPT_NBR_UNREACH 0x0B - Neighbor Unreachable + + OPT_PATH_NLA 0x0C - Path NLA + + OPT_INVALID 0x7F - Option invalidated + +4. Procedures - General + + Since SPMs, NCFs, and RDATA must be treated conditionally by PGM + network elements, they must be distinguished from other packets in + the chosen multicast network protocol if PGM network elements are to + extract them from the usual switching path. + + + + + + +Speakman, et. al. Experimental [Page 18] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + The most obvious way for network elements to achieve this is to + examine every packet in the network for the PGM transport protocol + and packet types. However, the overhead of this approach is costly + for high-performance, multi-protocol network elements. An + alternative, and a requirement for PGM over IP multicast, is that + SPMs, NCFs, and RDATA MUST be transmitted with the IP Router Alert + Option [6]. This option gives network elements a network-layer + indication that a packet should be extracted from IP switching for + more detailed processing. + +5. Procedures - Sources + +5.1. Data Transmission + + Since PGM relies on a purely rate-limited transmission strategy in + the source to bound the bandwidth consumed by PGM transport sessions, + an assortment of techniques is assembled here to make that strategy + as conservative and robust as possible. These techniques are the + minimum REQUIRED of a PGM source. + +5.1.1. Maximum Cumulative Transmit Rate + + A source MUST number ODATA packets in the order in which they are + submitted for transmission by the application. A source MUST + transmit ODATA packets in sequence and only within the transmit + window beginning with TXW_TRAIL at no greater a rate than + TXW_MAX_RTE. + + TXW_MAX_RTE is typically the maximum cumulative transmit rate of SPM, + ODATA, and RDATA. Different transmission strategies MAY define + TXW_MAX_RTE as appropriate for the implementation. + +5.1.2. Transmit Rate Regulation + + To regulate its transmit rate, a source MUST use a token bucket + scheme or any other traffic management scheme that yields equivalent + behavior. A token bucket [7] is characterized by a continually + sustainable data rate (the token rate) and the extent to which the + data rate may exceed the token rate for short periods of time (the + token bucket size). Over any arbitrarily chosen interval, the number + of bytes the source may transmit MUST NOT exceed the token bucket + size plus the product of the token rate and the chosen interval. + + In addition, a source MUST bound the maximum rate at which successive + packets may be transmitted using a leaky bucket scheme drained at a + maximum transmit rate, or equivalent mechanism. + + + + + +Speakman, et. al. Experimental [Page 19] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +5.1.3. Outgoing Packet Ordering + + To preserve the logic of PGM's transmit window, a source MUST + strictly prioritize sending of pending NCFs first, pending SPMs + second, and only send ODATA or RDATA when no NCFs or SPMs are + pending. The priority of RDATA versus ODATA is application + dependent. The sender MAY implement weighted bandwidth sharing + between RDATA and ODATA. Note that strict prioritization of RDATA + over ODATA may stall progress of ODATA if there are receivers that + keep generating NAKs so as to always have RDATA pending (e.g. a + steady stream of late joiners with OPT_JOIN). Strictly prioritizing + ODATA over RDATA may lead to a larger portion of receivers getting + unrecoverable losses. + +5.1.4. Ambient SPMs + + Interleaved with ODATA and RDATA, a source MUST transmit SPMs at a + rate at least sufficient to maintain current source path state in PGM + network elements. Note that source path state in network elements + does not track underlying changes in the distribution tree from a + source until an SPM traverses the altered distribution tree. The + consequence is that NAKs may go unconfirmed both at receivers and + amongst network elements while changes in the underlying distribution + tree take place. + +5.1.5. Heartbeat SPMs + + In the absence of data to transmit, a source SHOULD transmit SPMs at + a decaying rate in order to assist early detection of lost data, to + maintain current source path state in PGM network elements, and to + maintain current receive window state in the receivers. + + In this scheme [8], a source maintains an inter-heartbeat timer + IHB_TMR which times the interval between the most recent packet + (ODATA, RDATA, or SPM) transmission and the next heartbeat + transmission. IHB_TMR is initialized to a minimum interval IHB_MIN + after the transmission of any data packet. If IHB_TMR expires, the + source transmits a heartbeat SPM and initializes IHB_TMR to double + its previous value. The transmission of consecutive heartbeat SPMs + doubles IHB each time up to a maximum interval IHB_MAX. The + transmission of any data packet initializes IHB_TMR to IHB_MIN once + again. The effect is to provoke prompt detection of missing packets + in the absence of data to transmit, and to do so with minimal + bandwidth overhead. + + + + + + + +Speakman, et. al. Experimental [Page 20] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +5.1.6. Ambient and Heartbeat SPMs + + Ambient and heartbeat SPMs are described as driven by separate timers + in this specification to highlight their contrasting functions. + Ambient SPMs are driven by a count-down timer that expires regularly + while heartbeat SPMs are driven by a count-down timer that keeps + being reset by data, and the interval of which changes once it begins + to expire. The ambient SPM timer is just counting down in real-time + while the heartbeat timer is measuring the inter-data-packet + interval. + + In the presence of data, no heartbeat SPMs will be transmitted since + the transmission of data keeps setting the IHB_TMR back to its + initial value. At the same time however, ambient SPMs MUST be + interleaved into the data as a matter of course, not necessarily as a + heartbeat mechanism. This ambient transmission of SPMs is REQUIRED + to keep the distribution tree information in the network current and + to allow new receivers to synchronize with the session. + + An implementation SHOULD de-couple ambient and heartbeat SPM timers + sufficiently to permit them to be configured independently of each + other. + +5.2. Negative Acknowledgment Confirmation + + A source MUST immediately multicast an NCF in response to any NAK it + receives. The NCF is REQUIRED since the alternative of responding + immediately with RDATA would not allow other PGM network elements on + the same subnet to do NAK anticipation, nor would it allow DLRs on + the same subnet to provide repairs. A source SHOULD be able to + detect a NAK storm and adopt countermeasure to protect the network + against a denial of service. A possible countermeasure is to send + the first NCF immediately in response to a NAK and then delay the + generation of further NCFs (for identical NAKs) by a small interval, + so that identical NCFs are rate-limited, without affecting the + ability to suppress NAKs. + +5.3. Repairs + + After multicasting an NCF in response to a NAK, a source MUST then + multicast RDATA (while respecting TXW_MAX_RTE) in response to any NAK + it receives for data packets within the transmit window. + + In the interest of increasing the efficiency of a particular RDATA + packet, a source MAY delay RDATA transmission to accommodate the + arrival of NAKs from the whole loss neighborhood. This delay SHOULD + not exceed twice the greatest propagation delay in the loss + neighborhood. + + + +Speakman, et. al. Experimental [Page 21] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +6. Procedures - Receivers + +6.1. Data Reception + + Initial data reception + + A receiver SHOULD initiate data reception beginning with the first + data packet it receives within the advertised transmit window. This + packet's sequence number (ODATA_SQN) temporarily defines the trailing + edge of the transmit window from the receiver's perspective. That + is, it is assigned to RXW_TRAIL_INIT within the receiver, and until + the trailing edge sequence number advertised in subsequent packets + (SPMs or ODATA or RDATA) increments past RXW_TRAIL_INIT, the receiver + MUST only request repairs for sequence numbers subsequent to + RXW_TRAIL_INIT. Thereafter, it MAY request repairs anywhere in the + transmit window. This temporary restriction on repair requests + prevents receivers from requesting a potentially large amount of + history when they first begin to receive a given PGM transport + session. + + Note that the JOIN option, discussed later, MAY be used to provide a + different value for RXW_TRAIL_INIT. + + Receiving and discarding data packets + + Within a given transport session, a receiver MUST accept any ODATA or + RDATA packets within the receive window. A receiver MUST discard any + data packet that duplicates one already received in the transmit + window. A receiver MUST discard any data packet outside of the + receive window. + + Contiguous data + + Contiguous data is comprised of those data packets within the receive + window that have been received and are in the range from RXW_TRAIL up + to (but not including) the first missing sequence number in the + receive window. The most recently received data packet of contiguous + data defines the leading edge of contiguous data. + + As its default mode of operation, a receiver MUST deliver only + contiguous data packets to the application, and it MUST do so in the + order defined by those data packets' sequence numbers. This provides + applications with a reliable ordered data flow. + + + + + + + + +Speakman, et. al. Experimental [Page 22] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Non contiguous data + + PGM receiver implementations MAY optionally provide a mode of + operation in which data is delivered to an application in the order + received. However, the implementation MUST only deliver complete + application protocol data units (APDUs) to the application. That is, + APDUs that have been fragmented into different TPDUs MUST be + reassembled before delivery to the application. + +6.2. Source Path Messages + + Receivers MUST receive and sequence SPMs for any TSI they are + receiving. An SPM is in sequence if its sequence number is greater + than that of the most recent in-sequence SPM and within half the PGM + number space. Out-of-sequence SPMs MUST be discarded. + + For each TSI, receivers MUST use the most recent SPM to determine the + NLA of the upstream PGM network element for use in NAK addressing. A + receiver MUST NOT initiate repair requests until it has received at + least one SPM for the corresponding TSI. + + Since SPMs require per-hop processing, it is likely that they will be + forwarded at a slower rate than data, and that they will arrive out + of sync with the data stream. In this case, the window information + that the SPMs carry will be out of date. Receivers SHOULD expect + this to be the case and SHOULD detect it by comparing the packet lead + and trail values with the values the receivers have stored for lead + and trail. If the SPM packet values are less, they SHOULD be + ignored, but the rest of the packet SHOULD be processed as normal. + +6.3. Data Recovery by Negative Acknowledgment + + Detecting missing data packets + + Receivers MUST detect gaps in the expected data sequence in the + following manners: + + by comparing the sequence number on the most recently received + ODATA or RDATA packet with the leading edge of contiguous data + + by comparing SPM_LEAD of the most recently received SPM with the + leading edge of contiguous data + + In both cases, if the receiver has not received all intervening data + packets, it MAY initiate selective NAK generation for each missing + sequence number. + + + + + +Speakman, et. al. Experimental [Page 23] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + In addition, a receiver may detect a single missing data packet by + receiving an NCF or multicast NAK for a data packet within the + transmit window which it has not received. In this case it MAY + initiate selective NAK generation for the said sequence number. + + In all cases, receivers SHOULD temper the initiation of NAK + generation to account for simple mis-ordering introduced by the + network. A possible mechanism to achieve this is to assume loss only + after the reception of N packets with sequence numbers higher than + those of the (assumed) lost packets. A possible value for N is 2. + This method SHOULD be complemented with a timeout based mechanism + that handles the loss of the last packet before a pause in the + transmission of the data stream. The leading edge field in SPMs + SHOULD also be taken into account in the loss detection algorithm. + + Generating NAKs + + NAK generation follows the detection of a missing data packet and is + the cycle of: + + waiting for a random period of time (NAK_RB_IVL) while listening + for matching NCFs or NAKs + + transmitting a NAK if a matching NCF or NAK is not heard + + waiting a period (NAK_RPT_IVL) for a matching NCF and recommencing + NAK generation if the matching NCF is not received + + waiting a period (NAK_RDATA_IVL) for data and recommencing NAK + generation if the matching data is not received + + The entire generation process can be summarized by the following + state machine: + + + + + + + + + + + + + + + + + + +Speakman, et. al. Experimental [Page 24] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + | + | detect missing tpdu + | - clear data retry count + | - clear NCF retry count + V + matching NCF |--------------------------| + <---------------| BACK-OFF_STATE | <---------------------- + | | start timer(NAK_RB_IVL) | ^ ^ + | | | | | + | |--------------------------| | | + | matching | | timer expires | | + | NAK | | - send NAK | | + | | | | | + | V V | | + | |--------------------------| | | + | | WAIT_NCF_STATE | | | + | matching NCF | start timer(NAK_RPT_IVL) | | | + |<--------------| |------------> | + | |--------------------------| timer expires | + | | | ^ - increment NCF | + | NAK_NCF_RETRIES | | | retry count | + | exceeded | | | | + | V ----------- | + | Cancelation matching NAK | + | - restart timer(NAK_RPT_IVL) | + | | + | | + V |--------------------------| | + --------------->| WAIT_DATA_STATE |-----------------------> + |start timer(NAK_RDATA_IVL)| timer expires + | | - increment data + |--------------------------| retry count + | | ^ + NAK_DATA_RETRIES | | | + exceeded | | | + | ----------- + | matching NCF or NAK + V - restart timer(NAK_RDATA_IVL) + Cancellation + + In any state, receipt of matching RDATA or ODATA completes data + recovery and successful exit from the state machine. State + transition stops any running timers. + + In any state, if the trailing edge of the window moves beyond the + sequence number, data recovery for that sequence number terminates. + + + + + +Speakman, et. al. Experimental [Page 25] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + During NAK_RB_IVL a NAK is said to be pending. When awaiting data or + an NCF, a NAK is said to be outstanding. + + Backing off NAK transmission + + Before transmitting a NAK, a receiver MUST wait some interval + NAK_RB_IVL chosen randomly over some time period NAK_BO_IVL. During + this period, receipt of a matching NAK or a matching NCF will suspend + NAK generation. NAK_RB_IVL is counted down from the time a missing + data packet is detected. + + A value for NAK_BO_IVL learned from OPT_NAK_BO_IVL (see 16.4.1 below) + MUST NOT be used by a receiver (i.e., the receiver MUST NOT NAK) + unless either NAK_BO_IVL_SQN is zero, or the receiver has seen + POLL_RND == 0 for POLL_SQN =< NAK_BO_IVL_SQN within half the sequence + number space. + + When a parity NAK (Appendix A, FEC) is being generated, the back-off + interval SHOULD be inversely biased with respect to the number of + parity packets requested. This way NAKs requesting larger numbers of + parity packets are likely to be sent first and thus suppress other + NAKs. A NAK for a given transmission group suppresses another NAK + for the same transmission group only if it is requesting an equal or + larger number of parity packets. + + When a receiver has to transmit a sequence of NAKs, it SHOULD + transmit the NAKs in order from oldest to most recent. + + Suspending NAK generation + + Suspending NAK generation just means waiting for either NAK_RB_IVL, + NAK_RPT_IVL or NAK_RDATA_IVL to pass. A receiver MUST suspend NAK + generation if a duplicate of the NAK is already pending from this + receiver or the NAK is already outstanding from this or another + receiver. + + NAK suppression + + A receiver MUST suppress NAK generation and wait at least + NAK_RDATA_IVL before recommencing NAK generation if it hears a + matching NCF or NAK during NAK_RB_IVL. A matching NCF must match + NCF_TSI with NAK_TSI, and NCF_SQN with NAK_SQN. + + Transmitting a NAK + + Upon expiry of NAK_RB_IVL, a receiver MUST unicast a NAK to the + upstream PGM network element for the TSI specifying the transport + session identifier and missing sequence number. In addition, it MAY + + + +Speakman, et. al. Experimental [Page 26] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + multicast a NAK with TTL of 1 to the group, if the PGM parent is not + directly connected. It also records both the address of the source + of the corresponding ODATA and the address of the group in the NAK + header. + + It MUST repeat the NAK at a rate governed by NAK_RPT_IVL up to + NAK_NCF_RETRIES times while waiting for a matching NCF. It MUST then + wait NAK_RDATA_IVL before recommencing NAK generation. If it hears a + matching NCF or NAK during NAK_RDATA_IVL, it MUST wait anew for + NAK_RDATA_IVL before recommencing NAK generation (i.e. matching NCFs + and NAKs restart NAK_RDATA_IVL). + + Completion of NAK generation + + NAK generation is complete only upon the receipt of the matching + RDATA (or even ODATA) packet at any time during NAK generation. + + Cancellation of NAK generation + + NAK generation is cancelled upon the advancing of the receive window + so as to exclude the matching sequence number of a pending or + outstanding NAK, or NAK_DATA_RETRIES / NAK_NCF_RETRIES being + exceeded. Cancellation of NAK generation indicates unrecoverable + data loss. + + Receiving NCFs and multicast NAKs + + A receiver MUST discard any NCFs or NAKs it hears for data packets + outside the transmit window or for data packets it has received. + Otherwise they are treated as appropriate for the current repair + state. + +7. Procedures - Network Elements + +7.1. Source Path State + + Upon receipt of an in-sequence SPM, a network element records the + Source Path Address SPM_PATH with the multicast routing information + for the TSI. If the receiving network element is on the same subnet + as the forwarding network element, this address will be the same as + the address of the immediately upstream network element on the + distribution tree for the TSI. If, however, non-PGM network elements + intervene between the forwarding and the receiving network elements, + this address will be the address of the first PGM network element + across the intervening network elements. + + + + + + +Speakman, et. al. Experimental [Page 27] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + The network element then forwards the SPM on each outgoing interface + for that TSI. As it does so, it encodes the network address of the + outgoing interface in SPM_PATH in each copy of the SPM it forwards. + +7.2. NAK Confirmation + + Network elements MUST immediately transmit an NCF in response to any + unicast NAK they receive. The NCF MUST be multicast to the group on + the interface on which the NAK was received. + + Nota Bene: In order to avoid creating multicast routing state for + PGM network elements across non-PGM-capable clouds, the network- + header source address of NCFs transmitted by network elements MUST + be set to the ODATA source's NLA, not the network element's NLA as + might be expected. + + Network elements should be able to detect a NAK storm and adopt + counter-measure to protect the network against a denial of service. + A possible countermeasure is to send the first NCF immediately in + response to a NAK and then delay the generation of further NCFs (for + identical NAKs) by a small interval, so that identical NCFs are + rate-limited, without affecting the ability to suppress NAKs. + + Simultaneously, network elements MUST establish repair state for the + NAK if such state does not already exist, and add the interface on + which the NAK was received to the corresponding repair interface list + if the interface is not already listed. + +7.3. Constrained NAK Forwarding + + The NAK forwarding procedures for network elements are quite similar + to those for receivers, but three important differences should be + noted. + + First, network elements do NOT back off before forwarding a NAK + (i.e., there is no NAK_BO_IVL) since the resulting delay of the NAK + would compound with each hop. Note that NAK arrivals will be + randomized by the receivers from which they originate, and this + factor in conjunction with NAK anticipation and elimination will + combine to forestall NAK storms on subnets with a dense network + element population. + + Second, network elements do NOT retry confirmed NAKs if RDATA is not + seen; they simply discard the repair state and rely on receivers to + re-request the repair. This approach keeps the repair state in the + network elements relatively ephemeral and responsive to underlying + routing changes. + + + + +Speakman, et. al. Experimental [Page 28] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Third, note that ODATA does NOT cancel NAK forwarding in network + elements since it is switched by network elements without transport- + layer intervention. + + Nota Bene: Once confirmed by an NCF, network elements discard NAK + packets; they are NOT retained in network elements beyond this + forwarding operation. + + NAK forwarding requires that a network element listen to NCFs for the + same transport session. NAK forwarding also requires that a network + element observe two time out intervals for any given NAK (i.e., per + NAK_TSI and NAK_SQN): NAK_RPT_IVL and NAK_RDATA_IVL. + + The NAK repeat interval NAK_RPT_IVL, limits the length of time for + which a network element will repeat a NAK while waiting for a + corresponding NCF. NAK_RPT_IVL is counted down from the transmission + of a NAK. Expiry of NAK_RPT_IVL cancels NAK forwarding (due to + missing NCF). + + The NAK RDATA interval NAK_RDATA_IVL, limits the length of time for + which a network element will wait for the corresponding RDATA. + NAK_RDATA_IVL is counted down from the time a matching NCF is + received. Expiry of NAK_RDATA_IVL causes the network element to + discard the corresponding repair state (due to missing RDATA). + + During NAK_RPT_IVL, a NAK is said to be pending. During + NAK_RDATA_IVL, a NAK is said to be outstanding. + + A Network element MUST forward NAKs only to the upstream PGM network + element for the TSI. + + A network element MUST repeat a NAK at a rate of NAK_RPT_RTE for an + interval of NAK_RPT_IVL until it receives a matching NCF. A matching + NCF must match NCF_TSI with NAK_TSI, and NCF_SQN with NAK_SQN. + + Upon reception of the corresponding NCF, network elements MUST wait + at least NAK_RDATA_IVL for the corresponding RDATA. Receipt of the + corresponding RDATA at any time during NAK forwarding cancels NAK + forwarding and tears down the corresponding repair state in the + network element. + +7.4. NAK elimination + + Two NAKs duplicate each other if they bear the same NAK_TSI and + NAK_SQN. Network elements MUST discard all duplicates of a NAK that + is pending. + + + + + +Speakman, et. al. Experimental [Page 29] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Once a NAK is outstanding, network elements MUST discard all + duplicates of that NAK for NAK_ELIM_IVL. Upon expiry of + NAK_ELIM_IVL, network elements MUST suspend NAK elimination for that + TSI/SQN until the first duplicate of that NAK is seen after the + expiry of NAK_ELIM_IVL. This duplicate MUST be forwarded in the + usual manner. Once this duplicate NAK is outstanding, network + elements MUST once again discard all duplicates of that NAK for + NAK_ELIM_IVL, and so on. NAK_RDATA_IVL MUST be reset each time a NAK + for the corresponding TSI/SQN is confirmed (i.e., each time + NAK_ELIM_IVL is reset). NAK_ELIM_IVL MUST be some small fraction of + NAK_RDATA_IVL. + + NAK_ELIM_IVL acts to balance implosion prevention against repair + state liveness. That is, it results in the elimination of all but at + most one NAK per NAK_ELIM_IVL thereby allowing repeated NAKs to keep + the repair state alive in the PGM network elements. + +7.5. NAK Anticipation + + An unsolicited NCF is one that is received by a network element when + the network element has no corresponding pending or outstanding NAK. + Network elements MUST process unsolicited NCFs differently depending + on the interface on which they are received. + + If the interface on which an NCF is received is the same interface + the network element would use to reach the upstream PGM network + element, the network element simply establishes repair state for + NCF_TSI and NCF_SQN without adding the interface to the repair + interface list, and discards the NCF. If the repair state already + exists, the network element restarts the NAK_RDATA_IVL and + NAK_ELIM_IVL timers and discards the NCF. + + If the interface on which an NCF is received is not the same + interface the network element would use to reach the upstream PGM + network element, the network element does not establish repair state + and just discards the NCF. + + Anticipated NAKs permit the elimination of any subsequent matching + NAKs from downstream. Upon establishing anticipated repair state, + network elements MUST eliminate subsequent NAKs only for a period of + NAK_ELIM_IVL. Upon expiry of NAK_ELIM_IVL, network elements MUST + suspend NAK elimination for that TSI/SQN until the first duplicate of + that NAK is seen after the expiry of NAK_ELIM_IVL. This duplicate + MUST be forwarded in the usual manner. Once this duplicate NAK is + outstanding, network elements MUST once again discard all duplicates + of that NAK for NAK_ELIM_IVL, and so on. NAK_RDATA_IVL MUST be reset + + + + + +Speakman, et. al. Experimental [Page 30] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + each time a NAK for the corresponding TSI/SQN is confirmed (i.e., + each time NAK_ELIM_IVL is reset). NAK_ELIM_IVL must be some small + fraction of NAK_RDATA_IVL. + +7.6. NAK Shedding + + Network elements MAY implement local procedures for withholding NAK + confirmations for receivers detected to be reporting excessive loss. + The result of these procedures would ultimately be unrecoverable data + loss in the receiver. + +7.7. Addressing NAKs + + A PGM network element uses the source and group addresses (NLAs) + contained in the transport header to find the state for the + corresponding TSI, looks up the corresponding upstream PGM network + element's address, uses it to re-address the (unicast) NAK, and + unicasts it on the upstream interface for the distribution tree for + the TSI. + +7.8. Constrained RDATA Forwarding + + Network elements MUST maintain repair state for each interface on + which a given NAK is received at least once. Network elements MUST + then use this list of interfaces to constrain the forwarding of the + corresponding RDATA packet only to those interfaces in the list. An + RDATA packet corresponds to a NAK if it matches NAK_TSI and NAK_SQN. + + Network elements MUST maintain this repair state only until either + the corresponding RDATA is received and forwarded, or NAK_RDATA_IVL + passes after forwarding the most recent instance of a given NAK. + Thereafter, the corresponding repair state MUST be discarded. + + Network elements SHOULD discard and not forward RDATA packets for + which they have no repair state. Note that the consequence of this + procedure is that, while it constrains repairs to the interested + subset of the network, loss of repair state precipitates further NAKs + from neglected receivers. + +8. Packet Formats + + All of the packet formats described in this section are transport- + layer headers that MUST immediately follow the network-layer header + in the packet. Only data packet headers (ODATA and RDATA) may be + followed in the packet by application data. For each packet type, + the network-header source and destination addresses are specified in + + + + + +Speakman, et. al. Experimental [Page 31] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + addition to the format and contents of the transport layer header. + Recall from General Procedures that, for PGM over IP multicast, SPMs, + NCFs, and RDATA MUST also bear the IP Router Alert Option. + + For PGM over IP, the IP protocol number is 113. + + In all packets the descriptions of Data-Source Port, Data-Destination + Port, Type, Options, Checksum, Global Source ID (GSI), and Transport + Service Data Unit (TSDU) Length are: + + Data-Source Port: + + A random port number generated by the source. This port number + MUST be unique within the source. Source Port together with + Global Source ID forms the TSI. + + Data-Destination Port: + + A globally well-known port number assigned to the given PGM + application. + + Type: + + The high-order two bits of the Type field encode a version + number, 0x0 in this instance. The low-order nibble of the type + field encodes the specific packet type. The intervening two + bits (the low-order two bits of the high-order nibble) are + reserved and MUST be zero. + + Within the low-order nibble of the Type field: + + values in the range 0x0 through 0x3 represent SPM-like + packets (i.e., session-specific, sourced by a source, + periodic), + + values in the range 0x4 through 0x7 represent DATA-like + packets (i.e., data and repairs), + + values in the range 0x8 through 0xB represent NAK-like + packets (i.e., hop-by-hop reliable NAK forwarding + procedures), + + and values in the range 0xC through 0xF represent SPMR-like + packets (i.e., session-specific, sourced by a receiver, + asynchronous). + + + + + + +Speakman, et. al. Experimental [Page 32] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Options: + + This field encodes binary indications of the presence and + significance of any options. It also directly encodes some + options. + + bit 0 set => One or more Option Extensions are present + + bit 1 set => One or more Options are network-significant + + Note that this bit is clear when OPT_FRAGMENT and/or + OPT_JOIN are the only options present. + + bit 6 set => Packet is a parity packet for a transmission group + of variable sized packets (OPT_VAR_PKTLEN). Only present when + OPT_PARITY is also present. + + bit 7 set => Packet is a parity packet (OPT_PARITY) + + Bits are numbered here from left (0 = MSB) to right (7 = LSB). + + All the other options (option extensions) are encoded in + extensions to the PGM header. + + Checksum: + + This field is the usual 1's complement of the 1's complement + sum of the entire PGM packet including header. + + The checksum does not include a network-layer pseudo header for + compatibility with network address translation. If the + computed checksum is zero, it is transmitted as all ones. A + value of zero in this field means the transmitter generated no + checksum. + + Note that if any entity between a source and a receiver + modifies the PGM header for any reason, it MUST either + recompute the checksum or clear it. The checksum is mandatory + on data packets (ODATA and RDATA). + + Global Source ID: + + A globally unique source identifier. This ID MUST NOT change + throughout the duration of the transport session. A + RECOMMENDED identifier is the low-order 48 bits of the MD5 [9] + signature of the DNS name of the source. Global Source ID + together with Data-Source Port forms the TSI. + + + + +Speakman, et. al. Experimental [Page 33] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + TSDU Length: + + The length in octets of the transport data unit exclusive of + the transport header. + + Note that those who require the TPDU length must obtain it from + sum of the transport header length (TH) and the TSDU length. + TH length is the sum of the size of the particular PGM packet + header (type_specific_size) plus the length of any options that + might be present. + + Address Family Indicators (AFIs) are as specified in [10]. + +8.1. Source Path Messages + + SPMs are sent by a source to establish source path state in network + elements and to provide transmit window state to receivers. + + The network-header source address of an SPM is the unicast NLA of the + entity that originates the SPM. + + The network-header destination address of an SPM is a multicast group + NLA. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Source Port | Destination Port | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Options | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Global Source ID ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ... Global Source ID | TSDU Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SPM's Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Trailing Edge Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Leading Edge Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NLA AFI | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Path NLA ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + | Option Extensions when present ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + + +Speakman, et. al. Experimental [Page 34] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Source Port: + + SPM_SPORT + + Data-Source Port, together with SPM_GSI forms SPM_TSI + + Destination Port: + + SPM_DPORT + + Data-Destination Port + + Type: + + SPM_TYPE = 0x00 + + Global Source ID: + + SPM_GSI + + Together with SPM_SPORT forms SPM_TSI + + SPM's Sequence Number + + SPM_SQN + + The sequence number assigned to the SPM by the source. + + Trailing Edge Sequence Number: + + SPM_TRAIL + + The sequence number defining the current trailing edge of the + source's transmit window (TXW_TRAIL). + + Leading Edge Sequence Number: + + SPM_LEAD + + The sequence number defining the current leading edge of the + source's transmit window (TXW_LEAD). + + If SPM_TRAIL == 0 and SPM_LEAD == 0x80000000, this indicates that + no window information is present in the packet. + + + + + + + +Speakman, et. al. Experimental [Page 35] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Path NLA: + + SPM_PATH + + The NLA of the interface on the network element on which this SPM + was forwarded. Initialized by a source to the source's NLA, + rewritten by each PGM network element upon forwarding. + +8.2. Data Packets + + Data packets carry application data from a source or a repairer to + receivers. + + ODATA: + + Original data packets transmitted by a source. + + RDATA: + + Repairs transmitted by a source or by a designated local + repairer (DLR) in response to a NAK. + + The network-header source address of a data packet is the unicast NLA + of the entity that originates the data packet. + + The network-header destination address of a data packet is a + multicast group NLA. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Source Port | Destination Port | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Options | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Global Source ID ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ... Global Source ID | TSDU Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Data Packet Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Trailing Edge Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Option Extensions when present ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Data ... + +-+-+- ... + + + + +Speakman, et. al. Experimental [Page 36] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Source Port: + + OD_SPORT, RD_SPORT + + Data-Source Port, together with Global Source ID forms: + + OD_TSI, RD_TSI + + Destination Port: + + OD_DPORT, RD_DPORT + + Data-Destination Port + + Type: + + OD_TYPE = 0x04 RD_TYPE = 0x05 + + Global Source ID: + + OD_GSI, RD_GSI + + Together with Source Port forms: + + OD_TSI, RD_TSI + + Data Packet Sequence Number: + + OD_SQN, RD_SQN + + The sequence number originally assigned to the ODATA packet by the + source. + + Trailing Edge Sequence Number: + + OD_TRAIL, RD_TRAIL + + The sequence number defining the current trailing edge of the + source's transmit window (TXW_TRAIL). In RDATA, this MAY not be + the same as OD_TRAIL of the ODATA packet for which it is a repair. + + Data: + + Application data. + + + + + + + +Speakman, et. al. Experimental [Page 37] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +8.3. Negative Acknowledgments and Confirmations + + NAK: + + Negative Acknowledgments are sent by receivers to request the + repair of an ODATA packet detected to be missing from the + expected sequence. + + N-NAK: + + Null Negative Acknowledgments are sent by DLRs to provide flow + control feedback to the source of ODATA for which the DLR has + provided the corresponding RDATA. + + The network-header source address of a NAK is the unicast NLA of the + entity that originates the NAK. The network-header source address of + NAK is rewritten by each PGM network element with its own. + + The network-header destination address of a NAK is initialized by the + originator of the NAK (a receiver) to the unicast NLA of the upstream + PGM network element known from SPMs. The network-header destination + address of a NAK is rewritten by each PGM network element with the + unicast NLA of the upstream PGM network element to which this NAK is + forwarded. On the final hop, the network-header destination address + of a NAK is rewritten by the PGM network element with the unicast NLA + of the original source or the unicast NLA of a DLR. + + NCF: + + NAK Confirmations are sent by network elements and sources to + confirm the receipt of a NAK. + + The network-header source address of an NCF is the ODATA source's + NLA, not the network element's NLA as might be expected. + + The network-header destination address of an NCF is a multicast group + NLA. + + Note that in NAKs and N-NAKs, unlike the other packets, the field + SPORT contains the Data-Destination port and the field DPORT contains + the Data-Source port. As a general rule, the content of SPORT/DPORT + is determined by the direction of the flow: in packets which travel + down-stream SPORT is the port number chosen in the data source + (Data-Source Port) and DPORT is the data destination port number + (Data-Destination Port). The opposite holds for packets which travel + upstream. This makes DPORT the protocol endpoint in the recipient + host, regardless of the direction of the packet. + + + + +Speakman, et. al. Experimental [Page 38] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Source Port | Destination Port | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Options | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Global Source ID ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ... Global Source ID | TSDU Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Requested Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NLA AFI | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Source NLA ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + | NLA AFI | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Multicast Group NLA ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + | Option Extensions when present ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... + + Source Port: + + NAK_SPORT, NNAK_SPORT + + Data-Destination Port + + NCF_SPORT + + Data-Source Port, together with Global Source ID forms NCF_TSI + + Destination Port: + + NAK_DPORT, NNAK_DPORT + + Data-Source Port, together with Global Source ID forms: + + NAK_TSI, NNAK_TSI + + NCF_DPORT + + Data-Destination Port + + + + + + +Speakman, et. al. Experimental [Page 39] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Type: + + NAK_TYPE = 0x08 NNAK_TYPE = 0x09 + + NCF_TYPE = 0x0A + + Global Source ID: + + NAK_GSI, NNAK_GSI, NCF_GSI + + Together with Data-Source Port forms + + NAK_TSI, NNAK_TSI, NCF_TSI + + Requested Sequence Number: + + NAK_SQN, NNAK_SQN + + NAK_SQN is the sequence number of the ODATA packet for which a + repair is requested. + + NNAK_SQN is the sequence number of the RDATA packet for which a + repair has been provided by a DLR. + + NCF_SQN + + NCF_SQN is NAK_SQN from the NAK being confirmed. + + Source NLA: + + NAK_SRC, NNAK_SRC, NCF_SRC + + The unicast NLA of the original source of the missing ODATA. + + Multicast Group NLA: + + NAK_GRP, NNAK_GRP, NCF_GRP + + The multicast group NLA. NCFs MAY bear OPT_REDIRECT and/or + OPT_NAK_LIST + +9. Options + + PGM specifies several end-to-end options to address specific + application requirements. PGM specifies options to support + fragmentation, late joining, and redirection. + + + + + +Speakman, et. al. Experimental [Page 40] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Options MAY be appended to PGM data packet headers only by their + original transmitters. While they MAY be interpreted by network + elements, options are neither added nor removed by network elements. + + Options are all in the TLV style, or Type, Length, Value. The Type + field is contained in the first byte, where bit 0 is the OPT_END bit, + followed by 7 bits of type. The OPT_END bit MUST be set in the last + option in the option list, whichever that might be. The Length field + is the total length of the option in bytes, and directly follows the + Type field. Following the Length field are 5 reserved bits, the + OP_ENCODED flag, the 2 Option Extensibility bits OPX and the + OP_ENCODED_NULL flag. Last are 7 bits designated for option specific + information which may be defined on a per-option basis. If not + defined for a particular option, they MUST be set to 0. + + The Option Extensibility bits dictate the desired treatment of an + option if it is unknown to the network element processing it. + + Nota Bene: Only network elements pay any attention to these bits. + + The OPX bits are defined as follows: + + 00 - Ignore the option + + 01 - Invalidate the option by changing the type to OPT_INVALID + = 0x7F + + 10 - Discard the packet + + 11 - Unsupported, and reserved for future use + + Some options present in data packet (ODATA and RDATA) are strictly + associated with the packet content (PGM payload), OPT_FRAGMENT being + an example. These options must be preserved even when the data + packet that would normally contain them is not received, but its the + payload is recovered though the use of FEC. PGM specifies a + mechanism to accomplish this that uses the F (OP_ENCODED) and U + (OP_ENCODED_NULL) bits in the option common header. OP_ENCODED and + OP_ENCODED_NULL MUST be normally set to zero except when the option + is used in FEC packets to preserve original options. See Appendix A + for details. + + There is a limit of 16 options per packet. + + + + + + + + +Speakman, et. al. Experimental [Page 41] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + General Option Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U|Opt. Specific| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Option Value ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+...+-+-+ + +9.1. Option extension length - OPT_LENGTH + + When option extensions are appended to the standard PGM header, the + extensions MUST be preceded by an option extension length field + specifying the total length of all option extensions. + + In addition, the presence of the options MUST be encoded in the + Options field of the standard PGM header before the Checksum is + computed. + + All network-significant options MUST be appended before any + exclusively receiver-significant options. + + To provide an indication of the end of option extensions, OPT_END + (0x80) MUST be set in the Option Type field of the trailing option + extension. + +9.1.1. OPT_LENGTH - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Option Type | Option Length | Total length of all options | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x00 + + Option Length = 4 octets + + Total length of all options + + The total length in octets of all option extensions including + OPT_LENGTH. + + OPT_LENGTH is NOT network-significant. + + + + + + +Speakman, et. al. Experimental [Page 42] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.2. Fragmentation Option - OPT_FRAGMENT + + Fragmentation allows transport-layer entities at a source to break up + application protocol data units (APDUs) into multiple PGM data + packets (TPDUs) to conform with the MTU supported by the network + layer. The fragmentation option MAY be applied to ODATA and RDATA + packets only. + + Architecturally, the accumulation of TSDUs into APDUs is applied to + TPDUs that have already been received, duplicate eliminated, and + contiguously sequenced by the receiver. Thus APDUs MAY be + reassembled across increments of the transmit window. + +9.2.1. OPT_FRAGMENT - Packet Extension Contents + + OPT_FRAG_OFF the offset of the fragment from the beginning of the + APDU + + OPT_FRAG_LEN the total length of the original APDU + +9.2.2. OPT_FRAGMENT - Procedures - Sources + + A source fragments APDUs into a contiguous series of fragments no + larger than the MTU supported by the network layer. A source + sequentially and uniquely assigns OD_SQNs to these fragments in the + order in which they occur in the APDU. A source then sets + OPT_FRAG_OFF to the value of the offset of the fragment in the + original APDU (where the first byte of the APDU is at offset 0, and + OPT_FRAG_OFF numbers the first byte in the fragment), and set + OPT_FRAG_LEN to the value of the total length of the original APDU. + +9.2.3. OPT_FRAGMENT - Procedures - Receivers + + Receivers detect and accumulate fragmented packets until they have + received an entire contiguous sequence of packets comprising an APDU. + This sequence begins with the fragment bearing OPT_FRAG_OFF of 0, and + terminates with the fragment whose length added to its OPT_FRAG_OFF + is OPT_FRAG_LEN. + + + + + + + + + + + + + +Speakman, et. al. Experimental [Page 43] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.2.4. OPT_FRAGMENT - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | First Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Offset | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x01 + + Option Length = 12 octets + + First Sequence Number + + Sequence Number of the PGM DATA/RDATA packet containing the first + fragment of the APDU. + + Offset + + The byte offset of the fragment from the beginning of the APDU + (OPT_FRAG_OFF). + + Length + + The total length of the original APDU (OPT_FRAG_LEN). + + OPT_FRAGMENT is NOT network-significant. + +9.3. NAK List Option - OPT_NAK_LIST + + The NAK List option MAY be used in conjunction with NAKs to allow + receivers to request transmission for more than one sequence number + with a single NAK packet. The option is limited to 62 listed NAK + entries. The NAK list MUST be unique and duplicate free. It MUST be + ordered, and MUST consist of either a list of selective or a list of + parity NAKs. In general, network elements, sources and receivers + must process a NAK list as if they had received individual NAKs for + each sequence number in the list. The procedures for each are + outlined in detail earlier in this document. Clarifications and + differences are detailed here. + + + + + +Speakman, et. al. Experimental [Page 44] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.3.1. OPT_NAK_LIST - Packet Extensions Contents + + A list of sequence numbers for which retransmission is requested. + +9.3.2. OPT_NAK_LIST - Procedures - Receivers + + Receivers MAY append the NAK List option to a NAK to indicate that + they wish retransmission of a number of RDATA. + + Receivers SHOULD proceed to back off NAK transmission in a manner + consistent with the procedures outlined for single sequence number + NAKs. Note that the repair of each separate sequence number will be + completed upon receipt of a separate RDATA packet. + + Reception of an NCF or multicast NAK containing the NAK List option + suspends generation of NAKs for all sequence numbers within the NAK + list, as well as the sequence number within the NAK header. + +9.3.3. OPT_NAK_LIST - Procedures - Network Elements + + Network elements MUST immediately respond to a NAK with an identical + NCF containing the same NAK list as the NAK itself. + + Network elements MUST forward a NAK containing a NAK List option if + any one sequence number specified by the NAK (including that in the + main NAK header) is not currently outstanding. That is, it MUST + forward the NAK, if any one sequence number does not have an + elimination timer running for it. The NAK must be forwarded intact. + + Network elements MUST eliminate a NAK containing the NAK list option + only if all sequence numbers specified by the NAK (including that in + the main NAK header) are outstanding. That is, they are all running + an elimination timer. + + Upon receipt of an unsolicited NCF containing the NAK list option, a + network element MUST anticipate data for every sequence number + specified by the NAK as if it had received an NCF for every sequence + number specified by the NAK. + +9.3.4. OPT_NAK_LIST - Procedures - Sources + + A source MUST immediately respond to a NAK with an identical NCF + containing the same NAK list as the NAK itself. + + It MUST then multicast RDATA (while respecting TXW_MAX_RTE) for every + requested sequence number. + + + + + +Speakman, et. al. Experimental [Page 45] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.3.5. OPT_NAK_LIST - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Requested Sequence Number 1 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ..... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Requested Sequence Number N | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x02 + + Option Length = 4 + (4 * number of SQNs) octets + + Requested Sequence Number + + A list of up to 62 additional sequence numbers to which the NAK + applies. + + OPT_NAK_LIST is network-significant. + +9.4. Late Joining Option - OPT_JOIN + + Late joining allows a source to bound the amount of repair history + receivers may request when they initially join a particular transport + session. + + This option indicates that receivers that join a transport session in + progress MAY request repair of all data as far back as the given + minimum sequence number from the time they join the transport + session. The default is for receivers to receive data only from the + first packet they receive and onward. + +9.4.1. OPT_JOIN - Packet Extensions Contents + + OPT_JOIN_MIN the minimum sequence number for repair + +9.4.2. OPT_JOIN - Procedures - Receivers + + If a PGM packet (ODATA, RDATA, or SPM) bears OPT_JOIN, a receiver MAY + initialize the trailing edge of the receive window (RXW_TRAIL_INIT) + to the given Minimum Sequence Number and proceeds with normal data + reception. + + + + +Speakman, et. al. Experimental [Page 46] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.4.3. OPT_JOIN - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Minimum Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + Option Type = 0x03 + + Option Length = 8 octets + + Minimum Sequence Number + + The minimum sequence number defining the initial trailing edge of + the receive window for a late joining receiver. + + OPT_JOIN is NOT network-significant. + +9.5. Redirect Option - OPT_REDIRECT + + Redirection MAY be used by a designated local repairer (DLR) to + advertise its own address as an alternative to the original source, + for requesting repairs. + + These procedures allow a PGM Network Element to use a DLR that is one + PGM hop from it either upstream or downstream in the multicast + distribution tree. The former are referred to as upstream DLRs. The + latter are referred to as off-tree DLRs. Off-Tree because even + though they are downstream of the point of loss, they might not lie + on the subtree affected by the loss. + + A DLR MUST receive any PGM sessions for which it wishes to provide + retransmissions. A DLR SHOULD respond to NCFs or POLLs sourced by + its PGM parent with a redirecting POLR response packet containing an + OPT_REDIRECT which provides its own network layer address. + Recipients of redirecting POLRs MAY then direct NAKs for subsequent + ODATA sequence numbers to the DLR rather than to the original source. + In addition, DLRs that receive redirected NAKs for which they have + RDATA MUST send a NULL NAK to provide flow control to the original + source without also provoking a repair from that source. + + + + + + + +Speakman, et. al. Experimental [Page 47] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.5.1. OPT_REDIRECT - Packet Extensions Contents + + OPT_REDIR_NLA the DLR's own unicast network-layer address to which + recipients of the redirecting POLR MAY direct + subsequent NAKs for the corresponding TSI. + +9.5.2. OPT_REDIRECT - Procedures - DLRs + + A DLR MUST receive any PGM sessions for which it wishes to provide a + source of repairs. In addition to acting as an ordinary PGM + receiver, a DLR MAY then respond to NCFs or relevant POLLs sourced by + parent network elements (or even by the source itself) by sending a + POLR containing an OPT_REDIRECT providing its own network-layer + address. + + If a DLR can provide FEC repairs it MUST denote this by setting + OPT_PARITY in the PGM header of its POLR response. + +9.5.2.1. Upstream DLRs + + If the NCF completes NAK transmission initiated by the DLR itself, + the DLR MUST NOT send a redirecting POLR. + + When a DLR receives an NCF from its upstream PGM parent, it SHOULD + send a redirecting POLR, multicast to the group. The DLR SHOULD + record that it is acting as an upstream DLR for the said session. + Note that this POLR MUST have both the data source's source address + and the router alert option in its network header. + + An upstream DLR MUST act as an ordinary PGM source in responding to + any NAK it receives (i.e., directed to it). That is, it SHOULD + respond first with a normal NCF and then RDATA as usual. In + addition, an upstream DLR that receives redirected NAKs for which it + has RDATA MUST send a NULL NAK to provide flow control to the + original source. If it cannot provide the RDATA it forwards the NAK + to the upstream PGM neighbor as usual. + + Nota Bene: In order to propagate on exactly the same distribution + tree as ODATA, RDATA and POLR packets transmitted by DLRs MUST + bear the ODATA source's NLA as the network-header source address, + not the DLR's NLA as might be expected. + + + + + + + + + + +Speakman, et. al. Experimental [Page 48] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.5.2.2. Off-Tree DLRs + + A DLR that receives a POLL with sub-type PGM_POLL_DLR MUST respond + with a unicast redirecting POLR if it provides the appropriate + service. The DLR SHOULD respond using the rules outlined for polling + in Appendix D of this text. If the DLR responds, it SHOULD record + that it is acting as an off-tree DLR for the said session. + + An off-tree DLR acts in a special way in responding to any NAK it + receives (i.e., directed to it). It MUST respond to a NAK directed + to it from its parent by unicasting an NCF and RDATA to its parent. + The parent will then forward the RDATA down the distribution tree. + The DLR uses its own and the parent's NLA addresses in the network + header for the source and destination respectively. The unicast NCF + and RDATA packets SHOULD not have the router alert option. In all + other ways the RDATA header should be "as if" the packet had come + from the source. + + Again, an off-tree DLR that receives redirected NAKs for which it has + RDATA MUST originate a NULL NAK to provide flow control to the + original source. It MUST originate the NULL NAK before originating + the RDATA. This must be done to reduce the state held in the network + element. + + If it cannot provide the RDATA for a given NAK, an off-tree DLR + SHOULD confirm the NAK with a unicast NCF as normal, then immediately + send a NAK for the said data packet back to its parent. + +9.5.2.3. Simultaneous Upstream and Off-Tree DLR operation + + Note that it is possible for a DLR to provide service to its parent + and to downstream network elements simultaneously. A downstream loss + coupled with a loss for the same data on some other part of the + distribution tree served by its parent could cause this. In this + case it may provide both upstream and off-tree functionality + simultaneously. + + Note that a DLR differentiates between NAKs from an NE downstream or + from its parent by comparing the network-header source address of the + NAK with it's upstream PGM parent's NLA. The DLR knows the parent's + NLA from the session's SPM messages. + + + + + + + + + + +Speakman, et. al. Experimental [Page 49] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.5.3. OPT_REDIRECT - Procedures - Network Elements + +9.5.3.1. Discovering DLRs + + When a PGM router receives notification of a loss via a NAK, it + SHOULD first try to use a known DLR to recover the loss. If such a + DLR is not known it SHOULD initiate DLR discovery. DLR discovery may + occur in two ways. If there are upstream DLRs, the NAK transmitted + by this router to its PGM parent will trigger their discovery, via a + redirecting POLR. Also, a network element SHOULD initiate a search + for off-tree DLRs using the PGM polling mechanism, and the sub-type + PGM_POLL_DLR. + + If a DLR can provide FEC repairs it will denote this by setting + OPT_PARITY in the PGM header of its POLR response. A network element + SHOULD only direct parity NAKs to a DLR that can provide FEC repairs. + +9.5.3.2. Redirected Repair + + When it can, a network element SHOULD use upstream DLRs. + + Upon receiving a redirecting POLR, network elements SHOULD record the + redirecting information for the TSI, and SHOULD redirect subsequent + NAKs for the same TSI to the network address provided in the + redirecting POLR rather than to the PGM neighbor known via the SPMs. + Note, however, that a redirecting POLR is NOT regarded as matching + the NAK that provoked it, so it does not complete the transmission of + that NAK. Only a normal matching NCF can complete the transmission + of a NAK. + + For subsequent NAKs, if the network element has recorded redirection + information for the corresponding TSI, it MAY change the destination + network address of those NAKs and attempt to transmit them to the + DLR. No NAK for a specific SQN SHOULD be sent to an off-tree DLR if + a NAK for the SQN has been seen on the interface associated with the + DLR. Instead the NAK SHOULD be forwarded upstream. Subsequent NAKs + for different SQNs MAY be forwarded to the said DLR (again assuming + no NAK for them has been seen on the interface to the DLR). + + If a corresponding NCF is not received from the DLR within + NAK_RPT_IVL, the network element MUST discard the redirecting + information for the TSI and re-attempt to forward the NAK towards the + PGM upstream neighbor. + + + + + + + + +Speakman, et. al. Experimental [Page 50] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + If a NAK is received from the DLR for a requested SQN, the network + element MUST discard the redirecting information for the SQN and re- + attempt to forward the NAK towards the PGM upstream neighbor. The + network element MAY still direct NAKs for different SQNs to the DLR. + + RDATA and NCFs from upstream DLRs will flow down the distribution + tree. However, RDATA and NCFs from off-tree DLRs will be unicast to + the network element. The network element will terminate the NCF, but + MUST put the source's NLA and the group address into the network + header and MUST add router alert before forwarding the RDATA packet + to the distribution subtree. + + NULL NAKs from an off-tree DLR for an RDATA packet requested from + that off-tree DLR MUST always be forwarded upstream. The network + element can assume that these will arrive before the matching RDATA. + Other NULL NAKs are forwarded only if matching repair state has not + already been created. Network elements MUST NOT confirm or retry + NULL NAKs and they MUST NOT add the receiving interface to the repair + state. If a NULL NAK is used to initially create repair state, this + fact must be recorded so that any subsequent non-NULL NAK will not be + eliminated, but rather will be forwarded to provoke an actual repair. + State created by a NULL NAK exists only for NAK_ELIM_IVL. + +9.5.4. OPT_REDIRECT - Procedures - Receivers + + These procedures are intended to be applied in instances where a + receiver's first hop router on the reverse path to the source is not + a PGM Network Element. So, receivers MUST ignore a redirecting POLR + from a DLR on the same IP subnet that the receiver resides on, since + this is likely to suffer identical loss to the receiver and so be + useless. Therefore, these procedures are entirely OPTIONAL. A + receiver MAY choose to ignore all redirecting POLRs since in cases + where its first hop router on the reverse path is PGM capable, it + would ignore them anyway. Also, note that receivers will never learn + of off-tree DLRs. + + Upon receiving a redirecting POLR, receivers SHOULD record the + redirecting information for the TSI, and MAY redirect subsequent NAKs + for the same TSI to the network address provided in the redirecting + POLR rather than to the PGM neighbor for the corresponding ODATA for + which the receiver is requesting repair. Note, however, that a + redirecting POLR is NOT regarded as matching the NAK that provoked + it, so it does not complete the transmission of that NAK. Only a + normal matching NCF can complete the transmission of a NAK. + + For subsequent NAKs, if the receiver has recorded redirection + information for the corresponding TSI, it MAY change the destination + network address of those NAKs and attempt to transmit them to the + + + +Speakman, et. al. Experimental [Page 51] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + DLR. If a corresponding NCF is not received within NAK_RPT_IVL, the + receiver MUST discard the redirecting information for the TSI and + re-attempt to forward the NAK to the PGM neighbor for the original + source of the missing ODATA. + +9.5.5. OPT_REDIRECT - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NLA AFI | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | DLR's NLA ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + + Option Type = 0x07 + + Option Length = 4 + NLA length + + DLR's NLA + + The DLR's own unicast network address to which recipients of the + redirecting POLR may direct subsequent NAKs. + + OPT_REDIRECT is network-significant. + +9.6. OPT_SYN - Synchronization Option + + The SYN option indicates the starting data packet for a session. It + must only appear in ODATA or RDATA packets. + + The SYN option MAY be used to provide a useful abstraction to + applications that can simplify application design by providing stream + start notification. It MAY also be used to let a late joiner to a + session know that it is indeed late (i.e. it would not see the SYN + option). + +9.6.1. OPT_SYN - Procedures - Receivers + + Procedures for receivers are implementation dependent. A receiver + MAY use the SYN to provide its applications with abstractions of the + data stream. + + + + + + + +Speakman, et. al. Experimental [Page 52] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.6.2. OPT_SYN - Procedures - Sources + + Sources MAY include OPT_SYN in the first data for a session. That + is, they MAY include the option in: + + the first ODATA sent on a session by a PGM source + + any RDATA sent as a result of loss of this ODATA packet + + all FEC packets for the first transmission group; in this case it + is interpreted as the first packet having the SYN + +9.6.3. OPT_SYN - Procedures - DLRs + + In an identical manner to sources, DLRs MUST provide OPT_SYN in + any retransmitted data that is at the start of a session. + +9.6.4. OPT_SYN - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x0D + + Option Length = 4 + + OPT_SYN is NOT network-significant. + +9.7. OPT_FIN - Session Finish Option + + This FIN option indicates the last data packet for a session and + an orderly close down. + + The FIN option MAY be used to provide an abstraction to + applications that can simplify application design by providing + stream end notification. + + This option MAY be present in the last data packet or transmission + group for a session. The FIN PGM option MUST appear in every SPM + sent after the last ODATA for a session. The SPM_LEAD sequence + number in an SPM with the FIN option indicates the last known data + successfully transmitted for the session. + + + + + + +Speakman, et. al. Experimental [Page 53] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.7.1. OPT_FIN - Procedures - Receivers + + A receiver SHOULD use receipt of a FIN to let it know that it can + tear down its data structures for the said session once a suitable + time period has expired (TXW_SECS). It MAY still try to solicit + retransmissions within the existing transmit window. + + Other than this, procedures for receivers are implementation + dependent. A receiver MAY use the FIN to provide its applications + with abstractions of the data stream and to inform its + applications that the session is ending. + + 9.7.2. OPT_FIN - Procedures - Sources + + Sources MUST include OPT_FIN in every SPM sent after it has been + determined that the application has closed gracefully. If a + source is aware at the time of transmission that it is ending a + session the source MAY include OPT_FIN in, + + the last ODATA + + any associated RDATAs for the last data + + FEC packets for the last transmission group; in this case it is + interpreted as the last packet having the FIN + + When a source detects that it needs to send an OPT_FIN it SHOULD + immediately send it. This is done either by appending it to the last + data packet or transmission group or by immediately sending an SPM + and resetting the SPM heartbeat timer (i.e. it does not wait for a + timer to expire before sending the SPM). After sending an OPT_FIN, + the session SHOULD not close and stop sending SPMs until after a time + period equal to TXW_SECS. + +9.7.3. OPT_FIN - Procedures - DLRs + + In an identical manner to sources, DLRs MUST provide OPT_FIN in any + retransmitted data that is at the end of a session. + + + + + + + + + + + + + +Speakman, et. al. Experimental [Page 54] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.7.4. OPT_FIN - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x0E + + Option Length = 4 + + OPT_FIN is NOT network-significant. + +9.8. OPT_RST - Session Reset Option + + The RST option MAY appear in every SPM sent after an unrecoverable + error is identified by the source. This acts to notify the receivers + that the session is being aborted. This option MAY appear only in + SPMs. The SPM_LEAD sequence number in an SPM with the RST option + indicates the last known data successfully transmitted for the + session. + +9.8.1. OPT_RST - Procedures - Receivers + + Receivers SHOULD treat the reception of OPT_RST in an SPM as an abort + of the session. + + A receiver that receives an SPM with an OPT_RST with the N bit set + SHOULD not send any more NAKs for the said session towards the + source. If the N bit (see 9.8.5) is not set, the receiver MAY + continue to try to solicit retransmit data within the current + transmit window. + +9.8.2. OPT_RST - Procedures - Sources + + Sources SHOULD include OPT_RST in every SPM sent after it has been + determined that an unrecoverable error condition has occurred. The N + bit of the OPT_RST SHOULD only be sent if the source has determined + that it cannot process NAKs for the session. The cause of the + OPT_RST is set to an implementation specific value. If the error + code is unknown, then the value of 0x00 is used. When a source + detects that it needs to send an OPT_RST it SHOULD immediately send + it. This is done by immediately sending an SPM and resetting the SPM + heartbeat timer (i.e. it does not wait for a timer to expire before + sending the SPM). After sending an OPT_RST, the session SHOULD not + close and stop sending SPMs until after a time period equal to + TXW_SECS. + + + +Speakman, et. al. Experimental [Page 55] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.8.3. OPT_RST - Procedures - DLRs + + None. + +9.8.4. OPT_RST - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U|N|Error Code | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x0F + + Option Length = 4 + + N bit + + The N bit is set to 1 to indicate that NAKs for previous ODATA + will go unanswered from the source. The application will tell the + source to turn this bit on or off. + + Error Code + + The 6 bit error code field is used to forward an error code down + to the receivers from the source. + + The value of 0x00 indicates an unknown reset reason. Any other + value indicates the application purposely aborted and gave a + reason (the error code value) that may have meaning to the end + receiver application. These values are entirely application + dependent. + + OPT_RST is NOT network-significant. + +10. Security Considerations + + In addition to the usual problems of end-to-end authentication, PGM + is vulnerable to a number of security risks that are specific to the + mechanisms it uses to establish source path state, to establish + repair state, to forward NAKs, to identify DLRs, and to distribute + repairs. These mechanisms expose PGM network elements themselves to + security risks since network elements not only switch but also + interpret SPMs, NAKs, NCFs, and RDATA, all of which may legitimately + be transmitted by PGM sources, receivers, and DLRs. Short of full + authentication of all neighboring sources, receivers, DLRs, and + network elements, the protocol is not impervious to abuse. + + + + +Speakman, et. al. Experimental [Page 56] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + So putting aside the problems of rogue PGM network elements for the + moment, there are enough potential security risks to network elements + associated with sources, receivers, and DLRs alone. These risks + include denial of service through the exhausting of both CPU + bandwidth and memory, as well as loss of (repair) data connectivity + through the muddling of repair state. + + False SPMs may cause PGM network elements to mis-direct NAKs intended + for the legitimate source with the result that the requested RDATA + would not be forthcoming. + + False NAKs may cause PGM network elements to establish spurious + repair state that will expire only upon time-out and could lead to + memory exhaustion in the meantime. + + False NCFs may cause PGM network elements to suspend NAK forwarding + prematurely (or to mis-direct NAKs in the case of redirecting POLRs) + resulting eventually in loss of RDATA. + + False RDATA may cause PGM network elements to tear down legitimate + repair state resulting eventually in loss of legitimate RDATA. + + The development of precautions for network elements to protect + themselves against incidental or unsophisticated versions of these + attacks is work outside of this spec and includes: + + Damping of jitter in the value of either the network-header source + address of SPMs or the path NLA in SPMs. While the network-header + source address is expected to change seldom, the path NLA is + expected to change occasionally as a consequence of changes in + underlying multicast routing information. + + The extension of NAK shedding procedures to control the volume, not + just the rate, of confirmed NAKs. In either case, these procedures + assist network elements in surviving NAK attacks at the expense of + maintaining service. More efficiently, network elements may use the + knowledge of TSIs and their associated transmit windows gleaned from + SPMs to control the proliferation of repair state. + + A three-way handshake between network elements and DLRs that would + permit a network element to ascertain with greater confidence that an + alleged DLR is identified by the alleged network-header source + address, and is PGM conversant. + + + + + + + + +Speakman, et. al. Experimental [Page 57] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +11. Appendix A - Forward Error Correction + +11.1. Introduction + + The following procedures incorporate packet-level Reed Solomon + Erasure correcting techniques as described in [11] and [12] into PGM. + This approach to Forward Error Correction (FEC) is based upon the + computation of h parity packets from k data packets for a total of n + packets such that a receiver can reconstruct the k data packets out + of any k of the n packets. The original k data packets are referred + to as the Transmission Group, and the total n packets as the FEC + Block. + + These procedures permit any combination of pro-active FEC or on- + demand FEC with conventional ARQ (selective retransmission) within a + given TSI to provide any flavor of layered or integrated FEC. The + two approaches can be used by the same or different receivers in a + single transport session without conflict. Once provided by a + source, the actual use of FEC or selective retransmission for loss + recovery in the session is entirely at the discretion of the + receivers. Note however that receivers SHOULD NOT ask for selective + retransmissions when FEC is available, nevertheless sources MUST + provide selective retransmissions in response to selective NAKs from + the leading partial transmission group (i.e. the most recent + transmission group, which is not yet full). For any group that is + full, the source SHOULD provide FEC on demand in response to a + selective NAK. + + Pro-active FEC refers to the technique of computing parity packets at + transmission time and transmitting them as a matter of course + following the data packets. Pro-active FEC is RECOMMENDED for + providing loss recovery over simplex or asymmetric multicast channels + over which returning repair requests is either impossible or costly. + It provides increased reliability at the expense of bandwidth. + + On-demand FEC refers to the technique of computing parity packets at + repair time and transmitting them only upon demand (i.e., receiver- + based loss detection and repair request). On-demand FEC is + RECOMMENDED for providing loss recovery of uncorrelated loss in very + large receiver populations in which the probability of any single + packet being lost is substantial. It provides equivalent reliability + to selective NAKs (ARQ) at no more and typically less expense of + bandwidth. + + Selective NAKs are NAKs that request the retransmission of specific + packets by sequence number corresponding to the sequence number of + any data packets detected to be missing from the expected sequence + (conventional ARQ). Selective NAKs can be used for recovering losses + + + +Speakman, et. al. Experimental [Page 58] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + occurring in leading partial transmission groups, i.e. in the most + recent transmission group, which is not yet full. The RECOMMENDED + way of handling partial transmission groups, however, is for the data + source to use variable-size transmission groups (see below). + + Parity NAKs are NAKs that request the transmission of a specific + number of parity packets by count corresponding to the count of the + number of data packets detected to be missing from a group of k data + packets (on-demand FEC). + + The objective of these procedures is to incorporate these FEC + techniques into PGM so that: + + sources MAY provide parity packets either pro-actively or on- + demand, interchangeably within the same TSI, + + receivers MAY use either selective or parity NAKs interchangeably + within the same TSI (however, in a session where on-demand parity + is available receivers SHOULD only use parity NAKs). + + network elements maintain repair state based on either selective + or parity NAKs in the same data structure, altering only search, + RDATA constraint, and deletion algorithms in either case, + + and only OPTION additions to the basic packet formats are + REQUIRED. + +11.2. Overview + + Advertising FEC parameters in the transport session + + Sources add OPT_PARITY_PRM to SPMs to provide session-specific + parameters such as the number of packets (TGSIZE == k) in a + transmission group. This option lets receivers know how many packets + there are in a transmission group, and it lets network elements sort + repair state by transmission group number. This option includes an + indication of whether pro-active and/or on-demand parity is available + from the source. + + Distinguishing parity packets from data packets + + Sources send pro-active parity packets as ODATA (NEs do not forward + RDATA unless a repair state is present) and on-demand parity packets + as RDATA. A source MUST add OPT_PARITY to the ODATA/RDATA packet + header of parity packets to permit network elements and receivers to + distinguish them from data packets. + + + + + +Speakman, et. al. Experimental [Page 59] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Data and parity packet numbering + + Parity packets MUST be calculated over a fixed number k of data + packets known as the Transmission Group. Grouping of packets into + transmission groups effectively partitions a packet sequence number + into a high-order portion (TG_SQN) specifying the transmission group + (TG), and a low-order portion (PKT_SQN) specifying the packet number + (PKT-NUM in the range 0 through k-1) within that group. From an + implementation point of view, it's handy if k, the TG size, is a + power of 2. If so, then TG_SQN and PKT_SQN can be mapped side-by- + side into the 32 bit SQN. log2(TGSIZE) is then the size in bits of + PKT_SQN. + + This mapping does not reduce the effective sequence number space + since parity packets marked with OPT_PARITY allow the sequence space + (PKT_SQN) to be completely reused in order to number the h parity + packets, as long as h is not greater than k. + + In the case where h is greater than k, a source MUST add + OPT_PARITY_GRP to any parity packet numbered j greater than k-1, + specifying the number m of the group of k parity packets to which the + packet belongs, where m is just the quotient from the integer + division of j by k. Correspondingly, PKT-NUM for such parity packets + is just j modulo k. In other words, when a source needs to generate + more parity packets than there were original data packets (perhaps + because of a particularly lossy line such that a receiver lost not + only the original data but some of the parity RDATA as well), use the + OPT_PARITY_GRP option in order to number and identify the + transmission group of the extra packets that would exceed the normal + sequential number space. + + Note that parity NAKs (and consequently their corresponding parity + NCFs) MUST also contain the OPT_PARITY flag in the options field of + the fixed header, and that in these packets, PKT_SQN MUST contain + PKT_CNT, the number of missing packets, rather than PKT_NUM, the SQN + of a specific missing packet. More on all this later. + + Variable Transmission Group Size + + The transmission group size advertised in the OPT_PARITY_PRM option + on SPMs MUST be a power of 2 and constant for the duration of the + session. However, the actual transmission group size used MAY not be + constant for the duration of the session, and MAY not be a power of + 2. When a TG size different from the one advertised in + OPT_PARITY_PRM is used, the TG size advertised in OPT_PARITY_PRM MUST + be interpreted as specifying the maximum effective size of the TG. + + + + + +Speakman, et. al. Experimental [Page 60] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + When the actual TG size is not a power of 2 or is smaller than the + max TG size, there will be sparse utilization of the sequence number + space since some of the sequence numbers that would have been + consumed in numbering a maximum sized TG will not be assigned to + packets in the smaller TG. The start of the next transmission group + will always begin on the boundary of the maximum TG size as though + each of the sequence numbers had been utilized. + + When the source decides to use a smaller group size than that + advertised in OPT_PARITY_PRM, it appends OPT_CURR_TGSIZE to the last + data packet (ODATA) in the truncated transmission group. This lets + the receiver know that it should not expect any more packets in this + transmission group, and that it may start requesting repairs for any + missing packets. If the last data packet itself went missing, the + receiver will detect the end of the group when it receives a parity + packet for the group, an SPM with SPM_LEAD equal to OD_SQN of the + last data packet, or the first packet of the next group, whichever + comes first. In addition, any parity packet from this TG will also + carry the OPT_CURR_TGSIZE option as will any SPM sent with SPM_LEAD + equal to OD_SQN of the last data packet. + + Variable TSDU length + + If a non constant TSDU length is used within a given transmission + group, the size of parity packets in the corresponding FEC block MUST + be equal to the size of the largest original data packet in the + block. Parity packets MUST be computed by padding the original + packets with zeros up to the size of the largest data packet. Note + that original data packets are transmitted without padding. + + Receivers using a combination of original packets and FEC packets to + rebuild missing packets MUST pad the original packets in the same way + as the source does. The receiver MUST then feed the padded original + packets plus the parity packets to the FEC decoder. The decoder + produces the original packets padded with zeros up to the size of the + largest original packet in the group. In order for the receiver to + eliminate the padding on the reconstructed data packets, the original + size of the packet MUST be known, and this is accomplished as + follows: + + The source, along with the packet payloads, encodes the TSDU + length and appends the 2-byte encoded length to the padded FEC + packets. + + Receivers pad the original packets that they received to the + largest original packet size and then append the TSDU length to + the padded packets. They then pass them and the FEC packets to + the FEC decoder. + + + +Speakman, et. al. Experimental [Page 61] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + The decoder produces padded original packets with their original + TSDU length appended. Receivers MUST now use this length to get + rid of the padding. + + A source that transmits variable size packets MUST take into account + the fact that FEC packets will have a size equal to the maximum size + of the original packets plus the size of the length field (2 bytes). + + If a fixed packet size is used within a transmission group, the + encoded length is not appended to the parity packets. The presence + of the fixed header option flag OPT_VAR_PKTLEN in parity packets + allows receivers to distinguish between transmission groups with + variable sized packets and fixed-size ones, and behave accordingly. + + Payload-specific options + + Some options present in data packet (ODATA and RDATA) are strictly + associated with the packet content (PGM payload), OPT_FRAGMENT being + an example. These options must be preserved even when the data + packet that would normally contain them is not received, but its the + payload is recovered though the use of FEC. + + To achieve this, PGM encodes the content of these options in special + options that are inserted in parity packets. Two flags present in + the the option common-header are used for this process: bit F + (OP_ENCODED) and bit U (OP_ENCODED_NULL). + + Whenever at least one of the original packets of a TG contains a + payload-specific option of a given type, the source MUST include an + encoded version of that option type in all the parity packets it + transmits. The encoded option is computed by applying FEC encoding + to the whole option with the exception of the first three bytes of + the option common-header (E, Option Type, Option Length, OP_ENCODED + and OPX fields). The type, length and OPX of the encoded option are + the same as the type, length and OPX in the original options. + OP_ENCODED is set to 1 (all original option have OP_ENCODED = 0). + + The encoding is performed using the same process that is used to + compute the payload of the parity packet. i.e. the FEC encoder is fed + with one copy of that option type for each original packet in the TG. + If one (or more) original packet of the TG does not contain that + option type, an all zeroes option is used for the encoding process. + To be able to distinguish this "dummy" option from valid options with + all-zeroes payload, OP_ENCODED_NULL is used. OP_ENCODED_NULL is set + to 0 in all the original options, but the value of 1 is used in the + encoding process if the option did not exist in the original packet. + On the receiver side, all option with OP_ENCODED_NULL equal to 1 are + discarded after decoding. + + + +Speakman, et. al. Experimental [Page 62] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + When a receiver recovers a missing packet using FEC repair packets, + it MUST also recover payload-specific options, if any. The presence + of these can be unequivocally detected through the presence of + encoded options in parity packets (encoded options have OP_ENCODED + set to 1). Receivers apply FEC-recovery to encoded options and + possibly original options, as they do to recover packet payloads. + The FEC decoding is applied to the whole option with the exception of + the first three bytes of the option common-header (E, Option Type, + Option Length, OP_ENCODED and OPX fields). Each decoded option is + associated with the relative payload, unless OP_ENCODED_NULL turns + out to be 1, in which case the decoded option is discarded. + + The decoding MUST be performed using the 1st occurrence of a given + option type in original/parity packets. If one or more original + packets do not contain that option type, an option of the same type + with zero value must be used. This option MUST have OP_ENCODED_NULL + equal to 1. + +11.3. Packet Contents + + This section just provides enough short-hand to make the Procedures + intelligible. For the full details of packet contents, please refer + to Packet Formats below. + + OPT_PARITY indicated in pro-active (ODATA) and on-demand + (RDATA) parity packets to distinguish them from + data packets. This option is directly encoded in + the "Option" field of the fixed PGM header + + OPT_VAR_PKTLEN MAY be present in pro-active (ODATA) and on-demand + (RDATA) parity packets to indicate that the + corresponding transmission group is composed of + variable size data packets. This option is + directly encoded in the "Option" field of the fixed + PGM header + + OPT_PARITY_PRM appended by sources to SPMs to specify session- + specific parameters such as the transmission group + size and the availability of pro-active and/or on- + demand parity from the source + + OPT_PARITY_GRP the number of the group (greater than 0) of h + parity packets to which the parity packet belongs + when more than k parity packets are provided by the + source + + + + + + +Speakman, et. al. Experimental [Page 63] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + OPT_CURR_TGSIZE appended by sources to the last data packet and any + parity packets in a variable sized transmission + group to indicate to the receiver the actual size + of a transmission group. May also be appended to + certain SPMs + +11.3.1. Parity NAKs + + NAK_TG_SQN the high-order portion of NAK_SQN specifying the + transmission group for which parity packets are + requested + + NAK_PKT_CNT the low-order portion of NAK_SQN specifying the + number of missing data packets for which parity + packets are requested + + Nota Bene: NAK_PKT_CNT (and NCF_PKT_CNT) are 0-based counters, + meaning that NAK_PKT_CNT = 0 means that 1 FEC RDATA is being + requested, and in general NAK_PKT_CNT = k - 1 means that k FEC + RDATA are being requested. + +11.3.2. Parity NCFs + + NCF_TG_SQN the high-order portion of NCF_SQN specifying the + transmission group for which parity packets were + requested + + NCF_PKT_CNT the low-order portion of NCF_SQN specifying the + number of missing data packets for which parity + packets were requested + + Nota Bene: NCF_PKT_CNT (and NAK_PKT_CNT) are 0-based counters, + meaning that NAK_PKT_CNT = 0 means that 1 FEC RDATA is being + requested, and in general NAK_PKT_CNT = k - 1 means that k FEC + RDATA are being requested. + +11.3.3. On-demand Parity + + RDATA_TG_SQN the high-order portion of RDATA_SQN specifying the + transmission group to which the parity packet + belongs + + RDATA_PKT_SQN the low-order portion of RDATA_SQN specifying the + parity packet sequence number within the + transmission group + + + + + + +Speakman, et. al. Experimental [Page 64] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +11.3.4. Pro-active Parity + + ODATA_TG_SQN the high-order portion of ODATA_SQN specifying the + transmission group to which the parity packet + belongs + + ODATA_PKT_SQN the low-order portion of ODATA_SQN specifying the + parity packet sequence number within the + transmission group + +11.4. Procedures - Sources + + If a source elects to provide parity for a given transport session, + it MUST first provide the transmission group size PARITY_PRM_TGS in + the OPT_PARITY_PRM option of its SPMs. This becomes the maximum + effective transmission group size in the event that the source elects + to send smaller size transmission groups. If a source elects to + provide proactive parity for a given transport session, it MUST set + PARITY_PRM_PRO in the OPT_PARITY_PRM option of its SPMs. If a source + elects to provide on-demand parity for a given transport session, it + MUST set PARITY_PRM_OND in the OPT_PARITY_PRM option of its SPMs. + + A source MUST send any pro-active parity packets for a given + transmission group only after it has first sent all of the + corresponding k data packets in that group. Pro-active parity + packets MUST be sent as ODATA with OPT_PARITY in the fixed header. + + If a source elects to provide on-demand parity, it MUST respond to a + parity NAK for a transmission group with a parity NCF. The source + MUST complete the transmission of the k original data packets and the + proactive parity packets, possibly scheduled, before starting the + transmission of on-demand parity packets. Subsequently, the source + MUST send the number of parity packets requested by that parity NAK. + On-demand parity packets MUST be sent as RDATA with OPT_PARITY in the + fixed header. Previously transmitted pro-active parity packets + cannot be reused as on-demand parity packets, these MUST be computed + with new, previously unused, indexes. + + In either case, the source MUST provide selective retransmissions + only in response to selective NAKs from the leading partial + transmission group. For any group that is full, the source SHOULD + provide FEC on demand in response to a selective retransmission + request. + + In the absence of data to transmit, a source SHOULD prematurely + terminate the current transmission group by including OPT_CURR_TGSIZE + to the last data packet or to any proactive parity packets provided. + + + + +Speakman, et. al. Experimental [Page 65] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + If the last data packet has already been transmitted and there is no + provision for sending proactive parity packets, an SPM with + OPT_CURR_TGSIZE SHOULD be sent. + + A source consolidates requests for on-demand parity in the same + transmission group according to the following procedures. If the + number of pending (i.e., unsent) parity packets from a previous + request for on-demand parity packets is equal to or greater than + NAK_PKT_CNT in a subsequent NAK, that subsequent NAK MUST be + confirmed but MAY otherwise be ignored. If the number of pending + (i.e., unsent) parity packets from a previous request for on-demand + parity packets is less than NAK_PKT_CNT in a subsequent NAK, that + subsequent NAK MUST be confirmed but the source need only increase + the number of pending parity packets to NAK_PKT_CNT. + + When a source provides parity packets relative to a transmission + group with variable sized packets, it MUST compute parity packets by + padding the smaller original packets with zeroes out to the size of + the largest of the original packets. The source MUST also append the + encoded TSDU lengths at the end of any padding or directly to the end + of the largest packet, and add the OPT_VAR_PKTLEN option as specified + in the overview description. + + When a source provides variable sized transmission groups, it SHOULD + append the OPT_CURR_TGSIZE option to the last data packet in the + shortened group, and it MUST append the OPT_CURR_TGSIZE option to any + parity packets it sends within that group. In case the the last data + packet is sent before a determination has been made to shorten the + group and there is no provision for sending proactive parity packets, + an SPM with OPT_CURR_TGSIZE SHOULD be sent. The source MUST also add + OPT_CURR_TGSIZE to any SPM that it sends with SPM_LEAD equal to + OD_SQN of the last data packet. + + A receiver MUST NAK for the entire number of packets missing based on + the maximum TG size, even if it already knows that the actual TG size + is smaller. The source MUST take this into account and compute the + number of packets effectively needed as the difference between + NAK_PKT_CNT and an offset computed as the difference between the max + TG size and the effective TG size. + +11.5. Procedures - Receivers + + If a receiver elects to make use of parity packets for loss recovery, + it MUST first learn the transmission group size PARITY_PRM_TGS from + OPT_PARITY_PRM in the SPMs for the TSI. The transmission group size + is used by a receiver to determine the sequence number boundaries + between transmission groups. + + + + +Speakman, et. al. Experimental [Page 66] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Thereafter, if PARITY_PRM_PRO is also set in the SPMs for the TSI, a + receiver SHOULD use any pro-active parity packets it receives for + loss recovery, and if PARITY_PRM_OND is also set in the SPMs for the + TSI, it MAY solicit on-demand parity packets upon loss detection. If + PARITY_PRM_OND is set, a receiver MUST NOT send selective NAKs, + except in partial transmission groups if the source does not use the + variable transmission-group size option. Parity packets are ODATA + (pro-active) or RDATA (on-demand) packets distinguished by OPT_PARITY + which lets receivers know that ODATA/RDATA_TG_SQN identifies the + group of PARITY_PRM_TGS packets to which the parity may be applied + for loss recovery in the corresponding transmission group, and that + ODATA/RDATA_PKT_SQN is being reused to number the parity packets + within that group. Receivers order parity packets and eliminate + duplicates within a transmission group based on ODATA/RDATA_PKT_SQN + and on OPT_PARITY_GRP if present. + + To solicit on-demand parity packets, a receiver MUST send parity NAKs + upon loss detection. For the purposes of soliciting on-demand + parity, loss detection occurs at transmission group boundaries, i.e. + upon receipt of the last data packet in a transmission group, upon + receipt of any data packet in any subsequent transmission group, or + upon receipt of any parity packet in the current or a subsequent + transmission group. + + A parity NAK is simply a NAK with OPT_PARITY and NAK_PKT_CNT set to + the count of the number of packets detected to be missing from the + transmission group specified by NAK_TG_SQN. Note that this + constrains the receiver to request no more parity packets than there + are data packets in the transmission group. + + A receiver SHOULD bias the value of NAK_BO_IVL for parity NAKs + inversely proportional to NAK_PKT_CNT so that NAKs for larger losses + are likely to be scheduled ahead of NAKs for smaller losses in the + same receiver population. + + A confirming NCF for a parity NAK is a parity NCF with NCF_PKT_CNT + equal to or greater than that specified by the parity NAK. + + A receiver's NAK_RDATA_IVL timer is not cancelled until all requested + parity packets have been received. + + In the absence of data (detected from SPMs bearing SPM_LEAD equal to + RXW_LEAD) on non-transmission-group boundaries, receivers MAY resort + to selective NAKs for any missing packets in that partial + transmission group. + + + + + + +Speakman, et. al. Experimental [Page 67] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + When a receiver handles parity packets belonging to a transmission + group with variable sized packets, (detected from the presence of the + OPT_VAR_PKTLEN option in the parity packets), it MUST decode them as + specified in the overview description and use the decoded TSDU length + to get rid of the padding in the decoded packet. + + If the source was using a variable sized transmission group via the + OPT_CURR_TGSIZE, the receiver might learn this before having + requested (and received) any retransmission. The above happens if it + sees OPT_CURR_TGSIZE in the last data packet of the TG, in any + proactive parity packet or in a SPM. If the receivers learns this + and determines that it has missed one or more packets in the + shortened transmission group, it MAY then NAK for them without + waiting for the start of the next transmission group. Otherwise it + will start NAKing at the start of the next transmission group. + + In both cases, the receiver MUST NAK for the number of packets + missing assuming that the size of the transmission group is the + maximum effective transmission group. In other words, the receivers + cannot exploit the fact that it might already know that the + transmission group was smaller but MUST always NAK for the number of + packets it believes are missing, plus the number of packets required + to bring the total packets up to the maximum effective transmission + group size. + + After the first parity packet has been delivered to the receiver, the + actual TG size is known to him, either because already known or + because discovered via OPT_CURR_TGSIZE contained in the parity + packet. Hence the receiver can decode the whole group as soon as the + minimum number of parity packets needed is received. + +11.6. Procedures - Network Elements + + Pro-active parity packets (ODATA with OPT_PARITY) are switched by + network elements without transport-layer intervention. + + On-demand parity packets (RDATA with OPT_PARITY) necessitate modified + request, confirmation and repair constraint procedures for network + elements. In the context of these procedures, repair state is + maintained per NAK_TSI and NAK_TG_SQN, and in addition to recording + the interfaces on which corresponding NAKs have been received, + records the largest value of NAK_PKT_CNT seen in corresponding NAKs + on each interface. This value is referred to as the known packet + count. The largest of the known packet counts recorded for any + interface in the repair state for the transmit group or carried by an + NCF is referred to as the largest known packet count. + + + + + +Speakman, et. al. Experimental [Page 68] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Upon receipt of a parity NAK, a network element responds with the + corresponding parity NCF. The corresponding parity NCF is just an + NCF formed in the usual way (i.e., a multicast copy of the NAK with + the packet type changed), but with the addition of OPT_PARITY and + with NCF_PKT_CNT set to the larger of NAK_PKT_CNT and the known + packet count for the receiving interface. The network element then + creates repair state in the usual way with the following + modifications. + + If repair state for the receiving interface does not exist, the + network element MUST create it and additionally record NAK_PKT_CNT + from the parity NAK as the known packet count for the receiving + interface. + + If repair state for the receiving interface already exists, the + network element MUST eliminate the NAK only if NAK_ELIM_IVL has not + expired and NAK_PKT_CNT is equal to or less than the largest known + packet count. If NAK_PKT_CNT is greater than the known packet count + for the receiving interface, the network element MUST update the + latter with the larger NAK_PKT_CNT. + + Upon either adding a new interface or updating the known packet count + for an existing interface, the network element MUST determine if + NAK_PKT_CNT is greater than the largest known packet count. If so or + if NAK_ELIM_IVL has expired, the network element MUST forward the + parity NAK in the usual way with a value of NAK_PKT_CNT equal to the + largest known packet count. + + Upon receipt of an on-demand parity packet, a network element MUST + locate existing repair state for the corresponding RDATA_TSI and + RDATA_TG_SQN. If no such repair state exists, the network element + MUST discard the RDATA as usual. + + If corresponding repair state exists, the largest known packet count + MUST be decremented by one, then the network element MUST forward the + RDATA on all interfaces in the existing repair state, and decrement + the known packet count by one for each. Any interfaces whose known + packet count is thereby reduced to zero MUST be deleted from the + repair state. If the number of interfaces is thereby reduced to + zero, the repair state itself MUST be deleted. + + Upon reception of a parity NCF, network elements MUST cancel pending + NAK retransmission only if NCF_PKT_CNT is greater or equal to the + largest known packet count. Network elements MUST use parity NCFs to + anticipate NAKs in the usual way with the addition of recording + NCF_PKT_CNT from the parity NCF as the largest known packet count + with the anticipated state so that any subsequent NAKs received with + NAK_PKT_CNT equal to or less than NCF_PKT_CNT will be eliminated, and + + + +Speakman, et. al. Experimental [Page 69] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + any with NAK_PKT_CNT greater than NCF_PKT_CNT will be forwarded. + Network elements which receive a parity NCF with NCF_PKT_CNT larger + than the largest known packet count MUST also use it to anticipate + NAKs, increasing the largest known packet count to reflect + NCF_PKT_CNT (partial anticipation). + + Parity NNAKs follow the usual elimination procedures with the + exception that NNAKs are eliminated only if existing NAK state has a + NAK_PKT_CNT greater than NNAK_PKT_CNT. + + Network elements must take extra precaution when the source is using + a variable sized transmission group. Network elements learn that the + source is using a TG size smaller than the maximum from + OPT_CURR_TGSIZE in parity RDATAs or in SPMs. When this happens, they + compute a TG size offset as the difference between the maximum TG + size and the actual TG size advertised by OPT_CURR_TGSIZE. Upon + reception of parity RDATA, the TG size offset is used to update the + repair state as follows: + + Any interface whose known packet count is reduced to the TG size + offset is deleted from the repair state. + + This replaces the normal rule for deleting interfaces that applies + when the TG size is equal to the maximum TG size. + +11.7. Procedures - DLRs + + A DLR with the ability to provide FEC repairs MUST indicate this by + setting the OPT_PARITY bit in the redirecting POLR. It MUST then + process any redirected FEC NAKs in the usual way. + +11.8. Packet Formats + +11.8.1. OPT_PARITY_PRM - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| |P O| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Transmission Group Size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x08 + + Option Length = 8 octets + + P-bit (PARITY_PRM_PRO) + + + +Speakman, et. al. Experimental [Page 70] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Indicates when set that the source is providing pro-active parity + packets. + + O-bit (PARITY_PRM_OND) + + Indicates when set that the source is providing on-demand parity + packets. + + At least one of PARITY_PRM_PRO and PARITY_PRM_OND MUST be set. + + Transmission Group Size (PARITY_PRM_TGS) + + The number of data packets in the transmission group over which + the parity packets are calculated. If a variable transmission + group size is being used, then this becomes the maximum effective + transmission group size across the session. + + OPT_PARITY_PRM MAY be appended only to SPMs. + + OPT_PARITY_PRM is network-significant. + +11.8.2. OPT_PARITY_GRP - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Parity Group Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x09 + + Option Length = 8 octets + + Parity Group Number (PRM_GROUP) + + The number of the group of k parity packets amongst the h parity + packets within the transmission group to which the parity packet + belongs, where the first k parity packets are in group zero. + PRM_GROUP MUST NOT be zero. + + OPT_PARITY_GRP MAY be appended only to parity packets. + + OPT_PARITY_GRP is NOT network-significant. + + + + + + +Speakman, et. al. Experimental [Page 71] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +11.8.3. OPT_CURR_TGSIZE - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Actual Transmission Group Size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x0A + + Option Length = 8 octets + + Actual Transmission Group Size (PRM_ATGSIZE) + + The actual number of data packets in this transmission group. + This MUST be less than or equal to the maximum transmission group + size PARITY_PRM_TGS in OPT_PARITY_PRM. + + OPT_CURR_TGSIZE MAY be appended to data and parity packets (ODATA or + RDATA) and to SPMs. + + OPT_CURR_TGSIZE is network-significant except when appended to ODATA. + +12. Appendix B - Support for Congestion Control + +12.1. Introduction + + A source MUST implement strategies for congestion avoidance, aimed at + providing overall network stability, fairness among competing PGM + flows, and some degree of fairness towards coexisting TCP flows [13]. + In order to do this, the source must be provided with feedback on the + status of the network in terms of traffic load. This appendix + specifies NE procedures that provide such feedback to the source in a + scalable way. (An alternative TCP-friendly scheme for congestion + control that does not require NE support can be found in [16]). + + The procedures specified in this section enable the collection and + selective forwarding of three types of feedback to the source: + + o Worst link load as measured in network elements. + + o Worst end-to-end path load as measured in network elements. + + o Worst end-to-end path load as reported by receivers. + + + + + +Speakman, et. al. Experimental [Page 72] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + This specification defines in detail NE procedures, receivers + procedures and packet formats. It also defines basic procedures in + receivers for generating congestion reports. This specification does + not define the procedures used by PGM sources to adapt their + transmission rates in response of congestion reports. Those + procedures depend upon the specific congestion control scheme. + + PGM defines a header option that PGM receivers may append to NAKs + (OPT_CR). OPT_CR carries congestion reports in NAKs that propagate + upstream towards the source. + + During the process of hop-by-hop reverse NAK forwarding, NEs examine + OPT_CR and possibly modify its contents prior to forwarding the NAK + upstream. Forwarding CRs also has the side effect of creating + congestion report state in the NE. The presence of OPT_CR and its + contents also influences the normal NAK suppression rules. Both the + modification performed on the congestion report and the additional + suppression rules depend on the content of the congestion report and + on the congestion report state recorded in the NE as detailed below. + + OPT_CR contains the following fields: + + OPT_CR_NE_WL Reports the load in the worst link as detected though + NE internal measurements + + OPT_CR_NE_WP Reports the load in the worst end-to-end path as + detected though NE internal measurements + + OPT_CR_RX_WP Reports the load in the worst end-to-end path as + detected by receivers + + A load report is either a packet drop rate (as measured at an NE's + interfaces) or a packet loss rate (as measured in receivers). Its + value is linearly encoded in the range 0-0xFFFF, where 0xFFFF + represents a 100% loss/drop rate. Receivers that send a NAK bearing + OPT_CR determine which of the three report fields are being reported. + + OPT_CR also contains the following fields: + + OPT_CR_NEL A bit indicating that OPT_CR_NE_WL is being reported. + + OPT_CR_NEP A bit indicating that OPT_CR_NE_WP is being reported. + + OPT_CR_RXP A bit indicating that OPT_CR_RX_WP is being reported. + + + + + + + +Speakman, et. al. Experimental [Page 73] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + OPT_CR_LEAD A SQN in the ODATA space that serves as a temporal + reference for the load report values. This is + initialized by receivers with the leading edge of the + transmit window as known at the moment of transmitting + the NAK. This value MAY be advanced in NEs that + modify the content of OPT_CR. + + OPT_CR_RCVR The identity of the receiver that generated the worst + OPT_CR_RX_WP. + + The complete format of the option is specified later. + +12.2. NE-Based Worst Link Report + + To permit network elements to report worst link, receivers append + OPT_CR to a NAK with bit OPT_CR_NEL set and OPT_CR_NE_WL set to zero. + NEs receiving NAKs that contain OPT_CR_NE_WL process the option and + update per-TSI state related to it as described below. The ultimate + result of the NEs' actions ensures that when a NAK leaves a sub-tree, + OPT_CR_NE_WL contains a congestion report that reflects the load of + the worst link in that sub-tree. To achieve this, NEs rewrite + OPT_CR_NE_WL with the worst value among the loads measured on the + local (outgoing) links for the session and the congestion reports + received from those links. + + Note that the mechanism described in this sub-section does not permit + the monitoring of the load on (outgoing) links at non-PGM-capable + multicast routers. For this reason, NE-Based Worst Link Reports + SHOULD be used in pure PGM topologies only. Otherwise, this + mechanism might fail in detecting congestion. To overcome this + limitation PGM sources MAY use a heuristic that combines NE-Based + Worst Link Reports and Receiver-Based Reports. + +12.3. NE-Based Worst Path Report + + To permit network elements to report a worst path, receivers append + OPT_CR to a NAK with bit OPT_CR_NEP set and OPT_CR_NE_WP set to zero. + The processing of this field is similar to that of OPT_CR_NE_WL with + the difference that, on the reception of a NAK, the value of + OPT_CR_NE_WP is adjusted with the load measured on the interface on + which the NAK was received according to the following formula: + + OPT_CR_NE_WP = if_load + OPT_CR_NE_WP * (100% - if_loss_rate) + + The worst among the adjusted OPT_CR_NE_WP is then written in the + outgoing NAK. This results in a hop-by-hop accumulation of link loss + rates into a path loss rate. + + + + +Speakman, et. al. Experimental [Page 74] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + As with OPT_CR_NE_WL, the congestion report in OPT_CR_NE_WP may be + invalid if the multicast distribution tree includes non-PGM-capable + routers. + +12.4. Receiver-Based Worst Report + + To report a packet loss rate, receivers append OPT_CR to a NAK with + bit OPT_CR_RXP set and OPT_CR_RX_WP set to the packet loss rate. NEs + receiving NAKs that contain OPT_CR_RX_WP process the option and + update per-TSI state related to it as described below. The ultimate + result of the NEs' actions ensures that when a NAK leaves a sub-tree, + OPT_CR_RX_WP contains a congestion report that reflects the load of + the worst receiver in that sub-tree. To achieve this, NEs rewrite + OTP_CR_RE_WP with the worst value among the congestion reports + received on its outgoing links for the session. In addition to this, + OPT_CR_RCVR MUST contain the NLA of the receiver that originally + measured the value of OTP_CR_RE_WP being forwarded. + +12.5. Procedures - Receivers + + To enable the generation of any type of congestion report, receivers + MUST insert OPT_CR in each NAK they generate and provide the + corresponding field (OPT_CR_NE_WL, OPT_CR_NE_WP, OPT_CR_RX_WP). The + specific fields to be reported will be advertised to receivers in + OPT_CRQST on the session's SPMs. Receivers MUST provide only those + options requested in OPT_CRQST. + + Receivers MUST initialize OPT_CR_NE_WL and OPT_CR_NE_WP to 0 and they + MUST initialize OPT_CR_RCVR to their NLA. At the moment of sending + the NAK, they MUST also initialize OPT_CR_LEAD to the leading edge of + the transmission window. + + Additionally, if a receiver generates a NAK with OPT_CR with + OPT_CR_RX_WP, it MUST initialize OPT_CR_RX_WP to the proper value, + internally computed. + +12.6. Procedures - Network Elements + + Network elements start processing each OPT_CR by selecting a + reference SQN in the ODATA space. The reference SQN selected is the + highest SQN known to the NE. Usually this is OPT_CR_LEAD contained + in the NAK received. + + They use the selected SQN to age the value of load measurement as + follows: + + o locally measured load values (e.g. interface loads) are + considered up-to-date + + + +Speakman, et. al. Experimental [Page 75] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + o load values carried in OPT_CR are considered up-to-date and are + not aged so as to be independent of variance in round-trip + times from the network element to the receivers + + o old load values recorded in the NE are exponentially aged + according to the difference between the selected reference SQN + and the reference SQN associated with the old load value. + + The exponential aging is computed so that a recorded value gets + scaled down by a factor exp(-1/2) each time the expected inter-NAK + time elapses. Hence the aging formula must include the current loss + rate as follows: + + aged_loss_rate = loss_rate * exp( - SQN_difference * loss_rate / + 2) + + Note that the quantity 1/loss_rate is the expected SQN_lag between + two NAKs, hence the formula above can also be read as: + + aged_loss_rate = loss_rate * exp( - 1/2 * SQN_difference / + SQN_lag) + + which equates to (loss_rate * exp(-1/2)) when the SQN_difference is + equal to expected SQN_lag between two NAKs. + + All the subsequent computations refer to the aged load values. + + Network elements process OPT_CR by handling the three possible types + of congestion reports independently. + + For each congestion report in an incoming NAK, a new value is + computed to be used in the outgoing NAK: + + o The new value for OPT_CR_NE_WL is the maximum of the load + measured on the outgoing interfaces for the session, the value + of OPT_CR_NE_WL of the incoming NAK, and the value previously + sent upstream (recorded in the NE). All these values are as + obtained after the aging process. + + o The new value for OPT_CR_NE_WP is the maximum of the value + previously sent upstream (after aging) and the value of + OPT_CR_NE_WP in the incoming NAK adjusted with the load on the + interface upon which the NAK was received (as described above). + + o The new value for OPT_CR_RX_WP is the maximum of the value + previously sent upstream (after aging) and the value of + OPT_CR_RX_WP in the incoming NAK. + + + + +Speakman, et. al. Experimental [Page 76] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + o If OPT_CR_RX_WP was selected from the incoming NAK, the new + value for OPT_CR_RCVR is the one in the incoming NAK. + Otherwise it is the value previously sent upstream. + + o The new value for OPT_CR_LEAD is the reference SQN selected for + the aging procedure. + +12.6.1. Overriding Normal Suppression Rules + + Normal suppression rules hold to determine if a NAK should be + forwarded upstream or not. However if any of the outgoing congestion + reports has changed by more than 5% relative to the one previously + sent upstream, this new NAK is not suppressed. + +12.6.2. Link Load Measurement + + PGM routers monitor the load on all their outgoing links and record + it in the form of per-interface loss rate statistics. "load" MUST be + interpreted as the percentage of the packets meant to be forwarded on + the interface that were dropped. Load statistics refer to the + aggregate traffic on the links and not to PGM traffic only. + + This document does not specify the algorithm to be used to collect + such statistics, but requires that such algorithm provide both + accuracy and responsiveness in the measurement process. As far as + accuracy is concerned, the expected measurement error SHOULD be + upper-limited (e.g. less than than 10%). As far as responsiveness is + concerned, the measured load SHOULD converge to the actual value in a + limited time (e.g. converge to 90% of the actual value in less than + 200 milliseconds), when the instantaneous offered load changes. + Whenever both requirements cannot be met at the same time, accuracy + SHOULD be traded for responsiveness. + + + + + + + + + + + + + + + + + + + +Speakman, et. al. Experimental [Page 77] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +12.7. Packet Formats + +12.7.1. OPT_CR - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| L P R| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Congestion Report Reference SQN | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NE Worst Link | NE Worst Path | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Rcvr Worst Path | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NLA AFI | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Worst Receiver's NLA ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + + Option Type = 0x10 + + Option Length = 20 octets + NLA length + + L OPT_CR_NEL bit : set indicates OPT_CR_NE_WL is being reported + + P OPT_CR_NEP bit : set indicates OPT_CR_NE_WP is being reported + + R OPT_CR_RXP bit : set indicates OPT_CR_RX_WP is being reported + + Congestion Report Reference SQN (OPT_CR_LEAD). + + A SQN in the ODATA space that serves as a temporal reference point + for the load report values. + + NE Worst Link (OPT_CR_NE_WL). + + Reports the load in the worst link as detected though NE internal + measurements + + NE Worst Path (OPT_CR_NE_WP). + + Reports the load in the worst end-to-end path as detected though + NE internal measurements + + + + + + + +Speakman, et. al. Experimental [Page 78] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Rcvr Worst Path (OPT_CR_RX_WP). + + Reports the load in the worst end-to-end path as detected by + receivers + + Worst Receiver's NLA (OPT_CR_RCVR). + + The unicast address of the receiver that generated the worst + OPT_CR_RX_WP. + + OPT_CR MAY be appended only to NAKs. + + OPT-CR is network-significant. + +12.7.2. OPT_CRQST - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| L P R| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x11 + + Option Length = 4 octets + + L OPT_CRQST_NEL bit : set indicates OPT_CR_NE_WL is being + requested + + P OPT_CRQST_NEP bit : set indicates OPT_CR_NE_WP is being + requested + + R OPT_CRQST_RXP bit : set indicates OPT_CR_RX_WP is being + requested + + OPT_CRQST MAY be appended only to SPMs. + + OPT-CRQST is network-significant. + +13. Appendix C - SPM Requests + +13.1. Introduction + + SPM Requests (SPMRs) MAY be used to solicit an SPM from a source in a + non-implosive way. The typical application is for late-joining + receivers to solicit SPMs directly from a source in order to be able + to NAK for missing packets without having to wait for a regularly + scheduled SPM from that source. + + + +Speakman, et. al. Experimental [Page 79] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +13.2. Overview + + Allowing for SPMR implosion protection procedures, a receiver MAY + unicast an SPMR to a source to solicit the most current session, + window, and path state from that source any time after the receiver + has joined the group. A receiver may learn the TSI and source to + which to direct the SPMR from any other PGM packet it receives in the + group, or by any other means such as from local configuration or + directory services. The receiver MUST use the usual SPM procedures + to glean the unicast address to which it should direct its NAKs from + the solicited SPM. + +13.3. Packet Contents + + This section just provides enough short-hand to make the Procedures + intelligible. For the full details of packet contents, please refer + to Packet Formats below. + +13.3.1. SPM Requests + + SPMRs are transmitted by receivers to solicit SPMs from a source. + + SPMs are unicast to a source and contain: + + SPMR_TSI the source-assigned TSI for the session to which the + SPMR corresponds + +13.4. Procedures - Sources + + A source MUST respond immediately to an SPMR with the corresponding + SPM rate limited to once per IHB_MIN per TSI. The corresponding SPM + matches SPM_TSI to SPMR_TSI and SPM_DPORT to SPMR_DPORT. + +13.5. Procedures - Receivers + + To moderate the potentially implosive behavior of SPMRs at least on a + densely populated subnet, receivers MUST use the following back-off + and suppression procedure based on multicasting the SPMR with a TTL + of 1 ahead of and in addition to unicasting the SPMR to the source. + The role of the multicast SPMR is to suppress the transmission of + identical SPMRs from the subnet. + + More specifically, before unicasting a given SPMR, receivers MUST + choose a random delay on SPMR_BO_IVL (~250 msecs) during which they + listen for a multicast of an identical SPMR. If a receiver does not + see a matching multicast SPMR within its chosen random interval, it + MUST first multicast its own SPMR to the group with a TTL of 1 before + then unicasting its own SPMR to the source. If a receiver does see a + + + +Speakman, et. al. Experimental [Page 80] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + matching multicast SPMR within its chosen random interval, it MUST + refrain from unicasting its SPMR and wait instead for the + corresponding SPM. + + In addition, receipt of the corresponding SPM within this random + interval SHOULD cancel transmission of an SPMR. + + In either case, the receiver MUST wait at least SPMR_SPM_IVL before + attempting to repeat the SPMR by choosing another delay on + SPMR_BO_IVL and repeating the procedure above. + + The corresponding SPMR matches SPMR_TSI to SPMR_TSI and SPMR_DPORT to + SPMR_DPORT. The corresponding SPM matches SPM_TSI to SPMR_TSI and + SPM_DPORT to SPMR_DPORT. + +13.6. SPM Requests + + SPMR: + + SPM Requests are sent by receivers to request the immediate + transmission of an SPM for the given TSI from a source. + + The network-header source address of an SPMR is the unicast NLA of + the entity that originates the SPMR. + + The network-header destination address of an SPMR is the unicast NLA + of the source from which the corresponding SPM is requested. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Source Port | Destination Port | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Options | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Global Source ID ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ... Global Source ID | TSDU Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Option Extensions when present ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... + + Source Port: + + SPMR_SPORT + + Data-Destination Port + + + + +Speakman, et. al. Experimental [Page 81] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Destination Port: + + SPMR_DPORT + + Data-Source Port, together with Global Source ID forms SPMR_TSI + + Type: + + SPMR_TYPE = 0x0C + + Global Source ID: + + SPMR_GSI + + Together with Source Port forms + + SPMR_TSI + +14. Appendix D - Poll Mechanism + +14.1. Introduction + + These procedures provide PGM network elements and sources with the + ability to poll their downstream PGM neighbors to solicit replies + in an implosion-controlled way. + + Both general polls and specific polls are possible. The former + provide a PGM (parent) node with a way to check if there are any + PGM (children) nodes connected to it, both network elements and + receivers, and to estimate their number. The latter may be used + by PGM parent nodes to search for nodes with specific properties + among its PGM children. An example of application for this is DLR + discovery. + + Polling is implemented using two additional PGM packets: + + POLL a Poll Request that PGM parent nodes multicast to the group to + perform the poll. Similarly to NCFs, POLL packets stop at the + first PGM node they reach, as they are not forwarded by PGM + network elements. + + POLR a Poll Response that PGM children nodes (either network elements + or receivers) use to reply to a Poll Request by addressing it + to the NLA of the interface from which the triggering POLL was + sent. + + + + + + +Speakman, et. al. Experimental [Page 82] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + The polling mechanism dictates that PGM children nodes that receive a + POLL packet reply to it only if certain conditions are satisfied and + ignore the POLL otherwise. Two types of condition are possible: a + random condition that defines a probability of replying for the + polled child, and a deterministic condition. Both the random + condition and the deterministic condition are controlled by the + polling PGM parent node by specifying the probability of replying and + defining the deterministic condition(s) respectively. Random-only + poll, deterministic-only poll or a combination of the two are + possible. + + The random condition in polls allows the prevention of implosion of + replies by controlling their number. Given a probability of replying + P and assuming that each receiver makes an independent decision, the + number of expected replies to a poll is P*N where N is the number of + PGM children relative to the polling PGM parent. The polling node + can control the number of expected replies by specifying P in the + POLL packet. + +14.2. Packet Contents + + This section just provides enough short-hand to make the Procedures + intelligible. For the full details of packet contents, please refer + to Packet Formats below. + +14.2.1. POLL (Poll Request) + + POLL_SQN a sequence number assigned sequentially by the polling + parent in unit increments and scoped by POLL_PATH and + the TSI of the session. + + POLL_ROUND a poll round sequence number. Multiple poll rounds + are possible within a POLL_SQN. + + POLL_S_TYPE the sub-type of the poll request + + POLL_PATH the network-layer address (NLA) of the interface on + the PGM network element or source on which the POLL is + transmitted + + POLL_BO_IVL the back-off interval that MUST be used to compute the + random back-off time to wait before sending the + response to a poll. POLL_BO_IVL is expressed in + microseconds. + + POLL_RAND a random string used to implement the randomness in + replying + + + + +Speakman, et. al. Experimental [Page 83] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + POLL_MASK a bit-mask used to determine the probability of random + replies + + Poll request MAY also contain options which specify deterministic + conditions for the reply. No options are currently defined. + +14.2.2. POLR (Poll Response) + + POLR_SQN POLL_SQN of the poll request for which this is a reply + + POLR_ROUND POLL_ROUND of the poll request for which this is a + reply + + Poll response MAY also contain options. No options are currently + defined. + +14.3. Procedures - General + +14.3.1. General Polls + + General Polls may be used to check for and count PGM children that + are 1 PGM hop downstream of an interface of a given node. They have + POLL_S_TYPE equal to PGM_POLL_GENERAL. PGM children that receive a + general poll decide whether to reply to it only based on the random + condition present in the POLL. + + To prevent response implosion, PGM parents that initiate a general + poll SHOULD establish the probability of replying to the poll, P, so + that the expected number of replies is contained. The expected + number of replies is N * P, where N is the number of children. To be + able to compute this number, PGM parents SHOULD already have a rough + estimate of the number of children. If they do not have a recent + estimate of this number, they SHOULD send the first poll with a very + low probability of replying and increase it in subsequent polls in + order to get the desired number of replies. + + To prevent poll-response implosion caused by a sudden increase in the + children population occurring between two consecutive polls with + increasing probability of replying, PGM parents SHOULD use poll + rounds. Poll rounds allow PGM parents to "freeze" the size of the + children population when they start a poll and to maintain it + constant across multiple polls (with the same POLL_SQN but different + POLL_ROUND). This works by allowing PGM children to respond to a + poll only if its POLL_ROUND is zero or if they have previously + received a poll with the same POLL_SQN and POLL_ROUND equal to zero. + + + + + + +Speakman, et. al. Experimental [Page 84] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + In addition to this PGM children MUST observe a random back-off in + replying to a poll. This spreads out the replies in time and allows + a PGM parent to abort the poll if too many replies are being + received. To abort an ongoing poll a PGM parent MUST initiate + another poll with different POLL_SQN. PGM children that receive a + POLL MUST cancel any pending reply for POLLs with POLL_SQN different + from the one of the last POLL received. + + For a given poll with probability of replying P, a PGM parent + estimates the number of children as M / P, where M is the number of + responses received. PGM parents SHOULD keep polling periodically and + use some average of the result of recent polls as their estimate for + the number of children. + +14.3.2. Specific Polls + + Specific polls provide a way to search for PGM children that comply + to specific requisites. As an example specific poll could be used to + search for down-stream DLRs. A specific poll is characterized by a + POLL_S_TYPE different from PGM_POLL_GENERAL. PGM children decide + whether to reply to a specific poll or not based on the POLL_S_TYPE, + on the random condition and on options possibly present in the POLL. + The way options should be interpreted is defined by POLL_S_TYPE. The + random condition MUST be interpreted as an additional condition to be + satisfied. To disable the random condition PGM parents MUST specify + a probability of replying P equal to 1. + + PGM children MUST ignore a POLL packet if they do not understand + POLL_S_TYPE. Some specific POLL_S_TYPE may also require that the + children ignore a POLL if they do not fully understand all the PGM + options present in the packet. + +14.4. Procedures - PGM Parents (Sources or Network Elements) + + A PGM parent (source or network element), that wants to poll the + first PGM-hop children connected to one of its outgoing interfaces + MUST send a POLL packet on that interface with: + + POLL_SQN equal to POLL_SQN of the last POLL sent incremented by + one. If poll rounds are used, this must be equal to + POLL_SQN of the last group of rounds incremented by + one. + + POLL_ROUND The round of the poll. If the poll has a single + round, this must be zero. If the poll has multiple + rounds, this must be one plus the last POLL_ROUND for + the same POLL_SQN, or zero if this is the first round + within this POLL_SQN. + + + +Speakman, et. al. Experimental [Page 85] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + POLL_S_TYPE the type of the poll. For general poll use + PGM_POLL_GENERAL + + POLL_PATH set to the NLA of the outgoing interface + + POLL_BO_IVL set to the wanted reply back-off interval. As far as + the choice of this is concerned, using NAK_BO_IVL is + usually a conservative option, however a smaller value + MAY be used, if the number of expected replies can be + determined with a good confidence or if a + conservatively low probability of reply (P) is being + used (see POLL_MASK next). When the number of + expected replies is unknown, a large POLL_BO_IVL + SHOULD be used, so that the poll can be effectively + aborted if the number of replies being received is too + large. + + POLL_RAND MUST be a random string re-computed each time a new + poll is sent on a given interface + + POLL_MASK determines the probability of replying, P, according + to the relationship P = 1 / ( 2 ^ B ), where B is the + number of bits set in POLL_MASK [15]. If this is a + deterministic poll, B MUST be 0, i.e. POLL_MASK MUST + be a all-zeroes bit-mask. + + Nota Bene: POLLs transmitted by network elements MUST bear the + ODATA source's network-header source address, not the network + element's NLA. POLLs MUST also be transmitted with the IP + + Router Alert Option [6], to be allow PGM network element to + intercept them. + + A PGM parent that has started a poll by sending a POLL packet SHOULD + wait at least POLL_BO_IVL before starting another poll. During this + interval it SHOULD collect all the valid response (the one with + POLR_SQN and POLR_ROUND matching with the outstanding poll) and + process them at the end of the collection interval. + + A PGM parent SHOULD observe the rules mentioned in the description of + general procedures, to prevent implosion of response. These rules + should in general be observed both for generic polls and specific + polls. The latter however can be performed using deterministic poll + (with no implosion prevention) if the expected number of replies is + known to be small. A PGM parent that issue a generic poll with the + intent of estimating the children population size SHOULD use poll + rounds to "freeze" the children that are involved in the measure + process. This allows the sender to "open the door wider" across + + + +Speakman, et. al. Experimental [Page 86] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + subsequent rounds (by increasing the probability of response), + without fear of being flooded by late joiners. Note the use of + rounds has the drawback of introducing additional delay in the + estimate of the population size, as the estimate obtained at the end + of a round-group refers to the condition present at the time of the + first round. + + A PGM parent that has started a poll SHOULD monitor the number of + replies during the collection phase. If this become too large, the + PGM parent SHOULD abort the poll by immediately starting a new poll + (different POLL_SQN) and specifying a very low probability of + replying. + + + When polling is being used to estimate the receiver population for + the purpose of calculating NAK_BO_IVL, OPT_NAK_BO_IVL (see 16.4.1 + below) MUST be appended to SPMs, MAY be appended to NCFs and POLLs, + and in all cases MUST have NAK_BO_IVL_SQN set to POLL_SQN of the most + recent complete round of polls, and MUST bear that round's + corresponding derived value of NAK_BAK_IVL. In this way, + OPT_NAK_BO_IVL provides a current value for NAK_BO_IVL at the same + time as information is being gathered for the calculation of a future + value of NAK_BO_IVL. + +14.5. Procedures - PGM Children (Receivers or Network Elements) + + PGM receivers and network elements MUST compute a 32-bit random node + identifier (RAND_NODE_ID) at startup time. When a PGM child + (receiver or network element) receives a POLL it MUST use its + RAND_NODE_ID to match POLL_RAND of incoming POLLs. The match is + limited to the bits specified by POLL_MASK. If the incoming POLL + contain a POLL_MASK made of all zeroes, the match is successful + despite the content of POLL_RAND (deterministic reply). If the match + fails, then the receiver or network element MUST discard the POLL + without any further action, otherwise it MUST check the fields + POLL_ROUND, POLL_S_TYPE and any PGM option included in the POLL to + determine whether it SHOULD reply to the poll. + + If POLL_ROUND is non-zero and the PGM receiver has not received a + previous poll with the same POLL_SQN and a zero POLL_ROUND, it MUST + discard the poll without further action. + + If POLL_S_TYPE is equal to PGM_POLL_GENERAL, the PGM child MUST + schedule a reply to the POLL despite the presence of PGM options on + the POLL packet. + + + + + + +Speakman, et. al. Experimental [Page 87] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + If POLL_S_TYPE is different from PGM_POLL_GENERAL, the decision on + whether a reply should be scheduled depends on the actual type and on + the options possibly present in the POLL. + + If POLL_S_TYPE is unknown to the recipient of the POLL, it MUST NOT + reply and ignore the poll. Currently the only POLL_S_TYPE defined + are PGM_POLL_GENERAL and PGM_POLL_DLR. + + If a PGM receiver or network element has decided to reply to a POLL, + it MUST schedule the transmission of a single POLR at a random time + in the future. The random delay is chosen in the interval [0, + POLL_BO_IVL]. POLL_BO_IVL is the one contained in the POLL received. + When this timer expires, it MUST send a POLR using POLL_PATH of the + received POLL as destination address. POLR_SQN MUST be equal to + POLL_SQN and POLR_ROUND must be equal to POLL_ROUND. The POLR MAY + contain PGM options according to the semantic of POLL_S_TYPE or the + semantic of PGM options possibly present in the POLL. If POLL_S_TYPE + is PGM_POLL_GENERAL no option is REQUIRED. + + A PGM receiver or network element MUST cancel any pending + transmission of POLRs if a new POLL is received with POLL_SQN + different from POLR_SQN of the poll that scheduled POLRs. + +14.6. Constant Definition + + The POLL_S_TYPE values currently defined are: + + PGM_POLL_GENERAL 0 + + PGM_POLL_DLR 1 + +14.7. Packet Formats + + The packet formats described in this section are transport-layer + headers that MUST immediately follow the network-layer header in the + packet. + + The descriptions of Data-Source Port, Data-Destination Port, Options, + Checksum, Global Source ID (GSI), and TSDU Length are those provided + in Section 8. + +14.7.1. Poll Request + + POLL are sent by PGM parents (sources or network elements) to + initiate a poll among their first PGM-hop children. + + + + + + +Speakman, et. al. Experimental [Page 88] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + POLLs are sent to the ODATA multicast group. The network-header + source address of a POLL is the ODATA source's NLA. POLL MUST be + transmitted with the IP Router Alert Option. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Source Port | Destination Port | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Options | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Global Source ID ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ... Global Source ID | TSDU Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | POLL's Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | POLL's Round | POLL's Sub-type | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NLA AFI | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Path NLA ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + | POLL's Back-off Interval | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Random String | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Matching Bit-Mask | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Option Extensions when present ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Source Port: + + POLL_SPORT + + Data-Source Port, together with POLL_GSI forms POLL_TSI + + Destination Port: + + POLL_DPORT + + Data-Destination Port + + Type: + + POLL_TYPE = 0x01 + + + + +Speakman, et. al. Experimental [Page 89] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Global Source ID: + + POLL_GSI + + Together with POLL_SPORT forms POLL_TSI + + POLL's Sequence Number + + POLL_SQN + + The sequence number assigned to the POLL by the originator. + + POLL's Round + + POLL_ROUND + + The round number within the poll sequence number. + + POLL's Sub-type + + POLL_S_TYPE + + The sub-type of the poll request. + + Path NLA: + + POLL_PATH + + The NLA of the interface on the source or network element on which + this POLL was forwarded. + + POLL's Back-off Interval + + POLL_BO_IVL + + The back-off interval used to compute a random back-off for the + reply, expressed in microseconds. + + Random String + + POLL_RAND + + A random string used to implement the random condition in + replying. + + + + + + + +Speakman, et. al. Experimental [Page 90] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Matching Bit-Mask + + POLL_MASK + + A bit-mask used to determine the probability of random replies. + +14.7.2. Poll Response + + POLR are sent by PGM children (receivers or network elements) to + reply to a POLL. + + The network-header source address of a POLR is the unicast NLA of the + entity that originates the POLR. The network-header destination + address of a POLR is initialized by the originator of the POLL to the + unicast NLA of the upstream PGM element (source or network element) + known from the POLL that triggered the POLR. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Source Port | Destination Port | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Options | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Global Source ID ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ... Global Source ID | TSDU Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | POLR's Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | POLR's Round | reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Option Extensions when present ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Source Port: + + POLR_SPORT + + Data-Destination Port + + Destination Port: + + POLR_DPORT + + Data-Source Port, together with Global Source ID forms POLR_TSI + + + + + +Speakman, et. al. Experimental [Page 91] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Type: + + POLR_TYPE = 0x02 + + Global Source ID: + + POLR_GSI + + Together with POLR_DPORT forms POLR_TSI + + POLR's Sequence Number + + POLR_SQN + + The sequence number (POLL_SQN) of the POLL packet for which this + is a reply. + + POLR's Round + + POLR_ROUND + + The round number (POLL_ROUND) of the POLL packet for which this is + a reply. + +15. Appendix E - Implosion Prevention + +15.1. Introduction + + These procedures are intended to prevent NAK implosion and to limit + its extent in case of the loss of all or part of the suppressing + multicast distribution tree. They also provide a means to adaptively + tune the NAK back-off interval, NAK_BO_IVL. + + The PGM virtual topology is established and refreshed by SPMs. + Between one SPM and the next, PGM nodes may have an out-of-date view + of the PGM topology due to multicast routing changes, flapping, or a + link/router failure. If any of the above happens relative to a PGM + parent node, a potential NAK implosion problem arises because the + parent node is unable to suppress the generation of duplicate NAKs as + it cannot reach its children using NCFs. The procedures described + below introduce an alternative way of performing suppression in this + case. They also attempt to prevent implosion by adaptively tuning + NAK_BO_IVL. + + + + + + + + +Speakman, et. al. Experimental [Page 92] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +15.2. Tuning NAK_BO_IVL + + Sources and network elements continuously monitor the number of + duplicated NAKs received and use this observation to tune the NAK + back-off interval (NAK_BO_IVL) for the first PGM-hop receivers + connected to them. Receivers learn the current value of NAK_BO_IVL + through OPT_NAK_BO_IVL appended to NCFs or SPMs. + +15.2.1. Procedures - Sources and Network Elements + + For each TSI, sources and network elements advertise the value of + NAK_BO_IVL that their first PGM-hop receivers should use. They + advertise a separate value on all the outgoing interfaces for the TSI + and keep track of the last values advertised. + + For each interface and TSI, sources and network elements count the + number of NAKs received for a specific repair state (i.e., per + sequence number per TSI) from the time the interface was first added + to the repair state list until the time the repair state is + discarded. Then they use this number to tune the current value of + NAK_BO_IVL as follows: + + Increase the current value NAK_BO_IVL when the first duplicate NAK + is received for a given SQN on a particular interface. + + Decrease the value of NAK_BO_IVL if no duplicate NAKs are received on + a particular interface for the last NAK_PROBE_NUM measurements where + each measurement corresponds to the creation of a new repair state. + + An upper and lower limit are defined for the possible value of + NAK_BO_IVL at any time. These are NAK_BO_IVL_MAX and NAK_BO_IVL_MIN + respectively. The initial value that should be used as a starting + point to tune NAK_BO_IVL is NAK_BO_IVL_DEFAULT. The policies + RECOMMENDED for increasing and decreasing NAK_BO_IVL are multiplying + by two and dividing by two respectively. + + Sources and network elements advertise the current value of + NAK_BO_IVL through the OPT_NAK_BO_IVL that they append to NCFs. They + MAY also append OPT_NAK_BO_IVL to outgoing SPMs. + + In order to avoid forwarding the NAK_BO_IVL advertised by the parent, + network elements must be able to recognize OPT_NAK_BO_IVL. Network + elements that receive SPMs containing OPT_NAK_BO_IVL MUST either + remove the option or over-write its content (NAK_BO_IVL) with the + current value of NAK_BO_IVL for the outgoing interface(s), before + forwarding the SPMs. + + + + + +Speakman, et. al. Experimental [Page 93] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Sources MAY advertise the value of NAK_BO_IVL_MAX and NAK_BO_IVL_MIN + to the session by appending a OPT_NAK_BO_RNG to SPMs. + +15.2.2. Procedures - Receivers + + Receivers learn the value of NAK_BO_IVL to use through the option + OPT_NAK_BO_IVL, when this is present in NCFs or SPMs. A value for + NAK_BO_IVL learned from OPT_NAK_BO_IVL MUST NOT be used by a receiver + unless either NAK_BO_IVL_SQN is zero, or the receiver has seen + POLL_RND == 0 for POLL_SQN =< NAK_BO_IVL_SQN within half the sequence + number space. The initial value of NAK_BO_IVL is set to + NAK_BO_IVL_DEFAULT. + + Receivers that receive an SPM containing OPT_NAK_BO_RNG MUST use its + content to set the local values of NAK_BO_IVL_MAX and NAK_BO_IVL_MIN. + +15.2.3. Adjusting NAK_BO_IVL in the absence of NAKs + + Monitoring the number of duplicate NAKs provides a means to track + indirectly the change in the size of first PGM-hop receiver + population and adjust NAK_BO_IVL accordingly. Note that the number + of duplicate NAKs for a given SQN is related to the number of first + PGM-hop children that scheduled (or forwarded) a NAK and not to the + absolute number of first PGM-hop children. This mechanism, however, + does not work in the absence of packet loss, hence a large number of + duplicate NAKs is possible after a period without NAKs, if many new + receivers have joined the session in the meanwhile. To address this + issue, PGM Sources and network elements SHOULD periodically poll the + number of first PGM-hop children using the "general poll" procedures + described in Appendix D. If the result of the polls shows that the + population size has increased significantly during a period without + NAKs, they SHOULD increase NAK_BO_IVL as a safety measure. + +15.3. Containing Implosion in the Presence of Network Failures + +15.3.1. Detecting Network Failures + + In some cases PGM (parent) network elements can promptly detect the + loss of all or part of the suppressing multicast distribution tree + (due to network failures or route changes) by checking their + multicast connectivity, when they receive NAKs. In some other cases + this is not possible as the connectivity problem might occur at some + other non-PGM node downstream or might take time to reflect in the + multicast routing table. To address these latter cases, PGM uses a + simple heuristic: a failure is assumed for a TSI when the count of + duplicated NAKs received for a repair state reaches the value + DUP_NAK_MAX in one of the interfaces. + + + + +Speakman, et. al. Experimental [Page 94] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +15.3.2. Containing Implosion + + When a PGM source or network element detects or assumes a failure for + which it looses multicast connectivity to down-stream PGM agents + (either receivers or other network elements), it sends unicast NCFs + to them in response to NAKs. Downstream PGM network elements which + receive unicast NCFs and have multicast connectivity to the multicast + session send special SPMs to prevent further NAKs until a regular SPM + sent by the source refreshes the PGM tree. + + Procedures - Sources and Network Elements + + PGM sources or network elements which detect or assume a failure that + prevents them from reaching down-stream PGM agents through multicast + NCFs revert to confirming NAKs through unicast NCFs for a given TSI + on a given interface. If the PGM agent is the source itself, than it + MUST generate an SPM for the TSI, in addition to sending the unicast + NCF. + + Network elements MUST keep using unicast NCFs until they receive a + regular SPM from the source. + + When a unicast NCF is sent for the reasons described above, it MUST + contain the OPT_NBR_UNREACH option and the OPT_PATH_NLA option. + OPT_NBR_UNREACH indicates that the sender is unable to use multicast + to reach downstream PGM agents. OPT_PATH_NLA carries the network + layer address of the NCF sender, namely the NLA of the interface + leading to the unreachable subtree. + + When a PGM network element receives an NCF containing the + OPT_NBR_UNREACH option, it MUST ignore it if OPT_PATH_NLA specifies + an upstream neighbour different from the one currently known to be + the upstream neighbor for the TSI. Assuming the network element + matches the OPT_PATH_NLA of the upstream neighbour address, it MUST + stop forwarding NAKs for the TSI until it receives a regular SPM for + the TSI. In addition, it MUST also generate a special SPM to prevent + downstream receivers from sending more NAKs. This special SPM MUST + contain the OPT_NBR_UNREACH option and SHOULD have a SPM_SQN equal to + SPM_SQN of the last regular SPM forwarded. The OPT_NBR_UNREACH + option invalidates the windowing information in SPMs (SPM_TRAIL and + SPM_LEAD). The PGM network element that adds the OPT_NBR_UNREACH + option SHOULD invalidate the windowing information by setting + SPM_TRAIL to 0 and SPM_LEAD to 0x80000000. + + PGM network elements which receive an SPM containing the + OPT_NBR_UNREACH option and whose SPM_PATH matches the currently known + PGM parent, MUST forward them in the normal way and MUST stop + + + + +Speakman, et. al. Experimental [Page 95] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + forwarding NAKs for the TSI until they receive a regular SPM for the + TSI. If the SPM_PATH does not match the currently known PGM parent, + the SPM containing the OPT_NBR_UNREACH option MUST be ignored. + + Procedures - Receivers + + PGM receivers which receive either an NCF or an SPM containing the + OPT_NBR_UNREACH option MUST stop sending NAKs until a regular SPM is + received for the TSI. + + On reception of a unicast NCF containing the OPT_NBR_UNREACH option + receivers MUST generate a multicast copy of the packet with TTL set + to one on the RPF interface for the data source. This will prevent + other receivers in the same subnet from generating NAKs. + + Receivers MUST ignore windowing information in SPMs which contain the + OPT_NBR_UNREACH option. + + Receivers MUST ignore NCFs containing the OPT_NBR_UNREACH option if + the OPT_PATH_NLA specifies a neighbour different than the one + currently know to be the PGM parent neighbour. Similarly receivers + MUST ignore SPMs containing the OPT_NBR_UNREACH option if SPM_PATH + does not match the current PGM parent. + +15.4. Packet Formats + +15.4.1. OPT_NAK_BO_IVL - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NAK Back-Off Interval | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NAK Back-Off Interval SQN | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x04 + + NAK Back-Off Interval + + The value of NAK-generation Back-Off Interval in microseconds. + + + + + + + + +Speakman, et. al. Experimental [Page 96] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + NAK Back-Off Interval Sequence Number + + The POLL_SQN to which this value of NAK_BO_IVL corresponds. Zero + is reserved and means NAK_BO_IVL is NOT being determined through + polling (see Appendix D) and may be used immediately. Otherwise, + NAK_BO_IVL MUST NOT be used unless the receiver has also seen + POLL_ROUND = 0 for POLL_SQN =< NAK_BO_IVL_SQN within half the + sequence number space. + + OPT_NAK_BO_IVL MAY be appended to NCFs, SPMs, or POLLs. + + OPT_NAK_BO_IVL is network-significant. + +15.4.2. OPT_NAK_BO_RNG - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Maximum NAK Back-Off Interval | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Minimum NAK Back-Off Interval | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x05 + + Maximum NAK Back-Off Interval + + The maximum value of NAK-generation Back-Off Interval in + microseconds. + + Minimum NAK Back-Off Interval + + The minimum value of NAK-generation Back-Off Interval in + microseconds. + + OPT_NAK_BO_RNG MAY be appended to SPMs. + + OPT_NAK_BO_RNG is network-significant. + +15.4.3. OPT_NBR_UNREACH - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + +Speakman, et. al. Experimental [Page 97] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Option Type = 0x0B + + When present in SPMs, it invalidates the windowing information. + + OPT_NBR_UNREACH MAY be appended to SPMs and NCFs. + + OPT_NBR_UNREACH is network-significant. + +15.4.4. OPT_PATH_NLA - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Path NLA | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x0C + + Path NLA + + The NLA of the interface on the originating PGM network element + that it uses to send multicast SPMs to the recipient of the packet + containing this option. + + OPT_PATH_NLA MAY be appended to NCFs. + + OPT_PATH_NLA is network-significant. + +16. Appendix F - Transmit Window Example + + Nota Bene: The concept of and all references to the increment + window (TXW_INC) and the window increment (TXW_ADV_SECS) + throughout this document are for illustrative purposes only. They + provide the shorthand with which to describe the concept of + advancing the transmit window without also implying any particular + implementation or policy of advancement. + + The size of the transmit window in seconds is simply TXW_SECS. The + size of the transmit window in bytes (TXW_BYTES) is (TXW_MAX_RTE * + TXW_SECS). The size of the transmit window in sequence numbers + (TXW_SQNS) is (TXW_BYTES / bytes-per-packet). + + The fraction of the transmit window size (in seconds of data) by + which the transmit window is advanced (TXW_ADV_SECS) is called the + window increment. The trailing (oldest) such fraction of the + transmit window itself is called the increment window. + + + +Speakman, et. al. Experimental [Page 98] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + In terms of sequence numbers, the increment window is the range of + sequence numbers that will be the first to be expired from the + transmit window. The trailing (or left) edge of the increment window + is just TXW_TRAIL, the trailing (or left) edge of the transmit + window. The leading (or right) edge of the increment window + (TXW_INC) is defined as one less than the sequence number of the + first data packet transmitted by the source TXW_ADV_SECS after + transmitting TXW_TRAIL. + + A data packet is described as being "in" the transmit or increment + window, respectively, if its sequence number is in the range defined + by the transmit or increment window, respectively. + + The transmit window is advanced across the increment window by the + source when it increments TXW_TRAIL to TXW_INC. When the transmit + window is advanced across the increment window, the increment window + is emptied (i.e., TXW_TRAIL is momentarily equal to TXW_INC), begins + to refill immediately as transmission proceeds, is full again + TXW_ADV_SECS later (i.e., TXW_TRAIL is separated from TXW_INC by + TXW_ADV_SECS of data), at which point the transmit window is advanced + again, and so on. + +16.1. Advancing across the Increment Window + + In anticipation of advancing the transmit window, the source starts a + timer TXW_ADV_IVL_TMR which runs for time period TXW_ADV_IVL. + TXW_ADV_IVL has a value in the range (0, TXW_ADV_SECS). The value + MAY be configurable or MAY be determined statically by the strategy + used for advancing the transmit window. + + When TXW_ADV_IVL_TMR is running, a source MAY reset TXW_ADV_IVL_TMR + if NAKs are received for packets in the increment window. In + addition, a source MAY transmit RDATA in the increment window with + priority over other data within the transmit window. + + When TXW_ADV_IVL_TMR expires, a source SHOULD advance the trailing + edge of the transmit window from TXW_TRAIL to TXW_INC. + + Once the transmit window is advanced across the increment window, + SPM_TRAIL, OD_TRAIL and RD_TRAIL are set to the new value of + TXW_TRAIL in all subsequent transmitted packets, until the next + window advancement. + + PGM does not constrain the strategies that a source may use for + advancing the transmit window. The source MAY implement any scheme + or number of schemes. Three suggested strategies are outlined here. + + + + + +Speakman, et. al. Experimental [Page 99] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Consider the following example: + + Assuming a constant transmit rate of 128kbps and a constant data + packet size of 1500 bytes, if a source maintains the past 30 + seconds of data for repair and increments its transmit window in 5 + second increments, then + + TXW_MAX_RTE = 16kBps + TXW_ADV_SECS = 5 seconds, + TXW_SECS = 35 seconds, + TXW_BYTES = 560kB, + TXW_SQNS = 383 (rounded up), + + and the size of the increment window in sequence numbers + (TXW_MAX_RTE * TXW_ADV_SECS / 1500) = 54 (rounded down). + + Continuing this example, the following is a diagram of the transmit + window and the increment window therein in terms of sequence numbers. + + + TXW_TRAIL TXW_LEAD + | | + | | + |--|--------------- Transmit Window -------------|----| + v | | v + v v + n-1 | n | n+1 | ... | n+53 | n+54 | ... | n+381 | n+382 | n+383 + ^ + ^ | ^ + |--- Increment Window|---| + | + | + TXW_INC + + So the values of the sequence numbers defining these windows are: + + TXW_TRAIL = n + TXW_INC = n+53 + TXW_LEAD = n+382 + + Nota Bene: In this example the window sizes in terms of sequence + numbers can be determined only because of the assumption of a + constant data packet size of 1500 bytes. When the data packet + sizes are variable, more or fewer sequence numbers MAY be consumed + transmitting the same amount (TXW_BYTES) of data. + + So, for a given transport session identified by a TSI, a source + maintains: + + + +Speakman, et. al. Experimental [Page 100] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + TXW_MAX_RTE a maximum transmit rate in kBytes per second, the + cumulative transmit rate of some combination of SPMs, + ODATA, and RDATA depending on the transmit window + advancement strategy + + TXW_TRAIL the sequence number defining the trailing edge of the + transmit window, the sequence number of the oldest + data packet available for repair + + TXW_LEAD the sequence number defining the leading edge of the + transmit window, the sequence number of the most + recently transmitted ODATA packet + + TXW_INC the sequence number defining the leading edge of the + increment window, the sequence number of the most + recently transmitted data packet amongst those that + will expire upon the next increment of the transmit + window + + PGM does not constrain the strategies that a source may use for + advancing the transmit window. A source MAY implement any scheme or + number of schemes. This is possible because a PGM receiver must obey + the window provided by the source in its packets. Three strategies + are suggested within this document. + + In the first, called "Advance with Time", the transmit window + maintains the last TXW_SECS of data in real-time, regardless of + whether any data was sent in that real time period or not. The + actual number of bytes maintained at any instant in time will vary + between 0 and TXW_BYTES, depending on traffic during the last + TXW_SECS. In this case, TXW_MAX_RTE is the cumulative transmit rate + of SPMs and ODATA. + + In the second, called "Advance with Data", the transmit window + maintains the last TXW_BYTES bytes of data for repair. That is, it + maintains the theoretical maximum amount of data that could be + transmitted in the time period TXW_SECS, regardless of when they were + transmitted. In this case, TXW_MAX_RTE is the cumulative transmit + rate of SPMs, ODATA, and RDATA. + + The third strategy leaves control of the window in the hands of the + application. The API provided by a source implementation for this, + could allow the application to control the window in terms of APDUs + and to manually step the window. This gives a form of Application + Level Framing (ALF). In this case, TXW_MAX_RTE is the cumulative + transmit rate of SPMs, ODATA, and RDATA. + + + + + +Speakman, et. al. Experimental [Page 101] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +16.2. Advancing with Data + + In the first strategy, TXW_MAX_RTE is calculated from SPMs and both + ODATA and RDATA, and NAKs reset TXW_ADV_IVL_TMR. In this mode of + operation the transmit window maintains the last TXW_BYTES bytes of + data for repair. That is, it maintains the theoretical maximum + amount of data that could be transmitted in the time period TXW_SECS. + This means that the following timers are not treated as real-time + timers, instead they are "data driven". That is, they expire when + the amount of data that could be sent in the time period they define + is sent. They are the SPM ambient time interval, TXW_ADV_SECS, + TXW_SECS, TXW_ADV_IVL, TXW_ADV_IVL_TMR and the join interval. Note + that the SPM heartbeat timers still run in real-time. + + While TXW_ADV_IVL_TMR is running, a source uses the receipt of a NAK + for ODATA within the increment window to reset timer TXW_ADV_IVL_TMR + to TXW_ADV_IVL so that transmit window advancement is delayed until + no NAKs for data in the increment window are seen for TXW_ADV_IVL + seconds. If the transmit window should fill in the meantime, further + transmissions would be suspended until the transmit window can be + advanced. + + A source MUST advance the transmit window across the increment window + only upon expiry of TXW_ADV_IVL_TMR. + + This mode of operation is intended for non-real-time, messaging + applications based on the receipt of complete data at the expense of + delay. + +16.3. Advancing with Time + + This strategy advances the transmit window in real-time. In this + mode of operation, TXW_MAX_RTE is calculated from SPMs and ODATA only + to maintain a constant data throughput rate by consuming extra + bandwidth for repairs. TXW_ADV_IVL has the value 0 which advances + the transmit window without regard for whether NAKs for data in the + increment window are still being received. + + In this mode of operation, all timers are treated as real-time + timers. + + This mode of operation is intended for real-time, streaming + applications based on the receipt of timely data at the expense of + completeness. + + + + + + + +Speakman, et. al. Experimental [Page 102] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +16.4. Advancing under explicit application control + + Some applications may wish more explicit control of the transmit + window than that provided by the advance with data / time strategies + above. An implementation MAY provide this mode of operation and + allow an application to explicitly control the window in terms of + APDUs. + +17. Appendix G - Applicability Statement + + As stated in the introduction, PGM has been designed with a specific + class of applications in mind in recognition of the fact that a + general solution for reliable multicast has proven elusive. The + applicability of PGM is narrowed further, and perhaps more + significantly, by the prototypical nature of at least four of the + transport elements the protocol incorporates. These are congestion + control, router assist, local retransmission, and a programmatic API + for reliable multicast protocols of this class. At the same time as + standardization efforts address each of these elements individually, + this publication is intended to foster experimentation with these + elements in general, and to inform that standardization process with + results from practise. + + This section briefly describes some of the experimental aspects of + PGM and makes non-normative references to some examples of current + practise based upon them. + + At least 3 different approaches to congestion control can be explored + with PGM: a receiver-feedback based approach, a router-assist based + approach, and layer-coding based approach. The first is supported by + the negative acknowledgement mechanism in PGM augmented by an + application-layer acknowledgement mechanism. The second is supported + by the router exception processing mechanism in PGM. The third is + supported by the FEC mechanisms in PGM. An example of a receiver- + feedback based approach is provided in [16], and a proposal for a + router-assist based approach was proposed in [17]. Open issues for + the researchers include how do each of these approaches behave in the + presence of multiple competing sessions of the same discipline or of + different disciplines, TCP most notably; how do each of them behave + over a particular range of topologies, and over a particular range of + loads; and how do each of them scale as a function of the size of the + receiver population. + + Router assist has applications not just to implosion control and + retransmit constraint as described in this specification, but also to + congestion control as described above, and more generally to any + feature which may be enhanced by access to per-network-element state + and processing. The full range of these features is as yet + + + +Speakman, et. al. Experimental [Page 103] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + unexplored, but a general mechanism for providing router assist in a + transport-protocol independent way (GRA) is a topic of active + research [18]. That effort has been primarily informed by the router + assist component of PGM, and implementation and deployment experience + with PGM will continue to be fed back into the specification and + eventual standardization of GRA. Open questions facing the + researchers ([19], [20], [21]) include how router-based state scales + relative to the feature benefit obtained, how system-wide factors + (such as throughput and retransmit latency) vary relative to the + scale and topology of deployed router assistance, and how incremental + deployment considerations affect the tractability of router-assist + based features. Router assist may have additional implications in + the area of congestion control to the extent that it may be applied + in multi-group layered coding schemes to increase the granularity and + reduce the latency of receiver based congestion control. + + GRA itself explicitly incorporates elements of active networking, and + to the extent that the router assist component of PGM is reflected in + GRA, experimentation with the narrowly defined network-element + functionality of PGM will provide some of the first real world + experience with this promising if controversial technology. + + Local retransmission is not a new idea in general in reliable + multicast, but the specific approach taken in PGM of locating re- + transmitters on the distribution tree for the session, diverting + repair requests from network elements to the re-transmitters, and + then propagating repairs downward from the repair point on the + distribution tree raises interesting questions concerning where to + locate re-transmitters in a given topology, and how network elements + locate those re-transmitters and evaluate their efficiency relative + to other available sources of retransmissions, most notably the + source itself. This particular aspect of PGM, while fully specified, + has only been implemented on the network element side, and awaits a + host-side implementation before questions like these can be + addressed. + + PGM presents the opportunity to develop a programming API for + reliable multicast applications that reflects both those + applications' service requirements as well as the services provided + by PGM in support of those applications that may usefully be made + visible above the transport interface. At least a couple of host- + side implementations of PGM and a concomitant API have been developed + for research purposes ([22], [23]), and are available as open source + explicitly for the kind of experimentation described in this section. + + Perhaps the broadest experiment that PGM can enable in a community of + researchers using a reasonable scale experimental transport protocol + is simply in the definition, implementation, and deployment of IP + + + +Speakman, et. al. Experimental [Page 104] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + multicast applications for which the reliability provided by PGM is a + significant enabler. Experience with such applications will not just + illuminate the value of reliable multicast, but will also provoke + practical examination of and responses to the attendant policy issues + (such as peering, billing, access control, firewalls, NATs, etc.), + and, if successful, will ultimately encourage more wide spread + deployment of IP multicast itself. + +18. Abbreviations + + ACK Acknowledgment + AFI Address Family Indicator + ALF Application Level Framing + APDU Application Protocol Data Unit + ARQ Automatic Repeat reQuest + DLR Designated Local Repairer + GSI Globally Unique Source Identifier + FEC Forward Error Correction + MD5 Message-Digest Algorithm + MTU Maximum Transmission Unit + NAK Negative Acknowledgment + NCF NAK Confirmation + NLA Network Layer Address + NNAK Null Negative Acknowledgment + ODATA Original Data + POLL Poll Request + POLR Poll Response + RDATA Repair Data + RSN Receive State Notification + SPM Source Path Message + SPMR SPM Request + TG Transmission Group + TGSIZE Transmission Group Size + TPDU Transport Protocol Data Unit + TSDU Transport Service Data Unit + TSI Transport Session Identifier + TSN Transmit State Notification + + + + + + + + + + + + + + +Speakman, et. al. Experimental [Page 105] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +19. Acknowledgements + + The design and specification of PGM has been substantially influenced + by reviews and revisions provided by several people who took the time + to read and critique this document. These include, in alphabetical + order: + + Bob Albrightson + Joel Bion + Mark Bowles + Steve Deering + Tugrul Firatli + Dan Harkins + Dima Khoury + Gerard Newman + Dave Oran + Denny Page + Ken Pillay + Chetan Rai + Yakov Rekhter + Dave Rossetti + Paul Stirpe + Brian Whetten + Kyle York + +20. References + + [1] B. Whetten, T. Montgomery, S. Kaplan, "A High Performance + Totally Ordered Multicast Protocol", in "Theory and Practice in + Distributed Systems", Springer Verlag LCNS938, 1994. + + [2] S. Floyd, V. Jacobson, C. Liu, S. McCanne, L. Zhang, "A + Reliable Multicast Framework for Light-weight Sessions and + Application Level Framing", ACM Transactions on Networking, + November 1996. + + [3] J. C. Lin, S. Paul, "RMTP: A Reliable Multicast Transport + Protocol", ACM SIGCOMM August 1996. + + [4] Miller, K., Robertson, K., Tweedly, A. and M. White, "Multicast + File Transfer Protocol (MFTP) Specification", Work In Progress. + + [5] Deering, S., "Host Extensions for IP Multicasting", STD 5, RFC + 1112, August 1989. + + [6] Katz, D., "IP Router Alert Option", RFC 2113, February 1997. + + [7] C. Partridge, "Gigabit Networking", Addison Wesley 1994. + + + +Speakman, et. al. Experimental [Page 106] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + [8] H. W. Holbrook, S. K. Singhal, D. R. Cheriton, "Log-Based + Receiver-Reliable Multicast for Distributed Interactive + Simulation", ACM SIGCOMM 1995. + + [9] Rivest, R., "The MD5 Message-Digest Algorithm", RFC 1321, April + 1992. + + [10] Reynolds, J. and J. Postel, "Assigned Numbers", STD 2, RFC + 1700, October 1994. + + [11] J. Nonnenmacher, E. Biersack, D. Towsley, "Parity-Based Loss + Recovery for Reliable Multicast Transmission", ACM SIGCOMM + September 1997. + + [12] L. Rizzo, "Effective Erasure Codes for Reliable Computer + Communication Protocols", Computer Communication Review, April + 1997. + + [13] V. Jacobson, "Congestion Avoidance and Control", ACM SIGCOMM + August 1988. + + [14] Bradner, S., "Key words for use in RFCs to Indicate Requirement + Levels", BCP, 14, RFC 2119, March 1997. + + [15] J. Bolot, T. Turletti, I. Wakeman, "Scalable Feedback Control + for Multicast Video Distribution in the Internet", Proc. + ACM/Sigcomm 94, pp. 58-67. + + [16] L. Rizzo, "pgmcc: A TCP-friendly Single-Rate Multicast + Congestion Control Scheme", Proc. of ACM SIGCOMM August 2000. + + [17] M. Luby, L. Vicisano, T. Speakman. "Heterogeneous multicast + congestion control based on router packet filtering", RMT + working group, June 1999, Pisa, Italy. + + [18] Cain, B., Speakman, T. and D. Towsley, "Generic Router Assist + (GRA) Building Block, Motivation and Architecture", Work In + Progress. + + [19] C. Papadopoulos, and E. Laliotis,"Incremental Deployment of a + Router-assisted Reliable Multicast Scheme,", Proc. of Networked + Group Communications (NGC2000), Stanford University, Palo Alto, + CA. November 2000. + + + + + + + + +Speakman, et. al. Experimental [Page 107] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + [20] C. Papadopoulos, "RAIMS: an Architecture for Router-Assisted + Internet Multicast Services." Presented at ETH, Zurich, + Switzerland, October 23 2000. + + [21] J. Chesterfield, A. Diana, A. Greenhalgh, M. Lad, and M. Lim, + "A BSD Router Implementation of PGM", + http://www.cs.ucl.ac.uk/external/m.lad/rpgm/ + + [22] L. Rizzo, "A PGM Host Implementation for FreeBSD", + http://www.iet.unipi.it/~luigi/pgm.html + + [23] M. Psaltaki, R. Araujo, G. Aldabbagh, P. Kouniakis, and A. + Giannopoulos, "Pragmatic General Multicast (PGM) host + implementation for FreeBSD.", + http://www.cs.ucl.ac.uk/research/darpa/pgm/PGM_FINAL.html + +21. Authors' Addresses + + Tony Speakman + EMail: speakman@cisco.com + + Dino Farinacci + Procket Networks + 3850 North First Street + San Jose, CA 95134 + USA + EMail: dino@procket.com + + Steven Lin + Juniper Networks + 1194 N. Mathilda Ave. + Sunnyvale, CA 94086 + USA + EMail: steven@juniper.net + + Alex Tweedly + EMail: agt@cisco.com + + Nidhi Bhaskar + EMail: nbhaskar@cisco.com + + Richard Edmonstone + EMail: redmonst@cisco.com + + Rajitha Sumanasekera + EMail: rajitha@cisco.com + + + + + +Speakman, et. al. Experimental [Page 108] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Lorenzo Vicisano + Cisco Systems, Inc. + 170 West Tasman Drive, + San Jose, CA 95134 + USA + EMail: lorenzo@cisco.com + + Jon Crowcroft + Department of Computer Science + University College London + Gower Street + London WC1E 6BT + UK + EMail: j.crowcroft@cs.ucl.ac.uk + + Jim Gemmell + Microsoft Bay Area Research Center + 301 Howard Street, #830 + San Francisco, CA 94105 + USA + EMail: jgemmell@microsoft.com + + Dan Leshchiner + Tibco Software + 3165 Porter Dr. + Palo Alto, CA 94304 + USA + EMail: dleshc@tibco.com + + Michael Luby + Digital Fountain, Inc. + 39141 Civic Center Drive + Fremont CA 94538 + USA + EMail: luby@digitalfountain.com + + Todd L. Montgomery + Talarian Corporation + 124 Sherman Ave. + Morgantown, WV 26501 + USA + EMail: todd@talarian.com + + + + + + + + + +Speakman, et. al. Experimental [Page 109] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Luigi Rizzo + Dip. di Ing. dell'Informazione + Universita` di Pisa + via Diotisalvi 2 + 56126 Pisa + Italy + EMail: luigi@iet.unipi.it + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Speakman, et. al. Experimental [Page 110] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +22. Full Copyright Statement + + Copyright (C) The Internet Society (2001). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Speakman, et. al. Experimental [Page 111] + diff --git a/3rdparty/openpgm-svn-r1085/pgm/COPYING b/3rdparty/openpgm-svn-r1085/pgm/COPYING new file mode 100644 index 0000000..5ab7695 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/3rdparty/openpgm-svn-r1085/pgm/INSTALL b/3rdparty/openpgm-svn-r1085/pgm/INSTALL new file mode 100644 index 0000000..3ba60e5 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/INSTALL @@ -0,0 +1,4 @@ +For building instructions, see: + + http://code.google.com/p/openpgm/wiki/OpenPgm3CReferenceBuildLibrary + diff --git a/3rdparty/openpgm-svn-r1085/pgm/LICENSE b/3rdparty/openpgm-svn-r1085/pgm/LICENSE new file mode 100644 index 0000000..fabbeef --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/LICENSE @@ -0,0 +1,19 @@ +Most of OpenPGM is licensed under the terms of the GNU Lesser Public License, +the LGPL, with a special exception: + + As a special exception, the copyright holders of this library give + you permission to link this library with independent modules to + produce an executable, regardless of the license terms of these + independent modules, and to copy and distribute the resulting + executable under terms of your choice, provided that you also meet, + for each linked independent module, the terms and conditions of the + license of that module. An independent module is a module which is + not derived from or based on this library. If you modify this + library, you must extend this exception to your version of the + library. + +See the file COPYING for details of the LGPL. + +Hence you should treat the libraries libpgm, libpgmsnmp, and libpgmhttp of +OpenPGM as being LGPL licensed, with the special exception. + diff --git a/3rdparty/openpgm-svn-r1085/pgm/README b/3rdparty/openpgm-svn-r1085/pgm/README new file mode 100644 index 0000000..ece7aa1 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/README @@ -0,0 +1,7 @@ +OpenPGM is a library implementing the PGM reliable multicast network protocol. + +For more information about OpenPGM, see: + + http://openpgm.googlecode.com/ + + diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgm b/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgm new file mode 100644 index 0000000..8dd509d --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgm @@ -0,0 +1,170 @@ +# -*- mode: python -*- +# OpenPGM build script +# $Id$ + +import os; +Import('env') + +src = Split(""" + thread.c + mem.c + string.c + list.c + slist.c + queue.c + hashtable.c + messages.c + error.c + math.c + packet_parse.c + packet_test.c + sockaddr.c + time.c + if.c + getifaddrs.c + getnodeaddr.c + indextoaddr.c + indextoname.c + nametoindex.c + inet_network.c + md5.c + rand.c + gsi.c + tsi.c + txw.c + rxw.c + skbuff.c + socket.c + source.c + receiver.c + recv.c + engine.c + timer.c + net.c + rate_control.c + checksum.c + reed_solomon.c + galois_tables.c + wsastrerror.c + histogram.c +""") + +e = env.Clone(); +e.Append(CCFLAGS = '-DGETTEXT_PACKAGE=\'"pgm"\''); + +# Galois tables +e.Command ('galois_tables.c', 'galois_generator.pl', "perl $SOURCE > $TARGET"); + +# Version stamping +e.Command ('version.c', 'version_generator.py', "python $SOURCE > $TARGET"); +e.Depends ('version.c', src); +src += ['version.c']; + +e.StaticLibrary('libpgm', src); +e.StaticSharedLibrary('libpgm-pic', src); + +#----------------------------------------------------------------------------- +# unit testing + +if env['WITH_CHECK'] == 'true': + te = e.Clone(); + te.MergeFlags(env['GLIB_FLAGS']); + newCCFLAGS = []; + for flag in te['CCFLAGS']: + if ("-W" != flag[:2]) and ("-pedantic" != flag[:9]): + newCCFLAGS.append(flag); + te['CCFLAGS'] = newCCFLAGS; + te.ParseConfig ('pkg-config --cflags --libs check'); +# log dependencies + tlog = [ e.Object('messages.c'), + e.Object('thread.c'), + e.Object('galois_tables.c'), + e.Object('mem.c'), + e.Object('histogram.c'), + e.Object('string.c'), + e.Object('slist.c') + ]; +# framework + te.Program (['atomic_unittest.c']); + te.Program (['checksum_unittest.c'] + tlog); + te.Program (['error_unittest.c'] + tlog); + te.Program (['md5_unittest.c'] + tlog); + te.Program (['getifaddrs_unittest.c', + e.Object('error.c'), + e.Object('sockaddr.c'), + e.Object('list.c')] + tlog); + te.Program (['getnodeaddr_unittest.c', + e.Object('error.c'), + e.Object('sockaddr.c')] + tlog); + te.Program (['indextoaddr_unittest.c', + e.Object('error.c'), + e.Object('sockaddr.c')] + tlog); + te.Program (['inet_network_unittest.c', + e.Object('sockaddr.c')] + tlog); + te.Program (['rate_control_unittest.c'] + tlog); + te.Program (['reed_solomon_unittest.c'] + tlog); + te.Program (['time_unittest.c', + e.Object('error.c')] + tlog); +# collate + tframework = [ e.Object('checksum.c'), + e.Object('error.c'), + e.Object('galois_tables.c'), + e.Object('getifaddrs.c'), + e.Object('getnodeaddr.c'), + e.Object('hashtable.c'), + e.Object('histogram.c'), + e.Object('indextoaddr.c'), + e.Object('indextoname.c'), + e.Object('inet_network.c'), + e.Object('list.c'), + e.Object('math.c'), + e.Object('md5.c'), + e.Object('mem.c'), + e.Object('messages.c'), + e.Object('nametoindex.c'), + e.Object('queue.c'), + e.Object('rand.c'), + e.Object('rate_control.c'), + e.Object('reed_solomon.c'), + e.Object('slist.c'), + e.Object('sockaddr.c'), + e.Object('string.c'), + e.Object('thread.c'), + e.Object('time.c'), + e.Object('wsastrerror.c') + ]; +# library + te.Program (['txw_unittest.c', + e.Object('tsi.c'), + e.Object('skbuff.c')] + tframework); + te.Program (['rxw_unittest.c', + e.Object('tsi.c'), + e.Object('skbuff.c')] + tframework); + te.Program (['engine_unittest.c', + e.Object('version.c')] + tframework); + te.Program (['gsi_unittest.c', + e.Object('if.c')] + tframework); + te.Program (['tsi_unittest.c'] + tframework); + te.Program (['if_unittest.c'] + tframework); + te.Program (['socket_unittest.c', + e.Object('if.c'), + e.Object('tsi.c')] + tframework); +# te.Program (['source_unittest.c'] + tframework); +# te.Program (['receiver_unittest.c', +# e.Object('tsi.c')] + tframework); + te.Program (['recv_unittest.c', + e.Object('tsi.c'), + e.Object('gsi.c'), + e.Object('skbuff.c')] + tframework); + te.Program (['net_unittest.c'] + tframework); + te.Program (['timer_unittest.c'] + tframework); + te.Program (['packet_parse_unittest.c'] + tframework); + te.Program (['packet_test_unittest.c'] + tframework); + te.Program (['ip_unittest.c', + e.Object('if.c')] + tframework); +# performance tests + te.Program (['checksum_perftest.c', + e.Object('time.c'), + e.Object('error.c')] + tlog); + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgm89 b/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgm89 new file mode 100644 index 0000000..dae46b9 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgm89 @@ -0,0 +1,80 @@ +# -*- mode: python -*- +# OpenPGM build script +# $Id$ + +import os; +import string; +Import('env') + +src = Split(""" + thread.c + mem.c + string.c + list.c + slist.c + queue.c + hashtable.c + messages.c + error.c + math.c + packet_parse.c + packet_test.c + sockaddr.c + time.c + if.c + getifaddrs.c + getnodeaddr.c + indextoaddr.c + indextoname.c + nametoindex.c + inet_network.c + md5.c + rand.c + gsi.c + tsi.c + txw.c + rxw.c + skbuff.c + socket.c + source.c + receiver.c + recv.c + engine.c + timer.c + net.c + rate_control.c + checksum.c + reed_solomon.c + galois_tables.c + wsastrerror.c + histogram.c +""") + +e = env.Clone(); +e.Append(CCFLAGS = '-DGETTEXT_PACKAGE=\'"pgm"\''); + +# Galois tables +e.Command ('galois_tables.c', 'galois_generator.pl', "perl $SOURCE > $TARGET"); + +# Version stamping +e.Command ('version.c', 'version_generator.py', "python $SOURCE > $TARGET"); +e.Depends ('version.c', src); +src += ['version.c']; + +# C89 degrading +c89source = Builder(action = 'perl -p -e "s/%z(u|d)/%l\\1/g" $SOURCE > $TARGET', + suffix = '.c89.c', + src_suffix = '.c', + single_source = 1); +e.Append(BUILDERS = {'C89Source' : c89source}) + +c89src = [] +for c99file in src: + c89file = c99file.replace('.c', '.c89.c'); + c89src += [ c89file ]; + e.C89Source(c99file); + +e.StaticLibrary('libpgm89', c89src); +e.StaticSharedLibrary('libpgm89-pic', c89src); + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmex b/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmex new file mode 100644 index 0000000..b508a04 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmex @@ -0,0 +1,18 @@ +# -*- mode: python -*- +# OpenPGM build script +# $Id$ + +Import('env') + +e = env.Clone(); +e.MergeFlags(env['GLIB_FLAGS']); + +src = Split(""" + log.c + backtrace.c + signal.c +""") + +e.StaticLibrary('libpgmex', src); + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmhttp b/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmhttp new file mode 100644 index 0000000..9824b3c --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmhttp @@ -0,0 +1,53 @@ +# -*- mode: python -*- +# OpenPGM build script +# $Id$ + +Import('env') + +e = env.Clone() +e.Append(CCFLAGS = '-DGETTEXT_PACKAGE=\'"pgm-http"\''); + +# add binary tree to include path to find embedded htdocs +e.Append(CPPPATH = ['.']); + +src = Split(""" + http.c +""") + +htdocs = Split(""" + htdocs/404.html + htdocs/base.css + htdocs/robots.txt + htdocs/xhtml10_strict.doctype +""") + +pgmhttp = e.StaticLibrary('libpgmhttp', src); +pgmhttppic = e.StaticSharedLibrary('libpgmhttp-pic', src); + +# embed htdocs into C headers +embed_htdoc = Builder(action = './htdocs/convert_to_macro.pl $SOURCE > $TARGET') +e.Append(BUILDERS = {'EmbedHtdoc' : embed_htdoc}) + +for htdoc in htdocs: + embedded = htdoc + '.h' + e.EmbedHtdoc(embedded, htdoc) + +#----------------------------------------------------------------------------- +# unit testing + +if env['WITH_CHECK'] == 'true': + te = e.Clone(); +# add new suffix so we can re-use libpgm objects + te['SHOBJSUFFIX'] = '.http' + te['SHOBJSUFFIX']; + te['OBJSUFFIX'] = '.http' + te['OBJSUFFIX']; + + newCCFLAGS = []; + for flag in te['CCFLAGS']: + if ("-W" != flag[:2]) and ("-pedantic" != flag[:9]): + newCCFLAGS.append(flag); + te['CCFLAGS'] = newCCFLAGS; + te.ParseConfig ('pkg-config --cflags --libs check'); + te.Program (['http_unittest.c', te.Object('sockaddr.c'), te.Object('getifaddrs.c')]); + + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmsnmp b/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmsnmp new file mode 100644 index 0000000..8e35aab --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmsnmp @@ -0,0 +1,35 @@ +# -*- mode: python -*- +# OpenPGM build script +# $Id$ + +Import('env') + +e = env.Clone() +e.Append(CCFLAGS = '-DGETTEXT_PACKAGE=\'"pgm-snmp"\''); + +e.MergeFlags(e['SNMP_FLAGS']); + +src = Split(""" + snmp.c + pgmMIB.c +""") + +e.StaticLibrary('libpgmsnmp', src); +e.StaticSharedLibrary('libpgmsnmp-pic', src); + +#----------------------------------------------------------------------------- +# unit testing + +if env['WITH_CHECK'] == 'true': + te = e.Clone(); + newCCFLAGS = []; + for flag in te['CCFLAGS']: + if ("-W" != flag[:2]) and ("-pedantic" != flag[:9]): + newCCFLAGS.append(flag); + te['CCFLAGS'] = newCCFLAGS; + te.ParseConfig ('pkg-config --cflags --libs check'); + te.Program ('snmp_unittest.c'); + te.Program (['pgmMIB_unittest.c', e.Object('snmp.c')]); + + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct b/3rdparty/openpgm-svn-r1085/pgm/SConstruct new file mode 100644 index 0000000..5754901 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/SConstruct @@ -0,0 +1,326 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 1, 0 ) +SConsignFile('scons.signatures' + '-' + platform.system() + '-' + platform.machine()); + +vars = Variables() +vars.AddVariables ( + EnumVariable ('BUILD', 'build environment', 'debug', + allowed_values=('release', 'debug', 'profile')), + EnumVariable ('BRANCH', 'branch prediction', 'none', + allowed_values=('none', 'profile', 'seed')), + EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', + allowed_values=('true', 'false')), + EnumVariable ('COVERAGE', 'test coverage', 'none', + allowed_values=('none', 'full')), + EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CHECK', 'Check test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_TEST', 'Network test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CC', 'C++ examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', + allowed_values=('true', 'false')), +) + +#----------------------------------------------------------------------------- +# Dependencies + +env = Environment(); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment( + variables = vars, + ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', + '-Wunsafe-loop-optimizations', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', + '-pedantic', +# C99 + '-std=gnu99', + '-D_XOPEN_SOURCE=600', + '-D_BSD_SOURCE', +# re-entrant libc + '-D_REENTRANT', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R', + '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header + '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system + '-DCONFIG_HAVE_PROC', +# example: crash handling + '-DCONFIG_HAVE_BACKTRACE', +# timing + '-DCONFIG_HAVE_PSELECT', + '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', + '-DCONFIG_HAVE_HPET', +# event handling + '-DCONFIG_HAVE_POLL', + '-DCONFIG_HAVE_EPOLL', +# interface enumeration + '-DCONFIG_HAVE_GETIFADDRS', + '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg +# '-DCONFIG_HAVE_WSACMSGHDR', +# multicast + '-DCONFIG_HAVE_MCAST_JOIN', + '-DCONFIG_HAVE_IP_MREQN', +# sprintf + '-DCONFIG_HAVE_SPRINTF_GROUPING', + '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE', +# GNU getopt + '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ '-pipe' + ], + LIBS = [ +# histogram math + 'm', +# clock_gettime() + 'rt' + ] +) + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Coverage analysis +if env['COVERAGE'] == 'full': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(CCFLAGS = '-ftest-coverage') + env.Append(LINKFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-lgcov') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = '-O2') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = '-gdb') + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = '-pg') + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +if env['WITH_SNMP'] == 'true': + env['SNMP_FLAGS'] = env.ParseFlags('!net-snmp-config --agent-libs'); + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); + lastLIBS = context.env['LIBS']; + lastCCFLAGS= context.env['CCFLAGS']; + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); + context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +def CheckEventFD(context): + context.Message('Checking eventfd...'); + result = context.TryLink(""" +#include +int main(int argc, char**argv) +{ + eventfd(0,0); + return 0; +} +""", '.c') + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck, + 'CheckEventFD': CheckEventFD +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +if conf.CheckEventFD(): + print 'Enabling kernel eventfd notification mechanism.'; + conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097 b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097 new file mode 100644 index 0000000..9a1d029 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097 @@ -0,0 +1,321 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 0, 97 ) +SConsignFile('scons.signatures'+ '-' + platform.system() + '-' + platform.machine()); + +opt = Options(None, ARGUMENTS) +opt.AddOptions ( + (EnumOption ('BUILD', 'build environment', 'debug', ('release', 'debug', 'profile'))), + (EnumOption ('BRANCH', 'branch prediction', 'none', ('none', 'profile', 'seed'))), + (EnumOption ('WITH_GETTEXT', 'l10n support via libintl', 'false', ('true', 'false'))), + (EnumOption ('WITH_GLIB', 'Build GLib dependent modules', 'false', ('true', 'false'))), + (EnumOption ('COVERAGE', 'test coverage', 'none', ('none', 'full'))), + (EnumOption ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', ('true', 'false'))), + (EnumOption ('WITH_HTTP', 'HTTP administration', 'false', ('true', 'false'))), + (EnumOption ('WITH_SNMP', 'SNMP administration', 'false', ('true', 'false'))), + (EnumOption ('WITH_CHECK', 'Check test system', 'false', ('true', 'false'))), + (EnumOption ('WITH_TEST', 'Network test system', 'false', ('true', 'false'))), + (EnumOption ('WITH_CC', 'C++ Examples', 'true', ('true', 'false'))), + (EnumOption ('WITH_EXAMPLES', 'Examples', 'true', ('true', 'false'))), + (EnumOption ('WITH_NCURSES', 'NCURSES examples', 'false', ('true', 'false'))), + (EnumOption ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', ('true', 'false'))), +) + +#----------------------------------------------------------------------------- +# Dependencies + +env = Environment(); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment(ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', + '-Wunsafe-loop-optimizations', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', + '-pedantic', +# C99 + '-std=gnu99', + '-D_XOPEN_SOURCE=600', +# re-entrant libc + '-D_REENTRANT', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R', + '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header + '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system + '-DCONFIG_HAVE_PROC', +# example: crash handling + '-DCONFIG_HAVE_BACKTRACE', +# timing + '-DCONFIG_HAVE_PSELECT', + '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', +# event handling + '-DCONFIG_HAVE_POLL', + '-DCONFIG_HAVE_EPOLL', +# interface enumeration + '-DCONFIG_HAVE_GETIFADDRS', + '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg +# '-DCONFIG_HAVE_WSACMSGHDR', +# multicast + '-DCONFIG_HAVE_MCAST_JOIN', + '-DCONFIG_HAVE_IP_MREQN', +# sprintf + '-DCONFIG_HAVE_SPRINTF_GROUPING', + '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE', +# GNU getopt + '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ '-pipe' + ], + LIBS = [ +# histogram math + 'm', +# clock_gettime() + 'rt' + ], + PROTOBUF_CCFLAGS = '-I/miru/projects/protobuf/protobuf-2.1.0/src', + PROTOBUF_LIBS = '/miru/projects/protobuf/protobuf-2.1.0/src/.libs/libprotobuf.a', + PROTOBUF_PROTOC = '/miru/projects/protobuf/protobuf-2.1.0/src/protoc' +) +opt.Update (env) + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Coverage analysis +if env['COVERAGE'] == 'full': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(CCFLAGS = '-ftest-coverage') + env.Append(LINKFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-lgcov') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = '-O2') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = '-gdb') + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O0','-pg'], LINKFLAGS = '-pg') + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +env['SNMP_FLAGS'] = { + 'CCFLAGS' : [], + 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ], +}; + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); + lastLIBS = context.env['LIBS']; + lastCCFLAGS= context.env['CCFLAGS']; + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); + context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +def CheckEventFD(context): + context.Message('Checking eventfd...'); + result = context.TryLink(""" +#include +int main(int argc, char**argv) +{ + eventfd(0,0); + return 0; +} +""", '.c') + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck, + 'CheckEventFD': CheckEventFD +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Enabling extra Red Hat dependencies for Net-SNMP.'; + conf.env['SNMP_FLAGS']['LIBS'].append(['librpm', 'libsensors', 'libdl', 'libwrap']); + lastLIBS = conf.env['LIBS']; + conf.env.ParseConfig('perl -MExtUtils::Embed -e ldopts'); + conf.env['SNMP_FLAGS']['LIBS'].append(conf.env['LIBS']); + conf.env.Replace(LIBS = lastLIBS); + if not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +if conf.CheckEventFD(): + print 'Enabling kernel eventfd notification mechanism.'; + conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.intelc b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.intelc new file mode 100644 index 0000000..e2099d5 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.intelc @@ -0,0 +1,331 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 0, 97 ) +SConsignFile('scons.signatures'+ '-' + platform.system() + '-' + platform.machine() + '-intelc'); + +opt = Options(None, ARGUMENTS) +opt.AddOptions ( + (EnumOption ('BUILD', 'build environment', 'debug', ('release', 'debug', 'profile'))), + (EnumOption ('BRANCH', 'branch prediction', 'none', ('none', 'profile', 'seed'))), + (EnumOption ('WITH_GETTEXT', 'l10n support via libintl', 'false', ('true', 'false'))), + (EnumOption ('WITH_GLIB', 'Build GLib dependent modules', 'false', ('true', 'false'))), + (EnumOption ('COVERAGE', 'test coverage', 'none', ('none', 'full'))), + (EnumOption ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', ('true', 'false'))), + (EnumOption ('WITH_HTTP', 'HTTP administration', 'false', ('true', 'false'))), + (EnumOption ('WITH_SNMP', 'SNMP administration', 'false', ('true', 'false'))), + (EnumOption ('WITH_CHECK', 'Check test system', 'false', ('true', 'false'))), + (EnumOption ('WITH_TEST', 'Network test system', 'false', ('true', 'false'))), + (EnumOption ('WITH_CC', 'C++ Examples', 'true', ('true', 'false'))), + (EnumOption ('WITH_EXAMPLES', 'Examples', 'true', ('true', 'false'))), + (EnumOption ('WITH_NCURSES', 'NCURSES examples', 'false', ('true', 'false'))), + (EnumOption ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', ('true', 'false'))), +) + +#----------------------------------------------------------------------------- +# Dependencies + +def force_intelc(env): + env.Tool('intelc', version='11.1', topdir='/opt/intel/Compiler/11.1/064/bin/intel64'); + +env = Environment(); +force_intelc(env); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment(ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', +# '-Wextra', +# '-Wfloat-equal', + '-Wshadow', +# '-Wunsafe-loop-optimizations', + '-Wpointer-arith', +# '-Wbad-function-cast', +# '-Wcast-qual', +# '-Wcast-align', + '-Wwrite-strings', +# '-Waggregate-return', + '-Wstrict-prototypes', +# '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', +# '-Wmissing-noreturn', +# '-Wmissing-format-attribute', +# '-Wredundant-decls', +# '-Wnested-externs', + '-Winline', +# 981: operands are evaluated in unspecified order + '-wd981', +# 2259: non-pointer conversion from "*" to "*" may lose significant bits + '-wd2259', +# '-pedantic', +# C99 + '-std=c99', + '-D_XOPEN_SOURCE=600', + '-gcc-version=420', +# re-entrant libc + '-D_REENTRANT', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R', + '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header + '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system + '-DCONFIG_HAVE_PROC', +# example: crash handling + '-DCONFIG_HAVE_BACKTRACE', +# timing + '-DCONFIG_HAVE_PSELECT', + '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', +# event handling + '-DCONFIG_HAVE_POLL', + '-DCONFIG_HAVE_EPOLL', +# interface enumeration + '-DCONFIG_HAVE_GETIFADDRS', + '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg +# '-DCONFIG_HAVE_WSACMSGHDR', +# multicast + '-DCONFIG_HAVE_MCAST_JOIN', + '-DCONFIG_HAVE_IP_MREQN', +# sprintf + '-DCONFIG_HAVE_SPRINTF_GROUPING', + '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE', +# GNU getopt + '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ '-pipe' + ], + LIBS = [ +# histogram math + 'm', +# clock_gettime() + 'rt' + ], + PROTOBUF_CCFLAGS = '-I/miru/projects/protobuf/protobuf-2.1.0/src', + PROTOBUF_LIBS = '/miru/projects/protobuf/protobuf-2.1.0/src/.libs/libprotobuf.a', + PROTOBUF_PROTOC = '/miru/projects/protobuf/protobuf-2.1.0/src/protoc' +) +opt.Update (env) +force_intelc(env); + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Coverage analysis +if env['COVERAGE'] == 'full': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(CCFLAGS = '-ftest-coverage') + env.Append(LINKFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-lgcov') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = '-O2') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = '-gdb') + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O0','-pg'], LINKFLAGS = '-pg') + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +env['SNMP_FLAGS'] = { + 'CCFLAGS' : [], + 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ], +}; + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); + lastLIBS = context.env['LIBS']; + lastCCFLAGS= context.env['CCFLAGS']; + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); + context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +def CheckEventFD(context): + context.Message('Checking eventfd...'); + result = context.TryLink(""" +#include +int main(int argc, char**argv) +{ + eventfd(0,0); + return 0; +} +""", '.c') + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck, + 'CheckEventFD': CheckEventFD +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Enabling extra Red Hat dependencies for Net-SNMP.'; + conf.env['SNMP_FLAGS']['LIBS'].append(['librpm', 'libsensors', 'libdl', 'libwrap']); + lastLIBS = conf.env['LIBS']; + conf.env.ParseConfig('perl -MExtUtils::Embed -e ldopts'); + conf.env['SNMP_FLAGS']['LIBS'].append(conf.env['LIBS']); + conf.env.Replace(LIBS = lastLIBS); + if not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +if conf.CheckEventFD(): + print 'Enabling kernel eventfd notification mechanism.'; + conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '-intelc/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.mingw64 b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.mingw64 new file mode 100644 index 0000000..59b8063 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.mingw64 @@ -0,0 +1,325 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 0, 97 ) +SConsignFile('scons.signatures'+ '-Win64-' + platform.machine()); + +opt = Options(None, ARGUMENTS) +opt.AddOptions ( + (EnumOption ('BUILD', 'build environment', 'debug', ('release', 'debug', 'profile'))), + (EnumOption ('BRANCH', 'branch prediction', 'none', ('none', 'profile', 'seed'))), + (EnumOption ('WITH_GETTEXT', 'l10n support via libintl', 'false', ('true', 'false'))), + (EnumOption ('WITH_GLIB', 'Build GLib dependent modules', 'false', ('true', 'false'))), + (EnumOption ('COVERAGE', 'test coverage', 'none', ('none', 'full'))), + (EnumOption ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', ('true', 'false'))), + (EnumOption ('WITH_HTTP', 'HTTP administration', 'false', ('true', 'false'))), + (EnumOption ('WITH_SNMP', 'SNMP administration', 'false', ('true', 'false'))), + (EnumOption ('WITH_CHECK', 'Check test system', 'false', ('true', 'false'))), + (EnumOption ('WITH_TEST', 'Network test system', 'false', ('true', 'false'))), +# C++ broken scope + (EnumOption ('WITH_CC', 'C++ examples', 'false', ('true', 'false'))), + (EnumOption ('WITH_EXAMPLES', 'Examples', 'true', ('true', 'false'))), + (EnumOption ('WITH_NCURSES', 'NCURSES examples', 'false', ('true', 'false'))), + (EnumOption ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', ('true', 'false'))), +) + +#----------------------------------------------------------------------------- +# Dependencies + +def force_mingw(env): + env.PrependENVPath('PATH', '/opt/mingw/bin'); + env.Tool('crossmingw64', toolpath=['.']); + +env = Environment(); +force_mingw(env); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('PKG_CONFIG_PATH=win64/lib/pkgconfig pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('PKG_CONFIG_PATH=win64/lib/pkgconfig pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment(ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', + '-Wunsafe-loop-optimizations', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', + '-pedantic', +# C99 + '-std=gnu99', + '-D_GNU_SOURCE', + '-D_WIN32_WINNT=0x0501', +# re-entrant libc + '-D_REENTRANT', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header +# '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system +# '-DCONFIG_HAVE_PROC', +# example: crash handling +# '-DCONFIG_HAVE_BACKTRACE', +# timing +# '-DCONFIG_HAVE_PSELECT', +# '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', +# event handling +# '-DCONFIG_HAVE_POLL', +# '-DCONFIG_HAVE_EPOLL', +# interface enumeration +# '-DCONFIG_HAVE_GETIFADDRS', +# '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg + '-DCONFIG_HAVE_WSACMSGHDR', +# multicast +# '-DCONFIG_HAVE_MCAST_JOIN', +# '-DCONFIG_HAVE_IP_MREQN', +# sprintf +# '-DCONFIG_HAVE_SPRINTF_GROUPING', +# '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE' +# GNU getopt +# '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ '-pipe' + ], + LIBS = [ + 'iphlpapi.lib', + 'ws2_32.lib' + ] +) +opt.Update (env) +force_mingw(env); + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Coverage analysis +if env['COVERAGE'] == 'full': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(CCFLAGS = '-ftest-coverage') + env.Append(LINKFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-lgcov') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = '-O2') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = '-gdb') + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O0','-pg'], LINKFLAGS = '-pg') + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!PKG_CONFIG_PATH=win64/lib/pkgconfig pkg-config --cflags --libs glib-2.0 gthread-2.0'); + env.MergeFlags('-Iwin/include -Lwin64/lib'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +env['SNMP_FLAGS'] = { + 'CCFLAGS' : [], + 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ], +}; + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); + lastLIBS = context.env['LIBS']; + lastCCFLAGS= context.env['CCFLAGS']; + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); + context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('PKG_CONFIG_PATH=win64/lib/pkgconfig pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +def CheckEventFD(context): + context.Message('Checking eventfd...'); + result = context.TryLink(""" +#include +int main(int argc, char**argv) +{ + eventfd(0,0); + return 0; +} +""", '.c') + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck, + 'CheckEventFD': CheckEventFD +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Enabling extra Red Hat dependencies for Net-SNMP.'; + conf.env['SNMP_FLAGS']['LIBS'].append(['librpm', 'libsensors', 'libdl', 'libwrap']); + lastLIBS = conf.env['LIBS']; + conf.env.ParseConfig('perl -MExtUtils::Embed -e ldopts'); + conf.env['SNMP_FLAGS']['LIBS'].append(conf.env['LIBS']); + conf.env.Replace(LIBS = lastLIBS); + if not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +if conf.CheckEventFD(): + print 'Enabling kernel eventfd notification mechanism.'; + conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-Win64-' + platform.machine() + '/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm89'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript89'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.sunstudio b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.sunstudio new file mode 100644 index 0000000..1664b9b --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.sunstudio @@ -0,0 +1,312 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 0, 97 ) +SConsignFile('scons.signatures'+ '-' + platform.system() + '-' + platform.machine() + '-sunstudio'); + +opt = Options(None, ARGUMENTS) +opt.AddOptions ( + (EnumOption ('BUILD', 'build environment', 'debug', ('release', 'debug', 'profile'))), + (EnumOption ('BRANCH', 'branch prediction', 'none', ('none', 'profile', 'seed'))), + (EnumOption ('WITH_GETTEXT', 'l10n support via libintl', 'false', ('true', 'false'))), + (EnumOption ('WITH_GLIB', 'Build GLib dependent modules', 'false', ('true', 'false'))), + (EnumOption ('COVERAGE', 'test coverage', 'none', ('none', 'full'))), + (EnumOption ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', ('true', 'false'))), + (EnumOption ('WITH_HTTP', 'HTTP administration', 'false', ('true', 'false'))), + (EnumOption ('WITH_SNMP', 'SNMP administration', 'false', ('true', 'false'))), + (EnumOption ('WITH_CHECK', 'Check test system', 'false', ('true', 'false'))), + (EnumOption ('WITH_TEST', 'Network test system', 'false', ('true', 'false'))), + (EnumOption ('WITH_CC', 'C++ Examples', 'true', ('true', 'false'))), + (EnumOption ('WITH_EXAMPLES', 'Examples', 'true', ('true', 'false'))), + (EnumOption ('WITH_NCURSES', 'NCURSES examples', 'false', ('true', 'false'))), + (EnumOption ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', ('true', 'false'))), +) + +#----------------------------------------------------------------------------- +# Dependencies + +def force_sunstudio(env): + env.PrependENVPath('PATH', '/opt/sun/sunstudio12.1/bin'); + env.Tool('sunc++'); + env.Tool('suncc'); + env.Tool('sunlink'); + env.Tool('sunar'); + +env = Environment(); +force_sunstudio(env); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment(ENV = os.environ, + CCFLAGS = [ '-v', +# C99 + '-xc99=all', + '-D_XOPEN_SOURCE=600', + '-D__EXTENSIONS__', + '-DBSD_COMP', + '-D_BSD_SOURCE', +# re-entrant libc + '-D_REENTRANT', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R', + '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header + '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system + '-DCONFIG_HAVE_PROC', +# example: crash handling + '-DCONFIG_HAVE_BACKTRACE', +# timing + '-DCONFIG_HAVE_PSELECT', + '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', +# event handling + '-DCONFIG_HAVE_POLL', + '-DCONFIG_HAVE_EPOLL', +# interface enumeration + '-DCONFIG_HAVE_GETIFADDRS', + '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg +# '-DCONFIG_HAVE_WSACMSGHDR', +# multicast + '-DCONFIG_HAVE_MCAST_JOIN', + '-DCONFIG_HAVE_IP_MREQN', +# sprintf + '-DCONFIG_HAVE_SPRINTF_GROUPING', + '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE', +# GNU getopt + '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ + ], + LIBS = [ +# histogram math + 'm', +# clock_gettime() + 'rt' + ], + PROTOBUF_CCFLAGS = '-I/miru/projects/protobuf/protobuf-2.1.0/src', + PROTOBUF_LIBS = '/miru/projects/protobuf/protobuf-2.1.0/src/.libs/libprotobuf.a', + PROTOBUF_PROTOC = '/miru/projects/protobuf/protobuf-2.1.0/src/protoc' +) +opt.Update (env) +force_sunstudio(env); + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Coverage analysis +if env['COVERAGE'] == 'full': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(CCFLAGS = '-ftest-coverage') + env.Append(LINKFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-lgcov') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = '-O2') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG','-g'], LINKFLAGS = '-g') + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O0','-pg'], LINKFLAGS = '-pg') + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +env['SNMP_FLAGS'] = { + 'CCFLAGS' : [], + 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ], +}; + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); + lastLIBS = context.env['LIBS']; + lastCCFLAGS= context.env['CCFLAGS']; + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); + context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +def CheckEventFD(context): + context.Message('Checking eventfd...'); + result = context.TryLink(""" +#include +int main(int argc, char**argv) +{ + eventfd(0,0); + return 0; +} +""", '.c') + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck, + 'CheckEventFD': CheckEventFD +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Enabling extra Red Hat dependencies for Net-SNMP.'; + conf.env['SNMP_FLAGS']['LIBS'].append(['librpm', 'libsensors', 'libdl', 'libwrap']); + lastLIBS = conf.env['LIBS']; + conf.env.ParseConfig('perl -MExtUtils::Embed -e ldopts'); + conf.env['SNMP_FLAGS']['LIBS'].append(conf.env['LIBS']); + conf.env.Replace(LIBS = lastLIBS); + if not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +if conf.CheckEventFD(): + print 'Enabling kernel eventfd notification mechanism.'; + conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '-sunstudio/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Debian4 b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Debian4 new file mode 100644 index 0000000..952013e --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Debian4 @@ -0,0 +1,281 @@ +# -*- mode: python -*- +# OpenPGM build script + +import os +import time +import sys + +EnsureSConsVersion( 0, 97 ) +SConsignFile('scons.signatures'); + +opt = Options(None, ARGUMENTS) +opt.AddOptions ( + (EnumOption ('BUILD', 'build environment', 'debug', ('release', 'debug', 'profile'))), + (EnumOption ('BRANCH', 'branch prediction', 'none', ('none', 'profile', 'seed'))), + (EnumOption ('COVERAGE', 'test coverage', 'none', ('none', 'full'))), + (EnumOption ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', ('true', 'false'))), + (EnumOption ('WITH_HTTP', 'HTTP administration', 'true', ('true', 'false'))), + (EnumOption ('WITH_SNMP', 'SNMP administration', 'true', ('true', 'false'))), + (EnumOption ('WITH_CHECK', 'Check test system', 'false', ('true', 'false'))), + (EnumOption ('WITH_TEST', 'Network test system', 'false', ('true', 'false'))), + (EnumOption ('WITH_EXAMPLES', 'Examples', 'true', ('true', 'false'))), + (EnumOption ('WITH_NCURSES', 'NCURSES examples', 'false', ('true', 'false'))), + (EnumOption ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', ('true', 'false'))), + (EnumOption ('WITH_PLUS', 'libpgmplus GPL library', 'false', ('true', 'false'))), +) + +#----------------------------------------------------------------------------- +# Dependencies + +env = Environment(); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' + Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' + Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' + Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment(ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', + '-Wunsafe-loop-optimizations', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', + '-pedantic', + '-std=gnu99', + '--param', 'max-inline-insns-single=600', + '-D_REENTRANT', + '-D_GNU_SOURCE', + '-D__need_IOV_MAX', + '-DCONFIG_16BIT_CHECKSUM', + '-DCONFIG_HAVE_PROC', + '-DCONFIG_HAVE_BACKTRACE', + '-DCONFIG_HAVE_PSELECT', + '-DCONFIG_HAVE_POLL', + '-DCONFIG_HAVE_EPOLL', + '-DCONFIG_HAVE_CLOCK_GETTIME', + '-DCONFIG_HAVE_CLOCK_NANOSLEEP', + '-DCONFIG_HAVE_NANOSLEEP', + '-DCONFIG_HAVE_USLEEP', + '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', + '-DCONFIG_HAVE_IFR_NETMASK', + '-DCONFIG_HAVE_GETIFADDRS', + '-DCONFIG_HAVE_GETHOSTBYNAME2', + '-DCONFIG_HAVE_GETPROTOBYNAME_R', + '-DCONFIG_HAVE_MCAST_JOIN', + '-DCONFIG_HAVE_IP_MREQN', + '-DCONFIG_HAVE_DSO_VISIBILITY', + '-DCONFIG_BIND_INADDR_ANY', + '-DCONFIG_GALOIS_MUL_LUT', + ], + LINKFLAGS = [ '-pipe', + '-lm' + ] +) +opt.Update (env) + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Coverage analysis +if env['COVERAGE'] == 'full': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(CCFLAGS = '-ftest-coverage') + env.Append(LINKFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-lgcov') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = '-O2') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = '-ggdb', LINKFLAGS = '-gdb') + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = '-pg') + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +env.ParseConfig('pkg-config --cflags --libs glib-2.0 gthread-2.0'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +env['SNMP_FLAGS'] = { + 'CCFLAGS' : [], + 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ], +}; + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); + lastLIBS = context.env['LIBS']; + lastCCFLAGS= context.env['CCFLAGS']; + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); + context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +def CheckEventFD(context): + context.Message('Checking eventfd...'); + result = context.TryLink(""" +#include +int main(int argc, char**argv) +{ + eventfd(0,0); + return 0; +} +""", '.c') + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck, + 'CheckEventFD': CheckEventFD +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Enabling extra Red Hat dependencies for Net-SNMP.'; + conf.env['SNMP_FLAGS']['LIBS'].append(['librpm', 'libsensors', 'libdl', 'libwrap']); + lastLIBS = conf.env['LIBS']; + conf.env.ParseConfig('perl -MExtUtils::Embed -e ldopts'); + conf.env['SNMP_FLAGS']['LIBS'].append(conf.env['LIBS']); + conf.env.Replace(LIBS = lastLIBS); + if not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +if conf.CheckEventFD(): + print 'Enabling kernel eventfd notification mechanism.'; + conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); + +env = conf.Finish(); + +# DSO visibility flags +if '-DCONFIG_HAVE_DSO_VISIBILITY' in env['CCFLAGS']: + env.Append(CCFLAGS = '-DPGM_GNUC_INTERNAL=G_GNUC_INTERNAL') +else: + env.Append(CCFLAGS = '-DPGM_GNUC_INTERNAL=') + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +SConscript(ref_node + 'SConscript.libpgm'); +SConscript(ref_node + 'SConscript.libpgmex'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.FreeBSD80 b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.FreeBSD80 new file mode 100644 index 0000000..4d2b9a5 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.FreeBSD80 @@ -0,0 +1,337 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 1, 0 ) +SConsignFile('scons.signatures' + '-' + platform.system() + '-' + platform.machine()); + +vars = Variables() +vars.AddVariables ( + EnumVariable ('BUILD', 'build environment', 'debug', + allowed_values=('release', 'debug', 'profile')), + EnumVariable ('BRANCH', 'branch prediction', 'none', + allowed_values=('none', 'profile', 'seed')), + EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', + allowed_values=('true', 'false')), + EnumVariable ('COVERAGE', 'test coverage', 'none', + allowed_values=('none', 'full')), + EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CHECK', 'Check test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_TEST', 'Network test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CC', 'C++ examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', + allowed_values=('true', 'false')), +) + +#----------------------------------------------------------------------------- +# Dependencies + +env = Environment(); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment( + variables = vars, + ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', + '-Wunsafe-loop-optimizations', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', + '-pedantic', +# C99 + '-std=gnu99', +# re-entrant libc + '-D_REENTRANT', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header +# '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system +# '-DCONFIG_HAVE_PROC', +# example: crash handling +# '-DCONFIG_HAVE_BACKTRACE', +# timing + '-DCONFIG_HAVE_PSELECT', +# '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', +# '-DCONFIG_HAVE_HPET', +# event handling + '-DCONFIG_HAVE_POLL', +# '-DCONFIG_HAVE_EPOLL', +# interface enumeration + '-DCONFIG_HAVE_GETIFADDRS', + '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg +# '-DCONFIG_HAVE_WSACMSGHDR', +# multicast +# '-DCONFIG_HAVE_MCAST_JOIN', + '-DCONFIG_HAVE_IP_MREQN', +# sprintf + '-DCONFIG_HAVE_SPRINTF_GROUPING', + '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD + '-DCONFIG_HOST_ORDER_IP_LEN', + '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE', +# GNU getopt + '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ '-pipe' + ], + LIBS = [ +# histogram math + 'm', +# clock_gettime() + 'rt', +# ftime() + 'compat', +# POSIX threads + 'pthread' + ] +) + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Coverage analysis +if env['COVERAGE'] == 'full': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(CCFLAGS = '-ftest-coverage') + env.Append(LINKFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-lgcov') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = '-O2') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = '-gdb') + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = '-pg') + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +env['SNMP_FLAGS'] = { + 'CCFLAGS' : [], + 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ], +}; + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); + lastLIBS = context.env['LIBS']; + lastCCFLAGS= context.env['CCFLAGS']; + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); + context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +def CheckEventFD(context): + context.Message('Checking eventfd...'); + result = context.TryLink(""" +#include +int main(int argc, char**argv) +{ + eventfd(0,0); + return 0; +} +""", '.c') + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck, + 'CheckEventFD': CheckEventFD +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Enabling extra Red Hat dependencies for Net-SNMP.'; + conf.env['SNMP_FLAGS']['LIBS'].append(['librpm', 'libsensors', 'libdl', 'libwrap']); + lastLIBS = conf.env['LIBS']; + conf.env.ParseConfig('perl -MExtUtils::Embed -e ldopts'); + conf.env['SNMP_FLAGS']['LIBS'].append(conf.env['LIBS']); + conf.env.Replace(LIBS = lastLIBS); + if not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +if conf.CheckEventFD(): + print 'Enabling kernel eventfd notification mechanism.'; + conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.OpenSolaris b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.OpenSolaris new file mode 100644 index 0000000..c9833ed --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.OpenSolaris @@ -0,0 +1,310 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 1, 0 ) +SConsignFile('scons.signatures'+ '-OpenSolaris-' + platform.machine() + '-gcc'); + +vars = Variables() +vars.AddVariables ( + EnumVariable ('BUILD', 'build environment', 'debug', + allowed_values=('release', 'debug', 'profile')), + EnumVariable ('BRANCH', 'branch prediction', 'none', + allowed_values=('none', 'profile', 'seed')), + EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CHECK', 'Check test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_TEST', 'Network test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CC', 'C++ examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', + allowed_values=('true', 'false')), +) + +#----------------------------------------------------------------------------- +# Dependencies + +env = Environment(); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment( + variables = vars, + ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', +# '-Wunsafe-loop-optimizations', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', + '-pedantic', +# C99 + '-std=gnu99', + '-D_XOPEN_SOURCE=600', + '-D__EXTENSIONS__', + '-DBSD_COMP', + '-D_BSD_SOURCE', +# re-entrant libc + '-D_REENTRANT', + '-DCONFIG_HAVE_GETPROTOBYNAME_R', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header + '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system +# '-DCONFIG_HAVE_PROC', +# example: crash handling +# '-DCONFIG_HAVE_BACKTRACE', +# timing + '-DCONFIG_HAVE_PSELECT', +# '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', +# '-DCONFIG_HAVE_HPET', +# event handling + '-DCONFIG_HAVE_POLL', +# '-DCONFIG_HAVE_EPOLL', +# interface enumeration +# '-DCONFIG_HAVE_GETIFADDRS', +# '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg +# '-DCONFIG_HAVE_WSACMSGHDR', +# multicast + '-DCONFIG_HAVE_MCAST_JOIN', +# '-DCONFIG_HAVE_IP_MREQN', +# sprintf + '-DCONFIG_HAVE_SPRINTF_GROUPING', +# '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE', +# GNU getopt + '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ '-pipe' + ], + LIBS = [ +# histogram math + 'm', +# clock_gettime() + 'rt', +# Solaris sockets + 'resolv', + 'socket', + 'nsl' + ] +) + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = ['-O2'], LINKFLAGS = []) + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG', '-ggdb'], LINKFLAGS = ['-gdb']) + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = ['-pg']) + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +env['SNMP_FLAGS'] = { + 'CCFLAGS' : [], + 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp', + 'ssl', 'kstat', 'wrap', 'adm' ], + 'CPPPATH' : ['/opt/cws/include'], + 'LIBPATH' : ['/opt/csw/lib'], +}; + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); + lastLIBS = context.env['LIBS']; + lastCCFLAGS= context.env['CCFLAGS']; + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); + context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-OpenSolaris-' + platform.machine() + '-gcc/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.RHEL4 b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.RHEL4 new file mode 100644 index 0000000..1637ea5 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.RHEL4 @@ -0,0 +1,279 @@ +# -*- mode: python -*- +# OpenPGM build script + +import os +import time +import sys + +EnsureSConsVersion( 0, 97 ) +SConsignFile('scons.signatures'); + +opt = Options(None, ARGUMENTS) +opt.AddOptions ( + (EnumOption ('BUILD', 'build environment', 'debug', ('release', 'debug', 'profile'))), + (EnumOption ('BRANCH', 'branch prediction', 'none', ('none', 'profile', 'seed'))), + (EnumOption ('COVERAGE', 'test coverage', 'none', ('none', 'full'))), + (EnumOption ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', ('true', 'false'))), + (EnumOption ('WITH_HTTP', 'HTTP administration', 'true', ('true', 'false'))), + (EnumOption ('WITH_SNMP', 'SNMP administration', 'true', ('true', 'false'))), + (EnumOption ('WITH_CHECK', 'Check test system', 'false', ('true', 'false'))), + (EnumOption ('WITH_TEST', 'Network test system', 'false', ('true', 'false'))), + (EnumOption ('WITH_EXAMPLES', 'Examples', 'true', ('true', 'false'))), + (EnumOption ('WITH_NCURSES', 'NCURSES examples', 'false', ('true', 'false'))), + (EnumOption ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', ('true', 'false'))), + (EnumOption ('WITH_PLUS', 'libpgmplus GPL library', 'false', ('true', 'false'))), +) + +#----------------------------------------------------------------------------- +# Dependencies + +env = Environment(); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('PKG_CONFIG_PATH=/usr/evolution28/lib/pkgconfig:/usr/lib/pkconfig pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('PKG_CONFIG_PATH=/usr/evolution28/lib/pkgconfig:/usr/lib/pkconfig pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' + Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' + Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' + Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment(ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', + '-pedantic', + '-std=gnu99', + '--param', 'max-inline-insns-single=600', + '-D_REENTRANT', + '-D_GNU_SOURCE', + '-D__need_IOV_MAX', + '-DCONFIG_16BIT_CHECKSUM', + '-DCONFIG_HAVE_PROC', + '-DCONFIG_HAVE_BACKTRACE', + '-DCONFIG_HAVE_PSELECT', + '-DCONFIG_HAVE_POLL', + '-DCONFIG_HAVE_EPOLL', + '-DCONFIG_HAVE_CLOCK_GETTIME', + '-DCONFIG_HAVE_CLOCK_NANOSLEEP', + '-DCONFIG_HAVE_NANOSLEEP', + '-DCONFIG_HAVE_USLEEP', + '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', + '-DCONFIG_HAVE_GETIFADDRS', + '-DCONFIG_HAVE_GETHOSTBYNAME2', + '-DCONFIG_HAVE_GETPROTOBYNAME_R', + '-DCONFIG_HAVE_MCAST_JOIN', + '-DCONFIG_HAVE_IP_MREQN', + '-DCONFIG_HAVE_DSO_VISIBILITY', + '-DCONFIG_BIND_INADDR_ANY', + '-DCONFIG_GALOIS_MUL_LUT', + ], + LINKFLAGS = [ '-pipe', + '-lm' + ] +) +opt.Update (env) + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Coverage analysis +if env['COVERAGE'] == 'full': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(CCFLAGS = '-ftest-coverage') + env.Append(LINKFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-lgcov') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = '-O2') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = '-ggdb', LINKFLAGS = '-gdb') + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = '-pg') + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +env.ParseConfig('PKG_CONFIG_PATH=/usr/evolution28/lib/pkgconfig:/usr/lib/pkconfig pkg-config --cflags --libs glib-2.0 gthread-2.0'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +env['SNMP_FLAGS'] = { + 'CCFLAGS' : [], + 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ], +}; + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); + lastLIBS = context.env['LIBS']; + lastCCFLAGS= context.env['CCFLAGS']; + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); + context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +def CheckEventFD(context): + context.Message('Checking eventfd...'); + result = context.TryLink(""" +#include +int main(int argc, char**argv) +{ + eventfd(0,0); + return 0; +} +""", '.c') + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck, + 'CheckEventFD': CheckEventFD +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Enabling extra Red Hat dependencies for Net-SNMP.'; + conf.env['SNMP_FLAGS']['LIBS'].append(['librpm', 'libsensors', 'libdl', 'libwrap']); + lastLIBS = conf.env['LIBS']; + conf.env.ParseConfig('perl -MExtUtils::Embed -e ldopts'); + conf.env['SNMP_FLAGS']['LIBS'].append(conf.env['LIBS']); + conf.env.Replace(LIBS = lastLIBS); + if not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +if conf.CheckEventFD(): + print 'Enabling kernel eventfd notification mechanism.'; + conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); + +env = conf.Finish(); + +# DSO visibility flags +if '-DCONFIG_HAVE_DSO_VISIBILITY' in env['CCFLAGS']: + env.Append(CCFLAGS = '-DPGM_GNUC_INTERNAL=G_GNUC_INTERNAL') +else: + env.Append(CCFLAGS = '-DPGM_GNUC_INTERNAL=') + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +SConscript(ref_node + 'SConscript.libpgm'); +SConscript(ref_node + 'SConscript.libpgmex'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.gcc64 b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.gcc64 new file mode 100644 index 0000000..953e120 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.gcc64 @@ -0,0 +1,324 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 1, 0 ) +SConsignFile('scons.signatures' + '-' + platform.system() + '-' + platform.machine() + '-gcc64'); + +vars = Variables() +vars.AddVariables ( + EnumVariable ('BUILD', 'build environment', 'debug', + allowed_values=('release', 'debug', 'profile', 'thirtytwo')), + EnumVariable ('BRANCH', 'branch prediction', 'none', + allowed_values=('none', 'profile', 'seed')), + EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CHECK', 'Check test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_TEST', 'Network test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CC', 'C++ examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_PLUS', 'libpgmplus GPL library', 'false', + allowed_values=('true', 'false')), +) + +#----------------------------------------------------------------------------- +# Dependencies + +def force_gcc(env): + env.PrependENVPath('PATH', '/usr/sfw/bin'); + env.PrependENVPath('PATH', '/opt/glib-gcc64/bin'); + env.PrependENVPath('PATH', '/usr/local/bin'); + env.Tool('gcc'); + env.Tool('g++'); + +env = Environment(); +force_gcc(env); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment( + variables = vars, + ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', +# '-Wunsafe-loop-optimizations', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', + '-pedantic', +# C99 + '-std=gnu99', + '-D_XOPEN_SOURCE=600', + '-D__EXTENSIONS__', + '-DBSD_COMP', + '-D_BSD_SOURCE', +# re-entrant libc + '-D_REENTRANT', + '-DCONFIG_HAVE_GETPROTOBYNAME_R', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header + '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system +# '-DCONFIG_HAVE_PROC', +# example: crash handling +# '-DCONFIG_HAVE_BACKTRACE', +# timing +# '-DCONFIG_HAVE_PSELECT', +# '-DCONFIG_HAVE_RTC', +# '-DCONFIG_HAVE_TSC', +# '-DCONFIG_HAVE_HPET', +# event handling + '-DCONFIG_HAVE_POLL', +# '-DCONFIG_HAVE_EPOLL', +# interface enumeration +# '-DCONFIG_HAVE_GETIFADDRS', +# '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg +# '-DCONFIG_HAVE_WSACMSGHDR', +# multicast + '-DCONFIG_HAVE_MCAST_JOIN', +# '-DCONFIG_HAVE_IP_MREQN', +# sprintf +# '-DCONFIG_HAVE_SPRINTF_GROUPING', +# '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE', +# GNU getopt + '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ '-pipe' + ], + LIBS = [ +# histogram math + 'm', +# clock_gettime() + 'rt', +# Solaris sockets + 'resolv', + 'socket', + 'nsl' + ], + PROTOBUF_CCFLAGS = '-I/opt/glib-gcc64/include', + PROTOBUF_LIBS = '/opt/glib-gcc64/lib/sparcv9/libprotobuf.a', + PROTOBUF_PROTOC = '/opt/glib-gcc64/bin/protoc' +) +force_gcc(env); + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = ['-O2','-m64'], LINKFLAGS = '-m64') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb','-m64'], LINKFLAGS = ['-gdb','-m64']) + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O2','-pg','-m64'], LINKFLAGS = ['-pg','-m64']) + +thirtytwo = env.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = ['-O2','-m32'], LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +env['SNMP_FLAGS'] = { + 'CCFLAGS' : [], + 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp', + 'ssl', 'kstat', 'wrap', 'adm' ], + 'CPPPATH' : ['/opt/cws/include'], + 'LIBPATH' : ['/opt/csw/lib'], +}; + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); + lastLIBS = context.env['LIBS']; + lastCCFLAGS= context.env['CCFLAGS']; + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); + context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '-gcc64/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.sungcc b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.sungcc new file mode 100644 index 0000000..a5be81f --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.sungcc @@ -0,0 +1,321 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 1, 0 ) +SConsignFile('scons.signatures'+ '-' + platform.system() + '-' + platform.machine() + '-sungcc'); + +vars = Variables() +vars.AddVariables ( + EnumVariable ('BUILD', 'build environment', 'debug', + allowed_values=('release', 'debug', 'profile')), + EnumVariable ('BRANCH', 'branch prediction', 'none', + allowed_values=('none', 'profile', 'seed')), + EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CHECK', 'Check test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_TEST', 'Network test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CC', 'C++ examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', + allowed_values=('true', 'false')), +) + +#----------------------------------------------------------------------------- +# Dependencies + +def force_gcc(env): + env.PrependENVPath('PATH', '/usr/sfw/bin'); + env.PrependENVPath('PATH', '/opt/glib-gcc/bin'); + env.Tool('gcc'); + env.Tool('g++'); + +env = Environment(); +force_gcc(env); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment( + variables = vars, + ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', +# '-Wunsafe-loop-optimizations', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', + '-pedantic', +# C99 + '-std=gnu99', + '-D_XOPEN_SOURCE=600', + '-D__EXTENSIONS__', + '-DBSD_COMP', + '-D_BSD_SOURCE', +# re-entrant libc + '-D_REENTRANT', + '-DCONFIG_HAVE_GETPROTOBYNAME_R', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header + '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system +# '-DCONFIG_HAVE_PROC', +# example: crash handling +# '-DCONFIG_HAVE_BACKTRACE', +# timing +# '-DCONFIG_HAVE_PSELECT', +# '-DCONFIG_HAVE_RTC', +# '-DCONFIG_HAVE_TSC', +# '-DCONFIG_HAVE_HPET', +# event handling + '-DCONFIG_HAVE_POLL', +# '-DCONFIG_HAVE_EPOLL', +# interface enumeration +# '-DCONFIG_HAVE_GETIFADDRS', +# '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg +# '-DCONFIG_HAVE_WSACMSGHDR', +# multicast + '-DCONFIG_HAVE_MCAST_JOIN', +# '-DCONFIG_HAVE_IP_MREQN', +# sprintf +# '-DCONFIG_HAVE_SPRINTF_GROUPING', +# '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE', +# GNU getopt + '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ '-pipe' + ], + LIBS = [ +# histogram math + 'm', +# clock_gettime() + 'rt', +# Solaris sockets + 'resolv', + 'socket', + 'nsl' + ], + PROTOBUF_CCFLAGS = '-I/opt/glib-gcc/include', + PROTOBUF_LIBS = '/opt/glib-gcc/lib/libprotobuf.a', + PROTOBUF_PROTOC = '/opt/glib-gcc/bin/protoc' +) +force_gcc(env); + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = ['-O2'], LINKFLAGS = []) + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG', '-ggdb'], LINKFLAGS = ['-gdb']) + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = ['-pg']) + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +env['SNMP_FLAGS'] = { + 'CCFLAGS' : [], + 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp', + 'ssl', 'kstat', 'wrap', 'adm' ], + 'CPPPATH' : ['/opt/cws/include'], + 'LIBPATH' : ['/opt/csw/lib'], +}; + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); + lastLIBS = context.env['LIBS']; + lastCCFLAGS= context.env['CCFLAGS']; + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); + context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '-sungcc/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.sunstudio b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.sunstudio new file mode 100644 index 0000000..435d907 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.sunstudio @@ -0,0 +1,306 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 1, 0 ) +SConsignFile('scons.signatures' + '-' + platform.system() + '-' + platform.machine() + '-sunstudio'); + +vars = Variables() +vars.AddVariables ( + EnumVariable ('BUILD', 'build environment', 'debug', + allowed_values=('release', 'debug', 'profile')), + EnumVariable ('BRANCH', 'branch prediction', 'none', + allowed_values=('none', 'profile', 'seed')), + EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CHECK', 'Check test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_TEST', 'Network test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CC', 'C++ examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_PLUS', 'libpgmplus GPL library', 'false', + allowed_values=('true', 'false')), +) + +#----------------------------------------------------------------------------- +# Dependencies + +def force_sunstudio(env): + env.PrependENVPath('PATH', '/usr/ccs/bin'); + env.PrependENVPath('PATH', '/opt/glib-sunstudio/bin'); + env.PrependENVPath('PATH', '/opt/sunstudio12.1/bin'); + env.Tool('sunc++'); + env.Tool('suncc'); + env.Tool('sunlink'); + env.Tool('sunar'); + +env = Environment(); +force_sunstudio(env); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' + Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' + Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' + Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment( + variables = vars, + ENV = os.environ, + CCFLAGS = [ '-v', +# C99 + '-xc99=all', + '-D_XOPEN_SOURCE=600', + '-D__EXTENSIONS__', + '-DBSD_COMP', + '-D_BSD_SOURCE', +# re-entrant libc + '-D_REENTRANT', + '-DCONFIG_HAVE_GETPROTOBYNAME_R', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header + '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system +# '-DCONFIG_HAVE_PROC', +# example: crash handling +# '-DCONFIG_HAVE_BACKTRACE', +# timing +# '-DCONFIG_HAVE_PSELECT', +# '-DCONFIG_HAVE_RTC', +# '-DCONFIG_HAVE_TSC', +# '-DCONFIG_HAVE_HPET', +# event handling + '-DCONFIG_HAVE_POLL', +# '-DCONFIG_HAVE_EPOLL', +# interface enumeration +# '-DCONFIG_HAVE_GETIFADDRS', +# '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg +# '-DCONFIG_HAVE_WSACMSGHDR', +# multicast + '-DCONFIG_HAVE_MCAST_JOIN', +# '-DCONFIG_HAVE_IP_MREQN', +# sprintf +# '-DCONFIG_HAVE_SPRINTF_GROUPING', +# '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE', +# GNU getopt + '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [], + LIBS = [ +# histogram math + 'm', +# clock_gettime() + 'rt', +# Solaris sockets + 'resolv', + 'socket', + 'nsl' + ], + PROTOBUF_CCFLAGS = '-I/opt/glib-sunstudio/include', + PROTOBUF_LIBS = '/opt/glib-sunstudio/lib/sparcv9/libprotobuf.a', + PROTOBUF_PROTOC = '/opt/glib-sunstudio/bin/protoc' +) +force_sunstudio(env); + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = ['-xO2','-m64'], LINKFLAGS = '-m64') + +# outstanding defect with 12u1 cannot compile extended asm without optimization.:w +# +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG','-xO1', '-g','-m64'], LINKFLAGS = ['-g','-m64']) + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-xO2','-pg','-m64'], LINKFLAGS = ['-pg','-m64']) + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = ['-xO2','-m32'], LINKFLAGS = '-m32'); + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +env['SNMP_FLAGS'] = { + 'CCFLAGS' : [], + 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp', + 'ssl', 'kstat', 'wrap', 'adm' ], + 'CPPPATH' : ['/opt/cws/include'], + 'LIBPATH' : ['/opt/csw/lib'], +}; + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); + lastLIBS = context.env['LIBS']; + lastCCFLAGS= context.env['CCFLAGS']; + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); + context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '-sunstudio/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.clang b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.clang new file mode 100644 index 0000000..7f0a54a --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.clang @@ -0,0 +1,336 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 1, 0 ) +SConsignFile('scons.signatures' + '-' + platform.system() + '-' + platform.machine() + '-clang'); + +vars = Variables() +vars.AddVariables ( + EnumVariable ('BUILD', 'build environment', 'debug', + allowed_values=('release', 'debug', 'profile')), + EnumVariable ('BRANCH', 'branch prediction', 'none', + allowed_values=('none', 'profile', 'seed')), + EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', + allowed_values=('true', 'false')), + EnumVariable ('COVERAGE', 'test coverage', 'none', + allowed_values=('none', 'full')), + EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CHECK', 'Check test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_TEST', 'Network test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CC', 'C++ examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', + allowed_values=('true', 'false')), +) + +#----------------------------------------------------------------------------- +# Dependencies + +def force_clang(env): + env['CC'] = 'clang'; + +env = Environment(); +force_clang(env); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment( + variables = vars, + ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', +# '-Wunsafe-loop-optimizations', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', +# '-pedantic', +# C99 + '-std=gnu99', +# re-entrant libc + '-D_REENTRANT', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R', + '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system + '-DCONFIG_HAVE_PROC', +# example: crash handling + '-DCONFIG_HAVE_BACKTRACE', +# timing + '-DCONFIG_HAVE_PSELECT', + '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', + '-DCONFIG_HAVE_HPET', +# event handling + '-DCONFIG_HAVE_POLL', + '-DCONFIG_HAVE_EPOLL', +# interface enumeration + '-DCONFIG_HAVE_GETIFADDRS', + '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg +# '-DCONFIG_HAVE_WSACMSGHDR', +# multicast + '-DCONFIG_HAVE_MCAST_JOIN', + '-DCONFIG_HAVE_IP_MREQN', +# sprintf + '-DCONFIG_HAVE_SPRINTF_GROUPING', + '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE', +# GNU getopt + '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ '-pipe' + ], + LIBS = [ +# histogram math + 'm', +# clock_gettime() + 'rt' + ] +) +force_clang(env); + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Coverage analysis +if env['COVERAGE'] == 'full': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(CCFLAGS = '-ftest-coverage') + env.Append(LINKFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-lgcov') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = '-O2') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = '-gdb') + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = '-pg') + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +env['SNMP_FLAGS'] = { + 'CCFLAGS' : [], + 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ], +}; + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); + lastLIBS = context.env['LIBS']; + lastCCFLAGS= context.env['CCFLAGS']; + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); + context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +def CheckEventFD(context): + context.Message('Checking eventfd...'); + result = context.TryLink(""" +#include +int main(int argc, char**argv) +{ + eventfd(0,0); + return 0; +} +""", '.c') + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck, + 'CheckEventFD': CheckEventFD +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Enabling extra Red Hat dependencies for Net-SNMP.'; + conf.env['SNMP_FLAGS']['LIBS'].append(['librpm', 'libsensors', 'libdl', 'libwrap']); + lastLIBS = conf.env['LIBS']; + conf.env.ParseConfig('perl -MExtUtils::Embed -e ldopts'); + conf.env['SNMP_FLAGS']['LIBS'].append(conf.env['LIBS']); + conf.env.Replace(LIBS = lastLIBS); + if not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +if conf.CheckEventFD(): + print 'Enabling kernel eventfd notification mechanism.'; + conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '-clang/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.mingw b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.mingw new file mode 100644 index 0000000..5f54704 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.mingw @@ -0,0 +1,330 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 1, 0 ) +SConsignFile('scons.signatures' + '-Win32-' + platform.machine()); + +vars = Variables() +vars.AddVariables ( + EnumVariable ('BUILD', 'build environment', 'debug', + allowed_values=('release', 'debug', 'profile')), + EnumVariable ('BRANCH', 'branch prediction', 'none', + allowed_values=('none', 'profile', 'seed')), + EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', + allowed_values=('true', 'false')), + EnumVariable ('COVERAGE', 'test coverage', 'none', + allowed_values=('none', 'full')), + EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CHECK', 'Check test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_TEST', 'Network test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CC', 'C++ examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', + allowed_values=('true', 'false')), +) + +#----------------------------------------------------------------------------- +# Dependencies + +def force_mingw(env): + env.Tool('crossmingw', toolpath=['.']); + +env = Environment(); +force_mingw(env); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment( + variables = vars, + ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', + '-Wunsafe-loop-optimizations', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', + '-pedantic', +# C99 + '-std=gnu99', + '-D_GNU_SOURCE', + '-D_WIN32_WINNT=0x0501', +# re-entrant libc + '-D_REENTRANT', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header +# '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system +# '-DCONFIG_HAVE_PROC', +# example: crash handling +# '-DCONFIG_HAVE_BACKTRACE', +# timing +# '-DCONFIG_HAVE_PSELECT', +# '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', +# '-DCONFIG_HAVE_HPET', +# event handling +# '-DCONFIG_HAVE_POLL', +# '-DCONFIG_HAVE_EPOLL', +# interface enumeration +# '-DCONFIG_HAVE_GETIFADDRS', +# '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg + '-DCONFIG_HAVE_WSACMSGHDR', +# multicast +# '-DCONFIG_HAVE_MCAST_JOIN', +# '-DCONFIG_HAVE_IP_MREQN', +# sprintf +# '-DCONFIG_HAVE_SPRINTF_GROUPING', +# '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE', +# GNU getopt +# '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ '-pipe' + ], + LIBS = [ + 'iphlpapi.lib', + 'ws2_32.lib' + ] +) +force_mingw(env); + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Coverage analysis +if env['COVERAGE'] == 'full': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(CCFLAGS = '-ftest-coverage') + env.Append(LINKFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-lgcov') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = '-O2') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = ['-gdb']) + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = '-pg') + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --cflags --libs glib-2.0 gthread-2.0'); + env.MergeFlags('-Iwin/include -Lwin/lib'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +if env['WITH_SNMP'] == 'true': + env['SNMP_FLAGS'] = env.ParseFlags('-Iwin/include -Lwin/lib -lnetsnmpagent -lnetsnmphelpers -lnetsnmp'); + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); + lastLIBS = context.env['LIBS']; + lastCCFLAGS= context.env['CCFLAGS']; + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); + context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +def CheckEventFD(context): + context.Message('Checking eventfd...'); + result = context.TryLink(""" +#include +int main(int argc, char**argv) +{ + eventfd(0,0); + return 0; +} +""", '.c') + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck, + 'CheckEventFD': CheckEventFD +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +if conf.CheckEventFD(): + print 'Enabling kernel eventfd notification mechanism.'; + conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-Win32-' + platform.machine() + '/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.mingw-wine b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.mingw-wine new file mode 100644 index 0000000..6af1e0a --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.mingw-wine @@ -0,0 +1,339 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 1, 0 ) +SConsignFile('scons.signatures' + '-Wine-' + platform.machine()); + +vars = Variables() +vars.AddVariables ( + EnumVariable ('BUILD', 'build environment', 'debug', + allowed_values=('release', 'debug', 'profile')), + EnumVariable ('BRANCH', 'branch prediction', 'none', + allowed_values=('none', 'profile', 'seed')), + EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', + allowed_values=('true', 'false')), + EnumVariable ('COVERAGE', 'test coverage', 'none', + allowed_values=('none', 'full')), + EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CHECK', 'Check test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_TEST', 'Network test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CC', 'C++ examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', + allowed_values=('true', 'false')), +) + +#----------------------------------------------------------------------------- +# Dependencies + +def force_mingw(env): + env.Tool('crossmingw', toolpath=['.']); + +env = Environment(); +force_mingw(env); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment( + variables = vars, + ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', + '-Wunsafe-loop-optimizations', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', + '-pedantic', +# C99 + '-std=gnu99', + '-D_GNU_SOURCE', + '-D_WIN32_WINNT=0x0501', +# re-entrant libc + '-D_REENTRANT', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header +# '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system +# '-DCONFIG_HAVE_PROC', +# example: crash handling +# '-DCONFIG_HAVE_BACKTRACE', +# timing +# '-DCONFIG_HAVE_PSELECT', +# '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', +# '-DCONFIG_HAVE_HPET', +# event handling +# '-DCONFIG_HAVE_POLL', +# '-DCONFIG_HAVE_EPOLL', +# interface enumeration +# '-DCONFIG_HAVE_GETIFADDRS', +# '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg +# '-DCONFIG_HAVE_WSACMSGHDR', +# multicast +# '-DCONFIG_HAVE_MCAST_JOIN', +# '-DCONFIG_HAVE_IP_MREQN', +# sprintf +# '-DCONFIG_HAVE_SPRINTF_GROUPING', +# '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support + '-DCONFIG_TARGET_WINE', +# GNU getopt +# '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ '-pipe' + ], + LIBS = [ + 'iphlpapi.lib', + 'ws2_32.lib' + ] +) +force_mingw(env); + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Coverage analysis +if env['COVERAGE'] == 'full': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(CCFLAGS = '-ftest-coverage') + env.Append(LINKFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-lgcov') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = '-O2') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = ['-gdb']) + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = '-pg') + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --cflags --libs glib-2.0 gthread-2.0'); + env.MergeFlags('-Iwin/include -Lwin/lib'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +env['SNMP_FLAGS'] = { + 'CCFLAGS' : [], + 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ], +}; + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); + lastLIBS = context.env['LIBS']; + lastCCFLAGS= context.env['CCFLAGS']; + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); + context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +def CheckEventFD(context): + context.Message('Checking eventfd...'); + result = context.TryLink(""" +#include +int main(int argc, char**argv) +{ + eventfd(0,0); + return 0; +} +""", '.c') + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck, + 'CheckEventFD': CheckEventFD +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Enabling extra Red Hat dependencies for Net-SNMP.'; + conf.env['SNMP_FLAGS']['LIBS'].append(['librpm', 'libsensors', 'libdl', 'libwrap']); + lastLIBS = conf.env['LIBS']; + conf.env.ParseConfig('perl -MExtUtils::Embed -e ldopts'); + conf.env['SNMP_FLAGS']['LIBS'].append(conf.env['LIBS']); + conf.env.Replace(LIBS = lastLIBS); + if not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +if conf.CheckEventFD(): + print 'Enabling kernel eventfd notification mechanism.'; + conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-Wine-' + platform.machine() + '/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/atomic_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/atomic_unittest.c new file mode 100644 index 0000000..a301c28 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/atomic_unittest.c @@ -0,0 +1,166 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for atomic operations. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include + + +/* mock state */ + + +/* mock functions for external references */ + +#define PGM_COMPILATION +#include "pgm/atomic.h" + + +/* target: + * uint32_t + * pgm_atomic_exchange_and_add32 ( + * volatile uint32_t* atomic, + * const uint32_t val + * ) + */ + +START_TEST (test_int32_exchange_and_add_pass_001) +{ + volatile uint32_t atomic = 0; + fail_unless (0 == pgm_atomic_exchange_and_add32 (&atomic, 5)); + fail_unless (5 == atomic); + fail_unless (5 == pgm_atomic_exchange_and_add32 (&atomic, (uint32_t)-10)); + fail_unless ((uint32_t)-5 == atomic); +} +END_TEST + +/* target: + * void + * pgm_atomic_add32 ( + * volatile uint32_t* atomic, + * const uint32_t val + * ) + */ + +START_TEST (test_int32_add_pass_001) +{ + volatile uint32_t atomic = (uint32_t)-5; + pgm_atomic_add32 (&atomic, 20); + fail_unless (15 == atomic); + pgm_atomic_add32 (&atomic, (uint32_t)-35); + fail_unless ((uint32_t)-20 == atomic); +} +END_TEST + +/* ensure wrap around when casting uint32 */ +START_TEST (test_int32_add_pass_002) +{ + volatile uint32_t atomic = 0; + pgm_atomic_add32 (&atomic, UINT32_MAX/2); + fail_unless ((UINT32_MAX/2) == atomic); + pgm_atomic_add32 (&atomic, UINT32_MAX - (UINT32_MAX/2)); + fail_unless (UINT32_MAX == atomic); + pgm_atomic_add32 (&atomic, 1); + fail_unless (0 == atomic); +} +END_TEST + +/* target: + * uint32_t + * pgm_atomic_read32 ( + * volatile uint32_t* atomic + * ) + */ + +START_TEST (test_int32_get_pass_001) +{ + volatile uint32_t atomic = (uint32_t)-20; + fail_unless ((uint32_t)-20 == pgm_atomic_read32 (&atomic)); +} +END_TEST + +/* target: + * void + * pgm_atomic_int32_set ( + * volatile int32_t* atomic, + * const int32_t val + * ) + */ + +START_TEST (test_int32_set_pass_001) +{ + volatile uint32_t atomic = (uint32_t)-20; + pgm_atomic_write32 (&atomic, 5); + fail_unless (5 == atomic); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_exchange_and_add = tcase_create ("exchange-and-add"); + suite_add_tcase (s, tc_exchange_and_add); + tcase_add_test (tc_exchange_and_add, test_int32_exchange_and_add_pass_001); + + TCase* tc_add = tcase_create ("add"); + suite_add_tcase (s, tc_add); + tcase_add_test (tc_add, test_int32_add_pass_001); + tcase_add_test (tc_add, test_int32_add_pass_002); + + TCase* tc_get = tcase_create ("get"); + suite_add_tcase (s, tc_get); + tcase_add_test (tc_get, test_int32_get_pass_001); + + TCase* tc_set = tcase_create ("set"); + suite_add_tcase (s, tc_set); + tcase_add_test (tc_set, test_int32_set_pass_001); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/backtrace.c b/3rdparty/openpgm-svn-r1085/pgm/backtrace.c new file mode 100644 index 0000000..2e9943d --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/backtrace.c @@ -0,0 +1,69 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Dump back trace to stderr and try gdb. + * + * Copyright (c) 2006-2007 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef CONFIG_HAVE_BACKTRACE +# include +# include +# include +# include +# include +#endif +#include +#include + + +void +on_sigsegv ( + G_GNUC_UNUSED int signum + ) +{ +#ifdef CONFIG_HAVE_BACKTRACE + void* array[256]; + char** names; + char cmd[1024]; + int i, size; + gchar *out, *err; + gint exit_status; + + fprintf (stderr, "\n======= Backtrace: =========\n"); + + size = backtrace (array, G_N_ELEMENTS(array)); + names = backtrace_symbols (array, size); + + for (i = 0; i < size; i++) + fprintf (stderr, "%s\n", names[i]); + + free (names); + fflush (stderr); + + sprintf (cmd, "gdb --ex 'attach %ld' --ex 'info threads' --ex 'thread apply all bt' --batch", (long)getpid ()); + if ( g_spawn_command_line_sync (cmd, &out, &err, &exit_status, NULL) ) + { + fprintf (stderr, "======= GDB Backtrace: =========\n"); + fprintf (stderr, "%s\n", out); + } +#endif /* CONFIG_HAVE_BACKTRACE */ + + abort (); +} + +/* eof */ + diff --git a/3rdparty/openpgm-svn-r1085/pgm/checksum.c b/3rdparty/openpgm-svn-r1085/pgm/checksum.c new file mode 100644 index 0000000..959aceb --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/checksum.c @@ -0,0 +1,941 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM checksum routines + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + + +/* locals */ + +static inline uint16_t do_csum (const void*, uint16_t, uint32_t) PGM_GNUC_PURE; +static uint16_t do_csum_8bit (const void*, uint16_t, uint32_t) PGM_GNUC_PURE; +static uint16_t do_csum_16bit (const void*, uint16_t, uint32_t) PGM_GNUC_PURE; +static uint16_t do_csum_32bit (const void*, uint16_t, uint32_t) PGM_GNUC_PURE; +static uint16_t do_csum_64bit (const void*, uint16_t, uint32_t) PGM_GNUC_PURE; +#if defined(__amd64) || defined(__x86_64__) +static uint16_t do_csum_vector (const void*, uint16_t, uint32_t) PGM_GNUC_PURE; +#endif + + +/* endian independent checksum routine + */ + +static +uint16_t +do_csum_8bit ( + const void* addr, + uint16_t len, + uint32_t csum + ) +{ + uint_fast32_t acc; + uint16_t src; + const uint8_t* buf; + + acc = csum; + buf = (const uint8_t*)addr; + while (len > 1) { +/* first byte as most significant */ + src = (*buf++) << 8; +/* second byte as least significant */ + src |= (*buf++); + acc += src; + len -= 2; + } +/* trailing odd byte */ + if (len > 0) { + src = (*buf) << 8; + acc += src; + } + acc = (acc >> 16) + (acc & 0xffff); + acc += (acc >> 16); + return htons ((uint16_t)acc); +} + +static +uint16_t +do_csumcpy_8bit ( + const void* restrict srcaddr, + void* restrict dstaddr, + uint16_t len, + uint32_t csum + ) +{ + uint_fast32_t acc; + const uint8_t*restrict srcbuf; + uint8_t*restrict dstbuf; + uint_fast16_t val16; + + acc = csum; + srcbuf = (const uint8_t*restrict)srcaddr; + dstbuf = (uint8_t*restrict)dstaddr; + while (len > 1) { +/* first byte as most significant */ + val16 = (*dstbuf++ = *srcbuf++) << 8; +/* second byte as least significant */ + val16 |= (*dstbuf++ = *srcbuf++); + acc += val16; + len -= 2; + } +/* trailing odd byte */ + if (len > 0) { + val16 = (*dstbuf = *srcbuf) << 8; + acc += val16; + } + acc = (acc >> 16) + (acc & 0xffff); + acc += (acc >> 16); + return htons ((uint16_t)acc); +} + +static +uint16_t +do_csum_16bit ( + const void* addr, + uint16_t len, + uint32_t csum + ) +{ + uint_fast32_t acc; + const uint8_t* buf; + uint16_t remainder; + uint_fast16_t count8; + bool is_odd; + + acc = csum; + buf = (const uint8_t*)addr; + remainder = 0; + + if (PGM_UNLIKELY(len == 0)) + return acc; + is_odd = ((uintptr_t)buf & 1); +/* align first byte */ + if (PGM_UNLIKELY(is_odd)) { + ((uint8_t*)&remainder)[1] = *buf++; + len--; + } +/* 8-byte unrolls */ + count8 = len >> 3; + while (count8--) { + acc += ((const uint16_t*)buf)[ 0 ]; + acc += ((const uint16_t*)buf)[ 1 ]; + acc += ((const uint16_t*)buf)[ 2 ]; + acc += ((const uint16_t*)buf)[ 3 ]; + buf = &buf[ 8 ]; + } + len %= 8; +/* final 7 bytes */ + while (len > 1) { + acc += ((const uint16_t*)buf)[ 0 ]; + buf = &buf[ 2 ]; + len -= 2; + } +/* trailing odd byte */ + if (len > 0) { + ((uint8_t*)&remainder)[0] = *buf; + } + acc += remainder; + acc = (acc >> 16) + (acc & 0xffff); + acc += (acc >> 16); + if (PGM_UNLIKELY(is_odd)) + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + return acc; +} + +static +uint16_t +do_csumcpy_16bit ( + const void* restrict srcaddr, + void* restrict dstaddr, + uint16_t len, + uint32_t csum + ) +{ + uint_fast32_t acc; + const uint8_t*restrict srcbuf; + uint8_t*restrict dstbuf; + uint16_t remainder; + uint_fast16_t count8; + bool is_odd; + + acc = csum; + srcbuf = (const uint8_t*restrict)srcaddr; + dstbuf = (uint8_t*restrict)dstaddr; + remainder = 0; + + if (PGM_UNLIKELY(len == 0)) + return acc; + is_odd = ((uintptr_t)srcbuf & 1); +/* align first byte */ + if (PGM_UNLIKELY(is_odd)) { + ((uint8_t*restrict)&remainder)[1] = *dstbuf++ = *srcbuf++; + len--; + } +/* 8-byte unrolls, anything larger than 16-byte or less than 8 loses performance */ + count8 = len >> 3; + while (count8--) { + acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; + acc += ((uint16_t*restrict)dstbuf)[ 1 ] = ((const uint16_t*restrict)srcbuf)[ 1 ]; + acc += ((uint16_t*restrict)dstbuf)[ 2 ] = ((const uint16_t*restrict)srcbuf)[ 2 ]; + acc += ((uint16_t*restrict)dstbuf)[ 3 ] = ((const uint16_t*restrict)srcbuf)[ 3 ]; + srcbuf = &srcbuf[ 8 ]; + dstbuf = &dstbuf[ 8 ]; + } + len %= 8; +/* final 7 bytes */ + while (len > 1) { + acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; + srcbuf = &srcbuf[ 2 ]; + dstbuf = &dstbuf[ 2 ]; + len -= 2; + } +/* trailing odd byte */ + if (len > 0) { + ((uint8_t*restrict)&remainder)[0] = *dstbuf = *srcbuf; + } + acc += remainder; + acc = (acc >> 16) + (acc & 0xffff); + acc += (acc >> 16); + if (PGM_UNLIKELY(is_odd)) + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + return acc; +} + +static +uint16_t +do_csum_32bit ( + const void* addr, + uint16_t len, + uint32_t csum + ) +{ + uint_fast32_t acc; + const uint8_t* buf; + uint16_t remainder; + uint_fast16_t count; + bool is_odd; + + acc = csum; + buf = (const uint8_t*)addr; + remainder = 0; + + if (PGM_UNLIKELY(len == 0)) + return acc; + is_odd = ((uintptr_t)buf & 1); +/* align first byte */ + if (PGM_UNLIKELY(is_odd)) { + ((uint8_t*)&remainder)[1] = *buf++; + len--; + } +/* 16-bit words */ + count = len >> 1; + if (count) + { + if ((uintptr_t)buf & 2) { + acc += ((const uint16_t*)buf)[ 0 ]; + buf = &buf[ 2 ]; + count--; + len -= 2; + } +/* 32-bit words */ + count >>= 1; + if (count) + { + uint32_t carry = 0; + while (count) { + acc += carry; + acc += ((const uint32_t*)buf)[ 0 ]; + carry = ((const uint32_t*)buf)[ 0 ] > acc; + buf = &buf[ 4 ]; + count--; + } + acc += carry; + acc = (acc >> 16) + (acc & 0xffff); + } + if (len & 2) { + acc += ((const uint16_t*)buf)[ 0 ]; + buf = &buf[ 2 ]; + } + } +/* trailing odd byte */ + if (len & 1) { + ((uint8_t*)&remainder)[0] = *buf; + } + acc += remainder; + acc = (acc >> 16) + (acc & 0xffff); + acc += (acc >> 16); + if (PGM_UNLIKELY(is_odd)) + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + return acc; +} + +static +uint16_t +do_csumcpy_32bit ( + const void* restrict srcaddr, + void* restrict dstaddr, + uint16_t len, + uint32_t csum + ) +{ + uint_fast32_t acc; + const uint8_t*restrict srcbuf; + uint8_t*restrict dstbuf; + uint16_t remainder; + uint_fast16_t count; + bool is_odd; + + acc = csum; + srcbuf = (const uint8_t*restrict)srcaddr; + dstbuf = (uint8_t*restrict)dstaddr; + remainder = 0; + + if (PGM_UNLIKELY(len == 0)) + return acc; + is_odd = ((uintptr_t)srcbuf & 1); +/* align first byte */ + if (PGM_UNLIKELY(is_odd)) { + ((uint8_t*restrict)&remainder)[1] = *dstbuf++ = *srcbuf++; + len--; + } +/* 16-bit words */ + count = len >> 1; + if (count) + { + if ((uintptr_t)srcbuf & 2) { + acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; + srcbuf = &srcbuf[ 2 ]; + dstbuf = &dstbuf[ 2 ]; + count--; + len -= 2; + } +/* 32-bit words */ + count >>= 1; + if (count) + { + uint32_t carry = 0; + while (count) { + acc += carry; + acc += ((uint32_t*restrict)dstbuf)[ 0 ] = ((const uint32_t*restrict)srcbuf)[ 0 ]; + carry = ((const uint32_t*restrict)dstbuf)[ 0 ] > acc; + srcbuf = &srcbuf[ 4 ]; + dstbuf = &dstbuf[ 4 ]; + count--; + } + acc += carry; + acc = (acc >> 16) + (acc & 0xffff); + } + if (len & 2) { + acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; + srcbuf = &srcbuf[ 2 ]; + dstbuf = &dstbuf[ 2 ]; + } + } +/* trailing odd byte */ + if (len & 1) { + ((uint8_t*restrict)&remainder)[0] = *dstbuf = *srcbuf; + } + acc += remainder; + acc = (acc >> 16) + (acc & 0xffff); + acc += (acc >> 16); + if (PGM_UNLIKELY(is_odd)) + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + return acc; +} + +/* best if architecture has native 64-bit words + */ + +static +uint16_t +do_csum_64bit ( + const void* addr, + uint16_t len, + uint32_t csum + ) +{ + uint_fast64_t acc; + const uint8_t* buf; + uint16_t remainder; + uint_fast16_t count; + bool is_odd; + + acc = csum; + buf = (const uint8_t*)addr; + remainder = 0; + + if (PGM_UNLIKELY(len == 0)) + return acc; + is_odd = ((uintptr_t)buf & 1); +/* align first byte */ + if (PGM_UNLIKELY(is_odd)) { + ((uint8_t*)&remainder)[1] = *buf++; + len--; + } +/* 16-bit words */ + count = len >> 1; + if (count) + { + if ((uintptr_t)buf & 2) { + acc += ((const uint16_t*)buf)[ 0 ]; + buf = &buf[ 2 ]; + count--; + len -= 2; + } +/* 32-bit words */ + count >>= 1; + if (count) + { + if ((uintptr_t)buf & 4) { + acc += ((const uint32_t*)buf)[ 0 ]; + buf = &buf[ 4 ]; + count--; + len -= 4; + } +/* 64-bit words */ + count >>= 1; + if (count) + { + uint_fast64_t carry = 0; + while (count) { + acc += carry; + acc += ((const uint64_t*)buf)[ 0 ]; + carry = ((const uint64_t*)buf)[ 0 ] > acc; + buf = &buf[ 8 ]; + count--; + } + acc += carry; + acc = (acc >> 32) + (acc & 0xffffffff); + } + if (len & 4) { + acc += ((const uint32_t*)buf)[ 0 ]; + buf = &buf[ 4 ]; + } + } + if (len & 2) { + acc += ((const uint16_t*)buf)[ 0 ]; + buf = &buf[ 2 ]; + } + } +/* trailing odd byte */ + if (len & 1) { + ((uint8_t*)&remainder)[0] = *buf; + } + acc += remainder; + acc = (acc >> 32) + (acc & 0xffffffff); + acc = (acc >> 16) + (acc & 0xffff); + acc = (acc >> 16) + (acc & 0xffff); + acc += (acc >> 16); + if (PGM_UNLIKELY(is_odd)) + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + return acc; +} + +static +uint16_t +do_csumcpy_64bit ( + const void* restrict srcaddr, + void* restrict dstaddr, + uint16_t len, + uint32_t csum + ) +{ + uint_fast64_t acc; + const uint8_t* restrict srcbuf; + uint8_t* restrict dstbuf; + uint16_t remainder; + uint_fast16_t count; + bool is_odd; + + acc = csum; + srcbuf = (const uint8_t*restrict)srcaddr; + dstbuf = (uint8_t*restrict)dstaddr; + remainder = 0; + + if (PGM_UNLIKELY(len == 0)) + return acc; + is_odd = ((uintptr_t)srcbuf & 1); +/* align first byte */ + if (PGM_UNLIKELY(is_odd)) { + ((uint8_t*restrict)&remainder)[1] = *dstbuf++ = *srcbuf++; + len--; + } +/* 16-bit words */ + count = len >> 1; + if (count) + { + if ((uintptr_t)srcbuf & 2) { + acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; + srcbuf = &srcbuf[ 2 ]; + dstbuf = &dstbuf[ 2 ]; + count--; + len -= 2; + } +/* 32-bit words */ + count >>= 1; + if (count) + { + if ((uintptr_t)srcbuf & 4) { + acc += ((uint32_t*restrict)dstbuf)[ 0 ] = ((const uint32_t*restrict)srcbuf)[ 0 ]; + srcbuf = &srcbuf[ 4 ]; + dstbuf = &dstbuf[ 4 ]; + count--; + len -= 4; + } +/* 64-bit words */ + count >>= 1; + if (count) + { +/* 64-byte blocks */ + uint_fast64_t carry = 0; + uint_fast16_t count64 = count >> 3; + if (count64) + { + carry = 0; + while (count64) { + acc += carry; + acc += ((uint64_t*restrict)dstbuf)[ 0 ] = ((const uint64_t*restrict)srcbuf)[ 0 ]; + carry = ((const uint64_t*restrict)dstbuf)[ 0 ] > acc; + acc += carry; + acc += ((uint64_t*restrict)dstbuf)[ 1 ] = ((const uint64_t*restrict)srcbuf)[ 1 ]; + carry = ((const uint64_t*restrict)dstbuf)[ 1 ] > acc; + acc += carry; + acc += ((uint64_t*restrict)dstbuf)[ 2 ] = ((const uint64_t*restrict)srcbuf)[ 2 ]; + carry = ((const uint64_t*restrict)dstbuf)[ 2 ] > acc; + acc += carry; + acc += ((uint64_t*restrict)dstbuf)[ 3 ] = ((const uint64_t*restrict)srcbuf)[ 3 ]; + carry = ((const uint64_t*restrict)dstbuf)[ 3 ] > acc; + acc += carry; + acc += ((uint64_t*restrict)dstbuf)[ 4 ] = ((const uint64_t*restrict)srcbuf)[ 4 ]; + carry = ((const uint64_t*restrict)dstbuf)[ 4 ] > acc; + acc += carry; + acc += ((uint64_t*restrict)dstbuf)[ 5 ] = ((const uint64_t*restrict)srcbuf)[ 5 ]; + carry = ((const uint64_t*restrict)dstbuf)[ 5 ] > acc; + acc += carry; + acc += ((uint64_t*restrict)dstbuf)[ 6 ] = ((const uint64_t*restrict)srcbuf)[ 6 ]; + carry = ((const uint64_t*restrict)dstbuf)[ 6 ] > acc; + acc += carry; + acc += ((uint64_t*restrict)dstbuf)[ 7 ] = ((const uint64_t*restrict)srcbuf)[ 7 ]; + carry = ((const uint64_t*restrict)dstbuf)[ 7 ] > acc; + srcbuf = &srcbuf[ 64 ]; + dstbuf = &dstbuf[ 64 ]; + count64--; + } + acc += carry; + acc = (acc >> 32) + (acc & 0xffffffff); + count %= 8; + } + +/* last 56 bytes */ + carry = 0; + while (count) { + acc += carry; + acc += ((uint64_t*restrict)dstbuf)[ 0 ] = ((const uint64_t*restrict)srcbuf)[ 0 ]; + carry = ((const uint64_t*restrict)dstbuf)[ 0 ] > acc; + srcbuf = &srcbuf[ 8 ]; + dstbuf = &dstbuf[ 8 ]; + count--; + } + acc += carry; + acc = (acc >> 32) + (acc & 0xffffffff); + } + if (len & 4) { + acc += ((uint32_t*restrict)dstbuf)[ 0 ] = ((const uint32_t*restrict)srcbuf)[ 0 ]; + srcbuf = &srcbuf[ 4 ]; + dstbuf = &dstbuf[ 4 ]; + } + } + if (len & 2) { + acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; + srcbuf = &srcbuf[ 2 ]; + dstbuf = &dstbuf[ 2 ]; + } + } +/* trailing odd byte */ + if (len & 1) { + ((uint8_t*restrict)&remainder)[0] = *dstbuf = *srcbuf; + } + acc += remainder; + acc = (acc >> 32) + (acc & 0xffffffff); + acc = (acc >> 16) + (acc & 0xffff); + acc = (acc >> 16) + (acc & 0xffff); + acc += (acc >> 16); + if (PGM_UNLIKELY(is_odd)) + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + return acc; +} + +#if defined(__amd64) || defined(__x86_64__) +/* simd instructions unique to AMD/Intel 64-bit, so always little endian. + */ + +static +uint16_t +do_csum_vector ( + const void* addr, + uint16_t len, + uint32_t csum + ) +{ + uint64_t acc; /* fixed size for asm */ + const uint8_t* buf; + uint16_t remainder; /* fixed size for endian swap */ + uint_fast16_t count; + bool is_odd; + + acc = csum; + buf = (const uint8_t*)addr; + remainder = 0; + + if (PGM_UNLIKELY(len == 0)) + return acc; +/* align first byte */ + is_odd = ((uintptr_t)buf & 1); + if (PGM_UNLIKELY(is_odd)) { + ((uint8_t*)&remainder)[1] = *buf++; + len--; + } +/* 16-bit words */ + count = len >> 1; + if (count) + { + if ((uintptr_t)buf & 2) { + acc += ((const uint16_t*)buf)[ 0 ]; + buf = &buf[ 2 ]; + count--; + len -= 2; + } +/* 32-bit words */ + count >>= 1; + if (count) + { + if ((uintptr_t)buf & 4) { + acc += ((const uint32_t*)buf)[ 0 ]; + buf = &buf[ 4 ]; + count--; + len -= 4; + } +/* 64-bit words */ + count >>= 1; + if (count) + { + uint64_t carry = 0; + while (count) { + asm volatile ( "addq %1, %0\n\t" + "adcq %2, %0" + : "=r" (acc) + : "m" (*(const uint64_t*)buf), "r" (carry), "0" (acc) + : "cc" ); + buf = &buf[ 8 ]; + count--; + } + acc += carry; + acc = (acc >> 32) + (acc & 0xffffffff); + } + if (len & 4) { + acc += ((const uint32_t*)buf)[ 0 ]; + buf = &buf[ 4 ]; + } + } + if (len & 2) { + acc += ((const uint16_t*)buf)[ 0 ]; + buf = &buf[ 2 ]; + } + } +/* trailing odd byte */ + if (len & 1) { + ((uint8_t*)&remainder)[0] = *buf; + } + acc += remainder; + acc = (acc >> 32) + (acc & 0xffffffff); + acc = (acc >> 16) + (acc & 0xffff); + acc = (acc >> 16) + (acc & 0xffff); + acc += (acc >> 16); + if (PGM_UNLIKELY(is_odd)) + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + return acc; +} + +static +uint16_t +do_csumcpy_vector ( + const void* restrict srcaddr, + void* restrict dstaddr, + uint16_t len, + uint32_t csum + ) +{ + uint64_t acc; /* fixed size for asm */ + const uint8_t*restrict srcbuf; + uint8_t*restrict dstbuf; + uint16_t remainder; /* fixed size for endian swap */ + uint_fast16_t count; + bool is_odd; + + acc = csum; + srcbuf = (const uint8_t*restrict)srcaddr; + dstbuf = (uint8_t*restrict)dstaddr; + remainder = 0; + + if (PGM_UNLIKELY(len == 0)) + return acc; +/* fill cache line with source buffer, invalidate destination buffer, + * perversly for testing high temporal locality is better than no locality, + * whilst in production no locality may be preferred depending on skb re-use. + */ + pgm_prefetch (srcbuf); + pgm_prefetchw (dstbuf); +/* align first byte */ + is_odd = ((uintptr_t)srcbuf & 1); + if (PGM_UNLIKELY(is_odd)) { + ((uint8_t*restrict)&remainder)[1] = *dstbuf++ = *srcbuf++; + len--; + } +/* 16-bit words */ + count = len >> 1; + if (count) + { + if ((uintptr_t)srcbuf & 2) { + acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; + srcbuf = &srcbuf[ 2 ]; + dstbuf = &dstbuf[ 2 ]; + count--; + len -= 2; + } +/* 32-bit words */ + count >>= 1; + if (count) + { + if ((uintptr_t)srcbuf & 4) { + acc += ((uint32_t*restrict)dstbuf)[ 0 ] = ((const uint32_t*restrict)srcbuf)[ 0 ]; + srcbuf = &srcbuf[ 4 ]; + dstbuf = &dstbuf[ 4 ]; + count--; + len -= 4; + } +/* 64-bit words */ + count >>= 1; + if (count) + { +/* 64-byte blocks */ + uint64_t carry = 0; + uint_fast16_t count64 = count >> 3; + + while (count64) + { + pgm_prefetch (&srcbuf[ 64 ]); + pgm_prefetchw (&dstbuf[ 64 ]); + asm volatile ( "movq 0*8(%1), %%r8\n\t" /* load */ + "movq 1*8(%1), %%r9\n\t" + "movq 2*8(%1), %%r10\n\t" + "movq 3*8(%1), %%r11\n\t" + "movq 4*8(%1), %%r12\n\t" + "movq 5*8(%1), %%r13\n\t" + "movq 6*8(%1), %%r14\n\t" + "movq 7*8(%1), %%r15\n\t" + "adcq %%r8, %0\n\t" /* checksum */ + "adcq %%r9, %0\n\t" + "adcq %%r10, %0\n\t" + "adcq %%r11, %0\n\t" + "adcq %%r12, %0\n\t" + "adcq %%r13, %0\n\t" + "adcq %%r14, %0\n\t" + "adcq %%r15, %0\n\t" + "adcq %3, %0\n\t" + "movq %%r8, 0*8(%2)\n\t" /* save */ + "movq %%r9, 1*8(%2)\n\t" + "movq %%r10, 2*8(%2)\n\t" + "movq %%r11, 3*8(%2)\n\t" + "movq %%r12, 4*8(%2)\n\t" + "movq %%r13, 5*8(%2)\n\t" + "movq %%r14, 6*8(%2)\n\t" + "movq %%r15, 7*8(%2)" + : "=r" (acc) + : "r" (srcbuf), "r" (dstbuf), "r" (carry), "0" (acc) + : "cc", "memory", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" ); + srcbuf = &srcbuf[ 64 ]; + dstbuf = &dstbuf[ 64 ]; + count64--; + } + count %= 8; +/* last 56 bytes */ + while (count) { + asm volatile ( "addq %1, %0\n\t" + "adcq %2, %0" + : "=r" (acc) + : "m" (*(const uint64_t*restrict)srcbuf), "r" (carry), "0" (acc) + : "cc" ); + srcbuf = &srcbuf[ 8 ]; + count--; + } + acc += carry; + acc = (acc >> 32) + (acc & 0xffffffff); + } + if (len & 4) { + acc += ((uint32_t*restrict)dstbuf)[ 0 ] = ((const uint32_t*restrict)srcbuf)[ 0 ]; + srcbuf = &srcbuf[ 4 ]; + dstbuf = &dstbuf[ 4 ]; + } + } + if (len & 2) { + acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; + srcbuf = &srcbuf[ 2 ]; + dstbuf = &dstbuf[ 2 ]; + } + } +/* trailing odd byte */ + if (len & 1) { + ((uint8_t*restrict)&remainder)[0] = *dstbuf = *srcbuf; + } + acc += remainder; + acc = (acc >> 32) + (acc & 0xffffffff); + acc = (acc >> 16) + (acc & 0xffff); + acc = (acc >> 16) + (acc & 0xffff); + acc += (acc >> 16); + if (PGM_UNLIKELY(is_odd)) + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + return acc; +} + +#endif + +static inline +uint16_t +do_csum ( + const void* addr, + uint16_t len, + uint32_t csum + ) +{ +#if defined(CONFIG_8BIT_CHECKSUM) + return do_csum_8bit (addr, len, csum); +#elif defined(CONFIG_16BIT_CHECKSUM) + return do_csum_16bit (addr, len, csum); +#elif defined(CONFIG_32BIT_CHECKSUM) + return do_csum_32bit (addr, len, csum); +#elif defined(CONFIG_64BIT_CHECKSUM) + return do_csum_64bit (addr, len, csum); +#elif defined(CONFIG_VECTOR_CHECKSUM) + return do_csum_vector (addr, len, csum); +#else +# error "checksum routine undefined" +#endif +} + +/* Calculate an IP header style checksum + */ + +uint16_t +pgm_inet_checksum ( + const void* addr, + uint16_t len, + uint16_t csum + ) +{ +/* pre-conditions */ + pgm_assert (NULL != addr); + + return ~do_csum (addr, len, csum); +} + +/* Calculate a partial (unfolded) checksum + */ + +uint32_t +pgm_compat_csum_partial ( + const void* addr, + uint16_t len, + uint32_t csum + ) +{ +/* pre-conditions */ + pgm_assert (NULL != addr); + + csum = (csum >> 16) + (csum & 0xffff); + csum += do_csum (addr, len, 0); + csum = (csum >> 16) + (csum & 0xffff); + + return csum; +} + +/* Calculate & copy a partial PGM checksum + */ + +uint32_t +pgm_compat_csum_partial_copy ( + const void* restrict src, + void* restrict dst, + uint16_t len, + uint32_t csum + ) +{ +/* pre-conditions */ + pgm_assert (NULL != src); + pgm_assert (NULL != dst); + +#if defined(CONFIG_8BIT_CHECKSUM) + return do_csumcpy_8bit (src, dst, len, csum); +#elif defined(CONFIG_16BIT_CHECKSUM) + return do_csumcpy_16bit (src, dst, len, csum); +#elif defined(CONFIG_32BIT_CHECKSUM) + return do_csumcpy_32bit (src, dst, len, csum); +#elif defined(CONFIG_64BIT_CHECKSUM) + return do_csumcpy_64bit (src, dst, len, csum); +#elif defined(CONFIG_VECTOR_CHECKSUM) + return do_csumcpy_vector (src, dst, len, csum); +#else + memcpy (dst, src, len); + return pgm_csum_partial (dst, len, csum); +#endif +} + +/* Fold 32 bit checksum accumulator into 16 bit final value. + */ + +uint16_t +pgm_csum_fold ( + uint32_t csum + ) +{ + csum = (csum >> 16) + (csum & 0xffff); + csum += (csum >> 16); + +/* handle special case of no checksum */ + return csum == 0xffff ? csum : ~csum; +} + +/* Add together two unfolded checksum accumulators + */ + +uint32_t +pgm_csum_block_add ( + uint32_t csum, + uint32_t csum2, + const uint16_t offset + ) +{ + if (offset & 1) /* byte magic on odd offset */ + csum2 = ((csum2 & 0xff00ff) << 8) + + ((csum2 >> 8) & 0xff00ff); + + csum += csum2; + return csum + (csum < csum2); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/checksum_perftest.c b/3rdparty/openpgm-svn-r1085/pgm/checksum_perftest.c new file mode 100644 index 0000000..678a066 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/checksum_perftest.c @@ -0,0 +1,807 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * performance tests for PGM checksum routines + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include +#include +#include + + +/* mock state */ + +static unsigned perf_testsize = 0; +static unsigned perf_answer = 0; + + +static +void +mock_setup_100b (void) +{ + perf_testsize = 100; + perf_answer = 0x6ea8; +} + +static +void +mock_setup_200b (void) +{ + perf_testsize = 200; + perf_answer = 0x86e1; +} + +static +void +mock_setup_1500b (void) +{ + perf_testsize = 1500; + perf_answer = 0xec69; +} + +static +void +mock_setup_9kb (void) +{ + perf_testsize = 9000; + perf_answer = 0x576e; +} + +static +void +mock_setup_64kb (void) +{ + perf_testsize = 65535; + perf_answer = 0x3fc0; +} + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + +#define CHECKSUM_DEBUG +#include "checksum.c" + + +static +void +mock_setup (void) +{ + g_assert (pgm_time_init (NULL)); +} + +static +void +mock_teardown (void) +{ + g_assert (pgm_time_shutdown ()); +} + + +/* target: + * guint16 + * pgm_inet_checksum ( + * const void* src, + * guint len, + * int csum + * ) + */ + +START_TEST (test_8bit) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + csum = ~do_csum_8bit (source, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("8-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +/* checksum + memcpy */ +START_TEST (test_8bit_memcpy) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + char* target = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + memcpy (target, source, perf_testsize); + csum = ~do_csum_8bit (target, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("8-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +/* checksum & copy */ +START_TEST (test_8bit_csumcpy) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + char* target = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + csum = ~do_csumcpy_8bit (source, target, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("8-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +START_TEST (test_16bit) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + csum = ~do_csum_16bit (source, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("16-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +START_TEST (test_16bit_memcpy) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + char* target = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + memcpy (target, source, perf_testsize); + csum = ~do_csum_16bit (target, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("16-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +START_TEST (test_16bit_csumcpy) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + char* target = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + csum = ~do_csumcpy_16bit (source, target, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("16-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +START_TEST (test_32bit) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + csum = ~do_csum_32bit (source, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("32-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +START_TEST (test_32bit_memcpy) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + char* target = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + memcpy (target, source, perf_testsize); + csum = ~do_csum_32bit (target, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("32-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +START_TEST (test_32bit_csumcpy) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + char* target = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + csum = ~do_csumcpy_32bit (source, target, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("32-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +START_TEST (test_64bit) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + csum = ~do_csum_64bit (source, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("64-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +START_TEST (test_64bit_memcpy) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + char* target = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + memcpy (target, source, perf_testsize); + csum = ~do_csum_64bit (target, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("64-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +START_TEST (test_64bit_csumcpy) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + char* target = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + csum = ~do_csumcpy_64bit (source, target, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("64-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +#if defined(__amd64) || defined(__x86_64__) +START_TEST (test_vector) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + csum = ~do_csum_vector (source, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("vector/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +START_TEST (test_vector_memcpy) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + char* target = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + memcpy (target, source, perf_testsize); + csum = ~do_csum_vector (target, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("vector/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +START_TEST (test_vector_csumcpy) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + char* target = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + csum = ~do_csumcpy_vector (source, target, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("vector/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST +#endif /* defined(__amd64) || defined(__x86_64__) */ + + + +static +Suite* +make_csum_performance_suite (void) +{ + Suite* s; + + s = suite_create ("Raw checksum performance"); + + TCase* tc_100b = tcase_create ("100b"); + suite_add_tcase (s, tc_100b); + tcase_add_checked_fixture (tc_100b, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_100b, mock_setup_100b, NULL); + tcase_add_test (tc_100b, test_8bit); + tcase_add_test (tc_100b, test_16bit); + tcase_add_test (tc_100b, test_32bit); + tcase_add_test (tc_100b, test_64bit); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_100b, test_vector); +#endif + + TCase* tc_200b = tcase_create ("200b"); + suite_add_tcase (s, tc_200b); + tcase_add_checked_fixture (tc_200b, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_200b, mock_setup_200b, NULL); + tcase_add_test (tc_200b, test_8bit); + tcase_add_test (tc_200b, test_16bit); + tcase_add_test (tc_200b, test_32bit); + tcase_add_test (tc_200b, test_64bit); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_200b, test_vector); +#endif + + TCase* tc_1500b = tcase_create ("1500b"); + suite_add_tcase (s, tc_1500b); + tcase_add_checked_fixture (tc_1500b, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_1500b, mock_setup_1500b, NULL); + tcase_add_test (tc_1500b, test_8bit); + tcase_add_test (tc_1500b, test_16bit); + tcase_add_test (tc_1500b, test_32bit); + tcase_add_test (tc_1500b, test_64bit); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_1500b, test_vector); +#endif + + TCase* tc_9kb = tcase_create ("9KB"); + suite_add_tcase (s, tc_9kb); + tcase_add_checked_fixture (tc_9kb, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_9kb, mock_setup_9kb, NULL); + tcase_add_test (tc_9kb, test_8bit); + tcase_add_test (tc_9kb, test_16bit); + tcase_add_test (tc_9kb, test_32bit); + tcase_add_test (tc_9kb, test_64bit); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_9kb, test_vector); +#endif + + TCase* tc_64kb = tcase_create ("64KB"); + suite_add_tcase (s, tc_64kb); + tcase_add_checked_fixture (tc_64kb, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_64kb, mock_setup_64kb, NULL); + tcase_add_test (tc_64kb, test_8bit); + tcase_add_test (tc_64kb, test_16bit); + tcase_add_test (tc_64kb, test_32bit); + tcase_add_test (tc_64kb, test_64bit); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_64kb, test_vector); +#endif + + return s; +} + +static +Suite* +make_csum_memcpy_performance_suite (void) +{ + Suite* s; + + s = suite_create ("Checksum and memcpy performance"); + + TCase* tc_100b = tcase_create ("100b"); + suite_add_tcase (s, tc_100b); + tcase_add_checked_fixture (tc_100b, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_100b, mock_setup_100b, NULL); + tcase_add_test (tc_100b, test_8bit_memcpy); + tcase_add_test (tc_100b, test_16bit_memcpy); + tcase_add_test (tc_100b, test_32bit_memcpy); + tcase_add_test (tc_100b, test_64bit_memcpy); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_100b, test_vector_memcpy); +#endif + + TCase* tc_200b = tcase_create ("200b"); + suite_add_tcase (s, tc_200b); + tcase_add_checked_fixture (tc_200b, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_200b, mock_setup_200b, NULL); + tcase_add_test (tc_200b, test_8bit_memcpy); + tcase_add_test (tc_200b, test_16bit_memcpy); + tcase_add_test (tc_200b, test_32bit_memcpy); + tcase_add_test (tc_200b, test_64bit_memcpy); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_200b, test_vector_memcpy); +#endif + + TCase* tc_1500b = tcase_create ("1500b"); + suite_add_tcase (s, tc_1500b); + tcase_add_checked_fixture (tc_1500b, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_1500b, mock_setup_1500b, NULL); + tcase_add_test (tc_1500b, test_8bit_memcpy); + tcase_add_test (tc_1500b, test_16bit_memcpy); + tcase_add_test (tc_1500b, test_32bit_memcpy); + tcase_add_test (tc_1500b, test_64bit_memcpy); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_1500b, test_vector_memcpy); +#endif + + TCase* tc_9kb = tcase_create ("9KB"); + suite_add_tcase (s, tc_9kb); + tcase_add_checked_fixture (tc_9kb, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_9kb, mock_setup_9kb, NULL); + tcase_add_test (tc_9kb, test_8bit_memcpy); + tcase_add_test (tc_9kb, test_16bit_memcpy); + tcase_add_test (tc_9kb, test_32bit_memcpy); + tcase_add_test (tc_9kb, test_64bit_memcpy); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_9kb, test_vector_memcpy); +#endif + + TCase* tc_64kb = tcase_create ("64KB"); + suite_add_tcase (s, tc_64kb); + tcase_add_checked_fixture (tc_64kb, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_64kb, mock_setup_64kb, NULL); + tcase_add_test (tc_64kb, test_8bit_memcpy); + tcase_add_test (tc_64kb, test_16bit_memcpy); + tcase_add_test (tc_64kb, test_32bit_memcpy); + tcase_add_test (tc_64kb, test_64bit_memcpy); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_64kb, test_vector_memcpy); +#endif + + return s; +} + +static +Suite* +make_csumcpy_performance_suite (void) +{ + Suite* s; + + s = suite_create ("Checksum copy performance"); + + TCase* tc_100b = tcase_create ("100b"); + suite_add_tcase (s, tc_100b); + tcase_add_checked_fixture (tc_100b, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_100b, mock_setup_100b, NULL); + tcase_add_test (tc_100b, test_8bit_csumcpy); + tcase_add_test (tc_100b, test_16bit_csumcpy); + tcase_add_test (tc_100b, test_32bit_csumcpy); + tcase_add_test (tc_100b, test_64bit_csumcpy); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_100b, test_vector_csumcpy); +#endif + + TCase* tc_200b = tcase_create ("200b"); + suite_add_tcase (s, tc_200b); + tcase_add_checked_fixture (tc_200b, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_200b, mock_setup_200b, NULL); + tcase_add_test (tc_200b, test_8bit_csumcpy); + tcase_add_test (tc_200b, test_16bit_csumcpy); + tcase_add_test (tc_200b, test_32bit_csumcpy); + tcase_add_test (tc_200b, test_64bit_csumcpy); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_200b, test_vector_csumcpy); +#endif + + TCase* tc_1500b = tcase_create ("1500b"); + suite_add_tcase (s, tc_1500b); + tcase_add_checked_fixture (tc_1500b, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_1500b, mock_setup_1500b, NULL); + tcase_add_test (tc_1500b, test_8bit_csumcpy); + tcase_add_test (tc_1500b, test_16bit_csumcpy); + tcase_add_test (tc_1500b, test_32bit_csumcpy); + tcase_add_test (tc_1500b, test_64bit_csumcpy); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_1500b, test_vector_csumcpy); +#endif + + TCase* tc_9kb = tcase_create ("9KB"); + suite_add_tcase (s, tc_9kb); + tcase_add_checked_fixture (tc_9kb, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_9kb, mock_setup_9kb, NULL); + tcase_add_test (tc_9kb, test_8bit_csumcpy); + tcase_add_test (tc_9kb, test_16bit_csumcpy); + tcase_add_test (tc_9kb, test_32bit_csumcpy); + tcase_add_test (tc_9kb, test_64bit_csumcpy); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_9kb, test_vector_csumcpy); +#endif + + TCase* tc_64kb = tcase_create ("64KB"); + suite_add_tcase (s, tc_64kb); + tcase_add_checked_fixture (tc_64kb, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_64kb, mock_setup_64kb, NULL); + tcase_add_test (tc_64kb, test_8bit_csumcpy); + tcase_add_test (tc_64kb, test_16bit_csumcpy); + tcase_add_test (tc_64kb, test_32bit_csumcpy); + tcase_add_test (tc_64kb, test_64bit_csumcpy); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_64kb, test_vector_csumcpy); +#endif + + return s; +} + + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_csum_performance_suite ()); + srunner_add_suite (sr, make_csum_memcpy_performance_suite ()); + srunner_add_suite (sr, make_csumcpy_performance_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/checksum_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/checksum_unittest.c new file mode 100644 index 0000000..a25d36a --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/checksum_unittest.c @@ -0,0 +1,278 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for PGM checksum routines + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include + + +/* mock state */ + + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + + +#define CHECKSUM_DEBUG +#include "checksum.c" + + +/* target: + * uint16_t + * pgm_inet_checksum ( + * const void* src, + * uint16_t len, + * uint16_t csum + * ) + */ + +START_TEST (test_inet_pass_001) +{ + const char source[] = "i am not a string"; + const guint16 answer = 0x1fda; /* network order */ + + guint16 csum = pgm_inet_checksum (source, sizeof(source), 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + g_message ("IP checksum of \"%s\" (%d) is %u (%u)", + source, sizeof(source), csum, answer); + + fail_unless (answer == csum, "checksum mismatch"); +} +END_TEST + +START_TEST (test_inet_pass_002) +{ + char* source = alloca (65535); + for (unsigned i = 0, j = 0; i < 65535; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = 0x3fc0; /* network order */ + + guint16 csum = pgm_inet_checksum (source, 65535, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + g_message ("IP checksum of 64KB is %u (%u)", + csum, answer); + + fail_unless (answer == csum, "checksum mismatch"); +} +END_TEST + +START_TEST (test_inet_fail_001) +{ + pgm_inet_checksum (NULL, 0, 0); + fail ("reached"); +} +END_TEST + +/* target: + * guint16 + * pgm_csum_fold ( + * guint32 csum + * ) + */ + +START_TEST (test_fold_pass_001) +{ + const guint32 csum = 0xdd250300; /* network order */ + const guint16 answer = 0x1fda; + + guint16 folded_csum = pgm_csum_fold (g_ntohl (csum)); + folded_csum = g_htons (folded_csum); + g_message ("32-bit checksum %u folds into %u (%u)", csum, folded_csum, answer); + + fail_unless (answer == folded_csum, "folded checksum mismatch"); +} +END_TEST + + +/* target: + * guint32 + * pgm_csum_partial ( + * const void* src, + * guint len, + * guint32 sum + * ) + */ + +START_TEST (test_partial_pass_001) +{ + const char source[] = "i am not a string"; +#if __BYTE_ORDER == __BIG_ENDIAN + const guint32 answer = 0x0000e025; /* network order */ +#elif __BYTE_ORDER == __LITTLE_ENDIAN + const guint32 answer = 0xe0250000; /* network order */ +#else +# error "__BYTE_ORDER not supported." +#endif + + guint32 csum = pgm_csum_partial (source, sizeof(source), 0); + csum = g_htonl (csum); + g_message ("Partial checksum of \"%s\" is %u (%u)", source, csum, answer); + + fail_unless (answer == csum, "checksum mismatch"); +} +END_TEST + +START_TEST (test_partial_fail_001) +{ + pgm_csum_partial (NULL, 0, 0); + fail ("reached"); +} +END_TEST + +/* target: + * guint32 + * pgm_csum_partial_copy ( + * const void* src, + * void* dst, + * guint len, + * guint32 sum + * ) + */ + +START_TEST (test_partial_copy_pass_001) +{ + const char source[] = "i am not a string"; +#if __BYTE_ORDER == __BIG_ENDIAN + const guint32 answer = 0x0000e025; /* network order */ +#elif __BYTE_ORDER == __LITTLE_ENDIAN + const guint32 answer = 0xe0250000; /* network order */ +#else +# error "__BYTE_ORDER not supported." +#endif + + char dest[1024]; + guint32 csum_source = pgm_csum_partial_copy (source, dest, sizeof(source), 0); + csum_source = g_htonl (csum_source); + guint32 csum_dest = pgm_csum_partial (dest, sizeof(source), 0); + csum_dest = g_htonl (csum_dest); + g_message ("Partial copy checksum of \"%s\" is %u, partial checksum is %u (%u)", + source, csum_source, csum_dest, answer); + fail_unless (answer == csum_source, "checksum mismatch in partial-copy"); + fail_unless (answer == csum_dest, "checksum mismatch in partial"); +} +END_TEST + +START_TEST (test_partial_copy_fail_001) +{ + pgm_csum_partial_copy (NULL, NULL, 0, 0); + fail ("reached"); +} +END_TEST + +/* target: + * guint32 + * pgm_csum_block_add ( + * guint32 csum, + * guint32 csum2, + * guint offset + * ) + */ + +START_TEST (test_block_add_pass_001) +{ + const char source[] = "i am not a string"; + const guint16 answer = 0x1fda; /* network order */ + + guint32 csum_a = pgm_csum_partial (source, sizeof(source) / 2, 0); + guint32 csum_b = pgm_csum_partial (source + (sizeof(source) / 2), sizeof(source) - (sizeof(source) / 2), 0); + guint32 csum = pgm_csum_block_add (csum_a, csum_b, sizeof(source) / 2); + guint16 fold = pgm_csum_fold (csum); +/* convert to display in network order */ + csum_a = g_htonl (csum_a); + csum_b = g_htonl (csum_b); + csum = g_htonl (csum); + fold = g_htons (fold); + g_message ("Checksum A:%u + B:%u = %u -> %u (%u)", + csum_a, csum_b, csum, fold, answer); + fail_unless (answer == fold, "checksum mismatch"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_inet = tcase_create ("inet"); + suite_add_tcase (s, tc_inet); + tcase_add_test (tc_inet, test_inet_pass_001); + tcase_add_test (tc_inet, test_inet_pass_002); + tcase_add_test_raise_signal (tc_inet, test_inet_fail_001, SIGABRT); + + TCase* tc_fold = tcase_create ("fold"); + suite_add_tcase (s, tc_fold); + tcase_add_test (tc_fold, test_fold_pass_001); + + TCase* tc_block_add = tcase_create ("block-add"); + suite_add_tcase (s, tc_block_add); + tcase_add_test (tc_block_add, test_block_add_pass_001); + + TCase* tc_partial = tcase_create ("partial"); + suite_add_tcase (s, tc_partial); + tcase_add_test (tc_partial, test_partial_pass_001); + tcase_add_test_raise_signal (tc_partial, test_partial_fail_001, SIGABRT); + + TCase* tc_partial_copy = tcase_create ("partial-copy"); + suite_add_tcase (s, tc_partial_copy); + tcase_add_test (tc_partial_copy, test_partial_copy_pass_001); + tcase_add_test_raise_signal (tc_partial_copy, test_partial_copy_fail_001, SIGABRT); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/crossmingw.py b/3rdparty/openpgm-svn-r1085/pgm/crossmingw.py new file mode 100644 index 0000000..2981506 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/crossmingw.py @@ -0,0 +1,144 @@ +import os +import os.path +import string + +import SCons.Action +import SCons.Builder +import SCons.Tool +import SCons.Util + +# This is what we search for to find mingw: +prefixes = SCons.Util.Split(""" + mingw32- + i386-mingw32msvc- + i486-mingw32msvc- + i586-mingw32msvc- + i686-mingw32msvc- +""") + +def find(env): + for prefix in prefixes: + # First search in the SCons path and then the OS path: + if env.WhereIs(prefix + 'gcc') or SCons.Util.WhereIs(prefix + 'gcc'): + return prefix + + return '' + +def shlib_generator(target, source, env, for_signature): + cmd = SCons.Util.CLVar(['$SHLINK', '$SHLINKFLAGS']) + + dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') + if dll: cmd.extend(['-o', dll]) + + cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS']) + + implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') + if implib: cmd.append('-Wl,--out-implib,'+implib.get_string(for_signature)) + + def_target = env.FindIxes(target, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX') + if def_target: cmd.append('-Wl,--output-def,'+def_target.get_string(for_signature)) + + return [cmd] + +def shlib_emitter(target, source, env): + dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') + no_import_lib = env.get('no_import_lib', 0) + + if not dll: + raise SCons.Errors.UserError, "A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX") + + if not no_import_lib and \ + not env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX'): + + # Append an import library to the list of targets. + target.append(env.ReplaceIxes(dll, + 'SHLIBPREFIX', 'SHLIBSUFFIX', + 'LIBPREFIX', 'LIBSUFFIX')) + + # Append a def file target if there isn't already a def file target + # or a def file source. There is no option to disable def file + # target emitting, because I can't figure out why someone would ever + # want to turn it off. + def_source = env.FindIxes(source, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX') + def_target = env.FindIxes(target, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX') + if not def_source and not def_target: + target.append(env.ReplaceIxes(dll, + 'SHLIBPREFIX', 'SHLIBSUFFIX', + 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX')) + + return (target, source) + +# TODO: Backported to old scons version +#shlib_action = SCons.Action.CommandGenerator(shlib_generator) +shlib_action = SCons.Action.Action(shlib_generator,generator=1) + +res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR') + +res_builder = SCons.Builder.Builder(action=res_action, suffix='.o', + source_scanner=SCons.Tool.SourceFileScanner) +SCons.Tool.SourceFileScanner.add_scanner('.rc', SCons.Defaults.CScan) + +def generate(env): + mingw_prefix = find(env) + + if mingw_prefix: + dir = os.path.dirname(env.WhereIs(mingw_prefix + 'gcc') or SCons.Util.WhereIs(mingw_prefix + 'gcc')) + + # The mingw bin directory must be added to the path: + path = env['ENV'].get('PATH', []) + if not path: + path = [] + if SCons.Util.is_String(path): + path = string.split(path, os.pathsep) + + env['ENV']['PATH'] = string.join([dir] + path, os.pathsep) + + # Most of mingw is the same as gcc and friends... + gnu_tools = ['gcc', 'g++', 'gnulink', 'ar', 'gas'] + for tool in gnu_tools: + SCons.Tool.Tool(tool)(env) + + #... but a few things differ: + env['CC'] = mingw_prefix + 'gcc' + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') + env['CXX'] = mingw_prefix + 'g++' + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') + env['SHLINKCOM'] = shlib_action + env.Append(SHLIBEMITTER = [shlib_emitter]) + # This line isn't required and breaks C++ linking + #env['LINK'] = mingw_prefix + 'g++' + env['AS'] = mingw_prefix + 'as' + env['AR'] = mingw_prefix + 'ar' + env['RANLIB'] = mingw_prefix + 'ranlib' + env['WIN32DEFPREFIX'] = '' + env['WIN32DEFSUFFIX'] = '.def' + env['SHOBJSUFFIX'] = '.o' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 + + env['RC'] = mingw_prefix + 'windres' + env['RCFLAGS'] = SCons.Util.CLVar('') + env['RCINCFLAGS'] = '$( ${_concat(RCINCPREFIX, CPPPATH, RCINCSUFFIX, __env__, RDirs, TARGET)} $)' + env['RCINCPREFIX'] = '--include-dir ' + env['RCINCSUFFIX'] = '' + env['RCCOM'] = '$RC $RCINCFLAGS $RCINCPREFIX $SOURCE.dir $RCFLAGS -i $SOURCE -o $TARGET' + env['BUILDERS']['RES'] = res_builder + + # Some setting from the platform also have to be overridden: + env['OBJPREFIX'] = '' + env['OBJSUFFIX'] = '.o' + env['LIBPREFIX'] = 'lib' + env['LIBSUFFIX'] = '.a' + env['SHOBJPREFIX'] = '$OBJPREFIX' + env['SHOBJSUFFIX'] = '$OBJSUFFIX' + env['PROGPREFIX'] = '' + env['PROGSUFFIX'] = '.exe' + env['LIBPREFIX'] = '' + env['LIBSUFFIX'] = '.lib' + env['SHLIBPREFIX'] = '' + env['SHLIBSUFFIX'] = '.dll' + env['LIBPREFIXES'] = [ '$LIBPREFIX' ] + env['LIBSUFFIXES'] = [ '$LIBSUFFIX' ] + +def exists(env): + return find(env) diff --git a/3rdparty/openpgm-svn-r1085/pgm/crossmingw64.py b/3rdparty/openpgm-svn-r1085/pgm/crossmingw64.py new file mode 100644 index 0000000..111e0ed --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/crossmingw64.py @@ -0,0 +1,140 @@ +import os +import os.path +import string + +import SCons.Action +import SCons.Builder +import SCons.Tool +import SCons.Util + +# This is what we search for to find mingw: +prefixes = SCons.Util.Split(""" + x86_64-w64-mingw32- +""") + +def find(env): + for prefix in prefixes: + # First search in the SCons path and then the OS path: + if env.WhereIs(prefix + 'gcc') or SCons.Util.WhereIs(prefix + 'gcc'): + return prefix + + return '' + +def shlib_generator(target, source, env, for_signature): + cmd = SCons.Util.CLVar(['$SHLINK', '$SHLINKFLAGS']) + + dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') + if dll: cmd.extend(['-o', dll]) + + cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS']) + + implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') + if implib: cmd.append('-Wl,--out-implib,'+implib.get_string(for_signature)) + + def_target = env.FindIxes(target, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX') + if def_target: cmd.append('-Wl,--output-def,'+def_target.get_string(for_signature)) + + return [cmd] + +def shlib_emitter(target, source, env): + dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') + no_import_lib = env.get('no_import_lib', 0) + + if not dll: + raise SCons.Errors.UserError, "A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX") + + if not no_import_lib and \ + not env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX'): + + # Append an import library to the list of targets. + target.append(env.ReplaceIxes(dll, + 'SHLIBPREFIX', 'SHLIBSUFFIX', + 'LIBPREFIX', 'LIBSUFFIX')) + + # Append a def file target if there isn't already a def file target + # or a def file source. There is no option to disable def file + # target emitting, because I can't figure out why someone would ever + # want to turn it off. + def_source = env.FindIxes(source, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX') + def_target = env.FindIxes(target, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX') + if not def_source and not def_target: + target.append(env.ReplaceIxes(dll, + 'SHLIBPREFIX', 'SHLIBSUFFIX', + 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX')) + + return (target, source) + +# TODO: Backported to old scons version +#shlib_action = SCons.Action.CommandGenerator(shlib_generator) +shlib_action = SCons.Action.Action(shlib_generator,generator=1) + +res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR') + +res_builder = SCons.Builder.Builder(action=res_action, suffix='.o', + source_scanner=SCons.Tool.SourceFileScanner) +SCons.Tool.SourceFileScanner.add_scanner('.rc', SCons.Defaults.CScan) + +def generate(env): + mingw_prefix = find(env) + + if mingw_prefix: + dir = os.path.dirname(env.WhereIs(mingw_prefix + 'gcc') or SCons.Util.WhereIs(mingw_prefix + 'gcc')) + + # The mingw bin directory must be added to the path: + path = env['ENV'].get('PATH', []) + if not path: + path = [] + if SCons.Util.is_String(path): + path = string.split(path, os.pathsep) + + env['ENV']['PATH'] = string.join([dir] + path, os.pathsep) + + # Most of mingw is the same as gcc and friends... + gnu_tools = ['gcc', 'g++', 'gnulink', 'ar', 'gas'] + for tool in gnu_tools: + SCons.Tool.Tool(tool)(env) + + #... but a few things differ: + env['CC'] = mingw_prefix + 'gcc' + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') + env['CXX'] = mingw_prefix + 'g++' + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') + env['SHLINKCOM'] = shlib_action + env.Append(SHLIBEMITTER = [shlib_emitter]) + # This line isn't required and breaks C++ linking + #env['LINK'] = mingw_prefix + 'g++' + env['AS'] = mingw_prefix + 'as' + env['AR'] = mingw_prefix + 'ar' + env['RANLIB'] = mingw_prefix + 'ranlib' + env['WIN32DEFPREFIX'] = '' + env['WIN32DEFSUFFIX'] = '.def' + env['SHOBJSUFFIX'] = '.o' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 + + env['RC'] = mingw_prefix + 'windres' + env['RCFLAGS'] = SCons.Util.CLVar('') + env['RCINCFLAGS'] = '$( ${_concat(RCINCPREFIX, CPPPATH, RCINCSUFFIX, __env__, RDirs, TARGET)} $)' + env['RCINCPREFIX'] = '--include-dir ' + env['RCINCSUFFIX'] = '' + env['RCCOM'] = '$RC $RCINCFLAGS $RCINCPREFIX $SOURCE.dir $RCFLAGS -i $SOURCE -o $TARGET' + env['BUILDERS']['RES'] = res_builder + + # Some setting from the platform also have to be overridden: + env['OBJPREFIX'] = '' + env['OBJSUFFIX'] = '.o' + env['LIBPREFIX'] = 'lib' + env['LIBSUFFIX'] = '.a' + env['SHOBJPREFIX'] = '$OBJPREFIX' + env['SHOBJSUFFIX'] = '$OBJSUFFIX' + env['PROGPREFIX'] = '' + env['PROGSUFFIX'] = '.exe' + env['LIBPREFIX'] = '' + env['LIBSUFFIX'] = '.lib' + env['SHLIBPREFIX'] = '' + env['SHLIBSUFFIX'] = '.dll' + env['LIBPREFIXES'] = [ '$LIBPREFIX' ] + env['LIBSUFFIXES'] = [ '$LIBSUFFIX' ] + +def exists(env): + return find(env) diff --git a/3rdparty/openpgm-svn-r1085/pgm/engine.c b/3rdparty/openpgm-svn-r1085/pgm/engine.c new file mode 100644 index 0000000..994bca2 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/engine.c @@ -0,0 +1,277 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM engine. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _WIN32 +# include +#endif +#include +#include +#include +#include +#include +#include +#include + + +//#define ENGINE_DEBUG + + +/* globals */ +int pgm_ipproto_pgm PGM_GNUC_READ_MOSTLY = IPPROTO_PGM; + +#ifdef _WIN32 +LPFN_WSARECVMSG pgm_WSARecvMsg PGM_GNUC_READ_MOSTLY = NULL; +#endif + +#ifdef PGM_DEBUG +unsigned pgm_loss_rate PGM_GNUC_READ_MOSTLY = 0; +#endif + +/* locals */ +static bool pgm_is_supported = FALSE; +static volatile uint32_t pgm_ref_count = 0; + +#ifdef _WIN32 +# ifndef WSAID_WSARECVMSG +/* http://cvs.winehq.org/cvsweb/wine/include/mswsock.h */ +# define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}} +# endif +#endif + + +/* startup PGM engine, mainly finding PGM protocol definition, if any from NSS + * + * returns TRUE on success, returns FALSE if an error occurred, implying some form of + * system re-configuration is required to resolve before trying again. + * + * NB: Valgrind loves generating errors in getprotobyname(). + */ +bool +pgm_init ( + pgm_error_t** error + ) +{ + if (pgm_atomic_exchange_and_add32 (&pgm_ref_count, 1) > 0) + return TRUE; + +/* initialise dependent modules */ + pgm_messages_init(); + + pgm_minor ("OpenPGM %d.%d.%d%s%s%s %s %s %s %s", + pgm_major_version, pgm_minor_version, pgm_micro_version, + pgm_build_revision ? " (" : "", pgm_build_revision ? pgm_build_revision : "", pgm_build_revision ? ")" : "", + pgm_build_date, pgm_build_time, pgm_build_system, pgm_build_machine); + + pgm_thread_init(); + pgm_mem_init(); + pgm_rand_init(); + +#ifdef _WIN32 + WORD wVersionRequested = MAKEWORD (2, 2); + WSADATA wsaData; + if (WSAStartup (wVersionRequested, &wsaData) != 0) + { + const int save_errno = WSAGetLastError (); + pgm_set_error (error, + PGM_ERROR_DOMAIN_ENGINE, + pgm_error_from_wsa_errno (save_errno), + _("WSAStartup failure: %s"), + pgm_wsastrerror (save_errno)); + goto err_shutdown; + } + + if (LOBYTE (wsaData.wVersion) != 2 || HIBYTE (wsaData.wVersion) != 2) + { + WSACleanup(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_ENGINE, + PGM_ERROR_FAILED, + _("WSAStartup failed to provide requested version 2.2.")); + goto err_shutdown; + } + +# ifndef CONFIG_TARGET_WINE +/* find WSARecvMsg API */ + if (NULL == pgm_WSARecvMsg) { + GUID WSARecvMsg_GUID = WSAID_WSARECVMSG; + DWORD cbBytesReturned; + const SOCKET sock = socket (AF_INET, SOCK_DGRAM, 0); + if (SOCKET_ERROR == sock) { + WSACleanup(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_ENGINE, + PGM_ERROR_FAILED, + _("Cannot open socket.")); + goto err_shutdown; + } + if (SOCKET_ERROR == WSAIoctl (sock, + SIO_GET_EXTENSION_FUNCTION_POINTER, + &WSARecvMsg_GUID, sizeof(WSARecvMsg_GUID), + &pgm_WSARecvMsg, sizeof(pgm_WSARecvMsg), + &cbBytesReturned, + NULL, + NULL)) + { + closesocket (sock); + WSACleanup(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_ENGINE, + PGM_ERROR_FAILED, + _("WSARecvMsg function not found.")); + goto err_shutdown; + } + pgm_debug ("Retrieved address of WSARecvMsg."); + closesocket (sock); + } +# endif +#endif /* _WIN32 */ + +/* find PGM protocol id overriding default value, use first value from NIS */ +#ifdef CONFIG_HAVE_GETPROTOBYNAME_R + char b[1024]; + struct protoent protobuf; + const struct protoent* proto = getprotobyname_r ("pgm", &protobuf, b, sizeof(b)); + if (NULL != proto) { + if (proto->p_proto != pgm_ipproto_pgm) { + pgm_minor (_("Setting PGM protocol number to %i from /etc/protocols."), + proto->p_proto); + pgm_ipproto_pgm = proto->p_proto; + } + } +#elif defined(CONFIG_HAVE_GETPROTOBYNAME_R2) + char b[1024]; + struct protoent protobuf, *proto; + const int e = getprotobyname_r ("pgm", &protobuf, b, sizeof(b), &proto); + if (e != -1 && proto != NULL) { + if (proto->p_proto != pgm_ipproto_pgm) { + pgm_minor (_("Setting PGM protocol number to %i from /etc/protocols."), + proto->p_proto); + pgm_ipproto_pgm = proto->p_proto; + } + } +#else + const struct protoent *proto = getprotobyname ("pgm"); + if (proto != NULL) { + if (proto->p_proto != pgm_ipproto_pgm) { +#ifndef _WIN32 + pgm_minor (_("Setting PGM protocol number to %i from /etc/protocols."), + proto->p_proto); +#else + pgm_minor (_("Setting PGM protocol number to %i from %%SYSTEMROOT%%\\system32\\drivers\\etc\\protocols."), + proto->p_proto); +#endif + pgm_ipproto_pgm = proto->p_proto; + } + } +#endif + +/* ensure timing enabled */ + pgm_error_t* sub_error = NULL; + if (!pgm_time_init (&sub_error)) { + if (sub_error) + pgm_propagate_error (error, sub_error); +#ifdef _WIN32 + WSACleanup(); +#endif + goto err_shutdown; + } + +/* receiver simulated loss rate */ +#ifdef PGM_DEBUG + const char *loss_rate = getenv ("PGM_LOSS_RATE"); + if (NULL != loss_rate) { + int value = atoi (loss_rate); + if (value > 0 && value <= 100) { + pgm_loss_rate = value; + pgm_minor (_("Setting PGM packet loss rate to %i%%."), pgm_loss_rate); + } + } +#endif + +/* create global sock list lock */ + pgm_rwlock_init (&pgm_sock_list_lock); + + pgm_is_supported = TRUE; + return TRUE; + +err_shutdown: + pgm_rand_shutdown(); + pgm_mem_shutdown(); + pgm_thread_shutdown(); + pgm_messages_shutdown(); + pgm_atomic_dec32 (&pgm_ref_count); + return FALSE; +} + +/* returns TRUE if PGM engine has been initialized + */ + +bool +pgm_supported (void) +{ + return ( pgm_is_supported == TRUE ); +} + +/* returns TRUE on success, returns FALSE if an error occurred. + */ + +bool +pgm_shutdown (void) +{ + pgm_return_val_if_fail (pgm_atomic_read32 (&pgm_ref_count) > 0, FALSE); + + if (pgm_atomic_exchange_and_add32 (&pgm_ref_count, (uint32_t)-1) != 1) + return TRUE; + + pgm_is_supported = FALSE; + +/* destroy all open socks */ + while (pgm_sock_list) { + pgm_close ((pgm_sock_t*)pgm_sock_list->data, FALSE); + } + + pgm_time_shutdown(); + +#ifdef _WIN32 + WSACleanup(); +#endif + + pgm_rand_shutdown(); + pgm_mem_shutdown(); + pgm_thread_shutdown(); + pgm_messages_shutdown(); + return TRUE; +} + +/* helper to drop out of setuid 0 after creating PGM sockets + */ +void +pgm_drop_superuser (void) +{ +#ifndef _WIN32 + if (0 == getuid()) { + setuid((gid_t)65534); + setgid((uid_t)65534); + } +#endif +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/engine_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/engine_unittest.c new file mode 100644 index 0000000..f434e35 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/engine_unittest.c @@ -0,0 +1,232 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for PGM engine. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include + + +/* mock state */ + +struct pgm_rwlock_t; +struct pgm_slist_t; + +static gint mock_time_init = 0; +static struct pgm_rwlock_t mock_pgm_sock_list_lock; +static struct pgm_slist_t* mock_pgm_sock_list = NULL; + +#define pgm_time_init mock_pgm_time_init +#define pgm_time_shutdown mock_pgm_time_shutdown +#define pgm_close mock_pgm_close +#define pgm_sock_list_lock mock_pgm_sock_list_lock +#define pgm_sock_list mock_pgm_sock_list + +#define ENGINE_DEBUG +#include "engine.c" + + +static +void +mock_setup (void) +{ + mock_time_init = FALSE; +} + +static +void +mock_teardown (void) +{ +// null +} + + +/* mock functions for external references */ + +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return 0; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_time_init ( + pgm_error_t** error + ) +{ + mock_time_init++; + return TRUE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_time_shutdown (void) +{ + if (!mock_time_init) + return FALSE; + mock_time_init--; + return TRUE; +} + +bool +mock_pgm_close ( + pgm_sock_t* sock, + bool flush + ) +{ + return TRUE; +} + + +/* target: + * bool + * pgm_init (pgm_error_t** error) + */ + +/* reference counting on init */ +START_TEST (test_init_pass_001) +{ + fail_unless (TRUE == pgm_init (NULL), "init failed"); + fail_unless (TRUE == pgm_init (NULL), "init failed"); +} +END_TEST + +/* timing module already init */ +START_TEST (test_init_pass_003) +{ + pgm_error_t* err = NULL; + fail_unless (TRUE == pgm_time_init (&err), "time-init failed: %s", (err && err->message) ? err->message : "(null)"); + fail_unless (TRUE == pgm_init (&err), "init failed: %s", (err && err->message) ? err->message : "(null)"); + fail_unless (TRUE == pgm_init (&err), "init failed: %s", (err && err->message) ? err->message : "(null)"); +} +END_TEST + +/* target: + * bool + * pgm_shutdown (void) + */ + +START_TEST (test_shutdown_pass_001) +{ + fail_unless (TRUE == pgm_init (NULL), "init failed"); + fail_unless (TRUE == pgm_shutdown (), "shutdown failed"); +} +END_TEST + +/* no init */ +START_TEST (test_shutdown_pass_002) +{ + fail_unless (FALSE == pgm_shutdown (), "shutdown failed"); +} +END_TEST + +/* double call */ +START_TEST (test_shutdown_pass_003) +{ + fail_unless (TRUE == pgm_init (NULL), "init failed"); + fail_unless (TRUE == pgm_shutdown (), "shutdown failed"); + fail_unless (FALSE == pgm_shutdown (), "shutdown failed"); +} +END_TEST + +/* check reference counting */ +START_TEST (test_shutdown_pass_004) +{ + fail_unless (TRUE == pgm_init (NULL), "init failed"); + fail_unless (TRUE == pgm_init (NULL), "init failed"); + fail_unless (TRUE == pgm_shutdown (), "shutdown failed"); + fail_unless (TRUE == pgm_shutdown (), "shutdown failed"); + fail_unless (FALSE == pgm_shutdown (), "shutdown failed"); +} +END_TEST + +/* target: + * bool + * pgm_supported (void) + */ + +START_TEST (test_supported_pass_001) +{ + fail_unless (FALSE == pgm_supported(), "supported failed"); + fail_unless (TRUE == pgm_init (NULL), "init failed"); + fail_unless (TRUE == pgm_supported(), "supported failed"); + fail_unless (TRUE == pgm_shutdown (), "shutdown failed"); + fail_unless (FALSE == pgm_supported(), "supported failed"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_init = tcase_create ("init"); + tcase_add_checked_fixture (tc_init, mock_setup, mock_teardown); + suite_add_tcase (s, tc_init); + tcase_add_test (tc_init, test_init_pass_001); + tcase_add_test (tc_init, test_init_pass_003); + + TCase* tc_shutdown = tcase_create ("shutdown"); + tcase_add_checked_fixture (tc_shutdown, mock_setup, mock_teardown); + suite_add_tcase (s, tc_shutdown); + tcase_add_test (tc_shutdown, test_shutdown_pass_001); + tcase_add_test (tc_shutdown, test_shutdown_pass_002); + tcase_add_test (tc_shutdown, test_shutdown_pass_003); + tcase_add_test (tc_shutdown, test_shutdown_pass_004); + + TCase* tc_supported = tcase_create ("supported"); + tcase_add_checked_fixture (tc_supported, mock_setup, mock_teardown); + suite_add_tcase (s, tc_supported); + tcase_add_test (tc_supported, test_supported_pass_001); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/error.c b/3rdparty/openpgm-svn-r1085/pgm/error.c new file mode 100644 index 0000000..3f3fe30 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/error.c @@ -0,0 +1,518 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable error reporting. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _WIN32 +# include +#endif +#include +#include + + +//#define ERROR_DEBUG + + +#define ERROR_OVERWRITTEN_WARNING "pgm_error_t set over the top of a previous pgm_error_t or uninitialized memory.\n" \ + "This indicates a bug. You must ensure an error is NULL before it's set.\n" \ + "The overwriting error message was: %s" + +static pgm_error_t* pgm_error_new_valist (const int, const int, const char*, va_list) PGM_GNUC_PRINTF(3, 0); +static void pgm_error_add_prefix (char**restrict, const char*restrict, va_list) PGM_GNUC_PRINTF(2, 0); + + +static +pgm_error_t* +pgm_error_new_valist ( + const int error_domain, + const int error_code, + const char* format, + va_list args + ) +{ + pgm_error_t *error = pgm_new (pgm_error_t, 1); + error->domain = error_domain; + error->code = error_code; + error->message = pgm_strdup_vprintf (format, args); + return error; +} + +void +pgm_error_free ( + pgm_error_t* error + ) +{ + pgm_return_if_fail (error != NULL); + pgm_free (error->message); + pgm_free (error); +} + +void +pgm_set_error ( + pgm_error_t** restrict err, + const int error_domain, + const int error_code, + const char* restrict format, + ... + ) +{ + pgm_error_t *new; + va_list args; + + if (NULL == err) + return; + + va_start (args, format); + new = pgm_error_new_valist (error_domain, error_code, format, args); + va_end (args); + + if (NULL == *err) + *err = new; + else + pgm_warn (_(ERROR_OVERWRITTEN_WARNING), new->message); +} + +void +pgm_propagate_error ( + pgm_error_t** restrict dest, + pgm_error_t* restrict src + ) +{ + pgm_return_if_fail (src != NULL); + + if (NULL == dest) { + if (src) + pgm_error_free (src); + return; + } else { + if (NULL != *dest) { + pgm_warn (_(ERROR_OVERWRITTEN_WARNING), src->message); + } else { + *dest = src; + } + } +} + +void +pgm_clear_error ( + pgm_error_t** err + ) +{ + if (err && *err) { + pgm_error_free (*err); + *err = NULL; + } +} + +static +void +pgm_error_add_prefix ( + char** restrict string, + const char* restrict format, + va_list ap + ) +{ + char* prefix = pgm_strdup_vprintf (format, ap); + char* oldstring = *string; + *string = pgm_strconcat (prefix, oldstring, NULL); + pgm_free (oldstring); + pgm_free (prefix); +} + +void +pgm_prefix_error ( + pgm_error_t** restrict err, + const char* restrict format, + ... + ) +{ + if (err && *err) { + va_list ap; + va_start (ap, format); + pgm_error_add_prefix (&(*err)->message, format, ap); + va_end (ap); + } +} + +/* error from libc. + */ + +int +pgm_error_from_errno ( + const int from_errno + ) +{ + switch (from_errno) { +#ifdef EAFNOSUPPORT + case EAFNOSUPPORT: + return PGM_ERROR_AFNOSUPPORT; + break; +#endif + +#ifdef EAGAIN + case EAGAIN: + return PGM_ERROR_AGAIN; + break; +#endif + +#ifdef EBADF + case EBADF: + return PGM_ERROR_BADF; + break; +#endif + +#ifdef ECONNRESET + case ECONNRESET: + return PGM_ERROR_CONNRESET; + break; +#endif + +#ifdef EFAULT + case EFAULT: + return PGM_ERROR_FAULT; + break; +#endif + +#ifdef EINTR + case EINTR: + return PGM_ERROR_INTR; + break; +#endif + +#ifdef EINVAL + case EINVAL: + return PGM_ERROR_INVAL; + break; +#endif + +#ifdef EMFILE + case EMFILE: + return PGM_ERROR_MFILE; + break; +#endif + +#ifdef ENFILE + case ENFILE: + return PGM_ERROR_NFILE; + break; +#endif + +#ifdef ENODEV + case ENODEV: + return PGM_ERROR_NODEV; + break; +#endif + +#ifdef ENOENT + case ENOENT: + return PGM_ERROR_NOENT; + break; +#endif + +#ifdef ENOMEM + case ENOMEM: + return PGM_ERROR_NOMEM; + break; +#endif + +#ifdef ENONET + case ENONET: + return PGM_ERROR_NONET; + break; +#endif + +#ifdef ENOPROTOOPT + case ENOPROTOOPT: + return PGM_ERROR_NOPROTOOPT; + break; +#endif + +#ifdef ENOTUNIQ + case ENOTUNIQ: + return PGM_ERROR_NOTUNIQ; + break; +#endif + +#ifdef ENXIO + case ENXIO: + return PGM_ERROR_NXIO; + break; +#endif + +#ifdef EPERM + case EPERM: + return PGM_ERROR_PERM; + break; +#endif + +#ifdef EPROTO + case EPROTO: + return PGM_ERROR_PROTO; + break; +#endif + +#ifdef ERANGE + case ERANGE: + return PGM_ERROR_RANGE; + break; +#endif + +#ifdef EXDEV + case EXDEV: + return PGM_ERROR_XDEV; + break; +#endif + + default : + return PGM_ERROR_FAILED; + break; + } +} + +/* h_errno from gethostbyname. + */ + +int +pgm_error_from_h_errno ( + const int from_h_errno + ) +{ + switch (from_h_errno) { +#ifdef HOST_NOT_FOUND + case HOST_NOT_FOUND: + return PGM_ERROR_NONAME; + break; +#endif + +#ifdef TRY_AGAIN + case TRY_AGAIN: + return PGM_ERROR_AGAIN; + break; +#endif + +#ifdef NO_RECOVERY + case NO_RECOVERY: + return PGM_ERROR_FAIL; + break; +#endif + +#ifdef NO_DATA + case NO_DATA: + return PGM_ERROR_NODATA; + break; +#endif + + default: + return PGM_ERROR_FAILED; + break; + } +} + +/* errno must be preserved before calling to catch correct error + * status with EAI_SYSTEM. + */ + +int +pgm_error_from_eai_errno ( + const int from_eai_errno, +#ifdef EAI_SYSTEM + const int from_errno +#else + PGM_GNUC_UNUSED const int from_errno +#endif + ) +{ + switch (from_eai_errno) { +#ifdef EAI_ADDRFAMILY + case EAI_ADDRFAMILY: + return PGM_ERROR_ADDRFAMILY; + break; +#endif + +#ifdef EAI_AGAIN + case EAI_AGAIN: + return PGM_ERROR_AGAIN; + break; +#endif + +#ifdef EAI_BADFLAGS + case EAI_BADFLAGS: + return PGM_ERROR_INVAL; + break; +#endif + +#ifdef EAI_FAIL + case EAI_FAIL: + return PGM_ERROR_FAIL; + break; +#endif + +#ifdef EAI_FAMILY + case EAI_FAMILY: + return PGM_ERROR_AFNOSUPPORT; + break; +#endif + +#ifdef EAI_MEMORY + case EAI_MEMORY: + return PGM_ERROR_NOMEM; + break; +#endif + +#ifdef EAI_NODATA + case EAI_NODATA: + return PGM_ERROR_NODATA; + break; +#endif + +#if defined(EAI_NONAME) && EAI_NONAME != EAI_NODATA + case EAI_NONAME: + return PGM_ERROR_NONAME; + break; +#endif + +#ifdef EAI_SERVICE + case EAI_SERVICE: + return PGM_ERROR_SERVICE; + break; +#endif + +#ifdef EAI_SOCKTYPE + case EAI_SOCKTYPE: + return PGM_ERROR_SOCKTNOSUPPORT; + break; +#endif + +#ifdef EAI_SYSTEM + case EAI_SYSTEM: + return pgm_error_from_errno (from_errno); + break; +#endif + + default : + return PGM_ERROR_FAILED; + break; + } +} + +/* from WSAGetLastError() + */ + +int +pgm_error_from_wsa_errno ( + const int from_wsa_errno + ) +{ + switch (from_wsa_errno) { +#ifdef WSAEINVAL + case WSAEINVAL: + return PGM_ERROR_INVAL; + break; +#endif +#ifdef WSAEMFILE + case WSAEMFILE: + return PGM_ERROR_MFILE; + break; +#endif +#ifdef WSA_NOT_ENOUGH_MEMORY + case WSA_NOT_ENOUGH_MEMORY: + return PGM_ERROR_NOMEM; + break; +#endif +#ifdef WSAENOPROTOOPT + case WSAENOPROTOOPT: + return PGM_ERROR_NOPROTOOPT; + break; +#endif +#ifdef WSAECONNRESET + case WSAECONNRESET: + return PGM_ERROR_CONNRESET; + break; +#endif + + default : + return PGM_ERROR_FAILED; + break; + } +} + +/* from Last-Error codes, i.e. Windows non-WinSock and non-DOS errors. + */ + +int +pgm_error_from_win_errno ( + const int from_win_errno + ) +{ + switch (from_win_errno) { +#ifdef ERROR_ADDRESS_NOT_ASSOCIATED + case ERROR_ADDRESS_NOT_ASSOCIATED: + return PGM_ERROR_NODATA; + break; +#endif + +#ifdef ERROR_BUFFER_OVERFLOW + case ERROR_BUFFER_OVERFLOW: + return PGM_ERROR_NOBUFS; + break; +#endif + +#ifdef ERROR_INVALID_DATA + case ERROR_INVALID_DATA: + return PGM_ERROR_BADE; + break; +#endif + +#ifdef ERROR_INSUFFICIENT_BUFFER + case ERROR_INSUFFICIENT_BUFFER: + return PGM_ERROR_NOMEM; + break; +#endif + +#ifdef ERROR_INVALID_PARAMETER + case ERROR_INVALID_PARAMETER: + return PGM_ERROR_INVAL; + break; +#endif + +#ifdef ERROR_NOT_ENOUGH_MEMORY + case ERROR_NOT_ENOUGH_MEMORY: + return PGM_ERROR_NOMEM; + break; +#endif + +#ifdef ERROR_NO_DATA + case ERROR_NO_DATA: + return PGM_ERROR_NODATA; + break; +#endif + +#ifdef ERROR_NOT_SUPPORTED + case ERROR_NOT_SUPPORTED: + return PGM_ERROR_NOSYS; + break; +#endif + + default : + return PGM_ERROR_FAILED; + break; + } +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/error_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/error_unittest.c new file mode 100644 index 0000000..035c0f3 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/error_unittest.c @@ -0,0 +1,292 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for error reporting. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include + + +/* mock state */ + + + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + + +#define ERROR_DEBUG +#include "error.c" + + +/* target: + * void + * pgm_set_error ( + * pgm_error_t** err, + * int err_domain, + * int err_code, + * const char* format, + * ... + * ) + */ + +START_TEST (test_set_error_pass_001) +{ + pgm_error_t* err = NULL; + const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; + const gint err_code = 100; + pgm_set_error (&err, err_domain, err_code, "an error occurred."); + fail_unless (NULL != err); +} +END_TEST + +START_TEST (test_set_error_pass_002) +{ + pgm_error_t* err = NULL; + const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; + const gint err_code = 100; + pgm_set_error (&err, err_domain, err_code, "an error occurred: value=%d.", 123); + fail_unless (NULL != err); +} +END_TEST + +/* ignore NULL error */ +START_TEST (test_set_error_pass_003) +{ + pgm_error_t* err = NULL; + const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; + const gint err_code = 100; + pgm_set_error (NULL, err_domain, err_code, "an error occurred."); +} +END_TEST + +/* overwritten error */ +START_TEST (test_set_error_pass_004) +{ + pgm_error_t* err = NULL; + const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; + const gint err_code = 100; + pgm_set_error (&err, err_domain, err_code, "an error occurred."); + fail_unless (NULL != err); + pgm_set_error (&err, err_domain, err_code, "another error occurred."); +} +END_TEST + +/* target: + * void + * pgm_prefix_error ( + * pgm_error_t** err, + * const char* format, + * ... + * ) + */ + +START_TEST (test_prefix_error_pass_001) +{ + pgm_error_t* err = NULL; + const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; + const gint err_code = 100; + pgm_set_error (&err, err_domain, err_code, "an error occurred."); + fail_unless (NULL != err); + pgm_prefix_error (&err, "i am a prefix:"); + pgm_prefix_error (&err, "i am another prefix, value=%d:", 123); +} +END_TEST + +/* ignore null original error */ +START_TEST (test_prefix_error_pass_002) +{ + pgm_error_t* err = NULL; + pgm_prefix_error (&err, "i am a prefix:"); +} +END_TEST + +/* target: + * void + * pgm_propagate_error ( + * pgm_error_t** dest, + * pgm_error_t* src, + * ) + */ + +START_TEST (test_propagate_error_pass_001) +{ + pgm_error_t* dest = NULL; + pgm_error_t* err = NULL; + const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; + const gint err_code = 100; + pgm_set_error (&err, err_domain, err_code, "an error occurred."); + fail_unless (NULL != err); + pgm_propagate_error (&dest, err); + fail_unless (NULL != dest); +} +END_TEST + +/* ignore NULL destination */ +START_TEST (test_propagate_error_pass_002) +{ + pgm_error_t* err = NULL; + const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; + const gint err_code = 100; + pgm_set_error (&err, err_domain, err_code, "an error occurred."); + fail_unless (NULL != err); + pgm_propagate_error (NULL, err); +} +END_TEST + +/* src error SHOULD be valid */ +START_TEST (test_propagate_error_pass_003) +{ + pgm_error_t* dest = NULL; + pgm_error_t* err = NULL; + pgm_propagate_error (&dest, err); +} +END_TEST + +/* target: + * void + * pgm_clear_error ( + * pgm_error_t** err + * ) + */ + +START_TEST (test_clear_error_pass_001) +{ + pgm_error_t* err = NULL; + const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; + const gint err_code = 100; + pgm_set_error (&err, err_domain, err_code, "an error occurred."); + fail_unless (NULL != err); + pgm_clear_error (&err); + fail_unless (NULL == err); +} +END_TEST + +START_TEST (test_clear_error_pass_002) +{ + pgm_error_t* err = NULL; + pgm_clear_error (&err); + fail_unless (NULL == err); +} +END_TEST + +START_TEST (test_clear_error_pass_003) +{ + pgm_clear_error (NULL); +} +END_TEST + +/* target: + * void + * pgm_error_free ( + * pgm_error_t* err + * ) + */ + +START_TEST (test_error_free_pass_001) +{ + pgm_error_t* err = NULL; + const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; + const gint err_code = 100; + pgm_set_error (&err, err_domain, err_code, "an error occurred."); + fail_unless (NULL != err); + pgm_error_free (err); +} +END_TEST + +START_TEST (test_error_free_pass_002) +{ + pgm_error_free (NULL); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_set_error = tcase_create ("set-error"); + suite_add_tcase (s, tc_set_error); + tcase_add_test (tc_set_error, test_set_error_pass_001); + tcase_add_test (tc_set_error, test_set_error_pass_002); + tcase_add_test (tc_set_error, test_set_error_pass_003); + tcase_add_test (tc_set_error, test_set_error_pass_004); + + TCase* tc_prefix_error = tcase_create ("prefix-error"); + suite_add_tcase (s, tc_prefix_error); + tcase_add_test (tc_prefix_error, test_prefix_error_pass_001); + tcase_add_test (tc_prefix_error, test_prefix_error_pass_002); + + TCase* tc_propagate_error = tcase_create ("propagate-error"); + suite_add_tcase (s, tc_propagate_error); + tcase_add_test (tc_propagate_error, test_propagate_error_pass_001); + tcase_add_test (tc_propagate_error, test_propagate_error_pass_002); + tcase_add_test (tc_propagate_error, test_propagate_error_pass_003); + + TCase* tc_clear_error = tcase_create ("clear-error"); + suite_add_tcase (s, tc_clear_error); + tcase_add_test (tc_clear_error, test_clear_error_pass_001); + tcase_add_test (tc_clear_error, test_clear_error_pass_002); + tcase_add_test (tc_clear_error, test_clear_error_pass_003); + + TCase* tc_error_free = tcase_create ("error-free"); + suite_add_tcase (s, tc_error_free); + tcase_add_test (tc_error_free, test_error_free_pass_001); + tcase_add_test (tc_error_free, test_error_free_pass_002); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/SConscript b/3rdparty/openpgm-svn-r1085/pgm/examples/SConscript new file mode 100644 index 0000000..46ebce3 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/SConscript @@ -0,0 +1,88 @@ +# -*- mode: python -*- +# OpenPGM build script +# $Id$ + +import os; + +Import('env'); +e = env.Clone(); +e.Prepend(LIBS = ['libpgm']); +p = e.Clone(); +if '-DCONFIG_HAVE_GETOPT' in env['CCFLAGS']: + getopt = [] +else: + getopt = ['getopt.c'] + +if e['WITH_GLIB'] == 'true': + e.Prepend(LIBS = ['libpgmex']); + e.MergeFlags(env['GLIB_FLAGS']); + e2 = e.Clone(); + if e2['WITH_SNMP'] == 'true': + e2.Append(CCFLAGS = ['-DCONFIG_WITH_SNMP']); + e2.Prepend(LIBS = ['libpgmsnmp']); + e2.MergeFlags(e['SNMP_FLAGS']); + if e2['WITH_HTTP'] == 'true': + e2.Append(CCFLAGS = ['-DCONFIG_WITH_HTTP']); + e2.Prepend(LIBS = ['libpgmhttp']); + +# core preferred examples + e.Program(['pgmdump.c']) + e2.Program(['pgmsend.c']) + e2.Program(['pgmrecv.c']) + +# sync examples + e.Program(['blocksyncrecv.c']) + e.Program(['snonblocksyncrecv.c']) + if '-DCONFIG_HAVE_POLL' in e['CCFLAGS']: + e.Program(['pnonblocksyncrecv.c']) + +# epoll based examples + if '-DCONFIG_HAVE_EPOLL' in e['CCFLAGS']: + e.Program(['enonblocksyncrecv.c']) + e.Program(['enonblocksyncrecvmsg.c']) + e.Program(['enonblocksyncrecvmsgv.c']) + +# ncurses examples + if e['WITH_NCURSES'] == 'true': + en = e.Clone() + en.Append(LIBS = ['panel', 'ncurses']); + en.Program(['pgmtop.c']) + +# Google Protocol Buffer example + if e['WITH_PROTOBUF'] == 'true': + ep = e2.Clone(); + newCCFLAGS = []; + for flag in ep['CCFLAGS']: + if ("-W" != flag[:2]) and ("-std=gnu99" != flag[:10]) and ("-pedantic" != flag[:9]) and ("-D_XOPEN_SOURCE=600" != flag[:19]) and ("-xc99=all" != flag[:9]): + newCCFLAGS.append(flag); + if ("-D_XOPEN_SOURCE=600" == flag[:19]): + newCCFLAGS.append("-D_XOPEN_SOURCE=500"); + ep['CCFLAGS'] = newCCFLAGS; + ep.Append(CPPPATH = '.'); + ep.Append(CCFLAGS = ep['PROTOBUF_CCFLAGS']); + ep.Depends('pgmping.cc', ['ping.pb.cc', 'ping.pb.h']); + protobuf = Builder(action = 'cd ${SOURCE.dir} && %s ${SOURCE.file} --cpp_out=../${TARGET.dir}' % ep['PROTOBUF_PROTOC']) + ep.Append(BUILDERS = {'Protobuf' : protobuf}) + ep.Protobuf('ping.pb.cc', 'ping.proto') + ep.Program(['pgmping.cc', 'ping.pb.cc', ep['PROTOBUF_LIBS']]) + +# Vanilla example +p.Program(['purinsend.c'] + getopt) +p.Program(['purinrecv.c'] + getopt) +p.Program(['daytime.c'] + getopt) +p.Program(['shortcakerecv.c', 'async.c'] + getopt) + +# Vanilla C++ example +if e['WITH_CC'] == 'true': + pcc = p.Clone(); + newCCFLAGS = []; + for flag in pcc['CCFLAGS']: + if ("-W" != flag[:2]) and ("-std=gnu99" != flag[:10]) and ("-pedantic" != flag[:9]) and ("-D_XOPEN_SOURCE=600" != flag[:19]) and ("-xc99=all" != flag[:9]): + newCCFLAGS.append(flag); + if ("-D_XOPEN_SOURCE=600" == flag[:19]): + newCCFLAGS.append("-D_XOPEN_SOURCE=500"); + pcc['CCFLAGS'] = newCCFLAGS; + pcc.Program('purinsendcc', ['purinsendcc.cc'] + p.Object(getopt)) + pcc.Program('purinrecvcc', ['purinrecvcc.cc'] + p.Object(getopt)) + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/SConscript89 b/3rdparty/openpgm-svn-r1085/pgm/examples/SConscript89 new file mode 100644 index 0000000..5595d3d --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/SConscript89 @@ -0,0 +1,41 @@ +# -*- mode: python -*- +# OpenPGM build script +# $Id$ + +import os; + +Import('env'); +e = env.Clone(); +e.Prepend(LIBS = ['libpgm89']); +p = e.Clone(); +if '-DCONFIG_HAVE_GETOPT' in env['CCFLAGS']: + getopt = [] +else: + getopt = ['getopt.c'] + +c89source = Builder(action = 'perl -p -e "s/%z(u|d)/%l\\1/g" $SOURCE > $TARGET', + suffix = '.c89.c', + src_suffix = '.c', + single_source = 1); +p.Append(BUILDERS = {'C89Source' : c89source}) + +for c99file in ['purinsend.c', 'purinrecv.c']: + p.C89Source(c99file); + +p.Program('purinsend', ['purinsend.c89.c'] + getopt) +p.Program('purinrecv', ['purinrecv.c89.c'] + getopt) + +# Vanilla C++ example +if e['WITH_CC'] == 'true': + pcc = p.Clone(); + newCCFLAGS = []; + for flag in pcc['CCFLAGS']: + if ("-W" != flag[:2]) and ("-std=gnu99" != flag[:10]) and ("-pedantic" != flag[:9]) and ("-D_XOPEN_SOURCE=600" != flag[:19]) and ("-xc99=all" != flag[:9]): + newCCFLAGS.append(flag); + if ("-D_XOPEN_SOURCE=600" == flag[:19]): + newCCFLAGS.append("-D_XOPEN_SOURCE=500"); + pcc['CCFLAGS'] = newCCFLAGS; + pcc.Program('purinsendcc', ['purinsendcc.cc'] + p.Object(getopt)) + pcc.Program('purinrecvcc', ['purinrecvcc.cc'] + p.Object(getopt)) + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/async.c b/3rdparty/openpgm-svn-r1085/pgm/examples/async.c new file mode 100644 index 0000000..042bf8e --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/async.c @@ -0,0 +1,441 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Asynchronous queue for receiving packets in a separate managed thread. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#ifndef _WIN32 +# include +# include +# include +#else +# include +#endif +#include + +#include "async.h" + + +/* locals */ + +struct async_event_t { + struct async_event_t *next, *prev; + size_t len; + struct pgm_sockaddr_t addr; +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + char data[]; +#elif defined(__cplusplus) + char data[1]; +#else + char data[0]; +#endif +}; + + +static void on_data (async_t*const restrict, const void*restrict, const size_t, const struct pgm_sockaddr_t*restrict, const socklen_t); + + +/* queued data is stored as async_event_t objects + */ + +static inline +struct async_event_t* +async_event_alloc ( + size_t len + ) +{ + struct async_event_t* event; + event = (struct async_event_t*)calloc (1, len + sizeof(struct async_event_t)); + event->len = len; + return event; +} + +static inline +void +async_event_unref ( + struct async_event_t* const event + ) +{ + free (event); +} + +/* async_t implements a queue + */ + +static inline +void +async_push_event ( + async_t* restrict async, + struct async_event_t* restrict event + ) +{ + event->next = async->head; + if (async->head) + async->head->prev = event; + else + async->tail = event; + async->head = event; + async->length++; +} + +static inline +struct async_event_t* +async_pop_event ( + async_t* async + ) +{ + if (async->tail) + { + struct async_event_t *event = async->tail; + + async->tail = event->prev; + if (async->tail) + { + async->tail->next = NULL; + event->prev = NULL; + } + else + async->head = NULL; + async->length--; + + return event; + } + + return NULL; +} + +/* asynchronous receiver thread, sits in a loop processing incoming packets + */ + +static +#ifndef _WIN32 +void* +#else +unsigned +__stdcall +#endif +receiver_routine ( + void* arg + ) +{ + assert (NULL != arg); + async_t* async = (async_t*)arg; + assert (NULL != async->sock); +#ifndef _WIN32 + int fds; + fd_set readfds; +#else + int n_handles = 3, recv_sock, pending_sock; + HANDLE waitHandles[ 3 ]; + DWORD dwTimeout, dwEvents; + WSAEVENT recvEvent, pendingEvent; + socklen_t socklen = sizeof(int); + + recvEvent = WSACreateEvent (); + pgm_getsockopt (async->sock, PGM_RECV_SOCK, &recv_sock, &socklen); + WSAEventSelect (recv_sock, recvEvent, FD_READ); + pendingEvent = WSACreateEvent (); + pgm_getsockopt (async->sock, PGM_PENDING_SOCK, &pending_sock, &socklen); + WSAEventSelect (pending_sock, pendingEvent, FD_READ); + + waitHandles[0] = async->destroy_event; + waitHandles[1] = recvEvent; + waitHandles[2] = pendingEvent; +#endif /* !_WIN32 */ + +/* dispatch loop */ + do { + struct timeval tv; + char buffer[4096]; + size_t len; + struct pgm_sockaddr_t from; + socklen_t fromlen = sizeof (from); + const int status = pgm_recvfrom (async->sock, + buffer, + sizeof(buffer), + 0, + &len, + &from, + &fromlen, + NULL); + switch (status) { + case PGM_IO_STATUS_NORMAL: + on_data (async, buffer, len, &from, fromlen); + break; + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (async->sock, PGM_TIME_REMAIN, &tv, &optlen); + } + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (async->sock, PGM_RATE_REMAIN, &tv, &optlen); + } + case PGM_IO_STATUS_WOULD_BLOCK: +/* select for next event */ +block: +#ifndef _WIN32 + fds = async->destroy_pipe[0] + 1; + FD_ZERO(&readfds); + FD_SET(async->destroy_pipe[0], &readfds); + pgm_select_info (async->sock, &readfds, NULL, &fds); + fds = select (fds, &readfds, NULL, NULL, PGM_IO_STATUS_WOULD_BLOCK == status ? NULL : &tv); +#else + dwTimeout = PGM_IO_STATUS_WOULD_BLOCK == status ? INFINITE : (DWORD)((tv.tv_sec * 1000) + (tv. +tv_usec / 1000)); + dwEvents = WaitForMultipleObjects (n_handles, waitHandles, FALSE, dwTimeout); + switch (dwEvents) { + case WAIT_OBJECT_0+1: WSAResetEvent (recvEvent); break; + case WAIT_OBJECT_0+2: WSAResetEvent (pendingEvent); break; + default: break; + } +#endif /* !_WIN32 */ + break; + + default: + if (PGM_IO_STATUS_ERROR == status) + break; + } + } while (!async->is_destroyed); + +/* cleanup */ +#ifndef _WIN32 + return NULL; +#else + WSACloseEvent (recvEvent); + WSACloseEvent (pendingEvent); + _endthread(); + return 0; +#endif /* !_WIN32 */ +} + +/* enqueue a new data event. + */ + +static +void +on_data ( + async_t*const restrict async, + const void* restrict data, + const size_t len, + const struct pgm_sockaddr_t* restrict from, + const socklen_t fromlen + ) +{ + struct async_event_t* event = async_event_alloc (len); + memcpy (&event->addr, from, fromlen); + memcpy (&event->data, data, len); +#ifndef _WIN32 + pthread_mutex_lock (&async->pthread_mutex); + async_push_event (async, event); + if (1 == async->length) { + const char one = '1'; + const size_t writelen = write (async->notify_pipe[1], &one, sizeof(one)); + assert (sizeof(one) == writelen); + } + pthread_mutex_unlock (&async->pthread_mutex); +#else + WaitForSingleObject (async->win32_mutex, INFINITE); + async_push_event (async, event); + if (1 == async->length) { + SetEvent (async->notify_event); + } + ReleaseMutex (async->win32_mutex); +#endif /* _WIN32 */ +} + +/* create asynchronous thread handler from bound PGM sock. + * + * on success, 0 is returned. on error, -1 is returned, and errno set appropriately. + */ + +int +async_create ( + async_t** restrict async, + pgm_sock_t* const restrict sock + ) +{ + async_t* new_async; + + if (NULL == async || NULL == sock) { + errno = EINVAL; + return -1; + } + + new_async = (async_t*)calloc (1, sizeof(async_t)); + new_async->sock = sock; +#ifndef _WIN32 + int e; + e = pthread_mutex_init (&new_async->pthread_mutex, NULL); + if (0 != e) goto err_destroy; + e = pipe (new_async->notify_pipe); + const int flags = fcntl (new_async->notify_pipe[0], F_GETFL); + fcntl (new_async->notify_pipe[0], F_SETFL, flags | O_NONBLOCK); + if (0 != e) goto err_destroy; + e = pipe (new_async->destroy_pipe); + if (0 != e) goto err_destroy; + const int status = pthread_create (&new_async->thread, NULL, &receiver_routine, new_async); + if (0 != status) goto err_destroy; +#else + new_async->win32_mutex = CreateMutex (NULL, FALSE, NULL); + new_async->notify_event = CreateEvent (NULL, TRUE, FALSE, TEXT("AsyncNotify")); + new_async->destroy_event = CreateEvent (NULL, TRUE, FALSE, TEXT("AsyncDestroy")); + new_async->thread = (HANDLE)_beginthreadex (NULL, 0, &receiver_routine, new_async, 0, NULL); + if (0 == new_async->thread) goto err_destroy; +#endif /* _WIN32 */ + +/* return new object */ + *async = new_async; + return 0; + +err_destroy: +#ifndef _WIN32 + close (new_async->destroy_pipe[0]); + close (new_async->destroy_pipe[1]); + close (new_async->notify_pipe[0]); + close (new_async->notify_pipe[1]); + pthread_mutex_destroy (&new_async->pthread_mutex); +#else + CloseHandle (new_async->destroy_event); + CloseHandle (new_async->notify_event); + CloseHandle (new_async->win32_mutex); +#endif /* _WIN32 */ + if (new_async) + free (new_async); + return -1; +} + +/* Destroy asynchronous receiver, there must be no active queue consumer. + * + * on success, 0 is returned, on error -1 is returned and errno set appropriately. + */ + +int +async_destroy ( + async_t* const async + ) +{ + if (NULL == async || async->is_destroyed) { + errno = EINVAL; + return -1; + } + + async->is_destroyed = TRUE; +#ifndef _WIN32 + const char one = '1'; + const size_t writelen = write (async->destroy_pipe[1], &one, sizeof(one)); + assert (sizeof(one) == writelen); + pthread_join (async->thread, NULL); + close (async->destroy_pipe[0]); + close (async->destroy_pipe[1]); + close (async->notify_pipe[0]); + close (async->notify_pipe[1]); + pthread_mutex_destroy (&async->pthread_mutex); +#else + SetEvent (async->destroy_event); + WaitForSingleObject (async->thread, INFINITE); + CloseHandle (async->thread); + CloseHandle (async->destroy_event); + CloseHandle (async->notify_event); + CloseHandle (async->win32_mutex); +#endif /* !_WIN32 */ + while (async->head) { + struct async_event_t *next = async->head->next; + async_event_unref (async->head); + async->head = next; + async->length--; + } + free (async); + return 0; +} + +/* synchronous reading from the queue. + * + * returns GIOStatus with success, error, again, or eof. + */ + +ssize_t +async_recvfrom ( + async_t* const restrict async, + void* restrict buf, + size_t len, + struct pgm_sockaddr_t* restrict from, + socklen_t* restrict fromlen + ) +{ + struct async_event_t* event; + + if (NULL == async || NULL == buf || async->is_destroyed) { + errno = EINVAL; + return -1; + } + +#ifndef _WIN32 + pthread_mutex_lock (&async->pthread_mutex); + if (0 == async->length) { +/* flush event pipe */ + char tmp; + while (sizeof(tmp) == read (async->notify_pipe[0], &tmp, sizeof(tmp))); + pthread_mutex_unlock (&async->pthread_mutex); + errno = EAGAIN; + return -1; + } + event = async_pop_event (async); + pthread_mutex_unlock (&async->pthread_mutex); +#else + WaitForSingleObject (async->win32_mutex, INFINITE); + if (0 == async->length) { +/* clear event */ + ResetEvent (async->notify_event); + ReleaseMutex (async->win32_mutex); + errno = EAGAIN; + return -1; + } + event = async_pop_event (async); + ReleaseMutex (async->win32_mutex); +#endif /* _WIN32 */ + assert (NULL != event); + +/* pass data back to callee */ + const size_t event_len = MIN(event->len, len); + if (NULL != from && sizeof(struct pgm_sockaddr_t) == *fromlen) { + memcpy (from, &event->addr, *fromlen); + } + memcpy (buf, event->data, event_len); + async_event_unref (event); + return event_len; +} + +ssize_t +async_recv ( + async_t* const restrict async, + void* restrict buf, + size_t len + ) +{ + return async_recvfrom (async, buf, len, NULL, NULL); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/async.h b/3rdparty/openpgm-svn-r1085/pgm/examples/async.h new file mode 100644 index 0000000..788a777 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/async.h @@ -0,0 +1,82 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Asynchronous receive thread helper + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_ASYNC_H__ +#define __PGM_ASYNC_H__ + +struct async_event_t; + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct async_t { + pgm_sock_t* sock; +#ifndef _WIN32 + pthread_t thread; + int notify_pipe[2]; + int destroy_pipe[2]; + pthread_mutex_t pthread_mutex; +#else + HANDLE thread; + HANDLE notify_event; + HANDLE destroy_event; + HANDLE win32_mutex; +#endif + struct async_event_t *head, *tail; + unsigned length; + bool is_destroyed; +}; +typedef struct async_t async_t; + +int async_create (async_t** restrict, pgm_sock_t*const restrict); +int async_destroy (async_t* const); +ssize_t async_recv (async_t*const restrict, void* restrict, size_t); +ssize_t async_recvfrom (async_t*const restrict, void*restrict, size_t, struct pgm_sockaddr_t*restrict, socklen_t*restrict); + +#ifndef _WIN32 +static inline int async_get_fd (async_t* async) +{ + if (NULL == async) { + errno = EINVAL; + return -1; + } + return async->notify_pipe[0]; +} +#else +static inline HANDLE async_get_event (async_t* async) +{ + if (NULL == async) { + errno = EINVAL; + return NULL; + } + return async->notify_event; +} +#endif /* _WIN32 */ + +#ifdef __cplusplus +} +#endif + +#endif /* __PGM_ASYNC_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/blocksyncrecv.c b/3rdparty/openpgm-svn-r1085/pgm/examples/blocksyncrecv.c new file mode 100644 index 0000000..ec43d17 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/blocksyncrecv.c @@ -0,0 +1,350 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Simple PGM receiver: blocking synchronous receiver + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#ifndef G_OS_WIN32 +# include +#else +# include "getopt.h" +#endif +#include + +/* example dependencies */ +#include +#include + + +/* typedefs */ + +/* globals */ + +static int g_port = 0; +static const char* g_network = ""; +static gboolean g_multicast_loop = FALSE; +static int g_udp_encap_port = 0; + +static int g_max_tpdu = 1500; +static int g_sqns = 100; + +static pgm_sock_t* g_sock = NULL; +static gboolean g_quit = FALSE; + +#ifdef G_OS_UNIX +static void on_signal (int); +#else +static BOOL on_console_ctrl (DWORD); +#endif +static gboolean on_startup (void); +static int on_data (gconstpointer, size_t, struct pgm_sockaddr_t*); + + +G_GNUC_NORETURN static +void +usage ( + const char* bin + ) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); + exit (1); +} + +int +main ( + int argc, + char* argv[] + ) +{ + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + + log_init (); + g_message ("blocksyncrecv"); + + if (!pgm_init (&pgm_err)) { + g_error ("Unable to start PGM engine: %s", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:lh")) != -1) + { + switch (c) { + case 'n': g_network = optarg; break; + case 's': g_port = atoi (optarg); break; + case 'p': g_udp_encap_port = atoi (optarg); break; + case 'l': g_multicast_loop = TRUE; break; + + case 'h': + case '?': usage (binary_name); + } + } + +/* setup signal handlers */ + signal(SIGSEGV, on_sigsegv); +#ifdef SIGHUP + signal(SIGHUP, SIG_IGN); +#endif +#ifdef G_OS_UNIX + signal(SIGINT, on_signal); + signal(SIGTERM, on_signal); +#else + SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); +#endif + + on_startup(); + +/* dispatch loop */ + g_message ("entering PGM message loop ... "); + do { + char buffer[4096]; + size_t len; + struct pgm_sockaddr_t from; + socklen_t fromlen = sizeof(from); + const int status = pgm_recvfrom (g_sock, + buffer, + sizeof(buffer), + 0, + &len, + &from, + &fromlen, + &pgm_err); + if (PGM_IO_STATUS_NORMAL == status) + on_data (buffer, len, &from); + else { + if (pgm_err) { + g_warning ("%s", pgm_err->message); + pgm_error_free (pgm_err); + pgm_err = NULL; + } + if (PGM_IO_STATUS_ERROR == status) + break; + } + } while (!g_quit); + + g_message ("message loop terminated, cleaning up."); + +/* cleanup */ + if (g_sock) { + g_message ("closing PGM socket."); + pgm_close (g_sock, TRUE); + g_sock = NULL; + } + + g_message ("PGM engine shutdown."); + pgm_shutdown (); + g_message ("finished."); + return EXIT_SUCCESS; +} + +#ifdef G_OS_UNIX +static +void +on_signal ( + int signum + ) +{ + g_message ("on_signal (signum:%d)", signum); + g_quit = TRUE; +} +#else +static +BOOL +on_console_ctrl ( + DWORD dwCtrlType + ) +{ + g_message ("on_console_ctrl (dwCtrlType:%lu)", (unsigned long)dwCtrlType); + g_quit = TRUE; + return TRUE; +} +#endif /* !G_OS_UNIX */ + +static +gboolean +on_startup (void) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + + g_message ("startup."); + +/* parse network parameter into transport address structure */ + if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { + g_error ("parsing network parameter: %s", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + if (g_udp_encap_port) { + g_message ("create PGM/UDP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (g_sock, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + pgm_setsockopt (g_sock, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + } else { + g_message ("create PGM/IP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (g_sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int recv_only = 1, + passive = 0, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_secs (2), + nak_rdata_ivl = pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + pgm_setsockopt (g_sock, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + pgm_setsockopt (g_sock, PGM_PASSIVE, &passive, sizeof(passive)); + pgm_setsockopt (g_sock, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); + pgm_setsockopt (g_sock, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); + pgm_setsockopt (g_sock, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + pgm_setsockopt (g_sock, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + pgm_setsockopt (g_sock, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + pgm_setsockopt (g_sock, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + g_error ("creating GSI: %s", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (g_sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + g_error ("binding PGM socket: %s", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (g_sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (g_sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int blocking = 0, + multicast_loop = g_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (g_sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (g_sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (g_sock, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (g_sock, PGM_NOBLOCK, &blocking, sizeof(blocking)); + + if (!pgm_connect (g_sock, &pgm_err)) { + g_error ("connecting PGM socket: %s", pgm_err->message); + goto err_abort; + } + + g_message ("startup complete."); + return TRUE; + +err_abort: + if (NULL != g_sock) { + pgm_close (g_sock, FALSE); + g_sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +static +int +on_data ( + gconstpointer data, + size_t len, + struct pgm_sockaddr_t* from + ) +{ +/* protect against non-null terminated strings */ + char buf[1024], tsi[PGM_TSISTRLEN]; + const size_t buflen = MIN( sizeof(buf) - 1, len ); + strncpy (buf, data, buflen); + buf[buflen] = '\0'; + pgm_tsi_print_r (&from->sa_addr, tsi, sizeof(tsi)); + + g_message ("\"%s\" (%u bytes from %s)", + buf, + (unsigned)len, + tsi); + + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/daytime.c b/3rdparty/openpgm-svn-r1085/pgm/examples/daytime.c new file mode 100644 index 0000000..dda619b --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/daytime.c @@ -0,0 +1,546 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Daytime broadcast service. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#ifndef _WIN32 +# include +# include +# include +#else +# include +# include +# include "getopt.h" +# define snprintf _snprintf +#endif +#include + + +/* globals */ +#define TIME_FORMAT "%a, %d %b %Y %H:%M:%S %z" + +static int port = 0; +static const char* network = ""; +static bool use_multicast_loop = FALSE; +static int udp_encap_port = 0; + +static int max_tpdu = 1500; +static int max_rte = 400*1000; /* very conservative rate, 2.5mb/s */ +static int sqns = 100; + +static bool use_pgmcc = FALSE; +static bool use_fec = FALSE; +static bool use_ondemand_parity = FALSE; +static int proactive_packets = 0; +static int rs_k = 8; +static int rs_n = 255; + +static pgm_sock_t* sock = NULL; +static bool is_terminated = FALSE; + +#ifndef _WIN32 +static pthread_t nak_thread; +static int terminate_pipe[2]; +static void on_signal (int); +static void* nak_routine (void*); +#else +static HANDLE nak_thread; +static HANDLE terminate_event; +static BOOL on_console_ctrl (DWORD); +static unsigned __stdcall nak_routine (void*); +#endif +#ifndef _MSC_VER +static void usage (const char*) __attribute__((__noreturn__)); +#else +static void usage (const char*); +#endif + +static bool on_startup (void); +static bool create_sock (void); +static bool create_nak_thread (void); + + +static void +usage ( + const char* bin + ) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -r : Regulate to rate bytes per second\n"); + fprintf (stderr, " -c : Enable PGMCC\n"); + fprintf (stderr, " -f : Enable FEC: proactive, ondemand, or both\n"); + fprintf (stderr, " -N : Reed-Solomon block size (255)\n"); + fprintf (stderr, " -K : Reed-Solomon group size (8)\n"); + fprintf (stderr, " -P : Number of pro-active parity packets (h)\n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); + fprintf (stderr, " -i : List available interfaces\n"); + exit (EXIT_SUCCESS); +} + +int +main ( + int argc, + char *argv[] + ) +{ + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + + puts ("PGM daytime service"); + + if (!pgm_init (&pgm_err)) { + fprintf (stderr, "Unable to start PGM engine: %s\n", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:r:cf:N:K:P:lih")) != -1) + { + switch (c) { + case 'n': network = optarg; break; + case 's': port = atoi (optarg); break; + case 'p': udp_encap_port = atoi (optarg); break; + case 'r': max_rte = atoi (optarg); break; + case 'c': use_pgmcc = TRUE; break; + case 'f': + use_fec = TRUE; + switch (optarg[0]) { + case 'p': + case 'P': + proactive_packets = 1; + break; + case 'b': + case 'B': + proactive_packets = 1; + case 'o': + case 'O': + use_ondemand_parity = TRUE; + break; + } + break; + case 'N': rs_n = atoi (optarg); break; + case 'K': rs_k = atoi (optarg); break; + case 'P': proactive_packets = atoi (optarg); break; + + case 'l': use_multicast_loop = TRUE; break; + + case 'i': + pgm_if_print_all(); + return EXIT_SUCCESS; + + case 'h': + case '?': + usage (binary_name); + } + } + + if (use_fec && ( !rs_n || !rs_k )) { + fprintf (stderr, "Invalid Reed-Solomon parameters RS(%d,%d).\n", rs_n, rs_k); + usage (binary_name); + } + +/* setup signal handlers */ +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif +#ifndef _WIN32 + int e = pipe (terminate_pipe); + assert (0 == e); + const int flags = fcntl (terminate_pipe[0], F_GETFL); + fcntl (terminate_pipe[0], F_SETFL, flags | O_NONBLOCK); + signal (SIGINT, on_signal); + signal (SIGTERM, on_signal); +#else + terminate_event = CreateEvent (NULL, TRUE, FALSE, TEXT("TerminateEvent")); + SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); +#endif /* !_WIN32 */ + + if (!on_startup()) { + fprintf (stderr, "Startup failed\n"); + return EXIT_FAILURE; + } + +/* service loop */ + do { + time_t now; + time (&now); + const struct tm* time_ptr = localtime(&now); +#ifndef _WIN32 + char s[1024]; + const size_t slen = strftime (s, sizeof(s), TIME_FORMAT, time_ptr); + const int status = pgm_send (sock, s, slen + 1, NULL); +#else + char s[1024]; + const size_t slen = strftime (s, sizeof(s), TIME_FORMAT, time_ptr); + wchar_t ws[1024]; + size_t wslen = MultiByteToWideChar (CP_ACP, 0, s, slen, ws, 1024); + char us[1024]; + size_t uslen = WideCharToMultiByte (CP_UTF8, 0, ws, wslen + 1, us, sizeof(us), NULL, NULL); + const int status = pgm_send (sock, us, uslen + 1, NULL); +#endif + if (PGM_IO_STATUS_NORMAL != status) { + fprintf (stderr, "pgm_send() failed.\n"); + } +#ifndef _WIN32 + sleep (1); +#else + Sleep (1 * 1000); +#endif + } while (!is_terminated); + +/* cleanup */ + puts ("Waiting for NAK thread."); +#ifndef _WIN32 + pthread_join (nak_thread, NULL); + close (terminate_pipe[0]); + close (terminate_pipe[1]); +#else + WaitForSingleObject (nak_thread, INFINITE); + CloseHandle (nak_thread); + CloseHandle (terminate_event); +#endif /* !_WIN32 */ + + if (sock) { + puts ("Closing PGM sock."); + pgm_close (sock, TRUE); + sock = NULL; + } + + puts ("PGM engine shutdown."); + pgm_shutdown(); + puts ("finished."); + return EXIT_SUCCESS; +} + +#ifndef _WIN32 +static +void +on_signal ( + int signum + ) +{ + printf ("on_signal (signum:%d)\n", signum); + is_terminated = TRUE; + const char one = '1'; + const size_t writelen = write (terminate_pipe[1], &one, sizeof(one)); + assert (sizeof(one) == writelen); +} +#else +static +BOOL +on_console_ctrl ( + DWORD dwCtrlType + ) +{ + printf ("on_console_ctrl (dwCtrlType:%lu)\n", (unsigned long)dwCtrlType); + is_terminated = TRUE; + SetEvent (terminate_event); + return TRUE; +} +#endif /* !_WIN32 */ + +static +bool +on_startup (void) +{ + bool status = (create_sock() && create_nak_thread()); + if (status) + puts ("Startup complete."); + return status; +} + +static +bool +create_sock (void) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + +/* parse network parameter into sock address structure */ + if (!pgm_getaddrinfo (network, NULL, &res, &pgm_err)) { + fprintf (stderr, "Parsing network parameter: %s\n", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + puts ("Create PGM socket."); + if (udp_encap_port) { + if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + fprintf (stderr, "Creating PGM/UDP socket: %s\n", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (sock, PGM_UDP_ENCAP_UCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + pgm_setsockopt (sock, PGM_UDP_ENCAP_MCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + } else { + if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + fprintf (stderr, "Creating PGM/IP socket: %s\n", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int send_only = 1, + ambient_spm = pgm_secs (30), + heartbeat_spm[] = { pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (1300), + pgm_secs (7), + pgm_secs (16), + pgm_secs (25), + pgm_secs (30) }; + + pgm_setsockopt (sock, PGM_SEND_ONLY, &send_only, sizeof(send_only)); + pgm_setsockopt (sock, PGM_MTU, &max_tpdu, sizeof(max_tpdu)); + pgm_setsockopt (sock, PGM_TXW_SQNS, &sqns, sizeof(sqns)); + pgm_setsockopt (sock, PGM_TXW_MAX_RTE, &max_rte, sizeof(max_rte)); + pgm_setsockopt (sock, PGM_AMBIENT_SPM, &ambient_spm, sizeof(ambient_spm)); + pgm_setsockopt (sock, PGM_HEARTBEAT_SPM, &heartbeat_spm, sizeof(heartbeat_spm)); + +#ifdef I_UNDERSTAND_PGMCC_AND_FEC_ARE_NOT_SUPPORTED + if (use_pgmcc) { + struct pgm_pgmccinfo_t pgmccinfo; + pgmccinfo.ack_bo_ivl = pgm_msecs (50); + pgmccinfo.ack_c = 75; + pgmccinfo.ack_c_p = 500; + pgm_setsockopt (sock, PGM_USE_PGMCC, &pgmccinfo, sizeof(pgmccinfo)); + } + if (use_fec) { + struct pgm_fecinfo_t fecinfo; + fecinfo.block_size = rs_n; + fecinfo.proactive_packets = proactive_packets; + fecinfo.group_size = rs_k; + fecinfo.ondemand_parity_enabled = use_ondemand_parity; + fecinfo.var_pktlen_enabled = TRUE; + pgm_setsockopt (sock, PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); + } +#endif + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = port ? port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + fprintf (stderr, "Creating GSI: %s\n", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + fprintf (stderr, "Binding PGM socket: %s\n", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int nonblocking = 1, + multicast_loop = use_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (sock, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (sock, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); + + if (!pgm_connect (sock, &pgm_err)) { + fprintf (stderr, "Connecting PGM socket: %s\n", pgm_err->message); + goto err_abort; + } + + return TRUE; + +err_abort: + if (NULL != sock) { + pgm_close (sock, FALSE); + sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +static +bool +create_nak_thread (void) +{ +#ifndef _WIN32 + const int status = pthread_create (&nak_thread, NULL, &nak_routine, sock); + if (0 != status) { + fprintf (stderr, "Creating new thread: %s\n", strerror (status)); + return FALSE; + } +#else + nak_thread = (HANDLE)_beginthreadex (NULL, 0, &nak_routine, sock, 0, NULL); + const int save_errno = errno; + if (0 == nak_thread) { + fprintf (stderr, "Creating new thread: %s\n", strerror (save_errno)); + return FALSE; + } +#endif /* _WIN32 */ + return TRUE; +} + +static +#ifndef _WIN32 +void* +#else +unsigned +__stdcall +#endif +nak_routine ( + void* arg + ) +{ +/* dispatch loop */ + pgm_sock_t* nak_sock = (pgm_sock_t*)arg; +#ifndef _WIN32 + int fds; + fd_set readfds; +#else + int n_handles = 4, recv_sock, repair_sock, pending_sock; + HANDLE waitHandles[ 4 ]; + DWORD dwTimeout, dwEvents; + WSAEVENT recvEvent, repairEvent, pendingEvent; + socklen_t socklen = sizeof(int); + + recvEvent = WSACreateEvent (); + pgm_getsockopt (nak_sock, PGM_RECV_SOCK, &recv_sock, &socklen); + WSAEventSelect (recv_sock, recvEvent, FD_READ); + repairEvent = WSACreateEvent (); + pgm_getsockopt (nak_sock, PGM_REPAIR_SOCK, &repair_sock, &socklen); + WSAEventSelect (repair_sock, repairEvent, FD_READ); + pendingEvent = WSACreateEvent (); + pgm_getsockopt (nak_sock, PGM_PENDING_SOCK, &pending_sock, &socklen); + WSAEventSelect (pending_sock, pendingEvent, FD_READ); + + waitHandles[0] = terminate_event; + waitHandles[1] = recvEvent; + waitHandles[2] = repairEvent; + waitHandles[3] = pendingEvent; +#endif /* !_WIN32 */ + do { + struct timeval tv; + char buf[4064]; + pgm_error_t* pgm_err = NULL; + const int status = pgm_recv (nak_sock, buf, sizeof(buf), 0, NULL, &pgm_err); + switch (status) { + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (sock, PGM_TIME_REMAIN, &tv, &optlen); + } + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (sock, PGM_RATE_REMAIN, &tv, &optlen); + } + case PGM_IO_STATUS_WOULD_BLOCK: +block: +#ifndef _WIN32 + fds = terminate_pipe[0] + 1; + FD_ZERO(&readfds); + FD_SET(terminate_pipe[0], &readfds); + pgm_select_info (nak_sock, &readfds, NULL, &fds); + fds = select (fds, &readfds, NULL, NULL, PGM_IO_STATUS_WOULD_BLOCK == status ? NULL : &tv); +#else + dwTimeout = PGM_IO_STATUS_WOULD_BLOCK == status ? INFINITE : (DWORD)((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + dwEvents = WaitForMultipleObjects (n_handles, waitHandles, FALSE, dwTimeout); + switch (dwEvents) { + case WAIT_OBJECT_0+1: WSAResetEvent (recvEvent); break; + case WAIT_OBJECT_0+2: WSAResetEvent (repairEvent); break; + case WAIT_OBJECT_0+3: WSAResetEvent (pendingEvent); break; + default: break; + } +#endif /* !_WIN32 */ + break; + + default: + if (pgm_err) { + fprintf (stderr, "%s\n", pgm_err->message ? pgm_err->message : "(null)"); + pgm_error_free (pgm_err); + pgm_err = NULL; + } + if (PGM_IO_STATUS_ERROR == status) + break; + } + } while (!is_terminated); +#ifndef _WIN32 + return NULL; +#else + WSACloseEvent (recvEvent); + WSACloseEvent (repairEvent); + WSACloseEvent (pendingEvent); + _endthread(); + return 0; +#endif +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecv.c b/3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecv.c new file mode 100644 index 0000000..27625ce --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecv.c @@ -0,0 +1,382 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Simple PGM receiver: epoll based non-blocking synchronous receiver. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef G_OS_UNIX +# include +# include +# include +# include +#endif +#include + +/* example dependencies */ +#include +#include + + +/* typedefs */ + +/* globals */ + +static int g_port = 0; +static const char* g_network = ""; +static gboolean g_multicast_loop = FALSE; +static int g_udp_encap_port = 0; + +static int g_max_tpdu = 1500; +static int g_sqns = 100; + +static pgm_sock_t* g_sock = NULL; +static gboolean g_quit = FALSE; + +static void on_signal (int); +static gboolean on_startup (void); + +static int on_data (gconstpointer, size_t, struct pgm_sockaddr_t*); + + +G_GNUC_NORETURN static +void +usage ( + const char* bin + ) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); + exit (1); +} + +int +main ( + int argc, + char* argv[] + ) +{ + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + + log_init (); + g_message ("enonblocksyncrecv"); + + if (!pgm_init (&pgm_err)) { + g_error ("Unable to start PGM engine: %s", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:lh")) != -1) + { + switch (c) { + case 'n': g_network = optarg; break; + case 's': g_port = atoi (optarg); break; + case 'p': g_udp_encap_port = atoi (optarg); break; + case 'l': g_multicast_loop = TRUE; break; + + case 'h': + case '?': usage (binary_name); + } + } + + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); + signal (SIGINT, on_signal); + signal (SIGTERM, on_signal); +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif + + if (!on_startup ()) { + g_error ("startup failed"); + return EXIT_FAILURE; + } + +/* epoll file descriptor */ + int efd = epoll_create (IP_MAX_MEMBERSHIPS); + if (efd < 0) { + g_error ("epoll_create failed errno %i: \"%s\"", errno, strerror(errno)); + return EXIT_FAILURE; + } + + int retval = pgm_epoll_ctl (g_sock, efd, EPOLL_CTL_ADD, EPOLLIN); + if (retval < 0) { + g_error ("pgm_epoll_ctl failed."); + return EXIT_FAILURE; + } + + struct epoll_event events[1]; /* wait for maximum 1 event */ + +/* dispatch loop */ + g_message ("entering PGM message loop ... "); + do { + struct timeval tv; + int timeout; + char buffer[4096]; + size_t len; + struct pgm_sockaddr_t from; + socklen_t fromlen = sizeof(from); + const int status = pgm_recvfrom (g_sock, + buffer, + sizeof(buffer), + 0, + &len, + &from, + &fromlen, + &pgm_err); + switch (status) { + case PGM_IO_STATUS_NORMAL: + on_data (buffer, len, &from); + break; + + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (g_sock, PGM_TIME_REMAIN, &tv, &optlen); + } + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (g_sock, PGM_RATE_REMAIN, &tv, &optlen); + } +/* fall through */ + case PGM_IO_STATUS_WOULD_BLOCK: +/* poll for next event */ +block: + timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + epoll_wait (efd, events, G_N_ELEMENTS(events), timeout /* ms */); + break; + + default: + if (pgm_err) { + g_warning ("%s", pgm_err->message); + pgm_error_free (pgm_err); + pgm_err = NULL; + } + if (PGM_IO_STATUS_ERROR == status) + break; + } + } while (!g_quit); + + g_message ("message loop terminated, cleaning up."); + +/* cleanup */ + close (efd); + if (g_sock) { + g_message ("closing PGM socket."); + pgm_close (g_sock, TRUE); + g_sock = NULL; + } + + g_message ("PGM engine shutdown."); + pgm_shutdown (); + g_message ("finished."); + return EXIT_SUCCESS; +} + +static +void +on_signal ( + int signum + ) +{ + g_message ("on_signal (signum:%d)", signum); + g_quit = TRUE; +} + +static +gboolean +on_startup (void) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + + g_message ("startup."); + +/* parse network parameter into transport address structure */ + if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { + g_error ("parsing network parameter: %s", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + if (g_udp_encap_port) { + g_message ("create PGM/UDP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (g_sock, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + pgm_setsockopt (g_sock, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + } else { + g_message ("create PGM/IP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (g_sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int recv_only = 1, + passive = 0, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_secs (2), + nak_rdata_ivl = pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + pgm_setsockopt (g_sock, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + pgm_setsockopt (g_sock, PGM_PASSIVE, &passive, sizeof(passive)); + pgm_setsockopt (g_sock, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); + pgm_setsockopt (g_sock, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); + pgm_setsockopt (g_sock, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + pgm_setsockopt (g_sock, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + pgm_setsockopt (g_sock, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + pgm_setsockopt (g_sock, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + g_error ("creating GSI: %s", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (g_sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + g_error ("binding PGM socket: %s", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (g_sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (g_sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int nonblocking = 1, + multicast_loop = g_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (g_sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (g_sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (g_sock, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (g_sock, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); + + if (!pgm_connect (g_sock, &pgm_err)) { + g_error ("connecting PGM socket: %s", pgm_err->message); + goto err_abort; + } + + g_message ("startup complete."); + return TRUE; + +err_abort: + if (NULL != g_sock) { + pgm_close (g_sock, FALSE); + g_sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +static +int +on_data ( + gconstpointer data, + size_t len, + struct pgm_sockaddr_t* from + ) +{ +/* protect against non-null terminated strings */ + char buf[1024], tsi[PGM_TSISTRLEN]; + const size_t buflen = MIN(sizeof(buf) - 1, len); + strncpy (buf, (const char*)data, buflen); + buf[buflen] = '\0'; + pgm_tsi_print_r (&from->sa_addr, tsi, sizeof(tsi)); + + g_message ("\"%s\" (%u bytes from %s)", + buf, + (unsigned)len, + tsi); + + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecvmsg.c b/3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecvmsg.c new file mode 100644 index 0000000..9a0e9c6 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecvmsg.c @@ -0,0 +1,382 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Simple PGM receiver: blocking synchronous receiver with scatter/gather io + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef G_OS_UNIX +# include +# include +# include +# include +#endif +#include + +/* example dependencies */ +#include +#include + + +/* typedefs */ + +/* globals */ + +static int g_port = 0; +static const char* g_network = ""; +static gboolean g_multicast_loop = FALSE; +static int g_udp_encap_port = 0; + +static int g_max_tpdu = 1500; +static int g_sqns = 100; + +static pgm_sock_t* g_sock = NULL; +static gboolean g_quit = FALSE; + +static void on_signal (int); +static gboolean on_startup (void); + +static int on_datav (struct pgm_msgv_t*, size_t); + + +G_GNUC_NORETURN static +void +usage ( + const char* bin + ) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); + exit (1); +} + +int +main ( + int argc, + char* argv[] + ) +{ + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + + log_init (); + g_message ("enonblocksyncrecvmsg"); + + if (!pgm_init (&pgm_err)) { + g_error ("Unable to start PGM engine: %s", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:lh")) != -1) + { + switch (c) { + case 'n': g_network = optarg; break; + case 's': g_port = atoi (optarg); break; + case 'p': g_udp_encap_port = atoi (optarg); break; + case 'l': g_multicast_loop = TRUE; break; + + case 'h': + case '?': usage (binary_name); + } + } + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); + signal (SIGINT, on_signal); + signal (SIGTERM, on_signal); +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif + + if (!on_startup ()) { + g_error ("startup failed"); + return EXIT_FAILURE; + } + +/* epoll file descriptor */ + int efd = epoll_create (IP_MAX_MEMBERSHIPS); + if (efd < 0) { + g_error ("epoll_create failed errno %i: \"%s\"", errno, strerror(errno)); + return EXIT_FAILURE; + } + + int retval = pgm_epoll_ctl (g_sock, efd, EPOLL_CTL_ADD, EPOLLIN); + if (retval < 0) { + g_error ("pgm_epoll_ctl failed."); + return EXIT_FAILURE; + } + +/* incoming message buffer */ + struct pgm_msgv_t msgv; + struct epoll_event events[1]; /* wait for maximum 1 event */ + +/* dispatch loop */ + g_message ("entering PGM message loop ... "); + do { + struct timeval tv; + int timeout; + size_t len; + const int status = pgm_recvmsg (g_sock, + &msgv, + 0, + &len, + &pgm_err); + switch (status) { + case PGM_IO_STATUS_NORMAL: + on_datav (&msgv, len); + break; + + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (g_sock, PGM_TIME_REMAIN, &tv, &optlen); + } + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (g_sock, PGM_RATE_REMAIN, &tv, &optlen); + } +/* fall through */ + case PGM_IO_STATUS_WOULD_BLOCK: +/* poll for next event */ +block: + timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + epoll_wait (efd, events, G_N_ELEMENTS(events), timeout /* ms */); + break; + + default: + if (pgm_err) { + g_warning ("%s", pgm_err->message); + pgm_error_free (pgm_err); + pgm_err = NULL; + } + if (PGM_IO_STATUS_ERROR == status) + break; + } + } while (!g_quit); + + g_message ("message loop terminated, cleaning up."); + +/* cleanup */ + close (efd); + if (g_sock) { + g_message ("closing PGM socket."); + pgm_close (g_sock, TRUE); + g_sock = NULL; + } + + g_message ("PGM engine shutdown."); + pgm_shutdown (); + g_message ("finished."); + return EXIT_SUCCESS; +} + +static +void +on_signal ( + int signum + ) +{ + g_message ("on_signal (signum:%d)", signum); + g_quit = TRUE; +} + +static +gboolean +on_startup (void) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + + g_message ("startup."); + +/* parse network parameter into transport address structure */ + if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { + g_error ("parsing network parameter: %s", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + if (g_udp_encap_port) { + g_message ("create PGM/UDP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (g_sock, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + pgm_setsockopt (g_sock, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + } else { + g_message ("create PGM/IP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (g_sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int recv_only = 1, + passive = 0, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_secs (2), + nak_rdata_ivl = pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + pgm_setsockopt (g_sock, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + pgm_setsockopt (g_sock, PGM_PASSIVE, &passive, sizeof(passive)); + pgm_setsockopt (g_sock, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); + pgm_setsockopt (g_sock, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); + pgm_setsockopt (g_sock, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + pgm_setsockopt (g_sock, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + pgm_setsockopt (g_sock, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + pgm_setsockopt (g_sock, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + g_error ("creating GSI: %s", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (g_sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + g_error ("binding PGM socket: %s", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (g_sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (g_sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int nonblocking = 1, + multicast_loop = g_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (g_sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (g_sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (g_sock, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (g_sock, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); + + if (!pgm_connect (g_sock, &pgm_err)) { + g_error ("connecting PGM socket: %s", pgm_err->message); + goto err_abort; + } + + g_message ("startup complete."); + return TRUE; + +err_abort: + if (NULL != g_sock) { + pgm_close (g_sock, FALSE); + g_sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +static +int +on_datav ( + struct pgm_msgv_t* datav, /* one msgv object */ + size_t len + ) +{ + char tsi[PGM_TSISTRLEN]; + pgm_tsi_print_r (&datav->msgv_skb[0]->tsi, tsi, sizeof(tsi)); + g_message ("(%u bytes from %s)", (unsigned)len, tsi); + +/* protect against non-null terminated strings */ + const struct pgm_sk_buff_t* skb = datav->msgv_skb[0]; + int i = 0; + while (len) + { + char buf[1024]; + const size_t buflen = MIN( sizeof(buf) - 1, skb->len ); + strncpy (buf, (const char*)skb->data, buflen); + buf[buflen] = '\0'; + g_message ("\t%i: %s (%" G_GUINT16_FORMAT " bytes)", ++i, buf, skb->len); + len -= skb->len; + skb++; + } + + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecvmsgv.c b/3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecvmsgv.c new file mode 100644 index 0000000..0a04056 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecvmsgv.c @@ -0,0 +1,397 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Simple PGM receiver: epoll based non-blocking synchronous receiver with scatter/gather io + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef G_OS_UNIX +# include +# include +# include +# include +# include +#endif +#include + +/* example dependencies */ +#include +#include + + +/* typedefs */ + +/* globals */ + +static int g_port = 0; +static const char* g_network = ""; +static gboolean g_multicast_loop = FALSE; +static int g_udp_encap_port = 0; + +static int g_max_tpdu = 1500; +static int g_sqns = 100; + +static pgm_sock_t* g_sock = NULL; +static gboolean g_quit = FALSE; + +static void on_signal (int); +static gboolean on_startup (void); + +static int on_msgv (struct pgm_msgv_t*, size_t); + + +G_GNUC_NORETURN static +void +usage ( + const char* bin + ) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); + exit (1); +} + +int +main ( + int argc, + char* argv[] + ) +{ + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + + log_init (); + g_message ("enonblocksyncrecvmsgv"); + + if (!pgm_init (&pgm_err)) { + g_error ("Unable to start PGM engine: %s", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:lh")) != -1) + { + switch (c) { + case 'n': g_network = optarg; break; + case 's': g_port = atoi (optarg); break; + case 'p': g_udp_encap_port = atoi (optarg); break; + case 'l': g_multicast_loop = TRUE; break; + + case 'h': + case '?': usage (binary_name); + } + } + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); + signal (SIGINT, on_signal); + signal (SIGTERM, on_signal); +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif + + if (!on_startup ()) { + g_error ("startup failed"); + return EXIT_FAILURE; + } + +/* incoming message buffer, iov_len must be less than SC_IOV_MAX */ + const long iov_len = 8; + const long ev_len = 1; + g_message ("Using iov_len %li ev_len %li", iov_len, ev_len); + + struct pgm_msgv_t msgv[iov_len]; + struct epoll_event events[ev_len]; /* wait for maximum 1 event */ + +/* epoll file descriptor */ + const int efd = epoll_create (IP_MAX_MEMBERSHIPS); + if (efd < 0) { + g_error ("epoll_create failed errno %i: \"%s\"", errno, strerror(errno)); + return EXIT_FAILURE; + } + + const int retval = pgm_epoll_ctl (g_sock, efd, EPOLL_CTL_ADD, EPOLLIN); + if (retval < 0) { + g_error ("pgm_epoll_ctl failed."); + return EXIT_FAILURE; + } + +/* dispatch loop */ + g_message ("entering PGM message loop ... "); + do { + struct timeval tv; + int timeout; + size_t len; + const int status = pgm_recvmsgv (g_sock, + msgv, + iov_len, + 0, + &len, + &pgm_err); + switch (status) { + case PGM_IO_STATUS_NORMAL: + on_msgv (msgv, len); + break; + + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (g_sock, PGM_TIME_REMAIN, &tv, &optlen); + } + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (g_sock, PGM_RATE_REMAIN, &tv, &optlen); + } +/* fall through */ + case PGM_IO_STATUS_WOULD_BLOCK: +/* poll for next event */ +block: + timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + epoll_wait (efd, events, G_N_ELEMENTS(events), timeout /* ms */); + break; + + default: + if (pgm_err) { + g_warning ("%s", pgm_err->message); + pgm_error_free (pgm_err); + pgm_err = NULL; + } + if (PGM_IO_STATUS_ERROR == status) + break; + } + } while (!g_quit); + + g_message ("message loop terminated, cleaning up."); + +/* cleanup */ + close (efd); + if (g_sock) { + g_message ("closing PGM socket."); + pgm_close (g_sock, TRUE); + g_sock = NULL; + } + + g_message ("PGM engine shutdown."); + pgm_shutdown (); + g_message ("finished."); + return EXIT_SUCCESS; +} + +static +void +on_signal ( + int signum + ) +{ + g_message ("on_signal (signum:%d)", signum); + g_quit = TRUE; +} + +static +gboolean +on_startup (void) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + + g_message ("startup."); + +/* parse network parameter into transport address structure */ + if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { + g_error ("parsing network parameter: %s", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + if (g_udp_encap_port) { + g_message ("create PGM/UDP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (g_sock, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + pgm_setsockopt (g_sock, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + } else { + g_message ("create PGM/IP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (g_sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int recv_only = 1, + passive = 0, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_secs (2), + nak_rdata_ivl = pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + pgm_setsockopt (g_sock, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + pgm_setsockopt (g_sock, PGM_PASSIVE, &passive, sizeof(passive)); + pgm_setsockopt (g_sock, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); + pgm_setsockopt (g_sock, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); + pgm_setsockopt (g_sock, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + pgm_setsockopt (g_sock, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + pgm_setsockopt (g_sock, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + pgm_setsockopt (g_sock, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + g_error ("creating GSI: %s", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (g_sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + g_error ("binding PGM socket: %s", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (g_sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (g_sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int nonblocking = 1, + multicast_loop = g_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (g_sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (g_sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (g_sock, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (g_sock, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); + + if (!pgm_connect (g_sock, &pgm_err)) { + g_error ("connecting PGM socket: %s", pgm_err->message); + goto err_abort; + } + + g_message ("startup complete."); + return TRUE; + +err_abort: + if (NULL != g_sock) { + pgm_close (g_sock, FALSE); + g_sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +static +int +on_msgv ( + struct pgm_msgv_t* msgv, /* an array of msgv's */ + size_t len /* total size of all msgv's */ + ) +{ + g_message ("(%u bytes)", + (unsigned)len); + + guint i = 0; + +/* for each apdu display each fragment */ + do { + const struct pgm_sk_buff_t* pskb = msgv[i].msgv_skb[0]; + gsize apdu_len = 0; + for (unsigned j = 0; j < msgv[i].msgv_len; j++) + apdu_len += msgv[i].msgv_skb[j]->len; +/* truncate to first fragment to make GLib printing happy */ + char buf[1024], tsi[PGM_TSISTRLEN]; + const size_t buflen = MIN( sizeof(buf) - 1, pskb->len ); + strncpy (buf, (const char*)pskb->data, buflen); + buf[buflen] = '\0'; + pgm_tsi_print_r (&pskb->tsi, tsi, sizeof(tsi)); + if (msgv[i].msgv_len > 1) { + g_message ("\t%u: \"%s\" ... (%" G_GSIZE_FORMAT " bytes from %s)", i, buf, apdu_len, tsi); + } else { + g_message ("\t%u: \"%s\" (%" G_GSIZE_FORMAT " bytes from %s)", i, buf, apdu_len, tsi); + } + i++; + len -= apdu_len; + } while (len); + + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/getopt.c b/3rdparty/openpgm-svn-r1085/pgm/examples/getopt.c new file mode 100644 index 0000000..8c655b6 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/getopt.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include "getopt.h" + +int opterr = 1, /* if error message should be printed */ + optind = 1, /* index into parent argv vector */ + optopt, /* character checked for validity */ + optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int +getopt(int nargc, char* const* nargv, const char* ostr) +{ + static char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc || *(place = nargv[optind]) != '-') { + place = EMSG; + return (-1); + } + if (place[1] && *++place == '-') { /* found "--" */ + ++optind; + place = EMSG; + return (-1); + } + } /* option letter okay? */ + if ((optopt = (int)*place++) == (int)':' || + !(oli = strchr(ostr, optopt))) { + /* + * if the user didn't specify '-' as an option, + * assume it means -1. + */ + if (optopt == (int)'-') + return (-1); + if (!*place) + ++optind; + if (opterr && *ostr != ':' && optopt != BADCH) + (void)fprintf(stderr, "%s: illegal option -- %c\n", + "progname", optopt); + return (BADCH); + } + if (*++oli != ':') { /* don't need argument */ + optarg = NULL; + if (!*place) + ++optind; + } + else { /* need an argument */ + if (*place) /* no white space */ + optarg = place; + else if (nargc <= ++optind) { /* no arg */ + place = EMSG; + if (*ostr == ':') + return (BADARG); + if (opterr) + (void)fprintf(stderr, + "%s: option requires an argument -- %c\n", + "progname", optopt); + return (BADCH); + } + else /* white space */ + optarg = nargv[optind]; + place = EMSG; + ++optind; + } + return (optopt); /* dump back option letter */ +} + diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/getopt.h b/3rdparty/openpgm-svn-r1085/pgm/examples/getopt.h new file mode 100644 index 0000000..f04387b --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/getopt.h @@ -0,0 +1,62 @@ +/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */ +/* $FreeBSD: src/include/getopt.h,v 1.1 2002/09/29 04:14:30 eric Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _GETOPT_H_ +#define _GETOPT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* These are global getopt variables */ +extern int opterr, /* if error message should be printed */ + optind, /* index into parent argv vector */ + optopt, /* character checked for validity */ + optreset; /* reset getopt */ +extern char* optarg; /* argument associated with option */ + +/* Original getopt */ +int getopt (int, char*const*, const char*); + +#ifdef __cplusplus +} +#endif + +#endif /* !_GETOPT_H_ */ + diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/pgmdump.c b/3rdparty/openpgm-svn-r1085/pgm/examples/pgmdump.c new file mode 100644 index 0000000..b91b804 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/pgmdump.c @@ -0,0 +1,279 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Dump PGM packets to the console. + * + * Copyright (c) 2006-2007 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef G_OS_UNIX +# include +# include +# include +# include +#endif + +#include + +/* PGM internals */ +#include + +/* example dependencies */ +#include +#include + + +/* globals */ + +static const char* g_network = "239.192.0.1"; + +static GIOChannel* g_io_channel = NULL; +static GMainLoop* g_loop = NULL; + + +static void on_signal (int); +static gboolean on_startup (gpointer); +static gboolean on_mark (gpointer); + +static gboolean on_io_data (GIOChannel*, GIOCondition, gpointer); + + +int +main ( + G_GNUC_UNUSED int argc, + G_GNUC_UNUSED char *argv[] + ) +{ + GError* err = NULL; + + setlocale (LC_ALL, ""); + + log_init (); + g_message ("pgmdump"); + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); + signal (SIGINT, on_signal); + signal (SIGTERM, on_signal); +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif + +/* delayed startup */ + g_message ("scheduling startup."); + g_timeout_add (0, (GSourceFunc)on_startup, NULL); + +/* dispatch loop */ + g_loop = g_main_loop_new (NULL, FALSE); + + g_message ("entering main event loop ... "); + g_main_loop_run (g_loop); + + g_message ("event loop terminated, cleaning up."); + +/* cleanup */ + g_main_loop_unref (g_loop); + g_loop = NULL; + + if (g_io_channel) { + g_message ("closing socket."); + g_io_channel_shutdown (g_io_channel, FALSE, &err); + g_io_channel = NULL; + } + + g_message ("finished."); + return EXIT_SUCCESS; +} + +static void +on_signal ( + G_GNUC_UNUSED int signum + ) +{ + puts ("on_signal"); + + g_main_loop_quit(g_loop); +} + +static +gboolean +on_startup ( + G_GNUC_UNUSED gpointer data + ) +{ + int e; + + g_message ("startup."); + +/* find PGM protocol id */ +// TODO: fix valgrind errors + int ipproto_pgm = IPPROTO_PGM; +#if HAVE_GETPROTOBYNAME_R + char b[1024]; + struct protoent protobuf, *proto; + e = getprotobyname_r ("pgm", &protobuf, b, sizeof(b), &proto); + if (e != -1 && proto != NULL) { + if (proto->p_proto != ipproto_pgm) { + g_message ("Setting PGM protocol number to %i from /etc/protocols.\n"); + ipproto_pgm = proto->p_proto; + } + } +#else + struct protoent *proto = getprotobyname ("pgm"); + if (proto != NULL) { + if (proto->p_proto != ipproto_pgm) { + g_message ("Setting PGM protocol number to %i from /etc/protocols.\n", proto +->p_proto); + ipproto_pgm = proto->p_proto; + } + } +#endif + +/* open socket for snooping */ + g_message ("opening raw socket."); + int sock = socket (PF_INET, SOCK_RAW, ipproto_pgm); + if (sock < 0) { + perror("on_startup() failed"); +#ifdef G_OS_UNIX + if (EPERM == errno && 0 != getuid()) { + g_message ("PGM protocol requires this program to run as superuser."); + } +#endif + g_main_loop_quit (g_loop); + return FALSE; + } + +#ifdef G_OS_UNIX +/* drop out of setuid 0 */ + if (0 == getuid ()) { + g_message ("dropping superuser privileges."); + setuid ((gid_t)65534); + setgid ((uid_t)65534); + } +#endif + + char _t = 1; + e = setsockopt (sock, IPPROTO_IP, IP_HDRINCL, &_t, sizeof(_t)); + if (e < 0) { + perror ("on_startup() failed"); + close (sock); + g_main_loop_quit (g_loop); + return FALSE; + } + +/* buffers */ + int buffer_size = 0; + socklen_t len = 0; + e = getsockopt (sock, SOL_SOCKET, SO_RCVBUF, (char*)&buffer_size, &len); + if (e == 0) { + g_message ("receive buffer set at %i bytes.\n", buffer_size); + } + e = getsockopt (sock, SOL_SOCKET, SO_SNDBUF, (char*)&buffer_size, &len); + if (e == 0) { + g_message ("send buffer set at %i bytes.\n", buffer_size); + } + +/* bind */ + struct sockaddr_in addr; + memset (&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + e = bind (sock, (struct sockaddr*)&addr, sizeof(addr)); + if (e < 0) { + perror ("on_startup() failed"); + close (sock); + g_main_loop_quit (g_loop); + return FALSE; + } + +/* multicast */ + struct ip_mreq mreq; + memset (&mreq, 0, sizeof(mreq)); + mreq.imr_interface.s_addr = htonl (INADDR_ANY); + g_message ("listening on interface %s.\n", inet_ntoa (mreq.imr_interface)); + mreq.imr_multiaddr.s_addr = inet_addr (g_network); + g_message ("subscription on multicast address %s.\n", inet_ntoa (mreq.imr_multiaddr)); + e = setsockopt (sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)); + if (e < 0) { + perror ("on_startup() failed"); + close (sock); + g_main_loop_quit (g_loop); + return FALSE; + } + +/* multicast loopback */ +/* multicast ttl */ + +/* add socket to event manager */ + g_io_channel = g_io_channel_unix_new (sock); + g_message ("socket opened with encoding %s.\n", g_io_channel_get_encoding (g_io_channel)); + + /* guint event = */ g_io_add_watch (g_io_channel, G_IO_IN | G_IO_PRI, on_io_data, NULL); + +/* period timer to indicate some form of life */ +// TODO: Gnome 2.14: replace with g_timeout_add_seconds() + g_timeout_add (10 * 1000, (GSourceFunc)on_mark, NULL); + + g_message ("startup complete."); + return FALSE; +} + +static gboolean +on_mark ( + G_GNUC_UNUSED gpointer data + ) +{ + g_message ("-- MARK --"); + return TRUE; +} + +static gboolean +on_io_data ( + GIOChannel* source, + G_GNUC_UNUSED GIOCondition condition, + G_GNUC_UNUSED gpointer data + ) +{ + char buffer[4096]; + + int fd = g_io_channel_unix_get_fd (source); + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + int len = recvfrom (fd, buffer, sizeof(buffer), MSG_DONTWAIT, (struct sockaddr*)&addr, &addr_len); + + g_message ("%i bytes received from %s.\n", len, inet_ntoa (addr.sin_addr)); + + if (!pgm_print_packet (buffer, len)) { + g_message ("invalid packet :("); + } + + fflush (stdout); + + return TRUE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/pgmping.cc b/3rdparty/openpgm-svn-r1085/pgm/examples/pgmping.cc new file mode 100644 index 0000000..38ac560 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/pgmping.cc @@ -0,0 +1,1059 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Simple send/reply ping tool using the PGM transport. + * + * With no arguments, one message is sent per second. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* c99 compatibility for c++ */ +#define __STDC_LIMIT_MACROS + +/* Must be first for Sun */ +#include "ping.pb.h" + +/* c99 compatibility for c++ */ +#define __STDC_FORMAT_MACROS +#define restrict + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#ifdef CONFIG_HAVE_EPOLL +# include +#endif +#include +#ifndef _WIN32 +# include +# include +# include +# include +# include +#endif +#include +#include +#ifdef CONFIG_WITH_HTTP +# include +#endif +#ifdef CONFIG_WITH_SNMP +# include +#endif + +/* PGM internal time keeper */ +typedef pgm_time_t (*pgm_time_update_func)(void); +extern pgm_time_update_func pgm_time_update_now; +extern "C" { + size_t pgm_pkt_offset (bool, sa_family_t); +} + +/* example dependencies */ +#include +#include +#include + + +using namespace std; + + +/* globals */ + +static int g_port = 0; +static const char* g_network = ""; +static int g_udp_encap_port = 0; + +static int g_odata_rate = 0; +static int g_odata_interval = 0; +static guint32 g_payload = 0; +static int g_max_tpdu = 1500; +static int g_max_rte = 16*1000*1000; +static int g_sqns = 200; + +static gboolean g_use_pgmcc = FALSE; +static sa_family_t g_pgmcc_family = 0; /* 0 = disabled */ + +static gboolean g_use_fec = FALSE; +static int g_rs_k = 8; +static int g_rs_n = 255; + +static enum { + PGMPING_MODE_SOURCE, + PGMPING_MODE_RECEIVER, + PGMPING_MODE_INITIATOR, + PGMPING_MODE_REFLECTOR +} g_mode = PGMPING_MODE_INITIATOR; + +static pgm_sock_t* g_sock = NULL; + +/* stats */ +static guint64 g_msg_sent = 0; +static guint64 g_msg_received = 0; +static pgm_time_t g_interval_start = 0; +static pgm_time_t g_latency_current = 0; +static guint64 g_latency_seqno = 0; +static guint64 g_last_seqno = 0; +static double g_latency_total = 0.0; +static double g_latency_square_total = 0.0; +static guint64 g_latency_count = 0; +static double g_latency_max = 0.0; +#ifdef INFINITY +static double g_latency_min = INFINITY; +#else +static double g_latency_min = INT64_MAX; +#endif +static double g_latency_running_average = 0.0; +static guint64 g_out_total = 0; +static guint64 g_in_total = 0; + +static GMainLoop* g_loop = NULL; +static GThread* g_sender_thread = NULL; +static GThread* g_receiver_thread = NULL; +static gboolean g_quit; +#ifdef G_OS_UNIX +static int g_quit_pipe[2]; +static void on_signal (int, gpointer); +#else +static HANDLE g_quit_event; +static BOOL on_console_ctrl (DWORD); +#endif + +static gboolean on_startup (gpointer); +static gboolean on_shutdown (gpointer); +static gboolean on_mark (gpointer); + +static void send_odata (void); +static int on_msgv (struct pgm_msgv_t*, size_t); + +static gpointer sender_thread (gpointer); +static gpointer receiver_thread (gpointer); + + +G_GNUC_NORETURN static void +usage (const char* bin) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -d : Terminate transport after duration.\n"); + fprintf (stderr, " -m : Number of message to send per second\n"); + fprintf (stderr, " -o : Send-only mode (default send & receive mode)\n"); + fprintf (stderr, " -l : Listen-only mode\n"); + fprintf (stderr, " -e : Relect mode\n"); + fprintf (stderr, " -r : Regulate to rate bytes per second\n"); + fprintf (stderr, " -c : Enable PGMCC\n"); + fprintf (stderr, " -f : Enable FEC with either proactive or ondemand parity\n"); + fprintf (stderr, " -K : Configure Reed-Solomon code (n, k)\n"); + fprintf (stderr, " -N \n"); + fprintf (stderr, " -H : Enable HTTP administrative interface\n"); + fprintf (stderr, " -S : Enable SNMP interface\n"); + exit (1); +} + +int +main ( + int argc, + char *argv[] + ) +{ + GError* err = NULL; + pgm_error_t* pgm_err = NULL; + gboolean enable_http = FALSE; + gboolean enable_snmpx = FALSE; + int timeout = 0; + + GOOGLE_PROTOBUF_VERIFY_VERSION; + + setlocale (LC_ALL, ""); + setenv ("PGM_TIMER", "GTOD", 1); + setenv ("PGM_SLEEP", "USLEEP", 1); + + log_init (); + g_message ("pgmping"); + + g_thread_init (NULL); + + if (!pgm_init (&pgm_err)) { + g_error ("Unable to start PGM engine: %s", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = g_get_prgname(); + int c; + while ((c = getopt (argc, argv, "s:n:p:m:old:r:cfeK:N:HSh")) != -1) + { + switch (c) { + case 'n': g_network = optarg; break; + case 's': g_port = atoi (optarg); break; + case 'p': g_udp_encap_port = atoi (optarg); break; + case 'r': g_max_rte = atoi (optarg); break; + + case 'c': g_use_pgmcc = TRUE; break; + + case 'f': g_use_fec = TRUE; break; + case 'K': g_rs_k = atoi (optarg); break; + case 'N': g_rs_n = atoi (optarg); break; + + case 'H': enable_http = TRUE; break; + case 'S': enable_snmpx = TRUE; break; + + case 'm': g_odata_rate = atoi (optarg); + g_odata_interval = (1000 * 1000) / g_odata_rate; break; + case 'd': timeout = 1000 * atoi (optarg); break; + + case 'o': g_mode = PGMPING_MODE_SOURCE; break; + case 'l': g_mode = PGMPING_MODE_RECEIVER; break; + case 'e': g_mode = PGMPING_MODE_REFLECTOR; break; + + case 'h': + case '?': usage (binary_name); + } + } + + if (g_use_fec && ( !g_rs_k || !g_rs_n )) { + g_error ("Invalid Reed-Solomon parameters."); + usage (binary_name); + } + +#ifdef CONFIG_WITH_HTTP + if (enable_http) { + if (!pgm_http_init (PGM_HTTP_DEFAULT_SERVER_PORT, &pgm_err)) { + g_error ("Unable to start HTTP interface: %s", pgm_err->message); + pgm_error_free (pgm_err); + pgm_shutdown (); + return EXIT_FAILURE; + } + } +#endif +#ifdef CONFIG_WITH_SNMP + if (enable_snmpx) { + if (!pgm_snmp_init (&pgm_err)) { + g_error ("Unable to start SNMP interface: %s", pgm_err->message); + pgm_error_free (pgm_err); +#ifdef CONFIG_WITH_HTTP + if (enable_http) + pgm_http_shutdown (); +#endif + pgm_shutdown (); + return EXIT_FAILURE; + } + } +#endif + + g_loop = g_main_loop_new (NULL, FALSE); + + g_quit = FALSE; + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif +#ifdef G_OS_UNIX + pipe (g_quit_pipe); + pgm_signal_install (SIGINT, on_signal, g_loop); + pgm_signal_install (SIGTERM, on_signal, g_loop); +#else + g_quit_event = CreateEvent (NULL, TRUE, FALSE, TEXT("QuitEvent")); + SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); +#endif /* !G_OS_UNIX */ + +/* delayed startup */ + g_message ("scheduling startup."); + g_timeout_add (0, (GSourceFunc)on_startup, g_loop); + + if (timeout) { + g_message ("scheduling shutdown."); + g_timeout_add (timeout, (GSourceFunc)on_shutdown, g_loop); + } + +/* dispatch loop */ + g_message ("entering main event loop ... "); + g_main_loop_run (g_loop); + + g_message ("event loop terminated, cleaning up."); + +/* cleanup */ + g_quit = TRUE; +#ifdef G_OS_UNIX + const char one = '1'; + write (g_quit_pipe[1], &one, sizeof(one)); + if (PGMPING_MODE_SOURCE == g_mode || PGMPING_MODE_INITIATOR == g_mode) + g_thread_join (g_sender_thread); + g_thread_join (g_receiver_thread); + close (g_quit_pipe[0]); + close (g_quit_pipe[1]); +#else + SetEvent (g_quit_event); + if (PGMPING_MODE_SOURCE == g_mode || PGMPING_MODE_INITIATOR == g_mode) + g_thread_join (g_sender_thread); + g_thread_join (g_receiver_thread); + CloseHandle (g_quit_event); +#endif + + g_main_loop_unref (g_loop); + g_loop = NULL; + + if (g_sock) { + g_message ("closing PGM socket."); + pgm_close (g_sock, TRUE); + g_sock = NULL; + } + +#ifdef CONFIG_WITH_HTTP + if (enable_http) + pgm_http_shutdown(); +#endif +#ifdef CONFIG_WITH_SNMP + if (enable_snmpx) + pgm_snmp_shutdown(); +#endif + + google::protobuf::ShutdownProtobufLibrary(); + + g_message ("PGM engine shutdown."); + pgm_shutdown (); + g_message ("finished."); + return EXIT_SUCCESS; +} + +#ifdef G_OS_UNIX +static +void +on_signal ( + int signum, + gpointer user_data + ) +{ + GMainLoop* loop = (GMainLoop*)user_data; + g_message ("on_signal (signum:%d user-data:%p)", + signum, user_data); + g_main_loop_quit (loop); +} +#else +static +BOOL +on_console_ctrl ( + DWORD dwCtrlType + ) +{ + g_message ("on_console_ctrl (dwCtrlType:%lu)", (unsigned long)dwCtrlType); + g_main_loop_quit (g_loop); + return TRUE; +} +#endif /* !G_OS_UNIX */ + +static +gboolean +on_shutdown ( + gpointer user_data + ) +{ + GMainLoop* loop = (GMainLoop*)user_data; + g_message ("on_shutdown (user-data:%p)", user_data); + g_main_loop_quit (loop); + return FALSE; +} + +static +gboolean +on_startup ( + gpointer user_data + ) +{ + GMainLoop* loop = (GMainLoop*)user_data; + struct pgm_addrinfo_t* res = NULL; + GError* err = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + + g_message ("startup."); + +/* parse network parameter into transport address structure */ + if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { + g_error ("parsing network parameter: %s", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + if (g_use_pgmcc) + g_pgmcc_family = sa_family; + + if (g_udp_encap_port) { + g_message ("create PGM/UDP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (g_sock, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + pgm_setsockopt (g_sock, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + } else { + g_message ("create PGM/IP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + { + const int no_router_assist = 0; + pgm_setsockopt (g_sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + } + + pgm_drop_superuser(); + +/* set PGM parameters */ + if (PGMPING_MODE_SOURCE == g_mode || + PGMPING_MODE_INITIATOR == g_mode || + PGMPING_MODE_REFLECTOR == g_mode) + { + const int send_only = PGMPING_MODE_SOURCE == g_mode ? 1 : 0, + ambient_spm = pgm_secs (30), + heartbeat_spm[] = { pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (1300), + pgm_secs (7), + pgm_secs (16), + pgm_secs (25), + pgm_secs (30) }; + + pgm_setsockopt (g_sock, PGM_SEND_ONLY, &send_only, sizeof(send_only)); + pgm_setsockopt (g_sock, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); + pgm_setsockopt (g_sock, PGM_TXW_SQNS, &g_sqns, sizeof(g_sqns)); + pgm_setsockopt (g_sock, PGM_TXW_MAX_RTE, &g_max_rte, sizeof(g_max_rte)); + pgm_setsockopt (g_sock, PGM_AMBIENT_SPM, &ambient_spm, sizeof(ambient_spm)); + pgm_setsockopt (g_sock, PGM_HEARTBEAT_SPM, &heartbeat_spm, sizeof(heartbeat_spm)); + } + if (PGMPING_MODE_RECEIVER == g_mode || + PGMPING_MODE_INITIATOR == g_mode || + PGMPING_MODE_REFLECTOR == g_mode) + { + const int recv_only = PGMPING_MODE_RECEIVER == g_mode ? 1 : 0, + passive = 0, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_msecs (200), //pgm_secs (2), + nak_rdata_ivl = pgm_msecs (200), //pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + pgm_setsockopt (g_sock, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + pgm_setsockopt (g_sock, PGM_PASSIVE, &passive, sizeof(passive)); + pgm_setsockopt (g_sock, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); + pgm_setsockopt (g_sock, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); + pgm_setsockopt (g_sock, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + pgm_setsockopt (g_sock, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + pgm_setsockopt (g_sock, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + pgm_setsockopt (g_sock, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + } + +#ifdef I_UNDERSTAND_PGMCC_AND_FEC_ARE_NOT_SUPPORTED +/* PGMCC congestion control */ + if (g_use_pgmcc) { + struct pgm_pgmccinfo_t pgmccinfo; + pgmccinfo.ack_bo_ivl = pgm_msecs (50); + pgmccinfo.ack_c = 75; + pgmccinfo.ack_c_p = 500; + pgm_setsockopt (g_sock, PGM_USE_PGMCC, &pgmccinfo, sizeof(pgmccinfo)); + } + +/* Reed Solomon forward error correction */ + if (g_use_fec) { + struct pgm_fecinfo_t fecinfo; + fecinfo.block_size = g_rs_n; + fecinfo.proactive_packets = 0; + fecinfo.group_size = g_rs_k; + fecinfo.ondemand_parity_enabled = TRUE; + fecinfo.var_pktlen_enabled = TRUE; + pgm_setsockopt (g_sock, PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); + } +#endif + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + g_error ("creating GSI: %s", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (g_sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + g_error ("binding PGM socket: %s", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (g_sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (g_sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + { + const int nonblocking = 1, + multicast_loop = 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (g_sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (g_sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (g_sock, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (g_sock, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); + } + + if (!pgm_connect (g_sock, &pgm_err)) { + g_error ("connecting PGM socket: %s", pgm_err->message); + goto err_abort; + } + +/* period timer to indicate some form of life */ +// TODO: Gnome 2.14: replace with g_timeout_add_seconds() + g_timeout_add (2 * 1000, (GSourceFunc)on_mark, NULL); + + if (PGMPING_MODE_SOURCE == g_mode || PGMPING_MODE_INITIATOR == g_mode) + { + g_sender_thread = g_thread_create_full (sender_thread, + g_sock, + 0, + TRUE, + TRUE, + G_THREAD_PRIORITY_NORMAL, + &err); + if (!g_sender_thread) { + g_critical ("g_thread_create_full failed errno %i: \"%s\"", err->code, err->message); + goto err_abort; + } + } + + { + g_receiver_thread = g_thread_create_full (receiver_thread, + g_sock, + 0, + TRUE, + TRUE, + G_THREAD_PRIORITY_HIGH, + &err); + if (!g_receiver_thread) { + g_critical ("g_thread_create_full failed errno %i: \"%s\"", err->code, err->message); + goto err_abort; + } + } + + g_message ("startup complete."); + return FALSE; + +err_abort: + if (NULL != g_sock) { + pgm_close (g_sock, FALSE); + g_sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +static +gpointer +sender_thread ( + gpointer user_data + ) +{ + pgm_sock_t* tx_sock = (pgm_sock_t*)user_data; + example::Ping ping; + string subject("PING.PGM.TEST."); + char hostname[NI_MAXHOST + 1]; + const long payload_len = 1000; + char payload[payload_len]; + gpointer buffer = NULL; + guint64 latency, now, last; + +#ifdef CONFIG_HAVE_EPOLL + const long ev_len = 1; + struct epoll_event events[ev_len]; + + int efd_again = epoll_create (IP_MAX_MEMBERSHIPS); + if (efd_again < 0) { + g_error ("epoll_create failed errno %i: \"%s\"", errno, strerror(errno)); + g_main_loop_quit (g_loop); + return NULL; + } +/* Add write event to epoll domain in order to re-enable as required by return + * value. We use one-shot flag to disable ASAP, as we don't want such events + * until triggered. + */ + if (pgm_epoll_ctl (tx_sock, efd_again, EPOLL_CTL_ADD, EPOLLOUT | EPOLLONESHOT) < 0) { + g_error ("pgm_epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); + g_main_loop_quit (g_loop); + return NULL; + } + struct epoll_event event; + memset (&event, 0, sizeof(event)); + event.events = EPOLLIN; + event.data.fd = g_quit_pipe[0]; + if (epoll_ctl (efd_again, EPOLL_CTL_ADD, g_quit_pipe[0], &event) < 0) { + g_error ("epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); + g_main_loop_quit (g_loop); + return NULL; + } +#elif defined(CONFIG_HAVE_POLL) + int n_fds = 2; + struct pollfd fds[ 2 + 1 ]; +#endif /* !CONFIG_HAVE_EPOLL */ + + gethostname (hostname, sizeof(hostname)); + subject.append(hostname); + memset (payload, 0, sizeof(payload)); + + ping.mutable_subscription_header()->set_subject (subject); + ping.mutable_market_data_header()->set_msg_type (example::MarketDataHeader::MSG_VERIFY); + ping.mutable_market_data_header()->set_rec_type (example::MarketDataHeader::PING); + ping.mutable_market_data_header()->set_rec_status (example::MarketDataHeader::STATUS_OK); + ping.set_time (last); + + last = now = pgm_time_update_now(); + do { + if (g_msg_sent && g_latency_seqno + 1 == g_msg_sent) + latency = g_latency_current; + else + latency = g_odata_interval; + + ping.set_seqno (g_msg_sent); + ping.set_latency (latency); + ping.set_payload (payload, sizeof(payload)); + + const size_t header_size = pgm_pkt_offset (FALSE, g_pgmcc_family); + const size_t apdu_size = ping.ByteSize(); + struct pgm_sk_buff_t* skb = pgm_alloc_skb (g_max_tpdu); + pgm_skb_reserve (skb, header_size); + pgm_skb_put (skb, apdu_size); + +/* wait on packet rate limit */ + if ((last + g_odata_interval) > now) { +#ifndef _WIN32 + const unsigned int usec = g_odata_interval - (now - last); + usleep (usec); +#else + const DWORD msec = usecs_to_msecs (g_odata_interval - (now - last)); + Sleep (msec); +#endif + now = pgm_time_update_now(); + } + last += g_odata_interval; + ping.set_time (now); + ping.SerializeToArray (skb->data, skb->len); + + struct timeval tv; + int timeout; + size_t bytes_written; + int status; +again: + status = pgm_send_skbv (tx_sock, &skb, 1, TRUE, &bytes_written); + switch (status) { +/* rate control */ + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (tx_sock, PGM_RATE_REMAIN, &tv, &optlen); + timeout = (tv.tv_sec * 1000) + ((tv.tv_usec + 500) / 1000); +/* busy wait under 2ms */ + if (timeout < 2) timeout = 0; +#ifdef CONFIG_HAVE_EPOLL + const int ready = epoll_wait (efd_again, events, G_N_ELEMENTS(events), timeout /* ms */); +#elif defined(CONFIG_HAVE_POLL) + memset (fds, 0, sizeof(fds)); + fds[0].fd = g_quit_pipe[0]; + fds[0].events = POLLIN; + pgm_poll_info (tx_sock, &fds[1], &n_fds, POLLIN); + poll (fds, 1 + n_fds, timeout /* ms */); +#endif /* !CONFIG_HAVE_EPOLL */ + if (G_UNLIKELY(g_quit)) + break; + goto again; + } +/* congestion control */ + case PGM_IO_STATUS_CONGESTION: +/* kernel feedback */ + case PGM_IO_STATUS_WOULD_BLOCK: + { +#ifdef CONFIG_HAVE_EPOLL +/* re-enable write event for one-shot */ + if (pgm_epoll_ctl (tx_sock, efd_again, EPOLL_CTL_MOD, EPOLLOUT | EPOLLONESHOT) < 0) + { + g_error ("pgm_epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); + g_main_loop_quit (g_loop); + return NULL; + } + const int ready = epoll_wait (efd_again, events, G_N_ELEMENTS(events), -1 /* ms */); + if (G_UNLIKELY(g_quit)) + break; +#elif defined(CONFIG_HAVE_POLL) + memset (fds, 0, sizeof(fds)); + fds[0].fd = g_quit_pipe[0]; + fds[0].events = POLLIN; + pgm_poll_info (g_sock, &fds[1], &n_fds, POLLOUT); + poll (fds, 1 + n_fds, -1 /* ms */); +#endif /* !CONFIG_HAVE_EPOLL */ + goto again; + } +/* successful delivery */ + case PGM_IO_STATUS_NORMAL: +// g_message ("sent payload: %s", ping.DebugString().c_str()); +// g_message ("sent %u bytes", (unsigned)bytes_written); + break; + default: + g_warning ("pgm_send_skbv failed, status:%i", status); + g_main_loop_quit (g_loop); + return NULL; + } + g_out_total += bytes_written; + g_msg_sent++; + } while (!g_quit); + +#ifdef CONFIG_HAVE_EPOLL + close (efd_again); +#endif + return NULL; +} + +static +gpointer +receiver_thread ( + gpointer data + ) +{ + pgm_sock_t* rx_sock = (pgm_sock_t*)data; + const long iov_len = 20; + struct pgm_msgv_t msgv[iov_len]; + pgm_time_t lost_tstamp = 0; + pgm_tsi_t lost_tsi; + guint32 lost_count = 0; + +#ifdef CONFIG_HAVE_EPOLL + const long ev_len = 1; + struct epoll_event events[ev_len]; + + int efd = epoll_create (IP_MAX_MEMBERSHIPS); + if (efd < 0) { + g_error ("epoll_create failed errno %i: \"%s\"", errno, strerror(errno)); + g_main_loop_quit (g_loop); + return NULL; + } + if (pgm_epoll_ctl (rx_sock, efd, EPOLL_CTL_ADD, EPOLLIN) < 0) { + g_error ("pgm_epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); + g_main_loop_quit (g_loop); + return NULL; + } + struct epoll_event event; + memset (&event, 0, sizeof(event)); + event.events = EPOLLIN; + if (epoll_ctl (efd, EPOLL_CTL_ADD, g_quit_pipe[0], &event) < 0) { + g_error ("epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); + g_main_loop_quit (g_loop); + return NULL; + } +#elif defined(CONFIG_HAVE_POLL) + int n_fds = 3; + struct pollfd fds[ 3 + 1 ]; +#endif /* !CONFIG_HAVE_EPOLL */ + + memset (&lost_tsi, 0, sizeof(lost_tsi)); + + do { + struct timeval tv; + int timeout; + size_t len; + pgm_error_t* pgm_err = NULL; + const int status = pgm_recvmsgv (rx_sock, + msgv, + G_N_ELEMENTS(msgv), + MSG_ERRQUEUE, + &len, + &pgm_err); + if (lost_count) { + pgm_time_t elapsed = pgm_time_update_now() - lost_tstamp; + if (elapsed >= pgm_secs(1)) { + g_warning ("pgm data lost %" G_GUINT32_FORMAT " packets detected from %s", + lost_count, pgm_tsi_print (&lost_tsi)); + lost_count = 0; + } + } + + switch (status) { + case PGM_IO_STATUS_NORMAL: +// g_message ("recv %u bytes", (unsigned)len); + on_msgv (msgv, len); + break; + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (g_sock, PGM_TIME_REMAIN, &tv, &optlen); + } + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (g_sock, PGM_RATE_REMAIN, &tv, &optlen); + } +/* fall through */ + case PGM_IO_STATUS_WOULD_BLOCK: +block: + timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); +/* busy wait under 2ms */ + if (timeout > 0 && timeout < 2) timeout = 0; +#ifdef CONFIG_HAVE_EPOLL + epoll_wait (efd, events, G_N_ELEMENTS(events), timeout /* ms */); +#elif defined(CONFIG_HAVE_POLL) + memset (fds, 0, sizeof(fds)); + fds[0].fd = g_quit_pipe[0]; + fds[0].events = POLLIN; + pgm_transport_poll_info (g_transport, &fds[1], &n_fds, POLLIN); + poll (fds, 1 + n_fds, timeout /* ms */); +#endif /* !CONFIG_HAVE_EPOLL */ + break; + case PGM_IO_STATUS_RESET: + { + struct pgm_sk_buff_t* skb = msgv[0].msgv_skb[0]; + lost_tstamp = skb->tstamp; + if (pgm_tsi_equal (&skb->tsi, &lost_tsi)) + lost_count += skb->sequence; + else { + lost_count = skb->sequence; + memcpy (&lost_tsi, &skb->tsi, sizeof(pgm_tsi_t)); + } + pgm_free_skb (skb); + break; + } + default: + if (pgm_err) { + g_warning ("%s", pgm_err->message); + pgm_error_free (pgm_err); + pgm_err = NULL; + } + break; + } + } while (!g_quit); + +#ifdef CONFIG_HAVE_EPOLL + close (efd); +#endif + return NULL; +} + +static +int +on_msgv ( + struct pgm_msgv_t* msgv, /* an array of msgvs */ + size_t len + ) +{ + example::Ping ping; + guint i = 0; + static pgm_time_t last_time = pgm_time_update_now(); + + while (len) + { + const struct pgm_sk_buff_t* pskb = msgv[i].msgv_skb[0]; + gsize apdu_len = 0; + for (unsigned j = 0; j < msgv[i].msgv_len; j++) + apdu_len += msgv[i].msgv_skb[j]->len; + + if (PGMPING_MODE_REFLECTOR == g_mode) + { + int status; +again: + status = pgm_send (g_sock, pskb->data, pskb->len, NULL); + switch (status) { + case PGM_IO_STATUS_RATE_LIMITED: + case PGM_IO_STATUS_CONGESTION: + case PGM_IO_STATUS_WOULD_BLOCK: +/* busy wait always as reflector */ + goto again; + + case PGM_IO_STATUS_NORMAL: + break; + + default: + g_warning ("pgm_send_skbv failed"); + g_main_loop_quit (g_loop); + return 0; + } + goto next_msg; + } + +/* only parse first fragment of each apdu */ + if (!ping.ParseFromArray (pskb->data, pskb->len)) + goto next_msg; +// g_message ("payload: %s", ping.DebugString().c_str()); + + { + const pgm_time_t send_time = ping.time(); + const pgm_time_t recv_time = pskb->tstamp; + const guint64 seqno = ping.seqno(); + const guint64 latency = ping.latency(); + + if (seqno < g_latency_seqno) { + g_message ("seqno replay?"); + goto next_msg; + } + + g_in_total += pskb->len; + g_msg_received++; + +/* handle ping */ + const pgm_time_t now = pgm_time_update_now(); + if (send_time > now) + g_warning ("send time %" PGM_TIME_FORMAT " newer than now %" PGM_TIME_FORMAT, + send_time, now); + if (recv_time > now) + g_warning ("recv time %" PGM_TIME_FORMAT " newer than now %" PGM_TIME_FORMAT, + recv_time, now); + if (send_time >= recv_time){ + g_message ("timer mismatch, send time = recv time + %.3f ms (last time + %.3f ms)", + pgm_to_msecsf(send_time - recv_time), + pgm_to_msecsf(last_time - send_time)); + goto next_msg; + } + g_latency_current = pgm_to_secs(recv_time - send_time); + g_latency_seqno = seqno; + + const double elapsed = pgm_to_usecsf (recv_time - send_time); + g_latency_total += elapsed; + g_latency_square_total += elapsed * elapsed; + + if (elapsed > g_latency_max) + g_latency_max = elapsed; + if (elapsed < g_latency_min) + g_latency_min = elapsed; + + g_latency_running_average += elapsed; + g_latency_count++; + last_time = recv_time; + } + +/* move onto next apdu */ +next_msg: + i++; + len -= apdu_len; + } + + return 0; +} + +/* idle log notification + */ + +static +gboolean +on_mark ( + G_GNUC_UNUSED gpointer data + ) +{ + const pgm_time_t now = pgm_time_update_now (); + const double interval = pgm_to_secsf(now - g_interval_start); + g_interval_start = now; + +/* receiving a ping */ + if (g_latency_count) + { + const double average = g_latency_total / g_latency_count; + const double variance = g_latency_square_total / g_latency_count + - average * average; + const double standard_deviation = sqrt (variance); + + if (g_latency_count < 10) + { + if (average < 1000.0) + g_message ("seqno=%" G_GUINT64_FORMAT " time=%.01f us", + g_latency_seqno, average); + else + g_message ("seqno=%" G_GUINT64_FORMAT " time=%.01f ms", + g_latency_seqno, average / 1000); + } + else + { + double seq_rate = (g_latency_seqno - g_last_seqno) / interval; + double out_rate = g_out_total * 8.0 / 1000000.0 / interval; + double in_rate = g_in_total * 8.0 / 1000000.0 / interval; + if (g_latency_min < 1000.0) + g_message ("s=%.01f avg=%.01f min=%.01f max=%.01f stddev=%0.1f us o=%.2f i=%.2f mbit", + seq_rate, average, g_latency_min, g_latency_max, standard_deviation, out_rate, in_rate); + else + g_message ("s=%.01f avg=%.01f min=%.01f max=%.01f stddev=%0.1f ms o=%.2f i=%.2f mbit", + seq_rate, average / 1000, g_latency_min / 1000, g_latency_max / 1000, standard_deviation / 1000, out_rate, in_rate); + } + +/* reset interval counters */ + g_latency_total = 0.0; + g_latency_square_total = 0.0; + g_latency_count = 0; + g_last_seqno = g_latency_seqno; +#ifdef INFINITY + g_latency_min = INFINITY; +#else + g_latency_min = INT64_MAX; +#endif + g_latency_max = 0.0; + g_out_total = 0; + g_in_total = 0; + } + + return TRUE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/pgmrecv.c b/3rdparty/openpgm-svn-r1085/pgm/examples/pgmrecv.c new file mode 100644 index 0000000..035ccc4 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/pgmrecv.c @@ -0,0 +1,649 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Simple receiver using the PGM transport, based on enonblocksyncrecvmsgv :/ + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HAVE_EPOLL +# include +#endif +#include +#include +#ifdef G_OS_UNIX +# include +# include +# include +# include +# include +# include +# include +#else +# include "getopt.h" +#endif +#include +#ifdef CONFIG_WITH_HTTP +# include +#endif +#ifdef CONFIG_WITH_SNMP +# include +#endif + +/* example dependencies */ +#include +#include +#include + + +/* globals */ + +static int g_port = 0; +static const char* g_network = ""; +static const char* g_source = ""; +static gboolean g_multicast_loop = FALSE; +static int g_udp_encap_port = 0; + +static int g_max_tpdu = 1500; +static int g_sqns = 100; + +static pgm_sock_t* g_sock = NULL; +static GThread* g_thread = NULL; +static GMainLoop* g_loop = NULL; +static gboolean g_quit; +#ifdef G_OS_UNIX +static int g_quit_pipe[2]; +static void on_signal (int, gpointer); +#else +static HANDLE g_quit_event; +static BOOL on_console_ctrl (DWORD); +#endif + +static gboolean on_startup (gpointer); +static gboolean on_mark (gpointer); + +static gpointer receiver_thread (gpointer); +static int on_msgv (struct pgm_msgv_t*, size_t); + + +G_GNUC_NORETURN static void +usage ( + const char* bin + ) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -a : Source unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); +#ifdef CONFIG_WITH_HTTP + fprintf (stderr, " -H : Enable HTTP administrative interface\n"); +#endif +#ifdef CONFIG_WITH_SNMP + fprintf (stderr, " -S : Enable SNMP interface\n"); +#endif + fprintf (stderr, " -i : List available interfaces\n"); + exit (1); +} + +int +main ( + int argc, + char* argv[] + ) +{ + int e; + pgm_error_t* pgm_err = NULL; +#ifdef CONFIG_WITH_HTTP + gboolean enable_http = FALSE; +#endif +#ifdef CONFIG_WITH_SNMP + gboolean enable_snmpx = FALSE; +#endif + + setlocale (LC_ALL, ""); + +/* pre-initialise PGM messages module to add hook for GLib logging */ + pgm_messages_init(); + log_init (); + g_message ("pgmrecv"); + + if (!pgm_init (&pgm_err)) { + g_error ("Unable to start PGM engine: %s", (pgm_err && pgm_err->message) ? pgm_err->message : "(null)"); + pgm_error_free (pgm_err); + pgm_messages_shutdown(); + return EXIT_FAILURE; + } + + g_thread_init (NULL); + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "a:s:n:p:lih" +#ifdef CONFIG_WITH_HTTP + "H" +#endif +#ifdef CONFIG_WITH_SNMP + "S" +#endif + )) != -1) + { + switch (c) { + case 'n': g_network = optarg; break; + case 'a': g_source = optarg; break; + case 's': g_port = atoi (optarg); break; + case 'p': g_udp_encap_port = atoi (optarg); break; + + case 'l': g_multicast_loop = TRUE; break; +#ifdef CONFIG_WITH_HTTP + case 'H': enable_http = TRUE; break; +#endif +#ifdef CONFIG_WITH_SNMP + case 'S': enable_snmpx = TRUE; break; +#endif + + case 'i': + pgm_if_print_all(); + pgm_messages_shutdown(); + return EXIT_SUCCESS; + + case 'h': + case '?': + pgm_messages_shutdown(); + usage (binary_name); + } + } + +#ifdef CONFIG_WITH_HTTP + if (enable_http) { + if (!pgm_http_init (PGM_HTTP_DEFAULT_SERVER_PORT, &pgm_err)) { + g_error ("Unable to start HTTP interface: %s", pgm_err->message); + pgm_error_free (pgm_err); + pgm_shutdown(); + pgm_messages_shutdown(); + return EXIT_FAILURE; + } + } +#endif +#ifdef CONFIG_WITH_SNMP + if (enable_snmpx) { + if (!pgm_snmp_init (&pgm_err)) { + g_error ("Unable to start SNMP interface: %s", pgm_err->message); + pgm_error_free (pgm_err); +#ifdef CONFIG_WITH_HTTP + if (enable_http) + pgm_http_shutdown (); +#endif + pgm_shutdown (); + pgm_messages_shutdown(); + return EXIT_FAILURE; + } + } +#endif + + g_loop = g_main_loop_new (NULL, FALSE); + + g_quit = FALSE; + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif +#ifdef G_OS_UNIX + e = pipe (g_quit_pipe); + g_assert (0 == e); + pgm_signal_install (SIGINT, on_signal, g_loop); + pgm_signal_install (SIGTERM, on_signal, g_loop); +#else + g_quit_event = CreateEvent (NULL, TRUE, FALSE, TEXT("QuitEvent")); + SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); +#endif + +/* delayed startup */ + g_message ("scheduling startup."); + g_timeout_add (0, (GSourceFunc)on_startup, NULL); + +/* dispatch loop */ + g_message ("entering main event loop ... "); + g_main_loop_run (g_loop); + + g_message ("event loop terminated, cleaning up."); + +/* cleanup */ + g_quit = TRUE; +#ifdef G_OS_UNIX + const char one = '1'; + const size_t writelen = write (g_quit_pipe[1], &one, sizeof(one)); + g_assert (sizeof(one) == writelen); + g_thread_join (g_thread); + close (g_quit_pipe[0]); + close (g_quit_pipe[1]); +#else + SetEvent (g_quit_event); + g_thread_join (g_thread); + CloseHandle (g_quit_event); +#endif + + g_main_loop_unref (g_loop); + g_loop = NULL; + + if (g_sock) { + g_message ("closing PGM socket."); + + pgm_close (g_sock, TRUE); + g_sock = NULL; + } + +#ifdef CONFIG_WITH_HTTP + if (enable_http) + pgm_http_shutdown(); +#endif +#ifdef CONFIG_WITH_SNMP + if (enable_snmpx) + pgm_snmp_shutdown(); +#endif + + g_message ("PGM engine shutdown."); + pgm_shutdown(); + g_message ("finished."); + pgm_messages_shutdown(); + return EXIT_SUCCESS; +} + +#ifdef G_OS_UNIX +static +void +on_signal ( + int signum, + gpointer user_data + ) +{ + GMainLoop* loop = (GMainLoop*)user_data; + g_message ("on_signal (signum:%d user_data:%p)", + signum, user_data); + g_main_loop_quit (loop); +} +#else +static +BOOL +on_console_ctrl ( + DWORD dwCtrlType + ) +{ + g_message ("on_console_ctrl (dwCtrlType:%lu)", (unsigned long)dwCtrlType); + g_main_loop_quit (g_loop); + return TRUE; +} +#endif /* !G_OS_UNIX */ + +static +gboolean +on_startup ( + G_GNUC_UNUSED gpointer data + ) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + + g_message ("startup."); + +/* parse network parameter into transport address structure */ + if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { + g_error ("parsing network parameter: %s", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + if (g_udp_encap_port) { + g_message ("create PGM/UDP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (g_sock, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + pgm_setsockopt (g_sock, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + } else { + g_message ("create PGM/IP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (g_sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int recv_only = 1, + passive = 0, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_secs (2), + nak_rdata_ivl = pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + pgm_setsockopt (g_sock, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + pgm_setsockopt (g_sock, PGM_PASSIVE, &passive, sizeof(passive)); + pgm_setsockopt (g_sock, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); + pgm_setsockopt (g_sock, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); + pgm_setsockopt (g_sock, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + pgm_setsockopt (g_sock, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + pgm_setsockopt (g_sock, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + pgm_setsockopt (g_sock, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + g_error ("creating GSI: %s", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (g_sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + g_error ("binding PGM socket: %s", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (g_sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (g_sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int nonblocking = 1, + multicast_loop = g_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (g_sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (g_sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (g_sock, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (g_sock, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); + + if (!pgm_connect (g_sock, &pgm_err)) { + g_error ("connecting PGM socket: %s", pgm_err->message); + goto err_abort; + } + +/* create receiver thread */ + GError* glib_err = NULL; + g_thread = g_thread_create_full (receiver_thread, + g_sock, + 0, + TRUE, + TRUE, + G_THREAD_PRIORITY_HIGH, + &glib_err); + if (!g_thread) { + g_error ("g_thread_create_full failed errno %i: \"%s\"", glib_err->code, glib_err->message); + g_error_free (glib_err); + goto err_abort; + } + +/* period timer to indicate some form of life */ +// TODO: Gnome 2.14: replace with g_timeout_add_seconds() + g_timeout_add(10 * 1000, (GSourceFunc)on_mark, NULL); + + g_message ("startup complete."); + return FALSE; + +err_abort: + if (NULL != g_sock) { + pgm_close (g_sock, FALSE); + g_sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + g_main_loop_quit (g_loop); + return FALSE; +} + +/* idle log notification + */ + +static +gboolean +on_mark ( + G_GNUC_UNUSED gpointer data + ) +{ + g_message ("-- MARK --"); + return TRUE; +} + +static +gpointer +receiver_thread ( + gpointer data + ) +{ + pgm_sock_t* rx_sock = (pgm_sock_t*)data; + const long iov_len = 20; + const long ev_len = 1; + struct pgm_msgv_t msgv[iov_len]; + +#ifdef CONFIG_HAVE_EPOLL + struct epoll_event events[ev_len]; /* wait for maximum 1 event */ + int timeout; + const int efd = epoll_create (IP_MAX_MEMBERSHIPS); + if (efd < 0) { + g_error ("epoll_create failed errno %i: \"%s\"", errno, strerror(errno)); + g_main_loop_quit (g_loop); + return NULL; + } + + if (pgm_epoll_ctl (rx_sock, efd, EPOLL_CTL_ADD, EPOLLIN) < 0) + { + g_error ("pgm_epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); + g_main_loop_quit (g_loop); + return NULL; + } + struct epoll_event event; + event.events = EPOLLIN; + event.data.fd = g_quit_pipe[0]; + if (epoll_ctl (efd, EPOLL_CTL_ADD, g_quit_pipe[0], &event) < 0) + { + g_error ("epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); + g_main_loop_quit (g_loop); + return NULL; + } +#elif defined(CONFIG_HAVE_POLL) + int timeout; + int n_fds = 2; + struct pollfd fds[ 1 + n_fds ]; +#elif defined(G_OS_UNIX) /* HAVE_SELECT */ + int n_fds; + fd_set readfds; +#else /* G_OS_WIN32 */ + int n_handles = 3; +# if (__STDC_VERSION__ >= 199901L) + HANDLE waitHandles[n_handles]; +# else + HANDLE* waitHandles = (HANDLE*)g_malloc (n_handles * sizeof(HANDLE));; +# endif + DWORD timeout, dwEvents; + WSAEVENT recvEvent, pendingEvent; + + recvEvent = WSACreateEvent (); + WSAEventSelect (pgm_transport_get_recv_fd (g_transport), recvEvent, FD_READ); + pendingEvent = WSACreateEvent (); + WSAEventSelect (pgm_transport_get_pending_fd (g_transport), pendingEvent, FD_READ); + + waitHandles[0] = g_quit_event; + waitHandles[1] = recvEvent; + waitHandles[2] = pendingEvent; +#endif /* !CONFIG_HAVE_EPOLL */ + + do { + struct timeval tv; + size_t len; + pgm_error_t* pgm_err = NULL; + const int status = pgm_recvmsgv (rx_sock, + msgv, + G_N_ELEMENTS(msgv), + 0, + &len, + &pgm_err); + switch (status) { + case PGM_IO_STATUS_NORMAL: + on_msgv (msgv, len); + break; + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (rx_sock, PGM_TIME_REMAIN, &tv, &optlen); + } + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (rx_sock, PGM_RATE_REMAIN, &tv, &optlen); + } +/* fall through */ + case PGM_IO_STATUS_WOULD_BLOCK: +block: +#ifdef CONFIG_HAVE_EPOLL + timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + epoll_wait (efd, events, G_N_ELEMENTS(events), timeout /* ms */); +#elif defined(CONFIG_HAVE_POLL) + timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + memset (fds, 0, sizeof(fds)); + fds[0].fd = g_quit_pipe[0]; + fds[0].events = POLLIN; + pgm_poll_info (rx_sock, &fds[1], &n_fds, POLLIN); + poll (fds, 1 + n_fds, timeout /* ms */); +#elif defined(G_OS_UNIX) /* HAVE_SELECT */ + FD_ZERO(&readfds); + FD_SET(g_quit_pipe[0], &readfds); + n_fds = g_quit_pipe[0] + 1; + pgm_select_info (rx_sock, &readfds, NULL, &n_fds); + select (n_fds, &readfds, NULL, NULL, PGM_IO_STATUS_RATE_LIMITED == status ? &tv : NULL); +#else /* G_OS_WIN32 */ + timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? INFINITE : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + dwEvents = WaitForMultipleObjects (n_handles, waitHandles, FALSE, timeout); + switch (dwEvents) { + case WAIT_OBJECT_0+1: WSAResetEvent (recvEvent); break; + case WAIT_OBJECT_0+2: WSAResetEvent (pendingEvent); break; + default: break; + } +#endif /* !CONFIG_HAVE_EPOLL */ + break; + + default: + if (pgm_err) { + g_warning ("%s", pgm_err->message); + pgm_error_free (pgm_err); + pgm_err = NULL; + } + if (PGM_IO_STATUS_ERROR == status) + break; + } + } while (!g_quit); + +#ifdef CONFIG_HAVE_EPOLL + close (efd); +#elif defined(G_OS_WIN32) + WSACloseEvent (recvEvent); + WSACloseEvent (pendingEvent); +# if (__STDC_VERSION__ < 199901L) + g_free (waitHandles); +# endif +#endif + return NULL; +} + +static +int +on_msgv ( + struct pgm_msgv_t* msgv, /* an array of msgvs */ + size_t len + ) +{ + g_message ("(%u bytes)", + (unsigned)len); + + guint i = 0; +/* for each apdu */ + do { + const struct pgm_sk_buff_t* pskb = msgv[i].msgv_skb[0]; + gsize apdu_len = 0; + for (unsigned j = 0; j < msgv[i].msgv_len; j++) + apdu_len += msgv[i].msgv_skb[j]->len; +/* truncate to first fragment to make GLib printing happy */ + char buf[2048], tsi[PGM_TSISTRLEN]; + const gsize buflen = MIN(sizeof(buf) - 1, pskb->len); + strncpy (buf, (const char*)pskb->data, buflen); + buf[buflen] = '\0'; + pgm_tsi_print_r (&pskb->tsi, tsi, sizeof(tsi)); + if (msgv[i].msgv_len > 1) + g_message ("\t%u: \"%s\" ... (%" G_GSIZE_FORMAT " bytes from %s)", + i, buf, apdu_len, tsi); + else + g_message ("\t%u: \"%s\" (%" G_GSIZE_FORMAT " bytes from %s)", + i, buf, apdu_len, tsi); + i++; + len -= apdu_len; + } while (len); + + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/pgmsend.c b/3rdparty/openpgm-svn-r1085/pgm/examples/pgmsend.c new file mode 100644 index 0000000..d8d3539 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/pgmsend.c @@ -0,0 +1,305 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Simple sender using the PGM transport. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef G_OS_UNIX +# include +# include +# include +# include +# include +# include +#else +# include "getopt.h" +#endif +#include + +/* example dependencies */ +#include +#include + + +/* typedefs */ + +/* globals */ + +static int g_port = 0; +static const char* g_network = ""; +static gboolean g_multicast_loop = FALSE; +static int g_udp_encap_port = 0; + +static int g_max_tpdu = 1500; +static int g_max_rte = 400*1000; +static int g_sqns = 100; + +static gboolean g_fec = FALSE; +static int g_k = 8; +static int g_n = 255; + +static pgm_sock_t* g_sock = NULL; + +static gboolean create_pgm_socket (void); + + +G_GNUC_NORETURN static +void +usage (const char* bin) +{ + fprintf (stderr, "Usage: %s [options] message\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -r : Regulate to rate bytes per second\n"); + fprintf (stderr, " -f : Enable FEC with either proactive or ondemand parity\n"); + fprintf (stderr, " -K : Configure Reed-Solomon code (n, k)\n"); + fprintf (stderr, " -N \n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); + fprintf (stderr, " -i : List available interfaces\n"); + exit (1); +} + +int +main ( + int argc, + char *argv[] + ) +{ + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + +/* pre-initialise PGM messages module to add hook for GLib logging */ + pgm_messages_init(); + log_init(); + if (!pgm_init (&pgm_err)) { + g_error ("Unable to start PGM engine: %s", pgm_err->message); + pgm_error_free (pgm_err); + pgm_messages_shutdown(); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:r:f:K:N:lih")) != -1) + { + switch (c) { + case 'n': g_network = optarg; break; + case 's': g_port = atoi (optarg); break; + case 'p': g_udp_encap_port = atoi (optarg); break; + case 'r': g_max_rte = atoi (optarg); break; + + case 'f': g_fec = TRUE; break; + case 'K': g_k = atoi (optarg); break; + case 'N': g_n = atoi (optarg); break; + + case 'l': g_multicast_loop = TRUE; break; + + case 'i': + pgm_if_print_all(); + pgm_messages_shutdown(); + return EXIT_SUCCESS; + + case 'h': + case '?': + pgm_messages_shutdown(); + usage (binary_name); + } + } + + if (g_fec && ( !g_k || !g_n )) { + pgm_messages_shutdown(); + g_error ("Invalid Reed-Solomon parameters RS(%d, %d).", g_n, g_k); + usage (binary_name); + } + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif + + if (create_pgm_socket()) + { + while (optind < argc) { + const int status = pgm_send (g_sock, argv[optind], strlen(argv[optind]) + 1, NULL); + if (PGM_IO_STATUS_NORMAL != status) { + g_warning ("pgm_send failed."); + } + optind++; + } + } + +/* cleanup */ + if (g_sock) { + pgm_close (g_sock, TRUE); + g_sock = NULL; + } + pgm_shutdown(); + pgm_messages_shutdown(); + return EXIT_SUCCESS; +} + +static +gboolean +create_pgm_socket (void) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + +/* parse network parameter into PGM socket address structure */ + if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { + g_error ("Parsing network parameter: %s", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + if (g_udp_encap_port) { + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + g_error ("Creating PGM/UDP socket: %s", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (g_sock, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + pgm_setsockopt (g_sock, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + } else { + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + g_error ("Creating PGM/IP socket: %s", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (g_sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int send_only = 1, + ambient_spm = pgm_secs (30), + heartbeat_spm[] = { pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (1300), + pgm_secs (7), + pgm_secs (16), + pgm_secs (25), + pgm_secs (30) }; + + pgm_setsockopt (g_sock, PGM_SEND_ONLY, &send_only, sizeof(send_only)); + pgm_setsockopt (g_sock, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); + pgm_setsockopt (g_sock, PGM_TXW_SQNS, &g_sqns, sizeof(g_sqns)); + pgm_setsockopt (g_sock, PGM_TXW_MAX_RTE, &g_max_rte, sizeof(g_max_rte)); + pgm_setsockopt (g_sock, PGM_AMBIENT_SPM, &ambient_spm, sizeof(ambient_spm)); + pgm_setsockopt (g_sock, PGM_HEARTBEAT_SPM, &heartbeat_spm, sizeof(heartbeat_spm)); + if (g_fec) { + struct pgm_fecinfo_t fecinfo; + fecinfo.block_size = g_n; + fecinfo.proactive_packets = 0; + fecinfo.group_size = g_k; + fecinfo.ondemand_parity_enabled = TRUE; + fecinfo.var_pktlen_enabled = TRUE; + pgm_setsockopt (g_sock, PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); + } + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + g_error ("Creating GSI: %s", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (g_sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + g_error ("Binding PGM socket: %s", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (g_sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (g_sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int blocking = 0, + multicast_loop = g_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (g_sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (g_sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (g_sock, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (g_sock, PGM_NOBLOCK, &blocking, sizeof(blocking)); + + if (!pgm_connect (g_sock, &pgm_err)) { + g_error ("Connecting PGM socket: %s", pgm_err->message); + goto err_abort; + } + + return TRUE; + +err_abort: + if (NULL != g_sock) { + pgm_close (g_sock, FALSE); + g_sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/pgmtop.c b/3rdparty/openpgm-svn-r1085/pgm/examples/pgmtop.c new file mode 100644 index 0000000..9b18310 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/pgmtop.c @@ -0,0 +1,1031 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM packet monitor. + * + * Copyright (c) 2006-2007 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* PGM internals */ +#include + +/* example dependencies */ +#include +#include + + +struct ncurses_window; + +typedef void (*paint_func)(struct ncurses_window*); +typedef void (*resize_func)(struct ncurses_window*, int, int); + +struct ncurses_window { + WINDOW* window; + PANEL* panel; + char* title; + paint_func paint; + resize_func resize; +}; + +struct pgm_stat { + gulong count, snap_count; + gulong bytes, snap_bytes; + gulong tsdu; + + gulong duplicate; + gulong invalid; + + struct timeval last; + struct timeval last_valid; + struct timeval last_invalid; +}; + +struct pgm_netstat { + struct in_addr addr; + gulong corrupt; +}; + +struct pgm_hoststat { + pgm_tsi_t tsi; + + struct in_addr last_addr; + struct in_addr nla; + + gulong txw_secs; + gulong txw_trail; + gulong txw_lead; + gulong txw_sqns; + + gulong rxw_trail; + gulong rxw_lead; + + gulong rxw_trail_init; + gboolean window_defined; + gboolean rxw_constrained; + + gulong spm_sqn; + + struct pgm_stat spm, + poll, + polr, + odata, + rdata, + nak, + nnak, + ncf, + spmr, + + general; + + struct timeval session_start; +}; + + +/* globals */ + +static int g_port = 7500; +static const char* g_network = "239.192.0.1"; +static struct in_addr g_filter = { 0 }; + +static GIOChannel* g_io_channel = NULL; +static GIOChannel* g_stdin_channel = NULL; + +static GMainLoop* g_loop = NULL; + +static guint g_status_height = 6; +static guint g_info_width = 10; +static time_t start_time; + +static struct ncurses_window *g_peer, *g_info, *g_status, *g_active; +static GList* g_window_list = NULL; +static guint g_paint_interval = ( 1 * 1000 ) / 15; +static guint g_snap_interval = 10 * 1000; +static struct timeval g_last_snap, g_now; + +static GList* g_status_list = NULL; + +static guint32 g_packets = 0; +static GHashTable *g_hosts = NULL; +static GHashTable *g_nets = NULL; + +static void init_ncurses (void); +static void paint_ncurses (void); +static void resize_ncurses (int, int); + +static void paint_peer (struct ncurses_window*); +static gboolean tsi_row (gpointer, gpointer, gpointer); + +static void paint_info (struct ncurses_window*); +static void paint_status (struct ncurses_window*); +static void resize_peer (struct ncurses_window*, int, int); +static void resize_info (struct ncurses_window*, int, int); +static void resize_status (struct ncurses_window*, int, int); + +static void write_status (const gchar*, ...) G_GNUC_PRINTF (1, 2); +static void write_statusv (const gchar*, va_list); + +static void on_signal (int, gpointer); +static void on_winch (int); +static gboolean on_startup (gpointer); +static gboolean on_snap (gpointer); +static gboolean on_paint (gpointer); + +static gboolean on_io_data (GIOChannel*, GIOCondition, gpointer); +static gboolean on_io_error (GIOChannel*, GIOCondition, gpointer); + +static gboolean on_stdin_data (GIOChannel*, GIOCondition, gpointer); + +int +main ( + G_GNUC_UNUSED int argc, + G_GNUC_UNUSED char *argv[] + ) +{ + GError* err = NULL; + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + + log_init (); + g_message ("pgmtop"); + + if (!pgm_init (&pgm_err)) { + g_error ("Unable to start PGM engine: %s", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + + g_loop = g_main_loop_new (NULL, FALSE); + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); + signal (SIGHUP, SIG_IGN); + pgm_signal_install (SIGINT, on_signal, g_loop); + pgm_signal_install (SIGTERM, on_signal, g_loop); + +/* delayed startup */ + g_message ("scheduling startup.n"); + g_timeout_add(0, (GSourceFunc)on_startup, g_loop); + +/* dispatch loop */ + g_message ("entering main event loop ..."); + g_main_loop_run (g_loop); + + endwin(); + g_message ("event loop terminated, cleaning up."); + +/* cleanup */ + g_main_loop_unref (g_loop); + g_loop = NULL; + if (g_io_channel) { + g_message ("closing socket."); + g_io_channel_shutdown (g_io_channel, FALSE, &err); + g_io_channel = NULL; + } + + if (g_stdin_channel) { + g_message ("unbinding stdin."); + g_io_channel_unref (g_stdin_channel); + g_stdin_channel = NULL; + } + + g_message ("PGM engine shutdown."); + pgm_shutdown (); + g_message ("finished."); + return EXIT_SUCCESS; +} + +static struct ncurses_window* +create_window ( + char* name, + paint_func paint, + resize_func resize + ) +{ + struct ncurses_window* nw = g_malloc0 (sizeof(struct ncurses_window)); + nw->window = newwin (0, 0, 0, 0); + nw->panel = new_panel (nw->window); + nw->title = name; + + nw->paint = paint; + nw->resize = resize; + + g_window_list = g_list_append (g_window_list, nw); + return nw; +} + +/* +-Peer list --------------++-Info-+ + * | || | + * | < peer window > || < info window > + * | || | + * +-------------------------++------+ + * +-Status--------------------------+ + * | < status window > | + * +---------------------------------+ + */ + +static void +init_ncurses (void) +{ +/* setup ncurses terminal display */ + initscr(); /* init ncurses library */ + +// signal_install (SIGWINCH, on_winch); + + noecho(); /* hide entered keys */ + cbreak(); + +/* setup ncurses windows */ + g_peer = create_window ("Peers", paint_peer, resize_peer); + + g_info = create_window ("Info", paint_info, resize_info); + start_time = time (0); + + g_status = create_window ("Status", paint_status, resize_status); + scrollok (g_status->window, 1); + + g_active = g_peer; + top_panel (g_active->panel); + + paint_ncurses(); +} + +static void +resize_ncurses ( + int hsize, + int vsize + ) +{ + GList* nw_list = g_window_list; + while (nw_list) + { + struct ncurses_window* nw = (struct ncurses_window*)nw_list->data; + + nw->resize (nw, hsize, vsize); + + nw_list = nw_list->next; + } +} + +static void +paint_ncurses (void) +{ + static int hsize = 0, vsize = 0; + + if (hsize != COLS || vsize != LINES) + { + hsize = COLS; vsize = LINES; + resize_ncurses(hsize, vsize); + } + + GList* nw_list = g_window_list; + while (nw_list) + { + struct ncurses_window* nw = (struct ncurses_window*)nw_list->data; + werase (nw->window); + + box (nw->window, ACS_VLINE, ACS_HLINE); + mvwaddstr (nw->window, 0, 2, nw->title); + + nw->paint (nw); + + nw_list = nw_list->next; + } + +/* have cursor stay at top left of active window */ + wmove (g_active->window, 0, 0); + + update_panels(); /* update virtual screen */ + doupdate(); /* update real screen */ +} + +/* peer window */ + +static void +paint_peer ( + struct ncurses_window* nw + ) +{ + +/* 1 2 3 4 5 6 7 8 + * 012345678901234567890123456789012345678901234567890123456789012345678901234567890 + * TSI Packets Bytes Packet/s Bit/s Data Inv Dupe + * 100.200.300.400.500.600.70000 1,000K 1,000MB 1,000 1,000 100% 100% 100% + */ + mvwaddstr (nw->window, 1, 1, "TSI"); + mvwaddstr (nw->window, 1, 32, "Packets"); + mvwaddstr (nw->window, 1, 40, "Bytes"); + mvwaddstr (nw->window, 1, 48, "Packet/s"); + mvwaddstr (nw->window, 1, 58, "Bit/s"); + mvwaddstr (nw->window, 1, 68, "Data"); + mvwaddstr (nw->window, 1, 73, "Inv"); + mvwaddstr (nw->window, 1, 78, "Dupe"); + + if (g_hosts) + { + int row = 2; + gettimeofday(&g_now, NULL); + g_hash_table_foreach (g_hosts, (GHFunc)tsi_row, &row); + } +} + +static char* +print_si ( + float* v + ) +{ + static char prefix[5] = ""; + + if (*v > 100 * 1000 * 1000) { + strcpy (prefix, "G"); + *v /= 1000.0 * 1000.0 * 1000.0; + } else if (*v > 100 * 1000) { + strcpy (prefix, "M"); + *v /= 1000.0 * 1000.0; + } else if (*v > 100) { + strcpy (prefix, "K"); + *v /= 1000.0; + } else { + *prefix = 0; + } + + return prefix; +} + +static gboolean +tsi_row ( + G_GNUC_UNUSED gpointer key, + gpointer value, + gpointer user_data + ) +{ + struct pgm_hoststat* hoststat = value; + int* row = user_data; + + float secs = (g_now.tv_sec - g_last_snap.tv_sec) + + ( (g_now.tv_usec - g_last_snap.tv_usec) / 1000.0 / 1000.0 ); + +/* TSI */ + char* tsi_string = pgm_tsi_print (&hoststat->tsi); + mvwaddstr (g_peer->window, *row, 1, tsi_string); + +/* Packets */ + char buffer[100]; + float v = hoststat->general.count; + char* prefix = print_si (&v); + snprintf (buffer, sizeof(buffer), "%lu%s", (gulong)v, prefix); + mvwaddstr (g_peer->window, *row, 32, buffer); + +/* Bytes */ + v = hoststat->general.bytes; + prefix = print_si (&v); + snprintf (buffer, sizeof(buffer), "%lu%s", (gulong)v, prefix); + mvwaddstr (g_peer->window, *row, 40, buffer); + +/* Packet/s */ + v = ( hoststat->general.count - hoststat->general.snap_count ) / secs; + prefix = print_si (&v); + snprintf (buffer, sizeof(buffer), "%.1f%s", v, prefix); + mvwaddstr (g_peer->window, *row, 48, buffer); + +/* Bit/s */ + float bitrate = ((float)( hoststat->general.bytes - hoststat->general.snap_bytes ) * 8.0 / secs); + char* bitprefix = print_si (&bitrate); + snprintf (buffer, sizeof(buffer), "%.1f%s", bitrate, bitprefix); + mvwaddstr (g_peer->window, *row, 58, buffer); + +/* % Data */ + snprintf (buffer, sizeof(buffer), "%d%%", (int)((100.0 * hoststat->odata.tsdu) / hoststat->general.bytes)); + mvwaddstr (g_peer->window, *row, 68, buffer); + +/* % Invalid */ + snprintf (buffer, sizeof(buffer), "%d%%", (int)(hoststat->general.invalid ? (100.0 * hoststat->general.invalid) / hoststat->general.count : 0.0)); + mvwaddstr (g_peer->window, *row, 73, buffer); + +/* % Duplicate */ + snprintf (buffer, sizeof(buffer), "%d%%", (int)(hoststat->general.duplicate ? (100.0 * hoststat->general.duplicate) / hoststat->general.count : 0.0)); + mvwaddstr (g_peer->window, *row, 78, buffer); + + *row = *row + 1; + + return FALSE; +} + +static void +resize_peer ( + struct ncurses_window* nw, + int hsize, /* COLS */ + int vsize /* LINES */ + ) +{ + wresize (nw->window, vsize - g_status_height, hsize - g_info_width); + replace_panel (nw->panel, nw->window); + move_panel (nw->panel, 0, 0); +} + +/* info window */ + +static void +paint_info ( + struct ncurses_window* nw + ) +{ + char buffer[20]; + + mvwaddstr (nw->window, 1, 2, "Peers"); + snprintf (buffer, sizeof(buffer), "%d", g_hosts ? g_hash_table_size (g_hosts) : 0); + mvwaddstr (nw->window, 2, 2, buffer); + + mvwaddstr (nw->window, 3, 2, "Packets"); + snprintf (buffer, sizeof(buffer), "%d", g_packets); + mvwaddstr (nw->window, 4, 2, buffer); + + mvwaddstr (nw->window, LINES - g_status_height - 2, 2, "Elapsed"); + time_t elapsed = time(0) - start_time; + snprintf (buffer, sizeof(buffer), "%02d:%02d:%02d", + (int) (elapsed / 60) / 60, (int) (elapsed / 60) % 60, + (int) elapsed % 60); + mvwaddstr (nw->window, LINES - g_status_height - 1, 1, buffer); +} + +static void +resize_info ( + struct ncurses_window* nw, + int hsize, /* COLS */ + int vsize /* LINES */ + ) +{ + wresize (nw->window, vsize - g_status_height, g_info_width); + replace_panel (nw->panel, nw->window); + move_panel (nw->panel, 0, hsize - g_info_width); +} + +/* status window */ + +static void +paint_status ( + G_GNUC_UNUSED struct ncurses_window* nw + ) +{ + if (!g_status_list) return; + + guint len = g_list_length (g_status_list); + while (len > g_status_height) { + g_free (g_status_list->data); + g_status_list = g_list_delete_link (g_status_list, g_status_list); + len--; + } + guint y = 1; + GList* list = g_status_list; + while (list) { + mvwaddstr (g_status->window, y++, 3, (char*)list->data); + list = list->next; + } +} + +static void +resize_status ( + struct ncurses_window* nw, + int hsize, /* COLS */ + int vsize /* LINES */ + ) +{ + wresize (nw->window, g_status_height, hsize); + replace_panel (nw->panel, nw->window); + move_panel (nw->panel, vsize - g_status_height, 0); +} + +static void +write_status ( + const gchar* format, + ... + ) +{ + va_list args; + + va_start (args, format); + write_statusv (format, args); + va_end (args); +} + +static void +write_statusv ( + const gchar* format, + va_list args1 + ) +{ + char buffer[1024]; + vsnprintf (buffer, sizeof(buffer), format, args1); + + g_status_list = g_list_append (g_status_list, g_memdup (buffer, strlen(buffer)+1)); +} + +static +void +on_signal ( + int signum, + gpointer user_data + ) +{ + GMainLoop* loop = (GMainLoop*)user_data; + puts ("on_signal"); + g_main_loop_quit (loop); +} + +/* terminal resize signal + */ + +static void +on_winch ( + G_GNUC_UNUSED int signum + ) +{ + paint_ncurses (); +} + +static gboolean +on_startup ( + gpointer user_data + ) +{ + GMainLoop* loop = (GMainLoop*)user_data; + int e; + + puts ("startup."); + +/* find PGM protocol id */ +// TODO: fix valgrind errors + int ipproto_pgm = IPPROTO_PGM; +#if HAVE_GETPROTOBYNAME_R + char b[1024]; + struct protoent protobuf, *proto; + e = getprotobyname_r ("pgm", &protobuf, b, sizeof(b), &proto); + if (e != -1 && proto != NULL) { + if (proto->p_proto != ipproto_pgm) { + print f("Setting PGM protocol number to %i from /etc/protocols.\n", proto->p_proto); + ipproto_pgm = proto->p_proto; + } + } +#else + struct protoent *proto = getprotobyname ("pgm"); + if (proto != NULL) { + if (proto->p_proto != ipproto_pgm) { + printf("Setting PGM protocol number to %i from /etc/protocols.\n", proto->p_proto); + ipproto_pgm = proto->p_proto; + } + } +#endif + +/* open socket for snooping */ + puts ("opening raw socket."); + int sock = socket (PF_INET, SOCK_RAW, ipproto_pgm); + if (sock < 0) { + int _e = errno; + puts ("on_startup() failed"); + + if (_e == EPERM && 0 != getuid()) { + puts ("PGM protocol requires this program to run as superuser."); + } + g_main_loop_quit (loop); + return FALSE; + } + +/* drop out of setuid 0 */ + if (0 == getuid ()) { + puts ("dropping superuser privileges."); + setuid ((gid_t)65534); + setgid ((uid_t)65534); + } + + char _t = 1; + e = setsockopt (sock, IPPROTO_IP, IP_HDRINCL, &_t, sizeof(_t)); + if (e < 0) { + printw ("on_startup() failed\n"); + close (sock); + g_main_loop_quit (loop); + return FALSE; + } + +/* buffers */ + int buffer_size = 0; + socklen_t len = 0; + e = getsockopt (sock, SOL_SOCKET, SO_RCVBUF, &buffer_size, &len); + if (e == 0) { + printf ("receive buffer set at %i bytes.\n", buffer_size); + } + e = getsockopt (sock, SOL_SOCKET, SO_SNDBUF, &buffer_size, &len); + if (e == 0) { + printf ("send buffer set at %i bytes.\n", buffer_size); + } + +/* bind */ + struct sockaddr_in addr; + memset (&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + e = bind (sock, (struct sockaddr*)&addr, sizeof(addr)); + if (e < 0) { + printw ("on_startup() failed\n"); + close (sock); + g_main_loop_quit (loop); + return FALSE; + } + +/* multicast */ + struct ip_mreq mreq; + memset (&mreq, 0, sizeof(mreq)); + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + printf ("listening on interface %s.\n", inet_ntoa(mreq.imr_interface)); + mreq.imr_multiaddr.s_addr = inet_addr(g_network); + printf ("subscription on multicast address %s.\n", inet_ntoa(mreq.imr_multiaddr)); + e = setsockopt (sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + if (e < 0) { + printw ("on_startup() failed\n"); + close (sock); + g_main_loop_quit (loop); + return FALSE; + } + +/* multicast loopback */ +/* multicast ttl */ + +/* add socket to event manager */ + g_io_channel = g_io_channel_unix_new (sock); + printf ("socket opened with encoding %s.\n", g_io_channel_get_encoding(g_io_channel)); + + /* guint event = */ g_io_add_watch (g_io_channel, G_IO_IN | G_IO_PRI, on_io_data, NULL); + /* guint event = */ g_io_add_watch (g_io_channel, G_IO_ERR | G_IO_HUP | G_IO_NVAL, on_io_error, NULL); + +/* add stdin to event manager */ + g_stdin_channel = g_io_channel_unix_new (fileno(stdin)); + printf ("binding stdin with encoding %s.\n", g_io_channel_get_encoding(g_stdin_channel)); + + g_io_add_watch (g_stdin_channel, G_IO_IN | G_IO_PRI, on_stdin_data, NULL); + +/* periodic timer to snapshot statistics */ + g_timeout_add (g_snap_interval, (GSourceFunc)on_snap, NULL); + +/* period timer to update screen */ + g_timeout_add (g_paint_interval, (GSourceFunc)on_paint, NULL); + + puts ("READY"); + + init_ncurses(); + return FALSE; +} + +static gboolean +on_paint ( + G_GNUC_UNUSED gpointer data + ) +{ + paint_ncurses(); + + return TRUE; +} + +static guint +tsi_hash ( + gconstpointer v + ) +{ + return g_str_hash(pgm_tsi_print(v)); +} + +static gint +tsi_equal ( + gconstpointer v, + gconstpointer v2 + ) +{ + return memcmp (v, v2, (6 * sizeof(guint8)) + sizeof(guint16)) == 0; +} + +static gboolean +on_io_data ( + GIOChannel* source, + G_GNUC_UNUSED GIOCondition condition, + G_GNUC_UNUSED gpointer user_data + ) +{ + struct timeval now; + struct pgm_sk_buff_t* skb = pgm_alloc_skb (4096); + struct sockaddr_storage src, dst; + struct sockaddr_in* sin = (struct sockaddr_in*)&src; + socklen_t src_addr_len = sizeof(src); + int fd = g_io_channel_unix_get_fd(source); + + skb->len = recvfrom(fd, skb->head, 4096, MSG_DONTWAIT, (struct sockaddr*)&src, &src_addr_len); + + gettimeofday (&now, NULL); + g_packets++; + + GError* err = NULL; + gboolean is_valid = pgm_parse_raw (skb, (struct sockaddr*)&dst, &err); + if (!is_valid && err && PGM_PACKET_ERROR_CKSUM == err->code) + { +/* corrupt packet */ + if (!g_nets) { + g_nets = g_hash_table_new (g_int_hash, g_int_equal); + } + + struct pgm_netstat* netstat = g_hash_table_lookup (g_nets, &sin->sin_addr); + if (netstat == NULL) { + write_status ("new host publishing corrupt data, local nla %s", inet_ntoa(sin->sin_addr)); + netstat = g_malloc0(sizeof(struct pgm_netstat)); + netstat->addr = sin->sin_addr; + g_hash_table_insert (g_nets, (gpointer)&netstat->addr, (gpointer)netstat); + } + + netstat->corrupt++; + pgm_free_skb (skb); + return TRUE; + } + else if (!is_valid) + { +/* general error */ + pgm_free_skb (skb); + return TRUE; + } + +/* search for existing session */ + if (!g_hosts) { + g_hosts = g_hash_table_new (tsi_hash, tsi_equal); + } + + struct pgm_hoststat* hoststat = g_hash_table_lookup (g_hosts, &skb->tsi); + if (hoststat == NULL) { + write_status ("new tsi %s with local nla %s", pgm_tsi_print (&skb->tsi), inet_ntoa(sin->sin_addr)); + + hoststat = g_malloc0(sizeof(struct pgm_hoststat)); + memcpy (&hoststat->tsi, &skb->tsi, sizeof(pgm_tsi_t)); + hoststat->session_start = now; + + g_hash_table_insert (g_hosts, (gpointer)&hoststat->tsi, (gpointer)hoststat); + } + +/* increment statistics */ + memcpy (&hoststat->last_addr, &sin->sin_addr, sizeof(sin->sin_addr)); + hoststat->general.count++; + hoststat->general.bytes += skb->len; + hoststat->general.last = now; + + skb->data = (guint8*)skb->data + sizeof(struct pgm_header); + skb->len -= sizeof(struct pgm_header); + +/* repurpose is_valid for PGM subtype */ + is_valid = FALSE; + switch (skb->pgm_header->pgm_type) { + case PGM_SPM: + hoststat->spm.count++; + hoststat->spm.bytes += skb->len; + hoststat->spm.last = now; + + is_valid = pgm_verify_spm (skb); + if (!is_valid) { + hoststat->spm.invalid++; + hoststat->spm.last_invalid = now; + } else { + const struct pgm_spm* spm = (struct pgm_spm*)skb->data; + + hoststat->nla.s_addr = spm->spm_nla.s_addr; + if (pgm_uint32_lte (g_ntohl( spm->spm_sqn ), hoststat->spm_sqn)) { + hoststat->general.duplicate++; + break; + } + hoststat->spm_sqn = g_ntohl( spm->spm_sqn ); + hoststat->txw_trail = g_ntohl( spm->spm_trail ); + hoststat->txw_lead = g_ntohl( spm->spm_lead ); + hoststat->rxw_trail = hoststat->txw_trail; + hoststat->window_defined = TRUE; + } + break; + + case PGM_ODATA: + hoststat->odata.count++; + hoststat->odata.bytes += skb->len; + hoststat->odata.last = now; + + const struct pgm_data* data = (struct pgm_data*)skb->data; + + if (!hoststat->window_defined) { + hoststat->rxw_lead = g_ntohl (data->data_sqn) - 1; + hoststat->rxw_trail = hoststat->rxw_trail_init = hoststat->rxw_lead + 1; + hoststat->rxw_constrained = TRUE; + hoststat->window_defined = TRUE; + } else { + if (! pgm_uint32_gte( g_ntohl (data->data_sqn) , hoststat->rxw_trail ) ) + { + hoststat->odata.invalid++; + hoststat->odata.last_invalid = now; + break; + } + hoststat->rxw_trail = g_ntohl (data->data_trail); + } + + if (hoststat->rxw_constrained && hoststat->txw_trail > hoststat->rxw_trail_init) { + hoststat->rxw_constrained = FALSE; + } + + if ( pgm_uint32_lte ( g_ntohl (data->data_sqn), hoststat->rxw_lead ) ) { + hoststat->general.duplicate++; + break; + } else { + hoststat->rxw_lead = g_ntohl (data->data_sqn); + + hoststat->odata.tsdu += g_ntohs (skb->pgm_header->pgm_tsdu_length); + } + break; + + case PGM_RDATA: + hoststat->rdata.count++; + hoststat->rdata.bytes += skb->len; + hoststat->rdata.last = now; + break; + + case PGM_POLL: + hoststat->poll.count++; + hoststat->poll.bytes += skb->len; + hoststat->poll.last = now; + break; + + case PGM_POLR: + hoststat->polr.count++; + hoststat->polr.bytes += skb->len; + hoststat->polr.last = now; + break; + + case PGM_NAK: + hoststat->nak.count++; + hoststat->nak.bytes += skb->len; + hoststat->nak.last = now; + + is_valid = pgm_verify_nak (skb); + if (!is_valid) { + hoststat->nak.invalid++; + hoststat->nak.last_invalid = now; + } + break; + + case PGM_NNAK: + hoststat->nnak.count++; + hoststat->nnak.bytes += skb->len; + hoststat->nnak.last = now; + break; + + case PGM_NCF: + hoststat->ncf.count++; + hoststat->ncf.bytes += skb->len; + hoststat->ncf.last = now; + break; + + case PGM_SPMR: + hoststat->spmr.count++; + hoststat->spmr.bytes += skb->len; + hoststat->spmr.last = now; + + is_valid = pgm_verify_spmr (skb); + if (!is_valid) { + hoststat->spmr.invalid++; + hoststat->spmr.last_invalid = now; + } + break; + + default: + break; + } + + if (!is_valid) { + hoststat->general.invalid++; + hoststat->general.last_invalid = now; + } else { + hoststat->general.last_valid = now; + } + + pgm_free_skb (skb); + return TRUE; +} + +static gboolean +on_io_error ( + GIOChannel* source, + G_GNUC_UNUSED GIOCondition condition, + G_GNUC_UNUSED gpointer data + ) +{ + puts ("on_error."); + + GError *err; + g_io_channel_shutdown (source, FALSE, &err); + +/* remove event */ + return FALSE; +} + +/* process input commands from stdin/fd + */ + +static gboolean +on_stdin_data ( + G_GNUC_UNUSED GIOChannel* source, + G_GNUC_UNUSED GIOCondition condition, + G_GNUC_UNUSED gpointer data + ) +{ + int ch = wgetch (g_active->window); + if (ch == ERR) { + goto out; + } + +/* force redraw */ + if (ch == 12) { + clearok (curscr, TRUE); + paint_ncurses (); + goto out; + } + + if (ch == 'q') { + g_main_loop_quit(g_loop); + } + +out: + return TRUE; +} + +static gboolean +snap_stat ( + G_GNUC_UNUSED gpointer key, + gpointer value, + G_GNUC_UNUSED gpointer user_data + ) +{ + struct pgm_hoststat* hoststat = value; + +#define SNAP_STAT(name) \ + { \ + hoststat->name.snap_count = hoststat->name.count; \ + hoststat->name.snap_bytes = hoststat->name.bytes; \ + } + + SNAP_STAT(spm); + SNAP_STAT(poll); + SNAP_STAT(polr); + SNAP_STAT(odata); + SNAP_STAT(rdata); + SNAP_STAT(nak); + SNAP_STAT(nnak); + SNAP_STAT(ncf); + SNAP_STAT(spmr); + + SNAP_STAT(general); + + return FALSE; +} + +static gboolean +on_snap ( + gpointer data + ) +{ + if (!g_hosts) return TRUE; + + gettimeofday (&g_last_snap, NULL); + g_hash_table_foreach (g_hosts, (GHFunc)snap_stat, NULL); + + return TRUE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/ping.proto b/3rdparty/openpgm-svn-r1085/pgm/examples/ping.proto new file mode 100644 index 0000000..8c6dfd1 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/ping.proto @@ -0,0 +1,47 @@ +package example; + +message SubscriptionHeader { + required string subject = 1; +} + +message MarketDataHeader { + enum MsgType { + MSG_VERIFY = 0; + MSG_UPDATE = 1; + MSG_CORRECT = 2; + MSG_CLOSING = 3; + MSG_DROP = 4; + MSG_AGGREGATE = 5; + MSG_STATUS = 6; + MSG_CANCEL = 7; + MSG_INITIAL = 8; + } + required MsgType msg_type = 1; + enum RecType { + PING = 1; + } + required RecType rec_type = 2; + enum RecStatus { + STATUS_OK = 0; + STATUS_BAD_NAME = 1; + STATUS_BAD_LINE = 2; + STATUS_CACHE_FULL = 3; + STATUS_PERMISSION_DENIED = 4; + STATUS_PREEMPTED = 5; + STATUS_BAD_ACCESS = 6; + STATUS_TEMP_UNAVAIL = 7; + STATUS_REASSIGN = 8; + STATUS_NOSUBSCRIBERS = 9; + STATUS_EXPIRED = 10; + } + required RecStatus rec_status = 3; +} + +message Ping { + required SubscriptionHeader subscription_header = 1; + required MarketDataHeader market_data_header = 2; + required fixed64 time = 3; + required fixed64 seqno = 4; + required fixed64 latency = 5; + required bytes payload = 6; +} diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/pnonblocksyncrecv.c b/3rdparty/openpgm-svn-r1085/pgm/examples/pnonblocksyncrecv.c new file mode 100644 index 0000000..f66454f --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/pnonblocksyncrecv.c @@ -0,0 +1,385 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Simple PGM receiver: poll based non-blocking synchronous receiver. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef G_OS_UNIX +# include +# include +# include +# include +#endif +#include + +/* example dependencies */ +#include +#include + + +/* typedefs */ + +/* globals */ + +static int g_port = 0; +static const char* g_network = ""; +static gboolean g_multicast_loop = FALSE; +static int g_udp_encap_port = 0; + +static int g_max_tpdu = 1500; +static int g_sqns = 100; + +static pgm_sock_t* g_sock = NULL; +static gboolean g_quit; +static int g_quit_pipe[2]; + +static void on_signal (int); +static gboolean on_startup (void); + +static int on_data (gconstpointer, size_t, struct pgm_sockaddr_t*); + + +G_GNUC_NORETURN static +void +usage ( + const char* bin + ) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); + exit (1); +} + +int +main ( + int argc, + char* argv[] + ) +{ + int e; + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + + log_init (); + g_message ("pnonblocksyncrecv"); + + if (!pgm_init (&pgm_err)) { + g_error ("Unable to start PGM engine: %s", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:lh")) != -1) + { + switch (c) { + case 'n': g_network = optarg; break; + case 's': g_port = atoi (optarg); break; + case 'p': g_udp_encap_port = atoi (optarg); break; + case 'l': g_multicast_loop = TRUE; break; + + case 'h': + case '?': usage (binary_name); + } + } + + g_quit = FALSE; +#ifdef G_OS_UNIX + e = pipe (g_quit_pipe); +#else + e = _pipe (g_quit_pipe, 4096, _O_BINARY | _O_NOINHERIT); +#endif + g_assert (0 == e); + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); + signal (SIGINT, on_signal); + signal (SIGTERM, on_signal); +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif + + if (!on_startup()) { + g_error ("startup failed"); + exit(1); + } + +/* dispatch loop */ + g_message ("entering PGM message loop ... "); + do { + struct timeval tv; + int timeout; + int n_fds = 2; + struct pollfd fds[ 1 + n_fds ]; + char buffer[4096]; + size_t len; + struct pgm_sockaddr_t from; + socklen_t fromlen = sizeof(from); + const int status = pgm_recvfrom (g_sock, + buffer, + sizeof(buffer), + 0, + &len, + &from, + &fromlen, + &pgm_err); + switch (status) { + case PGM_IO_STATUS_NORMAL: + on_data (buffer, len, &from); + break; + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (g_sock, PGM_TIME_REMAIN, &tv, &optlen); + } + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (g_sock, PGM_RATE_REMAIN, &tv, &optlen); + } +/* fall through */ + case PGM_IO_STATUS_WOULD_BLOCK: +/* poll for next event */ +block: + timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + memset (fds, 0, sizeof(fds)); + fds[0].fd = g_quit_pipe[0]; + fds[0].events = POLLIN; + pgm_poll_info (g_sock, &fds[1], &n_fds, POLLIN); + poll (fds, 1 + n_fds, timeout /* ms */); + break; + default: + if (pgm_err) { + g_warning ("%s", pgm_err->message); + pgm_error_free (pgm_err); + pgm_err = NULL; + } + if (PGM_IO_STATUS_ERROR == status) + break; + } + } while (!g_quit); + + g_message ("message loop terminated, cleaning up."); + +/* cleanup */ + close (g_quit_pipe[0]); + close (g_quit_pipe[1]); + + if (g_sock) { + g_message ("closing PGM socket."); + pgm_close (g_sock, TRUE); + g_sock = NULL; + } + + g_message ("PGM engine shutdown."); + pgm_shutdown (); + g_message ("finished."); + return EXIT_SUCCESS; +} + +static +void +on_signal ( + int signum + ) +{ + g_message ("on_signal (signum:%d)", signum); + g_quit = TRUE; + const char one = '1'; + const size_t writelen = write (g_quit_pipe[1], &one, sizeof(one)); + g_assert (sizeof(one) == writelen); +} + +static +gboolean +on_startup (void) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + + g_message ("startup."); + +/* parse network parameter into transport address structure */ + if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { + g_error ("parsing network parameter: %s", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + if (g_udp_encap_port) { + g_message ("create PGM/UDP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (g_sock, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + pgm_setsockopt (g_sock, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + } else { + g_message ("create PGM/IP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (g_sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int recv_only = 1, + passive = 0, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_secs (2), + nak_rdata_ivl = pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + pgm_setsockopt (g_sock, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + pgm_setsockopt (g_sock, PGM_PASSIVE, &passive, sizeof(passive)); + pgm_setsockopt (g_sock, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); + pgm_setsockopt (g_sock, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); + pgm_setsockopt (g_sock, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + pgm_setsockopt (g_sock, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + pgm_setsockopt (g_sock, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + pgm_setsockopt (g_sock, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + g_error ("creating GSI: %s", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (g_sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + g_error ("binding PGM socket: %s", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (g_sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (g_sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int nonblocking = 1, + multicast_loop = g_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (g_sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (g_sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (g_sock, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (g_sock, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); + + if (!pgm_connect (g_sock, &pgm_err)) { + g_error ("connecting PGM socket: %s", pgm_err->message); + goto err_abort; + } + + g_message ("startup complete."); + return TRUE; + +err_abort: + if (NULL != g_sock) { + pgm_close (g_sock, FALSE); + g_sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +static +int +on_data ( + gconstpointer data, + size_t len, + struct pgm_sockaddr_t* from + ) +{ +/* protect against non-null terminated strings */ + char buf[1024], tsi[PGM_TSISTRLEN]; + const size_t buflen = MIN(sizeof(buf) - 1, len); + strncpy (buf, (const char*)data, buflen); + buf[buflen] = '\0'; + pgm_tsi_print_r (&from->sa_addr, tsi, sizeof(tsi)); + + g_message ("\"%s\" (%u bytes from %s)", + buf, + (unsigned)len, + tsi); + + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/purinrecv.c b/3rdparty/openpgm-svn-r1085/pgm/examples/purinrecv.c new file mode 100644 index 0000000..c5e4bd3 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/purinrecv.c @@ -0,0 +1,474 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * プリン PGM receiver + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#ifndef _WIN32 +# include +#else +# include "getopt.h" +# define snprintf _snprintf +#endif +#include + + +/* globals */ + +static int port = 0; +static const char* network = ""; +static bool use_multicast_loop = FALSE; +static int udp_encap_port = 0; + +static int max_tpdu = 1500; +static int sqns = 100; + +static bool use_pgmcc = FALSE; +static bool use_fec = FALSE; +static int rs_k = 8; +static int rs_n = 255; + +static pgm_sock_t* sock = NULL; +static bool is_terminated = FALSE; + +#ifndef _WIN32 +static int terminate_pipe[2]; +static void on_signal (int); +#else +static HANDLE terminate_event; +static BOOL on_console_ctrl (DWORD); +#endif +#ifndef _MSC_VER +static void usage (const char*) __attribute__((__noreturn__)); +#else +static void usage (const char*); +#endif + +static bool on_startup (void); +static int on_data (const void*restrict, const size_t, const struct pgm_sockaddr_t*restrict); + + +static void +usage ( + const char* bin + ) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -c : Enable PGMCC\n"); + fprintf (stderr, " -f : Enable FEC with either proactive or ondemand parity\n"); + fprintf (stderr, " -K : Configure Reed-Solomon code (n, k)\n"); + fprintf (stderr, " -N \n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); + fprintf (stderr, " -i : List available interfaces\n"); + exit (EXIT_SUCCESS); +} + +int +main ( + int argc, + char* argv[] + ) +{ + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + +#if !defined(_WIN32) || defined(CONFIG_TARGET_WINE) + puts ("プリン プリン"); +#else + _putws (L"プリン プリン"); +#endif + + if (!pgm_init (&pgm_err)) { + fprintf (stderr, "Unable to start PGM engine: %s\n", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:cf:K:N:lih")) != -1) + { + switch (c) { + case 'n': network = optarg; break; + case 's': port = atoi (optarg); break; + case 'p': udp_encap_port = atoi (optarg); break; + case 'c': use_pgmcc = TRUE; break; + case 'f': use_fec = TRUE; break; + case 'K': rs_k = atoi (optarg); break; + case 'N': rs_n = atoi (optarg); break; + case 'l': use_multicast_loop = TRUE; break; + + case 'i': + pgm_if_print_all(); + return EXIT_SUCCESS; + + case 'h': + case '?': usage (binary_name); + } + } + + if (use_fec && ( !rs_n || !rs_k )) { + fprintf (stderr, "Invalid Reed-Solomon parameters RS(%d,%d).\n", rs_n, rs_k); + usage (binary_name); + } + +/* setup signal handlers */ +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif +#ifndef _WIN32 + int e = pipe (terminate_pipe); + assert (0 == e); + signal (SIGINT, on_signal); + signal (SIGTERM, on_signal); +#else + terminate_event = CreateEvent (NULL, TRUE, FALSE, TEXT("TerminateEvent")); + SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); +#endif /* !_WIN32 */ + + if (!on_startup()) { + fprintf (stderr, "Startup failed\n"); + return EXIT_FAILURE; + } + +/* dispatch loop */ +#ifndef _WIN32 + int fds; + fd_set readfds; +#else + int n_handles = 3, recv_sock, pending_sock; + HANDLE waitHandles[ 3 ]; + DWORD dwTimeout, dwEvents; + WSAEVENT recvEvent, pendingEvent; + socklen_t socklen = sizeof(int); + + recvEvent = WSACreateEvent (); + pgm_getsockopt (sock, PGM_RECV_SOCK, &recv_sock, &socklen); + WSAEventSelect (recv_sock, recvEvent, FD_READ); + pendingEvent = WSACreateEvent (); + pgm_getsockopt (sock, PGM_PENDING_SOCK, &pending_sock, &socklen); + WSAEventSelect (pending_sock, pendingEvent, FD_READ); + + waitHandles[0] = terminate_event; + waitHandles[1] = recvEvent; + waitHandles[2] = pendingEvent; +#endif /* !_WIN32 */ + puts ("Entering PGM message loop ... "); + do { + struct timeval tv; + char buffer[4096]; + size_t len; + struct pgm_sockaddr_t from; + socklen_t fromlen = sizeof (from); + const int status = pgm_recvfrom (sock, + buffer, + sizeof(buffer), + 0, + &len, + &from, + &fromlen, + &pgm_err); + switch (status) { + case PGM_IO_STATUS_NORMAL: + on_data (buffer, len, &from); + break; + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (sock, PGM_TIME_REMAIN, &tv, &optlen); + } + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (sock, PGM_RATE_REMAIN, &tv, &optlen); + } + case PGM_IO_STATUS_WOULD_BLOCK: +/* select for next event */ +block: +#ifndef _WIN32 + fds = terminate_pipe[0] + 1; + FD_ZERO(&readfds); + FD_SET(terminate_pipe[0], &readfds); + pgm_select_info (sock, &readfds, NULL, &fds); + fds = select (fds, &readfds, NULL, NULL, PGM_IO_STATUS_WOULD_BLOCK == status ? NULL : &tv); +#else + dwTimeout = PGM_IO_STATUS_WOULD_BLOCK == status ? INFINITE : (DWORD)((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + dwEvents = WaitForMultipleObjects (n_handles, waitHandles, FALSE, dwTimeout); + switch (dwEvents) { + case WAIT_OBJECT_0+1: WSAResetEvent (recvEvent); break; + case WAIT_OBJECT_0+2: WSAResetEvent (pendingEvent); break; + default: break; + } +#endif /* !_WIN32 */ + break; + + default: + if (pgm_err) { + fprintf (stderr, "%s\n", pgm_err->message); + pgm_error_free (pgm_err); + pgm_err = NULL; + } + if (PGM_IO_STATUS_ERROR == status) + break; + } + } while (!is_terminated); + + puts ("Message loop terminated, cleaning up."); + +/* cleanup */ +#ifndef _WIN32 + close (terminate_pipe[0]); + close (terminate_pipe[1]); +#else + WSACloseEvent (recvEvent); + WSACloseEvent (pendingEvent); + CloseHandle (terminate_event); +#endif /* !_WIN32 */ + + if (sock) { + puts ("Destroying PGM socket."); + pgm_close (sock, TRUE); + sock = NULL; + } + + puts ("PGM engine shutdown."); + pgm_shutdown (); + puts ("finished."); + return EXIT_SUCCESS; +} + +#ifndef _WIN32 +static +void +on_signal ( + int signum + ) +{ + printf ("on_signal (signum:%d)\n", signum); + is_terminated = TRUE; + const char one = '1'; + const size_t writelen = write (terminate_pipe[1], &one, sizeof(one)); + assert (sizeof(one) == writelen); +} +#else +static +BOOL +on_console_ctrl ( + DWORD dwCtrlType + ) +{ + printf ("on_console_ctrl (dwCtrlType:%lu)\n", (unsigned long)dwCtrlType); + is_terminated = TRUE; + SetEvent (terminate_event); + return TRUE; +} +#endif /* !_WIN32 */ + +static +bool +on_startup (void) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + +/* parse network parameter into PGM socket address structure */ + if (!pgm_getaddrinfo (network, NULL, &res, &pgm_err)) { + fprintf (stderr, "Parsing network parameter: %s\n", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + if (udp_encap_port) { + puts ("Create PGM/UDP socket."); + if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + fprintf (stderr, "Creating PGM/UDP socket: %s\n", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (sock, PGM_UDP_ENCAP_UCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + pgm_setsockopt (sock, PGM_UDP_ENCAP_MCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + } else { + puts ("Create PGM/IP socket."); + if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + fprintf (stderr, "Creating PGM/IP socket: %s\n", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int recv_only = 1, + passive = 0, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_secs (2), + nak_rdata_ivl = pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + pgm_setsockopt (sock, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + pgm_setsockopt (sock, PGM_PASSIVE, &passive, sizeof(passive)); + pgm_setsockopt (sock, PGM_MTU, &max_tpdu, sizeof(max_tpdu)); + pgm_setsockopt (sock, PGM_RXW_SQNS, &sqns, sizeof(sqns)); + pgm_setsockopt (sock, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + pgm_setsockopt (sock, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + pgm_setsockopt (sock, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + pgm_setsockopt (sock, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + pgm_setsockopt (sock, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + pgm_setsockopt (sock, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + pgm_setsockopt (sock, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + +#ifdef I_UNDERSTAND_PGMCC_AND_FEC_ARE_NOT_SUPPORTED + if (use_pgmcc) { + struct pgm_pgmccinfo_t pgmccinfo; + pgmccinfo.ack_bo_ivl = pgm_msecs (50); + pgmccinfo.ack_c = 75; + pgmccinfo.ack_c_p = 500; + pgm_setsockopt (sock, PGM_USE_PGMCC, &pgmccinfo, sizeof(pgmccinfo)); + } + if (use_fec) { + struct pgm_fecinfo_t fecinfo; + fecinfo.block_size = rs_n; + fecinfo.proactive_packets = 0; + fecinfo.group_size = rs_k; + fecinfo.ondemand_parity_enabled = TRUE; + fecinfo.var_pktlen_enabled = FALSE; + pgm_setsockopt (sock, PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); + } +#endif + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = port ? port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + fprintf (stderr, "Creating GSI: %s\n", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + fprintf (stderr, "Binding PGM socket: %s\n", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int nonblocking = 1, + multicast_loop = use_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (sock, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (sock, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); + + if (!pgm_connect (sock, &pgm_err)) { + fprintf (stderr, "Connecting PGM socket: %s\n", pgm_err->message); + goto err_abort; + } + + puts ("Startup complete."); + return TRUE; + +err_abort: + if (NULL != sock) { + pgm_close (sock, FALSE); + sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + if (NULL != sock) { + pgm_close (sock, FALSE); + sock = NULL; + } + return FALSE; +} + +static +int +on_data ( + const void* restrict data, + const size_t len, + const struct pgm_sockaddr_t* restrict from + ) +{ +/* protect against non-null terminated strings */ + char buf[1024], tsi[PGM_TSISTRLEN]; + const size_t buflen = MIN(sizeof(buf) - 1, len); + strncpy (buf, (const char*)data, buflen); + buf[buflen] = '\0'; + pgm_tsi_print_r (&from->sa_addr, tsi, sizeof(tsi)); +#ifndef _MSC_VER + printf ("\"%s\" (%zu bytes from %s)\n", + buf, len, tsi); +#else +/* Microsoft CRT will crash on %zu */ + printf ("\"%s\" (%u bytes from %s)\n", + buf, (unsigned)len, tsi); +#endif + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/purinrecvcc.cc b/3rdparty/openpgm-svn-r1085/pgm/examples/purinrecvcc.cc new file mode 100644 index 0000000..72c1d23 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/purinrecvcc.cc @@ -0,0 +1,434 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * プリン PGM receiver + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#ifndef _WIN32 +# include +# include +# include +#else +# include "getopt.h" +#endif +#include + + +/* globals */ + +static int port = 0; +static const char* network = ""; +static bool use_multicast_loop = FALSE; +static int udp_encap_port = 0; + +static int max_tpdu = 1500; +static int sqns = 100; + +static bool use_fec = FALSE; +static int rs_k = 8; +static int rs_n = 255; + +static ip::pgm::endpoint* endpoint = NULL; +static ip::pgm::socket* sock = NULL; +static bool is_terminated = FALSE; + +#ifndef _WIN32 +static int terminate_pipe[2]; +static void on_signal (int); +#else +static HANDLE terminate_event; +static BOOL on_console_ctrl (DWORD); +#endif +#ifndef _MSC_VER +static void usage (const char*) __attribute__((__noreturn__)); +#else +static void usage (const char*); +#endif + +static bool on_startup (void); +static int on_data (const void*, size_t, const ip::pgm::endpoint&); + + +static void +usage ( + const char* bin + ) +{ + std::cerr << "Usage: " << bin << " [options]" << std::endl; + std::cerr << " -n : Multicast group or unicast IP address" << std::endl; + std::cerr << " -s : IP port" << std::endl; + std::cerr << " -p : Encapsulate PGM in UDP on IP port" << std::endl; + std::cerr << " -f : Enable FEC with either proactive or ondemand parity" << std::endl; + std::cerr << " -K : Configure Reed-Solomon code (n, k)" << std::endl; + std::cerr << " -N " << std::endl; + std::cerr << " -l : Enable multicast loopback and address sharing" << std::endl; + std::cerr << " -i : List available interfaces" << std::endl; + exit (EXIT_SUCCESS); +} + +int +main ( + int argc, + char* argv[] + ) +{ + cpgm::pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + +#if !defined(_WIN32) || defined(CONFIG_TARGET_WINE) + std::cout << "プリン プリン" << std::endl; +#else + std::wcout << L"プリン プリン" << std::endl; +#endif + + if (!cpgm::pgm_init (&pgm_err)) { + std::cerr << "Unable to start PGM engine: " << pgm_err->message << std::endl; + cpgm::pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = std::strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:f:K:N:lih")) != -1) + { + switch (c) { + case 'n': network = optarg; break; + case 's': port = atoi (optarg); break; + case 'p': udp_encap_port = atoi (optarg); break; + case 'f': use_fec = TRUE; break; + case 'K': rs_k = atoi (optarg); break; + case 'N': rs_n = atoi (optarg); break; + case 'l': use_multicast_loop = TRUE; break; + + case 'i': + cpgm::pgm_if_print_all(); + return EXIT_SUCCESS; + + case 'h': + case '?': usage (binary_name); + } + } + + if (use_fec && ( !rs_n || !rs_k )) { + std::cerr << "Invalid Reed-Solomon parameters RS(" << rs_n << "," << rs_k << ")." << std::endl; + usage (binary_name); + } + +/* setup signal handlers */ +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif +#ifndef _WIN32 + int e = pipe (terminate_pipe); + assert (0 == e); + signal (SIGINT, on_signal); + signal (SIGTERM, on_signal); +#else + terminate_event = CreateEvent (NULL, TRUE, FALSE, TEXT("TerminateEvent")); + SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); +#endif /* !_WIN32 */ + + if (!on_startup()) { + std::cerr << "Startup failed" << std::endl; + return EXIT_FAILURE; + } + +/* dispatch loop */ +#ifndef _WIN32 + int fds; + fd_set readfds; +#else + int n_handles = 3, recv_sock, pending_sock; + HANDLE waitHandles[ 3 ]; + DWORD dwTimeout, dwEvents; + WSAEVENT recvEvent, pendingEvent; + socklen_t socklen = sizeof(int); + + recvEvent = WSACreateEvent (); + sock->get_option (cpgm::PGM_RECV_SOCK, &recv_sock, &socklen); + WSAEventSelect (recv_sock, recvEvent, FD_READ); + pendingEvent = WSACreateEvent (); + sock->get_option (cpgm::PGM_PENDING_SOCK, &pending_sock, &socklen); + WSAEventSelect (pending_sock, pendingEvent, FD_READ); + + waitHandles[0] = terminate_event; + waitHandles[1] = recvEvent; + waitHandles[2] = pendingEvent; +#endif /* !_WIN32 */ + std::cout << "Entering PGM message loop ... " << std::endl; + do { + socklen_t optlen; + struct timeval tv; + char buffer[4096]; + size_t len; + ip::pgm::endpoint from; + const int status = sock->receive_from (buffer, + sizeof(buffer), + 0, + &len, + &from, + &pgm_err); + switch (status) { + case cpgm::PGM_IO_STATUS_NORMAL: + on_data (buffer, len, from); + break; + case cpgm::PGM_IO_STATUS_TIMER_PENDING: + optlen = sizeof (tv); + sock->get_option (cpgm::PGM_TIME_REMAIN, &tv, &optlen); + goto block; + case cpgm::PGM_IO_STATUS_RATE_LIMITED: + optlen = sizeof (tv); + sock->get_option (cpgm::PGM_RATE_REMAIN, &tv, &optlen); + case cpgm::PGM_IO_STATUS_WOULD_BLOCK: +/* select for next event */ +block: +#ifndef _WIN32 + fds = terminate_pipe[0] + 1; + FD_ZERO(&readfds); + FD_SET(terminate_pipe[0], &readfds); + pgm_select_info (sock->native(), &readfds, NULL, &fds); + fds = select (fds, &readfds, NULL, NULL, cpgm::PGM_IO_STATUS_WOULD_BLOCK == status ? NULL : &tv); +#else + dwTimeout = cpgm::PGM_IO_STATUS_WOULD_BLOCK == status ? INFINITE : (DWORD)((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + dwEvents = WaitForMultipleObjects (n_handles, waitHandles, FALSE, dwTimeout); + switch (dwEvents) { + case WAIT_OBJECT_0+1: WSAResetEvent (recvEvent); break; + case WAIT_OBJECT_0+2: WSAResetEvent (pendingEvent); break; + default: break; + } +#endif /* !_WIN32 */ + break; + + default: + if (pgm_err) { + std::cerr << pgm_err->message << std::endl; + cpgm::pgm_error_free (pgm_err); + pgm_err = NULL; + } + if (cpgm::PGM_IO_STATUS_ERROR == status) + break; + } + } while (!is_terminated); + + std::cout << "Message loop terminated, cleaning up." << std::endl; + +/* cleanup */ +#ifndef _WIN32 + close (terminate_pipe[0]); + close (terminate_pipe[1]); +#else + WSACloseEvent (recvEvent); + WSACloseEvent (pendingEvent); + CloseHandle (terminate_event); +#endif /* !_WIN32 */ + + if (sock) { + std::cout << "Closing PGM socket." << std::endl; + sock->close (TRUE); + sock = NULL; + } + + std::cout << "PGM engine shutdown." << std::endl; + cpgm::pgm_shutdown (); + std::cout << "finished." << std::endl; + return EXIT_SUCCESS; +} + +#ifndef _WIN32 +static +void +on_signal ( + int signum + ) +{ + std::cout << "on_signal (signum:" << signum << ")" << std::endl; + is_terminated = TRUE; + const char one = '1'; + const size_t writelen = write (terminate_pipe[1], &one, sizeof(one)); + assert (sizeof(one) == writelen); +} +#else +static +BOOL +on_console_ctrl ( + DWORD dwCtrlType + ) +{ + std::cout << "on_console_ctrl (dwCtrlType:" << dwCtrlType << ")" << std::endl; + is_terminated = TRUE; + SetEvent (terminate_event); + return TRUE; +} +#endif /* !_WIN32 */ + +static +bool +on_startup (void) +{ + struct cpgm::pgm_addrinfo_t* res = NULL; + cpgm::pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + +/* parse network parameter into PGM socket address structure */ + if (!pgm_getaddrinfo (network, NULL, &res, &pgm_err)) { + std::cerr << "Parsing network parameter: " << pgm_err->message << std::endl; + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + sock = new ip::pgm::socket(); + + if (udp_encap_port) { + std::cout << "Create PGM/UDP socket." << std::endl; + if (!sock->open (sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + std::cerr << "Creating PGM/UDP socket: " << pgm_err->message << std::endl; + goto err_abort; + } + sock->set_option (cpgm::PGM_UDP_ENCAP_UCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + sock->set_option (cpgm::PGM_UDP_ENCAP_MCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + } else { + std::cout << "Create PGM/IP socket." << std::endl; + if (!sock->open (sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + std::cerr << "Creating PGM/IP socket: " << pgm_err->message << std::endl; + goto err_abort; + } + } + + { +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + sock->set_option (cpgm::PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + } + + cpgm::pgm_drop_superuser(); + + { +/* set PGM parameters */ + const int recv_only = 1, + passive = 0, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_secs (2), + nak_rdata_ivl = pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + sock->set_option (cpgm::PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + sock->set_option (cpgm::PGM_PASSIVE, &passive, sizeof(passive)); + sock->set_option (cpgm::PGM_MTU, &max_tpdu, sizeof(max_tpdu)); + sock->set_option (cpgm::PGM_RXW_SQNS, &sqns, sizeof(sqns)); + sock->set_option (cpgm::PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + sock->set_option (cpgm::PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + sock->set_option (cpgm::PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + sock->set_option (cpgm::PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + sock->set_option (cpgm::PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + sock->set_option (cpgm::PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + sock->set_option (cpgm::PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + } + if (use_fec) { + struct cpgm::pgm_fecinfo_t fecinfo; + fecinfo.block_size = rs_n; + fecinfo.proactive_packets = 0; + fecinfo.group_size = rs_k; + fecinfo.ondemand_parity_enabled = TRUE; + fecinfo.var_pktlen_enabled = FALSE; + sock->set_option (cpgm::PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); + } + +/* create global session identifier */ + endpoint = new ip::pgm::endpoint (DEFAULT_DATA_DESTINATION_PORT); + +/* assign socket to specified address */ + if (!sock->bind (*endpoint, &pgm_err)) { + std::cerr << "Binding PGM socket: " << pgm_err->message << std::endl; + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + sock->set_option (cpgm::PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + sock->set_option (cpgm::PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + + { +/* set IP parameters */ + const int nonblocking = 1, + multicast_loop = use_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + sock->set_option (cpgm::PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + sock->set_option (cpgm::PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + sock->set_option (cpgm::PGM_TOS, &dscp, sizeof(dscp)); + sock->set_option (cpgm::PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); + } + + if (!sock->connect (&pgm_err)) { + std::cerr << "Connecting PGM socket: " << pgm_err->message << std::endl; + goto err_abort; + } + + std::cout << "Startup complete." << std::endl; + return TRUE; + +err_abort: + if (NULL != sock) { + sock->close (FALSE); + sock = NULL; + } + if (NULL != res) { + cpgm::pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + cpgm::pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +static +int +on_data ( + const void* data, + size_t len, + const ip::pgm::endpoint& from + ) +{ +/* protect against non-null terminated strings */ + char buf[1024], tsi[PGM_TSISTRLEN]; + const size_t buflen = MIN(sizeof(buf) - 1, len); + strncpy (buf, (char*)data, buflen); + buf[buflen] = '\0'; + cpgm::pgm_tsi_print_r (from.address(), tsi, sizeof(tsi)); + std::cout << "\"" << buf << "\" (" << len << " bytes from " << tsi << ")" << std::endl; + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/purinsend.c b/3rdparty/openpgm-svn-r1085/pgm/examples/purinsend.c new file mode 100644 index 0000000..811c14c --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/purinsend.c @@ -0,0 +1,280 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * プリン PGM sender + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#ifndef _WIN32 +# include +#else +# include "getopt.h" +# define snprintf _snprintf +#endif +#include + + +/* globals */ + +static int port = 0; +static const char* network = ""; +static bool use_multicast_loop = FALSE; +static int udp_encap_port = 0; + +static int max_tpdu = 1500; +static int max_rte = 400*1000; /* very conservative rate, 2.5mb/s */ +static int sqns = 100; + +static bool use_fec = FALSE; +static int rs_k = 8; +static int rs_n = 255; + +static pgm_sock_t* sock = NULL; + +#ifndef _MSC_VER +static void usage (const char*) __attribute__((__noreturn__)); +#else +static void usage (const char*); +#endif +static bool create_sock (void); + + +static void +usage ( + const char* bin + ) +{ + fprintf (stderr, "Usage: %s [options] message\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -r : Regulate to rate bytes per second\n"); + fprintf (stderr, " -f : Enable FEC with either proactive or ondemand parity\n"); + fprintf (stderr, " -K : Configure Reed-Solomon code (n, k)\n"); + fprintf (stderr, " -N \n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); + fprintf (stderr, " -i : List available interfaces\n"); + exit (EXIT_SUCCESS); +} + +int +main ( + int argc, + char *argv[] + ) +{ + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + + if (!pgm_init (&pgm_err)) { + fprintf (stderr, "Unable to start PGM engine: %s\n", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:r:f:K:N:lih")) != -1) + { + switch (c) { + case 'n': network = optarg; break; + case 's': port = atoi (optarg); break; + case 'p': udp_encap_port = atoi (optarg); break; + case 'r': max_rte = atoi (optarg); break; + case 'f': use_fec = TRUE; break; + case 'K': rs_k = atoi (optarg); break; + case 'N': rs_n = atoi (optarg); break; + + case 'l': use_multicast_loop = TRUE; break; + + case 'i': + pgm_if_print_all(); + return EXIT_SUCCESS; + + case 'h': + case '?': + usage (binary_name); + } + } + + if (use_fec && ( !rs_n || !rs_k )) { + fprintf (stderr, "Invalid Reed-Solomon parameters RS(%d,%d).\n", rs_n, rs_k); + usage (binary_name); + } + + if (create_sock()) + { + while (optind < argc) { + const int status = pgm_send (sock, argv[optind], strlen (argv[optind]) + 1, NULL); + if (PGM_IO_STATUS_NORMAL != status) { + fprintf (stderr, "pgm_send() failed.\n"); + } + optind++; + } + } + +/* cleanup */ + if (sock) { + pgm_close (sock, TRUE); + sock = NULL; + } + pgm_shutdown(); + return EXIT_SUCCESS; +} + +static +bool +create_sock (void) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + +/* parse network parameter into PGM socket address structure */ + if (!pgm_getaddrinfo (network, NULL, &res, &pgm_err)) { + fprintf (stderr, "Parsing network parameter: %s\n", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + if (udp_encap_port) { + if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + fprintf (stderr, "Creating PGM/UDP socket: %s\n", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (sock, PGM_UDP_ENCAP_UCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + pgm_setsockopt (sock, PGM_UDP_ENCAP_MCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + } else { + if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + fprintf (stderr, "Creating PGM/IP socket: %s\n", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int send_only = 1, + ambient_spm = pgm_secs (30), + heartbeat_spm[] = { pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (1300), + pgm_secs (7), + pgm_secs (16), + pgm_secs (25), + pgm_secs (30) }; + + pgm_setsockopt (sock, PGM_SEND_ONLY, &send_only, sizeof(send_only)); + pgm_setsockopt (sock, PGM_MTU, &max_tpdu, sizeof(max_tpdu)); + pgm_setsockopt (sock, PGM_TXW_SQNS, &sqns, sizeof(sqns)); + pgm_setsockopt (sock, PGM_TXW_MAX_RTE, &max_rte, sizeof(max_rte)); + pgm_setsockopt (sock, PGM_AMBIENT_SPM, &ambient_spm, sizeof(ambient_spm)); + pgm_setsockopt (sock, PGM_HEARTBEAT_SPM, &heartbeat_spm, sizeof(heartbeat_spm)); + if (use_fec) { + struct pgm_fecinfo_t fecinfo; + fecinfo.block_size = rs_n; + fecinfo.proactive_packets = 0; + fecinfo.group_size = rs_k; + fecinfo.ondemand_parity_enabled = TRUE; + fecinfo.var_pktlen_enabled = TRUE; + pgm_setsockopt (sock, PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); + } + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = port ? port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + fprintf (stderr, "Creating GSI: %s\n", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + fprintf (stderr, "Binding PGM socket: %s\n", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int blocking = 0, + multicast_loop = use_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (sock, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (sock, PGM_NOBLOCK, &blocking, sizeof(blocking)); + + if (!pgm_connect (sock, &pgm_err)) { + fprintf (stderr, "Connecting PGM socket: %s\n", pgm_err->message); + goto err_abort; + } + + return TRUE; + +err_abort: + if (NULL != sock) { + pgm_close (sock, FALSE); + sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/purinsendcc.cc b/3rdparty/openpgm-svn-r1085/pgm/examples/purinsendcc.cc new file mode 100644 index 0000000..fe3c430 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/purinsendcc.cc @@ -0,0 +1,269 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * プリン PGM sender + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#ifndef _WIN32 +# include +# include +#else +# include "getopt.h" +#endif +#include + + +/* globals */ + +static int port = 0; +static const char* network = ""; +static bool use_multicast_loop = FALSE; +static int udp_encap_port = 0; + +static int max_tpdu = 1500; +static int max_rte = 400*1000; /* very conservative rate, 2.5mb/s */ +static int sqns = 100; + +static bool use_fec = FALSE; +static int rs_k = 8; +static int rs_n = 255; + +static ip::pgm::endpoint* endpoint = NULL; +static ip::pgm::socket* sock = NULL; + +#ifndef _MSC_VER +static void usage (const char*) __attribute__((__noreturn__)); +#else +static void usage (const char*); +#endif +static bool create_sock (void); + + +static void +usage ( + const char* bin + ) +{ + std::cerr << "Usage: " << bin << " [options] message" << std::endl; + std::cerr << " -n : Multicast group or unicast IP address" << std::endl; + std::cerr << " -s : IP port" << std::endl; + std::cerr << " -p : Encapsulate PGM in UDP on IP port" << std::endl; + std::cerr << " -r : Regulate to rate bytes per second" << std::endl; + std::cerr << " -f : Enable FEC with either proactive or ondemand parity" << std::endl; + std::cerr << " -K : Configure Reed-Solomon code (n, k)" << std::endl; + std::cerr << " -N " << std::endl; + std::cerr << " -l : Enable multicast loopback and address sharing" << std::endl; + std::cerr << " -i : List available interfaces" << std::endl; + exit (EXIT_SUCCESS); +} + +int +main ( + int argc, + char *argv[] + ) +{ + cpgm::pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + + if (!cpgm::pgm_init (&pgm_err)) { + std::cerr << "Unable to start PGM engine: " << pgm_err->message << std::endl; + cpgm::pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:r:f:K:N:lih")) != -1) + { + switch (c) { + case 'n': network = optarg; break; + case 's': port = atoi (optarg); break; + case 'p': udp_encap_port = atoi (optarg); break; + case 'r': max_rte = atoi (optarg); break; + case 'f': use_fec = TRUE; break; + case 'K': rs_k = atoi (optarg); break; + case 'N': rs_n = atoi (optarg); break; + + case 'l': use_multicast_loop = TRUE; break; + + case 'i': + cpgm::pgm_if_print_all(); + return EXIT_SUCCESS; + + case 'h': + case '?': + usage (binary_name); + } + } + + if (use_fec && ( !rs_n || !rs_k )) { + std::cerr << "Invalid Reed-Solomon parameters RS(" << rs_n << "," << rs_k << ")." << std::endl; + usage (binary_name); + } + + if (create_sock()) + { + while (optind < argc) { + const int status = sock->send (argv[optind], strlen (argv[optind]) + 1, NULL); + if (cpgm::PGM_IO_STATUS_NORMAL != status) { + std::cerr << "pgm_send() failed.." << std::endl; + } + optind++; + } + } + +/* cleanup */ + if (sock) { + sock->close (TRUE); + sock = NULL; + } + cpgm::pgm_shutdown(); + return EXIT_SUCCESS; +} + +static +bool +create_sock (void) +{ + struct cpgm::pgm_addrinfo_t* res = NULL; + cpgm::pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + +/* parse network parameter into PGM socket address structure */ + if (!cpgm::pgm_getaddrinfo (network, NULL, &res, &pgm_err)) { + std::cerr << "Parsing network parameter: " << pgm_err->message << std::endl; + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + sock = new ip::pgm::socket(); + + if (udp_encap_port) { + if (!sock->open (sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + std::cerr << "Creating PGM/UDP socket: " << pgm_err->message << std::endl; + goto err_abort; + } + sock->set_option (cpgm::PGM_UDP_ENCAP_UCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + sock->set_option (cpgm::PGM_UDP_ENCAP_MCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + } else { + if (!sock->open (sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + std::cerr << "Creating PGM/IP socket: " << pgm_err->message << std::endl; + goto err_abort; + } + } + + { +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + sock->set_option (cpgm::PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + } + + cpgm::pgm_drop_superuser(); + + { +/* set PGM parameters */ + const int send_only = 1, + ambient_spm = pgm_secs (30), + heartbeat_spm[] = { pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (1300), + pgm_secs (7), + pgm_secs (16), + pgm_secs (25), + pgm_secs (30) }; + + sock->set_option (cpgm::PGM_SEND_ONLY, &send_only, sizeof(send_only)); + sock->set_option (cpgm::PGM_MTU, &max_tpdu, sizeof(max_tpdu)); + sock->set_option (cpgm::PGM_TXW_SQNS, &sqns, sizeof(sqns)); + sock->set_option (cpgm::PGM_TXW_MAX_RTE, &max_rte, sizeof(max_rte)); + sock->set_option (cpgm::PGM_AMBIENT_SPM, &ambient_spm, sizeof(ambient_spm)); + sock->set_option (cpgm::PGM_HEARTBEAT_SPM, &heartbeat_spm, sizeof(heartbeat_spm)); + } + if (use_fec) { + struct cpgm::pgm_fecinfo_t fecinfo; + fecinfo.block_size = rs_n; + fecinfo.proactive_packets = 0; + fecinfo.group_size = rs_k; + fecinfo.ondemand_parity_enabled = TRUE; + fecinfo.var_pktlen_enabled = TRUE; + sock->set_option (cpgm::PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); + } + +/* create global session identifier */ + endpoint = new ip::pgm::endpoint (DEFAULT_DATA_DESTINATION_PORT); + +/* assign socket to specified address */ + if (!sock->bind (*endpoint, &pgm_err)) { + std::cerr << "Binding PGM socket: " << pgm_err->message << std::endl; + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + sock->set_option (cpgm::PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + sock->set_option (cpgm::PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + cpgm::pgm_freeaddrinfo (res); + + { +/* set IP parameters */ + const int blocking = 0, + multicast_loop = use_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + sock->set_option (cpgm::PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + sock->set_option (cpgm::PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + sock->set_option (cpgm::PGM_TOS, &dscp, sizeof(dscp)); + sock->set_option (cpgm::PGM_NOBLOCK, &blocking, sizeof(blocking)); + } + + if (!sock->connect (&pgm_err)) { + fprintf (stderr, "Connecting PGM socket: %s\n", pgm_err->message); + goto err_abort; + } + + return TRUE; + +err_abort: + if (NULL != sock) { + sock->close (FALSE); + sock = NULL; + } + if (NULL != res) { + cpgm::pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + cpgm::pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/shortcakerecv.c b/3rdparty/openpgm-svn-r1085/pgm/examples/shortcakerecv.c new file mode 100644 index 0000000..87ce8dc --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/shortcakerecv.c @@ -0,0 +1,430 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * ショートケーキ PGM receiver + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#ifndef _WIN32 +# include +#else +# include "getopt.h" +# define snprintf _snprintf +#endif +#include + +#include "async.h" + + +/* globals */ + +static int port = 0; +static const char* network = ""; +static bool use_multicast_loop = FALSE; +static int udp_encap_port = 0; + +static int max_tpdu = 1500; +static int sqns = 100; + +static bool use_fec = FALSE; +static int rs_k = 8; +static int rs_n = 255; + +static pgm_sock_t* sock = NULL; +static async_t* async = NULL; +static bool is_terminated = FALSE; + +#ifndef _WIN32 +static int terminate_pipe[2]; +static void on_signal (int); +#else +static HANDLE terminate_event; +static BOOL on_console_ctrl (DWORD); +#endif +#ifndef _MSC_VER +static void usage (const char*) __attribute__((__noreturn__)); +#else +static void usage (const char*); +#endif + +static bool on_startup (void); +static int on_data (const void*restrict, const size_t, const struct pgm_sockaddr_t*restrict); + + +static void +usage ( + const char* bin + ) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -f : Enable FEC with either proactive or ondemand parity\n"); + fprintf (stderr, " -K : Configure Reed-Solomon code (n, k)\n"); + fprintf (stderr, " -N \n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); + fprintf (stderr, " -i : List available interfaces\n"); + exit (EXIT_SUCCESS); +} + +int +main ( + int argc, + char* argv[] + ) +{ + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + +#if !defined(_WIN32) || defined(CONFIG_TARGET_WINE) + puts ("いちごのショートケーキ"); +#else + _putws (L"いちごのショートケーキ"); +#endif + + if (!pgm_init (&pgm_err)) { + fprintf (stderr, "Unable to start PGM engine: %s\n", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:f:K:N:lih")) != -1) + { + switch (c) { + case 'n': network = optarg; break; + case 's': port = atoi (optarg); break; + case 'p': udp_encap_port = atoi (optarg); break; + case 'f': use_fec = TRUE; break; + case 'K': rs_k = atoi (optarg); break; + case 'N': rs_n = atoi (optarg); break; + case 'l': use_multicast_loop = TRUE; break; + + case 'i': + pgm_if_print_all(); + return EXIT_SUCCESS; + + case 'h': + case '?': usage (binary_name); + } + } + + if (use_fec && ( !rs_n || !rs_k )) { + fprintf (stderr, "Invalid Reed-Solomon parameters RS(%d,%d).\n", rs_n, rs_k); + usage (binary_name); + } + +/* setup signal handlers */ +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif +#ifndef _WIN32 + int e = pipe (terminate_pipe); + assert (0 == e); + signal (SIGINT, on_signal); + signal (SIGTERM, on_signal); +#else + terminate_event = CreateEvent (NULL, TRUE, FALSE, TEXT("TerminateEvent")); + SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); +#endif /* !_WIN32 */ + + if (!on_startup()) { + fprintf (stderr, "Startup failed\n"); + return EXIT_FAILURE; + } + +/* dispatch loop */ +#ifndef _WIN32 + int fds, read_fd = async_get_fd (async); + fd_set readfds; +#else + int n_handles = 2; + HANDLE waitHandles[ 2 ]; + DWORD dwEvents; + WSAEVENT recvEvent; + + recvEvent = async_get_event (async); + + waitHandles[0] = terminate_event; + waitHandles[1] = recvEvent; +#endif /* !_WIN32 */ + puts ("Entering PGM message loop ... "); + do { + char buffer[4096]; + struct pgm_sockaddr_t from; + socklen_t fromlen = sizeof (from); + const ssize_t len = async_recvfrom (async, + buffer, + sizeof(buffer), + &from, + &fromlen); + if (len >= 0) { + on_data (buffer, len, &from); + } else { +#ifndef _WIN32 + fds = MAX(terminate_pipe[0], read_fd) + 1; + FD_ZERO(&readfds); + FD_SET(terminate_pipe[0], &readfds); + FD_SET(read_fd, &readfds); + fds = select (fds, &readfds, NULL, NULL, NULL); +#else + dwEvents = WaitForMultipleObjects (n_handles, waitHandles, FALSE, INFINITE); + switch (dwEvents) { + case WAIT_OBJECT_0+1: ResetEvent (recvEvent); break; + default: break; + } +#endif /* _WIN32 */ + } + } while (!is_terminated); + + puts ("Message loop terminated, cleaning up."); + +/* cleanup */ +#ifndef _WIN32 + close (terminate_pipe[0]); + close (terminate_pipe[1]); +#else + CloseHandle (terminate_event); +#endif /* !_WIN32 */ + + if (async) { + puts ("Destroying asynchronous queue."); + async_destroy (async); + async = NULL; + } + + if (sock) { + puts ("Closing PGM socket."); + pgm_close (sock, TRUE); + sock = NULL; + } + + puts ("PGM engine shutdown."); + pgm_shutdown (); + puts ("finished."); + return EXIT_SUCCESS; +} + +#ifndef _WIN32 +static +void +on_signal ( + int signum + ) +{ + printf ("on_signal (signum:%d)\n", signum); + is_terminated = TRUE; + const char one = '1'; + const size_t writelen = write (terminate_pipe[1], &one, sizeof(one)); + assert (sizeof(one) == writelen); +} +#else +static +BOOL +on_console_ctrl ( + DWORD dwCtrlType + ) +{ + printf ("on_console_ctrl (dwCtrlType:%lu)\n", (unsigned long)dwCtrlType); + is_terminated = TRUE; + SetEvent (terminate_event); + return TRUE; +} +#endif /* !_WIN32 */ + +static +bool +on_startup (void) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + +/* parse network parameter into PGM socket address structure */ + if (!pgm_getaddrinfo (network, NULL, &res, &pgm_err)) { + fprintf (stderr, "Parsing network parameter: %s\n", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + puts ("Create PGM socket."); + if (udp_encap_port) { + if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + fprintf (stderr, "Creating PGM/UDP socket: %s\n", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (sock, PGM_UDP_ENCAP_UCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + pgm_setsockopt (sock, PGM_UDP_ENCAP_MCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + } else { + if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + fprintf (stderr, "Creating PGM/IP socket: %s\n", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int recv_only = 1, + passive = 0, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_secs (2), + nak_rdata_ivl = pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + pgm_setsockopt (sock, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + pgm_setsockopt (sock, PGM_PASSIVE, &passive, sizeof(passive)); + pgm_setsockopt (sock, PGM_MTU, &max_tpdu, sizeof(max_tpdu)); + pgm_setsockopt (sock, PGM_RXW_SQNS, &sqns, sizeof(sqns)); + pgm_setsockopt (sock, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + pgm_setsockopt (sock, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + pgm_setsockopt (sock, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + pgm_setsockopt (sock, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + pgm_setsockopt (sock, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + pgm_setsockopt (sock, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + pgm_setsockopt (sock, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + if (use_fec) { + struct pgm_fecinfo_t fecinfo; + fecinfo.block_size = rs_n; + fecinfo.proactive_packets = 0; + fecinfo.group_size = rs_k; + fecinfo.ondemand_parity_enabled = TRUE; + fecinfo.var_pktlen_enabled = TRUE; + pgm_setsockopt (sock, PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); + } + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = port ? port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + fprintf (stderr, "Creating GSI: %s\n", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + fprintf (stderr, "Binding PGM socket: %s\n", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int nonblocking = 1, + multicast_loop = use_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (sock, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (sock, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); + + if (!pgm_connect (sock, &pgm_err)) { + fprintf (stderr, "Connecting PGM socket: %s\n", pgm_err->message); + goto err_abort; + } + +/* wrap bound socket in asynchronous queue */ + if (0 != async_create (&async, sock)) { + fprintf (stderr, "Creating asynchronous queue failed: %s\n", strerror(errno)); + goto err_abort; + } + + puts ("Startup complete."); + return TRUE; + +err_abort: + if (NULL != sock) { + pgm_close (sock, FALSE); + sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +static +int +on_data ( + const void* restrict data, + const size_t len, + const struct pgm_sockaddr_t* restrict from + ) +{ +/* protect against non-null terminated strings */ + char buf[1024], tsi[PGM_TSISTRLEN]; + const size_t buflen = MIN(sizeof(buf) - 1, len); + strncpy (buf, (const char*)data, buflen); + buf[buflen] = '\0'; + pgm_tsi_print_r (&from->sa_addr, tsi, sizeof(tsi)); +#ifndef _MSC_VER + printf ("\"%s\" (%zu bytes from %s)\n", + buf, len, tsi); +#else +/* Microsoft CRT will crash on %zu */ + printf ("\"%s\" (%u bytes from %s)\n", + buf, (unsigned)len, tsi); +#endif + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/snonblocksyncrecv.c b/3rdparty/openpgm-svn-r1085/pgm/examples/snonblocksyncrecv.c new file mode 100644 index 0000000..434f72d --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/examples/snonblocksyncrecv.c @@ -0,0 +1,435 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Simple PGM receiver: select based non-blocking synchronous receiver. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef G_OS_UNIX +# include +# include +# include +# include +#endif +#include + +/* example dependencies */ +#include +#include + + +/* typedefs */ + +/* globals */ + +static int g_port = 0; +static const char* g_network = ""; +static gboolean g_multicast_loop = FALSE; +static int g_udp_encap_port = 0; + +static int g_max_tpdu = 1500; +static int g_sqns = 100; + +static pgm_sock_t* g_sock = NULL; +static gboolean g_quit; + +#ifdef G_OS_UNIX +static int g_quit_pipe[2]; +static void on_signal (int); +#else +static HANDLE g_quit_event; +static BOOL on_console_ctrl (DWORD); +#endif + +static gboolean on_startup (void); + +static int on_data (gconstpointer, size_t, struct pgm_sockaddr_t*); + + +G_GNUC_NORETURN static +void +usage ( + const char* bin + ) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); + exit (1); +} + +int +main ( + int argc, + char* argv[] + ) +{ + int e; + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + + log_init (); + g_message ("snonblocksyncrecv"); + + if (!pgm_init (&pgm_err)) { + g_error ("Unable to start PGM engine: %s", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:lh")) != -1) + { + switch (c) { + case 'n': g_network = optarg; break; + case 's': g_port = atoi (optarg); break; + case 'p': g_udp_encap_port = atoi (optarg); break; + case 'l': g_multicast_loop = TRUE; break; + + case 'h': + case '?': usage (binary_name); + } + } + + g_quit = FALSE; + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif +#ifdef G_OS_UNIX + e = pipe (g_quit_pipe); + g_assert (0 == e); + signal (SIGINT, on_signal); + signal (SIGTERM, on_signal); +#else + g_quit_event = CreateEvent (NULL, TRUE, FALSE, TEXT("QuitEvent")); + SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); +#endif /* !G_OS_UNIX */ + + if (!on_startup()) { + g_error ("startup failed"); + exit(1); + } + +/* dispatch loop */ +#ifdef G_OS_UNIX + int fds; + fd_set readfds; +#else + int n_handles = 3; + HANDLE waitHandles[ n_handles ]; + DWORD dwTimeout, dwEvents; + WSAEVENT recvEvent, pendingEvent; + + recvEvent = WSACreateEvent (); + WSAEventSelect (pgm_transport_get_recv_fd (g_transport), recvEvent, FD_READ); + pendingEvent = WSACreateEvent (); + WSAEventSelect (pgm_transport_get_pending_fd (g_transport), pendingEvent, FD_READ); + + waitHandles[0] = g_quit_event; + waitHandles[1] = recvEvent; + waitHandles[2] = pendingEvent; +#endif /* !G_OS_UNIX */ + g_message ("entering PGM message loop ... "); + do { + struct timeval tv; + char buffer[4096]; + size_t len; + struct pgm_sockaddr_t from; + socklen_t fromlen = sizeof(from); + const int status = pgm_recvfrom (g_sock, + buffer, + sizeof(buffer), + 0, + &len, + &from, + &fromlen, + &pgm_err); + switch (status) { + case PGM_IO_STATUS_NORMAL: + on_data (buffer, len, &from); + break; + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (g_sock, PGM_TIME_REMAIN, &tv, &optlen); + } + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (g_sock, PGM_RATE_REMAIN, &tv, &optlen); + } +/* fall through */ + case PGM_IO_STATUS_WOULD_BLOCK: +/* select for next event */ +block: +#ifdef G_OS_UNIX + fds = g_quit_pipe[0] + 1; + FD_ZERO(&readfds); + FD_SET(g_quit_pipe[0], &readfds); + pgm_select_info (g_sock, &readfds, NULL, &fds); + fds = select (fds, &readfds, NULL, NULL, PGM_IO_STATUS_WOULD_BLOCK == status ? NULL : &tv); +#else + dwTimeout = PGM_IO_STATUS_WOULD_BLOCK == status ? INFINITE : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + dwEvents = WaitForMultipleObjects (n_handles, waitHandles, FALSE, dwTimeout); + switch (dwEvents) { + case WAIT_OBJECT_0+1: WSAResetEvent (recvEvent); break; + case WAIT_OBJECT_0+2: WSAResetEvent (pendingEvent); break; + default: break; + } +#endif /* !G_OS_UNIX */ + break; + + default: + if (pgm_err) { + g_warning ("%s", pgm_err->message); + pgm_error_free (pgm_err); + pgm_err = NULL; + } + if (PGM_IO_STATUS_ERROR == status) + break; + } + } while (!g_quit); + + g_message ("message loop terminated, cleaning up."); + +/* cleanup */ +#ifdef G_OS_UNIX + close (g_quit_pipe[0]); + close (g_quit_pipe[1]); +#else + WSACloseEvent (recvEvent); + WSACloseEvent (pendingEvent); + CloseHandle (g_quit_event); +#endif /* !G_OS_UNIX */ + + if (g_sock) { + g_message ("closing PGM socket."); + pgm_close (g_sock, TRUE); + g_sock = NULL; + } + + g_message ("PGM engine shutdown."); + pgm_shutdown (); + g_message ("finished."); + return EXIT_SUCCESS; +} + +#ifdef G_OS_UNIX +static +void +on_signal ( + int signum + ) +{ + g_message ("on_signal (signum:%d)", signum); + g_quit = TRUE; + const char one = '1'; + const size_t writelen = write (g_quit_pipe[1], &one, sizeof(one)); + g_assert (sizeof(one) == writelen); +} +#else +static +BOOL +on_console_ctrl ( + DWORD dwCtrlType + ) +{ + g_message ("on_console_ctrl (dwCtrlType:%lu)", (unsigned long)dwCtrlType); + SetEvent (g_quit_event); + return TRUE; +} +#endif /* !G_OS_UNIX */ + +static +gboolean +on_startup (void) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + + g_message ("startup."); + +/* parse network parameter into transport address structure */ + if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { + g_error ("parsing network parameter: %s", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + if (g_udp_encap_port) { + g_message ("create PGM/UDP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (g_sock, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + pgm_setsockopt (g_sock, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + } else { + g_message ("create PGM/IP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (g_sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int recv_only = 1, + passive = 0, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_secs (2), + nak_rdata_ivl = pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + pgm_setsockopt (g_sock, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + pgm_setsockopt (g_sock, PGM_PASSIVE, &passive, sizeof(passive)); + pgm_setsockopt (g_sock, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); + pgm_setsockopt (g_sock, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); + pgm_setsockopt (g_sock, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + pgm_setsockopt (g_sock, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + pgm_setsockopt (g_sock, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + pgm_setsockopt (g_sock, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + pgm_setsockopt (g_sock, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + g_error ("creating GSI: %s", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (g_sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + g_error ("binding PGM socket: %s", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (g_sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (g_sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int nonblocking = 1, + multicast_loop = g_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (g_sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (g_sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (g_sock, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (g_sock, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); + + if (!pgm_connect (g_sock, &pgm_err)) { + g_error ("connecting PGM socket: %s", pgm_err->message); + goto err_abort; + } + + g_message ("startup complete."); + return TRUE; + +err_abort: + if (NULL != g_sock) { + pgm_close (g_sock, FALSE); + g_sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +static +int +on_data ( + gconstpointer data, + size_t len, + struct pgm_sockaddr_t* from + ) +{ +/* protect against non-null terminated strings */ + char buf[1024], tsi[PGM_TSISTRLEN]; + const size_t buflen = MIN(sizeof(buf) - 1, len); + strncpy (buf, (const char*)data, buflen); + buf[buflen] = '\0'; + pgm_tsi_print_r (&from->sa_addr, tsi, sizeof(tsi)); + + g_message ("\"%s\" (%u bytes from %s)", + buf, + (unsigned)len, + tsi); + + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/fec-block.txt b/3rdparty/openpgm-svn-r1085/pgm/fec-block.txt new file mode 100644 index 0000000..8278fb6 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/fec-block.txt @@ -0,0 +1,66 @@ +from rfc5052 +------------ + + +step one: + + Input: + + B -- Maximum Source Block Length, i.e., the maximum number of source + symbols per source block + + L -- Transfer Length in octets + + E -- Encoding Symbol Length in octets + + Output: + + T -- the number of source symbols in the object. + + N -- the number of source blocks into which the object shall be + partitioned. + + Algorithm: + + 1. The number of source symbols in the transport object is computed + as T = ceil[L/E]. + + 2. The transport object shall be partitioned into N = ceil[T/B] + source blocks. + + +B = maximum TPDU - IP header ( - UDP header ) - PGM header +L = APDU length (pgm_transport_send length parameter). +E = 1 (fixed). + +T = ceil( L / E ) = ceil( L / 1 ) = L +N = ceil( T / B ) = ceil( L / B ) + +step two: + + Input: + + T -- the number of source symbols in the object. + + N -- the number of source blocks into which the object is + partitioned. + + Output: + + I -- the number of larger source blocks. + + A_large -- the length of each of the larger source blocks in + symbols. + + A_small -- the length of each of the smaller source blocks in + symbols. + + Algorithm: + + 1. A_large = ceil[T/N] + + 2. A_small = floor[T/N] + + 3. I = T - A_small * N + + diff --git a/3rdparty/openpgm-svn-r1085/pgm/fec.txt b/3rdparty/openpgm-svn-r1085/pgm/fec.txt new file mode 100644 index 0000000..b83590d --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/fec.txt @@ -0,0 +1,77 @@ +pkt:k=1 + +non-parity + +rs eqn: + +n = 255 +k = 2t +255 = 2k +k = 128 +=> 2t = 128 + +-------------------------------------------------------------------------------- + +1456, 1442 + +pkt:k=2 ( 2 data packets ) + +1 packet loss: + +pkt:h=1 +pkt:n=3 + +rs eqn: + +n = 255 +# reed-solomon codes = tsdu / n +k = 2 4 6 ... 254 +=> 2t = 1 2 3 127 (k/2) + +255 = k + (k/2) +510 = 2k + k +510 = 3k +170 = k +=> 2t = 85 + +2 packet loss: + +pkt:h=2 pkts +pkt:n=4 pkts + +-------------------------------------------------------------------------------- + +pkt:k=4 ( 4 data packets ) + +1 packet loss: + +255 = k + (k/4) +k = 51 +2t = 13 (rounded up) + +-------------------------------------------------------------------------------- + +pkt:k=8 ( 8 data packets ) + +1 packet loss: + +255 = k + (k/8) +k = 29 +2t = 4 (rounded up) + +-------------------------------------------------------------------------------- + +pkt:k=16 ( 16 data packets ) + +1 packet loss: + +255 = k + (k/16) +k = 15 +2t = 1 (invalid?) + +2 packet loss: + +255 = k + (2k/16) +k = 29 +2t = 4 + diff --git a/3rdparty/openpgm-svn-r1085/pgm/galois_generator.pl b/3rdparty/openpgm-svn-r1085/pgm/galois_generator.pl new file mode 100755 index 0000000..b3f531d --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/galois_generator.pl @@ -0,0 +1,139 @@ +#!/usr/bin/perl +# +# Galois field table generator. +# +# Copyright (c) 2006-2010 Miru Limited. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +use strict; + +my $GF_ELEMENT_BYTES = 1; +my $GF_ELEMENT_BITS = 8 * $GF_ELEMENT_BYTES; +my $GF_NO_ELEMENTS = 1 << $GF_ELEMENT_BITS; +my $GF_MAX = $GF_NO_ELEMENTS - 1; + +my $GF_GENERATOR = 0x11d; + +my @gflog; +my @gfantilog; + +my $j = 1; + +for (my $i = 0; $i < $GF_MAX; $i++) +{ + $gflog[ $j ] = $i; + $gfantilog[ $i ] = $j; + + $j <<= 1; + if ($j & $GF_NO_ELEMENTS) { + $j ^= $GF_GENERATOR; + } +} + +$gflog[ 0 ] = $GF_MAX; +$gfantilog[ $GF_MAX ] = 0; + +print< + + +/* globals */ + +const pgm_gf8_t pgm_gflog[PGM_GF_NO_ELEMENTS] = +{ +MOO + +# print out y = log₂(x) table +for (my $i = 0; $i < $GF_NO_ELEMENTS; $i++) +{ + print "\t" if ($i % 8 == 0); + print sprintf("0x%2.2x", $gflog[ $i ]); + print ',' unless ($i == $GF_MAX); + print ( (($i % 8) == 7) ? "\n" : ' ' ); +} + +print<= $GF_MAX) ? $gfantilog[ $sum - $GF_MAX ] : $gfantilog[ $sum ]; +} + +# print out multiplication table z = x • y +for (my $i = 0; $i < $GF_NO_ELEMENTS; $i++) +{ + for (my $j = 0; $j < $GF_NO_ELEMENTS; $j++) + { + print "\t" if ($j % 8 == 0); + print sprintf("0x%2.2x", gfmul( $i, $j )); + print ',' unless ($i == $GF_MAX && $j == $GF_MAX); + print ( (($j % 8) == 7) ? "\n" : ' ' ); + } +} + +print<) { + chomp; + if (/^(Function|File) '(.+)'/) { + $type = $1; + $target = $2; + } elsif (/^Lines executed:(\d+\.\d+)% of (\d+)/) { +# print "$type,$target,$1,$2\n"; + if ($type cmp 'File') { + $files{$target} = $1; + } else { + $functions{$target} = $1; + } + } +} + +#@sorted = sort { $files{$a} <=> $files{$b} } keys %files; +#foreach $name (@sorted) +#{ +# print "$name:$files{$name}\n"; +#} +@sorted = sort { $functions{$a} <=> $functions{$b} } keys %functions; +$total = 0; +$count = 0; +foreach $name (@sorted) +{ + next if $name =~ m#^/#; + next if $name =~ m#.+\.h$#; + next if $name =~ m#_unittest\.c$#; + print sprintf("%20s: %3.1f%%\n", $name, $functions{$name}); + $total += $functions{$name}; + $count++; +} +$total /= $count; +print "\n TOTAL: ~" . int($total) . "%\n\n"; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/gcov-seed.sh b/3rdparty/openpgm-svn-r1085/pgm/gcov-seed.sh new file mode 100755 index 0000000..853fbe6 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/gcov-seed.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +./ref/debug/async_unittest +./ref/debug/checksum_unittest +./ref/debug/getifaddrs_unittest +./ref/debug/getnodeaddr_unittest +./ref/debug/gsi_unittest +./ref/debug/http_unittest +./ref/debug/if_unittest +./ref/debug/indextoaddr_unittest +./ref/debug/inet_network_unittest +./ref/debug/md5_unittest +./ref/debug/net_unittest +./ref/debug/packet_unittest +./ref/debug/pgmMIB_unittest +./ref/debug/pgm_unittest +./ref/debug/rate_control_unittest +./ref/debug/receiver_unittest +./ref/debug/recv_unittest +./ref/debug/reed_solomon_unittest +./ref/debug/rxwi_unittest +./ref/debug/signal_unittest +./ref/debug/snmp_unittest +./ref/debug/source_unittest +./ref/debug/timer_unittest +./ref/debug/time_unittest +user=`id -nu` +group=`id -ng` +sudo execcap 'cap_net_raw=ep' /sbin/sucap $user $group ./ref/debug/transport_unittest +sudo find ref/debug/ -user 0 -exec chown $user:$group {} \; +./ref/debug/tsi_unittest +./ref/debug/txwi_unittest + diff --git a/3rdparty/openpgm-svn-r1085/pgm/gcov.sh b/3rdparty/openpgm-svn-r1085/pgm/gcov.sh new file mode 100755 index 0000000..ca17d62 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/gcov.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +gcov -fno ref/debug async_unittest.c +gcov -fno ref/debug checksum_unittest.c +gcov -fno ref/debug getifaddrs_unittest.c +gcov -fno ref/debug getnodeaddr_unittest.c +gcov -fno ref/debug gsi_unittest.c +gcov -fno ref/debug http_unittest.c +gcov -fno ref/debug if_unittest.c +gcov -fno ref/debug indextoaddr_unittest.c +gcov -fno ref/debug inet_network_unittest.c +gcov -fno ref/debug md5_unittest.c +gcov -fno ref/debug net_unittest.c +gcov -fno ref/debug packet_unittest.c +gcov -fno ref/debug pgmMIB_unittest.c +gcov -fno ref/debug pgm_unittest.c +gcov -fno ref/debug rate_control_unittest.c +gcov -fno ref/debug receiver_unittest.c +gcov -fno ref/debug recv_unittest.c +gcov -fno ref/debug reed_solomon_unittest.c +gcov -fno ref/debug rxwi_unittest.c +gcov -fno ref/debug signal_unittest.c +gcov -fno ref/debug snmp_unittest.c +gcov -fno ref/debug source_unittest.c +gcov -fno ref/debug timer_unittest.c +gcov -fno ref/debug time_unittest.c +gcov -fno ref/debug tsi_unittest.c +gcov -fno ref/debug txwi_unittest.c + diff --git a/3rdparty/openpgm-svn-r1085/pgm/getifaddrs.c b/3rdparty/openpgm-svn-r1085/pgm/getifaddrs.c new file mode 100644 index 0000000..9db1d86 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/getifaddrs.c @@ -0,0 +1,840 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable getifaddrs implementation. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#ifdef CONFIG_HAVE_GETIFADDRS +# include +# include +#endif +#if defined( sun ) +# include +#endif +#if defined( _WIN32 ) +# include +# include +#endif +#include +#include + + +//#define GETIFADDRS_DEBUG + +/* locals */ +struct _pgm_ifaddrs_t +{ + struct pgm_ifaddrs_t _ifa; + char _name[IF_NAMESIZE]; + struct sockaddr_storage _addr; + struct sockaddr_storage _netmask; +}; + +/* Number of attempts to try enumerating the interface list */ +#define MAX_TRIES 3 +#define DEFAULT_BUFFER_SIZE 4096 + +/* returns TRUE on success setting ifap to a linked list of system interfaces, + * returns FALSE on failure and sets error appropriately. + */ + +#ifdef SIOCGLIFCONF +static +bool +_pgm_getlifaddrs ( + struct pgm_ifaddrs_t** restrict ifap, + pgm_error_t** restrict error + ) +{ + const int sock = socket (AF_INET, SOCK_DGRAM, 0); + if (-1 == sock) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("Opening IPv4 datagram socket: %s"), + strerror (errno)); + return FALSE; + } + +/* process IPv6 interfaces */ + const int sock6 = socket (AF_INET6, SOCK_DGRAM, 0); + if (-1 == sock6) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("Opening IPv6 datagram socket: %s"), + strerror (errno)); + close (sock); + return FALSE; + } + + struct lifnum lifn; +again: + lifn.lifn_family = AF_INET; + lifn.lifn_flags = 0; + if (-1 == ioctl (sock, SIOCGLIFNUM, &lifn)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("SIOCGLIFNUM failed on IPv4 socket: %s"), + strerror (errno)); + close (sock); + close (sock6); + return FALSE; + } + unsigned if_count = lifn.lifn_count; + pgm_debug ("ioctl(AF_INET, SIOCGLIFNUM) = %d", lifn.lifn_count); + +/* nb: Sun and Apple code likes to pad the interface count here in case interfaces + * are coming up between calls, + */ + lifn.lifn_count += 4; + +/* process all interfaces with family-agnostic ioctl, unfortunately it still + * must be called on each family socket despite what if_tcp(7P) says. + */ + struct lifconf lifc, lifc6; + lifc.lifc_family = AF_INET; + lifc.lifc_flags = 0; + lifc.lifc_len = lifn.lifn_count * sizeof(struct lifreq); + lifc.lifc_buf = alloca (lifc.lifc_len); + if (-1 == ioctl (sock, SIOCGLIFCONF, &lifc)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("SIOCGLIFCONF failed on IPv4 socket: %s"), + strerror (errno)); + close (sock); + close (sock6); + return FALSE; + } + pgm_debug ("ioctl(AF_INET, SIOCGLIFCONF) = %d (%d)", lifc.lifc_len, (int)(lifc.lifc_len / sizeof(struct lifreq))); + +/* repeat everything for IPv6 */ + lifn.lifn_family = AF_INET6; + lifn.lifn_flags = 0; + if (-1 == ioctl (sock6, SIOCGLIFNUM, &lifn)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("SIOCGLIFNUM failed on IPv6 socket: %s"), + strerror (errno)); + close (sock); + close (sock6); + return FALSE; + } + if_count += lifn.lifn_count; + pgm_debug ("ioctl(AF_INET6, SIOCGLIFNUM) = %d", lifn.lifn_count); + + lifn.lifn_count += 4; + + lifc6.lifc_family = AF_INET6; + lifc6.lifc_flags = 0; + lifc6.lifc_len = lifn.lifn_count * sizeof(struct lifreq); + lifc6.lifc_buf = alloca (lifc6.lifc_len); + if (-1 == ioctl (sock6, SIOCGLIFCONF, &lifc6)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("SIOCGLIFCONF failed on IPv6 socket: %s"), + strerror (errno)); + close (sock); + close (sock6); + return FALSE; + } + pgm_debug ("ioctl(AF_INET6, SIOCGLIFCONF) = %d (%d)", lifc6.lifc_len, (int)(lifc6.lifc_len / sizeof(struct lifreq))); + + unsigned nlifr = (lifc.lifc_len + lifc6.lifc_len) / sizeof(struct lifreq); + if (nlifr > if_count) + goto again; + +/* alloc a contiguous block for entire list */ + struct _pgm_ifaddrs_t* ifa = calloc (nlifr, sizeof (struct _pgm_ifaddrs_t)); + pgm_assert (NULL != ifa); + + struct _pgm_ifaddrs_t* ift = ifa; + struct lifreq* lifr = lifc.lifc_req; + struct lifreq* lifr_end = (struct lifreq *)&lifc.lifc_buf[lifc.lifc_len]; + + pgm_assert (IF_NAMESIZE >= LIFNAMSIZ); + + while (lifr < lifr_end) + { +/* name */ + pgm_debug ("AF_INET/name: %s", lifr->lifr_name ? lifr->lifr_name : "(null)"); + ift->_ifa.ifa_name = ift->_name; + strncpy (ift->_ifa.ifa_name, lifr->lifr_name, LIFNAMSIZ); + ift->_ifa.ifa_name[LIFNAMSIZ - 1] = 0; + +/* flags */ + if (-1 != ioctl (sock, SIOCGLIFFLAGS, lifr)) { + ift->_ifa.ifa_flags = lifr->lifr_flags; + } else { + pgm_warn (_("SIOCGLIFFLAGS failed on interface %s%s%s"), + lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); + } + +/* address */ + if (-1 != ioctl (sock, SIOCGLIFADDR, lifr)) { + ift->_ifa.ifa_addr = (void*)&ift->_addr; + memcpy (ift->_ifa.ifa_addr, &lifr->lifr_addr, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_addr)); + } else { + pgm_warn (_("SIOCGLIFADDR failed on interface %s%s%s"), + lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); + } + +/* netmask */ + if (-1 != ioctl (sock, SIOCGLIFNETMASK, lifr)) { + ift->_ifa.ifa_netmask = (void*)&ift->_netmask; +# ifdef CONFIG_HAVE_IFR_NETMASK + memcpy (ift->_ifa.ifa_netmask, &lifr->lifr_netmask, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_netmask)); +# else + memcpy (ift->_ifa.ifa_netmask, &lifr->lifr_addr, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_addr)); +# endif + } else { + pgm_warn (_("SIOCGLIFNETMASK failed on interface %s%s%s"), + lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); + } + + ++lifr; + if (lifr < lifr_end) { + ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); + ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); + } + } + +/* repeat everything for IPv6 */ + lifr = lifc6.lifc_req; + lifr_end = (struct lifreq *)&lifc6.lifc_buf[lifc6.lifc_len]; + + while (lifr < lifr_end) + { + if (ift != ifa) { + ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); + ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); + } + +/* name */ + pgm_debug ("AF_INET6/name: %s", lifr->lifr_name ? lifr->lifr_name : "(null)"); + ift->_ifa.ifa_name = ift->_name; + strncpy (ift->_ifa.ifa_name, lifr->lifr_name, sizeof(lifr->lifr_name)); + ift->_ifa.ifa_name[sizeof(lifr->lifr_name) - 1] = 0; + +/* flags */ + if (-1 != ioctl (sock6, SIOCGLIFFLAGS, lifr)) { + ift->_ifa.ifa_flags = lifr->lifr_flags; + } else { + pgm_warn (_("SIOCGLIFFLAGS failed on interface %s%s%s"), + lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); + } + +/* address */ + if (-1 != ioctl (sock6, SIOCGLIFADDR, lifr)) { + ift->_ifa.ifa_addr = (void*)&ift->_addr; + memcpy (ift->_ifa.ifa_addr, &lifr->lifr_addr, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_addr)); + } else { + pgm_warn (_("SIOCGLIFADDR failed on interface %s%s%s"), + lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); + } + +/* netmask */ + if (ioctl (sock6, SIOCGLIFNETMASK, lifr) != -1) { + ift->_ifa.ifa_netmask = (void*)&ift->_netmask; +# ifdef CONFIG_HAVE_IFR_NETMASK + memcpy (ift->_ifa.ifa_netmask, &lifr->lifr_netmask, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_netmask)); +# else + memcpy (ift->_ifa.ifa_netmask, &lifr->lifr_addr, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_addr)); +# endif + } else { + pgm_warn (_("SIOCGLIFNETMASK failed on interface %s%s%s"), + lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); + } + + ++lifr; + } + + if (-1 == close (sock6)) + pgm_warn (_("Closing IPv6 socket failed: %s"), strerror(errno)); + if (-1 == close (sock)) + pgm_warn (_("Closing IPv4 socket failed: %s"), strerror(errno)); + + *ifap = (struct pgm_ifaddrs_t*)ifa; + return TRUE; +} +#endif /* SIOCGLIFCONF */ + +#ifdef SIOCGIFCONF +static +bool +_pgm_getifaddrs ( + struct pgm_ifaddrs_t** restrict ifap, + pgm_error_t** restrict error + ) +{ + const int sock = socket (AF_INET, SOCK_DGRAM, 0); + if (-1 == sock) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("Opening IPv4 datagram socket: %s"), + strerror (errno)); + return FALSE; + } + +/* process IPv4 interfaces */ + char buf[ DEFAULT_BUFFER_SIZE ]; + struct ifconf ifc; + ifc.ifc_buf = buf; + ifc.ifc_len = sizeof(buf); + if (-1 == ioctl (sock, SIOCGIFCONF, &ifc)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("SIOCGIFCONF failed on IPv4 socket: %s"), + strerror (errno)); + close (sock); + return FALSE; + } + int if_count = ifc.ifc_len / sizeof(struct ifreq); + +# ifdef CONFIG_HAVE_IPV6_SIOCGIFADDR +/* process IPv6 interfaces */ + const int sock6 = socket (AF_INET6, SOCK_DGRAM, 0); + if (-1 == sock6) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("Opening IPv6 datagram socket: %s"), + strerror (errno)); + close (sock); + return FALSE; + } + + char buf6[1024]; + struct ifconf ifc6; + ifc6.ifc_buf = buf6; + ifc6.ifc_len = sizeof(buf6); + if (-1 == ioctl (sock6, SIOCGIFCONF, &ifc6)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("SIOCGIFCONF failed on IPv6 socket: %s"), + strerror (errno)); + close (sock); + close (sock6); + return FALSE; + } + if_count += ifc6.ifc_len / sizeof(struct ifreq); +# endif /* CONFIG_HAVE_IPV6_SIOCGIFADDR */ + +/* alloc a contiguous block for entire list */ + struct _pgm_ifaddrs_t* ifa = pgm_new0 (struct _pgm_ifaddrs_t, if_count); + struct _pgm_ifaddrs_t* ift = ifa; + struct ifreq *ifr = ifc.ifc_req; + struct ifreq *ifr_end = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len]; + + pgm_assert (IF_NAMESIZE >= sizeof(ifr->ifr_name)); + + while (ifr < ifr_end) + { +/* name */ + pgm_debug ("AF_INET/name:%s", ifr->ifr_name ? ifr->ifr_name : "(null)"); + ift->_ifa.ifa_name = ift->_name; + strncpy (ift->_ifa.ifa_name, ifr->ifr_name, sizeof(ifr->ifr_name)); + ift->_ifa.ifa_name[sizeof(ifr->ifr_name) - 1] = 0; + +/* flags */ + if (-1 != ioctl (sock, SIOCGIFFLAGS, ifr)) { + ift->_ifa.ifa_flags = ifr->ifr_flags; + } else { + pgm_warn (_("SIOCGIFFLAGS failed on interface %s%s%s"), + ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); + } + +/* address */ + if (-1 != ioctl (sock, SIOCGIFADDR, ifr)) { + ift->_ifa.ifa_addr = (void*)&ift->_addr; + memcpy (ift->_ifa.ifa_addr, &ifr->ifr_addr, pgm_sockaddr_len(&ifr->ifr_addr)); + } else { + pgm_warn (_("SIOCGIFADDR failed on interface %s%s%s"), + ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); + } + +/* netmask */ + if (-1 != ioctl (sock, SIOCGIFNETMASK, ifr)) { + ift->_ifa.ifa_netmask = (void*)&ift->_netmask; +# ifdef CONFIG_HAVE_IFR_NETMASK + memcpy (ift->_ifa.ifa_netmask, &ifr->ifr_netmask, pgm_sockaddr_len(&ifr->ifr_netmask)); +# else + memcpy (ift->_ifa.ifa_netmask, &ifr->ifr_addr, pgm_sockaddr_len(&ifr->ifr_addr)); +# endif + } else { + pgm_warn (_("SIOCGIFNETMASK failed on interface %s%s%s"), + ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); + } + + ++ifr; + if (ifr < ifr_end) { + ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); + ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); + } + } + +# ifdef CONFIG_HAVE_IPV6_SIOCGIFADDR +/* repeat everything for IPv6 */ + ifr = ifc6.ifc_req; + ifr_end = (struct ifreq *)&ifc6.ifc_buf[ifc6.ifc_len]; + + while (ifr < ifr_end) + { + if (ift != ifa) { + ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); + ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); + } + +/* name */ + pgm_debug ("AF_INET6/name:%s", ifr->ifr_name ? ifr->ifr_name : "(null)"); + ift->_ifa.ifa_name = ift->_name; + strncpy (ift->_ifa.ifa_name, ifr->ifr_name, sizeof(ifr->ifr_name)); + ift->_ifa.ifa_name[sizeof(ifr->ifr_name) - 1] = 0; + +/* flags */ + if (-1 != ioctl (sock6, SIOCGIFFLAGS, ifr)) { + ift->_ifa.ifa_flags = ifr->ifr_flags; + } else { + pgm_warn (_("SIOCGIFFLAGS failed on interface %s%s%s"), + ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); + } + +/* address, note this does not work on Linux as struct ifreq is too small for an IPv6 address */ + if (-1 != ioctl (sock6, SIOCGIFADDR, ifr)) { + ift->_ifa.ifa_addr = (void*)&ift->_addr; + memcpy (ift->_ifa.ifa_addr, &ifr->ifr_addr, pgm_sockaddr_len(&ifr->ifr_addr)); + } else { + pgm_warn (_("SIOCGIFADDR failed on interface %s%s%s"), + ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); + } + +/* netmask */ + if (-1 != ioctl (sock6, SIOCGIFNETMASK, ifr)) { + ift->_ifa.ifa_netmask = (void*)&ift->_netmask; +# ifdef CONFIG_HAVE_IFR_NETMASK + memcpy (ift->_ifa.ifa_netmask, &ifr->ifr_netmask, pgm_sockaddr_len(&ifr->ifr_netmask)); +# else + memcpy (ift->_ifa.ifa_netmask, &ifr->ifr_addr, pgm_sockaddr_len(&ifr->ifr_addr)); +# endif + } else { + pgm_warn (_("SIOCGIFNETMASK failed on interface %s%s%s"), + ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); + } + + ++ifr; + } + + if (-1 == close (sock6)) + pgm_warn (_("Closing IPv6 socket failed: %s"), strerror(errno)); +# endif /* CONFIG_HAVE_IPV6_SIOCGIFADDR */ + + if (-1 == close (sock)) + pgm_warn (_("Closing IPv4 socket failed: %s"), strerror(errno)); + + *ifap = (struct pgm_ifaddrs_t*)ifa; + return TRUE; +} +#endif /* SIOCLIFCONF */ + +#if defined(_WIN32) +static inline +void* +_pgm_heap_alloc ( + const size_t n_bytes + ) +{ +# ifdef CONFIG_USE_HEAPALLOC +/* Does not appear very safe with re-entrant calls on XP */ + return HeapAlloc (GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, n_bytes); +# else + return pgm_malloc (n_bytes); +# endif +} + +static inline +void +_pgm_heap_free ( + void* mem + ) +{ +# ifdef CONFIG_USE_HEAPALLOC + HeapFree (GetProcessHeap(), 0, mem); +# else + pgm_free (mem); +# endif +} + +/* NB: IP_ADAPTER_INFO size varies size due to sizeof (time_t), the API assumes + * 4-byte datatype whilst compiler uses an 8-byte datatype. Size can be forced + * with -D_USE_32BIT_TIME_T with side effects to everything else. + */ + +static +bool +_pgm_getadaptersinfo ( + struct pgm_ifaddrs_t** restrict ifap, + pgm_error_t** restrict error + ) +{ + DWORD dwRet; + ULONG ulOutBufLen = DEFAULT_BUFFER_SIZE; + PIP_ADAPTER_INFO pAdapterInfo = NULL; + PIP_ADAPTER_INFO pAdapter = NULL; + +/* loop to handle interfaces coming online causing a buffer overflow + * between first call to list buffer length and second call to enumerate. + */ + for (unsigned i = MAX_TRIES; i; i--) + { + pgm_debug ("IP_ADAPTER_INFO buffer length %lu bytes.", ulOutBufLen); + pAdapterInfo = (IP_ADAPTER_INFO*)_pgm_heap_alloc (ulOutBufLen); + dwRet = GetAdaptersInfo (pAdapterInfo, &ulOutBufLen); + if (ERROR_BUFFER_OVERFLOW == dwRet) { + _pgm_heap_free (pAdapterInfo); + pAdapterInfo = NULL; + } else { + break; + } + } + + switch (dwRet) { + case ERROR_SUCCESS: /* NO_ERROR */ + break; + case ERROR_BUFFER_OVERFLOW: + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NOBUFS, + _("GetAdaptersInfo repeatedly failed with ERROR_BUFFER_OVERFLOW.")); + if (pAdapterInfo) + _pgm_heap_free (pAdapterInfo); + return FALSE; + default: + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_win_errno (dwRet), + _("GetAdaptersInfo failed: %s"), + pgm_adapter_strerror (dwRet)); + if (pAdapterInfo) + _pgm_heap_free (pAdapterInfo); + return FALSE; + } + +/* count valid adapters */ + int n = 0, k = 0; + for (pAdapter = pAdapterInfo; + pAdapter; + pAdapter = pAdapter->Next) + { + for (IP_ADDR_STRING *pIPAddr = &pAdapter->IpAddressList; + pIPAddr; + pIPAddr = pIPAddr->Next) + { +/* skip null adapters */ + if (strlen (pIPAddr->IpAddress.String) == 0) + continue; + ++n; + } + } + + pgm_debug ("GetAdaptersInfo() discovered %d interfaces.", n); + +/* contiguous block for adapter list */ + struct _pgm_ifaddrs_t* ifa = pgm_new0 (struct _pgm_ifaddrs_t, n); + struct _pgm_ifaddrs_t* ift = ifa; + +/* now populate list */ + for (pAdapter = pAdapterInfo; + pAdapter; + pAdapter = pAdapter->Next) + { + for (IP_ADDR_STRING *pIPAddr = &pAdapter->IpAddressList; + pIPAddr; + pIPAddr = pIPAddr->Next) + { +/* skip null adapters */ + if (strlen (pIPAddr->IpAddress.String) == 0) + continue; + +/* address */ + ift->_ifa.ifa_addr = (void*)&ift->_addr; + pgm_assert (pgm_sockaddr_pton (pIPAddr->IpAddress.String, ift->_ifa.ifa_addr)); + +/* name */ + pgm_debug ("name:%s IPv4 index:%lu", + pAdapter->AdapterName, pAdapter->Index); + ift->_ifa.ifa_name = ift->_name; + strncpy (ift->_ifa.ifa_name, pAdapter->AdapterName, IF_NAMESIZE); + ift->_ifa.ifa_name[IF_NAMESIZE - 1] = 0; + +/* flags: assume up, broadcast and multicast */ + ift->_ifa.ifa_flags = IFF_UP | IFF_BROADCAST | IFF_MULTICAST; + if (pAdapter->Type == MIB_IF_TYPE_LOOPBACK) + ift->_ifa.ifa_flags |= IFF_LOOPBACK; + +/* netmask */ + ift->_ifa.ifa_netmask = (void*)&ift->_netmask; + pgm_assert (pgm_sockaddr_pton (pIPAddr->IpMask.String, ift->_ifa.ifa_netmask)); + +/* next */ + if (k++ < (n - 1)) { + ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); + ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); + } + } + } + + if (pAdapterInfo) + free (pAdapterInfo); + *ifap = (struct pgm_ifaddrs_t*)ifa; + return TRUE; +} + +static +bool +_pgm_getadaptersaddresses ( + struct pgm_ifaddrs_t** restrict ifap, + pgm_error_t** restrict error + ) +{ + DWORD dwSize = DEFAULT_BUFFER_SIZE, dwRet; + IP_ADAPTER_ADDRESSES *pAdapterAddresses = NULL, *adapter; + +/* loop to handle interfaces coming online causing a buffer overflow + * between first call to list buffer length and second call to enumerate. + */ + for (unsigned i = MAX_TRIES; i; i--) + { + pgm_debug ("IP_ADAPTER_ADDRESSES buffer length %lu bytes.", dwSize); + pAdapterAddresses = (IP_ADAPTER_ADDRESSES*)_pgm_heap_alloc (dwSize); + dwRet = GetAdaptersAddresses (AF_UNSPEC, + GAA_FLAG_INCLUDE_PREFIX | + GAA_FLAG_SKIP_ANYCAST | + GAA_FLAG_SKIP_DNS_SERVER | + GAA_FLAG_SKIP_FRIENDLY_NAME | + GAA_FLAG_SKIP_MULTICAST, + NULL, + pAdapterAddresses, + &dwSize); + if (ERROR_BUFFER_OVERFLOW == dwRet) { + _pgm_heap_free (pAdapterAddresses); + pAdapterAddresses = NULL; + } else { + break; + } + } + + switch (dwRet) { + case ERROR_SUCCESS: + break; + case ERROR_BUFFER_OVERFLOW: + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NOBUFS, + _("GetAdaptersAddresses repeatedly failed with ERROR_BUFFER_OVERFLOW.")); + if (pAdapterAddresses) + free (pAdapterAddresses); + return FALSE; + default: + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_win_errno (dwRet), + _("GetAdaptersAddresses failed: %s"), + pgm_adapter_strerror (dwRet)); + if (pAdapterAddresses) + free (pAdapterAddresses); + return FALSE; + } + +/* count valid adapters */ + int n = 0, k = 0; + for (adapter = pAdapterAddresses; + adapter; + adapter = adapter->Next) + { + for (IP_ADAPTER_UNICAST_ADDRESS *unicast = adapter->FirstUnicastAddress; + unicast; + unicast = unicast->Next) + { +/* ensure IP adapter */ + if (AF_INET != unicast->Address.lpSockaddr->sa_family && + AF_INET6 != unicast->Address.lpSockaddr->sa_family) + { + continue; + } + + ++n; + } + } + +/* contiguous block for adapter list */ + struct _pgm_ifaddrs_t* ifa = pgm_new0 (struct _pgm_ifaddrs_t, n); + struct _pgm_ifaddrs_t* ift = ifa; + +/* now populate list */ + for (adapter = pAdapterAddresses; + adapter; + adapter = adapter->Next) + { + int unicastIndex = 0; + for (IP_ADAPTER_UNICAST_ADDRESS *unicast = adapter->FirstUnicastAddress; + unicast; + unicast = unicast->Next, ++unicastIndex) + { +/* ensure IP adapter */ + if (AF_INET != unicast->Address.lpSockaddr->sa_family && + AF_INET6 != unicast->Address.lpSockaddr->sa_family) + { + continue; + } + +/* address */ + ift->_ifa.ifa_addr = (void*)&ift->_addr; + memcpy (ift->_ifa.ifa_addr, unicast->Address.lpSockaddr, unicast->Address.iSockaddrLength); + +/* name */ + pgm_debug ("name:%s IPv4 index:%lu IPv6 index:%lu", + adapter->AdapterName, adapter->IfIndex, adapter->Ipv6IfIndex); + ift->_ifa.ifa_name = ift->_name; + strncpy (ift->_ifa.ifa_name, adapter->AdapterName, IF_NAMESIZE); + ift->_ifa.ifa_name[IF_NAMESIZE - 1] = 0; + +/* flags */ + ift->_ifa.ifa_flags = 0; + if (IfOperStatusUp == adapter->OperStatus) + ift->_ifa.ifa_flags |= IFF_UP; + if (IF_TYPE_SOFTWARE_LOOPBACK == adapter->IfType) + ift->_ifa.ifa_flags |= IFF_LOOPBACK; + if (!(adapter->Flags & IP_ADAPTER_NO_MULTICAST)) + ift->_ifa.ifa_flags |= IFF_MULTICAST; + +/* netmask */ + ift->_ifa.ifa_netmask = (void*)&ift->_netmask; + +/* pre-Vista must hunt for matching prefix in linked list, otherwise use OnLinkPrefixLength */ + int prefixIndex = 0; + ULONG prefixLength = 0; + for (IP_ADAPTER_PREFIX *prefix = adapter->FirstPrefix; + prefix; + prefix = prefix->Next, ++prefixIndex) + { + if (prefixIndex == unicastIndex) { + prefixLength = prefix->PrefixLength; + break; + } + } + +/* map prefix to netmask */ + ift->_ifa.ifa_netmask->sa_family = unicast->Address.lpSockaddr->sa_family; + switch (unicast->Address.lpSockaddr->sa_family) { + case AF_INET: + if (0 == prefixLength) { + pgm_warn (_("IPv4 adapter %s prefix length is 0, overriding to 32."), adapter->AdapterName); + prefixLength = 32; + } + ((struct sockaddr_in*)ift->_ifa.ifa_netmask)->sin_addr.s_addr = htonl( 0xffffffffU << ( 32 - prefixLength ) ); + break; + + case AF_INET6: + if (0 == prefixLength) { + pgm_warn (_("IPv6 adapter %s prefix length is 0, overriding to 128."), adapter->AdapterName); + prefixLength = 128; + } + for (ULONG i = prefixLength, j = 0; i > 0; i -= 8, ++j) + { + ((struct sockaddr_in6*)ift->_ifa.ifa_netmask)->sin6_addr.s6_addr[ j ] = i >= 8 ? 0xff : (ULONG)(( 0xffU << ( 8 - i ) ) & 0xffU ); + } + break; + } + +/* next */ + if (k++ < (n - 1)) { + ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); + ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); + } + } + } + + if (pAdapterAddresses) + free (pAdapterAddresses); + *ifap = (struct pgm_ifaddrs_t*)ifa; + return TRUE; +} +#endif /* _WIN32 */ + +/* returns TRUE on success setting ifap to a linked list of system interfaces, + * returns FALSE on failure and sets error appropriately. + */ + +bool +pgm_getifaddrs ( + struct pgm_ifaddrs_t** restrict ifap, + pgm_error_t** restrict error + ) +{ + pgm_assert (NULL != ifap); + + pgm_debug ("pgm_getifaddrs (ifap:%p error:%p)", + (void*)ifap, (void*)error); + +#ifdef CONFIG_HAVE_GETIFADDRS + const int e = getifaddrs ((struct ifaddrs**)ifap); + if (-1 == e) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("getifaddrs failed: %s"), + strerror (errno)); + return FALSE; + } + return TRUE; +#elif defined(CONFIG_TARGET_WINE) + return _pgm_getadaptersinfo (ifap, error); +#elif defined(_WIN32) + return _pgm_getadaptersaddresses (ifap, error); +#elif defined(SIOCGLIFCONF) + return _pgm_getlifaddrs (ifap, error); +#elif defined(SIOCGIFCONF) + return _pgm_getifaddrs (ifap, error); +#else +# error "Unsupported interface enumeration on this platform." +#endif /* !CONFIG_HAVE_GETIFADDRS */ +} + +void +pgm_freeifaddrs ( + struct pgm_ifaddrs_t* ifa + ) +{ + pgm_return_if_fail (NULL != ifa); + +#ifdef CONFIG_HAVE_GETIFADDRS + freeifaddrs ((struct ifaddrs*)ifa); +#else + pgm_free (ifa); +#endif +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/getifaddrs_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/getifaddrs_unittest.c new file mode 100644 index 0000000..05063fe --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/getifaddrs_unittest.c @@ -0,0 +1,262 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for portable getifaddrs implementation. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* IFF_UP */ +#define _BSD_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* mock state */ + +/* mock functions for external references */ + +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return 0; +} + + +#define GETIFADDRS_DEBUG +#include "getifaddrs.c" + + +char* +ifflags_string ( + unsigned int flags + ) +{ + static char s[1024]; + + s[0] = '\0'; + if (flags & IFF_UP) + strcat (s, "IFF_UP"); +#define IFF(flag) \ + do { \ + if (flags & flag) { \ + strcat (s, s[0] ? ("|" #flag) : (#flag)); \ + } \ + } while (0) +#ifdef IFF_BROADCAST + IFF(IFF_BROADCAST); +#endif +#ifdef IFF_DEBUG + IFF(IFF_DEBUG); +#endif +#ifdef IFF_LOOPBACK + IFF(IFF_LOOPBACK); +#endif +#ifdef IFF_POINTOPOINT + IFF(IFF_POINTOPOINT); +#endif +#ifdef IFF_RUNNING + IFF(IFF_RUNNING); +#endif +#ifdef IFF_NOARP + IFF(IFF_NOARP); +#endif +#ifdef IFF_PROMISC + IFF(IFF_PROMISC); +#endif +#ifdef IFF_NOTRAILERS + IFF(IFF_NOTRAILERS); +#endif +#ifdef IFF_ALLMULTI + IFF(IFF_ALLMULTI); +#endif +#ifdef IFF_MASTER + IFF(IFF_MASTER); +#endif +#ifdef IFF_SLAVE + IFF(IFF_SLAVE); +#endif +#ifdef IFF_MULTICAST + IFF(IFF_MULTICAST); +#endif +#ifdef IFF_PORTSEL + IFF(IFF_PORTSEL); +#endif +#ifdef IFF_AUTOMEDIA + IFF(IFF_AUTOMEDIA); +#endif +#ifdef IFF_DYNAMIC + IFF(IFF_DYNAMIC); +#endif +#ifdef IFF_LOWER_UP + IFF(IFF_LOWER_UP); +#endif +#ifdef IFF_DORMANT + IFF(IFF_DORMANT); +#endif +#ifdef IFF_ECHO + IFF(IFF_ECHO); +#endif + if (!s[0]) { + if (flags) + sprintf (s, "0x%x", flags); + else + strcpy (s, "(null)"); + } + return s; +} + +/* target: + * bool + * pgm_getifaddrs ( + * struct pgm_ifaddrs_t**restrict ifap, + * pgm_error_t**restrict error + * ) + */ + +START_TEST (test_getifaddrs_pass_001) +{ + char saddr[INET6_ADDRSTRLEN], snetmask[INET6_ADDRSTRLEN]; + struct pgm_ifaddrs_t *ifap = NULL, *ifa; + pgm_error_t* err = NULL; + fail_unless (TRUE == pgm_getifaddrs (&ifap, &err), "getifaddrs failed"); + for (ifa = ifap; ifa; ifa = ifa->ifa_next) + { + fail_unless (NULL != ifa, "invalid address"); + if (ifa->ifa_addr) { + if (AF_INET == ifa->ifa_addr->sa_family || + AF_INET6 == ifa->ifa_addr->sa_family) + pgm_sockaddr_ntop (ifa->ifa_addr, saddr, sizeof(saddr)); +#ifdef AF_PACKET + else if (AF_PACKET == ifa->ifa_addr->sa_family) + strcpy (saddr, "(AF_PACKET)"); +#endif + else + sprintf (saddr, "(AF = %d)", ifa->ifa_addr->sa_family); + } else + strcpy (saddr, "(null)"); + if (ifa->ifa_netmask) { + if (AF_INET == ifa->ifa_addr->sa_family || + AF_INET6 == ifa->ifa_addr->sa_family) + pgm_sockaddr_ntop (ifa->ifa_netmask, snetmask, sizeof(snetmask)); +#ifdef AF_PACKET + else if (AF_PACKET == ifa->ifa_addr->sa_family) + strcpy (snetmask, "(AF_PACKET)"); +#endif + else + sprintf (snetmask, "(AF = %d)", ifa->ifa_addr->sa_family); + } else + strcpy (snetmask, "(null)"); + g_message ("ifa = {" + "ifa_next = %p, " + "ifa_name = %s%s%s, " + "ifa_flags = %s, " + "ifa_addr = %s, " + "ifa_netmask = %s" + "}", + ifa->ifa_next, + ifa->ifa_name ? "\"" : "", ifa->ifa_name ? ifa->ifa_name : "(null)", ifa->ifa_name ? "\"" : "", + ifflags_string (ifa->ifa_flags), + saddr, + snetmask); + } +} +END_TEST + +START_TEST (test_getifaddrs_fail_001) +{ + fail_unless (FALSE == pgm_getifaddrs (NULL, NULL), "getifaddrs failed"); + g_message ("errno:%d", errno); +} +END_TEST + +/* target: + * void + * pgm_freeifaddrs ( + * struct pgm_ifaddrs* ifa + * ) + */ + +START_TEST (test_freeifaddrs_pass_001) +{ + struct pgm_ifaddrs_t* ifap = NULL; + pgm_error_t* err = NULL; + fail_unless (TRUE == pgm_getifaddrs (&ifap, &err), "getifaddrs failed"); + pgm_freeifaddrs (ifap); +} +END_TEST + +/* silent failure */ +START_TEST (test_freeifaddrs_pass_002) +{ + pgm_freeifaddrs (NULL); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_getifaddrs = tcase_create ("getifaddrs"); + suite_add_tcase (s, tc_getifaddrs); + tcase_add_test (tc_getifaddrs, test_getifaddrs_pass_001); + tcase_add_test_raise_signal (tc_getifaddrs, test_getifaddrs_fail_001, SIGABRT); + + TCase* tc_freeifaddrs = tcase_create ("freeifaddrs"); + suite_add_tcase (s, tc_freeifaddrs); + tcase_add_test (tc_freeifaddrs, test_freeifaddrs_pass_001); + tcase_add_test (tc_freeifaddrs, test_freeifaddrs_pass_002); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/getnodeaddr.c b/3rdparty/openpgm-svn-r1085/pgm/getnodeaddr.c new file mode 100644 index 0000000..0765c9f --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/getnodeaddr.c @@ -0,0 +1,196 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable function to return the nodes IP address. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#ifndef _WIN32 +# include +#endif +#include +#include + + +//#define GETNODEADDR_DEBUG + + +/* globals */ + +static const char* pgm_family_string (const sa_family_t); + + +/* return node primary address on multi-address family interfaces. + * + * returns TRUE on success, returns FALSE on failure. + */ + +bool +pgm_if_getnodeaddr ( + const sa_family_t family, /* requested address family, AF_INET, AF_INET6, or AF_UNSPEC */ + struct sockaddr* restrict addr, + const socklen_t cnt, /* size of address pointed to by addr */ + pgm_error_t** restrict error + ) +{ + pgm_return_val_if_fail (AF_INET == family || AF_INET6 == family || AF_UNSPEC == family, FALSE); + pgm_return_val_if_fail (NULL != addr, FALSE); + if (AF_INET == family || AF_UNSPEC == family) + pgm_return_val_if_fail (cnt >= sizeof(struct sockaddr_in), FALSE); + else + pgm_return_val_if_fail (cnt >= sizeof(struct sockaddr_in6), FALSE); + + pgm_debug ("pgm_if_getnodeaddr (family:%s addr:%p cnt:%d error:%p)", + pgm_family_string (family), (const void*)addr, cnt, (const void*)error); + + char hostname[NI_MAXHOST + 1]; + struct hostent* he; + + if (0 != gethostname (hostname, sizeof(hostname))) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("Resolving hostname: %s"), +#ifndef _WIN32 + strerror (errno) +#else + pgm_wsastrerror (WSAGetLastError()) +#endif + ); + return FALSE; + } + + addr->sa_family = family; + struct addrinfo hints = { + .ai_family = family, + .ai_socktype = SOCK_STREAM, /* not really */ + .ai_protocol = IPPROTO_TCP, /* not really */ + .ai_flags = AI_ADDRCONFIG, + }, *res; + + int e = getaddrinfo (hostname, NULL, &hints, &res); + if (0 == e) { + const socklen_t addrlen = res->ai_addrlen; + memcpy (addr, res->ai_addr, addrlen); + freeaddrinfo (res); + return TRUE; + } else if (EAI_NONAME != e) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_eai_errno (e, errno), + _("Resolving hostname address: %s"), + gai_strerror (e)); + return FALSE; + } else if (AF_UNSPEC == family) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NONAME, + _("Resolving hostname address family.")); + return FALSE; + } + +/* Common case a dual stack host has incorrect IPv6 configuration, i.e. + * hostname is only IPv4 and despite one or more IPv6 addresses. Workaround + * for this case is to resolve the IPv4 hostname, find the matching interface + * and from that interface find an active IPv6 address taking global scope as + * preference over link scoped addresses. + */ + he = gethostbyname (hostname); + if (NULL == he) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_h_errno (h_errno), +#ifndef _WIN32 + _("Resolving IPv4 hostname address: %s"), + hstrerror (h_errno) +#else + _("Resolving IPv4 hostname address: %s"), + pgm_wsastrerror (WSAGetLastError()) +#endif + ); + return FALSE; + } + + struct pgm_ifaddrs_t *ifap, *ifa, *ifa6; + if (!pgm_getifaddrs (&ifap, error)) { + pgm_prefix_error (error, + _("Enumerating network interfaces: ")); + return FALSE; + } + +/* hunt for IPv4 interface */ + for (ifa = ifap; ifa; ifa = ifa->ifa_next) + { + if (NULL == ifa->ifa_addr || + AF_INET != ifa->ifa_addr->sa_family) + continue; + if (((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr == ((struct in_addr*)(he->h_addr_list[0]))->s_addr) + { + goto ipv4_found; + } + } + pgm_freeifaddrs (ifap); + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NONET, + _("Discovering primary IPv4 network interface.")); + return FALSE; +ipv4_found: + +/* hunt for IPv6 interface */ + for (ifa6 = ifap; ifa6; ifa6 = ifa6->ifa_next) + { + if (AF_INET6 != ifa6->ifa_addr->sa_family) + continue; + if (0 == strcmp (ifa->ifa_name, ifa6->ifa_name)) + { + goto ipv6_found; + } + } + pgm_freeifaddrs (ifap); + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NONET, + _("Discovering primary IPv6 network interface.")); + return FALSE; +ipv6_found: + + memcpy (addr, ifa6->ifa_addr, pgm_sockaddr_len (ifa6->ifa_addr)); + pgm_freeifaddrs (ifap); + return TRUE; +} + +static +const char* +pgm_family_string ( + const sa_family_t family + ) +{ + const char* c; + + switch (family) { + case AF_UNSPEC: c = "AF_UNSPEC"; break; + case AF_INET: c = "AF_INET"; break; + case AF_INET6: c = "AF_INET6"; break; + default: c = "(unknown)"; break; + } + + return c; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/getnodeaddr_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/getnodeaddr_unittest.c new file mode 100644 index 0000000..6792d2a --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/getnodeaddr_unittest.c @@ -0,0 +1,573 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for portable function to return the nodes IP address. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* IFF_UP */ +#define _BSD_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* mock state */ + +struct addrinfo; + +struct mock_host_t { + struct sockaddr_storage address; + char* canonical_hostname; + char* alias; +}; + +struct mock_interface_t { + unsigned int index; + char* name; + unsigned int flags; + struct sockaddr_storage addr; + struct sockaddr_storage netmask; +}; + +static GList *mock_hosts = NULL, *mock_interfaces = NULL; + +#define MOCK_HOSTNAME "kiku" +static char* mock_kiku = MOCK_HOSTNAME; +static char* mock_localhost = "localhost"; +static char* mock_invalid = "invalid.invalid"; /* RFC 2606 */ +static char* mock_toolong = "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij12345"; /* 65 */ +static char* mock_hostname = NULL; + +struct pgm_ifaddrs_t; +struct pgm_error_t; + +static bool mock_pgm_getifaddrs (struct pgm_ifaddrs_t**, struct pgm_error_t**); +static void mock_pgm_freeifaddrs (struct pgm_ifaddrs_t*); +static int mock_getaddrinfo (const char*, const char*, const struct addrinfo*, struct addrinfo**); +static void mock_freeaddrinfo (struct addrinfo*); +static int mock_gethostname (char*, size_t); +static struct hostent* mock_gethostbyname (const char*); + + +#define pgm_getifaddrs mock_pgm_getifaddrs +#define pgm_freeifaddrs mock_pgm_freeifaddrs +#define getaddrinfo mock_getaddrinfo +#define freeaddrinfo mock_freeaddrinfo +#define gethostname mock_gethostname +#define gethostbyname mock_gethostbyname + + +#define GETNODEADDR_DEBUG +#include "getnodeaddr.c" + + +static +gpointer +create_host ( + const char* address, + const char* canonical_hostname, + const char* alias + ) +{ + struct mock_host_t* new_host; + + g_assert (address); + g_assert (canonical_hostname); + + new_host = g_slice_alloc0 (sizeof(struct mock_host_t)); + g_assert (pgm_sockaddr_pton (address, (struct sockaddr*)&new_host->address)); + new_host->canonical_hostname = g_strdup (canonical_hostname); + new_host->alias = alias ? g_strdup (alias) : NULL; + + return new_host; +} + +static +gpointer +create_interface ( + const unsigned index, + const char* name, + const char* flags + ) +{ + struct mock_interface_t* new_interface; + + g_assert (name); + g_assert (flags); + + new_interface = g_slice_alloc0 (sizeof(struct mock_interface_t)); + new_interface->index = index; + new_interface->name = g_strdup (name); + + struct sockaddr_in* sin = (gpointer)&new_interface->addr; + struct sockaddr_in6* sin6 = (gpointer)&new_interface->addr; + + gchar** tokens = g_strsplit (flags, ",", 0); + for (guint i = 0; tokens[i]; i++) + { + if (strcmp (tokens[i], "up") == 0) + new_interface->flags |= IFF_UP; + else if (strcmp (tokens[i], "down") == 0) + new_interface->flags |= 0; + else if (strcmp (tokens[i], "loop") == 0) + new_interface->flags |= IFF_LOOPBACK; + else if (strcmp (tokens[i], "broadcast") == 0) + new_interface->flags |= IFF_BROADCAST; + else if (strcmp (tokens[i], "multicast") == 0) + new_interface->flags |= IFF_MULTICAST; + else if (strncmp (tokens[i], "ip=", strlen("ip=")) == 0) { + const char* addr = tokens[i] + strlen("ip="); + g_assert (pgm_sockaddr_pton (addr, (struct sockaddr*)&new_interface->addr)); + } + else if (strncmp (tokens[i], "netmask=", strlen("netmask=")) == 0) { + const char* addr = tokens[i] + strlen("netmask="); + g_assert (pgm_sockaddr_pton (addr, (struct sockaddr*)&new_interface->netmask)); + } + else if (strncmp (tokens[i], "scope=", strlen("scope=")) == 0) { + const char* scope = tokens[i] + strlen("scope="); + g_assert (AF_INET6 == ((struct sockaddr*)&new_interface->addr)->sa_family); + ((struct sockaddr_in6*)&new_interface->addr)->sin6_scope_id = atoi (scope); + } + else + g_error ("parsing failed for flag %s%s%s", + tokens[i] ? "\"" : "", tokens[i] ? tokens[i] : "(null)", tokens[i] ? "\"" : ""); + } + + g_strfreev (tokens); + return new_interface; +} + +#define APPEND_HOST2(a,b,c) \ + do { \ + gpointer data = create_host ((a), (b), (c)); \ + g_assert (data); \ + mock_hosts = g_list_append (mock_hosts, data); \ + g_assert (mock_hosts); g_assert (mock_hosts->data); \ + } while (0) +#define APPEND_HOST(a,b) APPEND_HOST2((a),(b),NULL) +#define APPEND_INTERFACE(a,b,c) \ + do { \ + gpointer data = create_interface ((a), (b), (c)); \ + g_assert (data); \ + mock_interfaces = g_list_append (mock_interfaces, data); \ + g_assert (mock_interfaces); g_assert (mock_interfaces->data); \ + } while (0) +static +void +mock_setup_net (void) +{ + mock_hostname = mock_kiku; + + APPEND_HOST ( "127.0.0.1", "localhost"); + APPEND_HOST2( "10.6.28.33", "kiku.hk.miru.hk", "kiku"); + APPEND_HOST2( "2002:dce8:d28e::33", "ip6-kiku", "kiku"); + APPEND_HOST2( "::1", "ip6-localhost", "ip6-loopback"); + + APPEND_INTERFACE( 1, "lo", "up,loop"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast"); + APPEND_INTERFACE( 3, "eth1", "down,broadcast,multicast"); + APPEND_INTERFACE( 1, "lo", "up,loop,ip=127.0.0.1,netmask=255.0.0.0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=10.6.28.33,netmask=255.255.255.0"); + APPEND_INTERFACE( 1, "lo", "up,loop,ip=::1,netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,scope=0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=2002:dce8:d28e::33,netmask=ffff:ffff:ffff:ffff::0,scope=0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=fe80::214:5eff:febd:6dda,netmask=ffff:ffff:ffff:ffff::0,scope=2"); +} + +/* with broken IPv6 hostname setup */ +static +void +mock_setup_net2 (void) +{ + mock_hostname = mock_kiku; + + APPEND_HOST ( "127.0.0.1", "localhost"); + APPEND_HOST2( "10.6.28.33", "kiku.hk.miru.hk", "kiku"); + APPEND_HOST( "2002:dce8:d28e::33", "ip6-kiku"); + APPEND_HOST2( "::1", "ip6-localhost", "ip6-loopback"); + + APPEND_INTERFACE( 1, "lo", "up,loop"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast"); + APPEND_INTERFACE( 3, "eth1", "down,broadcast,multicast"); + APPEND_INTERFACE( 1, "lo", "up,loop,ip=127.0.0.1,netmask=255.0.0.0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=10.6.28.33,netmask=255.255.255.0"); + APPEND_INTERFACE( 1, "lo", "up,loop,ip=::1,netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,scope=0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=2002:dce8:d28e::33,netmask=ffff:ffff:ffff:ffff::0,scope=0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=fe80::214:5eff:febd:6dda,netmask=ffff:ffff:ffff:ffff::0,scope=2"); +} + +static +void +mock_teardown_net (void) +{ + GList* list; + + list = mock_hosts; + while (list) { + struct mock_host_t* host = list->data; + g_free (host->canonical_hostname); + if (host->alias) + g_free (host->alias); + g_slice_free1 (sizeof(struct mock_host_t), host); + list = list->next; + } + g_list_free (mock_hosts); + + list = mock_interfaces; + while (list) { + struct mock_interface_t* interface = list->data; + g_free (interface->name); + g_slice_free1 (sizeof(struct mock_interface_t), interface); + list = list->next; + } + g_list_free (mock_interfaces); +} + + +/* mock functions for external references */ + +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return 0; +} + +static +bool +mock_pgm_getifaddrs ( + struct pgm_ifaddrs_t** ifap, + pgm_error_t** err + ) +{ + if (NULL == ifap) { + return FALSE; + } + + g_debug ("mock_getifaddrs (ifap:%p err:%p)", (gpointer)ifap, (gpointer)err); + + GList* list = mock_interfaces; + int n = g_list_length (list); + struct pgm_ifaddrs_t* ifa = malloc (n * sizeof(struct pgm_ifaddrs_t)); + memset (ifa, 0, n * sizeof(struct pgm_ifaddrs_t)); + struct pgm_ifaddrs_t* ift = ifa; + while (list) { + struct mock_interface_t* interface = list->data; + ift->ifa_addr = (gpointer)&interface->addr; + ift->ifa_name = interface->name; + ift->ifa_flags = interface->flags; + ift->ifa_netmask = (gpointer)&interface->netmask; + list = list->next; + if (list) { + ift->ifa_next = ift + 1; + ift = ift->ifa_next; + } + } + + *ifap = ifa; + return TRUE; +} + +static +void +mock_pgm_freeifaddrs ( + struct pgm_ifaddrs_t* ifa + ) +{ + g_debug ("mock_freeifaddrs (ifa:%p)", (gpointer)ifa); + free (ifa); +} + +static +struct hostent* +mock_gethostbyname ( + const char* name + ) +{ + static struct hostent he; + static char* aliases[2]; + static char* addr_list[2]; + +/* pre-conditions */ + g_assert (NULL != name); + + g_debug ("mock_gethostbyname (name:%s%s%s)", + name ? "\"" : "", name ? name : "(null)", name ? "\"" : ""); + + GList* list = mock_hosts; + while (list) { + struct mock_host_t* host = list->data; + const int host_family = ((struct sockaddr*)&host->address)->sa_family; + if (((strcmp (host->canonical_hostname, name) == 0) || + (host->alias && strcmp (host->alias, name) == 0))) + { + he.h_name = host->canonical_hostname; + aliases[0] = host->alias; + aliases[1] = NULL; + he.h_aliases = aliases; + he.h_addrtype = host_family; + switch (host->address.ss_family){ + case AF_INET: + he.h_length = sizeof (struct in_addr); + addr_list[0] = (char*)&host->address + G_STRUCT_OFFSET(struct sockaddr_in, sin_addr); + break; + case AF_INET6: + he.h_length = sizeof (struct in6_addr); + addr_list[0] = (char*)&host->address + G_STRUCT_OFFSET(struct sockaddr_in6, sin6_addr); + break; + default: + g_assert_not_reached(); + } + addr_list[1] = NULL; + he.h_addr_list = addr_list; + return &he; + } + list = list->next; + } + h_errno = HOST_NOT_FOUND; + return NULL; +} + +static +int +mock_getaddrinfo ( + const char* node, + const char* service, + const struct addrinfo* hints, + struct addrinfo** res + ) +{ + const int ai_flags = hints ? hints->ai_flags : (AI_V4MAPPED | AI_ADDRCONFIG); + const int ai_family = hints ? hints->ai_family : AF_UNSPEC; + GList* list; + struct sockaddr_storage addr; + + if (NULL == node && NULL == service) + return EAI_NONAME; + +/* pre-conditions */ + g_assert (NULL != node); + g_assert (NULL == service); + g_assert (!(ai_flags & AI_CANONNAME)); + g_assert (!(ai_flags & AI_NUMERICSERV)); + g_assert (!(ai_flags & AI_V4MAPPED)); + + g_debug ("mock_getaddrinfo (node:\"%s\" service:%s hints:%p res:%p)", + node ? node : "(null)", + service ? service : "(null)", + (gpointer)hints, + (gpointer)res); + + gboolean has_ip4_config; + gboolean has_ip6_config; + + if (hints && hints->ai_flags & AI_ADDRCONFIG) + { + has_ip4_config = has_ip6_config = FALSE; + list = mock_interfaces; + while (list) { + const struct mock_interface_t* interface = list->data; + if (AF_INET == ((struct sockaddr*)&interface->addr)->sa_family) + has_ip4_config = TRUE; + else if (AF_INET6 == ((struct sockaddr*)&interface->addr)->sa_family) + has_ip6_config = TRUE; + if (has_ip4_config && has_ip6_config) + break; + list = list->next; + } + } else { + has_ip4_config = has_ip6_config = TRUE; + } + + if (ai_flags & AI_NUMERICHOST) { + pgm_sockaddr_pton (node, (struct sockaddr*)&addr); + } + list = mock_hosts; + while (list) { + struct mock_host_t* host = list->data; + const int host_family = ((struct sockaddr*)&host->address)->sa_family; + if (((strcmp (host->canonical_hostname, node) == 0) || + (host->alias && strcmp (host->alias, node) == 0) || + (ai_flags & AI_NUMERICHOST && + 0 == pgm_sockaddr_cmp ((struct sockaddr*)&addr, (struct sockaddr*)&host->address))) + && + (host_family == ai_family || AF_UNSPEC == ai_family) && + ((AF_INET == host_family && has_ip4_config) || (AF_INET6 == host_family && has_ip6_config))) + { + struct addrinfo* ai = malloc (sizeof(struct addrinfo)); + memset (ai, 0, sizeof(struct addrinfo)); + ai->ai_family = host_family; + ai->ai_addrlen = AF_INET == host_family ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); + ai->ai_addr = (gpointer)&host->address; + *res = ai; + return 0; + } + list = list->next; + } + return EAI_NONAME; +} + +static +void +mock_freeaddrinfo ( + struct addrinfo* res + ) +{ + g_assert (NULL != res); + g_debug ("mock_freeaddrinfo (res:%p)", (gpointer)res); + free (res); +} + +static +int +mock_gethostname ( + char* name, + size_t len + ) +{ + g_debug ("mock_gethostname (name:%p len:%d)", + (gpointer)name, len); + + if (NULL == name) { + errno = EFAULT; + return -1; + } + if (len < 0) { + errno = EINVAL; + return -1; + } + if (len < (1 + strlen (mock_hostname))) { + errno = ENAMETOOLONG; + return -1; + } +/* force an error */ + if (mock_hostname == mock_toolong) { + errno = ENAMETOOLONG; + return -1; + } + strncpy (name, mock_hostname, len); + if (len > 0) + name[len - 1] = '\0'; + return 0; +} + + +/* target: + * bool + * pgm_if_getnodeaddr ( + * const sa_family_t family, + * struct sockaddr* addr, + * const socklen_t cnt, + * pgm_error_t** error + * ) + */ + +START_TEST (test_getnodeaddr_pass_001) +{ + struct sockaddr_storage addr; + char saddr[INET6_ADDRSTRLEN]; + pgm_error_t* err = NULL; + gboolean success = pgm_if_getnodeaddr (AF_UNSPEC, (struct sockaddr*)&addr, sizeof(addr), &err); + if (!success && err) { + g_error ("Resolving node address with AF_UNSPEC: %s", (err && err->message) ? err->message : "(null)"); + } + fail_unless (TRUE == success, "getnodeaddr failed"); + fail_unless (NULL == err, "error raised"); + pgm_sockaddr_ntop ((struct sockaddr*)&addr, saddr, sizeof(saddr)); + g_message ("AF_UNSPEC:%s", saddr ? saddr : "(null)"); + fail_unless (TRUE == pgm_if_getnodeaddr (AF_INET, (struct sockaddr*)&addr, sizeof(addr), &err), "getnodeaddr failed"); + fail_unless (NULL == err, "error raised"); + pgm_sockaddr_ntop ((struct sockaddr*)&addr, saddr, sizeof(saddr)); + g_message ("AF_INET:%s", saddr ? saddr : "(null)"); + fail_unless (TRUE == pgm_if_getnodeaddr (AF_INET6, (struct sockaddr*)&addr, sizeof(addr), &err), "getnodeaddr failed"); + fail_unless (NULL == err, "error raised"); + pgm_sockaddr_ntop ((struct sockaddr*)&addr, saddr, sizeof(saddr)); + g_message ("AF_INET6:%s", saddr ? saddr : "(null)"); +} +END_TEST + +START_TEST (test_getnodeaddr_fail_001) +{ + pgm_error_t* err = NULL; + fail_unless (FALSE == pgm_if_getnodeaddr (AF_UNSPEC, NULL, 0, &err), "getnodeaddr failed"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + +{ + mock_setup_net(); + + struct sockaddr_storage addr; + char saddr[INET6_ADDRSTRLEN]; + pgm_error_t* err = NULL; + gboolean success = pgm_if_getnodeaddr (AF_UNSPEC, (struct sockaddr*)&addr, sizeof(addr), &err); + if (!success && err) { + g_error ("Resolving node address with AF_UNSPEC: %s", (err && err->message) ? err->message : "(null)"); + } +} + s = suite_create (__FILE__); + + TCase* tc_getnodeaddr = tcase_create ("getnodeaddr"); + suite_add_tcase (s, tc_getnodeaddr); + tcase_add_checked_fixture (tc_getnodeaddr, mock_setup_net, mock_teardown_net); + tcase_add_test (tc_getnodeaddr, test_getnodeaddr_pass_001); + tcase_add_test (tc_getnodeaddr, test_getnodeaddr_fail_001); + + TCase* tc_getnodeaddr2 = tcase_create ("getnodeaddr/2"); + suite_add_tcase (s, tc_getnodeaddr2); + tcase_add_checked_fixture (tc_getnodeaddr2, mock_setup_net2, mock_teardown_net); + tcase_add_test (tc_getnodeaddr2, test_getnodeaddr_pass_001); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/gsi.c b/3rdparty/openpgm-svn-r1085/pgm/gsi.c new file mode 100644 index 0000000..e7ebec4 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/gsi.c @@ -0,0 +1,227 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * global session ID helper functions. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#ifndef _WIN32 +# include +#endif +#include +#include + + +//#define GSI_DEBUG + + +/* create a GSI based on md5 of a user provided data block. + * + * returns TRUE on success, returns FALSE on error and sets error appropriately, + */ + +bool +pgm_gsi_create_from_data ( + pgm_gsi_t* restrict gsi, + const uint8_t* restrict data, + const size_t length + ) +{ + pgm_return_val_if_fail (NULL != gsi, FALSE); + pgm_return_val_if_fail (NULL != data, FALSE); + pgm_return_val_if_fail (length > 1, FALSE); + +#ifdef CONFIG_HAVE_GLIB_CHECKSUM + GChecksum* checksum = g_checksum_new (G_CHECKSUM_MD5); + pgm_return_val_if_fail (NULL != checksum, FALSE); + g_checksum_update (checksum, data, length); + memcpy (gsi, g_checksum_get_string (checksum) + 10, 6); + g_checksum_free (checksum); +#else + struct pgm_md5_t ctx; + char resblock[16]; + pgm_md5_init_ctx (&ctx); + pgm_md5_process_bytes (&ctx, data, length); + pgm_md5_finish_ctx (&ctx, resblock); + memcpy (gsi, resblock + 10, 6); +#endif + return TRUE; +} + +bool +pgm_gsi_create_from_string ( + pgm_gsi_t* restrict gsi, + const char* restrict str, + ssize_t length /* -1 for NULL terminated */ + ) +{ + pgm_return_val_if_fail (NULL != gsi, FALSE); + pgm_return_val_if_fail (NULL != str, FALSE); + + if (length < 0) + length = strlen (str); + + return pgm_gsi_create_from_data (gsi, (const uint8_t*)str, length); +} + +/* create a global session ID as recommended by the PGM draft specification using + * low order 48 bits of md5 of the hostname. + * + * returns TRUE on success, returns FALSE on error and sets error appropriately, + */ + +bool +pgm_gsi_create_from_hostname ( + pgm_gsi_t* restrict gsi, + pgm_error_t** restrict error + ) +{ + pgm_return_val_if_fail (NULL != gsi, FALSE); + + char hostname[NI_MAXHOST]; + int retval = gethostname (hostname, sizeof(hostname)); + if (0 != retval) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("Resolving hostname: %s"), +#ifndef _WIN32 + strerror (errno) +#else + pgm_wsastrerror (WSAGetLastError()) +#endif + ); + return FALSE; + } + + return pgm_gsi_create_from_string (gsi, hostname, -1); +} + +/* create a global session ID based on the IP address. + * + * returns TRUE on succcess, returns FALSE on error and sets error. + */ + +bool +pgm_gsi_create_from_addr ( + pgm_gsi_t* restrict gsi, + pgm_error_t** restrict error + ) +{ + char hostname[NI_MAXHOST]; + struct addrinfo hints, *res = NULL; + + pgm_return_val_if_fail (NULL != gsi, FALSE); + + int retval = gethostname (hostname, sizeof(hostname)); + if (0 != retval) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("Resolving hostname: %s"), +#ifndef _WIN32 + strerror (errno) +#else + pgm_wsastrerror (WSAGetLastError()) +#endif + ); + return FALSE; + } + memset (&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_flags = AI_ADDRCONFIG; + retval = getaddrinfo (hostname, NULL, &hints, &res); + if (0 != retval) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_eai_errno (retval, errno), + _("Resolving hostname address: %s"), + gai_strerror (retval)); + return FALSE; + } + memcpy (gsi, &((struct sockaddr_in*)(res->ai_addr))->sin_addr, sizeof(struct in_addr)); + freeaddrinfo (res); + const uint16_t random_val = pgm_random_int_range (0, UINT16_MAX); + memcpy ((uint8_t*)gsi + sizeof(struct in_addr), &random_val, sizeof(random_val)); + return TRUE; +} + +/* re-entrant form of pgm_gsi_print() + * + * returns number of bytes written to buffer on success, returns -1 on + * invalid parameters. + */ + +int +pgm_gsi_print_r ( + const pgm_gsi_t* restrict gsi, + char* restrict buf, + const size_t bufsize + ) +{ + const uint8_t* restrict src = (const uint8_t* restrict)gsi; + + pgm_return_val_if_fail (NULL != gsi, -1); + pgm_return_val_if_fail (NULL != buf, -1); + pgm_return_val_if_fail (bufsize > 0, -1); + + return snprintf (buf, bufsize, "%i.%i.%i.%i.%i.%i", + src[0], src[1], src[2], src[3], src[4], src[5]); +} + +/* transform GSI to ASCII string form. + * + * on success, returns pointer to ASCII string. on error, returns NULL. + */ + +char* +pgm_gsi_print ( + const pgm_gsi_t* gsi + ) +{ + static char buf[PGM_GSISTRLEN]; + + pgm_return_val_if_fail (NULL != gsi, NULL); + + pgm_gsi_print_r (gsi, buf, sizeof(buf)); + return buf; +} + +/* compare two global session identifier GSI values and return TRUE if they are equal + */ + +bool +pgm_gsi_equal ( + const void* restrict p1, + const void* restrict p2 + ) +{ + const union { + pgm_gsi_t gsi; + uint16_t s[3]; + } *u1 = p1, *u2 = p2; + +/* pre-conditions */ + pgm_assert (NULL != p1); + pgm_assert (NULL != p2); + + return (u1->s[0] == u2->s[0] && u1->s[1] == u2->s[1] && u1->s[2] == u2->s[2]); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/gsi_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/gsi_unittest.c new file mode 100644 index 0000000..dc4c244 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/gsi_unittest.c @@ -0,0 +1,350 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for global session ID helper functions. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* mock state */ + +static char* mock_localhost = "localhost"; +static char* mock_invalid = "invalid.invalid"; /* RFC 2606 */ +static char* mock_toolong = "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij12345"; /* 65 */ +static char* mock_hostname = NULL; + + +static +void +mock_setup_invalid (void) +{ + mock_hostname = mock_invalid; +} + +static +void +mock_setup_toolong (void) +{ + mock_hostname = mock_toolong; +} + +static +void +mock_setup_localhost (void) +{ + mock_hostname = mock_localhost; +} + +static +void +mock_teardown (void) +{ +// null +} + + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + +int +mock_gethostname ( + char* name, + size_t len + ) +{ + if (mock_hostname == mock_toolong) { + errno = EINVAL; + return -1; + } + strncpy (name, mock_hostname, len); + if (len > 0) + name[len - 1] = '\0'; + return 0; +} + + +#define gethostname mock_gethostname + +#define GSI_DEBUG +#include "gsi.c" + + +/* target: + * bool + * pgm_gsi_create_from_hostname ( + * pgm_gsi_t* gsi, + * pgm_error_t** err + * ) + */ + +START_TEST (test_create_from_hostname_pass_001) +{ + pgm_gsi_t gsi; + pgm_error_t* err = NULL; + fail_unless (pgm_gsi_create_from_hostname (&gsi, &err), "create_from_hostname failed"); + fail_if (err, "error raised"); + fail_unless (pgm_gsi_create_from_hostname (&gsi, NULL), "create_from_hostname failed"); +} +END_TEST + +START_TEST (test_create_from_hostname_pass_002) +{ + pgm_error_t* err = NULL; + fail_if (pgm_gsi_create_from_hostname (NULL, &err), "create_from_hostname failed"); + fail_if (err, "error raised"); + fail_if (pgm_gsi_create_from_hostname (NULL, NULL), "create_from_hostname failed"); +} +END_TEST + +/* hostname too long */ +START_TEST (test_create_from_hostname_pass_003) +{ + pgm_gsi_t gsi; + pgm_error_t* err = NULL; + fail_if (pgm_gsi_create_from_hostname (&gsi, &err), "create_from_hostname failed"); + fail_if (NULL == err, "error not raised"); + fail_if (NULL == err->message, "no error message"); + g_debug ("pgm_error_t: %s", err->message); + fail_if (pgm_gsi_create_from_hostname (&gsi, NULL), "create_from_hostname failed"); +} +END_TEST + +/* target: + * bool + * pgm_gsi_create_from_addr ( + * pgm_gsi_t* gsi, + * pgm_error_t** err + * ) + */ + +START_TEST (test_create_from_addr_pass_001) +{ + pgm_gsi_t gsi; + pgm_error_t* err = NULL; + fail_unless (pgm_gsi_create_from_addr (&gsi, &err), "create_from_addr failed"); + fail_if (err, "error raised"); + fail_unless (pgm_gsi_create_from_addr (&gsi, NULL), "create_from_addr failed"); +} +END_TEST + +START_TEST (test_create_from_addr_pass_002) +{ + pgm_error_t* err = NULL; + fail_if (pgm_gsi_create_from_addr (NULL, &err), "create_from_addr failed"); + fail_if (pgm_gsi_create_from_addr (NULL, NULL), "create_from_addr failed"); +} +END_TEST + +/* invalid hostname */ +START_TEST (test_create_from_addr_pass_003) +{ + pgm_gsi_t gsi; + pgm_error_t* err = NULL; + fail_if (pgm_gsi_create_from_addr (&gsi, &err), "create_from_addr failed"); + fail_if (NULL == err, "error not raised"); + fail_if (NULL == err->message, "no error message"); + g_debug ("pgm_error_t: %s", err->message); + fail_if (pgm_gsi_create_from_addr (&gsi, NULL), "create_from_addr failed"); +} +END_TEST + +/* target: + * char* + * pgm_gsi_print ( + * const pgm_gsi_t* gsi + * ) + */ + +START_TEST (test_print_pass_001) +{ + pgm_gsi_t gsi; + fail_unless (pgm_gsi_create_from_hostname (&gsi, NULL), "create_from_hostname failed"); + fail_if (NULL == pgm_gsi_print (&gsi), "print failed"); +} +END_TEST + +START_TEST (test_print_pass_002) +{ + fail_unless (NULL == pgm_gsi_print (NULL), "print failed"); +} +END_TEST + +/* target: + * int + * pgm_gsi_print_r ( + * const pgm_gsi_t* gsi, + * char* buf, + * size_t bufsize + * ) + */ + +START_TEST (test_print_r_pass_001) +{ + pgm_gsi_t gsi; + char buf[PGM_GSISTRLEN]; + fail_unless (pgm_gsi_create_from_hostname (&gsi, NULL), "create_from_hostname failed"); + fail_unless (pgm_gsi_print_r (&gsi, buf, sizeof(buf)) > 0, "print_r failed"); +} +END_TEST + +START_TEST (test_print_r_pass_002) +{ + pgm_gsi_t gsi; + char buf[PGM_GSISTRLEN]; + fail_unless (pgm_gsi_create_from_hostname (&gsi, NULL), "create_from_hostname failed"); + fail_unless (pgm_gsi_print_r (NULL, buf, sizeof(buf)) == -1, "print_r failed"); + fail_unless (pgm_gsi_print_r (&gsi, NULL, sizeof(buf)) == -1, "print_r failed"); + fail_unless (pgm_gsi_print_r (&gsi, buf, 0) == -1, "print_r failed"); +} +END_TEST + +/* target: + * bool + * pgm_gsi_equal ( + * const void* gsi1, + * const void* gsi2 + * ) + */ + +START_TEST (test_equal_pass_001) +{ + pgm_gsi_t gsi1, gsi2; + fail_unless (pgm_gsi_create_from_hostname (&gsi1, NULL), "create_from_hostname failed"); + fail_unless (pgm_gsi_create_from_hostname (&gsi2, NULL), "create_from_hostname failed"); + fail_unless (pgm_gsi_equal (&gsi1, &gsi2), "equal failed"); +} +END_TEST + +START_TEST (test_equal_pass_002) +{ + pgm_gsi_t gsi1, gsi2; + fail_unless (pgm_gsi_create_from_hostname (&gsi1, NULL), "create_from_hostname failed"); + fail_unless (pgm_gsi_create_from_addr (&gsi2, NULL), "create_from_addr failed"); + fail_if (pgm_gsi_equal (&gsi1, &gsi2), "equal failed"); +} +END_TEST + +START_TEST (test_equal_fail_001) +{ + pgm_gsi_t gsi; + fail_unless (pgm_gsi_create_from_hostname (&gsi, NULL), "create_from_hostname failed"); + gboolean retval = pgm_gsi_equal (NULL, &gsi); + fail ("reached"); +} +END_TEST + +START_TEST (test_equal_fail_002) +{ + pgm_gsi_t gsi; + fail_unless (pgm_gsi_create_from_hostname (&gsi, NULL), "create_from_hostname failed"); + gboolean retval = pgm_gsi_equal (&gsi, NULL); + fail ("reached"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_create_from_hostname = tcase_create ("create-from-hostname"); + suite_add_tcase (s, tc_create_from_hostname); + tcase_add_checked_fixture (tc_create_from_hostname, mock_setup_localhost, mock_teardown); + tcase_add_test (tc_create_from_hostname, test_create_from_hostname_pass_001); + tcase_add_test (tc_create_from_hostname, test_create_from_hostname_pass_002); + + TCase* tc_create_from_hostname2 = tcase_create ("create-from-hostname/2"); + suite_add_tcase (s, tc_create_from_hostname2); + tcase_add_checked_fixture (tc_create_from_hostname2, mock_setup_toolong, mock_teardown); + tcase_add_test (tc_create_from_hostname2, test_create_from_hostname_pass_003); + + TCase* tc_create_from_addr = tcase_create ("create-from-addr"); + suite_add_tcase (s, tc_create_from_addr); + tcase_add_checked_fixture (tc_create_from_addr, mock_setup_localhost, mock_teardown); + tcase_add_test (tc_create_from_addr, test_create_from_addr_pass_001); + tcase_add_test (tc_create_from_addr, test_create_from_addr_pass_002); + + TCase* tc_create_from_addr2 = tcase_create ("create-from-addr/2"); + suite_add_tcase (s, tc_create_from_addr2); + tcase_add_checked_fixture (tc_create_from_addr2, mock_setup_invalid, mock_teardown); + tcase_add_test (tc_create_from_addr2, test_create_from_addr_pass_003); + + TCase* tc_print = tcase_create ("print"); + suite_add_tcase (s, tc_print); + tcase_add_checked_fixture (tc_print, mock_setup_localhost, mock_teardown); + tcase_add_test (tc_print, test_print_pass_001); + tcase_add_test (tc_print, test_print_pass_002); + + TCase* tc_print_r = tcase_create ("print-r"); + suite_add_tcase (s, tc_print_r); + tcase_add_checked_fixture (tc_print_r, mock_setup_localhost, mock_teardown); + tcase_add_test (tc_print_r, test_print_r_pass_001); + tcase_add_test (tc_print_r, test_print_r_pass_002); + + TCase* tc_equal = tcase_create ("equal"); + suite_add_tcase (s, tc_equal); + tcase_add_checked_fixture (tc_equal, mock_setup_localhost, mock_teardown); + tcase_add_test (tc_equal, test_equal_pass_001); + tcase_add_test (tc_equal, test_equal_pass_002); + tcase_add_test_raise_signal (tc_equal, test_equal_fail_001, SIGABRT); + tcase_add_test_raise_signal (tc_equal, test_equal_fail_002, SIGABRT); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/hashtable.c b/3rdparty/openpgm-svn-r1085/pgm/hashtable.c new file mode 100644 index 0000000..da842aa --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/hashtable.c @@ -0,0 +1,327 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable hashtable. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + + +//#define HASHTABLE_DEBUG + +#define HASHTABLE_MIN_SIZE 11 +#define HASHTABLE_MAX_SIZE 13845163 + +struct pgm_hashnode_t +{ + const void* key; + void* value; + struct pgm_hashnode_t* next; + uint_fast32_t key_hash; +}; + +typedef struct pgm_hashnode_t pgm_hashnode_t; + +struct pgm_hashtable_t +{ + unsigned size; + unsigned nnodes; + pgm_hashnode_t** nodes; + pgm_hashfunc_t hash_func; + pgm_equalfunc_t key_equal_func; +}; + +#define PGM_HASHTABLE_RESIZE(hash_ttable) \ + do { \ + if ( (hash_table->size >= 3 * hash_table->nnodes && hash_table->size > HASHTABLE_MIN_SIZE) || \ + (3 * hash_table->size <= hash_table->nnodes && hash_table->size < HASHTABLE_MAX_SIZE) ) \ + { \ + pgm_hashtable_resize (hash_table); \ + } \ + } while (0) + +static void pgm_hashtable_resize (pgm_hashtable_t*); +static pgm_hashnode_t** pgm_hashtable_lookup_node (const pgm_hashtable_t*restrict, const void*restrict, pgm_hash_t*restrict) PGM_GNUC_PURE; +static pgm_hashnode_t* pgm_hash_node_new (const void*restrict, void*restrict, const pgm_hash_t); +static void pgm_hash_node_destroy (pgm_hashnode_t*); +static void pgm_hash_nodes_destroy (pgm_hashnode_t*); + + +pgm_hashtable_t* +pgm_hashtable_new ( + pgm_hashfunc_t hash_func, + pgm_equalfunc_t key_equal_func + ) +{ + pgm_return_val_if_fail (NULL != hash_func, NULL); + pgm_return_val_if_fail (NULL != key_equal_func, NULL); + + pgm_hashtable_t *hash_table; + + hash_table = pgm_new (pgm_hashtable_t, 1); + hash_table->size = HASHTABLE_MIN_SIZE; + hash_table->nnodes = 0; + hash_table->hash_func = hash_func; + hash_table->key_equal_func = key_equal_func; + hash_table->nodes = pgm_new0 (pgm_hashnode_t*, hash_table->size); + + return hash_table; +} + +void +pgm_hashtable_unref ( + pgm_hashtable_t* hash_table + ) +{ + pgm_return_if_fail (hash_table != NULL); + + for (unsigned i = 0; i < hash_table->size; i++) + pgm_hash_nodes_destroy (hash_table->nodes[i]); + pgm_free (hash_table->nodes); + pgm_free (hash_table); +} + +void +pgm_hashtable_destroy ( + pgm_hashtable_t* hash_table + ) +{ + pgm_return_if_fail (hash_table != NULL); + + pgm_hashtable_remove_all (hash_table); + pgm_hashtable_unref (hash_table); +} + +static inline +pgm_hashnode_t** +pgm_hashtable_lookup_node ( + const pgm_hashtable_t* restrict hash_table, + const void* restrict key, + pgm_hash_t* restrict hash_return /* non-NULL to return hash value */ + ) +{ + const pgm_hash_t hash_value = (*hash_table->hash_func) (key); + pgm_hashnode_t** node = &hash_table->nodes[hash_value % hash_table->size]; + + if (hash_return) + *hash_return = hash_value; + + while (*node && (((*node)->key_hash != hash_value) || + !(*hash_table->key_equal_func) ((*node)->key, key))) + { + node = &(*node)->next; + } + + return node; +} + +void* +pgm_hashtable_lookup ( + const pgm_hashtable_t* restrict hash_table, + const void* restrict key + ) +{ + pgm_return_val_if_fail (hash_table != NULL, NULL); + + const pgm_hashnode_t* node = *pgm_hashtable_lookup_node (hash_table, key, NULL); + return node ? node->value : NULL; +} + +void* +pgm_hashtable_lookup_extended ( + const pgm_hashtable_t* restrict hash_table, + const void* restrict key, + void* restrict hash_return + ) +{ + pgm_return_val_if_fail (hash_table != NULL, NULL); + + const pgm_hashnode_t* node = *pgm_hashtable_lookup_node (hash_table, key, hash_return); + return node ? node->value : NULL; +} + +void +pgm_hashtable_insert ( + pgm_hashtable_t* restrict hash_table, + const void* restrict key, + void* restrict value + ) +{ + pgm_hashnode_t **node; + pgm_hash_t key_hash; + + pgm_return_if_fail (hash_table != NULL); + + node = pgm_hashtable_lookup_node (hash_table, key, &key_hash); + pgm_return_if_fail (NULL == *node); + + *node = pgm_hash_node_new (key, value, key_hash); + hash_table->nnodes++; + PGM_HASHTABLE_RESIZE (hash_table); +} + +bool +pgm_hashtable_remove ( + pgm_hashtable_t* restrict hash_table, + const void* restrict key + ) +{ + pgm_hashnode_t **node, *dest; + + pgm_return_val_if_fail (hash_table != NULL, FALSE); + + node = pgm_hashtable_lookup_node (hash_table, key, NULL); + if (*node) + { + dest = *node; + (*node) = dest->next; + pgm_hash_node_destroy (dest); + hash_table->nnodes--; + PGM_HASHTABLE_RESIZE (hash_table); + return TRUE; + } + return FALSE; +} + +void +pgm_hashtable_remove_all ( + pgm_hashtable_t* hash_table + ) +{ + pgm_return_if_fail (hash_table != NULL); + + for (unsigned i = 0; i < hash_table->size; i++) + { + pgm_hash_nodes_destroy (hash_table->nodes[i]); + hash_table->nodes[i] = NULL; + } + hash_table->nnodes = 0; + PGM_HASHTABLE_RESIZE (hash_table); +} + +static +void +pgm_hashtable_resize ( + pgm_hashtable_t* hash_table + ) +{ + const unsigned new_size = CLAMP (pgm_spaced_primes_closest (hash_table->nnodes), + HASHTABLE_MIN_SIZE, HASHTABLE_MAX_SIZE); + pgm_hashnode_t** new_nodes = pgm_new0 (pgm_hashnode_t*, new_size); + + for (unsigned i = 0; i < hash_table->size; i++) + for (pgm_hashnode_t *node = hash_table->nodes[i], *next; node; node = next) + { + next = node->next; + const pgm_hash_t hash_val = node->key_hash % new_size; + node->next = new_nodes[hash_val]; + new_nodes[hash_val] = node; + } + + pgm_free (hash_table->nodes); + hash_table->nodes = new_nodes; + hash_table->size = new_size; +} + +static +pgm_hashnode_t* +pgm_hash_node_new ( + const void* restrict key, + void* restrict value, + const pgm_hash_t key_hash + ) +{ + pgm_hashnode_t *hash_node = pgm_new (pgm_hashnode_t, 1); + hash_node->key = key; + hash_node->value = value; + hash_node->key_hash = key_hash; + hash_node->next = NULL; + return hash_node; +} + +static +void +pgm_hash_node_destroy ( + pgm_hashnode_t* hash_node + ) +{ + pgm_free (hash_node); +} + +static +void +pgm_hash_nodes_destroy ( + pgm_hashnode_t* hash_node + ) +{ + while (hash_node) { + pgm_hashnode_t *next = hash_node->next; + pgm_free (hash_node); + hash_node = next; + } +} + +/* common hash value compare and hash key generation functions */ + +bool +pgm_str_equal ( + const void* restrict p1, + const void* restrict p2 + ) +{ + const char *restrict s1 = p1, *restrict s2 = p2; + return (strcmp (s1, s2) == 0); +} + +/* 31 bit hash function */ + +pgm_hash_t +pgm_str_hash ( + const void* p + ) +{ + const char* s = p; + pgm_hash_t hash_val = *s; + + if (PGM_LIKELY (hash_val)) + for (s++; *s; s++) + hash_val = (hash_val << 5) - hash_val + *s; + return hash_val; +} + +bool +pgm_int_equal ( + const void* restrict p1, + const void* restrict p2 + ) +{ + const int i1 = *(const int*restrict)p1, i2 = *(const int*restrict)p2; + return (i1 == i2); +} + +pgm_hash_t +pgm_int_hash ( + const void* p + ) +{ + const int i = *(const int*)p; + return (pgm_hash_t)i; +} + + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/histogram.c b/3rdparty/openpgm-svn-r1085/pgm/histogram.c new file mode 100644 index 0000000..3e5ad66 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/histogram.c @@ -0,0 +1,414 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Histograms. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + + +//#define HISTOGRAM_DEBUG + + +pgm_slist_t* pgm_histograms = NULL; + + +static void sample_set_accumulate (pgm_sample_set_t*, pgm_sample_t, pgm_count_t, unsigned); +static pgm_count_t sample_set_total_count (const pgm_sample_set_t*) PGM_GNUC_PURE; + +static void set_bucket_range (pgm_histogram_t*, unsigned, pgm_sample_t); +static void initialize_bucket_range (pgm_histogram_t*); +static unsigned bucket_index (const pgm_histogram_t*, const pgm_sample_t); +static void accumulate (pgm_histogram_t*, pgm_sample_t, pgm_count_t, unsigned); +static double get_peak_bucket_size (const pgm_histogram_t*restrict, const pgm_sample_set_t*restrict); +static double get_bucket_size (const pgm_histogram_t*, const pgm_count_t, const unsigned); + +static void pgm_histogram_write_html_graph (pgm_histogram_t*restrict, pgm_string_t*restrict); +static void write_ascii (pgm_histogram_t*restrict, const char*restrict, pgm_string_t*restrict); +static void write_ascii_header (pgm_histogram_t*restrict, pgm_sample_set_t*restrict, pgm_count_t, pgm_string_t*restrict); +static void write_ascii_bucket_graph (double, double, pgm_string_t*); +static void write_ascii_bucket_context (int64_t, pgm_count_t, int64_t, unsigned, pgm_string_t*); +static void write_ascii_bucket_value (pgm_count_t, double, pgm_string_t*); +static pgm_string_t* get_ascii_bucket_range (pgm_histogram_t*, unsigned); + + +void +pgm_histogram_add ( + pgm_histogram_t* histogram, + int value + ) +{ + if (value > INT_MAX) + value = INT_MAX - 1; + if (value < 0) + value = 0; + const unsigned i = bucket_index (histogram, value); + pgm_assert (value >= histogram->ranges[ i ]); + pgm_assert (value < histogram->ranges[ i + 1 ]); + accumulate (histogram, value, 1, i); +} + +void +pgm_histogram_write_html_graph_all ( + pgm_string_t* string + ) +{ + if (!pgm_histograms) + return; + pgm_slist_t* snapshot = pgm_histograms; + while (snapshot) { + pgm_histogram_t* histogram = snapshot->data; + pgm_histogram_write_html_graph (histogram, string); + snapshot = snapshot->next; + } +} + +static +void +pgm_histogram_write_html_graph ( + pgm_histogram_t* histogram, + pgm_string_t* string + ) +{ + pgm_string_append (string, "
");
+	write_ascii (histogram, "
", string); + pgm_string_append (string, "
"); +} + +static +void +sample_set_accumulate ( + pgm_sample_set_t* sample_set, + pgm_sample_t value, + pgm_count_t count, + unsigned i + ) +{ + pgm_assert (1 == count || -1 == count); + sample_set->counts[ i ] += count; + sample_set->sum += count * value; + sample_set->square_sum += (count * value) * (int64_t)value; + pgm_assert (sample_set->counts[ i ] >= 0); + pgm_assert (sample_set->sum >= 0); + pgm_assert (sample_set->square_sum >= 0); +} + +static +pgm_count_t +sample_set_total_count ( + const pgm_sample_set_t* sample_set + ) +{ + pgm_count_t total = 0; + for (unsigned i = 0; i < sample_set->counts_len; i++) + total += sample_set->counts[ i ]; + return total; +} + +void +pgm_histogram_init ( + pgm_histogram_t* histogram + ) +{ + if (histogram->declared_min <= 0) + histogram->declared_min = 1; + pgm_assert (histogram->declared_min > 0); + histogram->declared_max = INT_MAX - 1; + pgm_assert (histogram->declared_min <= histogram->declared_max); + pgm_assert (1 < histogram->bucket_count); + set_bucket_range (histogram, histogram->bucket_count, INT_MAX); + initialize_bucket_range (histogram); + +/* register with global list */ + histogram->histograms_link.data = histogram; + histogram->histograms_link.next = pgm_histograms; + pgm_histograms = &histogram->histograms_link; + histogram->is_registered = TRUE; +} + +static +void +set_bucket_range ( + pgm_histogram_t* histogram, + unsigned i, + pgm_sample_t value + ) +{ + histogram->ranges[ i ] = value; +} + +static +void +initialize_bucket_range ( + pgm_histogram_t* histogram + ) +{ + const double log_max = log(histogram->declared_max); + double log_ratio; + double log_next; + unsigned i = 1; + pgm_sample_t current = histogram->declared_min; + + set_bucket_range (histogram, i, current); + while (histogram->bucket_count > ++i) { + double log_current = log(current); + log_ratio = (log_max - log_current) / (histogram->bucket_count - i); + log_next = log_current + log_ratio; + int next = floor(exp(log_next) + 0.5); + if (next > current) + current = next; + else + current++; + set_bucket_range (histogram, i, current); + } + pgm_assert (histogram->bucket_count == i); +} + +static +unsigned +bucket_index ( + const pgm_histogram_t* histogram, + const pgm_sample_t value + ) +{ + pgm_assert (histogram->ranges[0] <= value); + pgm_assert (histogram->ranges[ histogram->bucket_count ] > value); + unsigned under = 0; + unsigned over = histogram->bucket_count; + unsigned mid; + + do { + pgm_assert (over >= under); + mid = ((unsigned)under + (unsigned)over) >> 1; + if (mid == under) + break; + if (histogram->ranges[ mid ] <= value) + under = mid; + else + over = mid; + } while (TRUE); + pgm_assert (histogram->ranges[ mid ] <= value && + histogram->ranges[ mid + 1] > value); + return mid; +} + +static +void +accumulate ( + pgm_histogram_t* histogram, + pgm_sample_t value, + pgm_count_t count, + unsigned i + ) +{ + sample_set_accumulate (&histogram->sample, value, count, i); +} + +static +void +write_ascii ( + pgm_histogram_t* restrict histogram, + const char* restrict newline, + pgm_string_t* restrict output + ) +{ + pgm_count_t snapshot_counts[ histogram->sample.counts_len ]; + pgm_sample_set_t snapshot = { + .counts = snapshot_counts, + .counts_len = histogram->sample.counts_len, + .sum = histogram->sample.sum, + .square_sum = histogram->sample.square_sum + }; + memcpy (snapshot_counts, histogram->sample.counts, sizeof(pgm_count_t) * histogram->sample.counts_len); + + pgm_count_t sample_count = sample_set_total_count (&snapshot); + write_ascii_header (histogram, &snapshot, sample_count, output); + pgm_string_append (output, newline); + + double max_size = get_peak_bucket_size (histogram, &snapshot); + unsigned largest_non_empty_bucket = histogram->bucket_count - 1; + while (0 == snapshot.counts[ largest_non_empty_bucket ]) + { + if (0 == largest_non_empty_bucket) + break; + largest_non_empty_bucket--; + } + + int print_width = 1; + for (unsigned i = 0; i < histogram->bucket_count; ++i) + { + if (snapshot.counts[ i ]) { + pgm_string_t* bucket_range = get_ascii_bucket_range (histogram, i); + const int width = bucket_range->len + 1; + pgm_string_free (bucket_range, TRUE); + if (width > print_width) + print_width = width; + } + } + + int64_t remaining = sample_count; + int64_t past = 0; + for (unsigned i = 0; i < histogram->bucket_count; ++i) + { + pgm_count_t current = snapshot.counts[ i ]; + remaining -= current; + pgm_string_t* bucket_range = get_ascii_bucket_range (histogram, i); + pgm_string_append_printf (output, "%*s ", print_width, bucket_range->str); + pgm_string_free (bucket_range, TRUE); + if (0 == current && + i < histogram->bucket_count - 1 && + 0 == snapshot.counts[ i + 1 ]) + { + while (i < histogram->bucket_count - 1 && + 0 == snapshot.counts[ i + 1 ]) + { + i++; + } + pgm_string_append (output, "... "); + pgm_string_append (output, newline); + continue; + } + + const double current_size = get_bucket_size (histogram, current, i); + write_ascii_bucket_graph (current_size, max_size, output); + write_ascii_bucket_context (past, current, remaining, i, output); + pgm_string_append (output, newline); + past += current; + } +} + +static +void +write_ascii_header ( + pgm_histogram_t* restrict histogram, + pgm_sample_set_t* restrict sample_set, + pgm_count_t sample_count, + pgm_string_t* restrict output + ) +{ + pgm_string_append_printf (output, + "Histogram: %s recorded %d samples", + histogram->histogram_name ? histogram->histogram_name : "(null)", + sample_count); + if (sample_count > 0) { + const double average = sample_set->sum / sample_count; + const double variance = sample_set->square_sum / sample_count + - average * average; + const double standard_deviation = sqrt (variance); + pgm_string_append_printf (output, + ", average = %.1f, standard deviation = %.1f", + average, standard_deviation); + } +} + +static +void +write_ascii_bucket_graph ( + double current_size, + double max_size, + pgm_string_t* output + ) +{ + static const int k_line_length = 72; + int x_count = (k_line_length * (current_size / max_size) + 0.5); + int x_remainder = k_line_length - x_count; + while (0 < x_count--) + pgm_string_append_c (output, '-'); + pgm_string_append_c (output, 'O'); + while (0 < x_remainder--) + pgm_string_append_c (output, ' '); +} + +static +void +write_ascii_bucket_context ( + int64_t past, + pgm_count_t current, + int64_t remaining, + unsigned i, + pgm_string_t* output + ) +{ + const double scaled_sum = (past + current + remaining) / 100.0; + write_ascii_bucket_value (current, scaled_sum, output); + if (0 < i) { + const double percentage = past / scaled_sum; + pgm_string_append_printf (output, " {%3.1f%%}", percentage); + } +} + +static +void +write_ascii_bucket_value ( + pgm_count_t current, + double scaled_sum, + pgm_string_t* output + ) +{ + pgm_string_append_printf (output, " (%d = %3.1f%%)", current, current/scaled_sum); +} + +static +double +get_peak_bucket_size ( + const pgm_histogram_t* restrict histogram, + const pgm_sample_set_t* restrict sample_set + ) +{ + double max_size = 0; + for (unsigned i = 0; i < histogram->bucket_count; i++) { + const double current_size = get_bucket_size (histogram, sample_set->counts[ i ], i); + if (current_size > max_size) + max_size = current_size; + } + return max_size; +} + +static +double +get_bucket_size ( + const pgm_histogram_t* histogram, + const pgm_count_t current, + const unsigned i + ) +{ + pgm_assert (histogram->ranges[ i + 1 ] > histogram->ranges[ i ]); + static const double kTransitionWidth = 5; + double denominator = histogram->ranges[ i + 1 ] - histogram->ranges[ i ]; + if (denominator > kTransitionWidth) + denominator = kTransitionWidth; + return current / denominator; +} + +static +pgm_string_t* +get_ascii_bucket_range ( + pgm_histogram_t* histogram, + unsigned i + ) +{ + pgm_string_t* result = pgm_string_new (NULL); + pgm_string_printf (result, "%d", histogram->ranges[ i ]); + return result; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/htdocs/404.html b/3rdparty/openpgm-svn-r1085/pgm/htdocs/404.html new file mode 100644 index 0000000..538c90a --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/htdocs/404.html @@ -0,0 +1,11 @@ + + + + OpenPGM - Page Not Found + + + +

Lah, page not found.

+

Return to main page

+ + diff --git a/3rdparty/openpgm-svn-r1085/pgm/htdocs/base.css b/3rdparty/openpgm-svn-r1085/pgm/htdocs/base.css new file mode 100644 index 0000000..5aba236 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/htdocs/base.css @@ -0,0 +1,136 @@ +html { + background-color: white; + font-family: Verdana; + font-size: 12px; + color: black; +} + +a, a:link, a:visited { + color: #0033cc; + text-decoration: none; +} + +#header { + text-align: right; +} + +#header #hostname { + font-weight: bold; +} + +#header a { + color: black; +} + +#header a:hover { + text-decoration: underline; +} + +#footer { + clear: both; + margin-top: 3.5em; + margin-bottom: 1em; + padding-top: 20px; + text-align: center; +} + +#navigation a { + color: black; +} + +#navigation .tab { + -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; + padding: 4px 1em 2px; + margin-right: 8px; + float: left; + font-weight: bold; +} + +#navigation #tabtop,#tabline { + background-color: #fb879c; +} + +#navigation #tabbottom { + background-color: #fbc1a9; +} + +#navigation #tabline { + clear: left; + -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; + height: 4px; +} + +#content { + margin-top: 6px; + padding: 3px; +} + +#content a:hover { + background: #ffffaa; +} + +.heading { + -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; + background-color: #fb879c; + padding: 6px; + margin-bottom: 3px; +} + +table { + border-collapse: separate; +} + +th { + text-align: left; +} + +#information { + float: right; +} + +.rounded { + background-color: #fbc1a9; + -moz-border-radius: 4px; -webkit-border-radius: 4px; + padding: 5px; + margin-top: 6px; + margin-bottom: 6px; + width: 25em; +} + +.break { + border-top: 3px solid white; + margin-top: 1em; + padding-top: 6px; +} + +.bubbly { + background-color: #fb879c; + -moz-border-radius: 4px; -webkit-border-radius: 4px; + padding: 4px; +} + +.bubbly table { + width: 100%; +} + +.bubbly th,.bubbly td { + border-bottom: 1px solid #bbbbbb; +} + +.bubbly th { + background-color: #fbc1a9; + border-left: 1px solid #bbbbbb; + padding: 2px 1px 2px 2px; +} + +.bubbly td { + background-color: white; + padding: 4px; +} + +.bubbly .empty { + padding: 3em; + text-align: center; +} diff --git a/3rdparty/openpgm-svn-r1085/pgm/htdocs/convert_to_macro.pl b/3rdparty/openpgm-svn-r1085/pgm/htdocs/convert_to_macro.pl new file mode 100755 index 0000000..bea44af --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/htdocs/convert_to_macro.pl @@ -0,0 +1,20 @@ +#!/usr/bin/perl + +use strict; +use File::Basename; + +die "usage: $0 [text file]\n" unless ($ARGV[0]); +open(MOO, $ARGV[0]) or die "cannot open $ARGV[0]: $!"; +my $all = do { local $/; }; +close(MOO); +$all =~ s/"/\\"/g; +$all =~ s/\n/\\n/mg; +$all =~ s/\r/\\r/mg; + +my $var = uc (basename($ARGV[0])); +$var =~ s/\s+/_/g; +$var =~ s/\./_/g; + +print< + + diff --git a/3rdparty/openpgm-svn-r1085/pgm/http.c b/3rdparty/openpgm-svn-r1085/pgm/http.c new file mode 100644 index 0000000..82e934c --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/http.c @@ -0,0 +1,1718 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * HTTP administrative interface + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#ifndef _WIN32 +# include +#else +# include +# include +# include +#endif +#include +#include +#include +#include +#include +#include +#include + +#include "pgm/http.h" +#include "htdocs/404.html.h" +#include "htdocs/base.css.h" +#include "htdocs/robots.txt.h" +#include "htdocs/xhtml10_strict.doctype.h" + + +/* OpenSolaris */ +#ifndef LOGIN_NAME_MAX +# ifdef _WIN32 +# define LOGIN_NAME_MAX (UNLEN + 1) +# else +# define LOGIN_NAME_MAX 256 +# endif +#endif + +#ifdef _WIN32 +# define getpid _getpid +# define read _read +# define write _write +# define SHUT_WR SD_SEND +#endif + +#ifdef CONFIG_HAVE_SPRINTF_GROUPING +# define GROUP_FORMAT "'" +#else +# define GROUP_FORMAT "" +#endif + +#define HTTP_BACKLOG 10 /* connections */ +#define HTTP_TIMEOUT 60 /* seconds */ + + +/* locals */ + +struct http_connection_t { + pgm_list_t link_; + int sock; + enum { + HTTP_STATE_READ, + HTTP_STATE_WRITE, + HTTP_STATE_FINWAIT + } state; + + char* buf; + size_t buflen; + size_t bufoff; + unsigned status_code; + const char* status_text; + const char* content_type; +}; + +enum { + HTTP_MEMORY_STATIC, + HTTP_MEMORY_TAKE +}; + +static char http_hostname[NI_MAXHOST + 1]; +static char http_address[INET6_ADDRSTRLEN]; +static char http_username[LOGIN_NAME_MAX + 1]; +static int http_pid; + +#ifndef _WIN32 +static int http_sock = -1; +static pthread_t http_thread; +static void* http_routine (void*); +#else +static int http_sock = INVALID_SOCKET; +static HANDLE http_thread; +static unsigned __stdcall http_routine (void*); +#endif +static int http_max_sock = -1; +static fd_set http_readfds, http_writefds, http_exceptfds; +static pgm_list_t* http_socks = NULL; +static pgm_notify_t http_notify = PGM_NOTIFY_INIT; +static volatile uint32_t http_ref_count = 0; + + +static int http_tsi_response (struct http_connection_t*, pgm_tsi_t*); +static void http_each_receiver (pgm_peer_t*, pgm_string_t*); +static int http_receiver_response (struct http_connection_t*, pgm_peer_t*); + +static void default_callback (struct http_connection_t*, const char*); +static void robots_callback (struct http_connection_t*, const char*); +static void css_callback (struct http_connection_t*, const char*); +static void index_callback (struct http_connection_t*, const char*); +static void interfaces_callback (struct http_connection_t*, const char*); +static void transports_callback (struct http_connection_t*, const char*); +static void histograms_callback (struct http_connection_t*, const char*); + +static struct { + const char* path; + void (*callback) (struct http_connection_t*, const char*); +} http_directory[] = { + { "/robots.txt", robots_callback }, + { "/base.css", css_callback }, + { "/", index_callback }, + { "/interfaces", interfaces_callback }, + { "/transports", transports_callback } +#ifdef CONFIG_HISTOGRAMS + ,{ "/histograms", histograms_callback } +#endif +}; + + +static +int +http_sock_rcvtimeo ( + int sock, + int seconds + ) +{ +#if defined( sun ) + return 0; +#elif !defined( _WIN32 ) + const struct timeval timeout = { .tv_sec = seconds, .tv_usec = 0 }; + return setsockopt (sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)); +#else + const int optval = seconds * 1000; + return setsockopt (sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&optval, sizeof(optval)); +#endif +} + +static +int +http_sock_sndtimeo ( + int sock, + int seconds + ) +{ +#if defined( sun ) + return 0; +#elif !defined( _WIN32 ) + const struct timeval timeout = { .tv_sec = seconds, .tv_usec = 0 }; + return setsockopt (sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout)); +#else + const int optval = seconds * 1000; + return setsockopt (sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&optval, sizeof(optval)); +#endif +} + +bool +pgm_http_init ( + uint16_t http_port, + pgm_error_t** error + ) +{ + int e; + + if (pgm_atomic_exchange_and_add32 (&http_ref_count, 1) > 0) + return TRUE; + +/* resolve and store relatively constant runtime information */ + if (0 != gethostname (http_hostname, sizeof(http_hostname))) { + const int save_errno = errno; + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_errno (save_errno), + _("Resolving hostname: %s"), + strerror (save_errno)); + goto err_cleanup; + } + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, + .ai_protocol = IPPROTO_TCP, + .ai_flags = AI_ADDRCONFIG + }, *res = NULL; + e = getaddrinfo (http_hostname, NULL, &hints, &res); + if (0 != e) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_eai_errno (e, errno), + _("Resolving hostname address: %s"), + gai_strerror (e)); + goto err_cleanup; + } + e = getnameinfo (res->ai_addr, res->ai_addrlen, + http_address, sizeof(http_address), + NULL, 0, + NI_NUMERICHOST); + if (0 != e) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_eai_errno (e, errno), + _("Resolving numeric hostname: %s"), + gai_strerror (e)); + goto err_cleanup; + } + freeaddrinfo (res); +#ifndef _WIN32 + e = getlogin_r (http_username, sizeof(http_username)); + if (0 != e) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_errno (errno), + _("Retrieving user name: %s"), + strerror (errno)); + goto err_cleanup; + } +#else + wchar_t wusername[UNLEN + 1]; + DWORD nSize = PGM_N_ELEMENTS( wusername ); + if (!GetUserNameW (wusername, &nSize)) { + const DWORD save_errno = GetLastError(); + char winstr[1024]; + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_win_errno (save_errno), + _("Retrieving user name: %s"), + pgm_win_strerror (winstr, sizeof(winstr), save_errno)); + goto err_cleanup; + } + WideCharToMultiByte (CP_UTF8, 0, wusername, nSize + 1, http_username, sizeof(http_username), NULL, NULL); +#endif /* _WIN32 */ + http_pid = getpid(); + +/* create HTTP listen socket */ + if ((http_sock = socket (AF_INET, SOCK_STREAM, 0)) < 0) { +#ifndef _WIN32 + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_errno (errno), + _("Creating HTTP socket: %s"), + strerror (errno)); +#else + const int save_errno = WSAGetLastError(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_wsa_errno (save_errno), + _("Creating HTTP socket: %s"), + pgm_wsastrerror (save_errno)); +#endif + goto err_cleanup; + } + const int v = 1; + if (0 != setsockopt (http_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&v, sizeof(v))) { +#ifndef _WIN32 + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_errno (errno), + _("Enabling reuse of socket local address: %s"), + strerror (errno)); +#else + const int save_errno = WSAGetLastError(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_wsa_errno (save_errno), + _("Enabling reuse of socket local address: %s"), + pgm_wsastrerror (save_errno)); +#endif + goto err_cleanup; + } + if (0 != http_sock_rcvtimeo (http_sock, HTTP_TIMEOUT) || + 0 != http_sock_sndtimeo (http_sock, HTTP_TIMEOUT)) { +#ifndef _WIN32 + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_errno (errno), + _("Setting socket timeout: %s"), + strerror (errno)); +#else + const int save_errno = WSAGetLastError(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_wsa_errno (save_errno), + _("Setting socket timeout: %s"), + pgm_wsastrerror (save_errno)); +#endif + goto err_cleanup; + } + struct sockaddr_in http_addr; + memset (&http_addr, 0, sizeof(http_addr)); + http_addr.sin_family = AF_INET; + http_addr.sin_addr.s_addr = INADDR_ANY; + http_addr.sin_port = htons (http_port); + if (0 != bind (http_sock, (struct sockaddr*)&http_addr, sizeof(http_addr))) { + char addr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&http_addr, addr, sizeof(addr)); +#ifndef _WIN32 + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_errno (errno), + _("Binding HTTP socket to address %s: %s"), + addr, + strerror (errno)); +#else + const int save_errno = WSAGetLastError(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_wsa_errno (save_errno), + _("Binding HTTP socket to address %s: %s"), + addr, + pgm_wsastrerror (save_errno)); +#endif + goto err_cleanup; + } + if (listen (http_sock, HTTP_BACKLOG) < 0) { +#ifndef _WIN32 + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_errno (errno), + _("Listening to HTTP socket: %s"), + strerror (errno)); +#else + const int save_errno = WSAGetLastError(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_wsa_errno (save_errno), + _("Listening to HTTP socket: %s"), + pgm_wsastrerror (save_errno)); +#endif + goto err_cleanup; + } + +/* non-blocking notification of new connections */ + pgm_sockaddr_nonblocking (http_sock, TRUE); + +/* create notification channel */ + if (0 != pgm_notify_init (&http_notify)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_errno (errno), + _("Creating HTTP notification channel: %s"), + strerror (errno)); + goto err_cleanup; + } + +/* spawn thread to handle HTTP requests */ +#ifndef _WIN32 + const int status = pthread_create (&http_thread, NULL, &http_routine, NULL); + if (0 != status) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_errno (errno), + _("Creating HTTP thread: %s"), + strerror (errno)); + goto err_cleanup; + } +#else + http_thread = (HANDLE)_beginthreadex (NULL, 0, &http_routine, NULL, 0, NULL); + const int save_errno = errno; + if (0 == http_thread) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_errno (save_errno), + _("Creating HTTP thread: %s"), + strerror (save_errno)); + goto err_cleanup; + } +#endif /* _WIN32 */ + pgm_minor (_("Web interface: http://%s:%i"), + http_hostname, + http_port); + return TRUE; + +err_cleanup: +#ifndef _WIN32 + if (-1 != http_sock) { + close (http_sock); + http_sock = -1; + } +#else + if (INVALID_SOCKET != http_sock) { + closesocket (http_sock); + http_sock = INVALID_SOCKET; + } +#endif /* _WIN32 */ + if (pgm_notify_is_valid (&http_notify)) { + pgm_notify_destroy (&http_notify); + } + pgm_atomic_dec32 (&http_ref_count); + return FALSE; +} + +/* notify HTTP thread to shutdown, wait for shutdown and cleanup. + */ + +bool +pgm_http_shutdown (void) +{ + pgm_return_val_if_fail (pgm_atomic_read32 (&http_ref_count) > 0, FALSE); + + if (pgm_atomic_exchange_and_add32 (&http_ref_count, (uint32_t)-1) != 1) + return TRUE; + + pgm_notify_send (&http_notify); +#ifndef _WIN32 + pthread_join (http_thread, NULL); +#else + CloseHandle (http_thread); +#endif +#ifndef _WIN32 + if (-1 != http_sock) { + close (http_sock); + http_sock = -1; + } +#else + if (INVALID_SOCKET != http_sock) { + closesocket (http_sock); + http_sock = INVALID_SOCKET; + } +#endif /* _WIN32 */ + pgm_notify_destroy (&http_notify); + return TRUE; +} + +/* accept a new incoming HTTP connection. + */ + +static +void +http_accept ( + int listen_sock + ) +{ +/* new connection */ + struct sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + int new_sock = accept (listen_sock, (struct sockaddr*)&addr, &addrlen); + if (-1 == new_sock) { + if (EAGAIN == errno) + return; +#ifndef _WIN32 + pgm_warn (_("HTTP accept: %s"), strerror (errno)); +#else + const int save_errno = WSAGetLastError(); + pgm_warn (_("HTTP accept: %s"), pgm_wsastrerror (save_errno)); +#endif + return; + } + +#ifndef _WIN32 +/* out of bounds file descriptor for select() */ + if (new_sock >= FD_SETSIZE) { + close (new_sock); + pgm_warn (_("Rejected new HTTP client socket due to out of bounds file descriptor.")); + return; + } +#endif + + pgm_sockaddr_nonblocking (new_sock, TRUE); + + struct http_connection_t* connection = pgm_new0 (struct http_connection_t, 1); + connection->sock = new_sock; + connection->state = HTTP_STATE_READ; + http_socks = pgm_list_prepend_link (http_socks, &connection->link_); + FD_SET( new_sock, &http_readfds ); + FD_SET( new_sock, &http_exceptfds ); + if (new_sock > http_max_sock) + http_max_sock = new_sock; +} + +static +void +http_close ( + struct http_connection_t* connection + ) +{ +#ifndef _WIN32 + if (0 != close (connection->sock)) { + pgm_warn (_("Close HTTP client socket: %s"), strerror (errno)); + } +#else + if (0 != closesocket (connection->sock)) { + const int save_errno = WSAGetLastError(); + pgm_warn (_("Close HTTP client socket: %s"), pgm_wsastrerror (save_errno)); + } +#endif + switch (connection->state) { + case HTTP_STATE_READ: + case HTTP_STATE_FINWAIT: + FD_CLR( connection->sock, &http_readfds ); + break; + case HTTP_STATE_WRITE: + FD_CLR( connection->sock, &http_writefds ); + break; + } + FD_CLR( connection->sock, &http_exceptfds ); + http_socks = pgm_list_remove_link (http_socks, &connection->link_); + if (connection->buflen > 0) { + pgm_free (connection->buf); + connection->buf = NULL; + connection->buflen = 0; + } +/* find new highest fd */ + if (connection->sock == http_max_sock) + { + http_max_sock = -1; + for (pgm_list_t* list = http_socks; list; list = list->next) + { + struct http_connection_t* c = (void*)list; + if (c->sock > http_max_sock) + http_max_sock = c->sock; + } + } + pgm_free (connection); +} + +/* non-blocking read an incoming HTTP request + */ + +static +void +http_read ( + struct http_connection_t* connection + ) +{ + for (;;) + { +/* grow buffer as needed */ + if (connection->bufoff + 1024 > connection->buflen) { + connection->buf = pgm_realloc (connection->buf, connection->buflen + 1024); + connection->buflen += 1024; + } + const ssize_t bytes_read = recv (connection->sock, &connection->buf[ connection->bufoff ], connection->buflen - connection->bufoff, 0); + if (bytes_read < 0) { + if (EINTR == errno || EAGAIN == errno) + return; +#ifndef _WIN32 + pgm_warn (_("HTTP client read: %s"), strerror (errno)); +#else + const int save_errno = WSAGetLastError(); + pgm_warn (_("HTTP client read: %s"), pgm_wsastrerror (save_errno)); +#endif + http_close (connection); + return; + } + +/* complete */ + if (strstr (connection->buf, "\r\n\r\n")) + break; + } + +/* process request, e.g. GET /index.html HTTP/1.1\r\n + */ + connection->buf[ connection->buflen - 1 ] = '\0'; + if (0 != memcmp (connection->buf, "GET ", strlen("GET "))) { +/* 501 (not implemented) */ + http_close (connection); + return; + } + + char* request_uri = connection->buf + strlen("GET "); + char* p = request_uri; + do { + if (*p == '?' || *p == ' ') { + *p = '\0'; + break; + } + } while (*(++p)); + + connection->status_code = 200; /* OK */ + connection->status_text = "OK"; + connection->content_type = "text/html"; + connection->bufoff = 0; + for (unsigned i = 0; i < PGM_N_ELEMENTS(http_directory); i++) + { + if (0 == strcmp (request_uri, http_directory[i].path)) + { + http_directory[i].callback (connection, request_uri); + goto complete; + } + } + default_callback (connection, request_uri); + +complete: + connection->state = HTTP_STATE_WRITE; + FD_CLR( connection->sock, &http_readfds ); + FD_SET( connection->sock, &http_writefds ); +} + +/* non-blocking write a HTTP response + */ + +static +void +http_write ( + struct http_connection_t* connection + ) +{ + do { + const ssize_t bytes_written = send (connection->sock, &connection->buf[ connection->bufoff ], connection->buflen - connection->bufoff, 0); + if (bytes_written < 0) { + if (EINTR == errno || EAGAIN == errno) + return; +#ifndef _WIN32 + pgm_warn (_("HTTP client write: %s"), strerror (errno)); +#else + const int save_errno = WSAGetLastError(); + pgm_warn (_("HTTP client write: %s"), pgm_wsastrerror (save_errno)); +#endif + http_close (connection); + return; + } + connection->bufoff += bytes_written; + } while (connection->bufoff < connection->buflen); + + if (0 == shutdown (connection->sock, SHUT_WR)) { + http_close (connection); + } else { + pgm_debug ("HTTP socket entering finwait state."); + connection->state = HTTP_STATE_FINWAIT; + FD_CLR( connection->sock, &http_writefds ); + FD_SET( connection->sock, &http_readfds ); + } +} + +/* read and discard pending data waiting for FIN + */ + +static +void +http_finwait ( + struct http_connection_t* connection + ) +{ + char buf[1024]; + const ssize_t bytes_read = read (connection->sock, buf, sizeof(buf)); + if (bytes_read < 0 && (EINTR == errno || EAGAIN == errno)) + return; + http_close (connection); +} + +static +void +http_process ( + struct http_connection_t* connection + ) +{ + switch (connection->state) { + case HTTP_STATE_READ: http_read (connection); break; + case HTTP_STATE_WRITE: http_write (connection); break; + case HTTP_STATE_FINWAIT: http_finwait (connection); break; + } +} + +static +void +http_set_status ( + struct http_connection_t* connection, + int status_code, + const char* status_text + ) +{ + connection->status_code = status_code; + connection->status_text = status_text; +} + +static +void +http_set_content_type ( + struct http_connection_t* connection, + const char* content_type + ) +{ + connection->content_type = content_type; +} + +/* finalise response buffer with headers and content */ + +static +void +http_set_static_response ( + struct http_connection_t* connection, + const char* content, + size_t content_length + ) +{ + pgm_string_t* response = pgm_string_new (NULL); + pgm_string_printf (response, "HTTP/1.0 %d %s\r\n" + "Server: OpenPGM HTTP Server %u.%u.%u\r\n" + "Last-Modified: Fri, 1 Jan 2010, 00:00:01 GMT\r\n" + "Content-Length: %d\r\n" + "Content-Type: %s\r\n" + "Connection: close\r\n" + "\r\n", + connection->status_code, + connection->status_text, + pgm_major_version, pgm_minor_version, pgm_micro_version, + content_length, + connection->content_type + ); + pgm_string_append (response, content); + if (connection->buflen) + pgm_free (connection->buf); + connection->buflen = response->len; + connection->buf = pgm_string_free (response, FALSE); +} + +static +void +http_set_response ( + struct http_connection_t* connection, + char* content, + size_t content_length + ) +{ + pgm_string_t* response = pgm_string_new (NULL); + pgm_string_printf (response, "HTTP/1.0 %d %s\r\n" + "Server: OpenPGM HTTP Server %u.%u.%u\r\n" + "Content-Length: %d\r\n" + "Content-Type: %s\r\n" + "Connection: close\r\n" + "\r\n", + connection->status_code, + connection->status_text, + pgm_major_version, pgm_minor_version, pgm_micro_version, + content_length, + connection->content_type + ); + pgm_string_append (response, content); + pgm_free (content); + if (connection->buflen) + pgm_free (connection->buf); + connection->buflen = response->len; + connection->buf = pgm_string_free (response, FALSE); +} + +/* Thread routine for processing HTTP requests + */ + +static +#ifndef _WIN32 +void* +#else +unsigned +__stdcall +#endif +http_routine ( + PGM_GNUC_UNUSED void* arg + ) +{ + const int notify_fd = pgm_notify_get_fd (&http_notify); + const int max_fd = MAX( notify_fd, http_sock ); + + FD_ZERO( &http_readfds ); + FD_ZERO( &http_writefds ); + FD_ZERO( &http_exceptfds ); + FD_SET( notify_fd, &http_readfds ); + FD_SET( http_sock, &http_readfds ); + + for (;;) + { + int fds = MAX( http_max_sock, max_fd ) + 1; + fd_set readfds = http_readfds, writefds = http_writefds, exceptfds = http_exceptfds; + + fds = select (fds, &readfds, &writefds, &exceptfds, NULL); +/* signal interrupt */ + if (PGM_UNLIKELY(fds < 0 && EINTR == errno)) + continue; +/* terminate */ + if (PGM_UNLIKELY(FD_ISSET( notify_fd, &readfds ))) + break; +/* new connection */ + if (FD_ISSET( http_sock, &readfds )) { + http_accept (http_sock); + continue; + } +/* existing connection */ + for (pgm_list_t* list = http_socks; list;) + { + struct http_connection_t* c = (void*)list; + list = list->next; + if ((FD_ISSET( c->sock, &readfds ) && HTTP_STATE_READ == c->state) || + (FD_ISSET( c->sock, &writefds ) && HTTP_STATE_WRITE == c->state) || + (FD_ISSET( c->sock, &exceptfds ))) + { + http_process (c); + } + } + } + +/* cleanup */ +#ifndef _WIN32 + return NULL; +#else + _endthread(); + return 0; +#endif /* WIN32 */ +} + +/* add xhtml doctype and head, populate with runtime values + */ + +typedef enum { + HTTP_TAB_GENERAL_INFORMATION, + HTTP_TAB_INTERFACES, + HTTP_TAB_TRANSPORTS, + HTTP_TAB_HISTOGRAMS +} http_tab_e; + +static +pgm_string_t* +http_create_response ( + const char* subtitle, + http_tab_e tab + ) +{ + pgm_assert (NULL != subtitle); + pgm_assert (tab == HTTP_TAB_GENERAL_INFORMATION || + tab == HTTP_TAB_INTERFACES || + tab == HTTP_TAB_TRANSPORTS || + tab == HTTP_TAB_HISTOGRAMS); + +/* surprising deficiency of GLib is no support of display locale time */ + char timestamp[100]; + time_t now; + time (&now); + const struct tm* time_ptr = localtime (&now); +#ifndef _WIN32 + strftime (timestamp, sizeof(timestamp), "%c", time_ptr); +#else + wchar_t wtimestamp[100]; + const size_t slen = strftime (timestamp, sizeof(timestamp), "%c", time_ptr); + const size_t wslen = MultiByteToWideChar (CP_ACP, 0, timestamp, slen, wtimestamp, 100); + WideCharToMultiByte (CP_UTF8, 0, wtimestamp, wslen + 1, timestamp, sizeof(timestamp), NULL, NULL); +#endif + + pgm_string_t* response = pgm_string_new (WWW_XHTML10_STRICT_DOCTYPE); + pgm_string_append_printf (response, "\n" + "%s - %s" + "" + "\n" + "" + "
" + "%s" + " | OpenPGM %u.%u.%u" + " | %s" + "
" + "
" + "General Information" + "Interfaces" + "Transports" +#ifdef CONFIG_HISTOGRAMS + "Histograms" +#endif + "
" + "
" + "
", + http_hostname, + subtitle, + http_hostname, + pgm_major_version, pgm_minor_version, pgm_micro_version, + timestamp, + tab == HTTP_TAB_GENERAL_INFORMATION ? "top" : "bottom", + tab == HTTP_TAB_INTERFACES ? "top" : "bottom", + tab == HTTP_TAB_TRANSPORTS ? "top" : "bottom" +#ifdef CONFIG_HISTOGRAMS + ,tab == HTTP_TAB_HISTOGRAMS ? "top" : "bottom" +#endif + ); + + return response; +} + +static +void +http_finalize_response ( + struct http_connection_t* connection, + pgm_string_t* response + ) +{ + pgm_string_append (response, "
" + "
" + "©2010 Miru" + "
" + "\n" + ""); + + char* buf = pgm_string_free (response, FALSE); + http_set_response (connection, buf, strlen (buf)); +} + +static +void +robots_callback ( + struct http_connection_t* connection, + PGM_GNUC_UNUSED const char* path + ) +{ + http_set_content_type (connection, "text/plain"); + http_set_static_response (connection, WWW_ROBOTS_TXT, strlen(WWW_ROBOTS_TXT)); +} + +static +void +css_callback ( + struct http_connection_t* connection, + PGM_GNUC_UNUSED const char* path + ) +{ + http_set_content_type (connection, "text/css"); + http_set_static_response (connection, WWW_BASE_CSS, strlen(WWW_BASE_CSS)); +} + +static +void +index_callback ( + struct http_connection_t* connection, + const char* path + ) +{ + if (strlen (path) > 1) { + default_callback (connection, path); + return; + } + pgm_rwlock_reader_lock (&pgm_sock_list_lock); + const unsigned transport_count = pgm_slist_length (pgm_sock_list); + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + + pgm_string_t* response = http_create_response ("OpenPGM", HTTP_TAB_GENERAL_INFORMATION); + pgm_string_append_printf (response, "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
host name:%s
user name:%s
IP address:%s
transports:%i
process ID:%i
\n", + http_hostname, + http_username, + http_address, + transport_count, + http_pid); + http_finalize_response (connection, response); +} + +static +void +interfaces_callback ( + struct http_connection_t* connection, + PGM_GNUC_UNUSED const char* path + ) +{ + pgm_string_t* response = http_create_response ("Interfaces", HTTP_TAB_INTERFACES); + pgm_string_append (response, "
");
+	struct pgm_ifaddrs_t *ifap, *ifa;
+	pgm_error_t* err = NULL;
+	if (!pgm_getifaddrs (&ifap, &err)) {
+		pgm_string_append_printf (response, "pgm_getifaddrs(): %s", (err && err->message) ? err->message : "(null)");
+		http_finalize_response (connection, response);
+		return;
+	}
+	for (ifa = ifap; ifa; ifa = ifa->ifa_next)
+	{
+		int i = NULL == ifa->ifa_addr ? 0 : pgm_if_nametoindex (ifa->ifa_addr->sa_family, ifa->ifa_name);
+		char rname[IF_NAMESIZE * 2 + 3];
+		char b[IF_NAMESIZE * 2 + 3];
+
+		pgm_if_indextoname (i, rname);
+		sprintf (b, "%s (%s)", ifa->ifa_name, rname);
+
+		 if (NULL == ifa->ifa_addr ||
+		      (ifa->ifa_addr->sa_family != AF_INET &&
+		       ifa->ifa_addr->sa_family != AF_INET6) )
+		{
+			pgm_string_append_printf (response,
+				"#%d name %-15.15s ---- %-46.46s scope 0 status %s loop %s b/c %s m/c %s
\n", + i, + b, + "", + ifa->ifa_flags & IFF_UP ? "UP " : "DOWN", + ifa->ifa_flags & IFF_LOOPBACK ? "YES" : "NO ", + ifa->ifa_flags & IFF_BROADCAST ? "YES" : "NO ", + ifa->ifa_flags & IFF_MULTICAST ? "YES" : "NO " + ); + continue; + } + + char s[INET6_ADDRSTRLEN]; + getnameinfo (ifa->ifa_addr, pgm_sockaddr_len(ifa->ifa_addr), + s, sizeof(s), + NULL, 0, + NI_NUMERICHOST); + pgm_string_append_printf (response, + "#%d name %-15.15s IPv%i %-46.46s scope %u status %s loop %s b/c %s m/c %s
\n", + i, + b, + ifa->ifa_addr->sa_family == AF_INET ? 4 : 6, + s, + (unsigned)pgm_sockaddr_scope_id(ifa->ifa_addr), + ifa->ifa_flags & IFF_UP ? "UP " : "DOWN", + ifa->ifa_flags & IFF_LOOPBACK ? "YES" : "NO ", + ifa->ifa_flags & IFF_BROADCAST ? "YES" : "NO ", + ifa->ifa_flags & IFF_MULTICAST ? "YES" : "NO " + ); + } + pgm_freeifaddrs (ifap); + pgm_string_append (response, "
\n"); + http_finalize_response (connection, response); +} + +static +void +transports_callback ( + struct http_connection_t* connection, + PGM_GNUC_UNUSED const char* path + ) +{ + pgm_string_t* response = http_create_response ("Transports", HTTP_TAB_TRANSPORTS); + pgm_string_append (response, "
" + "\n" + "" + "" + "" + "" + "" + "" + ); + + if (pgm_sock_list) + { + pgm_rwlock_reader_lock (&pgm_sock_list_lock); + + pgm_slist_t* list = pgm_sock_list; + while (list) + { + pgm_slist_t* next = list->next; + pgm_sock_t* sock = list->data; + + char group_address[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&sock->send_gsr.gsr_group, pgm_sockaddr_len ((struct sockaddr*)&sock->send_gsr.gsr_group), + group_address, sizeof(group_address), + NULL, 0, + NI_NUMERICHOST); + char gsi[ PGM_GSISTRLEN ]; + pgm_gsi_print_r (&sock->tsi.gsi, gsi, sizeof(gsi)); + const uint16_t sport = ntohs (sock->tsi.sport); + const uint16_t dport = ntohs (sock->dport); + pgm_string_append_printf (response, "" + "" + "" + "" + "" + "", + group_address, + dport, + gsi, sport, + gsi, + gsi, sport, + sport); + list = next; + } + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + } + else + { +/* no transports */ + pgm_string_append (response, "" + "" + "" + ); + } + + pgm_string_append (response, "
Group addressDest portSource GSISource port
%s%i%s%u
This transport has no peers.
\n" + "
"); + http_finalize_response (connection, response); +} + +static +void +histograms_callback ( + struct http_connection_t* connection, + PGM_GNUC_UNUSED const char* path + ) +{ + pgm_string_t* response = http_create_response ("Histograms", HTTP_TAB_HISTOGRAMS); + pgm_histogram_write_html_graph_all (response); + http_finalize_response (connection, response); +} + +static +void +default_callback ( + struct http_connection_t* connection, + const char* path + ) +{ + pgm_tsi_t tsi; + const int count = sscanf (path, "/%hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hu", + (unsigned char*)&tsi.gsi.identifier[0], + (unsigned char*)&tsi.gsi.identifier[1], + (unsigned char*)&tsi.gsi.identifier[2], + (unsigned char*)&tsi.gsi.identifier[3], + (unsigned char*)&tsi.gsi.identifier[4], + (unsigned char*)&tsi.gsi.identifier[5], + &tsi.sport); + tsi.sport = htons (tsi.sport); + if (count == 7) + { + int retval = http_tsi_response (connection, &tsi); + if (!retval) return; + } + + http_set_status (connection, 404, "Not Found"); + http_set_static_response (connection, WWW_404_HTML, strlen(WWW_404_HTML)); +} + +static +int +http_tsi_response ( + struct http_connection_t* connection, + pgm_tsi_t* tsi + ) +{ +/* first verify this is a valid TSI */ + pgm_rwlock_reader_lock (&pgm_sock_list_lock); + + pgm_sock_t* sock = NULL; + pgm_slist_t* list = pgm_sock_list; + while (list) + { + pgm_sock_t* list_sock = (pgm_sock_t*)list->data; + pgm_slist_t* next = list->next; + +/* check source */ + if (pgm_tsi_equal (tsi, &list_sock->tsi)) + { + sock = list_sock; + break; + } + +/* check receivers */ + pgm_rwlock_reader_lock (&list_sock->peers_lock); + pgm_peer_t* receiver = pgm_hashtable_lookup (list_sock->peers_hashtable, tsi); + if (receiver) { + int retval = http_receiver_response (connection, receiver); + pgm_rwlock_reader_unlock (&list_sock->peers_lock); + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + return retval; + } + pgm_rwlock_reader_unlock (&list_sock->peers_lock); + + list = next; + } + + if (!sock) { + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + return -1; + } + +/* transport now contains valid matching TSI */ + char gsi[ PGM_GSISTRLEN ]; + pgm_gsi_print_r (&sock->tsi.gsi, gsi, sizeof(gsi)); + + char title[ sizeof("Transport .00000") + PGM_GSISTRLEN ]; + sprintf (title, "Transport %s.%hu", + gsi, + ntohs (sock->tsi.sport)); + + char source_address[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&sock->send_gsr.gsr_source, pgm_sockaddr_len ((struct sockaddr*)&sock->send_gsr.gsr_source), + source_address, sizeof(source_address), + NULL, 0, + NI_NUMERICHOST); + + char group_address[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&sock->send_gsr.gsr_group, pgm_sockaddr_len ((struct sockaddr*)&sock->send_gsr.gsr_group), + group_address, sizeof(group_address), + NULL, 0, + NI_NUMERICHOST); + + const uint16_t dport = ntohs (sock->dport); + const uint16_t sport = ntohs (sock->tsi.sport); + + const pgm_time_t ihb_min = sock->spm_heartbeat_len ? sock->spm_heartbeat_interval[ 1 ] : 0; + const pgm_time_t ihb_max = sock->spm_heartbeat_len ? sock->spm_heartbeat_interval[ sock->spm_heartbeat_len - 1 ] : 0; + + char spm_path[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&sock->recv_gsr[0].gsr_source, pgm_sockaddr_len ((struct sockaddr*)&sock->recv_gsr[0].gsr_source), + spm_path, sizeof(spm_path), + NULL, 0, + NI_NUMERICHOST); + + pgm_string_t* response = http_create_response (title, HTTP_TAB_TRANSPORTS); + pgm_string_append_printf (response, "
" + "Transport: " + "%s.%hu" + "
", + gsi, sport); + +/* peers */ + + pgm_string_append (response, "
" + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + ); + + if (sock->peers_list) + { + pgm_rwlock_reader_lock (&sock->peers_lock); + pgm_list_t* peers_list = sock->peers_list; + while (peers_list) { + pgm_list_t* next = peers_list->next; + http_each_receiver (peers_list->data, response); + peers_list = next; + } + pgm_rwlock_reader_unlock (&sock->peers_lock); + } + else + { +/* no peers */ + + pgm_string_append (response, "" + "" + "" + ); + + } + + pgm_string_append (response, "
Group addressDest portSource addressLast hopSource GSISource port
This transport has no peers.
\n" + "
"); + +/* source and configuration information */ + + pgm_string_append_printf (response, "
" + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "", + source_address, + group_address, + dport, + gsi, + sport); + +/* continue with source information */ + + pgm_string_append_printf (response, "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
Source address%s
Group address%s
Dest port%u
Source GSI%s
Source port%u
Ttl%u
Adv Mode%s
Late joindisable(2)
TXW_MAX_RTE%" GROUP_FORMAT "zd
TXW_SECS%" GROUP_FORMAT "u
TXW_ADV_SECS0
Ambient SPM interval%" GROUP_FORMAT PGM_TIME_FORMAT " ms
IHB_MIN%" GROUP_FORMAT PGM_TIME_FORMAT " ms
IHB_MAX%" GROUP_FORMAT PGM_TIME_FORMAT " ms
NAK_BO_IVL%" GROUP_FORMAT PGM_TIME_FORMAT " ms
FECdisabled(1)
Source Path Address%s
\n" + "
", + sock->hops, + 0 == sock->adv_mode ? "time(0)" : "data(1)", + sock->txw_max_rte, + sock->txw_secs, + pgm_to_msecs(sock->spm_ambient_interval), + ihb_min, + ihb_max, + pgm_to_msecs(sock->nak_bo_ivl), + spm_path); + +/* performance information */ + + const pgm_txw_t* window = sock->window; + pgm_string_append_printf (response, "\n

Performance information

" + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
Data bytes sent%" GROUP_FORMAT PRIu32 "
Data packets sent%" GROUP_FORMAT PRIu32 "
Bytes buffered%" GROUP_FORMAT PRIu32 "
Packets buffered%" GROUP_FORMAT PRIu32 "
Bytes sent%" GROUP_FORMAT PRIu32 "
Raw NAKs received%" GROUP_FORMAT PRIu32 "
Checksum errors%" GROUP_FORMAT PRIu32 "
Malformed NAKs%" GROUP_FORMAT PRIu32 "
Packets discarded%" GROUP_FORMAT PRIu32 "
Bytes retransmitted%" GROUP_FORMAT PRIu32 "
Packets retransmitted%" GROUP_FORMAT PRIu32 "
NAKs received%" GROUP_FORMAT PRIu32 "
NAKs ignored%" GROUP_FORMAT PRIu32 "
Transmission rate%" GROUP_FORMAT PRIu32 " bps
NNAK packets received%" GROUP_FORMAT PRIu32 "
NNAKs received%" GROUP_FORMAT PRIu32 "
Malformed NNAKs%" GROUP_FORMAT PRIu32 "
\n", + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT], + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT], + window ? pgm_txw_size (window) : 0, /* minus IP & any UDP header */ + window ? pgm_txw_length (window) : 0, + sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED], + sock->cumulative_stats[PGM_PC_SOURCE_CKSUM_ERRORS], + sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS], + sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED], + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_BYTES_RETRANSMITTED], + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED], + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED], + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_IGNORED], + sock->cumulative_stats[PGM_PC_SOURCE_TRANSMISSION_CURRENT_RATE], + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAK_PACKETS_RECEIVED], + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAKS_RECEIVED], + sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]); + + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + http_finalize_response (connection, response); + return 0; +} + +static +void +http_each_receiver ( + pgm_peer_t* peer, + pgm_string_t* response + ) +{ + char group_address[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&peer->group_nla, pgm_sockaddr_len ((struct sockaddr*)&peer->group_nla), + group_address, sizeof(group_address), + NULL, 0, + NI_NUMERICHOST); + + char source_address[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&peer->nla, pgm_sockaddr_len ((struct sockaddr*)&peer->nla), + source_address, sizeof(source_address), + NULL, 0, + NI_NUMERICHOST); + + char last_hop[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&peer->local_nla, pgm_sockaddr_len ((struct sockaddr*)&peer->local_nla), + last_hop, sizeof(last_hop), + NULL, 0, + NI_NUMERICHOST); + + char gsi[ PGM_GSISTRLEN + sizeof(".00000") ]; + pgm_gsi_print_r (&peer->tsi.gsi, gsi, sizeof(gsi)); + + const int sport = ntohs (peer->tsi.sport); + const int dport = ntohs (peer->sock->dport); /* by definition must be the same */ + pgm_string_append_printf (response, "" + "%s" + "%u" + "%s" + "%s" + "%s" + "%u" + "", + group_address, + dport, + source_address, + last_hop, + gsi, sport, gsi, + gsi, sport, sport + ); +} + +static +int +http_time_summary ( + const time_t* activity_time, + char* sz + ) +{ + time_t now_time = time (NULL); + + if (*activity_time > now_time) { + return sprintf (sz, "clock skew"); + } + + struct tm* activity_tm = localtime (activity_time); + + now_time -= *activity_time; + + if (now_time < (24 * 60 * 60)) + { + char hourmin[6]; + strftime (hourmin, sizeof(hourmin), "%H:%M", activity_tm); + + if (now_time < 60) { + return sprintf (sz, "%s (%li second%s ago)", + hourmin, now_time, now_time > 1 ? "s" : ""); + } + now_time /= 60; + if (now_time < 60) { + return sprintf (sz, "%s (%li minute%s ago)", + hourmin, now_time, now_time > 1 ? "s" : ""); + } + now_time /= 60; + return sprintf (sz, "%s (%li hour%s ago)", + hourmin, now_time, now_time > 1 ? "s" : ""); + } + else + { + char daymonth[32]; +#ifndef _WIN32 + strftime (daymonth, sizeof(daymonth), "%d %b", activity_tm); +#else + wchar_t wdaymonth[32]; + const size_t slen = strftime (daymonth, sizeof(daymonth), "%d %b", &activity_tm); + const size_t wslen = MultiByteToWideChar (CP_ACP, 0, daymonth, slen, wdaymonth, 32); + WideCharToMultiByte (CP_UTF8, 0, wdaymonth, wslen + 1, daymonth, sizeof(daymonth), NULL, NULL); +#endif + now_time /= 24; + if (now_time < 14) { + return sprintf (sz, "%s (%li day%s ago)", + daymonth, now_time, now_time > 1 ? "s" : ""); + } else { + return sprintf (sz, "%s", daymonth); + } + } +} + +static +int +http_receiver_response ( + struct http_connection_t* connection, + pgm_peer_t* peer + ) +{ + char gsi[ PGM_GSISTRLEN ]; + pgm_gsi_print_r (&peer->tsi.gsi, gsi, sizeof(gsi)); + char title[ sizeof("Peer .00000") + PGM_GSISTRLEN ]; + sprintf (title, "Peer %s.%u", + gsi, + ntohs (peer->tsi.sport)); + + char group_address[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&peer->group_nla, pgm_sockaddr_len ((struct sockaddr*)&peer->group_nla), + group_address, sizeof(group_address), + NULL, 0, + NI_NUMERICHOST); + + char source_address[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&peer->nla, pgm_sockaddr_len ((struct sockaddr*)&peer->nla), + source_address, sizeof(source_address), + NULL, 0, + NI_NUMERICHOST); + + char last_hop[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&peer->local_nla, pgm_sockaddr_len ((struct sockaddr*)&peer->local_nla), + last_hop, sizeof(last_hop), + NULL, 0, + NI_NUMERICHOST); + + const uint16_t sport = ntohs (peer->tsi.sport); + const uint16_t dport = ntohs (peer->sock->dport); /* by definition must be the same */ + const pgm_rxw_t* window = peer->window; + const uint32_t outstanding_naks = window->nak_backoff_queue.length + + window->wait_ncf_queue.length + + window->wait_data_queue.length; + + time_t last_activity_time; + pgm_time_since_epoch (&peer->last_packet, &last_activity_time); + + char last_activity[100]; + http_time_summary (&last_activity_time, last_activity); + + pgm_string_t* response = http_create_response (title, HTTP_TAB_TRANSPORTS); + pgm_string_append_printf (response, "
" + "Peer: " + "%s.%u" + "
", + gsi, sport); + + +/* peer information */ + pgm_string_append_printf (response, "
" + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "", + group_address, + dport, + source_address, + last_hop, + gsi, + sport); + + pgm_string_append_printf (response, "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
Group address%s
Dest port%u
Source address%s
Last hop%s
Source GSI%s
Source port%u
NAK_BO_IVL%" GROUP_FORMAT PGM_TIME_FORMAT " ms
NAK_RPT_IVL%" GROUP_FORMAT PGM_TIME_FORMAT " ms
NAK_NCF_RETRIES%" GROUP_FORMAT "u
NAK_RDATA_IVL%" GROUP_FORMAT PGM_TIME_FORMAT " ms
NAK_DATA_RETRIES%" GROUP_FORMAT "u
Send NAKsenabled(1)
Late joindisabled(2)
NAK TTL%u
Delivery orderordered(2)
Multicast NAKsdisabled(2)
\n" + "
", + pgm_to_msecs(peer->sock->nak_bo_ivl), + pgm_to_msecs(peer->sock->nak_rpt_ivl), + peer->sock->nak_ncf_retries, + pgm_to_msecs(peer->sock->nak_rdata_ivl), + peer->sock->nak_data_retries, + peer->sock->hops); + + pgm_string_append_printf (response, "\n

Performance information

" + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" /* detected missed packets */ + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
Data bytes received%" GROUP_FORMAT PRIu32 "
Data packets received%" GROUP_FORMAT PRIu32 "
NAK failures%" GROUP_FORMAT PRIu32 "
Bytes received%" GROUP_FORMAT PRIu32 "
Checksum errors%" GROUP_FORMAT PRIu32 "
Malformed SPMs%" GROUP_FORMAT PRIu32 "
Malformed ODATA%" GROUP_FORMAT PRIu32 "
Malformed RDATA%" GROUP_FORMAT PRIu32 "
Malformed NCFs%" GROUP_FORMAT PRIu32 "
Packets discarded%" GROUP_FORMAT PRIu32 "
Losses%" GROUP_FORMAT PRIu32 "
Bytes delivered to app%" GROUP_FORMAT PRIu32 "
Packets delivered to app%" GROUP_FORMAT PRIu32 "
Duplicate SPMs%" GROUP_FORMAT PRIu32 "
Duplicate ODATA/RDATA%" GROUP_FORMAT PRIu32 "
NAK packets sent%" GROUP_FORMAT PRIu32 "
NAKs sent%" GROUP_FORMAT PRIu32 "
NAKs retransmitted%" GROUP_FORMAT PRIu32 "
NAKs failed%" GROUP_FORMAT PRIu32 "
NAKs failed due to RXW advance%" GROUP_FORMAT PRIu32 "
NAKs failed due to NCF retries%" GROUP_FORMAT PRIu32 "
NAKs failed due to DATA retries%" GROUP_FORMAT PRIu32 "
NAK failures delivered to app%" GROUP_FORMAT PRIu32 "
NAKs suppressed%" GROUP_FORMAT PRIu32 "
Malformed NAKs%" GROUP_FORMAT PRIu32 "
Outstanding NAKs%" GROUP_FORMAT PRIu32 "
Last activity%s
NAK repair min time%" GROUP_FORMAT PRIu32 " μs
NAK repair mean time%" GROUP_FORMAT PRIu32 " μs
NAK repair max time%" GROUP_FORMAT PRIu32 " μs
NAK fail min time%" GROUP_FORMAT PRIu32 " μs
NAK fail mean time%" GROUP_FORMAT PRIu32 " μs
NAK fail max time%" GROUP_FORMAT PRIu32 " μs
NAK min retransmit count%" GROUP_FORMAT PRIu32 "
NAK mean retransmit count%" GROUP_FORMAT PRIu32 "
NAK max retransmit count%" GROUP_FORMAT PRIu32 "
\n", + peer->cumulative_stats[PGM_PC_RECEIVER_DATA_BYTES_RECEIVED], + peer->cumulative_stats[PGM_PC_RECEIVER_DATA_MSGS_RECEIVED], + peer->cumulative_stats[PGM_PC_RECEIVER_NAK_FAILURES], + peer->cumulative_stats[PGM_PC_RECEIVER_BYTES_RECEIVED], + peer->sock->cumulative_stats[PGM_PC_SOURCE_CKSUM_ERRORS], + peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_SPMS], + peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_ODATA], + peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_RDATA], + peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_NCFS], + peer->cumulative_stats[PGM_PC_RECEIVER_PACKETS_DISCARDED], + window->cumulative_losses, + window->bytes_delivered, + window->msgs_delivered, + peer->cumulative_stats[PGM_PC_RECEIVER_DUP_SPMS], + peer->cumulative_stats[PGM_PC_RECEIVER_DUP_DATAS], + peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAK_PACKETS_SENT], + peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SENT], + peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_RETRANSMITTED], + peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_FAILED], + peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_RXW_ADVANCED], + peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_NCF_RETRIES_EXCEEDED], + peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_DATA_RETRIES_EXCEEDED], + peer->cumulative_stats[PGM_PC_RECEIVER_NAK_FAILURES_DELIVERED], + peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED], + peer->cumulative_stats[PGM_PC_RECEIVER_NAK_ERRORS], + outstanding_naks, + last_activity, + window->min_fill_time, + peer->cumulative_stats[PGM_PC_RECEIVER_NAK_SVC_TIME_MEAN], + window->max_fill_time, + peer->min_fail_time, + peer->cumulative_stats[PGM_PC_RECEIVER_NAK_FAIL_TIME_MEAN], + peer->max_fail_time, + window->min_nak_transmit_count, + peer->cumulative_stats[PGM_PC_RECEIVER_TRANSMIT_MEAN], + window->max_nak_transmit_count); + http_finalize_response (connection, response); + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/http_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/http_unittest.c new file mode 100644 index 0000000..32ba11b --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/http_unittest.c @@ -0,0 +1,186 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for the HTTP administration interface. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include + +#include "pgm/transport.h" + + +/* mock state */ +static const guint mock_pgm_major_version = 0; +static const guint mock_pgm_minor_version = 0; +static const guint mock_pgm_micro_version = 0; +static GStaticRWLock mock_pgm_transport_list_lock = G_STATIC_RW_LOCK_INIT; +static GSList* mock_pgm_transport_list = NULL; + +static +gboolean +mock_pgm_tsi_equal ( + gconstpointer v1, + gconstpointer v2 + ) +{ + return memcmp (v1, v2, sizeof(struct pgm_tsi_t)) == 0; +} + +static +void +mock_pgm_time_since_epoch ( + pgm_time_t* pgm_time_t_time, + time_t* time_t_time + ) +{ + *time_t_time = pgm_to_secs (*pgm_time_t_time + 0); +} + +static +void +mock_pgm_histogram_write_html_graph_all + ( + GString* string + ) +{ +} + + +/* mock functions for external references */ + +#define pgm_major_version mock_pgm_major_version +#define pgm_minor_version mock_pgm_minor_version +#define pgm_micro_version mock_pgm_micro_version +#define pgm_transport_list_lock mock_pgm_transport_list_lock +#define pgm_transport_list mock_pgm_transport_list +#define pgm_tsi_equal mock_pgm_tsi_equal +#define pgm_time_since_epoch mock_pgm_time_since_epoch +#define pgm_histogram_write_html_graph_all mock_pgm_histogram_write_html_graph_all + +#define HTTP_DEBUG +#include "http.c" + + +/* target: + * gboolean + * pgm_http_init ( + * guint16* http_port, + * GError** error + * ) + */ + +START_TEST (test_init_pass_001) +{ + GError* err = NULL; + fail_unless (TRUE == pgm_http_init (8080, &err)); + fail_unless (NULL == err); +} +END_TEST + +/* duplicate servers */ +START_TEST (test_init_fail_001) +{ + GError* err = NULL; + fail_unless (TRUE == pgm_http_init (8080, &err)); + fail_unless (FALSE == pgm_http_init (8080, &err)); +} +END_TEST + +/* target: + * gboolean + * pgm_http_shutdown (void) + */ + +START_TEST (test_shutdown_pass_001) +{ + GError* err = NULL; + fail_unless (TRUE == pgm_http_init (8080, &err)); + fail_unless (NULL == err); + fail_unless (TRUE == pgm_http_shutdown ()); +} +END_TEST + +/* repeatability + */ +START_TEST (test_shutdown_pass_002) +{ + GError* err = NULL; + fail_unless (TRUE == pgm_http_init (8080, &err)); + fail_unless (NULL == err); + fail_unless (TRUE == pgm_http_shutdown ()); + fail_unless (TRUE == pgm_http_init (8080, &err)); + fail_unless (NULL == err); + fail_unless (TRUE == pgm_http_shutdown ()); +} +END_TEST + +/* no running server */ +START_TEST (test_shutdown_fail_001) +{ + fail_unless (FALSE == pgm_http_shutdown ()); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_init = tcase_create ("init"); + suite_add_tcase (s, tc_init); + tcase_add_test (tc_init, test_init_pass_001); + tcase_add_test (tc_init, test_init_fail_001); + + TCase* tc_shutdown = tcase_create ("shutdown"); + suite_add_tcase (s, tc_shutdown); + tcase_add_test (tc_shutdown, test_shutdown_pass_001); + tcase_add_test (tc_shutdown, test_shutdown_pass_002); + tcase_add_test (tc_shutdown, test_shutdown_fail_001); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/if.c b/3rdparty/openpgm-svn-r1085/pgm/if.c new file mode 100644 index 0000000..f034758 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/if.c @@ -0,0 +1,1595 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * network interface handling. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE +#include +#include +#include +#ifndef _WIN32 +# include +# include +# include /* _GNU_SOURCE for EAI_NODATA */ +#endif +#include +#include +#include + + +//#define IF_DEBUG + +/* temporary structure to contain interface name whilst address family + * has not been resolved. + */ +struct interface_req { + char ir_name[IF_NAMESIZE]; + unsigned int ir_flags; /* from SIOCGIFFLAGS */ + unsigned int ir_interface; /* interface index */ + struct sockaddr_storage ir_addr; /* interface address */ +}; + + +/* locals */ + +#ifndef _WIN32 +# define IF_DEFAULT_GROUP ((in_addr_t)0xefc00001) /* 239.192.0.1 */ +#else +# define IF_DEFAULT_GROUP ((u_long)0xefc00001) +#endif + +/* ff08::1 */ +#define IF6_DEFAULT_INIT { { { 0xff,8,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } } +const struct in6_addr if6_default_group_addr = IF6_DEFAULT_INIT; + + +static inline bool is_in_net (const struct in_addr*restrict, const struct in_addr*restrict, const struct in_addr*restrict) PGM_GNUC_WARN_UNUSED_RESULT; +static inline bool is_in_net6 (const struct in6_addr*restrict, const struct in6_addr*restrict, const struct in6_addr*restrict) PGM_GNUC_WARN_UNUSED_RESULT; +static inline bool is_network_char (const int, const char) PGM_GNUC_CONST; +static const char* pgm_family_string (const int) PGM_GNUC_CONST; + + +/* recommended address space for multicast: + * rfc4607, rfc3180, rfc2365 + * + * avoid 5 high-order bit overlap. + * + * loopback: ffx1::/16 + * segment: ffx2::/16 + * glop: 238/8 + * mysterious admin: 239/8, ffx6::/16 + * site: 239.252-255/16, ffx5::/16 + * org: 239.192/14, ffx8::/16 + * + * internets: 224.0.1.0-238.255.255.255, ffxe::/16 + */ + + +/* dump all interfaces to console. + * + * note that interface indexes are only in regard to the link layer and hence + * no 1-1 mapping between adapter name to index back to address. + */ + +void +pgm_if_print_all (void) +{ + struct pgm_ifaddrs_t *ifap, *ifa; + + if (!pgm_getifaddrs (&ifap, NULL)) + return; + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) + { + const unsigned int i = NULL == ifa->ifa_addr ? 0 : pgm_if_nametoindex (ifa->ifa_addr->sa_family, ifa->ifa_name); + char rname[IF_NAMESIZE * 2 + 3]; + char b[IF_NAMESIZE * 2 + 3]; + + pgm_if_indextoname (i, rname); + sprintf (b, "%s (%s)", + ifa->ifa_name ? ifa->ifa_name : "(null)", rname); + + if (NULL == ifa->ifa_addr || + (ifa->ifa_addr->sa_family != AF_INET && + ifa->ifa_addr->sa_family != AF_INET6) ) + { + pgm_info (_("#%d name %-15.15s ---- %-46.46s scope 0 status %s loop %s b/c %s m/c %s"), + i, + b, + "", + ifa->ifa_flags & IFF_UP ? "UP " : "DOWN", + ifa->ifa_flags & IFF_LOOPBACK ? "YES" : "NO ", + ifa->ifa_flags & IFF_BROADCAST ? "YES" : "NO ", + ifa->ifa_flags & IFF_MULTICAST ? "YES" : "NO " + ); + continue; + } + + char s[INET6_ADDRSTRLEN]; + getnameinfo (ifa->ifa_addr, pgm_sockaddr_len(ifa->ifa_addr), + s, sizeof(s), + NULL, 0, + NI_NUMERICHOST); + pgm_info (_("#%d name %-15.15s IPv%i %-46.46s scope %u status %s loop %s b/c %s m/c %s"), + i, + b, + ifa->ifa_addr->sa_family == AF_INET ? 4 : 6, + s, + (unsigned)pgm_sockaddr_scope_id(ifa->ifa_addr), + ifa->ifa_flags & IFF_UP ? "UP " : "DOWN", + ifa->ifa_flags & IFF_LOOPBACK ? "YES" : "NO ", + ifa->ifa_flags & IFF_BROADCAST ? "YES" : "NO ", + ifa->ifa_flags & IFF_MULTICAST ? "YES" : "NO " + ); + } + + pgm_freeifaddrs (ifap); +} + +static inline +bool +is_in_net ( + const struct in_addr* restrict addr, /* host byte order */ + const struct in_addr* restrict netaddr, + const struct in_addr* restrict netmask + ) +{ + pgm_assert (NULL != addr); + pgm_assert (NULL != netaddr); + pgm_assert (NULL != netmask); + +#ifdef IF_DEBUG + const struct in_addr taddr = { .s_addr = htonl (addr->s_addr) }; + const struct in_addr tnetaddr = { .s_addr = htonl (netaddr->s_addr) }; + const struct in_addr tnetmask = { .s_addr = htonl (netmask->s_addr) }; + char saddr[INET_ADDRSTRLEN], snetaddr[INET_ADDRSTRLEN], snetmask[INET_ADDRSTRLEN]; + pgm_debug ("is_in_net (addr:%s netaddr:%s netmask:%s)", + pgm_inet_ntop (AF_INET, &taddr, saddr, sizeof(saddr)), + pgm_inet_ntop (AF_INET, &tnetaddr, snetaddr, sizeof(snetaddr)), + pgm_inet_ntop (AF_INET, &tnetmask, snetmask, sizeof(snetmask))); +#endif + + if ((addr->s_addr & netmask->s_addr) == (netaddr->s_addr & netmask->s_addr)) + return TRUE; + return FALSE; +} + +static +bool +is_in_net6 ( + const struct in6_addr* restrict addr, + const struct in6_addr* restrict netaddr, + const struct in6_addr* restrict netmask + ) +{ + pgm_assert (NULL != addr); + pgm_assert (NULL != netaddr); + pgm_assert (NULL != netmask); + +#ifdef IF_DEBUG + char saddr[INET6_ADDRSTRLEN], snetaddr[INET6_ADDRSTRLEN], snetmask[INET6_ADDRSTRLEN]; + pgm_debug ("is_in_net6 (addr:%s netaddr:%s netmask:%s)", + pgm_inet_ntop (AF_INET6, addr, saddr, sizeof(saddr)), + pgm_inet_ntop (AF_INET6, netaddr, snetaddr, sizeof(snetaddr)), + pgm_inet_ntop (AF_INET6, netmask, snetmask, sizeof(snetmask))); +#endif + + for (unsigned i = 0; i < 16; i++) + if ((addr->s6_addr[i] & netmask->s6_addr[i]) != (netaddr->s6_addr[i] & netmask->s6_addr[i])) + return FALSE; + return TRUE; +} + +/* parse interface entity into an interface-request structure. + * + * e.g. eth0 + * 1.2.3.4 + * 1.2 + * abcd:: + * [abcd::] + * + * + * + * special addresses should be ignored: + * + * local physical link: 169.254.0.0/16, fe80::/64 + * broadcast: 255.255.255.255 + * multicast: 224.0.0.0/4 (224.0.0.0 to 239.255.255.255), ff00::/8 + * + * We could use if_nametoindex() but we might as well check that the interface is + * actually UP and capable of multicast traffic. + * + * returns TRUE on success, FALSE on error and sets error appropriately. + */ + +static +bool +parse_interface ( + int family, /* AF_UNSPEC | AF_INET | AF_INET6 */ + const char* restrict ifname, /* NULL terminated */ + struct interface_req* restrict ir, /* location to write interface details to */ + pgm_error_t** restrict error + ) +{ + bool check_inet_network = FALSE, check_inet6_network = FALSE; + bool check_addr = FALSE; + bool check_ifname = FALSE; + char literal[1024]; + struct in_addr in_addr; + struct in6_addr in6_addr; + struct pgm_ifaddrs_t *ifap, *ifa; + struct sockaddr_storage addr; + unsigned interface_matches = 0; + +/* pre-conditions */ + pgm_assert (AF_INET == family || AF_INET6 == family || AF_UNSPEC == family); + pgm_assert (NULL != ifname); + pgm_assert (NULL != ir); + + pgm_debug ("parse_interface (family:%s ifname:%s%s%s ir:%p error:%p)", + pgm_family_string (family), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : "", + (const void*)ir, + (const void*)error); + +/* strip any square brackets for IPv6 early evaluation */ + if (AF_INET != family && + '[' == ifname[0]) + { + const size_t ifnamelen = strlen(ifname); + if (']' == ifname[ ifnamelen - 1 ]) { + strncpy (literal, ifname + 1, ifnamelen - 2); + literal[ ifnamelen - 2 ] = 0; + family = AF_INET6; /* force IPv6 evaluation */ + check_inet6_network = TRUE; /* may be a network IP or CIDR block */ + check_addr = TRUE; /* cannot be not a name */ + ifname = literal; + } + } + +/* network address: in_addr in host byte order */ + if (AF_INET6 != family && 0 == pgm_inet_network (ifname, &in_addr)) + { +#ifdef IF_DEBUG + struct in_addr t = { .s_addr = htonl (in_addr.s_addr) }; + pgm_debug ("IPv4 network address: %s", inet_ntoa (t)); +#endif + if (IN_MULTICAST(in_addr.s_addr)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_XDEV, + _("Expecting network interface address, found IPv4 multicast network %s%s%s"), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + return FALSE; + } + struct sockaddr_in s4; + memset (&s4, 0, sizeof(s4)); + s4.sin_family = AF_INET; + s4.sin_addr.s_addr = htonl (in_addr.s_addr); + memcpy (&addr, &s4, sizeof(s4)); + + check_inet_network = TRUE; + check_addr = TRUE; + } + if (AF_INET != family && 0 == pgm_inet6_network (ifname, &in6_addr)) + { + if (IN6_IS_ADDR_MULTICAST(&in6_addr)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_XDEV, + _("Expecting network interface address, found IPv6 multicast network %s%s%s"), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + return FALSE; + } + struct sockaddr_in6 s6; + memset (&s6, 0, sizeof(s6)); + s6.sin6_family = AF_INET6; + s6.sin6_addr = in6_addr; + memcpy (&addr, &s6, sizeof(s6)); + + check_inet6_network = TRUE; + check_addr = TRUE; + } + +/* numeric host with scope id */ + if (!check_addr) + { + struct addrinfo hints = { + .ai_family = family, + .ai_socktype = SOCK_STREAM, /* not really, SOCK_RAW */ + .ai_protocol = IPPROTO_TCP, /* not really, IPPROTO_PGM */ + .ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST /* AI_V4MAPPED is unhelpful */ + }, *res; + const int eai = getaddrinfo (ifname, NULL, &hints, &res); + switch (eai) { + case 0: + if (AF_INET == res->ai_family && + IN_MULTICAST(ntohl (((struct sockaddr_in*)(res->ai_addr))->sin_addr.s_addr))) + { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_XDEV, + _("Expecting interface address, found IPv4 multicast address %s%s%s"), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + freeaddrinfo (res); + return FALSE; + } + else if (AF_INET6 == res->ai_family && + IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)res->ai_addr)->sin6_addr)) + { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_XDEV, + _("Expecting interface address, found IPv6 multicast address %s%s%s"), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + freeaddrinfo (res); + return FALSE; + } + + memcpy (&addr, res->ai_addr, pgm_sockaddr_len (res->ai_addr)); + freeaddrinfo (res); + check_addr = TRUE; + break; + +#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME + case EAI_NODATA: +#endif + case EAI_NONAME: + break; + + default: + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_eai_errno (eai, errno), + _("Numeric host resolution: %s"), + gai_strerror (eai)); + return FALSE; + } + } + +#ifndef _WIN32 +/* network name into network address, can be expensive with NSS network lookup + * + * Only Class A, B or C networks are supported, partitioned networks + * (i.e. network/26 or network/28) are not supported by this facility. + */ + if (!(check_inet_network || check_inet6_network)) + { + const struct netent* ne = getnetbyname (ifname); +/* ne::n_net in host byte order */ + + if (ne) { + switch (ne->n_addrtype) { + case AF_INET: + if (AF_INET6 == family) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("IP address family conflict when resolving network name %s%s%s, found AF_INET when AF_INET6 expected."), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + return FALSE; + } +/* ne->n_net in network order */ + in_addr.s_addr = ne->n_net; + if (IN_MULTICAST(in_addr.s_addr)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_XDEV, + _("Network name %s%s%s resolves to IPv4 mulicast address."), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + return FALSE; + } + check_inet_network = TRUE; + break; + case AF_INET6: +#ifndef CONFIG_HAVE_IP6_NETWORKS + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("Not configured for IPv6 network name support, %s%s%s is an IPv6 network name."), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + return FALSE; +#else + if (AF_INET == family) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("IP address family conflict when resolving network name %s%s%s, found AF_INET6 when AF_INET expected."), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + return FALSE; + } + if (IN6_IS_ADDR_MULTICAST(&ne->n_net)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_XDEV, + _("Network name resolves to IPv6 mulicast address %s%s%s"), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + return FALSE; + } + in6_addr = *(const struct in6_addr*)&ne->n_net; + check_inet6_network = TRUE; + break; +#endif + default: + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("Network name resolves to non-internet protocol address family %s%s%s"), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + return FALSE; + } + } + } +#endif /* _WIN32 */ + +/* hostname lookup with potential DNS delay or error */ + if (!check_addr) + { + struct addrinfo hints = { + .ai_family = family, + .ai_socktype = SOCK_STREAM, /* not really, SOCK_RAW */ + .ai_protocol = IPPROTO_TCP, /* not really, IPPROTO_PGM */ + .ai_flags = AI_ADDRCONFIG, /* AI_V4MAPPED is unhelpful */ + }, *res; + + const int eai = getaddrinfo (ifname, NULL, &hints, &res); + switch (eai) { + case 0: + if (AF_INET == res->ai_family && + IN_MULTICAST(ntohl (((struct sockaddr_in*)(res->ai_addr))->sin_addr.s_addr))) + { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_XDEV, + _("Expecting interface address, found IPv4 multicast name %s%s%s"), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + freeaddrinfo (res); + return FALSE; + } + else if (AF_INET6 == res->ai_family && + IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)res->ai_addr)->sin6_addr)) + { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_XDEV, + _("Expecting interface address, found IPv6 multicast name %s%s%s"), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + freeaddrinfo (res); + return FALSE; + } + memcpy (&addr, res->ai_addr, pgm_sockaddr_len (res->ai_addr)); + freeaddrinfo (res); + check_addr = TRUE; + break; + +#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME + case EAI_NODATA: +#endif + case EAI_NONAME: + check_ifname = TRUE; + break; + + default: + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_eai_errno (eai, errno), + _("Internet host resolution: %s(%d)"), + gai_strerror (eai), eai); + return FALSE; + } + } + +/* iterate through interface list and match device name, ip or net address */ + if (!pgm_getifaddrs (&ifap, error)) { + pgm_prefix_error (error, + _("Enumerating network interfaces: ")); + return FALSE; + } + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) + { + if (NULL == ifa->ifa_addr) + continue; + + switch (ifa->ifa_addr->sa_family) { +/* ignore raw entries on Linux */ +#ifdef AF_PACKET + case AF_PACKET: + continue; +#endif + case AF_INET: + if (AF_INET6 == family) + continue; + break; + case AF_INET6: + if (AF_INET == family) + continue; + break; + default: + continue; + } + + const unsigned ifindex = pgm_if_nametoindex (ifa->ifa_addr->sa_family, ifa->ifa_name); + pgm_assert (0 != ifindex); + +/* check numeric host */ + if (check_addr && + (0 == pgm_sockaddr_cmp (ifa->ifa_addr, (const struct sockaddr*)&addr))) + { + strcpy (ir->ir_name, ifa->ifa_name); + ir->ir_flags = ifa->ifa_flags; + if (ir->ir_flags & IFF_LOOPBACK) + pgm_warn (_("Interface %s reports as a loopback device."), ir->ir_name); + if (!(ir->ir_flags & IFF_MULTICAST)) + pgm_warn (_("Interface %s reports as a non-multicast capable device."), ir->ir_name); + ir->ir_interface = ifindex; + memcpy (&ir->ir_addr, ifa->ifa_addr, pgm_sockaddr_len (ifa->ifa_addr)); + pgm_freeifaddrs (ifap); + return TRUE; + } + +/* check network address */ + if (check_inet_network && + AF_INET == ifa->ifa_addr->sa_family) + { + const struct in_addr ifaddr = { .s_addr = ntohl (((struct sockaddr_in*)ifa->ifa_addr)->sin_addr.s_addr) }; + const struct in_addr netmask = { .s_addr = ntohl (((struct sockaddr_in*)ifa->ifa_netmask)->sin_addr.s_addr) }; + if (is_in_net (&ifaddr, &in_addr, &netmask)) { + strcpy (ir->ir_name, ifa->ifa_name); + ir->ir_flags = ifa->ifa_flags; + if (ir->ir_flags & IFF_LOOPBACK) { + pgm_warn (_("Skipping matching loopback network device %s."), ir->ir_name); + goto skip_inet_network; + } + if (!(ir->ir_flags & IFF_MULTICAST)) { + pgm_warn (_("Skipping matching non-multicast capable network device %s."), ir->ir_name); + goto skip_inet_network; + } + ir->ir_interface = ifindex; + memcpy (&ir->ir_addr, ifa->ifa_addr, pgm_sockaddr_len (ifa->ifa_addr)); + pgm_freeifaddrs (ifap); + return TRUE; + } + } + if (check_inet6_network && + AF_INET6 == ifa->ifa_addr->sa_family) + { + const struct in6_addr ifaddr = ((struct sockaddr_in6*)ifa->ifa_addr)->sin6_addr; + const struct in6_addr netmask = ((struct sockaddr_in6*)ifa->ifa_netmask)->sin6_addr; + if (is_in_net6 (&ifaddr, &in6_addr, &netmask)) { + strcpy (ir->ir_name, ifa->ifa_name); + ir->ir_flags = ifa->ifa_flags; + if (ir->ir_flags & IFF_LOOPBACK) { + pgm_warn (_("Skipping matching loopback network device %s."), ir->ir_name); + goto skip_inet_network; + } + if (!(ir->ir_flags & IFF_MULTICAST)) { + pgm_warn (_("Skipping matching non-multicast capable network device %s."), ir->ir_name); + goto skip_inet_network; + } + ir->ir_interface = ifindex; + memcpy (&ir->ir_addr, ifa->ifa_addr, pgm_sockaddr_len (ifa->ifa_addr)); + pgm_freeifaddrs (ifap); + return TRUE; + } + } +skip_inet_network: + +/* check interface name */ + if (check_ifname) + { + if (0 != strcmp (ifname, ifa->ifa_name)) + continue; + + ir->ir_flags = ifa->ifa_flags; +/* skip loopback and non-multicast capable devices */ + if ((ir->ir_flags & IFF_LOOPBACK) || !(ir->ir_flags & IFF_MULTICAST)) + continue; + +/* check for multiple interfaces */ + if (interface_matches++) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NOTUNIQ, + _("Network interface name not unique %s%s%s"), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + pgm_freeifaddrs (ifap); + return FALSE; + } + + ir->ir_interface = ifindex; + strcpy (ir->ir_name, ifa->ifa_name); + memcpy (&ir->ir_addr, ifa->ifa_addr, pgm_sockaddr_len (ifa->ifa_addr)); + continue; + } + + } + + if (0 == interface_matches) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("No matching non-loopback and multicast capable network interface %s%s%s"), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + pgm_freeifaddrs (ifap); + return FALSE; + } + + pgm_freeifaddrs (ifap); + return TRUE; +} + +/* parse one multicast address, conflict resolution of multiple address families of DNS multicast names is + * deferred to libc. + * + * Zone indices are ignored as interface specification is already available. + * + * reserved addresses may flag warnings: + * + * 224.0.0.0/24 for local network control + * 224.0.1/24 for internetwork control + * 169.254.255.255, ff02::1 all local nodes on segment + * ff02::2 all routers + * ff05::1 all nodes + * ff0x::fb multicast DNS + * ff0x::108 NIS + * ff05::1:3 DHCP + * + * returns TRUE on success, FALSE on error and sets error appropriately. + */ + +static +bool +parse_group ( + const int family, /* AF_UNSPEC | AF_INET | AF_INET6 */ + const char* restrict group, /* NULL terminated */ + struct sockaddr* restrict addr, /* pointer to sockaddr_storage for writing */ + pgm_error_t** restrict error + ) +{ +/* pre-conditions */ + pgm_assert (AF_INET == family || AF_INET6 == family || AF_UNSPEC == family); + pgm_assert (NULL != group); + pgm_assert (NULL != addr); + + pgm_debug ("parse_group (family:%s group:%s%s%s addr:%p error:%p)", + pgm_family_string (family), + group ? "\"" : "", group ? group : "(null)", group ? "\"" : "", + (const void*)addr, + (const void*)error); + +/* strip any square brackets for early IPv6 literal evaluation */ + if (AF_INET != family && + '[' == group[0]) + { + const size_t grouplen = strlen(group); + if (']' == group[ grouplen - 1 ]) { + char literal[1024]; + strncpy (literal, group + 1, grouplen - 2); + literal[ grouplen - 2 ] = 0; + if (pgm_inet_pton (AF_INET6, literal, &((struct sockaddr_in6*)addr)->sin6_addr) && + IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)addr)->sin6_addr)) + { + addr->sa_family = AF_INET6; + ((struct sockaddr_in6*)addr)->sin6_port = 0; + ((struct sockaddr_in6*)addr)->sin6_flowinfo = 0; + ((struct sockaddr_in6*)addr)->sin6_scope_id = 0; + return TRUE; + } + } + } + +/* IPv4 address */ + if (AF_INET6 != family && + pgm_inet_pton (AF_INET, group, &((struct sockaddr_in*)addr)->sin_addr) && + IN_MULTICAST(ntohl (((struct sockaddr_in*)addr)->sin_addr.s_addr))) + { + addr->sa_family = AF_INET; + return TRUE; + } + if (AF_INET != family && + pgm_inet_pton (AF_INET6, group, &((struct sockaddr_in6*)addr)->sin6_addr) && + IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)addr)->sin6_addr)) + { + addr->sa_family = AF_INET6; + ((struct sockaddr_in6*)addr)->sin6_port = 0; + ((struct sockaddr_in6*)addr)->sin6_flowinfo = 0; + ((struct sockaddr_in6*)addr)->sin6_scope_id = 0; + return TRUE; + } + +#ifndef _WIN32 +/* NSS network */ + const struct netent* ne = getnetbyname (group); +/* ne::n_net in host byte order */ + if (ne) { + switch (ne->n_addrtype) { + case AF_INET: + if (AF_INET6 == family) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("IP address family conflict when resolving network name %s%s%s, found IPv4 when IPv6 expected."), + group ? "\"" : "", group ? group : "(null)", group ? "\"" : ""); + return FALSE; + } + if (IN_MULTICAST(ne->n_net)) { + addr->sa_family = AF_INET; + ((struct sockaddr_in*)addr)->sin_addr.s_addr = htonl (ne->n_net); + return TRUE; + } + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("IP address class conflict when resolving network name %s%s%s, expected IPv4 multicast."), + group ? "\"" : "", group ? group : "(null)", group ? "\"" : ""); + return FALSE; + case AF_INET6: +#ifndef CONFIG_HAVE_IP6_NETWORKS + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("Not configured for IPv6 network name support, %s%s%s is an IPv6 network name."), + group ? "\"" : "", group ? group : "(null)", group ? "\"" : ""); + return FALSE; +#else + if (AF_INET == family) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("IP address family conflict when resolving network name %s%s%s, found IPv6 when IPv4 expected."), + group ? "\"" : "", group ? group : "(null)", group ? "\"" : ""); + return FALSE; + } + if (IN6_IS_ADDR_MULTICAST(&ne->n_net)) { + addr->sa_family = AF_INET6; + ((struct sockaddr_in6*)addr)->sin6_addr = *(const struct in6_addr*)ne->n_net; + ((struct sockaddr_in6*)&addr)->sin6_port = 0; + ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; + ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; + return TRUE; + } + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("IP address class conflict when resolving network name %s%s%s, expected IPv6 multicast."), + group ? "\"" : "", group ? group : "(null)", group ? "\"" : ""); + return FALSE; +#endif /* CONFIG_HAVE_IP6_NETWORKS */ + default: + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("Network name resolves to non-internet protocol address family %s%s%s"), + group ? "\"" : "", group ? group : "(null)", group ? "\"" : ""); + return FALSE; + } + } +#endif /* _WIN32 */ + +/* lookup group through name service */ + struct addrinfo hints = { + .ai_family = family, + .ai_socktype = SOCK_STREAM, /* not really, SOCK_RAW */ + .ai_protocol = IPPROTO_TCP, /* not really, IPPROTO_PGM */ + .ai_flags = AI_ADDRCONFIG, /* AI_V4MAPPED is unhelpful */ + }, *res; + + const int eai = getaddrinfo (group, NULL, &hints, &res); + if (0 != eai) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_eai_errno (eai, errno), + _("Resolving receive group: %s"), + gai_strerror (eai)); + return FALSE; + } + + if ((AF_INET6 != family && IN_MULTICAST(ntohl (((struct sockaddr_in*)res->ai_addr)->sin_addr.s_addr))) || + (AF_INET != family && IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)res->ai_addr)->sin6_addr))) + { + memcpy (addr, res->ai_addr, res->ai_addrlen); + freeaddrinfo (res); + return TRUE; + } + + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_INVAL, + _("Unresolvable receive group %s%s%s"), + group ? "\"" : "", group ? group : "(null)", group ? "\"" : ""); + freeaddrinfo (res); + return FALSE; +} + +/* parse an interface entity from a network parameter. + * + * family can be unspecified - AF_UNSPEC, can return interfaces with the unspecified + * address family + * + * examples: "eth0" + * "hme0,hme1" + * "qe0,qe1,qe2" + * "qe0,qe2,qe2" => valid even though duplicate interface name + * + * returns TRUE on success with device_list containing double linked list of devices as + * sockaddr/idx pairs. returns FALSE on error, including multiple matching adapters. + * + * memory ownership of linked list is passed to caller and must be freed with pgm_free + * and the pgm_list_free* api. + */ + +static +bool +parse_interface_entity ( + int family, /* AF_UNSPEC | AF_INET | AF_INET6 */ + const char* restrict entity, /* NULL terminated */ + pgm_list_t** restrict interface_list, /* */ + pgm_error_t** restrict error + ) +{ + struct interface_req* ir; + pgm_list_t* source_list = NULL; + +/* pre-conditions */ + pgm_assert (AF_INET == family || AF_INET6 == family || AF_UNSPEC == family); + pgm_assert (NULL != interface_list); + pgm_assert (NULL == *interface_list); + pgm_assert (NULL != error); + + pgm_debug ("parse_interface_entity (family:%s entity:%s%s%s interface_list:%p error:%p)", + pgm_family_string (family), + entity ? "\"":"", entity ? entity : "(null)", entity ? "\"":"", + (const void*)interface_list, + (const void*)error); + +/* the empty entity, returns in_addr_any for both receive and send interfaces */ + if (NULL == entity) + { + ir = pgm_new0 (struct interface_req, 1); + ir->ir_addr.ss_family = family; + *interface_list = pgm_list_append (*interface_list, ir); + return TRUE; + } + +/* check interface name length limit */ + char** tokens = pgm_strsplit (entity, ",", 10); + int j = 0; + while (tokens && tokens[j]) + { + pgm_error_t* sub_error = NULL; + ir = pgm_new (struct interface_req, 1); + if (!parse_interface (family, tokens[j], ir, &sub_error)) + { +/* mark multiple interfaces for later decision based on group families */ + if (sub_error && PGM_ERROR_NOTUNIQ == sub_error->code) + { + ir->ir_addr.ss_family = AF_UNSPEC; + pgm_error_free (sub_error); + } +/* bail out on first interface with an error */ + else + { + pgm_propagate_error (error, sub_error); + pgm_free (ir); + pgm_strfreev (tokens); + while (source_list) { + pgm_free (source_list->data); + source_list = pgm_list_delete_link (source_list, source_list); + } + return FALSE; + } + } + + source_list = pgm_list_append (source_list, ir); + ++j; + } + + pgm_strfreev (tokens); + *interface_list = source_list; + return TRUE; +} + +/* parse a receive multicast group entity. can contain more than one multicast group to + * support asymmetric fan-out. + * + * if group is ambiguous, i.e. empty or a name mapping then the address family of the matching + * interface is queried. if the interface is also ambiguous, i.e. empty interface and receive group + * then the hostname will be used to determine the default node address family. if the hosts + * node name resolves both IPv4 and IPv6 address families then the first matching value is taken. + * + * e.g. "239.192.0.1" + * "239.192.0.100,239.192.0.101" + * + * unspecified address family interfaces are forced to AF_INET or AF_INET6. + * + * returns TRUE on success, returns FALSE on error and sets error appropriately. + */ + +static +bool +parse_receive_entity ( + int family, /* AF_UNSPEC | AF_INET | AF_INET6 */ + const char* restrict entity, /* NULL terminated */ + pgm_list_t** restrict interface_list, /* */ + pgm_list_t** restrict recv_list, /* */ + pgm_error_t** restrict error + ) +{ +/* pre-conditions */ + pgm_assert (AF_INET == family || AF_INET6 == family || AF_UNSPEC == family); + pgm_assert (NULL != recv_list); + pgm_assert (NULL == *recv_list); + pgm_assert (NULL != error); + + pgm_debug ("parse_receive_entity (family:%s entity:%s%s%s interface_list:%p recv_list:%p error:%p)", + pgm_family_string (family), + entity ? "\"":"", entity ? entity : "(null)", entity ? "\"":"", + (const void*)interface_list, + (const void*)recv_list, + (const void*)error); + + struct group_source_req* recv_gsr; + struct interface_req* primary_interface = (struct interface_req*)pgm_memdup ((*interface_list)->data, sizeof(struct interface_req)); + +/* the empty entity */ + if (NULL == entity) + { +/* default receive object */ + recv_gsr = pgm_new0 (struct group_source_req, 1); + recv_gsr->gsr_interface = primary_interface->ir_interface; + recv_gsr->gsr_group.ss_family = family; + +/* track IPv6 scope from any resolved interface */ + unsigned scope_id = 0; + +/* if using unspec default group check the interface for address family + */ + if (AF_UNSPEC == recv_gsr->gsr_group.ss_family) + { + if (AF_UNSPEC == primary_interface->ir_addr.ss_family) + { + struct sockaddr_storage addr; + if (!pgm_if_getnodeaddr (AF_UNSPEC, (struct sockaddr*)&addr, sizeof(addr), error)) + { + pgm_prefix_error (error, + _("Node primary address family cannot be determined: ")); + pgm_free (recv_gsr); + pgm_free (primary_interface); + return FALSE; + } + recv_gsr->gsr_group.ss_family = addr.ss_family; + scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&addr); + +/* was an interface actually specified */ + if (primary_interface->ir_name[0] != '\0') + { + struct interface_req ir; + if (!parse_interface (recv_gsr->gsr_group.ss_family, primary_interface->ir_name, &ir, error)) + { + pgm_prefix_error (error, + _("Unique address cannot be determined for interface %s%s%s: "), + primary_interface->ir_name ? "\"" : "", primary_interface->ir_name ? primary_interface->ir_name : "(null)", primary_interface->ir_name ? "\"" : ""); + pgm_free (recv_gsr); + pgm_free (primary_interface); + return FALSE; + } + + recv_gsr->gsr_interface = ir.ir_interface; + memcpy (&primary_interface->ir_addr, &ir.ir_addr, pgm_sockaddr_len ((struct sockaddr*)&ir.ir_addr)); + scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&ir.ir_addr); + } + } + else + { +/* use interface address family for multicast group */ + recv_gsr->gsr_group.ss_family = primary_interface->ir_addr.ss_family; + scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&primary_interface->ir_addr); + } + } + + + pgm_assert (AF_UNSPEC != recv_gsr->gsr_group.ss_family); + if (AF_UNSPEC != primary_interface->ir_addr.ss_family) + { + pgm_assert (recv_gsr->gsr_group.ss_family == primary_interface->ir_addr.ss_family); + } + else + { +/* check if we can now resolve the interface by address family of the receive group */ + if (primary_interface->ir_name[0] != '\0') + { + struct interface_req ir; + if (!parse_interface (recv_gsr->gsr_group.ss_family, primary_interface->ir_name, &ir, error)) + { + pgm_prefix_error (error, + _("Unique address cannot be determined for interface %s%s%s: "), + primary_interface->ir_name ? "\"" : "", primary_interface->ir_name ? primary_interface->ir_name : "(null)", primary_interface->ir_name ? "\"" : ""); + pgm_free (recv_gsr); + pgm_free (primary_interface); + return FALSE; + } + + recv_gsr->gsr_interface = ir.ir_interface; + scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&ir.ir_addr); + } + } + +/* copy default PGM multicast group */ + switch (recv_gsr->gsr_group.ss_family) { + case AF_INET6: + memcpy (&((struct sockaddr_in6*)&recv_gsr->gsr_group)->sin6_addr, + &if6_default_group_addr, + sizeof(if6_default_group_addr)); + ((struct sockaddr_in6*)&recv_gsr->gsr_group)->sin6_scope_id = scope_id; + break; + + case AF_INET: + ((struct sockaddr_in*)&recv_gsr->gsr_group)->sin_addr.s_addr = htonl(IF_DEFAULT_GROUP); + break; + + default: + pgm_assert_not_reached(); + } + +/* ASM: source = group */ + memcpy (&recv_gsr->gsr_source, &recv_gsr->gsr_group, pgm_sockaddr_len ((struct sockaddr*)&recv_gsr->gsr_group)); + *recv_list = pgm_list_append (*recv_list, recv_gsr); + pgm_free (primary_interface); + return TRUE; + } + +/* parse one or more multicast receive groups. + */ + + int j = 0; + char** tokens = pgm_strsplit (entity, ",", 10); + while (tokens && tokens[j]) + { +/* default receive object */ + recv_gsr = pgm_new0 (struct group_source_req, 1); + recv_gsr->gsr_interface = primary_interface->ir_interface; + recv_gsr->gsr_group.ss_family = family; + + if (AF_UNSPEC == recv_gsr->gsr_group.ss_family) + { + if (AF_UNSPEC == primary_interface->ir_addr.ss_family) + { + pgm_debug ("Address family of receive group cannot be determined from interface."); + } + else + { + recv_gsr->gsr_group.ss_family = primary_interface->ir_addr.ss_family; + ((struct sockaddr_in6*)&recv_gsr->gsr_group)->sin6_scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&primary_interface->ir_addr); + } + } + + if (!parse_group (recv_gsr->gsr_group.ss_family, tokens[j], (struct sockaddr*)&recv_gsr->gsr_group, error)) + { + pgm_prefix_error (error, + _("Unresolvable receive entity %s%s%s: "), + tokens[j] ? "\"" : "", tokens[j] ? tokens[j] : "(null)", tokens[j] ? "\"" : ""); + pgm_free (recv_gsr); + pgm_strfreev (tokens); + pgm_free (primary_interface); + return FALSE; + } + +/* check if we can now resolve the source interface by address family of the receive group */ + if (AF_UNSPEC == primary_interface->ir_addr.ss_family) + { + if (primary_interface->ir_name[0] != '\0') + { + struct interface_req ir; + if (!parse_interface (recv_gsr->gsr_group.ss_family, primary_interface->ir_name, &ir, error)) + { + pgm_prefix_error (error, + _("Unique address cannot be determined for interface %s%s%s: "), + primary_interface->ir_name ? "\"" : "", primary_interface->ir_name ? primary_interface->ir_name : "(null)", primary_interface->ir_name ? "\"" : ""); + pgm_free (recv_gsr); + pgm_free (primary_interface); + return FALSE; + } + + recv_gsr->gsr_interface = ir.ir_interface; + ((struct sockaddr_in6*)&recv_gsr->gsr_group)->sin6_scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&ir.ir_addr); + } + } + else + { +/* keep interface scope */ + ((struct sockaddr_in6*)&recv_gsr->gsr_group)->sin6_scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&primary_interface->ir_addr); + } + +/* ASM: source = group */ + memcpy (&recv_gsr->gsr_source, &recv_gsr->gsr_group, pgm_sockaddr_len ((struct sockaddr*)&recv_gsr->gsr_group)); + *recv_list = pgm_list_append (*recv_list, recv_gsr); + ++j; + } + + pgm_strfreev (tokens); + pgm_free (primary_interface); + return TRUE; +} + +static +bool +parse_send_entity ( + int family, /* AF_UNSPEC | AF_INET | AF_INET6 */ + const char* restrict entity, /* null = empty entity */ + pgm_list_t** restrict interface_list, /* */ + pgm_list_t** restrict recv_list, /* */ + pgm_list_t** restrict send_list, /* */ + pgm_error_t** restrict error + ) +{ +/* pre-conditions */ + pgm_assert (AF_INET == family || AF_INET6 == family || AF_UNSPEC == family); + pgm_assert (NULL != recv_list); + pgm_assert (NULL != *recv_list); + pgm_assert (NULL != send_list); + pgm_assert (NULL == *send_list); + pgm_assert (NULL != error); + + pgm_debug ("parse_send_entity (family:%s entity:%s%s%s interface_list:%p recv_list:%p send_list:%p error:%p)", + pgm_family_string (family), + entity ? "\"":"", entity ? entity : "(null)", entity ? "\"":"", + (const void*)interface_list, + (const void*)recv_list, + (const void*)send_list, + (const void*)error); + + struct group_source_req* send_gsr; + const struct interface_req* primary_interface = (struct interface_req*)(*interface_list)->data; + + if (entity == NULL) + { + send_gsr = pgm_memdup ((*recv_list)->data, sizeof(struct group_source_req)); + *send_list = pgm_list_append (*send_list, send_gsr); + return TRUE; + } + +/* default send object */ + send_gsr = pgm_new0 (struct group_source_req, 1); + send_gsr->gsr_interface = primary_interface->ir_interface; + if (!parse_group (family, entity, (struct sockaddr*)&send_gsr->gsr_group, error)) + { + pgm_prefix_error (error, + _("Unresolvable send entity %s%s%s: "), + entity ? "\"":"", entity ? entity : "(null)", entity ? "\"":""); + pgm_free (send_gsr); + return FALSE; + } + +/* check if we can now resolve the source interface by address family of the send group */ + if (AF_UNSPEC == primary_interface->ir_addr.ss_family) + { + if (primary_interface->ir_name[0] != '\0') + { + struct interface_req ir; + if (!parse_interface (send_gsr->gsr_group.ss_family, primary_interface->ir_name, &ir, error)) + { + pgm_prefix_error (error, + _("Unique address cannot be determined for interface %s%s%s: "), + primary_interface->ir_name ? "\"":"", primary_interface->ir_name ? primary_interface->ir_name : "(null)", primary_interface->ir_name ? "\"":""); + pgm_free (send_gsr); + return FALSE; + } + + send_gsr->gsr_interface = ir.ir_interface; + ((struct sockaddr_in6*)&send_gsr->gsr_group)->sin6_scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&ir.ir_addr); + } + } + +/* ASM: source = group */ + memcpy (&send_gsr->gsr_source, &send_gsr->gsr_group, pgm_sockaddr_len ((struct sockaddr*)&send_gsr->gsr_group)); + *send_list = pgm_list_append (*send_list, send_gsr); + return TRUE; +} + +/* parse network parameter + * + * interface list; receive multicast group list; send multicast group + */ + +#define IS_HOSTNAME(x) ( /* RFC 952 */ \ + isalnum(x) || \ + ((x) == '-') || \ + ((x) == '.') \ + ) +#define IS_IP(x) ( \ + isdigit(x) || \ + ((x) == '.') || \ + ((x) == '/') \ + ) +#define IS_IP6(x) ( \ + isxdigit(x) || \ + ((x) == ':') || \ + ((x) == '/') || \ + ((x) == '.') || \ + ((x) == '[') || \ + ((x) == ']') \ + ) +/* e.g. fe80::1%eth0.620 vlan tag, + * fe80::1%eth0:0 IP alias + * fe80::1%qe0_0 Solaris link name + * + * The Linux kernel generally doesn't care too much, but everything else falls apart with + * random characters in interface names. Hyphen is a popular problematic character. + */ +#define IS_IP6_WITH_ZONE(x) ( \ + IS_IP6(x) || \ + ((x) == '%') || \ + isalpha(x) || \ + ((x) == '_') \ + ) +#define IS_NETPARAM(x) ( \ + ((x) == ',') || \ + ((x) == ';') \ + ) + +static inline +bool +is_network_char ( + const int family, + const char c + ) +{ + if (IS_HOSTNAME(c) || + (AF_INET == family && IS_IP(c)) || + ((AF_INET6 == family || AF_UNSPEC == family) && IS_IP6_WITH_ZONE(c)) || + IS_NETPARAM(c)) + return TRUE; + else + return FALSE; +} + +static +bool +network_parse ( + const char* restrict network, /* NULL terminated */ + int family, /* AF_UNSPEC | AF_INET | AF_INET6 */ + pgm_list_t** restrict recv_list, /* */ + pgm_list_t** restrict send_list, /* */ + pgm_error_t** restrict error + ) +{ + bool retval = FALSE; + const char *p = network; + const char *e = p + strlen(network); + enum { ENTITY_INTERFACE, ENTITY_RECEIVE, ENTITY_SEND, ENTITY_ERROR } ec = ENTITY_INTERFACE; + const char *b = p; /* begin of entity */ + pgm_list_t* source_list = NULL; + pgm_error_t* sub_error = NULL; + +/* pre-conditions */ + pgm_assert (NULL != network); + pgm_assert (AF_UNSPEC == family || AF_INET == family || AF_INET6 == family); + pgm_assert (NULL != recv_list); + pgm_assert (NULL != send_list); + + pgm_debug ("network_parse (network:%s%s%s family:%s recv_list:%p send_list:%p error:%p)", + network ? "\"" : "", network ? network : "(null)", network ? "\"" : "", + pgm_family_string (family), + (const void*)recv_list, + (const void*)send_list, + (const void*)error); + + while (p < e) + { + if (!is_network_char (family, *p)) + { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_INVAL, + _("'%c' is not a valid character."), + *p); + goto free_lists; + } + + if (*p == ';') /* end of entity */ + { + if (b == p) /* empty entity */ + { + switch (ec++) { + case ENTITY_INTERFACE: + retval = parse_interface_entity (family, NULL, &source_list, error); + break; + + case ENTITY_RECEIVE: + retval = parse_receive_entity (family, NULL, &source_list, recv_list, error); + break; + + case ENTITY_SEND: + retval = parse_send_entity (family, NULL, &source_list, recv_list, send_list, error); + break; + + default: + pgm_assert_not_reached(); + break; + } + + if (!retval) + goto free_lists; + + b = ++p; + continue; + } + +/* entity from b to p-1 */ + char entity[1024]; + strncpy (entity, b, sizeof(entity)); + entity[p - b] = 0; + + switch (ec++) { + case ENTITY_INTERFACE: + if (parse_interface_entity (family, entity, &source_list, &sub_error)) + break; + if (!(sub_error && PGM_ERROR_XDEV == sub_error->code)) + { +/* fall through on multicast */ + if (!(sub_error && PGM_ERROR_NOTUNIQ == sub_error->code)) + { + pgm_propagate_error (error, sub_error); + goto free_lists; + } + pgm_clear_error (&sub_error); +/* FIXME: too many interfaces */ + if (pgm_list_length (source_list) > 1) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_INVAL, + _("Send group list contains more than one entity.")); + goto free_lists; + } + break; + } + pgm_clear_error (&sub_error); + while (source_list) { + pgm_free (source_list->data); + source_list = pgm_list_delete_link (source_list, source_list); + } + if (!parse_interface_entity (family, NULL, &source_list, &sub_error) && + !(sub_error && PGM_ERROR_NOTUNIQ == sub_error->code)) + { + pgm_propagate_error (error, sub_error); + goto free_lists; + } + pgm_clear_error (&sub_error); + ec++; + + case ENTITY_RECEIVE: + if (!parse_receive_entity (family, entity, &source_list, recv_list, error)) + goto free_lists; + break; + + case ENTITY_SEND: + if (!parse_send_entity (family, entity, &source_list, recv_list, send_list, error)) + goto free_lists; + break; + + default: + pgm_assert_not_reached(); + break; + } + + b = ++p; + continue; + } + + p++; + } + + if (b < e) { + switch (ec++) { + case ENTITY_INTERFACE: + if (parse_interface_entity (family, b, &source_list, &sub_error)) + break; + if (!(sub_error && PGM_ERROR_XDEV == sub_error->code)) + { +/* fall through on multicast */ + if (!(sub_error && PGM_ERROR_NOTUNIQ == sub_error->code)) + { + pgm_propagate_error (error, sub_error); + goto free_lists; + } + pgm_clear_error (&sub_error); + +/* FIXME: too many interfaces */ + if (pgm_list_length (source_list) > 1) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_INVAL, + _("Send group list contains more than one entity.")); + goto free_lists; + } + break; + } + pgm_clear_error (&sub_error); + while (source_list) { + pgm_free (source_list->data); + source_list = pgm_list_delete_link (source_list, source_list); + } + if (!parse_interface_entity (family, NULL, &source_list, &sub_error) && + !(sub_error && PGM_ERROR_NOTUNIQ == sub_error->code)) + { + pgm_propagate_error (error, sub_error); + goto free_lists; + } + ec++; + + case ENTITY_RECEIVE: + if (!parse_receive_entity (family, b, &source_list, recv_list, error)) + goto free_lists; + break; + + case ENTITY_SEND: + if (!parse_send_entity (family, b, &source_list, recv_list, send_list, error)) + goto free_lists; + break; + + default: + pgm_assert_not_reached(); + break; + } + } + + while (ec <= ENTITY_SEND) + { + switch (ec++) { + case ENTITY_INTERFACE: + if (!parse_interface_entity (family, NULL, &source_list, error)) + goto free_lists; + break; + + case ENTITY_RECEIVE: + if (!parse_receive_entity (family, NULL, &source_list, recv_list, error)) + goto free_lists; + break; + + case ENTITY_SEND: + if (!parse_send_entity (family, NULL, &source_list, recv_list, send_list, error)) + goto free_lists; + break; + + default: + pgm_assert_not_reached(); + break; + } + } + + if (pgm_list_length (source_list) > 1) + goto free_lists; + +/* cleanup source interface list */ + while (source_list) { + pgm_free (source_list->data); + source_list = pgm_list_delete_link (source_list, source_list); + } + + return TRUE; + +free_lists: + while (source_list) { + pgm_free (source_list->data); + source_list = pgm_list_delete_link (source_list, source_list); + } + while (*recv_list) { + pgm_free ((*recv_list)->data); + *recv_list = pgm_list_delete_link (*recv_list, *recv_list); + } + while (*send_list) { + pgm_free ((*send_list)->data); + *send_list = pgm_list_delete_link (*send_list, *send_list); + } + return FALSE; +} + +/* create group_source_req as used by pgm_transport_create which specify port, address & interface. + * gsr_source is copied from gsr_group for ASM, caller needs to populate gsr_source for SSM. + * + * returns TRUE on success, returns FALSE on error and sets error appropriately. + */ + +bool +pgm_getaddrinfo ( + const char* restrict network, + const struct pgm_addrinfo_t* const restrict hints, + struct pgm_addrinfo_t** restrict res, + pgm_error_t** restrict error + ) +{ + struct pgm_addrinfo_t* ai; + const int family = hints ? hints->ai_family : AF_UNSPEC; + pgm_list_t* recv_list = NULL; /* */ + pgm_list_t* send_list = NULL; /* */ + + pgm_return_val_if_fail (NULL != network, FALSE); + pgm_return_val_if_fail (AF_UNSPEC == family || AF_INET == family || AF_INET6 == family, FALSE); + pgm_return_val_if_fail (NULL != res, FALSE); + + if (hints) { + pgm_debug ("pgm_getaddrinfo (network:%s%s%s hints: {family:%s} res:%p error:%p)", + network ? "\"" : "", network ? network : "(null)", network ? "\"" : "", + pgm_family_string (family), + (const void*)res, + (const void*)error); + } else { + pgm_debug ("pgm_getaddrinfo (network:%s%s%s hints:%p res:%p error:%p)", + network ? "\"" : "", network ? network : "(null)", network ? "\"" : "", + (const void*)hints, + (const void*)res, + (const void*)error); + } + + if (!network_parse (network, family, &recv_list, &send_list, error)) + return FALSE; + const size_t recv_list_len = pgm_list_length (recv_list); + const size_t send_list_len = pgm_list_length (send_list); + ai = pgm_malloc0 (sizeof(struct pgm_addrinfo_t) + + (recv_list_len + send_list_len) * sizeof(struct group_source_req)); + ai->ai_recv_addrs_len = recv_list_len; + ai->ai_recv_addrs = (void*)((char*)ai + sizeof(struct pgm_addrinfo_t)); + ai->ai_send_addrs_len = send_list_len; + ai->ai_send_addrs = (void*)((char*)ai->ai_recv_addrs + recv_list_len * sizeof(struct group_source_req)); + + size_t i = 0; + while (recv_list) { + memcpy (&ai->ai_recv_addrs[i++], recv_list->data, sizeof(struct group_source_req)); + pgm_free (recv_list->data); + recv_list = pgm_list_delete_link (recv_list, recv_list); + } + i = 0; + while (send_list) { + memcpy (&ai->ai_send_addrs[i++], send_list->data, sizeof(struct group_source_req)); + pgm_free (send_list->data); + send_list = pgm_list_delete_link (send_list, send_list); + } + *res = ai; + return TRUE; +} + +void +pgm_freeaddrinfo ( + struct pgm_addrinfo_t* res + ) +{ + pgm_free (res); +} + + +static +const char* +pgm_family_string ( + const int family + ) +{ + const char* c; + + switch (family) { + case AF_UNSPEC: c = "AF_UNSPEC"; break; + case AF_INET: c = "AF_INET"; break; + case AF_INET6: c = "AF_INET6"; break; + default: c = "(unknown)"; break; + } + + return c; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/if_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/if_unittest.c new file mode 100644 index 0000000..dbaa684 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/if_unittest.c @@ -0,0 +1,1495 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for network interface declaration parsing. + * + * CAUTION: Assumes host is IPv4 by default for AF_UNSPEC + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* IFF_UP */ +#define _BSD_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* mock state */ + +struct mock_host_t { + struct sockaddr_storage address; + char* canonical_hostname; + char* alias; +}; + +struct mock_network_t { + char* name; + struct sockaddr_storage number; + char** aliases; +}; + +struct mock_interface_t { + unsigned int index; + char* name; + unsigned int flags; + struct sockaddr_storage addr; + struct sockaddr_storage netmask; +}; + +static GList *mock_hosts = NULL, *mock_networks = NULL, *mock_interfaces = NULL; + +#define MOCK_HOSTNAME "kiku" +#define MOCK_HOSTNAME6 "ip6-kiku" /* ping6 doesn't work on fe80:: */ +#define MOCK_NETWORK "private" /* /etc/networks */ +#define MOCK_NETWORK6 "ip6-private" +#define MOCK_PGM_NETWORK "pgm-private" +#define MOCK_PGM_NETWORK6 "pgm-ip6-private" +#define MOCK_INTERFACE "eth0" +#define MOCK_INTERFACE_INDEX 2 +#define MOCK_ADDRESS "10.6.28.33" +#define MOCK_GROUP ((in_addr_t) 0xefc00001) /* 239.192.0.1 */ +#define MOCK_GROUP6_INIT { { { 0xff,8,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } } /* ff08::1 */ +static const struct in6_addr mock_group6_addr = MOCK_GROUP6_INIT; +#define MOCK_ADDRESS6 "2002:dce8:d28e::33" +#define MOCK_ADDRESS6_INIT { { { 0x20,2,0xdc,0xe8,0xd2,0x8e,0,0,0,0,0,0,0,0,0,0x33 } } } +static const struct in6_addr mock_address6_addr = MOCK_ADDRESS6_INIT; + +static int mock_family = 0; +static char* mock_kiku = MOCK_HOSTNAME; +static char* mock_localhost = "localhost"; +static char* mock_invalid = "invalid.invalid"; /* RFC 2606 */ +static char* mock_toolong = "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij12345"; /* 65 */ +static char* mock_hostname = NULL; + +struct pgm_ifaddrs_t; +struct pgm_error_t; + +bool mock_pgm_getifaddrs (struct pgm_ifaddrs_t**, struct pgm_error_t**); +void mock_pgm_freeifaddrs (struct pgm_ifaddrs_t*); +unsigned mock_pgm_if_nametoindex (const sa_family_t, const char*); +char* mock_if_indextoname (unsigned int, char*); +int mock_getnameinfo (const struct sockaddr*, socklen_t, char*, size_t, char*, size_t, int); +int mock_getaddrinfo (const char*, const char*, const struct addrinfo*, struct addrinfo**); +void mock_freeaddrinfo (struct addrinfo*); +int mock_gethostname (char*, size_t); +struct netent* mock_getnetbyname (const char*); +bool mock_pgm_if_getnodeaddr (const sa_family_t, struct sockaddr*, const socklen_t, struct pgm_error_t**); + +#define pgm_getifaddrs mock_pgm_getifaddrs +#define pgm_freeifaddrs mock_pgm_freeifaddrs +#define pgm_if_nametoindex mock_pgm_if_nametoindex +#define if_indextoname mock_if_indextoname +#define getnameinfo mock_getnameinfo +#define getaddrinfo mock_getaddrinfo +#define freeaddrinfo mock_freeaddrinfo +#define gethostname mock_gethostname +#define getnetbyname mock_getnetbyname +#define pgm_if_getnodeaddr mock_pgm_if_getnodeaddr + + +#define IF_DEBUG +#include "if.c" + + +static +gpointer +create_host ( + const char* address, + const char* canonical_hostname, + const char* alias + ) +{ + struct mock_host_t* new_host; + + g_assert (address); + g_assert (canonical_hostname); + + new_host = g_slice_alloc0 (sizeof(struct mock_host_t)); + g_assert (pgm_sockaddr_pton (address, (struct sockaddr*)&new_host->address)); + new_host->canonical_hostname = g_strdup (canonical_hostname); + new_host->alias = alias ? g_strdup (alias) : NULL; + + return new_host; +} + +static +gpointer +create_network ( + const char* name, + const char* number + ) +{ + struct mock_network_t* new_network; + + g_assert (name); + g_assert (number); + + new_network = g_slice_alloc0 (sizeof(struct mock_network_t)); + new_network->name = g_strdup (name); + g_assert (pgm_sockaddr_pton (number, (struct sockaddr*)&new_network->number)); + + return new_network; +} + +static +gpointer +create_interface ( + const unsigned index, + const char* name, + const char* flags + ) +{ + struct mock_interface_t* new_interface; + + g_assert (name); + g_assert (flags); + + new_interface = g_slice_alloc0 (sizeof(struct mock_interface_t)); + new_interface->index = index; + new_interface->name = g_strdup (name); + + struct sockaddr_in* sin = (gpointer)&new_interface->addr; + struct sockaddr_in6* sin6 = (gpointer)&new_interface->addr; + + gchar** tokens = g_strsplit (flags, ",", 0); + for (guint i = 0; tokens[i]; i++) + { + if (strcmp (tokens[i], "up") == 0) + new_interface->flags |= IFF_UP; + else if (strcmp (tokens[i], "down") == 0) + new_interface->flags |= 0; + else if (strcmp (tokens[i], "loop") == 0) + new_interface->flags |= IFF_LOOPBACK; + else if (strcmp (tokens[i], "broadcast") == 0) + new_interface->flags |= IFF_BROADCAST; + else if (strcmp (tokens[i], "multicast") == 0) + new_interface->flags |= IFF_MULTICAST; + else if (strncmp (tokens[i], "ip=", strlen("ip=")) == 0) { + const char* addr = tokens[i] + strlen("ip="); + g_assert (pgm_sockaddr_pton (addr, (struct sockaddr*)&new_interface->addr)); + } + else if (strncmp (tokens[i], "netmask=", strlen("netmask=")) == 0) { + const char* addr = tokens[i] + strlen("netmask="); + g_assert (pgm_sockaddr_pton (addr, (struct sockaddr*)&new_interface->netmask)); + } + else if (strncmp (tokens[i], "scope=", strlen("scope=")) == 0) { + const char* scope = tokens[i] + strlen("scope="); + g_assert (AF_INET6 == ((struct sockaddr*)&new_interface->addr)->sa_family); + ((struct sockaddr_in6*)&new_interface->addr)->sin6_scope_id = atoi (scope); + } + else + g_error ("parsing failed for flag %s%s%s", + tokens[i] ? "\"" : "", tokens[i] ? tokens[i] : "(null)", tokens[i] ? "\"" : ""); + } + + g_strfreev (tokens); + return new_interface; +} + +#define APPEND_HOST2(a,b,c) \ + do { \ + gpointer data = create_host ((a), (b), (c)); \ + g_assert (data); \ + mock_hosts = g_list_append (mock_hosts, data); \ + g_assert (mock_hosts); g_assert (mock_hosts->data); \ + } while (0) +#define APPEND_HOST(a,b) APPEND_HOST2((a),(b),NULL) +#define APPEND_NETWORK(a,b) \ + do { \ + gpointer data = create_network ((a), (b)); \ + g_assert (data); \ + mock_networks = g_list_append (mock_networks, data); \ + g_assert (mock_networks); g_assert (mock_networks->data); \ + } while (0) +#define APPEND_INTERFACE(a,b,c) \ + do { \ + gpointer data = create_interface ((a), (b), (c)); \ + g_assert (data); \ + mock_interfaces = g_list_append (mock_interfaces, data); \ + g_assert (mock_interfaces); g_assert (mock_interfaces->data); \ + } while (0) +static +void +mock_setup_net (void) +{ + mock_hostname = mock_kiku; + + APPEND_HOST ( "127.0.0.1", "localhost"); + APPEND_HOST2( "10.6.28.33", "kiku.hk.miru.hk", "kiku"); + APPEND_HOST2( "2002:dce8:d28e::33", "ip6-kiku", "kiku"); + APPEND_HOST2( "172.12.90.1", "mi-hee.ko.miru.hk", "mi-hee"); + APPEND_HOST2( "::1", "ip6-localhost", "ip6-loopback"); + APPEND_HOST ( "239.192.0.1", "PGM.MCAST.NET"); + APPEND_HOST ( "ff08::1", "IP6-PGM.MCAST.NET"); + + APPEND_NETWORK( "loopback", "127.0.0.0"); + APPEND_NETWORK( "private", "10.6.28.0"); + APPEND_NETWORK( "private2", "172.16.90.0"); + APPEND_NETWORK( "pgm-private", "239.192.0.1"); +#ifdef CONFIG_HAVE_IP6_NETWORKS + APPEND_NETWORK( "ip6-private", "2002:dce8:d28e:0:0:0"); + APPEND_NETWORK( "ip6-pgm-private","ff08::1"); +#endif + + APPEND_INTERFACE( 1, "lo", "up,loop"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast"); + APPEND_INTERFACE( 3, "eth1", "down,broadcast,multicast"); + APPEND_INTERFACE( 1, "lo", "up,loop,ip=127.0.0.1,netmask=255.0.0.0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=10.6.28.33,netmask=255.255.255.0"); + APPEND_INTERFACE( 1, "lo", "up,loop,ip=::1,netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,scope=0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=2002:dce8:d28e::33,netmask=ffff:ffff:ffff:ffff::0,scope=0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=fe80::214:5eff:febd:6dda,netmask=ffff:ffff:ffff:ffff::0,scope=2"); +} + +static +void +mock_teardown_net (void) +{ + GList* list; + + list = mock_hosts; + while (list) { + struct mock_host_t* host = list->data; + g_free (host->canonical_hostname); + if (host->alias) + g_free (host->alias); + g_slice_free1 (sizeof(struct mock_host_t), host); + list = list->next; + } + g_list_free (mock_hosts); + + list = mock_networks; + while (list) { + struct mock_network_t* network = list->data; + g_free (network->name); + g_slice_free1 (sizeof(struct mock_network_t), network); + list = list->next; + } + g_list_free (mock_networks); + + list = mock_interfaces; + while (list) { + struct mock_interface_t* interface = list->data; + g_free (interface->name); + g_slice_free1 (sizeof(struct mock_interface_t), interface); + list = list->next; + } + g_list_free (mock_interfaces); +} + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + +bool +mock_pgm_getifaddrs ( + struct pgm_ifaddrs_t** ifap, + pgm_error_t** err + ) +{ + if (NULL == ifap) { + return -1; + } + + g_debug ("mock_getifaddrs (ifap:%p err:%p)", (gpointer)ifap, (gpointer)err); + + GList* list = mock_interfaces; + int n = g_list_length (list); + struct pgm_ifaddrs_t* ifa = calloc (n, sizeof(struct pgm_ifaddrs_t)); + struct pgm_ifaddrs_t* ift = ifa; + while (list) { + struct mock_interface_t* interface = list->data; + ift->ifa_addr = (gpointer)&interface->addr; + ift->ifa_name = interface->name; + ift->ifa_flags = interface->flags; + ift->ifa_netmask = (gpointer)&interface->netmask; + list = list->next; + if (list) { + ift->ifa_next = ift + 1; + ift = ift->ifa_next; + } + } + + *ifap = ifa; + return TRUE; +} + +void +mock_pgm_freeifaddrs ( + struct pgm_ifaddrs_t* ifa + ) +{ + free (ifa); +} + +unsigned +mock_pgm_if_nametoindex ( + const sa_family_t iffamily, + const char* ifname + ) +{ + GList* list = mock_interfaces; + while (list) { + const struct mock_interface_t* interface = list->data; + if (0 == strcmp (ifname, interface->name)) + return interface->index; + list = list->next; + } + return 0; +} + +char* +mock_if_indextoname ( + unsigned ifindex, + char* ifname + ) +{ + GList* list = mock_interfaces; + while (list) { + const struct mock_interface_t* interface = list->data; + if (interface->index == ifindex) { + strcpy (ifname, interface->name); + return ifname; + } + list = list->next; + } + errno = ENXIO; + return NULL; +} + +int +mock_getnameinfo ( + const struct sockaddr* sa, + socklen_t salen, + char* host, + size_t hostlen, + char* serv, + size_t servlen, + int flags + ) +{ + if ((0 == hostlen && 0 == servlen) || + (NULL == host && NULL == serv)) + return EAI_NONAME; + + if (flags & NI_NUMERICHOST && flags & NI_NAMEREQD) + return EAI_BADFLAGS; + +/* pre-conditions */ + g_assert (NULL != host); + g_assert (hostlen > 0); + g_assert (NULL == serv); + g_assert (0 == servlen); + + const int sa_family = sa->sa_family; + + if (AF_INET == sa_family) + g_assert (sizeof(struct sockaddr_in) == salen); + else { + g_assert (AF_INET6 == sa_family); + g_assert (sizeof(struct sockaddr_in6) == salen); + } + + if (!(flags & NI_NUMERICHOST)) + { + GList* list = mock_hosts; + while (list) { + const struct mock_host_t* _host = list->data; + const int host_family = ((struct sockaddr*)&_host->address)->sa_family; + const size_t host_len = AF_INET == host_family ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); + + if (host_family == sa_family && + host_len == salen && + 0 == memcmp (sa, &_host->address, salen)) + { + if (hostlen < (1 + strlen(_host->canonical_hostname))) + return EAI_OVERFLOW; + strncpy (host, _host->canonical_hostname, hostlen); + return 0; + } + list = list->next; + } + + if (flags & NI_NAMEREQD) + return EAI_NONAME; + } + + if (AF_INET == sa_family) + pgm_inet_ntop (sa_family, &((const struct sockaddr_in*)sa)->sin_addr, host, hostlen); + else { + const unsigned scope = ((const struct sockaddr_in6*)sa)->sin6_scope_id; + pgm_inet_ntop (sa_family, &((const struct sockaddr_in6*)sa)->sin6_addr, host, hostlen); + if (scope) { + char buffer[1+IF_NAMESIZE]; + strcat (host, "%"); + strcat (host, mock_if_indextoname (scope, buffer)); + } + } + return 0; +} + +int +mock_getaddrinfo ( + const char* node, + const char* service, + const struct addrinfo* hints, + struct addrinfo** res + ) +{ + const int ai_flags = hints ? hints->ai_flags : (AI_V4MAPPED | AI_ADDRCONFIG); + const int ai_family = hints ? hints->ai_family : AF_UNSPEC; + GList* list; + struct sockaddr_storage addr; + + if (NULL == node && NULL == service) + return EAI_NONAME; + +/* pre-conditions */ + g_assert (NULL != node); + g_assert (NULL == service); + g_assert (!(ai_flags & AI_CANONNAME)); + g_assert (!(ai_flags & AI_NUMERICSERV)); + g_assert (!(ai_flags & AI_V4MAPPED)); + + g_message ("mock_getaddrinfo (node:%s%s%s service:%s%s%s hints:%p res:%p)", + node ? "\"" : "", node ? node : "(null)", node ? "\"" : "", + service ? "\"" : "", service ? service : "(null)", service ? "\"" : "", + (gpointer)hints, + (gpointer)res); + + gboolean has_ip4_config; + gboolean has_ip6_config; + + if (hints && hints->ai_flags & AI_ADDRCONFIG) + { + has_ip4_config = has_ip6_config = FALSE; + list = mock_interfaces; + while (list) { + const struct mock_interface_t* interface = list->data; + if (AF_INET == ((struct sockaddr*)&interface->addr)->sa_family) + has_ip4_config = TRUE; + else if (AF_INET6 == ((struct sockaddr*)&interface->addr)->sa_family) + has_ip6_config = TRUE; + if (has_ip4_config && has_ip6_config) + break; + list = list->next; + } + } else { + has_ip4_config = has_ip6_config = TRUE; + } + + if (ai_flags & AI_NUMERICHOST) { + pgm_sockaddr_pton (node, (struct sockaddr*)&addr); + } + list = mock_hosts; + while (list) { + struct mock_host_t* host = list->data; + const int host_family = ((struct sockaddr*)&host->address)->sa_family; + if (((strcmp (host->canonical_hostname, node) == 0) || + (host->alias && strcmp (host->alias, node) == 0) || + (ai_flags & AI_NUMERICHOST && + 0 == pgm_sockaddr_cmp ((struct sockaddr*)&addr, (struct sockaddr*)&host->address))) + && + (host_family == ai_family || AF_UNSPEC == ai_family) && + ((AF_INET == host_family && has_ip4_config) || (AF_INET6 == host_family && has_ip6_config))) + { + struct addrinfo* ai = malloc (sizeof(struct addrinfo)); + memset (ai, 0, sizeof(struct addrinfo)); + ai->ai_family = host_family; + ai->ai_addrlen = AF_INET == host_family ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); + ai->ai_addr = (gpointer)&host->address; + *res = ai; + return 0; + } + list = list->next; + } + return EAI_NONAME; +} + +void +mock_freeaddrinfo ( + struct addrinfo* res + ) +{ + free (res); +} + +int +mock_gethostname ( + char* name, + size_t len + ) +{ + if (NULL == name) { + errno = EFAULT; + return -1; + } + if (len < 0) { + errno = EINVAL; + return -1; + } + if (len < (1 + strlen (mock_hostname))) { + errno = ENAMETOOLONG; + return -1; + } +/* force an error */ + if (mock_hostname == mock_toolong) { + errno = ENAMETOOLONG; + return -1; + } + strncpy (name, mock_hostname, len); + if (len > 0) + name[len - 1] = '\0'; + return 0; +} + +struct netent* +mock_getnetbyname ( + const char* name + ) +{ + static struct netent ne; + GList* list = mock_networks; + + if (NULL == name) + return NULL; + + while (list) { + const struct mock_network_t* network = list->data; + if (strcmp (network->name, name) == 0) { + ne.n_name = network->name; + ne.n_aliases = network->aliases; + ne.n_addrtype = AF_INET; + ne.n_net = g_ntohl (((struct sockaddr_in*)&network->number)->sin_addr.s_addr); + return ≠ + } + list = list->next; + } + return NULL; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_if_getnodeaddr ( + const sa_family_t family, + struct sockaddr* addr, + const socklen_t cnt, + pgm_error_t** error + ) +{ + switch (family) { + case AF_UNSPEC: + case AF_INET: + ((struct sockaddr*)addr)->sa_family = AF_INET; + ((struct sockaddr_in*)addr)->sin_addr.s_addr = inet_addr(MOCK_ADDRESS); + break; + case AF_INET6: + ((struct sockaddr*)addr)->sa_family = AF_INET6; + ((struct sockaddr_in6*)addr)->sin6_addr = mock_address6_addr; + break; + default: + g_assert_not_reached(); + } + return TRUE; +} + +/* following tests will use AF_UNSPEC address family */ + +static +void +mock_setup_unspec (void) +{ + mock_family = AF_UNSPEC; +} + +/* following tests will use AF_INET address family */ + +static +void +mock_setup_ip4 (void) +{ + mock_family = AF_INET; +} + +/* following tests will use AF_INET6 address family */ + +static +void +mock_setup_ip6 (void) +{ + mock_family = AF_INET6; +} + + +/* return 0 if gsr multicast group does not match the default PGM group for + * the address family, return -1 on no match. + */ + +static +gboolean +match_default_group ( + const int ai_family, + const struct group_source_req* gsr + ) +{ + const struct sockaddr_in sa_default = { + .sin_family = AF_INET, + .sin_addr.s_addr = g_htonl (MOCK_GROUP) + }; + const struct sockaddr_in6 sa6_default = { + .sin6_family = AF_INET6, + .sin6_addr = MOCK_GROUP6_INIT + }; + gboolean is_match = FALSE; + + switch (ai_family) { + case AF_UNSPEC: + case AF_INET: + is_match = (0 == pgm_sockaddr_cmp ((struct sockaddr*)&gsr->gsr_group, (const struct sockaddr*)&sa_default)); + if (!is_match) { + char addr1[INET6_ADDRSTRLEN], addr2[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&gsr->gsr_group, addr1, sizeof(addr1)); + pgm_sockaddr_ntop ((struct sockaddr*)&sa_default, addr2, sizeof(addr2)); + g_message ("FALSE == cmp(%s%s%s, default-group %s%s%s)", + addr1 ? "\"" : "", addr1 ? addr1 : "(null)", addr1 ? "\"" : "", + addr2 ? "\"" : "", addr2 ? addr2 : "(null)", addr2 ? "\"" : ""); + } + break; + case AF_INET6: + is_match = (0 == pgm_sockaddr_cmp ((struct sockaddr*)&gsr->gsr_group, (const struct sockaddr*)&sa6_default)); + if (!is_match) { + char addr1[INET6_ADDRSTRLEN], addr2[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&gsr->gsr_group, addr1, sizeof(addr1)); + pgm_sockaddr_ntop ((struct sockaddr*)&sa6_default, addr2, sizeof(addr2)); + g_message ("FALSE == cmp(%s%s%s, default-group %s%s%s)", + addr1 ? "\"" : "", addr1 ? addr1 : "(null)", addr1 ? "\"" : "", + addr2 ? "\"" : "", addr2 ? addr2 : "(null)", addr2 ? "\"" : ""); + } + default: + break; + } + return is_match; +} + +/* return 0 if gsr source inteface does not match the INADDR_ANY reserved + * address, return -1 on no match. + */ + +static +int +match_default_source ( + const int ai_family, + const struct group_source_req* gsr + ) +{ + if (0 != gsr->gsr_interface) + return FALSE; + +/* ASM: source == group */ + return (0 == pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_group, (const struct sockaddr*)&gsr->gsr_source)); +} + +/* return 0 if gsr source interface does not match the hosts default interface, + * return -1 on mismatch + */ + +static +int +match_default_interface ( + const int ai_family, + const struct group_source_req* gsr + ) +{ + if (MOCK_INTERFACE_INDEX != gsr->gsr_interface) + return FALSE; + +/* ASM: source == group */ + return (0 == pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_group, (const struct sockaddr*)&gsr->gsr_source)); +} + +/* target: + * bool + * pgm_getaddrinfo ( + * const char* s, + * const struct pgm_addrinfo_t* const hints, + * struct pgm_addrinfo_t** res, + * pgm_error_t** err + * ) + */ + +struct test_case_t { + const char* ip4; + const char* ip6; +}; + +#define IP4_AND_IP6(x) x, x + +static const struct test_case_t cases_001[] = { + { IP4_AND_IP6("") }, + { IP4_AND_IP6(";") }, + { IP4_AND_IP6(";;") }, + { "239.192.0.1", "ff08::1" }, + { "239.192.0.1", "[ff08::1]" }, + { ";239.192.0.1", ";ff08::1" }, + { ";239.192.0.1", ";[ff08::1]" }, + { ";239.192.0.1;239.192.0.1", ";ff08::1;ff08::1" }, + { ";239.192.0.1;239.192.0.1", ";[ff08::1];[ff08::1]" }, + { "PGM.MCAST.NET", "IP6-PGM.MCAST.NET" }, + { ";PGM.MCAST.NET", ";IP6-PGM.MCAST.NET" }, + { ";PGM.MCAST.NET;PGM.MCAST.NET", ";IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { ";239.192.0.1;PGM.MCAST.NET", ";ff08::1;IP6-PGM.MCAST.NET" }, + { ";239.192.0.1;PGM.MCAST.NET", ";[ff08::1];IP6-PGM.MCAST.NET" }, + { ";PGM.MCAST.NET;239.192.0.1", ";IP6-PGM.MCAST.NET;ff08::1" }, + { ";PGM.MCAST.NET;239.192.0.1", ";IP6-PGM.MCAST.NET;[ff08::1]" }, + { "pgm-private", /* ‡ */ "pgm-ip6-private" }, + { ";pgm-private", /* ‡ */ ";pgm-ip6-private" }, + { ";pgm-private;pgm-private", /* ‡ */ ";pgm-ip6-private;pgm-ip6-private" }, + { ";PGM.MCAST.NET;pgm-private", /* ‡ */ ";IP6-PGM.MCAST.NET;pgm-ip6-private" }, + { ";pgm-private;PGM.MCAST.NET", /* ‡ */ ";pgm-ip6-private;IP6-PGM.MCAST.NET" }, + { ";239.192.0.1;pgm-private", /* ‡ */ ";ff08::1;pgm-ip6-private" }, + { ";239.192.0.1;pgm-private", /* ‡ */ ";[ff08::1];pgm-ip6-private" }, + { ";pgm-private;239.192.0.1", /* ‡ */ ";pgm-ip6-private;ff08::1" }, + { ";pgm-private;239.192.0.1", /* ‡ */ ";pgm-ip6-private;[ff08::1]" }, +}; + +START_TEST (test_parse_transport_pass_001) +{ + fail_unless (mock_family == AF_UNSPEC || mock_family == AF_INET || mock_family == AF_INET6, "invalid mock address family"); + + const char* s = (mock_family == AF_INET6) ? cases_001[_i].ip6 : cases_001[_i].ip4; + struct pgm_addrinfo_t hints = { + .ai_family = mock_family + }, *res = NULL; + pgm_error_t* err = NULL; + + g_message ("%i: test_parse_transport_001(%s, %s%s%s)", + _i, + (mock_family == AF_INET6) ? "AF_INET6" : ( (mock_family == AF_INET) ? "AF_INET" : "AF_UNSPEC" ), + s ? "\"" : "", s ? s : "(null)", s ? "\"" : ""); + +/* ‡ Linux does not support IPv6 /etc/networks so IPv6 entries appear as 255.255.255.255 and + * pgm_if_parse_transport will fail. + */ +#ifndef CONFIG_HAVE_IP6_NETWORKS + if (NULL != strstr (s, MOCK_NETWORK6) || NULL != strstr (s, MOCK_PGM_NETWORK6)) + { + g_message ("IPv6 exception, /etc/networks not supported on this platform."); + return; + } +#endif + + gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); + if (!retval) { + g_message ("pgm_getaddrinfo: %s", + (err && err->message) ? err->message : "(null)"); + } + fail_unless (TRUE == retval, "pgm_getaddrinfo failed"); + fail_if (NULL == res, "no result"); + fail_unless (NULL == err, "error raised"); + + fail_unless (1 == res->ai_recv_addrs_len, "not exactly one receive address"); + fail_unless (match_default_group (mock_family, &res->ai_recv_addrs[0]), "receive address not match default group"); + fail_unless (match_default_source (mock_family, &res->ai_recv_addrs[0]), "receive address not match default source"); + fail_unless (1 == res->ai_send_addrs_len, "not exactly one send address"); + fail_unless (match_default_group (mock_family, &res->ai_send_addrs[0]), "send address not match default group"); + fail_unless (match_default_source (mock_family, &res->ai_send_addrs[0]), "send address not match default source"); +} +END_TEST + +/* interface name + * + * pre-condition: interface defined to match running host + * ipv4 and ipv6 hostnames are different, otherwise "" tests might go unexpected. + */ + +static const struct test_case_t cases_002[] = { + { MOCK_INTERFACE, /* † */ MOCK_INTERFACE }, + { MOCK_INTERFACE ";", /* † */ MOCK_INTERFACE ";" }, + { MOCK_INTERFACE ";;", /* † */ MOCK_INTERFACE ";;" }, + { MOCK_INTERFACE ";239.192.0.1", /* † */ MOCK_INTERFACE ";ff08::1" }, + { MOCK_INTERFACE ";239.192.0.1", /* † */ MOCK_INTERFACE ";[ff08::1]" }, + { MOCK_INTERFACE ";239.192.0.1;239.192.0.1", /* † */ MOCK_INTERFACE ";ff08::1;ff08::1" }, + { MOCK_INTERFACE ";239.192.0.1;239.192.0.1", /* † */ MOCK_INTERFACE ";[ff08::1];[ff08::1]" }, + { MOCK_INTERFACE ";PGM.MCAST.NET", /* † */ MOCK_INTERFACE ";IP6-PGM.MCAST.NET" }, + { MOCK_INTERFACE ";PGM.MCAST.NET;PGM.MCAST.NET",/* † */ MOCK_INTERFACE ";IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { MOCK_INTERFACE ";239.192.0.1;PGM.MCAST.NET", /* † */ MOCK_INTERFACE ";ff08::1;IP6-PGM.MCAST.NET" }, + { MOCK_INTERFACE ";239.192.0.1;PGM.MCAST.NET", /* † */ MOCK_INTERFACE ";[ff08::1];IP6-PGM.MCAST.NET" }, + { MOCK_INTERFACE ";PGM.MCAST.NET;239.192.0.1", /* † */ MOCK_INTERFACE ";IP6-PGM.MCAST.NET;ff08::1" }, + { MOCK_INTERFACE ";PGM.MCAST.NET;239.192.0.1", /* † */ MOCK_INTERFACE ";IP6-PGM.MCAST.NET;[ff08::1]" }, + { MOCK_INTERFACE ";pgm-private", /* ‡ */ MOCK_INTERFACE ";pgm-ip6-private" }, + { MOCK_INTERFACE ";pgm-private;pgm-private", /* ‡ */ MOCK_INTERFACE ";pgm-ip6-private;pgm-ip6-private" }, + { MOCK_ADDRESS, MOCK_ADDRESS6 }, + { MOCK_ADDRESS, "[" MOCK_ADDRESS6 "]" }, + { MOCK_ADDRESS ";", MOCK_ADDRESS6 ";" }, + { MOCK_ADDRESS ";", "[" MOCK_ADDRESS6 "];" }, + { MOCK_ADDRESS ";;", MOCK_ADDRESS6 ";;" }, + { MOCK_ADDRESS ";;", "[" MOCK_ADDRESS6 "];;" }, + { MOCK_ADDRESS ";239.192.0.1", MOCK_ADDRESS6 ";ff08::1" }, + { MOCK_ADDRESS ";239.192.0.1", "[" MOCK_ADDRESS6 "];[ff08::1]" }, + { MOCK_ADDRESS ";239.192.0.1;239.192.0.1", MOCK_ADDRESS6 ";ff08::1;ff08::1" }, + { MOCK_ADDRESS ";239.192.0.1;239.192.0.1", "[" MOCK_ADDRESS6 "];[ff08::1];[ff08::1]" }, + { MOCK_ADDRESS ";PGM.MCAST.NET", MOCK_ADDRESS6 ";IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS ";PGM.MCAST.NET", "[" MOCK_ADDRESS6 "];IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS ";PGM.MCAST.NET;PGM.MCAST.NET", MOCK_ADDRESS6 ";IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS ";PGM.MCAST.NET;PGM.MCAST.NET", "[" MOCK_ADDRESS6 "];IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS ";239.192.0.1;PGM.MCAST.NET", MOCK_ADDRESS6 ";ff08::1;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS ";239.192.0.1;PGM.MCAST.NET", "[" MOCK_ADDRESS6 "];[ff08::1];IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS ";PGM.MCAST.NET;239.192.0.1", MOCK_ADDRESS6 ";IP6-PGM.MCAST.NET;ff08::1" }, + { MOCK_ADDRESS ";PGM.MCAST.NET;239.192.0.1", "[" MOCK_ADDRESS6 "];IP6-PGM.MCAST.NET;[ff08::1]" }, + { MOCK_ADDRESS ";pgm-private", MOCK_ADDRESS6 ";pgm-ip6-private" }, + { MOCK_ADDRESS ";pgm-private", "[" MOCK_ADDRESS6 "];pgm-ip6-private" }, + { MOCK_ADDRESS ";pgm-private;pgm-private", MOCK_ADDRESS6 ";pgm-ip6-private;pgm-ip6-private" }, + { MOCK_ADDRESS ";pgm-private;pgm-private", "[" MOCK_ADDRESS6 "];pgm-ip6-private;pgm-ip6-private" }, + { MOCK_NETWORK, /* ‡ */ MOCK_NETWORK6 }, + { MOCK_NETWORK ";", /* ‡ */ MOCK_NETWORK6 ";" }, + { MOCK_NETWORK ";;", /* ‡ */ MOCK_NETWORK6 ";;" }, + { MOCK_NETWORK ";239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";ff08::1" }, + { MOCK_NETWORK ";239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";[ff08::1]" }, + { MOCK_NETWORK ";239.192.0.1;239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";ff08::1;ff08::1" }, + { MOCK_NETWORK ";239.192.0.1;239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";[ff08::1];[ff08::1]" }, + { MOCK_NETWORK ";PGM.MCAST.NET", /* ‡ */ MOCK_NETWORK6 ";IP6-PGM.MCAST.NET" }, + { MOCK_NETWORK ";PGM.MCAST.NET;PGM.MCAST.NET", /* ‡ */ MOCK_NETWORK6 ";IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { MOCK_NETWORK ";239.192.0.1;PGM.MCAST.NET", /* ‡ */ MOCK_NETWORK6 ";ff08::1;IP6-PGM.MCAST.NET" }, + { MOCK_NETWORK ";239.192.0.1;PGM.MCAST.NET", /* ‡ */ MOCK_NETWORK6 ";[ff08::1];IP6-PGM.MCAST.NET" }, + { MOCK_NETWORK ";PGM.MCAST.NET;239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";IP6-PGM.MCAST.NET;ff08::1" }, + { MOCK_NETWORK ";PGM.MCAST.NET;239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";IP6-PGM.MCAST.NET;[ff08::1]" }, + { MOCK_NETWORK ";pgm-private", /* ‡ */ MOCK_NETWORK6 ";pgm-ip6-private" }, + { MOCK_NETWORK ";pgm-private;pgm-private", /* ‡ */ MOCK_NETWORK6 ";pgm-ip6-private;pgm-ip6-private" }, + { MOCK_HOSTNAME, MOCK_HOSTNAME6 }, + { MOCK_HOSTNAME ";", MOCK_HOSTNAME6 ";" }, + { MOCK_HOSTNAME ";;", MOCK_HOSTNAME6 ";;" }, + { MOCK_HOSTNAME ";239.192.0.1", MOCK_HOSTNAME6 ";ff08::1" }, + { MOCK_HOSTNAME ";239.192.0.1", MOCK_HOSTNAME6 ";[ff08::1]" }, + { MOCK_HOSTNAME ";239.192.0.1;239.192.0.1", MOCK_HOSTNAME6 ";ff08::1;ff08::1" }, + { MOCK_HOSTNAME ";239.192.0.1;239.192.0.1", MOCK_HOSTNAME6 ";[ff08::1];[ff08::1]" }, + { MOCK_HOSTNAME ";PGM.MCAST.NET", MOCK_HOSTNAME6 ";IP6-PGM.MCAST.NET" }, + { MOCK_HOSTNAME ";PGM.MCAST.NET;PGM.MCAST.NET", MOCK_HOSTNAME6 ";IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { MOCK_HOSTNAME ";239.192.0.1;PGM.MCAST.NET", MOCK_HOSTNAME6 ";ff08::1;IP6-PGM.MCAST.NET" }, + { MOCK_HOSTNAME ";239.192.0.1;PGM.MCAST.NET", MOCK_HOSTNAME6 ";[ff08::1];IP6-PGM.MCAST.NET" }, + { MOCK_HOSTNAME ";PGM.MCAST.NET;239.192.0.1", MOCK_HOSTNAME6 ";IP6-PGM.MCAST.NET;ff08::1" }, + { MOCK_HOSTNAME ";PGM.MCAST.NET;239.192.0.1", MOCK_HOSTNAME6 ";IP6-PGM.MCAST.NET;[ff08::1]" }, + { MOCK_HOSTNAME ";pgm-private", MOCK_HOSTNAME6 ";pgm-ip6-private" }, + { MOCK_HOSTNAME ";pgm-private;pgm-private", MOCK_HOSTNAME6 ";pgm-ip6-private;pgm-ip6-private" }, +}; + +START_TEST (test_parse_transport_pass_002) +{ + fail_unless (mock_family == AF_UNSPEC || mock_family == AF_INET || mock_family == AF_INET6, "invalid mock address family"); + + const char* s = (mock_family == AF_INET6) ? cases_002[_i].ip6 : cases_002[_i].ip4; + struct pgm_addrinfo_t hints = { + .ai_family = mock_family + }, *res = NULL; + pgm_error_t* err = NULL; + + g_message ("%i: test_parse_transport_002(%s, %s%s%s)", + _i, + (mock_family == AF_INET6) ? "AF_INET6" : ( (mock_family == AF_INET) ? "AF_INET" : "AF_UNSPEC" ), + s ? "\"" : "", s ? s : "(null)", s ? "\"" : ""); + +/* ‡ Linux does not support IPv6 /etc/networks so IPv6 entries appear as 255.255.255.255 and + * pgm_if_parse_transport will fail. + */ +#ifndef CONFIG_HAVE_IP6_NETWORKS + if (NULL != strstr (s, MOCK_NETWORK6) || NULL != strstr (s, MOCK_PGM_NETWORK6)) + { + g_message ("IPv6 exception, /etc/networks not supported on this platform."); + return; + } +#endif + +/* † Multiple scoped IPv6 interfaces match a simple interface name network parameter and so + * pgm-if_parse_transport will fail finding multiple matching interfaces + */ + if (AF_INET6 == mock_family && 0 == strncmp (s, MOCK_INTERFACE, strlen (MOCK_INTERFACE))) + { + g_message ("IPv6 exception, multiple scoped addresses on one interface"); + fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); + fail_if (NULL == err, "error not raised"); + fail_unless (PGM_ERROR_NOTUNIQ == err->code, "interfaces not found unique"); + return; + } + + fail_unless (TRUE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (1 == res->ai_recv_addrs_len, "not exactly one receive address"); + fail_unless (match_default_group (mock_family, &res->ai_recv_addrs[0]), "receive address not match default group"); + fail_unless (match_default_interface (mock_family, &res->ai_recv_addrs[0]), "receive address not match default interface"); + fail_unless (1 == res->ai_send_addrs_len, "not exactly one send address"); + fail_unless (match_default_group (mock_family, &res->ai_send_addrs[0]), "send address not match default group"); + fail_unless (match_default_interface (mock_family, &res->ai_send_addrs[0]), "send address not match default interface"); +} +END_TEST + +/* network to node address in bits, 8-32 + * + * e.g. 127.0.0.1/16 + */ + +static const struct test_case_t cases_003[] = { + { MOCK_ADDRESS "/24", MOCK_ADDRESS6 "/64" }, + { MOCK_ADDRESS "/24;", MOCK_ADDRESS6 "/64;" }, + { MOCK_ADDRESS "/24;;", MOCK_ADDRESS6 "/64;;" }, + { MOCK_ADDRESS "/24;239.192.0.1", MOCK_ADDRESS6 "/64;ff08::1" }, + { MOCK_ADDRESS "/24;239.192.0.1", MOCK_ADDRESS6 "/64;[ff08::1]" }, + { MOCK_ADDRESS "/24;239.192.0.1;239.192.0.1", MOCK_ADDRESS6 "/64;ff08::1;ff08::1" }, + { MOCK_ADDRESS "/24;239.192.0.1;239.192.0.1", MOCK_ADDRESS6 "/64;[ff08::1];[ff08::1]" }, + { MOCK_ADDRESS "/24;PGM.MCAST.NET", MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS "/24;PGM.MCAST.NET;PGM.MCAST.NET",MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS "/24;239.192.0.1;PGM.MCAST.NET", MOCK_ADDRESS6 "/64;ff08::1;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS "/24;239.192.0.1;PGM.MCAST.NET", MOCK_ADDRESS6 "/64;[ff08::1];IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS "/24;PGM.MCAST.NET;239.192.0.1", MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET;ff08::1" }, + { MOCK_ADDRESS "/24;PGM.MCAST.NET;239.192.0.1", MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET;[ff08::1]" }, + { MOCK_ADDRESS "/24;PGM.MCAST.NET", MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS "/24;PGM.MCAST.NET;PGM.MCAST.NET",MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS "/24;pgm-private", /* ‡ */ MOCK_ADDRESS6 "/64;pgm-ip6-private" }, + { MOCK_ADDRESS "/24;pgm-private;pgm-private", /* ‡ */ MOCK_ADDRESS6 "/64;pgm-ip6-private;pgm-ip6-private" }, + { MOCK_ADDRESS "/24;239.192.0.1;pgm-private", /* ‡ */ MOCK_ADDRESS6 "/64;ff08::1;pgm-ip6-private" }, + { MOCK_ADDRESS "/24;239.192.0.1;pgm-private", /* ‡ */ MOCK_ADDRESS6 "/64;[ff08::1];pgm-ip6-private" }, + { MOCK_ADDRESS "/24;pgm-private;239.192.0.1", /* ‡ */ MOCK_ADDRESS6 "/64;pgm-ip6-private;ff08::1" }, + { MOCK_ADDRESS "/24;pgm-private;239.192.0.1", /* ‡ */ MOCK_ADDRESS6 "/64;pgm-ip6-private;[ff08::1]" }, + { MOCK_ADDRESS "/24;PGM.MCAST.NET;pgm-private", /* ‡ */ MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET;pgm-ip6-private" }, + { MOCK_ADDRESS "/24;pgm-private;PGM.MCAST.NET", /* ‡ */ MOCK_ADDRESS6 "/64;pgm-ip6-private;IP6-PGM.MCAST.NET" }, +}; + +START_TEST (test_parse_transport_pass_003) +{ + fail_unless (mock_family == AF_UNSPEC || mock_family == AF_INET || mock_family == AF_INET6, "invalid mock address family"); + + const char* s = (mock_family == AF_INET6) ? cases_003[_i].ip6 : cases_003[_i].ip4; + struct pgm_addrinfo_t hints = { + .ai_family = mock_family + }, *res = NULL; + pgm_error_t* err = NULL; + + g_message ("%i: test_parse_transport_003(%s, %s%s%s)", + _i, + (mock_family == AF_INET6) ? "AF_INET6" : ( (mock_family == AF_INET) ? "AF_INET" : "AF_UNSPEC" ), + s ? "\"" : "", s ? s : "(null)", s ? "\"" : ""); + +/* ‡ Linux does not support IPv6 /etc/networks so IPv6 entries appear as 255.255.255.255 and + * pgm_if_parse_transport will fail. + */ +#ifndef CONFIG_HAVE_IP6_NETWORKS + if (NULL != strstr (s, MOCK_NETWORK6) || NULL != strstr (s, MOCK_PGM_NETWORK6)) + { + g_message ("IPv6 exception, /etc/networks not supported on this platform."); + return; + } +#endif + + gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); + if (!retval) { + g_message ("pgm_getaddrinfo: %s", + (err && err->message) ? err->message : "(null)"); + } + fail_unless (TRUE == retval, "pgm_getaddrinfo failed"); + fail_unless (1 == res->ai_recv_addrs_len, "not exactly one receive address"); + fail_unless (match_default_group (mock_family, &res->ai_recv_addrs[0]), "receive address not match default group"); + fail_unless (match_default_interface (mock_family, &res->ai_recv_addrs[0]), "receive address not match default interface"); + fail_unless (1 == res->ai_send_addrs_len, "not exactly one send address"); + fail_unless (match_default_group (mock_family, &res->ai_send_addrs[0]), "send address not match default group"); + fail_unless (match_default_interface (mock_family, &res->ai_send_addrs[0]), "send address not match default interface"); +} +END_TEST + +/* asymmetric groups + */ + +START_TEST (test_parse_transport_pass_004) +{ + fail_unless (mock_family == AF_UNSPEC || mock_family == AF_INET || mock_family == AF_INET6, "invalid mock address family"); + + const char* s = (mock_family == AF_INET6) ? ";ff08::1;ff08::2" + /* AF_INET */: ";239.192.56.1;239.192.56.2"; + struct pgm_addrinfo_t hints = { + .ai_family = mock_family + }, *res = NULL; + pgm_error_t* err = NULL; + struct sockaddr_storage addr; + + fail_unless (TRUE == pgm_getaddrinfo (s, &hints, &res, &err), "get_transport_info failed"); + fail_unless (1 == res->ai_recv_addrs_len, "not exactly one receive address"); + fail_unless (1 == res->ai_send_addrs_len, "not exactly one send address"); + if (mock_family == AF_INET6) + { + inet_pton (AF_INET6, "ff08::1", &((struct sockaddr_in6*)&addr)->sin6_addr); + ((struct sockaddr*)&addr)->sa_family = mock_family; + ((struct sockaddr_in6*)&addr)->sin6_port = 0; + ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; + ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + inet_pton (AF_INET6, "ff08::2", &((struct sockaddr_in6*)&addr)->sin6_addr); + ((struct sockaddr*)&addr)->sa_family = mock_family; + ((struct sockaddr_in6*)&addr)->sin6_port = 0; + ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; + ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_send_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + } else { + inet_pton (AF_INET, "239.192.56.1", &((struct sockaddr_in*)&addr)->sin_addr); + ((struct sockaddr*)&addr)->sa_family = AF_INET; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + inet_pton (AF_INET, "239.192.56.2", &((struct sockaddr_in*)&addr)->sin_addr); + ((struct sockaddr*)&addr)->sa_family = AF_INET; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_send_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + } + fail_unless (match_default_source (mock_family, &res->ai_recv_addrs[0]), "source not match"); + fail_unless (match_default_source (mock_family, &res->ai_send_addrs[0]), "source not match"); +} +END_TEST + +/* multiple receive groups and asymmetric sending + */ + +START_TEST (test_parse_transport_pass_005) +{ + fail_unless (mock_family == AF_UNSPEC || mock_family == AF_INET || mock_family == AF_INET6, "invalid mock address family"); + + const char* s = (mock_family == AF_INET6) ? ";ff08::1,ff08::2;ff08::3" + /* AF_INET */: ";239.192.56.1,239.192.56.2;239.192.56.3"; + struct pgm_addrinfo_t hints = { + .ai_family = mock_family + }, *res = NULL; + pgm_error_t* err = NULL; + struct sockaddr_storage addr; + + fail_unless (TRUE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (2 == res->ai_recv_addrs_len, "not exactly one receive address"); + fail_unless (1 == res->ai_send_addrs_len, "not exactly one send address"); + if (mock_family == AF_INET6) + { + inet_pton (AF_INET6, "ff08::1", &((struct sockaddr_in6*)&addr)->sin6_addr); + ((struct sockaddr*)&addr)->sa_family = mock_family; + ((struct sockaddr_in6*)&addr)->sin6_port = 0; + ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; + ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + inet_pton (AF_INET6, "ff08::2", &((struct sockaddr_in6*)&addr)->sin6_addr); + ((struct sockaddr*)&addr)->sa_family = mock_family; + ((struct sockaddr_in6*)&addr)->sin6_port = 0; + ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; + ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[1].gsr_group, (struct sockaddr*)&addr), "group not match"); + inet_pton (AF_INET6, "ff08::3", &((struct sockaddr_in6*)&addr)->sin6_addr); + ((struct sockaddr*)&addr)->sa_family = mock_family; + ((struct sockaddr_in6*)&addr)->sin6_port = 0; + ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; + ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_send_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + } else { + inet_pton (AF_INET, "239.192.56.1", &((struct sockaddr_in*)&addr)->sin_addr); + ((struct sockaddr*)&addr)->sa_family = AF_INET; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + inet_pton (AF_INET, "239.192.56.2", &((struct sockaddr_in*)&addr)->sin_addr); + ((struct sockaddr*)&addr)->sa_family = AF_INET; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[1].gsr_group, (struct sockaddr*)&addr), "group not match"); + inet_pton (AF_INET, "239.192.56.3", &((struct sockaddr_in*)&addr)->sin_addr); + ((struct sockaddr*)&addr)->sa_family = AF_INET; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_send_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + } + fail_unless (match_default_source (mock_family, &res->ai_recv_addrs[0]), "source not match"); + fail_unless (match_default_source (mock_family, &res->ai_send_addrs[0]), "source not match"); +} +END_TEST + + +/* too many interfaces + */ +START_TEST (test_parse_transport_fail_001) +{ + const char* s = "eth0,lo;;;"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* invalid characters, or simply just bogus + */ +START_TEST (test_parse_transport_fail_002) +{ + const char* s = "!@#$%^&*()"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* too many groups + */ +START_TEST (test_parse_transport_fail_003) +{ + const char* s = ";239.192.0.1,239.192.0.2,239.192.0.3,239.192.0.4,239.192.0.5,239.192.0.6,239.192.0.7,239.192.0.8,239.192.0.9,239.192.0.10,239.192.0.11,239.192.0.12,239.192.0.13,239.192.0.14,239.192.0.15,239.192.0.16,239.192.0.17,239.192.0.18,239.192.0.19,239.192.0.20;239.192.0.21"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* too many receiver groups in asymmetric pairing + */ +START_TEST (test_parse_transport_fail_004) +{ + const char* s = ";239.192.0.1,239.192.0.2,239.192.0.3,239.192.0.4,239.192.0.5,239.192.0.6,239.192.0.7,239.192.0.8,239.192.0.9,239.192.0.10,239.192.0.11,239.192.0.12,239.192.0.13,239.192.0.14,239.192.0.15,239.192.0.16,239.192.0.17,239.192.0.18,239.192.0.19,239.192.0.20,239.192.0.21;239.192.0.22"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* null string + */ +START_TEST (test_parse_transport_fail_005) +{ + const char* s = NULL; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* invalid address family + */ +START_TEST (test_parse_transport_fail_006) +{ + const char* s = ";"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_IPX + }, *res = NULL; + pgm_error_t* err = NULL; + + fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* invalid transport info pointer + */ +START_TEST (test_parse_transport_fail_007) +{ + const char* s = ";"; + pgm_error_t* err = NULL; + + fail_unless (FALSE == pgm_getaddrinfo (s, NULL, NULL, &err), "pgm_getaddrinfo failed"); +} +END_TEST + +/* invalid interface + */ +START_TEST (test_parse_transport_fail_008) +{ + const char* s = "qe0;"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); + if (!retval) { + g_message ("pgm_getaddrinfo: %s", err ? err->message : "(null)"); + } + fail_unless (FALSE == retval, "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* non-existing interface IP address + */ +START_TEST (test_parse_transport_fail_009) +{ + const char* s = "172.16.90.1;"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); + if (!retval) { + g_message ("pgm_getaddrinfo: %s", + (err && err->message) ? err->message : "(null)"); + } + fail_unless (FALSE == retval, "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* non-existing network name address + */ +START_TEST (test_parse_transport_fail_010) +{ + const char* s = "private2;"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); + if (!retval) { + g_message ("pgm_getaddrinfo: %s", + (err && err->message) ? err->message : "(null)"); + } + fail_unless (FALSE == retval, "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* non-existing host name interface + */ +START_TEST (test_parse_transport_fail_011) +{ + const char* s = "mi-hee.ko.miru.hk;"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); + if (!retval) { + g_message ("pgm_getaddrinfo: %s", + (err && err->message) ? err->message : "(null)"); + } + fail_unless (FALSE == retval, "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* target: + * pgm_if_print_all (void) + */ + +START_TEST (test_print_all_pass_001) +{ + pgm_if_print_all (); +} +END_TEST + + +/* target: + * bool + * is_in_net ( + * const struct in_addr* addr, -- in host byte order + * const struct in_addr* netaddr, + * const struct in_addr* netmask + * ) + */ + +struct test_case_net_t { + const char* addr; + const char* netaddr; + const char* netmask; + const gboolean answer; +}; + +static const struct test_case_net_t cases_004[] = { + { "127.0.0.1", "127.0.0.1", "255.0.0.0", TRUE }, + { "127.0.0.1", "127.0.0.1", "255.255.0.0", TRUE }, + { "127.0.0.1", "127.0.0.1", "255.255.255.0", TRUE }, + { "127.0.0.1", "127.0.0.1", "255.255.255.255", TRUE }, + { "127.0.0.1", "127.0.0.0", "255.0.0.0", TRUE }, + { "127.0.0.1", "127.0.0.0", "255.255.0.0", TRUE }, + { "127.0.0.1", "127.0.0.0", "255.255.255.0", TRUE }, + { "127.0.0.1", "127.0.0.0", "255.255.255.255", FALSE }, + { "172.15.1.1", "172.16.0.0", "255.240.0.0", FALSE }, + { "172.16.1.1", "172.16.0.0", "255.240.0.0", TRUE }, + { "172.18.1.1", "172.16.0.0", "255.240.0.0", TRUE }, + { "172.31.1.1", "172.16.0.0", "255.240.0.0", TRUE }, + { "172.32.1.1", "172.16.0.0", "255.240.0.0", FALSE }, +}; + +START_TEST (test_is_in_net_pass_001) +{ + struct in_addr addr, netaddr, netmask; + fail_unless (pgm_inet_pton (AF_INET, cases_004[_i].addr, &addr)); + fail_unless (pgm_inet_pton (AF_INET, cases_004[_i].netaddr, &netaddr)); + fail_unless (pgm_inet_pton (AF_INET, cases_004[_i].netmask, &netmask)); + const gboolean answer = cases_004[_i].answer; + + addr.s_addr = g_ntohl (addr.s_addr); + netaddr.s_addr = g_ntohl (netaddr.s_addr); + netmask.s_addr = g_ntohl (netmask.s_addr); + gboolean result = is_in_net (&addr, &netaddr, &netmask); + + g_message ("result %s (%s)", + result ? "TRUE" : "FALSE", + answer ? "TRUE" : "FALSE"); + + fail_unless (answer == result); +} +END_TEST + +static const struct test_case_net_t cases_005[] = { + { "::1", "::1", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", TRUE }, + { "fe80::203:baff:fe4e:6cc8", "fe80::", "ffff:0000:0000:0000:0000:0000:0000:0000", TRUE }, + { "2002:dec8:d28e::36", "2002:dec8:d28e::", "ffff:ffff:ffff:0000:0000:0000:0000:0000", TRUE }, + { "2002:dec8:d28e::36", "2002:dafa:939:0::", "ffff:ffff:ffff:ffff:0000:0000:0000:0000", FALSE }, +}; + +START_TEST (test_is_in_net6_pass_001) +{ + struct in6_addr addr, netaddr, netmask; + fail_unless (pgm_inet_pton (AF_INET6, cases_005[_i].addr, &addr)); + fail_unless (pgm_inet_pton (AF_INET6, cases_005[_i].netaddr, &netaddr)); + fail_unless (pgm_inet_pton (AF_INET6, cases_005[_i].netmask, &netmask)); + const gboolean answer = cases_005[_i].answer; + + gboolean result = is_in_net6 (&addr, &netaddr, &netmask); + + g_message ("result %s (%s)", + result ? "TRUE" : "FALSE", + answer ? "TRUE" : "FALSE"); + + fail_unless (answer == result); +} +END_TEST + + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_is_in_net = tcase_create ("is_in_net"); + suite_add_tcase (s, tc_is_in_net); + tcase_add_checked_fixture (tc_is_in_net, mock_setup_net, mock_teardown_net); + tcase_add_checked_fixture (tc_is_in_net, mock_setup_unspec, NULL); + tcase_add_loop_test (tc_is_in_net, test_is_in_net_pass_001, 0, G_N_ELEMENTS(cases_004)); + + TCase* tc_is_in_net6 = tcase_create ("is_in_net6"); + suite_add_tcase (s, tc_is_in_net6); + tcase_add_checked_fixture (tc_is_in_net6, mock_setup_net, mock_teardown_net); + tcase_add_checked_fixture (tc_is_in_net6, mock_setup_unspec, NULL); + tcase_add_loop_test (tc_is_in_net6, test_is_in_net6_pass_001, 0, G_N_ELEMENTS(cases_005)); + +/* three variations of all parse-transport tests, one for each valid + * address family value: AF_UNSPEC, AF_INET, AF_INET6. + */ + +/* unspecified address family, ai_family == AF_UNSPEC */ + TCase* tc_parse_transport_unspec = tcase_create ("parse_transport/unspec"); + suite_add_tcase (s, tc_parse_transport_unspec); + tcase_add_checked_fixture (tc_parse_transport_unspec, mock_setup_net, mock_teardown_net); + tcase_add_checked_fixture (tc_parse_transport_unspec, mock_setup_unspec, NULL); + tcase_add_loop_test (tc_parse_transport_unspec, test_parse_transport_pass_001, 0, G_N_ELEMENTS(cases_001)); + tcase_add_loop_test (tc_parse_transport_unspec, test_parse_transport_pass_002, 0, G_N_ELEMENTS(cases_002)); + tcase_add_loop_test (tc_parse_transport_unspec, test_parse_transport_pass_003, 0, G_N_ELEMENTS(cases_003)); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_pass_004); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_pass_005); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_001); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_002); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_003); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_004); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_005); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_006); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_007); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_008); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_009); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_010); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_011); + +/* IP version 4, ai_family = AF_INET */ + TCase* tc_parse_transport_ip4 = tcase_create ("parse_transport/af_inet"); + suite_add_tcase (s, tc_parse_transport_ip4); + tcase_add_checked_fixture (tc_parse_transport_ip4, mock_setup_net, mock_teardown_net); + tcase_add_checked_fixture (tc_parse_transport_ip4, mock_setup_ip4, NULL); + tcase_add_loop_test (tc_parse_transport_ip4, test_parse_transport_pass_001, 0, G_N_ELEMENTS(cases_001)); + tcase_add_loop_test (tc_parse_transport_ip4, test_parse_transport_pass_002, 0, G_N_ELEMENTS(cases_002)); + tcase_add_loop_test (tc_parse_transport_ip4, test_parse_transport_pass_003, 0, G_N_ELEMENTS(cases_003)); + tcase_add_test (tc_parse_transport_ip4, test_parse_transport_pass_004); + tcase_add_test (tc_parse_transport_ip4, test_parse_transport_pass_005); + +/* IP version 6, ai_family = AF_INET6 */ + TCase* tc_parse_transport_ip6 = tcase_create ("parse_transport/af_inet6"); + suite_add_tcase (s, tc_parse_transport_ip6); + tcase_add_checked_fixture (tc_parse_transport_ip6, mock_setup_net, mock_teardown_net); + tcase_add_checked_fixture (tc_parse_transport_ip6, mock_setup_ip6, NULL); + tcase_add_loop_test (tc_parse_transport_ip6, test_parse_transport_pass_001, 0, G_N_ELEMENTS(cases_001)); + tcase_add_loop_test (tc_parse_transport_ip6, test_parse_transport_pass_002, 0, G_N_ELEMENTS(cases_002)); + tcase_add_loop_test (tc_parse_transport_ip6, test_parse_transport_pass_003, 0, G_N_ELEMENTS(cases_003)); + tcase_add_test (tc_parse_transport_ip6, test_parse_transport_pass_004); + tcase_add_test (tc_parse_transport_ip6, test_parse_transport_pass_005); + + TCase* tc_print_all = tcase_create ("print-all"); + tcase_add_checked_fixture (tc_print_all, mock_setup_net, mock_teardown_net); + suite_add_tcase (s, tc_print_all); + tcase_add_test (tc_print_all, test_print_all_pass_001); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/checksum.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/checksum.h new file mode 100644 index 0000000..67f71a6 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/checksum.h @@ -0,0 +1,75 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM checksum routines + * + * Copyright (c) 2006-2008 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_CHECKSUM_H__ +#define __PGM_IMPL_CHECKSUM_H__ + +#include + +PGM_BEGIN_DECLS + +uint16_t pgm_inet_checksum (const void*, uint16_t, uint16_t); +uint16_t pgm_csum_fold (uint32_t) PGM_GNUC_CONST; +uint32_t pgm_csum_block_add (uint32_t, uint32_t, const uint16_t) PGM_GNUC_CONST; +uint32_t pgm_compat_csum_partial (const void*, uint16_t, uint32_t); +uint32_t pgm_compat_csum_partial_copy (const void*restrict, void*restrict, uint16_t, uint32_t); + +static inline uint32_t add32_with_carry (uint32_t, uint32_t) PGM_GNUC_CONST; + +#if defined(__x86_64__) || defined(__i386__) || defined(__i386) || defined(__amd64) +static inline uint32_t add32_with_carry (uint32_t a, uint32_t b) +{ + asm("addl %2, %0 \n\t" + "adcl $0, %0" + : "=r" (a) /* output operands */ + : "0" (a), "r" (b)); /* input operands */ + return a; +} +#elif defined(__sparc__) || defined(__sparc) || defined(__sparcv9) +static inline uint32_t add32_with_carry (uint32_t a, uint32_t b) +{ + asm("addcc %2, %0, %0 \n\t" + "addx %0, %%g0, %0" + : "=r" (a) /* output operands */ + : "0" (a), "r" (b) /* input operands */ + : "cc"); /* list of clobbered registers */ + return a; +} +#else +static inline uint32_t add32_with_carry (uint32_t a, uint32_t b) +{ + a += b; + a = (a >> 16) + (a & 0xffff); + return a; +} +#endif + +# define pgm_csum_partial pgm_compat_csum_partial +# define pgm_csum_partial_copy pgm_compat_csum_partial_copy + +PGM_END_DECLS + +#endif /* __PGM_IMPL_CHECKSUM_H__ */ + diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/engine.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/engine.h new file mode 100644 index 0000000..eeacbe7 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/engine.h @@ -0,0 +1,43 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM engine. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_ENGINE_H__ +#define __PGM_IMPL_ENGINE_H__ + +#ifdef _WIN32 +# include +# include +#endif +#include + +PGM_BEGIN_DECLS + +#ifdef _WIN32 +extern LPFN_WSARECVMSG pgm_WSARecvMsg; +#endif + +#ifdef PGM_DEBUG +extern unsigned pgm_loss_rate; +#endif + +PGM_END_DECLS + +#endif /* __PGM_IMPL_ENGINE_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/features.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/features.h new file mode 100644 index 0000000..a8dadf7 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/features.h @@ -0,0 +1,42 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Compiler feature flags. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_FEATURES_H__ +#define __PGM_IMPL_FEATURES_H__ + +#if defined(_POSIX_C_SOURCE) || defined(__POSIX_VISIBLE) +# if (_POSIX_C_SOURCE - 0) >= 200112L || (__POSIX_VISIBLE - 0) >= 200112L +# define CONFIG_HAVE_FTIME 1 +# define CONFIG_HAVE_GETTIMEOFDAY 1 +# endif +# if (_POSIX_C_SOURCE - 0) >= 199309L || (__POSIX_VISIBLE - 0) >= 199309L +# define CONFIG_HAVE_CLOCK_GETTIME 1 +# endif +#endif +#if defined(_WIN32) +# define CONFIG_HAVE_FTIME +#endif + +#endif /* __PGM_IMPL_FEATURES_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/fixed.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/fixed.h new file mode 100644 index 0000000..f84ef33 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/fixed.h @@ -0,0 +1,140 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * 8-bit and 16-bit shift fixed point math + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_FIXED_H__ +#define __PGM_IMPL_FIXED_H__ + +#include + +PGM_BEGIN_DECLS + +static inline uint_fast32_t pgm_fp8 (unsigned) PGM_GNUC_CONST; +static inline uint_fast32_t pgm_fp16 (unsigned) PGM_GNUC_CONST; +static inline unsigned pgm_fp8tou (uint_fast32_t) PGM_GNUC_CONST; +static inline unsigned pgm_fp16tou (uint_fast32_t) PGM_GNUC_CONST; +static inline uint_fast32_t pgm_fp8mul (uint_fast32_t, uint_fast32_t) PGM_GNUC_CONST; +static inline uint_fast32_t pgm_fp16mul (uint_fast32_t, uint_fast32_t) PGM_GNUC_CONST; +static inline uint_fast32_t pgm_fp8div (uint_fast32_t, uint_fast32_t) PGM_GNUC_CONST; +static inline uint_fast32_t pgm_fp16div (uint_fast32_t, uint_fast32_t) PGM_GNUC_CONST; +static inline uint_fast32_t pgm_fp16pow (uint_fast32_t, uint_fast32_t) PGM_GNUC_CONST; + +static inline +uint_fast32_t +pgm_fp8 ( + unsigned v + ) +{ + return (uint32_t)(v << 8); +} + +static inline +uint_fast32_t +pgm_fp16 ( + unsigned v + ) +{ + return (uint_fast32_t)(v << 16); +} + +static inline +unsigned +pgm_fp8tou ( + uint_fast32_t f + ) +{ + return (f + (1 << 7)) >> 8; +} + +static inline +unsigned +pgm_fp16tou ( + uint_fast32_t f + ) +{ + return (f + (1 << 15)) >> 16; +} + +static inline +uint_fast32_t +pgm_fp8mul ( + uint_fast32_t a, + uint_fast32_t b + ) +{ + return ( a * b + 128 ) >> 8; +} + +static inline +uint_fast32_t +pgm_fp16mul ( + uint_fast32_t a, + uint_fast32_t b + ) +{ + return ( a * b + 32768 ) >> 16; +} + +static inline +uint_fast32_t +pgm_fp8div ( + uint_fast32_t a, + uint_fast32_t b + ) +{ + return ( ( (a << 9) / b ) + 1 ) / 2; +} + +static inline +uint_fast32_t +pgm_fp16div ( + uint_fast32_t a, + uint_fast32_t b + ) +{ + return ( ( (a << 17) / b ) + 1 ) / 2; +} + +static inline +uint_fast32_t +pgm_fp16pow ( + uint_fast32_t x, + uint_fast32_t y + ) +{ + uint_fast32_t result = pgm_fp16 (1); + for (uint_fast32_t i = x; + y; + y >>= 1) + { + if (y & 1) + result = (result * i + 32768) >> 16; + i = (i * i + 32768) >> 16; + } + return result; +} + +PGM_END_DECLS + +#endif /* __PGM_IMPL_FIXED_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/framework.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/framework.h new file mode 100644 index 0000000..951ad26 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/framework.h @@ -0,0 +1,76 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Framework collection. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_FRAMEWORK_H__ +#define __PGM_IMPL_FRAMEWORK_H__ + +#define __PGM_IMPL_FRAMEWORK_H_INSIDE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef __PGM_IMPL_FRAMEWORK_H_INSIDE__ + +#endif /* __PGM_IMPL_FRAMEWORK_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/galois.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/galois.h new file mode 100644 index 0000000..2f008ed --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/galois.h @@ -0,0 +1,138 @@ +/* + * Galois field maths. + * + * Copyright (c) 2006-2008 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_GALOIS_H__ +#define __PGM_IMPL_GALOIS_H__ + +#include + +PGM_BEGIN_DECLS + +/* 8 bit wide galois field integer: GF(2⁸) */ +typedef uint8_t __attribute__((__may_alias__)) pgm_gf8_t; + +/* E denotes the encoding symbol length in bytes. + * S denotes the symbol size in units of m-bit elements. When m = 8, + * then S and E are equal. + */ +#define PGM_GF_ELEMENT_BYTES sizeof(pgm_gf8_t) + +/* m defines the length of the elements in the finite field, in bits. + * m belongs to {2..16}. + */ +#define PGM_GF_ELEMENT_BITS ( 8 * PGM_GF_ELEMENT_BYTES ) + +/* q defines the number of elements in the finite field. + */ +#define PGM_GF_NO_ELEMENTS ( 1 << PGM_GF_ELEMENT_BITS ) +#define PGM_GF_MAX ( PGM_GF_NO_ELEMENTS - 1 ) + + +extern const pgm_gf8_t pgm_gflog[PGM_GF_NO_ELEMENTS]; +extern const pgm_gf8_t pgm_gfantilog[PGM_GF_NO_ELEMENTS]; + +#ifdef CONFIG_GALOIS_MUL_LUT +extern const pgm_gf8_t pgm_gftable[PGM_GF_NO_ELEMENTS * PGM_GF_NO_ELEMENTS]; +#endif + +/* In a finite field with characteristic 2, addition and subtraction are + * identical, and are accomplished using the XOR operator. + */ +static inline +pgm_gf8_t +pgm_gfadd ( + pgm_gf8_t a, + pgm_gf8_t b + ) +{ + return a ^ b; +} + +static inline +pgm_gf8_t +pgm_gfadd_equals ( + pgm_gf8_t a, + pgm_gf8_t b + ) +{ + return a ^= b; +} + +static inline +pgm_gf8_t +pgm_gfsub ( + pgm_gf8_t a, + pgm_gf8_t b + ) +{ + return pgm_gfadd (a, b); +} + +static inline +pgm_gf8_t +pgm_gfsub_equals ( + pgm_gf8_t a, + pgm_gf8_t b + ) +{ + return pgm_gfadd_equals (a, b); +} + +static inline +pgm_gf8_t +pgm_gfmul ( + pgm_gf8_t a, + pgm_gf8_t b + ) +{ + if (PGM_UNLIKELY( !(a && b) )) { + return 0; + } + +#ifdef CONFIG_GALOIS_MUL_LUT + return pgm_gftable[ (uint16_t)a << 8 | (uint16_t)b ]; +#else + unsigned sum = pgm_gflog[ a ] + pgm_gflog[ b ]; + return sum >= PGM_GF_MAX ? pgm_gfantilog[ sum - PGM_GF_MAX ] : pgm_gfantilog[ sum ]; +#endif +} + +static inline +pgm_gf8_t +pgm_gfdiv ( + pgm_gf8_t a, + pgm_gf8_t b + ) +{ + if (PGM_UNLIKELY( !a )) { + return 0; + } + + int sum = pgm_gflog[ a ] - pgm_gflog[ b ]; + return sum < 0 ? pgm_gfantilog[ sum + PGM_GF_MAX ] : pgm_gfantilog[ sum ]; +} + +PGM_END_DECLS + +#endif /* __PGM_IMPL_GALOIS_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/getifaddrs.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/getifaddrs.h new file mode 100644 index 0000000..4732a21 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/getifaddrs.h @@ -0,0 +1,73 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable getifaddrs + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_GETIFADDRS_H__ +#define __PGM_IMPL_GETIFADDRS_H__ + +#ifndef _WIN32 +# include +# include +# include +#else +# include +#endif + +struct pgm_ifaddrs_t; + +#include +#include + +PGM_BEGIN_DECLS + +#ifndef IF_NAMESIZE +# ifdef IFNAMSIZ +# define IF_NAMESIZE IFNAMSIZ +# elif defined(_WIN32) +# define IF_NAMESIZE 40 +# else +# define IF_NAMESIZE 16 +# endif +#endif + +struct pgm_ifaddrs_t +{ + struct pgm_ifaddrs_t* ifa_next; /* Pointer to the next structure. */ + + char* ifa_name; /* Name of this network interface. */ + unsigned int ifa_flags; /* Flags as from SIOCGIFFLAGS ioctl. */ + +#ifdef ifa_addr +# undef ifa_addr +#endif + struct sockaddr* ifa_addr; /* Network address of this interface. */ + struct sockaddr* ifa_netmask; /* Netmask of this interface. */ +}; + +bool pgm_getifaddrs (struct pgm_ifaddrs_t**restrict, pgm_error_t**restrict); +void pgm_freeifaddrs (struct pgm_ifaddrs_t*); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_GETIFADDRS_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/getnodeaddr.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/getnodeaddr.h new file mode 100644 index 0000000..befcf35 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/getnodeaddr.h @@ -0,0 +1,41 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable function to return node IP address. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_GETNODEADDR_H__ +#define __PGM_IMPL_GETNODEADDR_H__ + +#ifndef _WIN32 +# include +#endif +#include +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL bool pgm_if_getnodeaddr (const sa_family_t, struct sockaddr*restrict, const socklen_t, pgm_error_t**restrict); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_GETNODEADDR_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/hashtable.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/hashtable.h new file mode 100644 index 0000000..4271cb6 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/hashtable.h @@ -0,0 +1,58 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable hash table. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_HASHTABLE_H__ +#define __PGM_IMPL_HASHTABLE_H__ + +#include + +PGM_BEGIN_DECLS + +typedef struct pgm_hashtable_t pgm_hashtable_t; +typedef uint_fast32_t pgm_hash_t; + +typedef pgm_hash_t (*pgm_hashfunc_t) (const void*); +typedef bool (*pgm_equalfunc_t) (const void*restrict, const void*restrict); + +PGM_GNUC_INTERNAL pgm_hashtable_t* pgm_hashtable_new (pgm_hashfunc_t, pgm_equalfunc_t); +PGM_GNUC_INTERNAL void pgm_hashtable_destroy (pgm_hashtable_t*); +PGM_GNUC_INTERNAL void pgm_hashtable_insert (pgm_hashtable_t*restrict, const void*restrict, void*restrict); +PGM_GNUC_INTERNAL bool pgm_hashtable_remove (pgm_hashtable_t*restrict, const void*restrict); +PGM_GNUC_INTERNAL void pgm_hashtable_remove_all (pgm_hashtable_t*); +PGM_GNUC_INTERNAL void* pgm_hashtable_lookup (const pgm_hashtable_t*restrict, const void*restrict); +PGM_GNUC_INTERNAL void* pgm_hashtable_lookup_extended (const pgm_hashtable_t*restrict, const void*restrict, void*restrict); +PGM_GNUC_INTERNAL void pgm_hashtable_unref (pgm_hashtable_t*); + +/* Hash Functions + */ + +PGM_GNUC_INTERNAL bool pgm_str_equal (const void*restrict, const void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL pgm_hash_t pgm_str_hash (const void*) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_int_equal (const void*restrict, const void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL pgm_hash_t pgm_int_hash (const void*) PGM_GNUC_WARN_UNUSED_RESULT; + +PGM_END_DECLS + +#endif /* __PGM_IMPL_HASHTABLE_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/histogram.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/histogram.h new file mode 100644 index 0000000..92ddeb9 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/histogram.h @@ -0,0 +1,129 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * histograms. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_HISTOGRAM_H__ +#define __PGM_IMPL_HISTOGRAM_H__ + +#include +#include +#include +#include + +PGM_BEGIN_DECLS + +typedef int pgm_sample_t; +typedef int pgm_count_t; + +struct pgm_sample_set_t { + pgm_count_t* counts; + unsigned counts_len; + int64_t sum; + int64_t square_sum; +}; + +typedef struct pgm_sample_set_t pgm_sample_set_t; + +struct pgm_histogram_t { + const char* restrict histogram_name; + unsigned bucket_count; + pgm_sample_t declared_min; + pgm_sample_t declared_max; + pgm_sample_t* restrict ranges; + pgm_sample_set_t sample; + bool is_registered; + pgm_slist_t histograms_link; +}; + +typedef struct pgm_histogram_t pgm_histogram_t; + +#define PGM_HISTOGRAM_DEFINE(name, minimum, maximum, count) \ + static pgm_count_t counts[ (count) ]; \ + static pgm_sample_t ranges[ (count) + 1 ]; \ + static pgm_histogram_t counter = { \ + .histogram_name = (name), \ + .bucket_count = (count), \ + .declared_min = (minimum), \ + .declared_max = (maximum), \ + .ranges = ranges, \ + .sample = { \ + .counts = counts, \ + .counts_len = (count), \ + .sum = 0, \ + .square_sum = 0 \ + }, \ + .is_registered = FALSE \ + } + +#ifdef CONFIG_HISTOGRAMS + +# define PGM_HISTOGRAM_TIMES(name, sample) do { \ + PGM_HISTOGRAM_DEFINE(name, pgm_msecs(1), pgm_secs(10), 50); \ + if (!counter.is_registered) { \ + memset (counts, 0, sizeof(counts)); \ + memset (ranges, 0, sizeof(ranges)); \ + pgm_histogram_init (&counter); \ + } \ + pgm_histogram_add_time (&counter, sample); \ + } while (0) + +# define PGM_HISTOGRAM_COUNTS(name, sample) do { \ + PGM_HISTOGRAM_DEFINE(name, 1, 1000000, 50); \ + if (!counter.is_registered) { \ + memset (counts, 0, sizeof(counts)); \ + memset (ranges, 0, sizeof(ranges)); \ + pgm_histogram_init (&counter); \ + } \ + pgm_histogram_add (&counter, (sample)); \ + } while (0) + +#else /* !CONFIG_HISTOGRAMS */ + +# define PGM_HISTOGRAM_TIMES(name, sample) +# define PGM_HISTOGRAM_COUNTS(name, sample) + +#endif /* !CONFIG_HISTOGRAMS */ + + +extern pgm_slist_t* pgm_histograms; + +void pgm_histogram_init (pgm_histogram_t*); +void pgm_histogram_add (pgm_histogram_t*, int); +void pgm_histogram_write_html_graph_all (pgm_string_t*); + +static inline +void +pgm_histogram_add_time ( + pgm_histogram_t*const histogram, + pgm_time_t sample_time + ) +{ + pgm_histogram_add (histogram, (int)pgm_to_msecs (sample_time)); +} + +PGM_END_DECLS + +#endif /* __PGM_IMPL_HISTOGRAM_H__ */ + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/i18n.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/i18n.h new file mode 100644 index 0000000..c97f3de --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/i18n.h @@ -0,0 +1,32 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * i18n & l10n support + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_I18N_H__ +#define __PGM_IMPL_I18N_H__ + +#ifdef CONFIG_HAVE_GETTEXT +# include +# define _(String) dgettext (GETTEXT_PACKAGE, String) +#else +# define _(String) (String) +#endif + +#endif /* __PGM_IMPL_I18N_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/indextoaddr.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/indextoaddr.h new file mode 100644 index 0000000..6c6599e --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/indextoaddr.h @@ -0,0 +1,41 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable interface index to socket address function. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_INDEXTOADDR_H__ +#define __PGM_IMPL_INDEXTOADDR_H__ + +#ifndef _WIN32 +# include +#endif +#include +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL bool pgm_if_indextoaddr (unsigned, sa_family_t, uint32_t, struct sockaddr*restrict, pgm_error_t**restrict); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_INDEXTOADDR_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/indextoname.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/indextoname.h new file mode 100644 index 0000000..d5d7964 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/indextoname.h @@ -0,0 +1,37 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Windows interface index to interface name function. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_INDEXTONAME_H__ +#define __PGM_IMPL_INDEXTONAME_H__ + +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL char* pgm_if_indextoname (unsigned, char*); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_INDEXTONAME_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/inet_network.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/inet_network.h new file mode 100644 index 0000000..64c43fb --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/inet_network.h @@ -0,0 +1,41 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable implementations of inet_network and inet_network6. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_INET_NETWORK_H__ +#define __PGM_IMPL_INET_NETWORK_H__ + +#ifndef _WIN32 +# include +#endif +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL int pgm_inet_network (const char*restrict, struct in_addr*restrict); +PGM_GNUC_INTERNAL int pgm_inet6_network (const char*restrict, struct in6_addr*restrict); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_INET_NETWORK_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/ip.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/ip.h new file mode 100644 index 0000000..8f18775 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/ip.h @@ -0,0 +1,150 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Internet header for protocol version 4, RFC 791. + * + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_IP_H__ +#define __PGM_IMPL_IP_H__ + +#ifndef _WIN32 +# include +#endif +#include +#include + +PGM_BEGIN_DECLS + +/* Byte alignment for packet memory maps. + * NB: Solaris and OpenSolaris don't support #pragma pack(push) even on x86. + */ +#if defined( __GNUC__ ) && !defined( sun ) +# pragma pack(push) +#endif +#pragma pack(1) + +/* RFC 791 */ + +/* nb: first four bytes are forced bitfields for win32 "feature" */ +struct pgm_ip +{ +#if (defined( sun ) && defined( _BIT_FIELDS_LTOH )) || (!defined( sun ) && __BYTE_ORDER == __LITTLE_ENDIAN) + unsigned ip_hl:4; /* header length */ + unsigned ip_v:4; /* version */ +#else + unsigned ip_v:4; /* version */ + unsigned ip_hl:4; /* header length */ +#endif + unsigned ip_tos:8; /* type of service */ + unsigned ip_len:16; /* total length */ + uint16_t ip_id; /* identification */ + uint16_t ip_off; /* fragment offset field */ + uint8_t ip_ttl; /* time to live */ + uint8_t ip_p; /* protocol */ + uint16_t ip_sum; /* checksum */ + struct in_addr ip_src, ip_dst; /* source and dest address */ +}; + +PGM_STATIC_ASSERT(sizeof(struct pgm_ip) == 20); + +/* RFC 2460 */ +#ifdef ip6_vfc +# undef ip6_vfc +#endif +#ifdef ip6_plen +# undef ip6_plen +#endif +#ifdef ip6_nxt +# undef ip6_nxt +#endif +#ifdef ip6_hops +# undef ip6_hops +#endif +struct pgm_ip6_hdr +{ + uint32_t ip6_vfc; /* version:4, traffic class:8, flow label:20 */ + uint16_t ip6_plen; /* payload length: packet length - 40 */ + uint8_t ip6_nxt; /* next header type */ + uint8_t ip6_hops; /* hop limit */ + struct in6_addr ip6_src, ip6_dst; /* source and dest address */ +}; + +PGM_STATIC_ASSERT(sizeof(struct pgm_ip6_hdr) == 40); + +#define PGM_IPOPT_EOL 0 /* end of option list */ +#define PGM_IPOPT_NOP 1 /* no operation */ +#define PGM_IPOPT_RR 7 /* record packet route */ +#define PGM_IPOPT_TS 68 /* timestamp */ +#define PGM_IPOPT_SECURITY 130 /* provide s, c, h, tcc */ +#define PGM_IPOPT_LSRR 131 /* loose source route */ +#define PGM_IPOPT_ESO 133 +#define PGM_IPOPT_CIPSO 134 +#define PGM_IPOPT_SATID 136 /* satnet id */ +#define PGM_IPOPT_SSRR 137 /* strict source route */ +#define PGM_IPOPT_RA 148 /* router alert */ + +/* RFC 768 */ +struct pgm_udphdr +{ + uint16_t uh_sport; /* source port */ + uint16_t uh_dport; /* destination port */ + uint16_t uh_ulen; /* udp length */ + uint16_t uh_sum; /* udp checksum */ +}; + +PGM_STATIC_ASSERT(sizeof(struct pgm_udphdr) == 8); + +#if defined( __GNUC__ ) && !defined( sun ) +# pragma pack(pop) +#else +# pragma pack() +#endif + +PGM_END_DECLS + +#endif /* __PGM_IMPL_IP_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/list.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/list.h new file mode 100644 index 0000000..91a3ed3 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/list.h @@ -0,0 +1,43 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable doubly-linked list. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_LIST_H__ +#define __PGM_IMPL_LIST_H__ + +#include +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL pgm_list_t* pgm_list_append (pgm_list_t*restrict, void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL pgm_list_t* pgm_list_prepend_link (pgm_list_t*restrict, pgm_list_t*restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL pgm_list_t* pgm_list_remove_link (pgm_list_t*, pgm_list_t*) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL pgm_list_t* pgm_list_delete_link (pgm_list_t*, pgm_list_t*) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL pgm_list_t* pgm_list_last (pgm_list_t*) PGM_GNUC_PURE PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL unsigned pgm_list_length (pgm_list_t*) PGM_GNUC_PURE PGM_GNUC_WARN_UNUSED_RESULT; + +PGM_END_DECLS + +#endif /* __PGM_IMPL_LIST_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/math.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/math.h new file mode 100644 index 0000000..8c7c2ba --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/math.h @@ -0,0 +1,75 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Shared math routines. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_MATH_H__ +#define __PGM_IMPL_MATH_H__ + +#include + +PGM_BEGIN_DECLS + +/* fast log base 2 of power of 2 + */ + +static inline unsigned pgm_power2_log2 (unsigned) PGM_GNUC_CONST; + +static inline +unsigned +pgm_power2_log2 ( + unsigned v + ) +{ + static const unsigned int b[] = { 0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0, 0xFF00FF00, 0xFFFF0000 }; + unsigned int r = (v & b[0]) != 0; + for (unsigned i = 4; i > 0; i--) { + r |= ((v & b[i]) != 0) << i; + } + return r; +} + +/* nearest power of 2 + */ + +static inline size_t pgm_nearest_power (size_t, size_t) PGM_GNUC_CONST; + +static inline +size_t +pgm_nearest_power ( + size_t b, + size_t v + ) +{ + if (v > (SIZE_MAX/2)) + return SIZE_MAX; + while (b < v) + b <<= 1; + return b; +} + +unsigned pgm_spaced_primes_closest (unsigned) PGM_GNUC_PURE; + +PGM_END_DECLS + +#endif /* __PGM_IMPL_MATH_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/md5.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/md5.h new file mode 100644 index 0000000..b28ab7d --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/md5.h @@ -0,0 +1,61 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * MD5 hashing algorithm. + * + * MD5 original source GNU C Library: + * Includes functions to compute MD5 message digest of files or memory blocks + * according to the definition of MD5 in RFC 1321 from April 1992. + * + * Copyright (C) 1995, 1996, 2001, 2003 Free Software Foundation, Inc. + * + * This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_MD5_H__ +#define __PGM_IMPL_MD5_H__ + +struct pgm_md5_t; + +#include + +PGM_BEGIN_DECLS + +struct pgm_md5_t +{ + uint32_t A; + uint32_t B; + uint32_t C; + uint32_t D; + + uint32_t total[2]; + uint32_t buflen; + char buffer[128] +#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) + __attribute__ ((__aligned__ (__alignof__ (uint32_t)))) +#endif + ; +}; + +PGM_GNUC_INTERNAL void pgm_md5_init_ctx (struct pgm_md5_t*); +PGM_GNUC_INTERNAL void pgm_md5_process_bytes (struct pgm_md5_t*restrict, const void*restrict, size_t); +PGM_GNUC_INTERNAL void* pgm_md5_finish_ctx (struct pgm_md5_t*, void*); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_MD5_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/mem.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/mem.h new file mode 100644 index 0000000..9377ced --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/mem.h @@ -0,0 +1,34 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable fail fast memory allocation. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_MEM_H__ +#define __PGM_IMPL_MEM_H__ + +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL void pgm_mem_init (void); +PGM_GNUC_INTERNAL void pgm_mem_shutdown (void); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_MEM_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/messages.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/messages.h new file mode 100644 index 0000000..e7065b0 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/messages.h @@ -0,0 +1,352 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * basic message reporting. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_MESSAGES_H__ +#define __PGM_IMPL_MESSAGES_H__ + +#include +#include +#include +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL void pgm__log (const int, const char*, ...) PGM_GNUC_PRINTF (2, 3); +PGM_GNUC_INTERNAL void pgm__logv (const int, const char*, va_list) PGM_GNUC_PRINTF (2, 0); + +#ifdef CONFIG_HAVE_ISO_VARARGS + +/* debug trace level only valid in debug mode */ +# ifdef PGM_DEBUG +# define pgm_debug(...) \ + do { \ + if (pgm_min_log_level == PGM_LOG_LEVEL_DEBUG) \ + pgm__log (PGM_LOG_LEVEL_DEBUG, __VA_ARGS__); \ + } while (0) +# else +# define pgm_debug(...) while (0) +# endif /* !PGM_DEBUG */ + +# define pgm_trace(r,...) \ + do { \ + if (pgm_min_log_level <= PGM_LOG_LEVEL_TRACE && pgm_log_mask & (r)) \ + pgm__log (PGM_LOG_LEVEL_TRACE, __VA_ARGS__); \ + } while (0) +# define pgm_minor(...) \ + do { \ + if (pgm_min_log_level <= PGM_LOG_LEVEL_MINOR) \ + pgm__log (PGM_LOG_LEVEL_MINOR, __VA_ARGS__); \ + } while (0) +# define pgm_info(...) \ + do { \ + if (pgm_min_log_level <= PGM_LOG_LEVEL_NORMAL) \ + pgm__log (PGM_LOG_LEVEL_NORMAL, __VA_ARGS__); \ + } while (0) +# define pgm_warn(...) \ + do { \ + if (pgm_min_log_level <= PGM_LOG_LEVEL_WARNING) \ + pgm__log (PGM_LOG_LEVEL_WARNING, __VA_ARGS__); \ + } while (0) +# define pgm_error(...) \ + do { \ + if (pgm_min_log_level <= PGM_LOG_LEVEL_ERROR) \ + pgm__log (PGM_LOG_LEVEL_ERROR, __VA_ARGS__); \ + } while (0) +# define pgm_fatal(...) \ + do { \ + pgm__log (PGM_LOG_LEVEL_FATAL, __VA_ARGS__); \ + } while (0) + +#elif defined(CONFIG_HAVE_GNUC_VARARGS) + +# ifdef PGM_DEBUG +# define pgm_debug(f...) \ + do { \ + if (pgm_min_log_level == PGM_LOG_LEVEL_DEBUG) \ + pgm__log (PGM_LOG_LEVEL_DEBUG, f); \ + } while (0) +# else +# define pgm_debug(f...) while (0) +# endif /* !PGM_DEBUG */ + +# define pgm_trace(r,f...) if (pgm_min_log_level <= PGM_LOG_LEVEL_TRACE && pgm_log_mask & (r)) \ + pgm__log (PGM_LOG_LEVEL_TRACE, f) +# define pgm_minor(f...) if (pgm_min_log_level <= PGM_LOG_LEVEL_MINOR) pgm__log (PGM_LOG_LEVEL_MINOR, f) +# define pgm_info(f...) if (pgm_min_log_level <= PGM_LOG_LEVEL_NORMAL) pgm__log (PGM_LOG_LEVEL_NORMAL, f) +# define pgm_warn(f...) if (pgm_min_log_level <= PGM_LOG_LEVEL_WARNING) pgm__log (PGM_LOG_LEVEL_WARNING, f) +# define pgm_error(f...) if (pgm_min_log_level <= PGM_LOG_LEVEL_ERROR) pgm__log (PGM_LOG_LEVEL_ERROR, f) +# define pgm_fatal(f...) pgm__log (PGM_LOG_LEVEL_FATAL, f) + +#else /* no varargs macros */ + +/* declare for GCC attributes */ +static inline void pgm_debug (const char*, ...) PGM_GNUC_PRINTF (1, 2); +static inline void pgm_trace (const int, const char*, ...) PGM_GNUC_PRINTF (2, 3); +static inline void pgm_minor (const char*, ...) PGM_GNUC_PRINTF (1, 2); +static inline void pgm_info (const char*, ...) PGM_GNUC_PRINTF (1, 2); +static inline void pgm_warn (const char*, ...) PGM_GNUC_PRINTF (1, 2); +static inline void pgm_error (const char*, ...) PGM_GNUC_PRINTF (1, 2); +static inline void pgm_fatal (const char*, ...) PGM_GNUC_PRINTF (1, 2); + +static inline void pgm_debug (const char* format, ...) { + if (PGM_LOG_LEVEL_DEBUG == pgm_min_log_level) { + va_list args; + va_start (args, format); + pgm__logv (PGM_LOG_LEVEL_DEBUG, format, args); + va_end (args); + } +} + +static inline void pgm_trace (const int role, const char* format, ...) { + if (PGM_LOG_LEVEL_TRACE >= pgm_min_log_level && pgm_log_mask & role) { + va_list args; + va_start (args, format); + pgm__logv (PGM_LOG_LEVEL_TRACE, format, args); + va_end (args); + } +} + +static inline void pgm_minor (const char* format, ...) { + if (PGM_LOG_LEVEL_MINOR >= pgm_min_log_level) { + va_list args; + va_start (args, format); + pgm__logv (PGM_LOG_LEVEL_MINOR, format, args); + va_end (args); + } +} + +static inline void pgm_info (const char* format, ...) { + if (PGM_LOG_LEVEL_NORMAL >= pgm_min_log_level) { + va_list args; + va_start (args, format); + pgm__logv (PGM_LOG_LEVEL_NORMAL, format, args); + va_end (args); + } +} + +static inline void pgm_warn (const char* format, ...) { + if (PGM_LOG_LEVEL_WARNING >= pgm_min_log_level) { + va_list args; + va_start (args, format); + pgm__logv (PGM_LOG_LEVEL_WARNING, format, args); + va_end (args); + } +} + +static inline void pgm_error (const char* format, ...) { + if (PGM_LOG_LEVEL_ERROR >= pgm_min_log_level) { + va_list args; + va_start (args, format); + pgm__logv (PGM_LOG_LEVEL_WARNING, format, args); + va_end (args); + } +} + +static inline void pgm_fatal (const char* format, ...) { + va_list args; + va_start (args, format); + pgm__logv (PGM_LOG_LEVEL_FATAL, format, args); + va_end (args); +} + +#endif /* varargs */ + +#define pgm_warn_if_reached() \ + do { \ + pgm_warn ("file %s: line %d (%s): code should not be reached", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__); \ + } while (0) +#define pgm_warn_if_fail(expr) \ + do { \ + if (PGM_LIKELY (expr)); \ + else \ + pgm_warn ("file %s: line %d (%s): runtime check failed: (%s)", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, #expr); \ + } while (0) + + +#ifdef PGM_DISABLE_ASSERT + +# define pgm_assert(expr) while (0) +# define pgm_assert_not_reached() while (0) +# define pgm_assert_cmpint(n1, cmp, n2) while (0) +# define pgm_assert_cmpuint(n1, cmp, n2) while (0) + +#elif defined(__GNUC__) + +# define pgm_assert(expr) \ + do { \ + if (PGM_LIKELY(expr)); \ + else { \ + pgm_fatal ("file %s: line %d (%s): assertion failed: (%s)", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, #expr); \ + abort (); \ + } \ + } while (0) +# define pgm_assert_not_reached() \ + do { \ + pgm_fatal ("file %s: line %d (%s): should not be reached", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__); \ + abort (); \ + } while (0) +# define pgm_assert_cmpint(n1, cmp, n2) \ + do { \ + const int _n1 = (n1), _n2 = (n2); \ + if (PGM_LIKELY(_n1 cmp _n2)); \ + else { \ + pgm_fatal ("file %s: line %d (%s): assertion failed (%s): (%u %s %u)", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, #n1 " " #cmp " " #n2, _n1, #cmp, _n2); \ + abort (); \ + } \ + } while (0) +# define pgm_assert_cmpuint(n1, cmp, n2) \ + do { \ + const unsigned _n1 = (n1), _n2 = (n2); \ + if (PGM_LIKELY(_n1 cmp _n2)); \ + else { \ + pgm_fatal ("file %s: line %d (%s): assertion failed (%s): (%u %s %u)", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, #n1 " " #cmp " " #n2, _n1, #cmp, _n2); \ + abort (); \ + } \ + } while (0) + +#else + +# define pgm_assert(expr) \ + do { \ + if (PGM_LIKELY(expr)); \ + else { \ + pgm_fatal ("file %s: line %d: assertion failed: (%s)", \ + __FILE__, __LINE__, #expr); \ + abort (); \ + } \ + } while (0) +# define pgm_assert_not_reached() \ + do { \ + pgm_fatal ("file %s: line %d: assertion failed: (%s)", \ + __FILE__, __LINE__); \ + abort (); \ + } while (0) +# define pgm_assert_cmpint(n1, cmp, n2) \ + do { \ + const int _n1 = (n1), _n2 = (n2); \ + if (PGM_LIKELY(_n1 cmp _n2)); \ + else { \ + pgm_fatal ("file %s: line %d: assertion failed (%s): (%u %s %u)", \ + __FILE__, __LINE__, #n1 " " #cmp " " #n2, _n1, #cmp, _n2); \ + abort (); \ + } \ + } while (0) +# define pgm_assert_cmpuint(n1, cmp, n2) \ + do { \ + const unsigned _n1 = (n1), _n2 = (n2); \ + if (PGM_LIKELY(_n1 cmp _n2)); \ + else { \ + pgm_fatal ("file %s: line %d: assertion failed (%s): (%u %s %u)", \ + __FILE__, __LINE__, #n1 " " #cmp " " #n2, _n1, #cmp, _n2); \ + abort (); \ + } \ + } while (0) + +#endif /* !PGM_DISABLE_ASSERT */ + +#ifdef PGM_DISABLE_CHECKS + +# define pgm_return_if_fail(expr) while (0) +# define pgm_return_val_if_fail(expr, val) while (0) +# define pgm_return_if_reached() return +# define pgm_return_val_if_reached(val) return (val) + +#elif defined(__GNUC__) + +# define pgm_return_if_fail(expr) \ + do { \ + if (PGM_LIKELY(expr)); \ + else { \ + pgm_warn ("file %s: line %d (%s): assertion `%s' failed", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, #expr); \ + return; \ + } \ + } while (0) +# define pgm_return_val_if_fail(expr, val) \ + do { \ + if (PGM_LIKELY(expr)); \ + else { \ + pgm_warn ("file %s: line %d (%s): assertion `%s' failed", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, #expr); \ + return (val); \ + } \ + } while (0) +# define pgm_return_if_reached() \ + do { \ + pgm_warn ("file %s: line %d (%s): should not be reached", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__); \ + return; \ + } while (0) +# define pgm_return_val_if_reached(val) \ + do { \ + pgm_warn ("file %s: line %d (%s): should not be reached", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__); \ + return (val); \ + } while (0) + +#else + +# define pgm_return_if_fail(expr) \ + do { \ + if (PGM_LIKELY(expr)); \ + else { \ + pgm_warn ("file %s: line %d: assertion `%s' failed", \ + __FILE__, __LINE__, #expr); \ + return; \ + } \ + } while (0) +# define pgm_return_val_if_fail(expr, val) \ + do { \ + if (PGM_LIKELY(expr)); \ + else { \ + pgm_warn ("file %s: line %d: assertion `%s' failed", \ + __FILE__, __LINE__, #expr); \ + return (val); \ + } \ + } while (0) +# define pgm_return_if_reached() \ + do { \ + pgm_warn ("file %s: line %d): should not be reached", \ + __FILE__, __LINE__); \ + return; \ + } while (0) +# define pgm_return_val_if_reached(val) \ + do { \ + pgm_warn ("file %s: line %d: should not be reached", \ + __FILE__, __LINE__); \ + return (val); \ + } while (0) + +#endif /* !PGM_DISABLE_CHECKS */ + +PGM_END_DECLS + +#endif /* __PGM_IMPL_MESSAGES_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/nametoindex.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/nametoindex.h new file mode 100644 index 0000000..a25bd89 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/nametoindex.h @@ -0,0 +1,40 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Windows interface name to interface index function. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_NAMETOINDEX_H__ +#define __PGM_IMPL_NAMETOINDEX_H__ + +#ifndef _WIN32 +# include +#endif +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL unsigned pgm_if_nametoindex (const sa_family_t, const char*); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_NAMETOINDEX_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/net.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/net.h new file mode 100644 index 0000000..aa9beaf --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/net.h @@ -0,0 +1,38 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * network send wrapper. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_NET_H__ +#define __PGM_IMPL_NET_H__ + +#ifndef _WIN32 +# include +#endif +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL ssize_t pgm_sendto (pgm_sock_t*restrict, bool, bool, const void*restrict, size_t, const struct sockaddr*restrict, socklen_t); +PGM_GNUC_INTERNAL int pgm_set_nonblocking (int fd[2]); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_NET_H__ */ + diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/notify.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/notify.h new file mode 100644 index 0000000..1db4475 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/notify.h @@ -0,0 +1,298 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Low kernel overhead event notify mechanism, or standard pipes. + * + * Copyright (c) 2008 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_NOTIFY_H__ +#define __PGM_IMPL_NOTIFY_H__ + +typedef struct pgm_notify_t pgm_notify_t; + +#ifndef _WIN32 +# include +# include +# ifdef CONFIG_HAVE_EVENTFD +# include +# endif +#else /* _WIN32 */ +# include +# include +#endif +#include +#include + +PGM_BEGIN_DECLS + +struct pgm_notify_t { +#if defined(CONFIG_HAVE_EVENTFD) + int eventfd; +#elif !defined(_WIN32) + int pipefd[2]; +#else + SOCKET s[2]; +#endif /* _WIN32 */ +}; + +#if defined(CONFIG_HAVE_EVENTFD) +# define PGM_NOTIFY_INIT { -1 } +#elif !defined(_WIN32) +# define PGM_NOTIFY_INIT { { -1, -1 } } +#else +# define PGM_NOTIFY_INIT { { INVALID_SOCKET, INVALID_SOCKET } } +#endif + + +static inline +bool +pgm_notify_is_valid ( + pgm_notify_t* notify + ) +{ + if (PGM_UNLIKELY(NULL == notify)) + return FALSE; +#if defined(CONFIG_HAVE_EVENTFD) + if (PGM_UNLIKELY(-1 == notify->eventfd)) + return FALSE; +#elif !defined(_WIN32) + if (PGM_UNLIKELY(-1 == notify->pipefd[0] || -1 == notify->pipefd[1])) + return FALSE; +#else + if (PGM_UNLIKELY(INVALID_SOCKET == notify->s[0] || INVALID_SOCKET == notify->s[1])) + return FALSE; +#endif /* _WIN32 */ + return TRUE; +} + +static inline +int +pgm_notify_init ( + pgm_notify_t* notify + ) +{ + pgm_assert (NULL != notify); + +#if defined(CONFIG_HAVE_EVENTFD) + notify->eventfd = -1; + int retval = eventfd (0, 0); + if (-1 == retval) + return retval; + notify->eventfd = retval; + const int fd_flags = fcntl (notify->eventfd, F_GETFL); + if (-1 != fd_flags) + retval = fcntl (notify->eventfd, F_SETFL, fd_flags | O_NONBLOCK); + return 0; +#elif !defined(_WIN32) + notify->pipefd[0] = notify->pipefd[1] = -1; + int retval = pipe (notify->pipefd); + pgm_assert (0 == retval); +/* set non-blocking */ +/* write-end */ + int fd_flags = fcntl (notify->pipefd[1], F_GETFL); + if (fd_flags != -1) + retval = fcntl (notify->pipefd[1], F_SETFL, fd_flags | O_NONBLOCK); + pgm_assert (notify->pipefd[1]); +/* read-end */ + fd_flags = fcntl (notify->pipefd[0], F_GETFL); + if (fd_flags != -1) + retval = fcntl (notify->pipefd[0], F_SETFL, fd_flags | O_NONBLOCK); + pgm_assert (notify->pipefd[0]); + return retval; +#else +/* use loopback sockets to simulate a pipe suitable for win32/select() */ + struct sockaddr_in addr; + SOCKET listener; + int addrlen = sizeof (addr); + + notify->s[0] = notify->s[1] = INVALID_SOCKET; + + listener = socket (AF_INET, SOCK_STREAM, 0); + pgm_assert (listener != INVALID_SOCKET); + + memset (&addr, 0, sizeof (addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr ("127.0.0.1"); + pgm_assert (addr.sin_addr.s_addr != INADDR_NONE); + + int rc = bind (listener, (const struct sockaddr*)&addr, sizeof (addr)); + pgm_assert (rc != SOCKET_ERROR); + + rc = getsockname (listener, (struct sockaddr*)&addr, &addrlen); + pgm_assert (rc != SOCKET_ERROR); + +// Listen for incoming connections. + rc = listen (listener, 1); + pgm_assert (rc != SOCKET_ERROR); + +// Create the socket. + notify->s[1] = WSASocket (AF_INET, SOCK_STREAM, 0, NULL, 0, 0); + pgm_assert (notify->s[1] != INVALID_SOCKET); + +// Connect to the remote peer. + rc = connect (notify->s[1], (struct sockaddr*)&addr, addrlen); + pgm_assert (rc != SOCKET_ERROR); + +// Accept connection. + notify->s[0] = accept (listener, NULL, NULL); + pgm_assert (notify->s[0] != INVALID_SOCKET); + +// Set read-end to non-blocking mode + const unsigned long one = 1; + rc = ioctlsocket (notify->s[0], FIONBIO, &one); + pgm_assert (rc != SOCKET_ERROR); + +// We don't need the listening socket anymore. Close it. + rc = closesocket (listener); + pgm_assert (rc != SOCKET_ERROR); + + return 0; +#endif +} + +static inline +int +pgm_notify_destroy ( + pgm_notify_t* notify + ) +{ + pgm_assert (NULL != notify); + +#if defined(CONFIG_HAVE_EVENTFD) + if (-1 != notify->eventfd) { + close (notify->eventfd); + notify->eventfd = -1; + } +#elif !defined(_WIN32) + if (-1 != notify->pipefd[0]) { + close (notify->pipefd[0]); + notify->pipefd[0] = -1; + } + if (-1 != notify->pipefd[1]) { + close (notify->pipefd[1]); + notify->pipefd[1] = -1; + } +#else + if (INVALID_SOCKET != notify->s[0]) { + closesocket (notify->s[0]); + notify->s[0] = INVALID_SOCKET; + } + if (INVALID_SOCKET != notify->s[1]) { + closesocket (notify->s[1]); + notify->s[1] = INVALID_SOCKET; + } +#endif + return 0; +} + +static inline +int +pgm_notify_send ( + pgm_notify_t* notify + ) +{ + pgm_assert (NULL != notify); + +#if defined(CONFIG_HAVE_EVENTFD) + pgm_assert (-1 != notify->eventfd); + uint64_t u = 1; + ssize_t s = write (notify->eventfd, &u, sizeof(u)); + return (s == sizeof(u)); +#elif !defined(_WIN32) + pgm_assert (-1 != notify->pipefd[1]); + const char one = '1'; + return (1 == write (notify->pipefd[1], &one, sizeof(one))); +#else + pgm_assert (INVALID_SOCKET != notify->s[1]); + const char one = '1'; + return (1 == send (notify->s[1], &one, sizeof(one), 0)); +#endif +} + +static inline +int +pgm_notify_read ( + pgm_notify_t* notify + ) +{ + pgm_assert (NULL != notify); + +#if defined(CONFIG_HAVE_EVENTFD) + pgm_assert (-1 != notify->eventfd); + uint64_t u; + return (sizeof(u) == read (notify->eventfd, &u, sizeof(u))); +#elif !defined(_WIN32) + pgm_assert (-1 != notify->pipefd[0]); + char buf; + return (sizeof(buf) == read (notify->pipefd[0], &buf, sizeof(buf))); +#else + pgm_assert (INVALID_SOCKET != notify->s[0]); + char buf; + return (sizeof(buf) == recv (notify->s[0], &buf, sizeof(buf), 0)); +#endif +} + +static inline +void +pgm_notify_clear ( + pgm_notify_t* notify + ) +{ + pgm_assert (NULL != notify); + +#if defined(CONFIG_HAVE_EVENTFD) + pgm_assert (-1 != notify->eventfd); + uint64_t u; + while (sizeof(u) == read (notify->eventfd, &u, sizeof(u))); +#elif !defined(_WIN32) + pgm_assert (-1 != notify->pipefd[0]); + char buf; + while (sizeof(buf) == read (notify->pipefd[0], &buf, sizeof(buf))); +#else + pgm_assert (INVALID_SOCKET != notify->s[0]); + char buf; + while (sizeof(buf) == recv (notify->s[0], &buf, sizeof(buf), 0)); +#endif +} + +static inline +int +pgm_notify_get_fd ( + pgm_notify_t* notify + ) +{ + pgm_assert (NULL != notify); + +#if defined(CONFIG_HAVE_EVENTFD) + pgm_assert (-1 != notify->eventfd); + return notify->eventfd; +#elif !defined(_WIN32) + pgm_assert (-1 != notify->pipefd[0]); + return notify->pipefd[0]; +#else + pgm_assert (INVALID_SOCKET != notify->s[0]); + return notify->s[0]; +#endif +} + +PGM_END_DECLS + +#endif /* __PGM_IMPL_NOTIFY_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/packet_parse.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/packet_parse.h new file mode 100644 index 0000000..4416e21 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/packet_parse.h @@ -0,0 +1,45 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM packet formats, RFC 3208. + * + * Copyright (c) 2006 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_PACKET_PARSE_H__ +#define __PGM_IMPL_PACKET_PARSE_H__ + +#ifndef _WIN32 +# include +#endif +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL bool pgm_parse_raw (struct pgm_sk_buff_t*const restrict, struct sockaddr*const restrict, pgm_error_t**restrict); +PGM_GNUC_INTERNAL bool pgm_parse_udp_encap (struct pgm_sk_buff_t*const restrict, pgm_error_t**restrict); +PGM_GNUC_INTERNAL bool pgm_verify_spm (const struct pgm_sk_buff_t* const); +PGM_GNUC_INTERNAL bool pgm_verify_spmr (const struct pgm_sk_buff_t* const); +PGM_GNUC_INTERNAL bool pgm_verify_nak (const struct pgm_sk_buff_t* const); +PGM_GNUC_INTERNAL bool pgm_verify_nnak (const struct pgm_sk_buff_t* const); +PGM_GNUC_INTERNAL bool pgm_verify_ncf (const struct pgm_sk_buff_t* const); +PGM_GNUC_INTERNAL bool pgm_verify_poll (const struct pgm_sk_buff_t* const); +PGM_GNUC_INTERNAL bool pgm_verify_polr (const struct pgm_sk_buff_t* const); +PGM_GNUC_INTERNAL bool pgm_verify_ack (const struct pgm_sk_buff_t* const); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_PACKET_PARSE_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/packet_test.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/packet_test.h new file mode 100644 index 0000000..1b39006 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/packet_test.h @@ -0,0 +1,40 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM packet formats, RFC 3208. + * + * Copyright (c) 2006 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_PACKET_TEST_H__ +#define __PGM_IMPL_PACKET_TEST_H__ + +#ifndef _WIN32 +# include +#endif +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL bool pgm_print_packet (const void*, size_t); +PGM_GNUC_INTERNAL const char* pgm_type_string (uint8_t) PGM_GNUC_WARN_UNUSED_RESULT PGM_GNUC_CONST; +PGM_GNUC_INTERNAL const char* pgm_udpport_string (uint16_t) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL const char* pgm_gethostbyaddr (const struct in_addr*) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_ipopt_print (const void*, size_t); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_PACKET_TEST_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB.h new file mode 100644 index 0000000..6f80271 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB.h @@ -0,0 +1,28 @@ +/* + * Note: this file originally auto-generated by mib2c using + * : mib2c.notify.conf,v 5.3 2004/04/15 12:29:19 dts12 Exp $ + */ + +#ifndef __PGM_IMPL_MIB_H__ +#define __PGM_IMPL_MIB_H__ + +#include + +PGM_BEGIN_DECLS + +/* function declarations */ +PGM_GNUC_INTERNAL bool pgm_mib_init (pgm_error_t**); + +PGM_GNUC_INTERNAL int send_pgmStart_trap(void); +PGM_GNUC_INTERNAL int send_pgmStop_trap(void); +PGM_GNUC_INTERNAL int send_pgmNewSourceTrap_trap(void); +PGM_GNUC_INTERNAL int send_pgmClosedSourceTrap_trap(void); +PGM_GNUC_INTERNAL int send_pgmNewReceiverTrap_trap(void); +PGM_GNUC_INTERNAL int send_pgmClosedReceiverTrap_trap(void); +PGM_GNUC_INTERNAL int send_pgmNakFailuresTrap_trap(void); +PGM_GNUC_INTERNAL int send_pgmNewDlrSourceTrap_trap(void); +PGM_GNUC_INTERNAL int send_pgmClosedDlrSourceTrap_trap(void); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_MIB_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB_columns.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB_columns.h new file mode 100644 index 0000000..545e519 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB_columns.h @@ -0,0 +1,372 @@ +/* + * Note: this file originally auto-generated by mib2c using + * : mib2c.column_defines.conf,v 5.1 2002/05/08 05:42:47 hardaker Exp $ + */ +#ifndef PGMMIB_COLUMNS_H +#define PGMMIB_COLUMNS_H + +/* column number definitions for table pgmNeIfConfigTable */ + #define COLUMN_PGMNEIFCONFIGINDEX 1 + #define COLUMN_PGMNEIFPGMENABLE 2 + #define COLUMN_PGMNEIFNAKRPTINTERVAL 3 + #define COLUMN_PGMNEIFNAKRPTRATE 4 + #define COLUMN_PGMNEIFNAKRDATAINTERVAL 5 + #define COLUMN_PGMNEIFNAKELIMINATEINTERVAL 6 + +/* column number definitions for table pgmNeIfPerformanceTable */ + #define COLUMN_PGMNEIFPERFORMANCEINDEX 1 + #define COLUMN_PGMNEIFREXMITSTATES 2 + #define COLUMN_PGMNEIFREXMITTIMEDOUT 3 + #define COLUMN_PGMNEIFINSPMS 4 + #define COLUMN_PGMNEIFOUTSPMS 5 + #define COLUMN_PGMNEIFINPARITYSPMS 6 + #define COLUMN_PGMNEIFOUTPARITYSPMS 7 + #define COLUMN_PGMNEIFINRDATA 8 + #define COLUMN_PGMNEIFOUTRDATA 9 + #define COLUMN_PGMNEIFINPARITYRDATA 10 + #define COLUMN_PGMNEIFOUTPARITYRDATA 11 + #define COLUMN_PGMNEIFINRDATANOSESSIONERRORS 12 + #define COLUMN_PGMNEIFUNIQUENAKS 13 + #define COLUMN_PGMNEIFINNAKS 14 + #define COLUMN_PGMNEIFOUTNAKS 15 + #define COLUMN_PGMNEIFUNIQUEPARITYNAKS 16 + #define COLUMN_PGMNEIFINPARITYNAKS 17 + #define COLUMN_PGMNEIFOUTPARITYNAKS 18 + #define COLUMN_PGMNEIFINNAKNOSESSIONERRORS 19 + #define COLUMN_PGMNEIFINNAKSEQERRORS 20 + #define COLUMN_PGMNEIFINPARITYNAKTGERRORS 21 + #define COLUMN_PGMNEIFINNNAKS 22 + #define COLUMN_PGMNEIFOUTNNAKS 23 + #define COLUMN_PGMNEIFINPARITYNNAKS 24 + #define COLUMN_PGMNEIFOUTPARITYNNAKS 25 + #define COLUMN_PGMNEIFINNNAKNOSESSIONERRORS 26 + #define COLUMN_PGMNEIFINNCFS 27 + #define COLUMN_PGMNEIFOUTNCFS 28 + #define COLUMN_PGMNEIFINPARITYNCFS 29 + #define COLUMN_PGMNEIFOUTPARITYNCFS 30 + #define COLUMN_PGMNEIFINNCFNOSESSIONERRORS 31 + #define COLUMN_PGMNEIFINREDIRECTNCFS 32 + #define COLUMN_PGMNEIFMALFORMED 33 + #define COLUMN_PGMNEIFSPMFROMSOURCE 34 + #define COLUMN_PGMNEIFSPMBADSQN 35 + #define COLUMN_PGMNEIFSPMERROR 36 + #define COLUMN_PGMNEIFPOLLRANDOMIGNORE 37 + #define COLUMN_PGMNEIFPOLLTSISTATEERROR 38 + #define COLUMN_PGMNEIFPOLLPARENTERROR 39 + #define COLUMN_PGMNEIFPOLLTYPEERROR 40 + #define COLUMN_PGMNEIFPOLLERROR 41 + #define COLUMN_PGMNEIFPOLLSUCCESS 42 + #define COLUMN_PGMNEIFPOLLORIGINATED 43 + #define COLUMN_PGMNEIFPOLRNOSTATE 44 + #define COLUMN_PGMNEIFPOLRERROR 45 + #define COLUMN_PGMNEIFPOLRPARITYERROR 46 + #define COLUMN_PGMNEIFPOLRSUCCESS 47 + #define COLUMN_PGMNEIFPOLRORIGINATED 48 + #define COLUMN_PGMNEIFNCFERROR 49 + #define COLUMN_PGMNEIFNCFPARITYERROR 50 + #define COLUMN_PGMNEIFNCFPARTIALPARITY 51 + #define COLUMN_PGMNEIFNCFRECEIVED 52 + #define COLUMN_PGMNEIFNCFANTICIPATED 53 + #define COLUMN_PGMNEIFNCFREDIRECTING 54 + #define COLUMN_PGMNEIFNAKELIMINATED 55 + #define COLUMN_PGMNEIFNAKERROR 56 + #define COLUMN_PGMNEIFNAKPARITYERROR 57 + #define COLUMN_PGMNEIFNNAKELIMINATED 58 + #define COLUMN_PGMNEIFNNAKERROR 59 + #define COLUMN_PGMNEIFNNAKPARITYERROR 60 + #define COLUMN_PGMNEIFNNAKCONGESTIONREPORTS 61 + #define COLUMN_PGMNEIFNAKRETRYEXPIRED 62 + #define COLUMN_PGMNEIFNAKRETRYEXPIREDDLR 63 + #define COLUMN_PGMNEIFNAKFORWARDEDDLR 64 + #define COLUMN_PGMNEIFNAKRETRANSMITTED 65 + #define COLUMN_PGMNEIFRDATAELIMINATEDOIF 66 + #define COLUMN_PGMNEIFRDATAELIMINATEDSQN 67 + #define COLUMN_PGMNEIFINRDATAFRAGMENTS 68 + #define COLUMN_PGMNEIFRDATAFRAGMENTSNOSESSIONERRORS 69 + #define COLUMN_PGMNEIFRDATAFRAGMENTSELIMINATEDOIF 70 + #define COLUMN_PGMNEIFRDATAFRAGMENTSELIMINATEDSQN 71 + #define COLUMN_PGMNEIFOUTRDATAFRAGMENTS 72 + +/* column number definitions for table pgmNeTsiTable */ + #define COLUMN_PGMNETSIGLOBALID 1 + #define COLUMN_PGMNETSIDATASOURCEPORT 2 + #define COLUMN_PGMNETSISTATEBITS 3 + #define COLUMN_PGMNETSIDATADESTINATIONPORT 4 + #define COLUMN_PGMNETSISOURCEADDRESS 5 + #define COLUMN_PGMNETSIGROUPADDRESS 6 + #define COLUMN_PGMNETSIUPSTREAMADDRESS 7 + #define COLUMN_PGMNETSIUPSTREAMIFINDEX 8 + #define COLUMN_PGMNETSIDLRADDRESS 9 + +/* column number definitions for table pgmNeTsiPerformanceTable */ + #define COLUMN_PGMNETSIPERFORMANCEGLOBALID 1 + #define COLUMN_PGMNETSIPERFORMANCEDATASOURCEPORT 2 + #define COLUMN_PGMNETSISESSIONTRAILEDGESEQ 3 + #define COLUMN_PGMNETSISESSIONINCRSEQ 4 + #define COLUMN_PGMNETSILEADEDGESEQ 5 + #define COLUMN_PGMNETSIINSPMS 6 + #define COLUMN_PGMNETSIOUTSPMS 7 + #define COLUMN_PGMNETSIINPARITYSPMS 8 + #define COLUMN_PGMNETSIOUTPARITYSPMS 9 + #define COLUMN_PGMNETSITOTALREXMITSTATES 10 + #define COLUMN_PGMNETSITOTALREXMITTIMEDOUT 11 + #define COLUMN_PGMNETSIINRDATA 12 + #define COLUMN_PGMNETSIOUTRDATA 13 + #define COLUMN_PGMNETSIINPARITYRDATA 14 + #define COLUMN_PGMNETSIOUTPARITYRDATA 15 + #define COLUMN_PGMNETSIINRDATANOSTATEERRORS 16 + #define COLUMN_PGMNETSIUNIQUENAKS 17 + #define COLUMN_PGMNETSIINNAKS 18 + #define COLUMN_PGMNETSIOUTNAKS 19 + #define COLUMN_PGMNETSIUNIQUEPARITYNAKS 20 + #define COLUMN_PGMNETSIINPARITYNAKS 21 + #define COLUMN_PGMNETSIOUTPARITYNAKS 22 + #define COLUMN_PGMNETSIINNAKSEQERRORS 23 + #define COLUMN_PGMNETSIINNNAKS 24 + #define COLUMN_PGMNETSIOUTNNAKS 25 + #define COLUMN_PGMNETSIINPARITYNNAKS 26 + #define COLUMN_PGMNETSIOUTPARITYNNAKS 27 + #define COLUMN_PGMNETSIINNCFS 28 + #define COLUMN_PGMNETSIOUTNCFS 29 + #define COLUMN_PGMNETSIINPARITYNCFS 30 + #define COLUMN_PGMNETSIOUTPARITYNCFS 31 + #define COLUMN_PGMNETSISPMSEQUENCENUMBER 32 + #define COLUMN_PGMNETSITRANSMISSIONGROUPSIZE 33 + #define COLUMN_PGMNETSITIMEOUT 34 + #define COLUMN_PGMNETSILASTTTL 35 + #define COLUMN_PGMNETSILINKLOSSRATE 36 + #define COLUMN_PGMNETSIPATHLOSSRATE 37 + #define COLUMN_PGMNETSIRECEIVERLOSSRATE 38 + #define COLUMN_PGMNETSICONGESTIONREPORTLEAD 39 + #define COLUMN_PGMNETSICONGESTIONREPORTWORSTRECEIVER 40 + +/* column number definitions for table pgmNeTsiRtxTable */ + #define COLUMN_PGMNETSIRTXSEQUENCENUMBER 1 + #define COLUMN_PGMNETSIRTXSEQUENCENUMBERTYPE 2 + #define COLUMN_PGMNETSIRTXREQPARITYTGCOUNT 4 + #define COLUMN_PGMNETSIRTXTIMEOUT 5 + #define COLUMN_PGMNETSIRTXSTATEBITS 6 + +/* column number definitions for table pgmNeTsiRtxIfTable */ + #define COLUMN_PGMNETSIRTXIFINDEX 1 + #define COLUMN_PGMNETSIRTXIFPACKETCOUNT 2 + +/* column number definitions for table pgmNeTsiPolrTable */ + #define COLUMN_PGMNETSIPOLRSOURCE 1 + #define COLUMN_PGMNETSIPOLRSEQUENCENUMBER 2 + +/* column number definitions for table pgmNeTsiPollTable */ + #define COLUMN_PGMNETSIPOLLTYPE 1 + #define COLUMN_PGMNETSIPOLLSEQUENCE 2 + #define COLUMN_PGMNETSIPOLLCHILDBACKOFF 3 + #define COLUMN_PGMNETSIPOLLMASK 4 + #define COLUMN_PGMNETSIPOLLPERIOD 5 + #define COLUMN_PGMNETSIPOLLCOUNT 6 + #define COLUMN_PGMNETSIPOLLTIMEOUT 7 + +/* column number definitions for table pgmSourceTable */ + #define COLUMN_PGMSOURCEGLOBALID 1 + #define COLUMN_PGMSOURCESOURCEPORT 2 + #define COLUMN_PGMSOURCESOURCEADDRESS 3 + #define COLUMN_PGMSOURCEGROUPADDRESS 4 + #define COLUMN_PGMSOURCEDESTPORT 5 + #define COLUMN_PGMSOURCESOURCEGSI 6 + #define COLUMN_PGMSOURCESOURCEPORTNUMBER 7 + +/* column number definitions for table pgmSourceConfigTable */ + #define COLUMN_PGMSOURCECONFIGGLOBALID 1 + #define COLUMN_PGMSOURCECONFIGSOURCEPORT 2 + #define COLUMN_PGMSOURCETTL 3 + #define COLUMN_PGMSOURCEADVMODE 4 + #define COLUMN_PGMSOURCELATEJOIN 5 + #define COLUMN_PGMSOURCETXWMAXRTE 6 + #define COLUMN_PGMSOURCETXWSECS 7 + #define COLUMN_PGMSOURCETXWADVSECS 8 + #define COLUMN_PGMSOURCEADVIVL 9 + #define COLUMN_PGMSOURCESPMIVL 10 + #define COLUMN_PGMSOURCESPMHEARTBEATIVLMIN 11 + #define COLUMN_PGMSOURCESPMHEARTBEATIVLMAX 12 + #define COLUMN_PGMSOURCERDATABACKOFFIVL 13 + #define COLUMN_PGMSOURCEFEC 14 + #define COLUMN_PGMSOURCEFECTRANSMISSIONGRPSIZE 15 + #define COLUMN_PGMSOURCEFECPROACTIVEPARITYSIZE 16 + #define COLUMN_PGMSOURCESPMPATHADDRESS 17 + +/* column number definitions for table pgmSourcePerformanceTable */ + #define COLUMN_PGMSOURCEPERFORMANCEGLOBALID 1 + #define COLUMN_PGMSOURCEPERFORMANCESOURCEPORT 2 + #define COLUMN_PGMSOURCEDATABYTESSENT 3 + #define COLUMN_PGMSOURCEDATAMSGSSENT 4 + #define COLUMN_PGMSOURCEBYTESBUFFERED 5 + #define COLUMN_PGMSOURCEMSGSBUFFERED 6 + #define COLUMN_PGMSOURCEBYTESRETRANSMITTED 7 + #define COLUMN_PGMSOURCEMSGSRETRANSMITTED 8 + #define COLUMN_PGMSOURCEBYTESSENT 9 + #define COLUMN_PGMSOURCERAWNAKSRECEIVED 10 + #define COLUMN_PGMSOURCENAKSIGNORED 11 + #define COLUMN_PGMSOURCECKSUMERRORS 12 + #define COLUMN_PGMSOURCEMALFORMEDNAKS 13 + #define COLUMN_PGMSOURCEPACKETSDISCARDED 14 + #define COLUMN_PGMSOURCENAKSRCVD 15 + #define COLUMN_PGMSOURCEPARITYBYTESRETRANSMITTED 16 + #define COLUMN_PGMSOURCESELECTIVEBYTESRETRANSMITED 17 + #define COLUMN_PGMSOURCEPARITYMSGSRETRANSMITTED 18 + #define COLUMN_PGMSOURCESELECTIVEMSGSRETRANSMITTED 19 + #define COLUMN_PGMSOURCEBYTESADMIT 20 + #define COLUMN_PGMSOURCEMSGSADMIT 21 + #define COLUMN_PGMSOURCEPARITYNAKPACKETSRECEIVED 22 + #define COLUMN_PGMSOURCESELECTIVENAKPACKETSRECEIVED 23 + #define COLUMN_PGMSOURCEPARITYNAKSRECEIVED 24 + #define COLUMN_PGMSOURCESELECTIVENAKSRECEIVED 25 + #define COLUMN_PGMSOURCEPARITYNAKSIGNORED 26 + #define COLUMN_PGMSOURCESELECTIVENAKSIGNORED 27 + #define COLUMN_PGMSOURCEACKERRORS 28 + #define COLUMN_PGMSOURCEPGMCCACKER 29 + #define COLUMN_PGMSOURCETRANSMISSIONCURRENTRATE 30 + #define COLUMN_PGMSOURCEACKPACKETSRECEIVED 31 + #define COLUMN_PGMSOURCENNAKPACKETSRECEIVED 32 + #define COLUMN_PGMSOURCEPARITYNNAKPACKETSRECEIVED 33 + #define COLUMN_PGMSOURCESELECTIVENNAKPACKETSRECEIVED 34 + #define COLUMN_PGMSOURCENNAKSRECEIVED 35 + #define COLUMN_PGMSOURCEPARITYNNAKSRECEIVED 36 + #define COLUMN_PGMSOURCESELECTIVENNAKSRECEIVED 37 + #define COLUMN_PGMSOURCENNAKERRORS 38 + +/* column number definitions for table pgmReceiverTable */ + #define COLUMN_PGMRECEIVERGLOBALID 1 + #define COLUMN_PGMRECEIVERSOURCEPORT 2 + #define COLUMN_PGMRECEIVERINSTANCE 3 + #define COLUMN_PGMRECEIVERGROUPADDRESS 4 + #define COLUMN_PGMRECEIVERDESTPORT 5 + #define COLUMN_PGMRECEIVERSOURCEADDRESS 6 + #define COLUMN_PGMRECEIVERLASTHOP 7 + #define COLUMN_PGMRECEIVERSOURCEGSI 8 + #define COLUMN_PGMRECEIVERSOURCEPORTNUMBER 9 + #define COLUMN_PGMRECEIVERUNIQUEINSTANCE 10 + +/* column number definitions for table pgmReceiverConfigTable */ + #define COLUMN_PGMRECEIVERCONFIGGLOBALID 1 + #define COLUMN_PGMRECEIVERCONFIGSOURCEPORT 2 + #define COLUMN_PGMRECEIVERCONFIGINSTANCE 3 + #define COLUMN_PGMRECEIVERNAKBACKOFFIVL 4 + #define COLUMN_PGMRECEIVERNAKREPEATIVL 5 + #define COLUMN_PGMRECEIVERNAKNCFRETRIES 6 + #define COLUMN_PGMRECEIVERNAKRDATAIVL 7 + #define COLUMN_PGMRECEIVERNAKDATARETRIES 8 + #define COLUMN_PGMRECEIVERSENDNAKS 9 + #define COLUMN_PGMRECEIVERLATEJOIN 10 + #define COLUMN_PGMRECEIVERNAKTTL 11 + #define COLUMN_PGMRECEIVERDELIVERYORDER 12 + #define COLUMN_PGMRECEIVERMCASTNAKS 13 + #define COLUMN_PGMRECEIVERNAKFAILURETHRESHOLDTIMER 14 + #define COLUMN_PGMRECEIVERNAKFAILURETHRESHOLD 15 + +/* column number definitions for table pgmReceiverPerformanceTable */ + #define COLUMN_PGMRECEIVERPERFORMANCEGLOBALID 1 + #define COLUMN_PGMRECEIVERPERFORMANCESOURCEPORT 2 + #define COLUMN_PGMRECEIVERPERFORMANCEINSTANCE 3 + #define COLUMN_PGMRECEIVERDATABYTESRECEIVED 4 + #define COLUMN_PGMRECEIVERDATAMSGSRECEIVED 5 + #define COLUMN_PGMRECEIVERNAKSSENT 6 + #define COLUMN_PGMRECEIVERNAKSRETRANSMITTED 7 + #define COLUMN_PGMRECEIVERNAKFAILURES 8 + #define COLUMN_PGMRECEIVERBYTESRECEIVED 9 + #define COLUMN_PGMRECEIVERNAKSSUPPRESSED 10 + #define COLUMN_PGMRECEIVERCKSUMERRORS 11 + #define COLUMN_PGMRECEIVERMALFORMEDSPMS 12 + #define COLUMN_PGMRECEIVERMALFORMEDODATA 13 + #define COLUMN_PGMRECEIVERMALFORMEDRDATA 14 + #define COLUMN_PGMRECEIVERMALFORMEDNCFS 15 + #define COLUMN_PGMRECEIVERPACKETSDISCARDED 16 + #define COLUMN_PGMRECEIVERLOSSES 17 + #define COLUMN_PGMRECEIVERBYTESDELIVEREDTOAPP 18 + #define COLUMN_PGMRECEIVERMSGSDELIVEREDTOAPP 19 + #define COLUMN_PGMRECEIVERDUPSPMS 20 + #define COLUMN_PGMRECEIVERDUPDATAS 21 + #define COLUMN_PGMRECEIVERDUPPARITIES 22 + #define COLUMN_PGMRECEIVERNAKPACKETSSENT 23 + #define COLUMN_PGMRECEIVERPARITYNAKPACKETSSENT 24 + #define COLUMN_PGMRECEIVERSELECTIVENAKPACKETSSENT 25 + #define COLUMN_PGMRECEIVERPARITYNAKSSENT 26 + #define COLUMN_PGMRECEIVERSELECTIVENAKSSENT 27 + #define COLUMN_PGMRECEIVERPARITYNAKSRETRANSMITTED 28 + #define COLUMN_PGMRECEIVERSELECTIVENAKSRETRANSMITTED 29 + #define COLUMN_PGMRECEIVERNAKSFAILED 30 + #define COLUMN_PGMRECEIVERPARITYNAKSFAILED 31 + #define COLUMN_PGMRECEIVERSELECTIVENAKSFAILED 32 + #define COLUMN_PGMRECEIVERNAKSFAILEDRXWADVANCED 33 + #define COLUMN_PGMRECEIVERNAKSFALEDNCFRETRIESEXCEEDED 34 + #define COLUMN_PGMRECEIVERNAKSFAILEDDATARETRIESEXCEEDED 35 + #define COLUMN_PGMRECEIVERNAKSFAILEDGENEXPIRED 36 + #define COLUMN_PGMRECEIVERNAKFAILURESDELIVERED 37 + #define COLUMN_PGMRECEIVERPARITYNAKSSUPPRESSED 38 + #define COLUMN_PGMRECEIVERSELECTIVENAKSSUPPRESSED 39 + #define COLUMN_PGMRECEIVERNAKERRORS 40 + #define COLUMN_PGMRECEIVEROUTSTANDINGPARITYNAKS 41 + #define COLUMN_PGMRECEIVEROUTSTANDINGSELECTIVENAKS 42 + #define COLUMN_PGMRECEIVERLASTACTIVITY 43 + #define COLUMN_PGMRECEIVERNAKSVCTIMEMIN 44 + #define COLUMN_PGMRECEIVERNAKSVCTIMEMEAN 45 + #define COLUMN_PGMRECEIVERNAKSVCTIMEMAX 46 + #define COLUMN_PGMRECEIVERNAKFAILTIMEMIN 47 + #define COLUMN_PGMRECEIVERNAKFAILTIMEMEAN 48 + #define COLUMN_PGMRECEIVERNAKFAILTIMEMAX 49 + #define COLUMN_PGMRECEIVERNAKTRANSMITMIN 50 + #define COLUMN_PGMRECEIVERNAKTRANSMITMEAN 51 + #define COLUMN_PGMRECEIVERNAKTRANSMITMAX 52 + #define COLUMN_PGMRECEIVERACKSSENT 53 + #define COLUMN_PGMRECEIVERRXWTRAIL 54 + #define COLUMN_PGMRECEIVERRXWLEAD 55 + #define COLUMN_PGMRECEIVERNAKFAILURESLASTINTERVAL 56 + #define COLUMN_PGMRECEIVERLASTINTERVALNAKFAILURES 57 + +/* column number definitions for table pgmDlrSourceTable */ + #define COLUMN_PGMDLRSOURCEGLOBALID 1 + #define COLUMN_PGMDLRSOURCESOURCEPORT 2 + #define COLUMN_PGMDLRSOURCEGROUPADDRESS 3 + #define COLUMN_PGMDLRSOURCESOURCEGSI 4 + #define COLUMN_PGMDLRSOURCESOURCEPORTNUMBER 5 + +/* column number definitions for table pgmDlrSourceConfigTable */ + #define COLUMN_PGMDLRSOURCECONFIGGLOBALID 1 + #define COLUMN_PGMDLRSOURCECONFIGSOURCEPORT 2 + #define COLUMN_PGMDLRSOURCEGROUPTTL 3 + #define COLUMN_PGMDLRSOURCERDATABACKOFFIVL 4 + +/* column number definitions for table pgmDlrSourcePerformanceTable */ + #define COLUMN_PGMDLRSOURCEPERFORMANCEGLOBALID 1 + #define COLUMN_PGMDLRSOURCEPERFORMANCESOURCEPORT 2 + #define COLUMN_PGMDLRSOURCERDATAMSGSSENT 3 + #define COLUMN_PGMDLRSOURCERDATABYTESSENT 4 + #define COLUMN_PGMDLRSOURCEBYTESSENT 5 + #define COLUMN_PGMDLRSOURCENAKSRCVD 6 + #define COLUMN_PGMDLRSOURCENAKSIGNORED 7 + #define COLUMN_PGMDLRSOURCENAKERRORS 8 + #define COLUMN_PGMDLRSOURCEDISCARDS 9 + #define COLUMN_PGMDLRSOURCECKSUMERRORS 10 + #define COLUMN_PGMDLRSOURCENNAKSSENT 11 + #define COLUMN_PGMDLRSOURCEBYTESBUFFERED 12 + #define COLUMN_PGMDLRSOURCEMSGSBUFFERED 13 + #define COLUMN_PGMDLRSOURCEPARITYBYTESRETRANSMITTED 14 + #define COLUMN_PGMDLRSOURCESELECTIVEBYTESRETRANSMITED 15 + #define COLUMN_PGMDLRSOURCEPARITYMSGSRETRANSMITTED 16 + #define COLUMN_PGMDLRSOURCESELECTIVEMSGSRETRANSMITTED 17 + #define COLUMN_PGMDLRSOURCEBYTESADMIT 18 + #define COLUMN_PGMDLRSOURCEMSGSADMIT 19 + #define COLUMN_PGMDLRSOURCENAKPACKETSRECEIVED 20 + #define COLUMN_PGMDLRSOURCEPARITYNAKPACKETSRECEIVED 21 + #define COLUMN_PGMDLRSOURCESELECTIVENAKPACKETSRECEIVED 22 + #define COLUMN_PGMDLRSOURCEPARITYNAKSRECEIVED 23 + #define COLUMN_PGMDLRSOURCESELECTIVENAKSRECEIVED 24 + #define COLUMN_PGMDLRSOURCEPARITYNAKSIGNORED 25 + #define COLUMN_PGMDLRSOURCESELECTIVENAKSIGNORED 26 + #define COLUMN_PGMDLRSOURCEACKERRORS 27 + #define COLUMN_PGMDLRSOURCENNAKERRORS 28 + #define COLUMN_PGMDLRSOURCEACKPACKETSRECEIVED 29 + #define COLUMN_PGMDLRSOURCENNAKPACKETSRECEIVED 30 + #define COLUMN_PGMDLRSOURCEPARITYNNAKPACKETSRECEIVED 31 + #define COLUMN_PGMDLRSOURCESELECTIVENNAKPACKETSRECEIVED 32 + #define COLUMN_PGMDLRSOURCENNAKSRECEIVED 33 + #define COLUMN_PGMDLRSOURCEPARITYNNAKSRECEIVED 34 + #define COLUMN_PGMDLRSOURCESELECTIVENNAKSRECEIVED 35 +#endif /* PGMMIB_COLUMNS_H */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB_enums.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB_enums.h new file mode 100644 index 0000000..9ae5760 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB_enums.h @@ -0,0 +1,64 @@ +/* + * Note: this file originally auto-generated by mib2c using + * : mib2c.column_enums.conf,v 5.2 2003/02/22 04:09:25 hardaker Exp $ + */ +#ifndef PGMMIB_ENUMS_H +#define PGMMIB_ENUMS_H + +/* enums for column pgmNeIfPgmEnable */ + #define PGMNEIFPGMENABLE_ENABLE 1 + #define PGMNEIFPGMENABLE_DISABLE 2 + +/* enums for column pgmNeTsiStateBits */ + #define PGMNETSISTATEBITS_INITIALISING 0 + #define PGMNETSISTATEBITS_SPMSQNSTATEVALID 1 + #define PGMNETSISTATEBITS_DLRCANPROVIDEPARITY 2 + +/* enums for column pgmNeTsiRtxSequenceNumberType */ + #define PGMNETSIRTXSEQUENCENUMBERTYPE_SELECTIVE 1 + #define PGMNETSIRTXSEQUENCENUMBERTYPE_TG 2 + +/* enums for column pgmNeTsiRtxStateBits */ + #define PGMNETSIRTXSTATEBITS_INITIALISING 0 + #define PGMNETSIRTXSTATEBITS_ELIMINATING 1 + #define PGMNETSIRTXSTATEBITS_REDIRECTING 2 + #define PGMNETSIRTXSTATEBITS_STATECREATEDBYNULLNAK 3 + #define PGMNETSIRTXSTATEBITS_LISTNAKENTRY 4 + #define PGMNETSIRTXSTATEBITS_PARITYSTATE 5 + +/* enums for column pgmNeTsiPollType */ + #define PGMNETSIPOLLTYPE_GENERAL 1 + #define PGMNETSIPOLLTYPE_DLR 2 + +/* enums for column pgmSourceAdvMode */ + #define PGMSOURCEADVMODE_DATA 1 + #define PGMSOURCEADVMODE_TIME 2 + #define PGMSOURCEADVMODE_APPLCTRL 3 + #define PGMSOURCEADVMODE_OTHER 4 + +/* enums for column pgmSourceLateJoin */ + #define PGMSOURCELATEJOIN_ENABLE 1 + #define PGMSOURCELATEJOIN_DISABLE 2 + +/* enums for column pgmSourceFEC */ + #define PGMSOURCEFEC_DISABLED 1 + #define PGMSOURCEFEC_ENABLEDFIXEDPACKETSIZE 2 + #define PGMSOURCEFEC_ENABLEDVARIABLEPACKETSIZE 3 + +/* enums for column pgmReceiverSendNaks */ + #define PGMRECEIVERSENDNAKS_ENABLED 1 + #define PGMRECEIVERSENDNAKS_DISABLED 2 + +/* enums for column pgmReceiverLateJoin */ + #define PGMRECEIVERLATEJOIN_ENABLED 1 + #define PGMRECEIVERLATEJOIN_DISABLED 2 + +/* enums for column pgmReceiverDeliveryOrder */ + #define PGMRECEIVERDELIVERYORDER_UNORDERED 1 + #define PGMRECEIVERDELIVERYORDER_ORDERED 2 + +/* enums for column pgmReceiverMcastNaks */ + #define PGMRECEIVERMCASTNAKS_ENABLED 1 + #define PGMRECEIVERMCASTNAKS_DISABLED 2 + +#endif /* PGMMIB_ENUMS_H */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/processor.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/processor.h new file mode 100644 index 0000000..f7ee31a --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/processor.h @@ -0,0 +1,61 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Processor macros for cross-platform, cross-compiler froyo. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_PROCESSOR_H__ +#define __PGM_IMPL_PROCESSOR_H__ + +/* Memory prefetch */ +#if defined( sun ) +static inline void pgm_prefetch (const void *x) +{ + asm volatile ( "prefetch [%0], #one_write" + : /* nil */ + : "r" (x)); +} +static inline void pgm_prefetchw (const void *x) +{ + asm volatile ( "prefetch [%0], #n_writes" + : /* nil */ + : "r" (x)); +} +#elif defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) +static inline void pgm_prefetch (const void *x) +{ + __builtin_prefetch (x, 0 /* read */, 0 /* no temporal locality */); +} +static inline void pgm_prefetchw (const void *x) +{ + __builtin_prefetch (x, 1 /* write */, 3 /* high temporal */); +} +#else +static inline void pgm_prefetch (PGM_GNUC_UNUSED const void *x) +{ +} +static inline void pgm_prefetchw (PGM_GNUC_UNUSED const void *x) +{ +} +#endif + +#endif /* __PGM_IMPL_PROCESSOR_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/queue.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/queue.h new file mode 100644 index 0000000..c640e52 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/queue.h @@ -0,0 +1,51 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable double-ended queue. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_QUEUE_H__ +#define __PGM_IMPL_QUEUE_H__ + +typedef struct pgm_queue_t pgm_queue_t; + +#include +#include + +PGM_BEGIN_DECLS + +struct pgm_queue_t +{ + pgm_list_t* head; /* head & tail equal on 1 element */ + pgm_list_t* tail; + unsigned length; +}; + +PGM_GNUC_INTERNAL bool pgm_queue_is_empty (const pgm_queue_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_queue_push_head_link (pgm_queue_t*restrict, pgm_list_t*restrict); +PGM_GNUC_INTERNAL pgm_list_t* pgm_queue_pop_tail_link (pgm_queue_t*); +PGM_GNUC_INTERNAL pgm_list_t* pgm_queue_peek_tail_link (pgm_queue_t*) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_queue_unlink (pgm_queue_t*restrict, pgm_list_t*restrict); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_QUEUE_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/rand.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/rand.h new file mode 100644 index 0000000..0adfd78 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/rand.h @@ -0,0 +1,50 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable weak pseudo-random generator. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_RAND_H__ +#define __PGM_IMPL_RAND_H__ + +typedef struct pgm_rand_t pgm_rand_t; + +#include + +PGM_BEGIN_DECLS + +struct pgm_rand_t { + uint32_t seed; +}; + +PGM_GNUC_INTERNAL void pgm_rand_create (pgm_rand_t*); +PGM_GNUC_INTERNAL uint32_t pgm_rand_int (pgm_rand_t*); +PGM_GNUC_INTERNAL int32_t pgm_rand_int_range (pgm_rand_t*, int32_t, int32_t); +PGM_GNUC_INTERNAL uint32_t pgm_random_int (void); +PGM_GNUC_INTERNAL int32_t pgm_random_int_range (int32_t, int32_t); + +PGM_GNUC_INTERNAL void pgm_rand_init (void); +PGM_GNUC_INTERNAL void pgm_rand_shutdown (void); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_RAND_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/rate_control.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/rate_control.h new file mode 100644 index 0000000..b27b266 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/rate_control.h @@ -0,0 +1,54 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Rate regulation. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_RATE_CONTROL_H__ +#define __PGM_IMPL_RATE_CONTROL_H__ + +typedef struct pgm_rate_t pgm_rate_t; + +#include +#include +#include + +PGM_BEGIN_DECLS + +struct pgm_rate_t { + ssize_t rate_per_sec; + ssize_t rate_per_msec; + size_t iphdr_len; + + ssize_t rate_limit; /* signed for math */ + pgm_time_t last_rate_check; + pgm_spinlock_t spinlock; +}; + +PGM_GNUC_INTERNAL void pgm_rate_create (pgm_rate_t*, const ssize_t, const size_t, const uint16_t); +PGM_GNUC_INTERNAL void pgm_rate_destroy (pgm_rate_t*); +PGM_GNUC_INTERNAL bool pgm_rate_check (pgm_rate_t*, const size_t, const bool); +PGM_GNUC_INTERNAL pgm_time_t pgm_rate_remaining (pgm_rate_t*, const size_t); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_RATE_CONTROL_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/receiver.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/receiver.h new file mode 100644 index 0000000..e9c3a75 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/receiver.h @@ -0,0 +1,142 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM receiver socket. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_RECEIVER_H__ +#define __PGM_IMPL_RECEIVER_H__ + +typedef struct pgm_peer_t pgm_peer_t; + +#ifndef _WIN32 +# include +#endif +#include +#include + +PGM_BEGIN_DECLS + +/* Performance Counters */ + +enum { + PGM_PC_RECEIVER_DATA_BYTES_RECEIVED, + PGM_PC_RECEIVER_DATA_MSGS_RECEIVED, + PGM_PC_RECEIVER_NAK_FAILURES, + PGM_PC_RECEIVER_BYTES_RECEIVED, +/* PGM_PC_RECEIVER_CKSUM_ERRORS, */ /* inherently same as source */ + PGM_PC_RECEIVER_MALFORMED_SPMS, + PGM_PC_RECEIVER_MALFORMED_ODATA, + PGM_PC_RECEIVER_MALFORMED_RDATA, + PGM_PC_RECEIVER_MALFORMED_NCFS, + PGM_PC_RECEIVER_PACKETS_DISCARDED, + PGM_PC_RECEIVER_LOSSES, +/* PGM_PC_RECEIVER_BYTES_DELIVERED_TO_APP, */ +/* PGM_PC_RECEIVER_MSGS_DELIVERED_TO_APP, */ + PGM_PC_RECEIVER_DUP_SPMS, + PGM_PC_RECEIVER_DUP_DATAS, + PGM_PC_RECEIVER_PARITY_NAK_PACKETS_SENT, + PGM_PC_RECEIVER_SELECTIVE_NAK_PACKETS_SENT, + PGM_PC_RECEIVER_PARITY_NAKS_SENT, + PGM_PC_RECEIVER_SELECTIVE_NAKS_SENT, + PGM_PC_RECEIVER_PARITY_NAKS_RETRANSMITTED, + PGM_PC_RECEIVER_SELECTIVE_NAKS_RETRANSMITTED, + PGM_PC_RECEIVER_PARITY_NAKS_FAILED, + PGM_PC_RECEIVER_SELECTIVE_NAKS_FAILED, + PGM_PC_RECEIVER_NAKS_FAILED_RXW_ADVANCED, + PGM_PC_RECEIVER_NAKS_FAILED_NCF_RETRIES_EXCEEDED, + PGM_PC_RECEIVER_NAKS_FAILED_DATA_RETRIES_EXCEEDED, +/* PGM_PC_RECEIVER_NAKS_FAILED_GEN_EXPIRED */ + PGM_PC_RECEIVER_NAK_FAILURES_DELIVERED, + PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED, + PGM_PC_RECEIVER_NAK_ERRORS, +/* PGM_PC_RECEIVER_LAST_ACTIVITY, */ +/* PGM_PC_RECEIVER_NAK_SVC_TIME_MIN, */ + PGM_PC_RECEIVER_NAK_SVC_TIME_MEAN, +/* PGM_PC_RECEIVER_NAK_SVC_TIME_MAX, */ +/* PGM_PC_RECEIVER_NAK_FAIL_TIME_MIN, */ + PGM_PC_RECEIVER_NAK_FAIL_TIME_MEAN, +/* PGM_PC_RECEIVER_NAK_FAIL_TIME_MAX, */ +/* PGM_PC_RECEIVER_TRANSMIT_MIN, */ + PGM_PC_RECEIVER_TRANSMIT_MEAN, +/* PGM_PC_RECEIVER_TRANSMIT_MAX, */ + PGM_PC_RECEIVER_ACKS_SENT, + +/* marker */ + PGM_PC_RECEIVER_MAX +}; + +struct pgm_peer_t { + volatile uint32_t ref_count; /* atomic integer */ + + pgm_tsi_t tsi; + struct sockaddr_storage group_nla; + struct sockaddr_storage nla, local_nla; /* nla = advertised, local_nla = from packet */ + struct sockaddr_storage poll_nla; /* from parent to direct poll-response */ + struct sockaddr_storage redirect_nla; /* from dlr */ + pgm_time_t polr_expiry; + pgm_time_t spmr_expiry; + pgm_time_t spmr_tstamp; + + pgm_rxw_t* restrict window; + pgm_sock_t* restrict sock; + pgm_list_t peers_link; + pgm_slist_t pending_link; + + unsigned is_fec_enabled:1; + unsigned has_proactive_parity:1; /* indicating availability from this source */ + unsigned has_ondemand_parity:1; + + uint32_t spm_sqn; + pgm_time_t expiry; + + pgm_time_t ack_rb_expiry; /* 0 = no ACK pending */ + pgm_time_t ack_last_tstamp; /* in source time reference */ + pgm_list_t ack_link; + + uint32_t last_poll_sqn; + uint16_t last_poll_round; + pgm_time_t last_packet; + pgm_time_t last_data_tstamp; /* local timestamp of ack_last_tstamp */ + unsigned last_commit; + uint32_t lost_count; + uint32_t last_cumulative_losses; + volatile uint32_t cumulative_stats[PGM_PC_RECEIVER_MAX]; + uint32_t snap_stats[PGM_PC_RECEIVER_MAX]; + + uint32_t min_fail_time; + uint32_t max_fail_time; +}; + +PGM_GNUC_INTERNAL pgm_peer_t* pgm_new_peer (pgm_sock_t*const restrict, const pgm_tsi_t*const restrict, const struct sockaddr*const restrict, const socklen_t, const struct sockaddr*const restrict, const socklen_t, const pgm_time_t); +PGM_GNUC_INTERNAL void pgm_peer_unref (pgm_peer_t*); +PGM_GNUC_INTERNAL int pgm_flush_peers_pending (pgm_sock_t*const restrict, struct pgm_msgv_t**restrict, const struct pgm_msgv_t*const, size_t*const restrict, unsigned*const restrict); +PGM_GNUC_INTERNAL bool pgm_peer_has_pending (pgm_peer_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_peer_set_pending (pgm_sock_t*const, pgm_peer_t*const); +PGM_GNUC_INTERNAL bool pgm_check_peer_state (pgm_sock_t*const, const pgm_time_t); +PGM_GNUC_INTERNAL void pgm_set_reset_error (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_msgv_t*const restrict); +PGM_GNUC_INTERNAL pgm_time_t pgm_min_receiver_expiry (pgm_time_t, pgm_sock_t*) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_on_peer_nak (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_on_data (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_on_ncf (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_on_spm (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_on_poll (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; + +PGM_END_DECLS + +#endif /* __PGM_IMPL_RECEIVER_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/reed_solomon.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/reed_solomon.h new file mode 100644 index 0000000..98f6734 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/reed_solomon.h @@ -0,0 +1,51 @@ +/* + * Reed-Solomon forward error correction based on Vandermonde matrices + * + * Copyright (c) 2006-2008 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_REED_SOLOMON_H__ +#define __PGM_IMPL_REED_SOLOMON_H__ + +typedef struct pgm_rs_t pgm_rs_t; + +#include +#include + +PGM_BEGIN_DECLS + +struct pgm_rs_t { + uint8_t n, k; /* RS(n, k) */ + pgm_gf8_t* GM; + pgm_gf8_t* RM; +}; + +#define PGM_RS_DEFAULT_N 255 + +PGM_GNUC_INTERNAL void pgm_rs_create (pgm_rs_t*, const uint8_t, const uint8_t); +PGM_GNUC_INTERNAL void pgm_rs_destroy (pgm_rs_t*); +PGM_GNUC_INTERNAL void pgm_rs_encode (pgm_rs_t*restrict, const pgm_gf8_t**restrict, const uint8_t, pgm_gf8_t*restrict, const uint16_t); +PGM_GNUC_INTERNAL void pgm_rs_decode_parity_inline (pgm_rs_t*restrict, pgm_gf8_t**restrict, const uint8_t*restrict, const uint16_t); +PGM_GNUC_INTERNAL void pgm_rs_decode_parity_appended (pgm_rs_t*restrict, pgm_gf8_t**restrict, const uint8_t*restrict, const uint16_t); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_REED_SOLOMON_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/rxw.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/rxw.h new file mode 100644 index 0000000..89a8921 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/rxw.h @@ -0,0 +1,220 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * basic receive window. + * + * Copyright (c) 2006-2008 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_RXW_H__ +#define __PGM_IMPL_RXW_H__ + +typedef struct pgm_rxw_state_t pgm_rxw_state_t; +typedef struct pgm_rxw_t pgm_rxw_t; + +#include + +PGM_BEGIN_DECLS + +enum +{ + PGM_PKT_STATE_ERROR = 0, + PGM_PKT_STATE_BACK_OFF, /* PGM protocol recovery states */ + PGM_PKT_STATE_WAIT_NCF, + PGM_PKT_STATE_WAIT_DATA, + PGM_PKT_STATE_HAVE_DATA, /* data received waiting to commit to application layer */ + PGM_PKT_STATE_HAVE_PARITY, /* contains parity information not original data */ + PGM_PKT_STATE_COMMIT_DATA, /* commited data waiting for purging */ + PGM_PKT_STATE_LOST_DATA /* if recovery fails, but packet has not yet been commited */ +}; + +enum +{ + PGM_RXW_OK = 0, + PGM_RXW_INSERTED, + PGM_RXW_APPENDED, + PGM_RXW_UPDATED, + PGM_RXW_MISSING, + PGM_RXW_DUPLICATE, + PGM_RXW_MALFORMED, + PGM_RXW_BOUNDS, + PGM_RXW_SLOW_CONSUMER, + PGM_RXW_UNKNOWN +}; + +/* must be smaller than PGM skbuff control buffer */ +struct pgm_rxw_state_t { + pgm_time_t timer_expiry; + int pkt_state; + + uint8_t nak_transmit_count; /* 8-bit for size constraints */ + uint8_t ncf_retry_count; + uint8_t data_retry_count; + +/* only valid on tg_sqn::pkt_sqn = 0 */ + unsigned is_contiguous:1; /* transmission group */ +}; + +struct pgm_rxw_t { + const pgm_tsi_t* tsi; + + pgm_queue_t ack_backoff_queue; + pgm_queue_t nak_backoff_queue; + pgm_queue_t wait_ncf_queue; + pgm_queue_t wait_data_queue; +/* window context counters */ + uint32_t lost_count; /* failed to repair */ + uint32_t fragment_count; /* incomplete apdu */ + uint32_t parity_count; /* parity for repairs */ + uint32_t committed_count; /* but still in window */ + + uint16_t max_tpdu; /* maximum packet size */ + uint32_t lead, trail; + uint32_t rxw_trail, rxw_trail_init; + uint32_t commit_lead; + unsigned is_constrained:1; + unsigned is_defined:1; + unsigned has_event:1; /* edge triggered */ + unsigned is_fec_available:1; + pgm_rs_t rs; + uint32_t tg_size; /* transmission group size for parity recovery */ + uint8_t tg_sqn_shift; + + uint32_t bitmap; /* receive status of last 32 packets */ + uint32_t data_loss; /* p */ + uint32_t ack_c_p; /* constant Cᵨ */ + +/* counters all guint32 */ + uint32_t min_fill_time; /* restricted from pgm_time_t */ + uint32_t max_fill_time; + uint32_t min_nak_transmit_count; + uint32_t max_nak_transmit_count; + uint32_t cumulative_losses; + uint32_t bytes_delivered; + uint32_t msgs_delivered; + + size_t size; /* in bytes */ + unsigned alloc; /* in pkts */ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + struct pgm_sk_buff_t* pdata[]; +#elif defined(__cplusplus) + struct pgm_sk_buff_t* pdata[1]; +#else + struct pgm_sk_buff_t* pdata[0]; +#endif +}; + + +PGM_GNUC_INTERNAL pgm_rxw_t* pgm_rxw_create (const pgm_tsi_t*const, const uint16_t, const unsigned, const unsigned, const ssize_t, const uint32_t) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_rxw_destroy (pgm_rxw_t*const); +PGM_GNUC_INTERNAL int pgm_rxw_add (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*const restrict, const pgm_time_t, const pgm_time_t) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_rxw_add_ack (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*const restrict, const pgm_time_t); +PGM_GNUC_INTERNAL void pgm_rxw_remove_ack (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*const restrict); +PGM_GNUC_INTERNAL void pgm_rxw_remove_commit (pgm_rxw_t*const); +PGM_GNUC_INTERNAL ssize_t pgm_rxw_readv (pgm_rxw_t*const restrict, struct pgm_msgv_t** restrict, const unsigned) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL unsigned pgm_rxw_remove_trail (pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL unsigned pgm_rxw_update (pgm_rxw_t*const, const uint32_t, const uint32_t, const pgm_time_t, const pgm_time_t) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_rxw_update_fec (pgm_rxw_t*const, const uint8_t); +PGM_GNUC_INTERNAL int pgm_rxw_confirm (pgm_rxw_t*const, uint32_t, pgm_time_t, pgm_time_t, pgm_time_t) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_rxw_lost (pgm_rxw_t*const, const uint32_t); +PGM_GNUC_INTERNAL void pgm_rxw_state (pgm_rxw_t* restrict, struct pgm_sk_buff_t*restrict, const int); +PGM_GNUC_INTERNAL struct pgm_sk_buff_t* pgm_rxw_peek (pgm_rxw_t*const, const uint32_t) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL const char* pgm_pkt_state_string (const int) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL const char* pgm_rxw_returns_string (const int) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_rxw_dump (const pgm_rxw_t*const); + +/* declare for GCC attributes */ +static inline unsigned pgm_rxw_max_length (const pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline uint32_t pgm_rxw_length (const pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline size_t pgm_rxw_size (const pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline bool pgm_rxw_is_empty (const pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline bool pgm_rxw_is_full (const pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline uint32_t pgm_rxw_lead (const pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline uint32_t pgm_rxw_next_lead (const pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; + +static inline +unsigned +pgm_rxw_max_length ( + const pgm_rxw_t* const window + ) +{ + pgm_assert (NULL != window); + return window->alloc; +} + +static inline +uint32_t +pgm_rxw_length ( + const pgm_rxw_t* const window + ) +{ + pgm_assert (NULL != window); + return ( 1 + window->lead ) - window->trail; +} + +static inline +size_t +pgm_rxw_size ( + const pgm_rxw_t* const window + ) +{ + pgm_assert (NULL != window); + return window->size; +} + +static inline +bool +pgm_rxw_is_empty ( + const pgm_rxw_t* const window + ) +{ + pgm_assert (NULL != window); + return pgm_rxw_length (window) == 0; +} + +static inline +bool +pgm_rxw_is_full ( + const pgm_rxw_t* const window + ) +{ + pgm_assert (NULL != window); + return pgm_rxw_length (window) == pgm_rxw_max_length (window); +} + +static inline +uint32_t +pgm_rxw_lead ( + const pgm_rxw_t* const window + ) +{ + pgm_assert (NULL != window); + return window->lead; +} + +static inline +uint32_t +pgm_rxw_next_lead ( + const pgm_rxw_t* const window + ) +{ + pgm_assert (NULL != window); + return (uint32_t)(pgm_rxw_lead (window) + 1); +} + +PGM_END_DECLS + +#endif /* __PGM_IMPL_RXW_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/slist.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/slist.h new file mode 100644 index 0000000..e71b15d --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/slist.h @@ -0,0 +1,52 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable singly-linked list. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_SLIST_H__ +#define __PGM_IMPL_SLIST_H__ + +typedef struct pgm_slist_t pgm_slist_t; + +#include + +PGM_BEGIN_DECLS + +struct pgm_slist_t +{ + void* restrict data; + struct pgm_slist_t* restrict next; +}; + +PGM_GNUC_INTERNAL pgm_slist_t* pgm_slist_append (pgm_slist_t*restrict, void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL pgm_slist_t* pgm_slist_prepend (pgm_slist_t*restrict, void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL pgm_slist_t* pgm_slist_prepend_link (pgm_slist_t*restrict, pgm_slist_t*restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL pgm_slist_t* pgm_slist_remove (pgm_slist_t*restrict, const void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL pgm_slist_t* pgm_slist_remove_first (pgm_slist_t*) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_slist_free (pgm_slist_t*); +PGM_GNUC_INTERNAL pgm_slist_t* pgm_slist_last (pgm_slist_t*) PGM_GNUC_PURE PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL unsigned pgm_slist_length (pgm_slist_t*) PGM_GNUC_PURE PGM_GNUC_WARN_UNUSED_RESULT; + +PGM_END_DECLS + +#endif /* __PGM_IMPL_SLIST_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/sn.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/sn.h new file mode 100644 index 0000000..667db0b --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/sn.h @@ -0,0 +1,186 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * serial number arithmetic: rfc 1982 + * + * Copyright (c) 2006-2007 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_SN_H__ +#define __PGM_IMPL_SN_H__ + +#include +#include + +PGM_BEGIN_DECLS + +#define PGM_UINT32_SIGN_BIT (1UL<<31) +#define PGM_UINT64_SIGN_BIT (1ULL<<63) + +/* declare for GCC attributes */ +static inline bool pgm_uint32_lt (const uint32_t, const uint32_t) PGM_GNUC_CONST; +static inline bool pgm_uint32_lte (const uint32_t, const uint32_t) PGM_GNUC_CONST; +static inline bool pgm_uint32_gt (const uint32_t, const uint32_t) PGM_GNUC_CONST; +static inline bool pgm_uint32_gte (const uint32_t, const uint32_t) PGM_GNUC_CONST; +static inline bool pgm_uint64_lt (const uint64_t, const uint64_t) PGM_GNUC_CONST; +static inline bool pgm_uint64_lte (const uint64_t, const uint64_t) PGM_GNUC_CONST; +static inline bool pgm_uint64_gt (const uint64_t, const uint64_t) PGM_GNUC_CONST; +static inline bool pgm_uint64_gte (const uint64_t, const uint64_t) PGM_GNUC_CONST; + +/* 32 bit */ +static inline +bool pgm_uint32_lt ( + const uint32_t s, + const uint32_t t + ) +{ + pgm_assert (sizeof(int) >= 4); + return ( ((s) - (t)) & PGM_UINT32_SIGN_BIT ); +} + +static inline +bool +pgm_uint32_lte ( + const uint32_t s, + const uint32_t t + ) +{ + pgm_assert (sizeof(int) >= 4); + return ( ((s) == (t)) || ( ((s) - (t)) & PGM_UINT32_SIGN_BIT ) ); +} + +static inline +bool +pgm_uint32_gt ( + const uint32_t s, + const uint32_t t + ) +{ + pgm_assert (sizeof(int) >= 4); + return ( ((t) - (s)) & PGM_UINT32_SIGN_BIT ); +} + +static inline +bool +pgm_uint32_gte ( + const uint32_t s, + const uint32_t t + ) +{ + pgm_assert (sizeof(int) >= 4); + return ( ((s) == (t)) || ( ((t) - (s)) & PGM_UINT32_SIGN_BIT ) ); +} + +/* 64 bit */ +static inline +bool +pgm_uint64_lt ( + const uint64_t s, + const uint64_t t + ) +{ + if (sizeof(int) == 4) + { + return ( + ( ((s) - (t)) & PGM_UINT64_SIGN_BIT ) + > 0 /* need to force boolean conversion when int = 32bits */ + ); + } + else + { + pgm_assert (sizeof(int) >= 8); + return ( ((s) - (t)) & PGM_UINT64_SIGN_BIT ); + } +} + +static inline +bool +pgm_uint64_lte ( + const uint64_t s, + const uint64_t t + ) +{ + if (sizeof(int) == 4) + { + return ( + ( (s) == (t) ) + || + ( + ( ((s) - (t)) & PGM_UINT64_SIGN_BIT ) + > 0 /* need to force boolean conversion when int = 32bits */ + ) + ); + } + else + { + pgm_assert (sizeof(int) >= 8); + return ( ((s) == (t)) || ( ((s) - (t)) & PGM_UINT64_SIGN_BIT ) ); + } +} + +static inline +bool +pgm_uint64_gt ( + const uint64_t s, + const uint64_t t + ) +{ + if (sizeof(int) == 4) + { + return ( + ( ((t) - (s)) & PGM_UINT64_SIGN_BIT ) + > 0 /* need to force boolean conversion when int = 32bits */ + ); + } + else + { + pgm_assert (sizeof(int) >= 8); + return ( ((t) - (s)) & PGM_UINT64_SIGN_BIT ); + } +} + +static inline +bool +pgm_uint64_gte ( + const uint64_t s, + const uint64_t t + ) +{ + if (sizeof(int) == 4) + { + return ( + ( (s) == (t) ) + || + ( + ( ((t) - (s)) & PGM_UINT64_SIGN_BIT ) + > 0 /* need to force boolean conversion when int = 32bits */ + ) + ); + } + else + { + pgm_assert (sizeof(int) >= 8); + return ( ((s) == (t)) || ( ((t) - (s)) & PGM_UINT64_SIGN_BIT ) ); + } +} + +PGM_END_DECLS + +#endif /* __PGM_IMPL_SN_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/sockaddr.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/sockaddr.h new file mode 100644 index 0000000..cea84c9 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/sockaddr.h @@ -0,0 +1,105 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * struct sockaddr functions independent of in or in6. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_SOCKADDR_H__ +#define __PGM_IMPL_SOCKADDR_H__ + +#ifndef _WIN32 +# include +#endif +#include + +PGM_BEGIN_DECLS + +/* fallback values where not directly supported */ +#ifndef MSG_DONTWAIT +# define MSG_DONTWAIT 0 +#endif +#ifndef MSG_ERRQUEUE +# define MSG_ERRQUEUE 0x2000 +#endif +#if !defined(EAFNOSUPPORT) && defined(WSAEAFNOSUPPORT) +# define EAFNOSUPPORT WSAEAFNOSUPPORT +#endif + +#ifndef _WIN32 +# define PGM_INVALID_SOCKET -1 +# define PGM_SOCKET_ERROR -1 +# define pgm_closesocket close +# define pgm_sock_errno() (errno) +# define pgm_sock_strerror(e) strerror(e) +# define pgm_error_from_sock_errno pgm_error_from_errno +#else +# define PGM_INVALID_SOCKET INVALID_SOCKET +# define PGM_SOCKET_ERROR SOCKET_ERROR +# define pgm_closesocket closesocket +# define pgm_sock_errno() WSAGetLastError() +# define pgm_sock_strerror(e) pgm_wsastrerror(e) +# define pgm_error_from_sock_errno pgm_error_from_wsa_errno +#endif + +PGM_GNUC_INTERNAL sa_family_t pgm_sockaddr_family (const struct sockaddr* sa); +PGM_GNUC_INTERNAL uint16_t pgm_sockaddr_port (const struct sockaddr* sa); +PGM_GNUC_INTERNAL socklen_t pgm_sockaddr_len (const struct sockaddr* sa); +PGM_GNUC_INTERNAL socklen_t pgm_sockaddr_storage_len (const struct sockaddr_storage* ss); +PGM_GNUC_INTERNAL uint32_t pgm_sockaddr_scope_id (const struct sockaddr* sa); +PGM_GNUC_INTERNAL int pgm_sockaddr_ntop (const struct sockaddr*restrict sa, char*restrict dst, size_t ulen); +PGM_GNUC_INTERNAL int pgm_sockaddr_pton (const char*restrict src, struct sockaddr*restrict dst); +PGM_GNUC_INTERNAL int pgm_sockaddr_is_addr_multicast (const struct sockaddr* sa); +PGM_GNUC_INTERNAL int pgm_sockaddr_is_addr_unspecified (const struct sockaddr* sa); +PGM_GNUC_INTERNAL int pgm_sockaddr_cmp (const struct sockaddr*restrict sa1, const struct sockaddr*restrict sa2); +PGM_GNUC_INTERNAL int pgm_sockaddr_hdrincl (const int s, const sa_family_t sa_family, const bool v); +PGM_GNUC_INTERNAL int pgm_sockaddr_pktinfo (const int s, const sa_family_t sa_family, const bool v); +PGM_GNUC_INTERNAL int pgm_sockaddr_router_alert (const int s, const sa_family_t sa_family, const bool v); +PGM_GNUC_INTERNAL int pgm_sockaddr_tos (const int s, const sa_family_t sa_family, const int tos); +PGM_GNUC_INTERNAL int pgm_sockaddr_join_group (const int s, const sa_family_t sa_family, const struct group_req* gr); +PGM_GNUC_INTERNAL int pgm_sockaddr_leave_group (const int s, const sa_family_t sa_family, const struct group_req* gr); +PGM_GNUC_INTERNAL int pgm_sockaddr_block_source (const int s, const sa_family_t sa_family, const struct group_source_req* gsr); +PGM_GNUC_INTERNAL int pgm_sockaddr_unblock_source (const int s, const sa_family_t sa_family, const struct group_source_req* gsr); +PGM_GNUC_INTERNAL int pgm_sockaddr_join_source_group (const int s, const sa_family_t sa_family, const struct group_source_req* gsr); +PGM_GNUC_INTERNAL int pgm_sockaddr_leave_source_group (const int s, const sa_family_t sa_family, const struct group_source_req* gsr); +#if defined(MCAST_MSFILTER) || defined(SIOCSMSFILTER) +# ifndef GROUP_FILTER_SIZE +# define GROUP_FILTER_SIZE(numsrc) (sizeof (struct group_filter) \ + - sizeof (struct sockaddr_storage) \ + + ((numsrc) \ + * sizeof (struct sockaddr_storage))) +# endif +PGM_GNUC_INTERNAL int pgm_sockaddr_msfilter (const int s, const sa_family_t sa_family, const struct group_filter* gf_list); +#endif +PGM_GNUC_INTERNAL int pgm_sockaddr_multicast_if (int s, const struct sockaddr* address, unsigned ifindex); +PGM_GNUC_INTERNAL int pgm_sockaddr_multicast_loop (const int s, const sa_family_t sa_family, const bool v); +PGM_GNUC_INTERNAL int pgm_sockaddr_multicast_hops (const int s, const sa_family_t sa_family, const unsigned hops); +PGM_GNUC_INTERNAL void pgm_sockaddr_nonblocking (const int s, const bool v); + +PGM_GNUC_INTERNAL const char* pgm_inet_ntop (int af, const void*restrict src, char*restrict dst, socklen_t size); +PGM_GNUC_INTERNAL int pgm_inet_pton (int af, const char*restrict src, void*restrict dst); + +PGM_GNUC_INTERNAL int pgm_nla_to_sockaddr (const void*restrict nla, struct sockaddr*restrict sa); +PGM_GNUC_INTERNAL int pgm_sockaddr_to_nla (const struct sockaddr*restrict sa, void*restrict nla); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_SOCKADDR_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/socket.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/socket.h new file mode 100644 index 0000000..ee175d8 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/socket.h @@ -0,0 +1,182 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM socket. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_SOCKET_H__ +#define __PGM_IMPL_SOCKET_H__ + +struct pgm_sock_t; + +#include +#include +#include + +PGM_BEGIN_DECLS + +#ifndef IP_MAX_MEMBERSHIPS +# define IP_MAX_MEMBERSHIPS 20 +#endif + +struct pgm_sock_t { + sa_family_t family; /* communications domain */ + int socket_type; + int protocol; + pgm_tsi_t tsi; + uint16_t dport; + uint16_t udp_encap_ucast_port; + uint16_t udp_encap_mcast_port; + uint32_t rand_node_id; /* node identifier */ + + pgm_rwlock_t lock; /* running / destroyed */ + pgm_mutex_t receiver_mutex; /* receiver API */ + pgm_mutex_t source_mutex; /* source API */ + pgm_spinlock_t txw_spinlock; /* transmit window */ + pgm_mutex_t send_mutex; /* non-router alert socket */ + pgm_mutex_t timer_mutex; /* next timer expiration */ + + bool is_bound; + bool is_connected; + bool is_destroyed; + bool is_reset; + bool is_abort_on_reset; + + bool can_send_data; /* and SPMs */ + bool can_send_nak; /* muted receiver */ + bool can_recv_data; /* send-only */ + bool is_edge_triggered_recv; + bool is_nonblocking; + + struct group_source_req send_gsr; /* multicast */ + struct sockaddr_storage send_addr; /* unicast nla */ + int send_sock; + int send_with_router_alert_sock; + struct group_source_req recv_gsr[IP_MAX_MEMBERSHIPS]; /* sa_family = 0 terminated */ + unsigned recv_gsr_len; + int recv_sock; + + size_t max_apdu; + uint16_t max_tpdu; + uint16_t max_tsdu; /* excluding optional var_pktlen word */ + uint16_t max_tsdu_fragment; + size_t iphdr_len; + bool use_multicast_loop; /* and reuseaddr for UDP encapsulation */ + unsigned hops; + unsigned txw_sqns, txw_secs; + unsigned rxw_sqns, rxw_secs; + ssize_t txw_max_rte, rxw_max_rte; + size_t sndbuf, rcvbuf; /* setsockopt (SO_SNDBUF/SO_RCVBUF) */ + + pgm_txw_t* restrict window; + pgm_rate_t rate_control; + pgm_time_t adv_ivl; /* advancing with data */ + unsigned adv_mode; /* 0 = time, 1 = data */ + bool is_controlled_spm; + bool is_controlled_odata; + bool is_controlled_rdata; + + bool use_cr; /* congestion reports */ + bool use_pgmcc; /* congestion control */ + bool is_pending_crqst; + unsigned ack_c; /* constant C */ + unsigned ack_c_p; /* constant Cᵨ */ + uint32_t ssthresh; /* slow-start threshold */ + uint32_t tokens; + uint32_t cwnd_size; /* congestion window size */ + uint32_t ack_rx_max; + uint32_t ack_bitmap; + uint32_t acks_after_loss; + uint32_t suspended_sqn; + bool is_congested; + pgm_time_t ack_expiry; + pgm_time_t ack_expiry_ivl; + pgm_time_t next_crqst; + pgm_time_t crqst_ivl; + pgm_time_t ack_bo_ivl; + struct sockaddr_storage acker_nla; + uint64_t acker_loss; + + pgm_notify_t ack_notify; + pgm_notify_t rdata_notify; + + pgm_hash_t last_hash_key; + void* restrict last_hash_value; + unsigned last_commit; + size_t blocklen; /* length of buffer blocked */ + bool is_apdu_eagain; /* writer-lock on window_lock exists as send would block */ + bool is_spm_eagain; /* writer-lock in receiver */ + + struct { + size_t data_pkt_offset; + size_t data_bytes_offset; + uint32_t first_sqn; + struct pgm_sk_buff_t* skb; /* references external buffer */ + size_t tsdu_length; + uint32_t unfolded_odata; + size_t apdu_length; + unsigned vector_index; + size_t vector_offset; + bool is_rate_limited; + } pkt_dontwait_state; + + uint32_t spm_sqn; + unsigned spm_ambient_interval; /* microseconds */ + unsigned* restrict spm_heartbeat_interval; /* zero terminated, zero lead-pad */ + unsigned spm_heartbeat_state; /* indexof spm_heartbeat_interval */ + unsigned spm_heartbeat_len; + unsigned peer_expiry; /* from absence of SPMs */ + unsigned spmr_expiry; /* waiting for peer SPMRs */ + + pgm_rand_t rand_; /* for calculating nak_rb_ivl from nak_bo_ivl */ + unsigned nak_data_retries, nak_ncf_retries; + pgm_time_t nak_bo_ivl, nak_rpt_ivl, nak_rdata_ivl; + pgm_time_t next_heartbeat_spm, next_ambient_spm; + + bool use_proactive_parity; + bool use_ondemand_parity; + bool use_var_pktlen; + uint8_t rs_n; + uint8_t rs_k; + uint8_t rs_proactive_h; /* 0 <= proactive-h <= ( n - k ) */ + uint8_t tg_sqn_shift; + struct pgm_sk_buff_t* restrict rx_buffer; + + pgm_rwlock_t peers_lock; + pgm_hashtable_t* restrict peers_hashtable; /* fast lookup */ + pgm_list_t* restrict peers_list; /* easy iteration */ + pgm_slist_t* restrict peers_pending; /* rxw: have or lost data */ + pgm_notify_t pending_notify; /* timer to rx */ + bool is_pending_read; + pgm_time_t next_poll; + + uint32_t cumulative_stats[PGM_PC_SOURCE_MAX]; + uint32_t snap_stats[PGM_PC_SOURCE_MAX]; + pgm_time_t snap_time; +}; + + +/* global variables */ +extern pgm_rwlock_t pgm_sock_list_lock; +extern pgm_slist_t* pgm_sock_list; + +size_t pgm_pkt_offset (bool, sa_family_t); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_SOCKET_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/source.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/source.h new file mode 100644 index 0000000..dae417a --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/source.h @@ -0,0 +1,75 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM source socket. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_SOURCE_H__ +#define __PGM_IMPL_SOURCE_H__ + +#include +#include + +PGM_BEGIN_DECLS + +/* Performance Counters */ +enum { + PGM_PC_SOURCE_DATA_BYTES_SENT, + PGM_PC_SOURCE_DATA_MSGS_SENT, /* msgs = packets not APDUs */ +/* PGM_PC_SOURCE_BYTES_BUFFERED, */ /* tx window contents in bytes */ +/* PGM_PC_SOURCE_MSGS_BUFFERED, */ + PGM_PC_SOURCE_BYTES_SENT, +/* PGM_PC_SOURCE_RAW_NAKS_RECEIVED, */ + PGM_PC_SOURCE_CKSUM_ERRORS, + PGM_PC_SOURCE_MALFORMED_NAKS, + PGM_PC_SOURCE_PACKETS_DISCARDED, + PGM_PC_SOURCE_PARITY_BYTES_RETRANSMITTED, + PGM_PC_SOURCE_SELECTIVE_BYTES_RETRANSMITTED, + PGM_PC_SOURCE_PARITY_MSGS_RETRANSMITTED, + PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED, + PGM_PC_SOURCE_PARITY_NAK_PACKETS_RECEIVED, + PGM_PC_SOURCE_SELECTIVE_NAK_PACKETS_RECEIVED, /* total packets */ + PGM_PC_SOURCE_PARITY_NAKS_RECEIVED, + PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED, /* serial numbers */ + PGM_PC_SOURCE_PARITY_NAKS_IGNORED, + PGM_PC_SOURCE_SELECTIVE_NAKS_IGNORED, + PGM_PC_SOURCE_ACK_ERRORS, +/* PGM_PC_SOURCE_PGMCC_ACKER, */ + PGM_PC_SOURCE_TRANSMISSION_CURRENT_RATE, + PGM_PC_SOURCE_ACK_PACKETS_RECEIVED, + PGM_PC_SOURCE_PARITY_NNAK_PACKETS_RECEIVED, + PGM_PC_SOURCE_SELECTIVE_NNAK_PACKETS_RECEIVED, + PGM_PC_SOURCE_PARITY_NNAKS_RECEIVED, + PGM_PC_SOURCE_SELECTIVE_NNAKS_RECEIVED, + PGM_PC_SOURCE_NNAK_ERRORS, + +/* marker */ + PGM_PC_SOURCE_MAX +}; + +PGM_GNUC_INTERNAL bool pgm_send_spm (pgm_sock_t*, int) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_on_deferred_nak (pgm_sock_t*const); +PGM_GNUC_INTERNAL bool pgm_on_spmr (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_on_nak (pgm_sock_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_on_nnak (pgm_sock_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_on_ack (pgm_sock_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; + +PGM_END_DECLS + +#endif /* __PGM_IMPL_SOURCE_H__ */ + diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/sqn_list.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/sqn_list.h new file mode 100644 index 0000000..4d216ae --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/sqn_list.h @@ -0,0 +1,38 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM sequence list. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_SQN_LIST_H__ +#define __PGM_IMPL_SQN_LIST_H__ + +struct pgm_sqn_list_t; + +#include + +PGM_BEGIN_DECLS + +struct pgm_sqn_list_t { + uint8_t len; + uint32_t sqn[63]; /* list of sequence numbers */ +}; + +PGM_END_DECLS + +#endif /* __PGM_IMPL_SQN_LIST_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/string.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/string.h new file mode 100644 index 0000000..8357c2a --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/string.h @@ -0,0 +1,59 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable string manipulation functions. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_STRING_H__ +#define __PGM_IMPL_STRING_H__ + +typedef struct pgm_string_t pgm_string_t; + +#include +#include + +PGM_BEGIN_DECLS + +struct pgm_string_t { + char* str; + size_t len; + size_t allocated_len; +}; + +PGM_GNUC_INTERNAL char* pgm_strdup (const char*) PGM_GNUC_MALLOC; +PGM_GNUC_INTERNAL int pgm_printf_string_upper_bound (const char*, va_list) PGM_GNUC_PRINTF(1, 0); +PGM_GNUC_INTERNAL int pgm_vasprintf (char**restrict, char const*restrict, va_list args) PGM_GNUC_PRINTF(2, 0); +PGM_GNUC_INTERNAL char* pgm_strdup_vprintf (const char*, va_list) PGM_GNUC_PRINTF(1, 0) PGM_GNUC_MALLOC; +PGM_GNUC_INTERNAL char* pgm_strconcat (const char*, ...) PGM_GNUC_MALLOC PGM_GNUC_NULL_TERMINATED; +PGM_GNUC_INTERNAL char** pgm_strsplit (const char*restrict, const char*restrict, int) PGM_GNUC_MALLOC; +PGM_GNUC_INTERNAL void pgm_strfreev (char**); + +PGM_GNUC_INTERNAL pgm_string_t* pgm_string_new (const char*); +PGM_GNUC_INTERNAL char* pgm_string_free (pgm_string_t*, bool); +PGM_GNUC_INTERNAL void pgm_string_printf (pgm_string_t*restrict, const char*restrict, ...) PGM_GNUC_PRINTF(2, 3); +PGM_GNUC_INTERNAL pgm_string_t* pgm_string_append (pgm_string_t*restrict, const char*restrict); +PGM_GNUC_INTERNAL pgm_string_t* pgm_string_append_c (pgm_string_t*, char); +PGM_GNUC_INTERNAL void pgm_string_append_printf (pgm_string_t*restrict, const char*restrict, ...) PGM_GNUC_PRINTF(2, 3); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_STRING_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/thread.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/thread.h new file mode 100644 index 0000000..e1484d2 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/thread.h @@ -0,0 +1,210 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * mutexes and locks. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_THREAD_H__ +#define __PGM_IMPL_THREAD_H__ + +typedef struct pgm_mutex_t pgm_mutex_t; +typedef struct pgm_spinlock_t pgm_spinlock_t; +typedef struct pgm_cond_t pgm_cond_t; +typedef struct pgm_rwlock_t pgm_rwlock_t; + +#ifndef _WIN32 +# include +# include +#else +# define WIN32_LEAN_AND_MEAN +# include +#endif +#include + +PGM_BEGIN_DECLS + +struct pgm_mutex_t { +#ifndef _WIN32 + pthread_mutex_t pthread_mutex; +#else + HANDLE win32_mutex; +#endif /* !_WIN32 */ +}; + +struct pgm_spinlock_t { +#ifndef _WIN32 + pthread_spinlock_t pthread_spinlock; +#else + CRITICAL_SECTION win32_spinlock; +#endif +}; + +struct pgm_cond_t { +#ifndef _WIN32 + pthread_cond_t pthread_cond; +#elif defined(CONFIG_HAVE_WIN_COND) + CONDITION_VARIABLE win32_cond; +#else + CRITICAL_SECTION win32_spinlock; + size_t len; + size_t allocated_len; + HANDLE* phandle; +#endif /* !_WIN32 */ +}; + +struct pgm_rwlock_t { +#ifndef _WIN32 + pthread_rwlock_t pthread_rwlock; +#elif CONFIG_HAVE_WIN_SRW_LOCK + SRWLOCK win32_lock; + pthread_rwlock_t pthread_rwlock; +#else + CRITICAL_SECTION win32_spinlock; + pgm_cond_t read_cond; + pgm_cond_t write_cond; + unsigned read_counter; + bool have_writer; + unsigned want_to_read; + unsigned want_to_write; +#endif /* !CONFIG_HAVE_WIN_SRW_LOCK */ +}; + +PGM_GNUC_INTERNAL void pgm_mutex_init (pgm_mutex_t*); +PGM_GNUC_INTERNAL void pgm_mutex_free (pgm_mutex_t*); +PGM_GNUC_INTERNAL bool pgm_mutex_trylock (pgm_mutex_t*); + +static inline void pgm_mutex_lock (pgm_mutex_t* mutex) { +#ifndef _WIN32 + pthread_mutex_lock (&mutex->pthread_mutex); +#else + WaitForSingleObject (mutex->win32_mutex, INFINITE); +#endif /* !_WIN32 */ +} + +static inline void pgm_mutex_unlock (pgm_mutex_t* mutex) { +#ifndef _WIN32 + pthread_mutex_unlock (&mutex->pthread_mutex); +#else + ReleaseMutex (mutex->win32_mutex); +#endif /* !_WIN32 */ +} + +PGM_GNUC_INTERNAL void pgm_spinlock_init (pgm_spinlock_t*); +PGM_GNUC_INTERNAL void pgm_spinlock_free (pgm_spinlock_t*); +PGM_GNUC_INTERNAL bool pgm_spinlock_trylock (pgm_spinlock_t*); + +static inline void pgm_spinlock_lock (pgm_spinlock_t* spinlock) { +#ifndef _WIN32 + pthread_spin_lock (&spinlock->pthread_spinlock); +#else + EnterCriticalSection (&spinlock->win32_spinlock); +#endif /* !_WIN32 */ +} + +static inline void pgm_spinlock_unlock (pgm_spinlock_t* spinlock) { +#ifndef _WIN32 + pthread_spin_unlock (&spinlock->pthread_spinlock); +#else + LeaveCriticalSection (&spinlock->win32_spinlock); +#endif /* !_WIN32 */ +} + +PGM_GNUC_INTERNAL void pgm_cond_init (pgm_cond_t*); +PGM_GNUC_INTERNAL void pgm_cond_signal (pgm_cond_t*); +PGM_GNUC_INTERNAL void pgm_cond_broadcast (pgm_cond_t*); +#ifndef _WIN32 +PGM_GNUC_INTERNAL void pgm_cond_wait (pgm_cond_t*, pthread_mutex_t*); +#else +PGM_GNUC_INTERNAL void pgm_cond_wait (pgm_cond_t*, CRITICAL_SECTION*); +#endif +PGM_GNUC_INTERNAL void pgm_cond_free (pgm_cond_t*); + +#ifndef _WIN32 +static inline void pgm_rwlock_reader_lock (pgm_rwlock_t* rwlock) { + pthread_rwlock_rdlock (&rwlock->pthread_rwlock); +} +static inline bool pgm_rwlock_reader_trylock (pgm_rwlock_t* rwlock) { + return !pthread_rwlock_tryrdlock (&rwlock->pthread_rwlock); +} +static inline void pgm_rwlock_reader_unlock(pgm_rwlock_t* rwlock) { + pthread_rwlock_unlock (&rwlock->pthread_rwlock); +} +static inline void pgm_rwlock_writer_lock (pgm_rwlock_t* rwlock) { + pthread_rwlock_wrlock (&rwlock->pthread_rwlock); +} +static inline bool pgm_rwlock_writer_trylock (pgm_rwlock_t* rwlock) { + return !pthread_rwlock_trywrlock (&rwlock->pthread_rwlock); +} +static inline void pgm_rwlock_writer_unlock (pgm_rwlock_t* rwlock) { + pthread_rwlock_unlock (&rwlock->pthread_rwlock); +} +#elif defined(CONFIG_HAVE_WIN_SRW_LOCK) +static inline void pgm_rwlock_reader_lock (pgm_rwlock_t* rwlock) { + AcquireSRWLockShared (&rwlock->win32_lock); +} +static inline bool pgm_rwlock_reader_trylock (pgm_rwlock_t* rwlock) { + return TryAcquireSRWLockShared (&rwlock->win32_lock); +} +static inline void pgm_rwlock_reader_unlock(pgm_rwlock_t* rwlock) { + ReleaseSRWLockShared (&rwlock->win32_lock); +} +static inline void pgm_rwlock_writer_lock (pgm_rwlock_t* rwlock) { + AcquireSRWLockExclusive (&rwlock->win32_lock); +} +static inline bool pgm_rwlock_writer_trylock (pgm_rwlock_t* rwlock) { + return AcquireSRWLockExclusive (&rwlock->win32_lock); +} +static inline void pgm_rwlock_writer_unlock (pgm_rwlock_t* rwlock) { + ReleaseSRWLockExclusive (&rwlock->win32_lock); +} +#else +PGM_GNUC_INTERNAL void pgm_rwlock_init (pgm_rwlock_t*); +PGM_GNUC_INTERNAL void pgm_rwlock_free (pgm_rwlock_t*); +PGM_GNUC_INTERNAL void pgm_rwlock_reader_lock (pgm_rwlock_t*); +PGM_GNUC_INTERNAL bool pgm_rwlock_reader_trylock (pgm_rwlock_t*); +PGM_GNUC_INTERNAL void pgm_rwlock_reader_unlock(pgm_rwlock_t*); +PGM_GNUC_INTERNAL void pgm_rwlock_writer_lock (pgm_rwlock_t*); +PGM_GNUC_INTERNAL bool pgm_rwlock_writer_trylock (pgm_rwlock_t*); +PGM_GNUC_INTERNAL void pgm_rwlock_writer_unlock (pgm_rwlock_t*); +#endif + +PGM_GNUC_INTERNAL void pgm_thread_init (void); +PGM_GNUC_INTERNAL void pgm_thread_shutdown (void); + +static inline +void +pgm_thread_yield (void) +{ +#ifndef _WIN32 +# ifdef _POSIX_PRIORITY_SCHEDULING + sched_yield(); +# else + pthread_yield(); /* requires _GNU */ +# endif +#else + Sleep (0); /* If you specify 0 milliseconds, the thread will relinquish + * the remainder of its time slice but remain ready. + */ +#endif /* _WIN32 */ +} + +PGM_END_DECLS + +#endif /* __PGM_IMPL_THREAD_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/time.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/time.h new file mode 100644 index 0000000..70c0d37 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/time.h @@ -0,0 +1,51 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * high resolution timers. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_TIME_H__ +#define __PGM_IMPL_TIME_H__ + +#include +#include +#include + +PGM_BEGIN_DECLS + +typedef pgm_time_t (*pgm_time_update_func)(void); + +#define pgm_time_after(a,b) ( (a) > (b) ) +#define pgm_time_before(a,b) ( pgm_time_after((b),(a)) ) + +#define pgm_time_after_eq(a,b) ( (a) >= (b) ) +#define pgm_time_before_eq(a,b) ( pgm_time_after_eq((b),(a)) ) + +extern pgm_time_update_func pgm_time_update_now; + +PGM_GNUC_INTERNAL bool pgm_time_init (pgm_error_t**) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_time_shutdown (void); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_TIME_H__ */ + diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/timer.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/timer.h new file mode 100644 index 0000000..4f900e4 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/timer.h @@ -0,0 +1,58 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM timer thread. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_TIMER_H__ +#define __PGM_IMPL_TIMER_H__ + +#include +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL bool pgm_timer_prepare (pgm_sock_t*const); +PGM_GNUC_INTERNAL bool pgm_timer_check (pgm_sock_t*const); +PGM_GNUC_INTERNAL pgm_time_t pgm_timer_expiration (pgm_sock_t*const); +PGM_GNUC_INTERNAL bool pgm_timer_dispatch (pgm_sock_t*const); + +static inline +void +pgm_timer_lock ( + pgm_sock_t* const sock + ) +{ + if (sock->can_send_data) + pgm_mutex_lock (&sock->timer_mutex); +} + +static inline +void +pgm_timer_unlock ( + pgm_sock_t* const sock + ) +{ + if (sock->can_send_data) + pgm_mutex_unlock (&sock->timer_mutex); +} + +PGM_END_DECLS + +#endif /* __PGM_IMPL_TIMER_H__ */ + diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/tsi.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/tsi.h new file mode 100644 index 0000000..be93e62 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/tsi.h @@ -0,0 +1,39 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * transport session ID helper functions + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_TSI_H__ +#define __PGM_IMPL_TSI_H__ + +#include +#include +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL pgm_hash_t pgm_tsi_hash (const void*) PGM_GNUC_WARN_UNUSED_RESULT; + +PGM_END_DECLS + +#endif /* __PGM_IMPL_TSI_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/txw.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/txw.h new file mode 100644 index 0000000..cf33eb5 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/txw.h @@ -0,0 +1,204 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * basic transmit window. + * + * Copyright (c) 2006 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_TXW_H__ +#define __PGM_IMPL_TXW_H__ + +typedef struct pgm_txw_state_t pgm_txw_state_t; +typedef struct pgm_txw_t pgm_txw_t; + +#include + +PGM_BEGIN_DECLS + +/* must be smaller than PGM skbuff control buffer */ +struct pgm_txw_state_t { + uint32_t unfolded_checksum; /* first 32-bit word must be checksum */ + + unsigned waiting_retransmit:1; /* in retransmit queue */ + unsigned retransmit_count:15; + unsigned nak_elimination_count:16; + + uint8_t pkt_cnt_requested; /* # parity packets to send */ + uint8_t pkt_cnt_sent; /* # parity packets already sent */ +}; + +struct pgm_txw_t { + const pgm_tsi_t* restrict tsi; + +/* option: lockless atomics */ + volatile uint32_t lead; + volatile uint32_t trail; + + pgm_queue_t retransmit_queue; + + pgm_rs_t rs; + uint8_t tg_sqn_shift; + struct pgm_sk_buff_t* restrict parity_buffer; + +/* Advance with data */ + pgm_time_t adv_ivl_expiry; + unsigned increment_window_naks; + unsigned adv_secs; /* TXW_ADV_SECS */ + unsigned adv_sqns; /* TXW_ADV_SECS in sequences */ + + unsigned is_fec_enabled:1; + unsigned adv_mode:1; /* 0 = advance by time, 1 = advance by data */ + + size_t size; /* window content size in bytes */ + unsigned alloc; /* length of pdata[] */ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + struct pgm_sk_buff_t* pdata[]; +#elif defined(__cplusplus) + struct pgm_sk_buff_t* pdata[1]; +#else + struct pgm_sk_buff_t* pdata[0]; +#endif +}; + +PGM_GNUC_INTERNAL pgm_txw_t* pgm_txw_create (const pgm_tsi_t*const, const uint16_t, const uint32_t, const unsigned, const ssize_t, const bool, const uint8_t, const uint8_t) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_txw_shutdown (pgm_txw_t*const); +PGM_GNUC_INTERNAL void pgm_txw_add (pgm_txw_t*const restrict, struct pgm_sk_buff_t*const restrict); +PGM_GNUC_INTERNAL struct pgm_sk_buff_t* pgm_txw_peek (const pgm_txw_t*const, const uint32_t) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_txw_retransmit_push (pgm_txw_t*const, const uint32_t, const bool, const uint8_t) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL struct pgm_sk_buff_t* pgm_txw_retransmit_try_peek (pgm_txw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_txw_retransmit_remove_head (pgm_txw_t*const); +PGM_GNUC_INTERNAL uint32_t pgm_txw_get_unfolded_checksum (const struct pgm_sk_buff_t*const) PGM_GNUC_PURE; +PGM_GNUC_INTERNAL void pgm_txw_set_unfolded_checksum (struct pgm_sk_buff_t*const, const uint32_t); +PGM_GNUC_INTERNAL void pgm_txw_inc_retransmit_count (struct pgm_sk_buff_t*const); +PGM_GNUC_INTERNAL bool pgm_txw_retransmit_is_empty (const pgm_txw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; + +/* declare for GCC attributes */ +static inline size_t pgm_txw_max_length (const pgm_txw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline uint32_t pgm_txw_length (const pgm_txw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline size_t pgm_txw_size (const pgm_txw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline bool pgm_txw_is_empty (const pgm_txw_t* const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline bool pgm_txw_is_full (const pgm_txw_t* const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline uint32_t pgm_txw_lead (const pgm_txw_t* const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline uint32_t pgm_txw_lead_atomic (const pgm_txw_t* const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline uint32_t pgm_txw_next_lead (const pgm_txw_t* const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline uint32_t pgm_txw_trail (const pgm_txw_t* const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline uint32_t pgm_txw_trail_atomic (const pgm_txw_t* const) PGM_GNUC_WARN_UNUSED_RESULT; + +static inline +size_t +pgm_txw_max_length ( + const pgm_txw_t*const window + ) +{ + pgm_assert (NULL != window); + return window->alloc; +} + +static inline +uint32_t +pgm_txw_length ( + const pgm_txw_t*const window + ) +{ + pgm_assert (NULL != window); + return ( 1 + window->lead ) - window->trail; +} + +static inline +size_t +pgm_txw_size ( + const pgm_txw_t*const window + ) +{ + pgm_assert (NULL != window); + return window->size; +} + +static inline +bool +pgm_txw_is_empty ( + const pgm_txw_t*const window + ) +{ + pgm_assert (NULL != window); + return (0 == pgm_txw_length (window)); +} + +static inline +bool +pgm_txw_is_full ( + const pgm_txw_t*const window + ) +{ + pgm_assert (NULL != window); + return (pgm_txw_length (window) == pgm_txw_max_length (window)); +} + +static inline +uint32_t +pgm_txw_lead ( + const pgm_txw_t*const window + ) +{ + pgm_assert (NULL != window); + return window->lead; +} + +/* atomics may rely on global variables and so cannot be defined __pure__ */ +static inline +uint32_t +pgm_txw_lead_atomic ( + const pgm_txw_t*const window + ) +{ + pgm_assert (NULL != window); + return pgm_atomic_read32 (&window->lead); +} + +static inline +uint32_t +pgm_txw_next_lead ( + const pgm_txw_t*const window + ) +{ + pgm_assert (NULL != window); + return (uint32_t)(pgm_txw_lead (window) + 1); +} + +static inline +uint32_t +pgm_txw_trail ( + const pgm_txw_t*const window + ) +{ + pgm_assert (NULL != window); + return window->trail; +} + +static inline +uint32_t +pgm_txw_trail_atomic ( + const pgm_txw_t*const window + ) +{ + pgm_assert (NULL != window); + return pgm_atomic_read32 (&window->trail); +} + +PGM_END_DECLS + +#endif /* __PGM_IMPL_TXW_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/wsastrerror.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/wsastrerror.h new file mode 100644 index 0000000..1be4ef2 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/impl/wsastrerror.h @@ -0,0 +1,37 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Winsock Error strings. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_WSASTRERROR_H__ +#define __PGM_IMPL_WSASTRERROR_H__ + +#include + +PGM_BEGIN_DECLS + +char* pgm_wsastrerror (const int); +char* pgm_adapter_strerror (const int); +char* pgm_win_strerror (char*, size_t, const int); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_WSASTRERROR_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/atomic.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/atomic.h new file mode 100644 index 0000000..15a5272 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/atomic.h @@ -0,0 +1,140 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * 32-bit atomic operations. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_ATOMIC_H__ +#define __PGM_ATOMIC_H__ + +#ifdef sun +# include +#endif +#include + +static inline +uint32_t +pgm_atomic_exchange_and_add32 ( + volatile uint32_t* atomic, + const uint32_t val + ) +{ +#if defined( __GNUC__ ) && ( defined( __i386__ ) || defined( __x86_64__ ) ) + uint32_t result; + asm volatile ( "lock\n\t" + "xaddl %0, %1" + : "=r" (result), "=m" (*atomic) + : "0" (val), "m" (*atomic) + : "memory", "cc" ); + return result; +#elif defined( __SUNPRO_C ) && (defined( __i386 ) || defined( __amd64 )) + uint32_t result = val; + asm volatile ( "lock\n\t" + "xaddl %0, %1" + :: "r" (result), "m" (*atomic) ); + return result; +#elif defined( sun ) + const uint32_t nv = atomic_add_32_nv (atomic, (int32_t)val); + return nv - val; +#elif defined( __GNUC__ ) && ( __GNUC__ * 100 + __GNUC_MINOR__ >= 401 ) + return __sync_fetch_and_add (atomic, val); +#elif defined( _WIN32 ) + return InterlockedExchangeAdd ((volatile LONG*)atomic, val); +#else +# error "No supported atomic operations for this platform." +#endif +} + +static inline +void +pgm_atomic_add32 ( + volatile uint32_t* atomic, + const uint32_t val + ) +{ +#if defined( __GNUC__ ) && ( defined( __i386__ ) || defined( __x86_64__ ) ) + asm volatile ( "lock\n\t" + "addl %1, %0" + : "=m" (*atomic) + : "ir" (val), "m" (*atomic) + : "memory", "cc" ); +#elif defined( __SUNPRO_C ) && (defined( __i386 ) || defined( __amd64 )) + asm volatile ( "lock\n\t" + "addl %1, %0" + :: "r" (val), "m" (*atomic) ); +#elif defined( sun ) + atomic_add_32 (atomic, (int32_t)val); +#elif defined( __GNUC__ ) && ( __GNUC__ * 100 + __GNUC_MINOR__ >= 401 ) + __sync_fetch_and_add (atomic, val); +#elif defined( _WIN32 ) + InterlockedExchangeAdd ((volatile LONG*)atomic, val); +#endif +} + +static inline +void +pgm_atomic_inc32 ( + volatile uint32_t* atomic + ) +{ +#if (defined( __GNUC__ ) && (defined( __i386__ ) || defined( __x86_64__ ))) || (defined( __SUNPRO_C ) && (defined( __i386 ) || defined( __amd64 ))) + pgm_atomic_add32 (atomic, 1); +#elif defined( sun ) + atomic_inc_32 (atomic); +#elif defined( __GNUC__ ) && ( __GNUC__ * 100 + __GNUC_MINOR__ >= 401 ) + pgm_atomic_add32 (atomic, 1); +#elif defined( _WIN32 ) + InterlockedIncrement ((volatile LONG*)atomic); +#endif +} + +static inline +void +pgm_atomic_dec32 ( + volatile uint32_t* atomic + ) +{ +#if (defined( __GNUC__ ) && (defined( __i386__ ) || defined( __x86_64__ ))) || (defined( __SUNPRO_C ) && (defined( __i386 ) || defined( __amd64 ))) + pgm_atomic_add32 (atomic, (uint32_t)-1); +#elif defined( sun ) + atomic_dec_32 (atomic); +#elif defined( __GNUC__ ) && ( __GNUC__ * 100 + __GNUC_MINOR__ >= 401 ) + pgm_atomic_add32 (atomic, (uint32_t)-1); +#elif defined( _WIN32 ) + InterlockedDecrement ((volatile LONG*)atomic); +#endif +} + +static inline +uint32_t +pgm_atomic_read32 ( + const volatile uint32_t* atomic + ) +{ + return *atomic; +} + +static inline +void +pgm_atomic_write32 ( + volatile uint32_t* atomic, + const uint32_t val + ) +{ + *atomic = val; +} + +#endif /* __PGM_ATOMIC_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/backtrace.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/backtrace.h new file mode 100644 index 0000000..24f8469 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/backtrace.h @@ -0,0 +1,33 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Dump back trace to stderr and try gdb. + * + * Copyright (c) 2006 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_BACKTRACE_H__ +#define __PGM_BACKTRACE_H__ + +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_NORETURN void on_sigsegv (int); + +PGM_END_DECLS + +#endif /* __PGM_BACKTRACE_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/engine.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/engine.h new file mode 100644 index 0000000..43115e8 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/engine.h @@ -0,0 +1,37 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM engine. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_ENGINE_H__ +#define __PGM_ENGINE_H__ + +#include +#include + +PGM_BEGIN_DECLS + +bool pgm_init (pgm_error_t**); +bool pgm_supported (void) PGM_GNUC_WARN_UNUSED_RESULT PGM_GNUC_PURE; +bool pgm_shutdown (void); +void pgm_drop_superuser (void); + +PGM_END_DECLS + +#endif /* __PGM_ENGINE_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/error.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/error.h new file mode 100644 index 0000000..c87755c --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/error.h @@ -0,0 +1,109 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable error reporting. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_ERROR_H__ +#define __PGM_ERROR_H__ + +typedef struct pgm_error_t pgm_error_t; + +#include + +PGM_BEGIN_DECLS + +/* error domains */ +enum +{ + PGM_ERROR_DOMAIN_IF, /* interface and host */ + PGM_ERROR_DOMAIN_PACKET, + PGM_ERROR_DOMAIN_RECV, + PGM_ERROR_DOMAIN_TIME, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_DOMAIN_ENGINE, + PGM_ERROR_DOMAIN_HTTP, + PGM_ERROR_DOMAIN_SNMP +}; + +/* error codes */ +enum +{ + /* Derived from errno, eai_errno, etc */ + PGM_ERROR_ADDRFAMILY, /* EAI_ADDRFAMILY */ + PGM_ERROR_AFNOSUPPORT, /* EAI_FAMILY */ + PGM_ERROR_AGAIN, + PGM_ERROR_BADE, /* ERROR_INVALID_DATA */ + PGM_ERROR_BADF, + PGM_ERROR_BOUNDS, /* sequence out-of-bounds */ + PGM_ERROR_CKSUM, /* pkt cksum incorrect */ + PGM_ERROR_CONNRESET, + PGM_ERROR_FAIL, /* EAI_FAIL */ + PGM_ERROR_FAULT, + PGM_ERROR_INPROGRESS, /* WSAEINPROGRESS */ + PGM_ERROR_INTR, + PGM_ERROR_INVAL, + PGM_ERROR_MFILE, + PGM_ERROR_NFILE, + PGM_ERROR_NOBUFS, /* ERROR_BUFFER_OVERFLOW */ + PGM_ERROR_NODATA, /* EAI_NODATA */ + PGM_ERROR_NODEV, + PGM_ERROR_NOENT, + PGM_ERROR_NOMEM, + PGM_ERROR_NONAME, /* EAI_NONAME */ + PGM_ERROR_NONET, + PGM_ERROR_NOPROTOOPT, + PGM_ERROR_NOSYS, /* ERROR_NOT_SUPPORTED */ + PGM_ERROR_NOTUNIQ, + PGM_ERROR_NXIO, + PGM_ERROR_PERM, + PGM_ERROR_PROCLIM, /* WSAEPROCLIM */ + PGM_ERROR_PROTO, + PGM_ERROR_RANGE, + PGM_ERROR_SERVICE, /* EAI_SERVICE */ + PGM_ERROR_SOCKTNOSUPPORT, /* EAI_SOCKTYPE */ + PGM_ERROR_SYSNOTAREADY, /* WSASYSNOTAREADY */ + PGM_ERROR_SYSTEM, /* EAI_SYSTEM */ + PGM_ERROR_VERNOTSUPPORTED, /* WSAVERNOTSUPPORTED */ + PGM_ERROR_XDEV, + + PGM_ERROR_FAILED /* generic error */ +}; + +struct pgm_error_t +{ + int domain; + int code; + char* message; +}; + +void pgm_error_free (pgm_error_t*); +void pgm_set_error (pgm_error_t**restrict, int, int, const char*restrict, ...) PGM_GNUC_PRINTF (4, 5); +void pgm_propagate_error (pgm_error_t**restrict, pgm_error_t*restrict); +void pgm_clear_error (pgm_error_t**); +void pgm_prefix_error (pgm_error_t**restrict, const char*restrict, ...) PGM_GNUC_PRINTF (2, 3); + +int pgm_error_from_errno (const int) PGM_GNUC_CONST; +int pgm_error_from_h_errno (const int) PGM_GNUC_CONST; +int pgm_error_from_eai_errno (const int, const int) PGM_GNUC_CONST; +int pgm_error_from_wsa_errno (const int) PGM_GNUC_CONST; +int pgm_error_from_win_errno (const int) PGM_GNUC_CONST; + +PGM_END_DECLS + +#endif /* __PGM_ERROR_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/gsi.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/gsi.h new file mode 100644 index 0000000..938214a --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/gsi.h @@ -0,0 +1,49 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * global session ID helper functions + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_GSI_H__ +#define __PGM_GSI_H__ + +typedef struct pgm_gsi_t pgm_gsi_t; + +#include +#include + +PGM_BEGIN_DECLS + +#define PGM_GSISTRLEN (sizeof("000.000.000.000.000.000")) +#define PGM_GSI_INIT {{ 0, 0, 0, 0, 0, 0 }} + +struct pgm_gsi_t { + uint8_t identifier[6]; +}; + +bool pgm_gsi_create_from_hostname (pgm_gsi_t*restrict, pgm_error_t**restrict); +bool pgm_gsi_create_from_addr (pgm_gsi_t*restrict, pgm_error_t**restrict); +bool pgm_gsi_create_from_data (pgm_gsi_t*restrict, const uint8_t*restrict, const size_t); +bool pgm_gsi_create_from_string (pgm_gsi_t*restrict, const char*restrict, ssize_t); +int pgm_gsi_print_r (const pgm_gsi_t*restrict, char*restrict, const size_t); +char* pgm_gsi_print (const pgm_gsi_t*); +bool pgm_gsi_equal (const void*restrict, const void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; + +PGM_END_DECLS + +#endif /* __PGM_GSI_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/http.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/http.h new file mode 100644 index 0000000..5c65d15 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/http.h @@ -0,0 +1,37 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * HTTP administrative interface + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_HTTP_H__ +#define __PGM_HTTP_H__ + +#include +#include + +PGM_BEGIN_DECLS + +#define PGM_HTTP_DEFAULT_SERVER_PORT 4968 + +bool pgm_http_init (uint16_t, pgm_error_t**) PGM_GNUC_WARN_UNUSED_RESULT; +bool pgm_http_shutdown (void); + +PGM_END_DECLS + +#endif /* __PGM_HTTP_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/if.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/if.h new file mode 100644 index 0000000..814d235 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/if.h @@ -0,0 +1,33 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * network interface handling. + * + * Copyright (c) 2006 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IF_H__ +#define __PGM_IF_H__ + +#include + +PGM_BEGIN_DECLS + +void pgm_if_print_all (void); + +PGM_END_DECLS + +#endif /* __PGM_IF_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/ip/pgm.hh b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/ip/pgm.hh new file mode 100644 index 0000000..a5e260f --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/ip/pgm.hh @@ -0,0 +1,100 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM protocol + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IP_PGM_HH__ +#define __PGM_IP_PGM_HH__ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +namespace pgm { +#define restrict +#include +} + +namespace ip { + +class pgm +{ +public: + /// The type of a PGM endpoint. + typedef pgm_endpoint endpoint; + + /// Construct to represent PGM over IPv4. + static pgm v4() + { + return pgm (PF_INET); + } + + /// Construct to represent PGM over IPv6. + static pgm v6() + { + return pgm (PF_INET6); + } + + /// Obtain an identifier for the type of the protocol. + int type() const + { + return SOCK_SEQPACKET; + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return IPPROTO_PGM; + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return family_; + } + + /// The PGM socket type. + typedef pgm_socket socket; + + /// Compare two protocols for equality. + friend bool operator== (const pgm& p1, const pgm& p2) + { + return p1.family_ == p2.family_; + } + + /// Compare two protocols for inequality. + friend bool operator!= (const pgm& p1, const pgm& p2) + { + return p1.family_ != p2.family_; + } + +private: + // Construct with a specific family. + explicit pgm (int family) + : family_ (family) + { + } + + int family_; +}; + +} // namespace ip + + +#endif /* __PGM_IP_PGM_HH__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/ip/pgm_endpoint.hh b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/ip/pgm_endpoint.hh new file mode 100644 index 0000000..1114719 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/ip/pgm_endpoint.hh @@ -0,0 +1,171 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM endpoint + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IP_PGM_ENDPOINT_HH__ +#define __PGM_IP_PGM_ENDPOINT_HH__ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +namespace pgm { +#define restrict +#include +} + +namespace ip { + +template +class pgm_endpoint +{ +public: + /// The protocol type associated with the endpoint. + typedef InternetProtocol protocol_type; + + typedef struct cpgm::pgm_sockaddr_t data_type; + + /// Default constructor. + pgm_endpoint() + : data_() + { + data_.sa_port = 0; + cpgm::pgm_tsi_t tmp_addr = PGM_TSI_INIT; + data_.sa_addr = tmp_addr; + } + + /// Construct an endpoint using a port number, specified in host byte + /// order. The GSI will be generated from the node name. + /** + * @par examples + * To initialise a PGM endpoint for port 7500, use: + * @code + * ip::pgm::endpoint ep (7500); + * @endcode + */ + pgm_endpoint (unsigned short port_num) + : data_() + { + data_.sa_port = port_num; + data_.sa_addr.sport = 0; + pgm_gsi_create_from_hostname (&data_.sa_addr.gsi, NULL); + } + + /// Construct an endpoint using a port number and a TSI. + pgm_endpoint (const cpgm::pgm_tsi_t& tsi, unsigned short port_num) + : data_() + { + data_.sa_port = port_num; + data_.sa_addr = tsi; + } + + /// Construct an endpoint using a port number and a memory area. + pgm_endpoint (const void* src, std::size_t len, unsigned short port_num) + : data_() + { + data_.sa_port = port_num; + data_.sa_addr.sport = 0; + pgm_gsi_create_from_data (&data_.sa_addr.gsi, src, len); + } + + /// Copy constructor. + pgm_endpoint (const pgm_endpoint& other) + : data_ (other.data_) + { + } + + /// Assign from another endpoint. + pgm_endpoint& operator= (const pgm_endpoint& other) + { + data_ = other.data_; + return *this; + } + + /// Get the underlying endpoint in the native type. + const data_type* data() const + { + return &data_; + } + + /// Get the underlying size of the endpoint in the native type. + std::size_t size() const + { + return sizeof(data_type); + } + + /// Get the port associated with the endpoint. The port number is always in + /// the host's byte order. + unsigned short port() const + { + return data_.sa_port; + } + + /// Set the port associated with the endpoint. The port number is always in + /// the host's byte order. + void port (unsigned short port_num) + { + data_.sa_port = port_num; + } + + /// Get the TSI associated with the endpoint. + const cpgm::pgm_tsi_t* address() const + { + return &data_.sa_addr; + } + + /// Set the TSI associated with the endpoint. + void address (cpgm::pgm_tsi_t& addr) + { + data_.sa_addr = addr; + } + + /// Compare two endpoints for equality. + friend bool operator== (const pgm_endpoint& e1, + const pgm_endpoint& e2) + { + return e1.address() == e2.address() && e1.port() == e2.port(); + } + + /// Compare two endpoints for inequality. + friend bool operator!= (const pgm_endpoint& e1, + const pgm_endpoint& e2) + { + return e1.address() != e2.address() || e1.port() != e2.port(); + } + + /// Compare endpoints for ordering. + friend bool operator<(const pgm_endpoint& e1, + const pgm_endpoint& e2) + { + if (e1.address() < e2.address()) + return true; + if (e1.address() != e2.address()) + return false; + return e1.port() < e2.port(); + } + +private: + // The underlying PGM socket address. + data_type data_; +}; + +} // namespace ip + +#endif /* __PGM_IP_PGM_ENDPOINT_HH__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/list.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/list.h new file mode 100644 index 0000000..b91abc9 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/list.h @@ -0,0 +1,40 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable doubly-linked list. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_LIST_H__ +#define __PGM_LIST_H__ + +typedef struct pgm_list_t pgm_list_t; + +#include + +PGM_BEGIN_DECLS + +struct pgm_list_t +{ + void* data; + struct pgm_list_t* next; + struct pgm_list_t* prev; +}; + +PGM_END_DECLS + +#endif /* __PGM_LIST_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/log.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/log.h new file mode 100644 index 0000000..2b6c036 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/log.h @@ -0,0 +1,33 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * basic logging. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_LOG_H__ +#define __PGM_LOG_H__ + +#include + +PGM_BEGIN_DECLS + +bool log_init (void); + +PGM_END_DECLS + +#endif /* __PGM_LOG_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/macros.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/macros.h new file mode 100644 index 0000000..8808ccb --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/macros.h @@ -0,0 +1,171 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Pre-processor macros for cross-platform, cross-compiler ice cream. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_MACROS_H__ +#define __PGM_MACROS_H__ + +/* NULL, ptrdiff_t, and size_t + */ + +#include + + +/* GCC function attributes + * http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html + */ + +#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96) + +/* No side-effects except return value, may follow pointers and read globals */ +# define PGM_GNUC_PURE __attribute__((__pure__)) + +/* Returns new memory like malloc() */ +# define PGM_GNUC_MALLOC __attribute__((__malloc__)) + +# define PGM_GNUC_CACHELINE_ALIGNED __attribute__((__aligned__(SMP_CACHE_BYTES), \ + __section__((".data.cacheline_aligned"))) +# define PGM_GNUC_READ_MOSTLY __attribute__((__section__(".data.read_mostly"))) + +#else +# define PGM_GNUC_PURE +# define PGM_GNUC_MALLOC +# define PGM_GNUC_CACHELINE_ALIGNED +# define PGM_GNUC_READ_MOSTLY +#endif + +#if (__GNUC__ >= 4) + +/* Variable argument function with NULL terminated list */ +# define PGM_GNUC_NULL_TERMINATED __attribute__((__sentinel__)) +#else +# define PGM_GNUC_NULL_TERMINATED +#endif + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) + +/* malloc() with xth parameter being size */ +# define PGM_GNUC_ALLOC_SIZE(x) __attribute__((__alloc_size__(x))) + +/* malloc() with xth*yth parameters being size */ +# define PGM_GNUC_ALLOC_SIZE2(x,y) __attribute__((__alloc_size__(x,y))) +#else +# define PGM_GNUC_ALLOC_SIZE(x) +# define PGM_GNUC_ALLOC_SIZE2(x,y) +#endif + +#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) + +/* printf() like function */ +# define PGM_GNUC_PRINTF(format, args) __attribute__((__format__ (__printf__, format, args))) +# define PGM_GNUC_FORMAT(format) __attribute__((__format_arg__ (format))) + +/* Function will never return */ +# define PGM_GNUC_NORETURN __attribute__((__noreturn__)) + +/* No side-effects except return value, must not follow pointers or read globals */ +# define PGM_GNUC_CONST __attribute__((__const__)) + +/* Unused function */ +# define PGM_GNUC_UNUSED __attribute__((__unused__)) + +#else /* !__GNUC__ */ +# define PGM_GNUC_PRINTF(format, args) +# define PGM_GNUC_NORETURN +# define PGM_GNUC_CONST +# define PGM_GNUC_UNUSED +#endif /* !__GNUC__ */ + +#if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) + +/* Raise compiler warning if caller ignores return value */ +# define PGM_GNUC_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) + +# ifdef CONFIG_HAVE_DSO_VISIBILITY +/* Hidden visibility */ +# define PGM_GNUC_INTERNAL __attribute__((visibility("hidden"))) +# else +# define PGM_GNUC_INTERNAL +# endif +#else /* !__GNUC__ */ +# define PGM_GNUC_WARN_UNUSED_RESULT +# if defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) && defined(CONFIG_HAVE_DSO_VISIBILITY) +# define PGM_GNUC_INTERNAL __hidden +# else +# define PGM_GNUC_INTERNAL +# endif +#endif /* __GNUC__ */ + + +/* Compiler time assertions, must be on unique lines in the project */ +#define PGM_PASTE_ARGS(identifier1,identifier2) identifier1 ## identifier2 +#define PGM_PASTE(identifier1,identifier2) PGM_PASTE_ARGS (identifier1, identifier2) +#define PGM_STATIC_ASSERT(expr) typedef struct { char compile_time_assertion[(expr) ? 1 : -1]; } PGM_PASTE (_pgm_static_assert_, __LINE__) + +/* Function declaration wrappers for C++ */ +#ifdef __cplusplus +# define PGM_BEGIN_DECLS extern "C" { +# define PGM_END_DECLS } +#else +# define PGM_BEGIN_DECLS +# define PGM_END_DECLS +#endif + +/* Surprisingly still not defined in C99 */ +#ifndef FALSE +# define FALSE (0) +#endif + +#ifndef TRUE +# define TRUE (!FALSE) +#endif + +#undef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#undef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +#undef ABS +#define ABS(a) (((a) < 0) ? -(a) : (a)) + +#undef CLAMP +#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) + +/* Number of elements */ +#define PGM_N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0])) + +/* Structure offsets */ +#if defined(__GNUC__) && __GNUC__ >= 4 +# define PGM_OFFSETOF(struct_type, member) (offsetof (struct_type, member)) +#else +# define PGM_OFFSETOF(struct_type, member) ((size_t)((char*)&((struct_type*) 0)->member)) +#endif + +/* Branch prediction hint */ +#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) +# define PGM_LIKELY(expr) __builtin_expect ((expr), 1) +# define PGM_UNLIKELY(expr) __builtin_expect ((expr), 0) +#else +# define PGM_LIKELY(expr) (expr) +# define PGM_UNLIKELY(expr) (expr) +#endif + +#endif /* __PGM_MACROS_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/mem.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/mem.h new file mode 100644 index 0000000..eea31fe --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/mem.h @@ -0,0 +1,60 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable fail fast memory allocation. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_MEM_H__ +#define __PGM_MEM_H__ + +#ifdef CONFIG_HAVE_ALLOCA_H +# include +#elif defined(_WIN32) +# include +#else +# include +#endif +#include + +PGM_BEGIN_DECLS + +extern bool pgm_mem_gc_friendly; + +void* pgm_malloc (const size_t) PGM_GNUC_MALLOC PGM_GNUC_ALLOC_SIZE(1); +void* pgm_malloc_n (const size_t, const size_t) PGM_GNUC_MALLOC PGM_GNUC_ALLOC_SIZE2(1, 2); +void* pgm_malloc0 (const size_t) PGM_GNUC_MALLOC PGM_GNUC_ALLOC_SIZE(1); +void* pgm_malloc0_n (const size_t, const size_t) PGM_GNUC_MALLOC PGM_GNUC_ALLOC_SIZE2(1, 2); +void* pgm_memdup (const void*, const size_t) PGM_GNUC_MALLOC; +void* pgm_realloc (void*, const size_t) PGM_GNUC_WARN_UNUSED_RESULT; +void pgm_free (void*); + +/* Convenience memory allocators that wont work well above 32-bit sizes + */ +#define pgm_new(struct_type, n_structs) \ + ((struct_type*)pgm_malloc_n ((size_t)sizeof(struct_type), (size_t)(n_structs))) +#define pgm_new0(struct_type, n_structs) \ + ((struct_type*)pgm_malloc0_n ((size_t)sizeof(struct_type), (size_t)(n_structs))) + +#define pgm_alloca(size) \ + alloca (size) +#define pgm_newa(struct_type, n_structs) \ + ((struct_type*) pgm_alloca (sizeof(struct_type) * (size_t)(n_structs))) + +PGM_END_DECLS + +#endif /* __PGM_MEM_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/messages.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/messages.h new file mode 100644 index 0000000..6a00b8f --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/messages.h @@ -0,0 +1,66 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * basic message reporting. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_MESSAGES_H__ +#define __PGM_MESSAGES_H__ + +#include + +PGM_BEGIN_DECLS + +/* Set bitmask of log roles in environmental variable PGM_LOG_MASK, + * borrowed from SmartPGM. + */ +enum { + PGM_LOG_ROLE_MEMORY = 0x001, + PGM_LOG_ROLE_NETWORK = 0x002, + PGM_LOG_ROLE_CONFIGURATION = 0x004, + PGM_LOG_ROLE_SESSION = 0x010, + PGM_LOG_ROLE_NAK = 0x020, + PGM_LOG_ROLE_RATE_CONTROL = 0x040, + PGM_LOG_ROLE_TX_WINDOW = 0x080, + PGM_LOG_ROLE_RX_WINDOW = 0x100, + PGM_LOG_ROLE_FEC = 0x400, + PGM_LOG_ROLE_CONGESTION_CONTROL = 0x800 +}; + +enum { + PGM_LOG_LEVEL_DEBUG = 0, + PGM_LOG_LEVEL_TRACE = 1, + PGM_LOG_LEVEL_MINOR = 2, + PGM_LOG_LEVEL_NORMAL = 3, + PGM_LOG_LEVEL_WARNING = 4, + PGM_LOG_LEVEL_ERROR = 5, + PGM_LOG_LEVEL_FATAL = 6 +}; + +extern int pgm_log_mask; +extern int pgm_min_log_level; + +typedef void (*pgm_log_func_t) (const int, const char*restrict, void*restrict); + +pgm_log_func_t pgm_log_set_handler (pgm_log_func_t, void*); +void pgm_messages_init (void); +void pgm_messages_shutdown (void); + +PGM_END_DECLS + +#endif /* __PGM_MESSAGES_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/msgv.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/msgv.h new file mode 100644 index 0000000..f5effcb --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/msgv.h @@ -0,0 +1,54 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Vector message container + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_MSGV_H__ +#define __PGM_MSGV_H__ + +struct pgm_iovec; +struct pgm_msgv_t; + +#include +#include +#include + +PGM_BEGIN_DECLS + +/* struct for scatter/gather I/O */ +struct pgm_iovec { +#ifndef _WIN32 +/* match struct iovec */ + void* iov_base; + size_t iov_len; /* size of iov_base */ +#else +/* match WSABUF */ + u_long iov_len; + char* iov_base; +#endif /* _WIN32 */ +}; + +struct pgm_msgv_t { + uint32_t msgv_len; /* number of elements in skb */ + struct pgm_sk_buff_t* msgv_skb[PGM_MAX_FRAGMENTS]; /* PGM socket buffer array */ +}; + +PGM_END_DECLS + +#endif /* __PGM_MSGV_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/packet.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/packet.h new file mode 100644 index 0000000..b1bb639 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/packet.h @@ -0,0 +1,472 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM packet formats, RFC 3208. + * + * Copyright (c) 2006 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_PACKET_H__ +#define __PGM_PACKET_H__ + +#ifndef _WIN32 +# include +# include +# include +#endif +#include + +PGM_BEGIN_DECLS + +/* protocol number assigned by IANA */ +#ifndef IPPROTO_PGM +# define IPPROTO_PGM 113 +#endif + +/* read from /etc/protocols if available */ +extern int pgm_ipproto_pgm; + + +/* address family indicator, rfc 1700 (ADDRESS FAMILY NUMBERS) */ +#ifndef AFI_IP +# define AFI_IP 1 /* IP (IP version 4) */ +# define AFI_IP6 2 /* IP6 (IP version 6) */ +#endif + +/* UDP ports for UDP encapsulation, as per IBM WebSphere MQ */ +#define DEFAULT_UDP_ENCAP_UCAST_PORT 3055 +#define DEFAULT_UDP_ENCAP_MCAST_PORT 3056 + +/* PGM default ports */ +#define DEFAULT_DATA_DESTINATION_PORT 7500 +#define DEFAULT_DATA_SOURCE_PORT 0 /* random */ + +/* DoS limitation to protocol (MS08-036, KB950762) */ +#ifndef PGM_MAX_APDU +# define PGM_MAX_APDU UINT16_MAX +#endif + +/* Cisco default: 24 (max 8200), Juniper & H3C default: 16, SmartPGM: 64 */ +#ifndef PGM_MAX_FRAGMENTS +# define PGM_MAX_FRAGMENTS 16 +#endif + + +enum pgm_type_e { + PGM_SPM = 0x00, /* 8.1: source path message */ + PGM_POLL = 0x01, /* 14.7.1: poll request */ + PGM_POLR = 0x02, /* 14.7.2: poll response */ + PGM_ODATA = 0x04, /* 8.2: original data */ + PGM_RDATA = 0x05, /* 8.2: repair data */ + PGM_NAK = 0x08, /* 8.3: NAK or negative acknowledgement */ + PGM_NNAK = 0x09, /* 8.3: N-NAK or null negative acknowledgement */ + PGM_NCF = 0x0a, /* 8.3: NCF or NAK confirmation */ + PGM_SPMR = 0x0c, /* 13.6: SPM request */ + PGM_ACK = 0x0d, /* PGMCC: congestion control ACK */ + PGM_MAX = 0xff +}; + +#define PGM_OPT_LENGTH 0x00 /* options length */ +#define PGM_OPT_FRAGMENT 0x01 /* fragmentation */ +#define PGM_OPT_NAK_LIST 0x02 /* list of nak entries */ +#define PGM_OPT_JOIN 0x03 /* late joining */ +#define PGM_OPT_REDIRECT 0x07 /* redirect */ +#define PGM_OPT_SYN 0x0d /* synchronisation */ +#define PGM_OPT_FIN 0x0e /* session end */ +#define PGM_OPT_RST 0x0f /* session reset */ + +#define PGM_OPT_PARITY_PRM 0x08 /* forward error correction parameters */ +#define PGM_OPT_PARITY_GRP 0x09 /* group number */ +#define PGM_OPT_CURR_TGSIZE 0x0a /* group size */ + +#define PGM_OPT_CR 0x10 /* congestion report */ +#define PGM_OPT_CRQST 0x11 /* congestion report request */ + +#define PGM_OPT_PGMCC_DATA 0x12 +#define PGM_OPT_PGMCC_FEEDBACK 0x13 + +#define PGM_OPT_NAK_BO_IVL 0x04 /* nak back-off interval */ +#define PGM_OPT_NAK_BO_RNG 0x05 /* nak back-off range */ +#define PGM_OPT_NBR_UNREACH 0x0b /* neighbour unreachable */ +#define PGM_OPT_PATH_NLA 0x0c /* path nla */ + +#define PGM_OPT_INVALID 0x7f /* option invalidated */ + +/* byte alignment for packet memory maps */ +#if defined( __GNUC__ ) && !defined( sun ) +# pragma pack(push) +#endif +#pragma pack(1) + +/* 8. PGM header */ +struct pgm_header { + uint16_t pgm_sport; /* source port: tsi::sport or UDP port depending on direction */ + uint16_t pgm_dport; /* destination port */ + uint8_t pgm_type; /* version / packet type */ + uint8_t pgm_options; /* options */ +#define PGM_OPT_PARITY 0x80 /* parity packet */ +#define PGM_OPT_VAR_PKTLEN 0x40 /* + variable sized packets */ +#define PGM_OPT_NETWORK 0x02 /* network-significant: must be interpreted by network elements */ +#define PGM_OPT_PRESENT 0x01 /* option extension are present */ + uint16_t pgm_checksum; /* checksum */ + uint8_t pgm_gsi[6]; /* global source id */ + uint16_t pgm_tsdu_length; /* tsdu length */ + /* tpdu length = th length (header + options) + tsdu length */ +}; + +/* 8.1. Source Path Messages (SPM) */ +struct pgm_spm { + uint32_t spm_sqn; /* spm sequence number */ + uint32_t spm_trail; /* trailing edge sequence number */ + uint32_t spm_lead; /* leading edge sequence number */ + uint16_t spm_nla_afi; /* nla afi */ + uint16_t spm_reserved; /* reserved */ + struct in_addr spm_nla; /* path nla */ + /* ... option extensions */ +}; + +struct pgm_spm6 { + uint32_t spm6_sqn; /* spm sequence number */ + uint32_t spm6_trail; /* trailing edge sequence number */ + uint32_t spm6_lead; /* leading edge sequence number */ + uint16_t spm6_nla_afi; /* nla afi */ + uint16_t spm6_reserved; /* reserved */ + struct in6_addr spm6_nla; /* path nla */ + /* ... option extensions */ +}; + +/* 8.2. Data Packet */ +struct pgm_data { + uint32_t data_sqn; /* data packet sequence number */ + uint32_t data_trail; /* trailing edge sequence number */ + /* ... option extensions */ + /* ... data */ +}; + +/* 8.3. Negative Acknowledgments and Confirmations (NAK, N-NAK, & NCF) */ +struct pgm_nak { + uint32_t nak_sqn; /* requested sequence number */ + uint16_t nak_src_nla_afi; /* nla afi */ + uint16_t nak_reserved; /* reserved */ + struct in_addr nak_src_nla; /* source nla */ + uint16_t nak_grp_nla_afi; /* nla afi */ + uint16_t nak_reserved2; /* reserved */ + struct in_addr nak_grp_nla; /* multicast group nla */ + /* ... option extension */ +}; + +struct pgm_nak6 { + uint32_t nak6_sqn; /* requested sequence number */ + uint16_t nak6_src_nla_afi; /* nla afi */ + uint16_t nak6_reserved; /* reserved */ + struct in6_addr nak6_src_nla; /* source nla */ + uint16_t nak6_grp_nla_afi; /* nla afi */ + uint16_t nak6_reserved2; /* reserved */ + struct in6_addr nak6_grp_nla; /* multicast group nla */ + /* ... option extension */ +}; + +/* 9. Option header (max 16 per packet) */ +struct pgm_opt_header { + uint8_t opt_type; /* option type */ +#define PGM_OPT_MASK 0x7f +#define PGM_OPT_END 0x80 /* end of options flag */ + uint8_t opt_length; /* option length */ + uint8_t opt_reserved; +#define PGM_OP_ENCODED 0x8 /* F-bit */ +#define PGM_OPX_MASK 0x3 +#define PGM_OPX_IGNORE 0x0 /* extensibility bits */ +#define PGM_OPX_INVALIDATE 0x1 +#define PGM_OPX_DISCARD 0x2 +#define PGM_OP_ENCODED_NULL 0x80 /* U-bit */ +}; + +/* 9.1. Option extension length - OPT_LENGTH */ +struct pgm_opt_length { + uint8_t opt_type; /* include header as total length overwrites reserved/OPX bits */ + uint8_t opt_length; + uint16_t opt_total_length; /* total length of all options */ +}; + +/* 9.2. Option fragment - OPT_FRAGMENT */ +struct pgm_opt_fragment { + uint8_t opt_reserved; /* reserved */ + uint32_t opt_sqn; /* first sequence number */ + uint32_t opt_frag_off; /* offset */ + uint32_t opt_frag_len; /* length */ +}; + +/* 9.3.5. Option NAK List - OPT_NAK_LIST + * + * GNU C allows opt_sqn[0], ISO C89 requireqs opt_sqn[1], ISO C99 permits opt_sqn[] + */ +struct pgm_opt_nak_list { + uint8_t opt_reserved; /* reserved */ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + uint32_t opt_sqn[]; /* requested sequence number [62] */ +#elif defined(__cplusplus) + uint32_t opt_sqn[1]; +#else + uint32_t opt_sqn[0]; +#endif +}; + +/* 9.4.2. Option Join - OPT_JOIN */ +struct pgm_opt_join { + uint8_t opt_reserved; /* reserved */ + uint32_t opt_join_min; /* minimum sequence number */ +}; + +/* 9.5.5. Option Redirect - OPT_REDIRECT */ +struct pgm_opt_redirect { + uint8_t opt_reserved; /* reserved */ + uint16_t opt_nla_afi; /* nla afi */ + uint16_t opt_reserved2; /* reserved */ + struct in_addr opt_nla; /* dlr nla */ +}; + +struct pgm_opt6_redirect { + uint8_t opt6_reserved; /* reserved */ + uint16_t opt6_nla_afi; /* nla afi */ + uint16_t opt6_reserved2; /* reserved */ + struct in6_addr opt6_nla; /* dlr nla */ +}; + +/* 9.6.2. Option Sources - OPT_SYN */ +struct pgm_opt_syn { + uint8_t opt_reserved; /* reserved */ +}; + +/* 9.7.4. Option End Session - OPT_FIN */ +struct pgm_opt_fin { + uint8_t opt_reserved; /* reserved */ +}; + +/* 9.8.4. Option Reset - OPT_RST */ +struct pgm_opt_rst { + uint8_t opt_reserved; /* reserved */ +}; + + +/* + * Forward Error Correction - FEC + */ + +/* 11.8.1. Option Parity - OPT_PARITY_PRM */ +struct pgm_opt_parity_prm { + uint8_t opt_reserved; /* reserved */ +#define PGM_PARITY_PRM_MASK 0x3 +#define PGM_PARITY_PRM_PRO 0x1 /* source provides pro-active parity packets */ +#define PGM_PARITY_PRM_OND 0x2 /* on-demand parity packets */ + uint32_t parity_prm_tgs; /* transmission group size */ +}; + +/* 11.8.2. Option Parity Group - OPT_PARITY_GRP */ +struct pgm_opt_parity_grp { + uint8_t opt_reserved; /* reserved */ + uint32_t prm_group; /* parity group number */ +}; + +/* 11.8.3. Option Current Transmission Group Size - OPT_CURR_TGSIZE */ +struct pgm_opt_curr_tgsize { + uint8_t opt_reserved; /* reserved */ + uint32_t prm_atgsize; /* actual transmission group size */ +}; + +/* + * Congestion Control + */ + +/* 12.7.1. Option Congestion Report - OPT_CR */ +struct pgm_opt_cr { + uint8_t opt_reserved; /* reserved */ +#define PGM_OPT_CR_NEL 0x0 /* OPT_CR_NE_WL report */ +#define PGM_OPT_CR_NEP 0x1 /* OPT_CR_NE_WP report */ +#define PGM_OPT_CR_RXP 0x2 /* OPT_CR_RX_WP report */ + uint32_t opt_cr_lead; /* congestion report reference sqn */ + uint16_t opt_cr_ne_wl; /* ne worst link */ + uint16_t opt_cr_ne_wp; /* ne worst path */ + uint16_t opt_cr_rx_wp; /* rcvr worst path */ + uint16_t opt_reserved2; /* reserved */ + uint16_t opt_nla_afi; /* nla afi */ + uint16_t opt_reserved3; /* reserved */ + uint32_t opt_cr_rcvr; /* worst receivers nla */ +}; + +/* 12.7.2. Option Congestion Report Request - OPT_CRQST */ +struct pgm_opt_crqst { + uint8_t opt_reserved; /* reserved */ +#define PGM_OPT_CRQST_NEL 0x0 /* request OPT_CR_NE_WL report */ +#define PGM_OPT_CRQST_NEP 0x1 /* request OPT_CR_NE_WP report */ +#define PGM_OPT_CRQST_RXP 0x2 /* request OPT_CR_RX_WP report */ +}; + +/* PGMCC. ACK Packet */ +struct pgm_ack { + uint32_t ack_rx_max; /* RX_MAX */ + uint32_t ack_bitmap; /* received packets */ + /* ... option extensions */ +}; + +/* PGMCC Options */ +struct pgm_opt_pgmcc_data { + uint8_t opt_reserved; /* reserved */ + uint32_t opt_tstamp; /* timestamp */ + uint16_t opt_nla_afi; /* nla afi */ + uint16_t opt_reserved2; /* reserved */ + struct in_addr opt_nla; /* ACKER nla */ +}; + +struct pgm_opt6_pgmcc_data { + uint8_t opt6_reserved; /* reserved */ + uint32_t opt6_tstamp; /* timestamp */ + uint16_t opt6_nla_afi; /* nla afi */ + uint16_t opt6_reserved2; /* reserved */ + struct in6_addr opt6_nla; /* ACKER nla */ +}; + +struct pgm_opt_pgmcc_feedback { + uint8_t opt_reserved; /* reserved */ + uint32_t opt_tstamp; /* timestamp */ + uint16_t opt_nla_afi; /* nla afi */ + uint16_t opt_loss_rate; /* loss rate */ + struct in_addr opt_nla; /* ACKER nla */ +}; + +struct pgm_opt6_pgmcc_feedback { + uint8_t opt6_reserved; /* reserved */ + uint32_t opt6_tstamp; /* timestamp */ + uint16_t opt6_nla_afi; /* nla afi */ + uint16_t opt6_loss_rate; /* loss rate */ + struct in6_addr opt6_nla; /* ACKER nla */ +}; + + +/* + * SPM Requests + */ + +/* 13.6. SPM Requests */ +#if 0 +struct pgm_spmr { + /* ... option extensions */ +}; +#endif + + +/* + * Poll Mechanism + */ + +/* 14.7.1. Poll Request */ +struct pgm_poll { + uint32_t poll_sqn; /* poll sequence number */ + uint16_t poll_round; /* poll round */ + uint16_t poll_s_type; /* poll sub-type */ +#define PGM_POLL_GENERAL 0x0 /* general poll */ +#define PGM_POLL_DLR 0x1 /* DLR poll */ + uint16_t poll_nla_afi; /* nla afi */ + uint16_t poll_reserved; /* reserved */ + struct in_addr poll_nla; /* path nla */ + uint32_t poll_bo_ivl; /* poll back-off interval */ + char poll_rand[4]; /* random string */ + uint32_t poll_mask; /* matching bit-mask */ + /* ... option extensions */ +}; + +struct pgm_poll6 { + uint32_t poll6_sqn; /* poll sequence number */ + uint16_t poll6_round; /* poll round */ + uint16_t poll6_s_type; /* poll sub-type */ + uint16_t poll6_nla_afi; /* nla afi */ + uint16_t poll6_reserved; /* reserved */ + struct in6_addr poll6_nla; /* path nla */ + uint32_t poll6_bo_ivl; /* poll back-off interval */ + char poll6_rand[4]; /* random string */ + uint32_t poll6_mask; /* matching bit-mask */ + /* ... option extensions */ +}; + +/* 14.7.2. Poll Response */ +struct pgm_polr { + uint32_t polr_sqn; /* polr sequence number */ + uint16_t polr_round; /* polr round */ + uint16_t polr_reserved; /* reserved */ + /* ... option extensions */ +}; + + +/* + * Implosion Prevention + */ + +/* 15.4.1. Option NAK Back-Off Interval - OPT_NAK_BO_IVL */ +struct pgm_opt_nak_bo_ivl { + uint8_t opt_reserved; /* reserved */ + uint32_t opt_nak_bo_ivl; /* nak back-off interval */ + uint32_t opt_nak_bo_ivl_sqn; /* nak back-off interval sqn */ +}; + +/* 15.4.2. Option NAK Back-Off Range - OPT_NAK_BO_RNG */ +struct pgm_opt_nak_bo_rng { + uint8_t opt_reserved; /* reserved */ + uint32_t opt_nak_max_bo_ivl; /* maximum nak back-off interval */ + uint32_t opt_nak_min_bo_ivl; /* minimum nak back-off interval */ +}; + +/* 15.4.3. Option Neighbour Unreachable - OPT_NBR_UNREACH */ +struct pgm_opt_nbr_unreach { + uint8_t opt_reserved; /* reserved */ +}; + +/* 15.4.4. Option Path - OPT_PATH_NLA */ +struct pgm_opt_path_nla { + uint8_t opt_reserved; /* reserved */ + struct in_addr opt_path_nla; /* path nla */ +}; + +struct pgm_opt6_path_nla { + uint8_t opt6_reserved; /* reserved */ + struct in6_addr opt6_path_nla; /* path nla */ +}; + + +#if defined( __GNUC__ ) && !defined( sun ) +# pragma pack(pop) +#else +# pragma pack() +#endif + +#define PGM_IS_UPSTREAM(t) \ + ((t) == PGM_NAK /* unicast */ \ + || (t) == PGM_NNAK /* unicast */ \ + || (t) == PGM_SPMR /* multicast + unicast */ \ + || (t) == PGM_POLR /* unicast */ \ + || (t) == PGM_ACK) /* unicast */ + +#define PGM_IS_PEER(t) \ + ((t) == PGM_SPMR) /* multicast */ + +#define PGM_IS_DOWNSTREAM(t) \ + ((t) == PGM_SPM /* all types are multicast */ \ + || (t) == PGM_ODATA \ + || (t) == PGM_RDATA \ + || (t) == PGM_POLL \ + || (t) == PGM_NCF) + +PGM_END_DECLS + +#endif /* __PGM_PACKET_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm.h new file mode 100644 index 0000000..a122f48 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm.h @@ -0,0 +1,42 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * OpenPGM, an implementation of the PGM network protocol. + * + * Copyright (c) 2006-2008 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_H__ +#define __PGM_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* __PGM_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm.hh b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm.hh new file mode 100644 index 0000000..fc3476f --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm.hh @@ -0,0 +1,33 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * OpenPGM C++ wrapper. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_HH__ +#define __PGM_HH__ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include +#include + +#endif /* __PGM_HH__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm_socket.hh b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm_socket.hh new file mode 100644 index 0000000..9a3f11b --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm_socket.hh @@ -0,0 +1,157 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM socket + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_SOCKET_HH__ +#define __PGM_SOCKET_HH__ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#ifndef _WIN32 +# include +# include +#else +# include +#endif + +namespace cpgm { +#define restrict +#include +}; + +template +class pgm_socket +{ +public: + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// The native socket type. + typedef struct cpgm::pgm_sock_t* native_type; + + /// Construct a pgm_socket without opening it. + pgm_socket() + { + } + + // Open a new PGM socket implementation. + bool open (::sa_family_t family, int sock_type, int protocol, cpgm::pgm_error_t** error) + { + return cpgm::pgm_socket (&this->native_type_, family, sock_type, protocol, error); + } + + /// Close a PGM socket implementation. + bool close (bool flush) + { + return pgm_close (this->native_type_, flush); + } + + /// Get the native socket implementation. + native_type native (void) + { + return this->native_type_; + } + + // Bind the datagram socket to the specified local endpoint. + bool bind (const endpoint_type& addr, cpgm::pgm_error_t** error) + { + return pgm_bind (this->native_type_, addr.data(), sizeof(addr.data()), error); + } + + /// Connect the PGM socket to the specified endpoint. + bool connect (cpgm::pgm_error_t** error) + { + return pgm_connect (this->native_type_, error); + } + + /// Set a socket option. + bool set_option (int optname, const void* optval, ::socklen_t optlen) + { + return pgm_setsockopt (this->native_type_, optname, optval, optlen); + } + + /// Get a socket option. + bool get_option (int optname, void* optval, ::socklen_t* optlen) + { + return pgm_getsockopt (this->native_type_, optname, optval, optlen); + } + + /// Get the local endpoint. + endpoint_type local_endpoint() const + { + endpoint_type endpoint; + pgm_getsockname (this->native_type_, &endpoint); + return endpoint; + } + + /// Disable sends or receives on the socket. + bool shutdown (int what) + { + int optname, v = 1; +#ifndef _WIN32 + if (SHUT_RD == what) optname = cpgm::PGM_SEND_ONLY; + else if (SHUT_WR == what) optname = cpgm::PGM_RECV_ONLY; +#else + if (SD_RECEIVE == what) optname = cpgm::PGM_SEND_ONLY; + else if (SD_SEND == what) optname = cpgm::PGM_RECV_ONLY; +#endif + else { + errno = EINVAL; + return false; + } + return pgm_setsockopt (this->native_type_, optname, v, sizeof(v)); + } + + /// Send some data on a connected socket. + int send (const void* buf, std::size_t len, std::size_t* bytes_sent) + { + return pgm_send (this->native_type_, buf, len, bytes_sent); + } + + /// Receive some data from the peer. + int receive (void* buf, std::size_t len, int flags, std::size_t* bytes_read, cpgm::pgm_error_t** error) + { + return pgm_recv (this->native_type_, buf, len, flags, bytes_read, error); + } + + /// Receive a datagram with the endpoint of the sender. + int receive_from (void* buf, std::size_t len, int flags, std::size_t* bytes_read, endpoint_type* from, cpgm::pgm_error_t** error) + { + int ec; + struct cpgm::pgm_sockaddr_t addr; + socklen_t addrlen = sizeof (addr); + ec = pgm_recvfrom (this->native_type_, buf, len, flags, bytes_read, &addr, &addrlen, error); + from->port (addr.sa_port); + from->address (addr.sa_addr); +/* TODO: set data-destination port */ + return ec; + } + +private: + native_type native_type_; +}; + +#endif /* __PGM_SOCKET_HH__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/signal.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/signal.h new file mode 100644 index 0000000..546fb7e --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/signal.h @@ -0,0 +1,36 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Re-entrant safe signal handling. + * + * Copyright (c) 2006-2007 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_SIGNAL_H__ +#define __PGM_SIGNAL_H__ + +#include +#include + +typedef void (*pgm_sighandler_t)(int, gpointer); + +G_BEGIN_DECLS + +gboolean pgm_signal_install (int, pgm_sighandler_t, gpointer); + +G_END_DECLS + +#endif /* __PGM_SIGNAL_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/skbuff.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/skbuff.h new file mode 100644 index 0000000..0701dfb --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/skbuff.h @@ -0,0 +1,243 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM socket buffers + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_SKBUFF_H__ +#define __PGM_SKBUFF_H__ + +#include + +struct pgm_sk_buff_t; + +#include +#include +#include +#include +#include +#include +#include +#include + +PGM_BEGIN_DECLS + +struct pgm_sk_buff_t { + pgm_list_t link_; + + pgm_sock_t* restrict sock; + pgm_time_t tstamp; + pgm_tsi_t tsi; + + uint32_t sequence; + uint32_t __padding; /* push alignment of pgm_sk_buff_t::cb to 8 bytes */ + + char cb[48]; /* control buffer */ + + uint16_t len; /* actual data */ + unsigned zero_padded:1; + + struct pgm_header* pgm_header; + struct pgm_opt_fragment* pgm_opt_fragment; +#define of_apdu_first_sqn pgm_opt_fragment->opt_sqn +#define of_frag_offset pgm_opt_fragment->opt_frag_off +#define of_apdu_len pgm_opt_fragment->opt_frag_len + struct pgm_opt_pgmcc_data* pgm_opt_pgmcc_data; + struct pgm_data* pgm_data; + + void *head, /* all may-alias */ + *data, + *tail, + *end; + uint32_t truesize; + volatile uint32_t users; /* atomic */ +}; + +void pgm_skb_over_panic (const struct pgm_sk_buff_t*const, const uint16_t) PGM_GNUC_NORETURN; +void pgm_skb_under_panic (const struct pgm_sk_buff_t*const, const uint16_t) PGM_GNUC_NORETURN; +bool pgm_skb_is_valid (const struct pgm_sk_buff_t*const) PGM_GNUC_PURE PGM_GNUC_WARN_UNUSED_RESULT; + +/* attribute __pure__ only valid for platforms with atomic ops. + * attribute __malloc__ not used as only part of the memory should be aliased. + * attribute __alloc_size__ does not allow headroom. + */ +static inline struct pgm_sk_buff_t* pgm_alloc_skb (const uint16_t) PGM_GNUC_WARN_UNUSED_RESULT; + +static inline +struct pgm_sk_buff_t* +pgm_alloc_skb ( + const uint16_t size + ) +{ + struct pgm_sk_buff_t* skb; + + skb = (struct pgm_sk_buff_t*)pgm_malloc (size + sizeof(struct pgm_sk_buff_t)); + if (PGM_UNLIKELY(pgm_mem_gc_friendly)) { + memset (skb, 0, size + sizeof(struct pgm_sk_buff_t)); + skb->zero_padded = 1; + } else { + memset (skb, 0, sizeof(struct pgm_sk_buff_t)); + } + skb->truesize = size + sizeof(struct pgm_sk_buff_t); + pgm_atomic_write32 (&skb->users, 1); + skb->head = skb + 1; + skb->data = skb->tail = skb->head; + skb->end = (char*)skb->data + size; + return skb; +} + +/* increase reference count */ +static inline +struct pgm_sk_buff_t* +pgm_skb_get ( + struct pgm_sk_buff_t*const skb + ) +{ + pgm_atomic_inc32 (&skb->users); + return skb; +} + +static inline +void +pgm_free_skb ( + struct pgm_sk_buff_t*const skb + ) +{ + if (pgm_atomic_exchange_and_add32 (&skb->users, (uint32_t)-1) == 1) + pgm_free (skb); +} + +/* add data */ +static inline +void* +pgm_skb_put ( + struct pgm_sk_buff_t* const skb, + const uint16_t len + ) +{ + void* tmp = skb->tail; + skb->tail = (char*)skb->tail + len; + skb->len += len; + if (PGM_UNLIKELY(skb->tail > skb->end)) + pgm_skb_over_panic (skb, len); + return tmp; +} + +static inline +void* +__pgm_skb_pull ( + struct pgm_sk_buff_t*const skb, + const uint16_t len + ) +{ + skb->len -= len; + return skb->data = (char*)skb->data + len; +} + +/* remove data from start of buffer */ +static inline +void* +pgm_skb_pull ( + struct pgm_sk_buff_t*const skb, + const uint16_t len + ) +{ + return PGM_UNLIKELY(len > skb->len) ? NULL : __pgm_skb_pull (skb, len); +} + +static inline uint16_t pgm_skb_headroom (const struct pgm_sk_buff_t*const) PGM_GNUC_PURE PGM_GNUC_WARN_UNUSED_RESULT; +static inline uint16_t pgm_skb_tailroom (const struct pgm_sk_buff_t*const) PGM_GNUC_PURE PGM_GNUC_WARN_UNUSED_RESULT; + +static inline +uint16_t +pgm_skb_headroom ( + const struct pgm_sk_buff_t*const skb + ) +{ + return (char*)skb->data - (char*)skb->head; +} + +static inline +uint16_t +pgm_skb_tailroom ( + const struct pgm_sk_buff_t*const skb + ) +{ + return (char*)skb->end - (char*)skb->tail; +} + +/* reserve space to add data */ +static inline +void +pgm_skb_reserve ( + struct pgm_sk_buff_t*const skb, + const uint16_t len + ) +{ + skb->data = (char*)skb->data + len; + skb->tail = (char*)skb->tail + len; + if (PGM_UNLIKELY(skb->tail > skb->end)) + pgm_skb_over_panic (skb, len); + if (PGM_UNLIKELY(skb->data < skb->head)) + pgm_skb_under_panic (skb, len); +} + +static inline struct pgm_sk_buff_t* pgm_skb_copy (const struct pgm_sk_buff_t* const) PGM_GNUC_WARN_UNUSED_RESULT; + +static inline +struct pgm_sk_buff_t* +pgm_skb_copy ( + const struct pgm_sk_buff_t* const skb + ) +{ + struct pgm_sk_buff_t* newskb; + newskb = (struct pgm_sk_buff_t*)pgm_malloc (skb->truesize); + memcpy (newskb, skb, PGM_OFFSETOF(struct pgm_sk_buff_t, pgm_header)); + newskb->zero_padded = 0; + newskb->truesize = skb->truesize; + pgm_atomic_write32 (&newskb->users, 1); + newskb->head = newskb + 1; + newskb->end = (char*)newskb->head + ((char*)skb->end - (char*)skb->head); + newskb->data = (char*)newskb->head + ((char*)skb->data - (char*)skb->head); + newskb->tail = (char*)newskb->head + ((char*)skb->tail - (char*)skb->head); + newskb->pgm_header = skb->pgm_header ? (struct pgm_header*)((char*)newskb->head + ((char*)skb->pgm_header - (char*)skb->head)) : skb->pgm_header; + newskb->pgm_opt_fragment = skb->pgm_opt_fragment ? (struct pgm_opt_fragment*)((char*)newskb->head + ((char*)skb->pgm_opt_fragment - (char*)skb->head)) : skb->pgm_opt_fragment; + newskb->pgm_data = skb->pgm_data ? (struct pgm_data*)((char*)newskb->head + ((char*)skb->pgm_data - (char*)skb->head)) : skb->pgm_data; + memcpy (newskb->head, skb->head, (char*)skb->end - (char*)skb->head); + return newskb; +} + +static inline +void +pgm_skb_zero_pad ( + struct pgm_sk_buff_t* const skb, + const uint16_t len + ) +{ + if (skb->zero_padded) + return; + + const uint16_t tailroom = MIN(pgm_skb_tailroom (skb), len); + if (tailroom > 0) + memset (skb->tail, 0, tailroom); + skb->zero_padded = 1; +} + +PGM_END_DECLS + +#endif /* __PGM_SKBUFF_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/snmp.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/snmp.h new file mode 100644 index 0000000..922f4e2 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/snmp.h @@ -0,0 +1,35 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * SNMP + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_SNMP_H__ +#define __PGM_SNMP_H__ + +#include +#include + +PGM_BEGIN_DECLS + +bool pgm_snmp_init (pgm_error_t**) PGM_GNUC_WARN_UNUSED_RESULT; +bool pgm_snmp_shutdown (void); + +PGM_END_DECLS + +#endif /* __PGM_SNMP_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/socket.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/socket.h new file mode 100644 index 0000000..f3a2360 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/socket.h @@ -0,0 +1,170 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM socket. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_SOCKET_H__ +#define __PGM_SOCKET_H__ + +typedef struct pgm_sock_t pgm_sock_t; +struct pgm_sockaddr_t; +struct pgm_addrinfo_t; +struct pgm_fecinto_t; + +#ifdef CONFIG_HAVE_POLL +# include +#endif +#ifdef CONFIG_HAVE_EPOLL +# include +#endif +#ifndef _WIN32 +# include +# include +#endif +#include +#include +#include +#include + +PGM_BEGIN_DECLS + +struct pgm_sockaddr_t { + uint16_t sa_port; /* data-destination port */ + pgm_tsi_t sa_addr; +}; + +struct pgm_addrinfo_t { + sa_family_t ai_family; + uint32_t ai_recv_addrs_len; + struct group_source_req* restrict ai_recv_addrs; + uint32_t ai_send_addrs_len; + struct group_source_req* restrict ai_send_addrs; +}; + +struct pgm_interface_req_t { + uint32_t ir_interface; + uint32_t ir_scope_id; +}; + +struct pgm_fecinfo_t { + uint8_t block_size; + uint8_t proactive_packets; + uint8_t group_size; + bool ondemand_parity_enabled; + bool var_pktlen_enabled; +}; + +struct pgm_pgmccinfo_t { + uint32_t ack_bo_ivl; + uint32_t ack_c; + uint32_t ack_c_p; +}; + +/* socket options */ +enum { + PGM_RECV_SOCK, + PGM_REPAIR_SOCK, + PGM_PENDING_SOCK, + PGM_ACK_SOCK, + PGM_TIME_REMAIN, + PGM_RATE_REMAIN, + PGM_IP_ROUTER_ALERT, + PGM_MTU, + PGM_MULTICAST_LOOP, + PGM_MULTICAST_HOPS, + PGM_TOS, + PGM_SNDBUF, + PGM_RCVBUF, + PGM_AMBIENT_SPM, + PGM_HEARTBEAT_SPM, + PGM_TXW_SQNS, + PGM_TXW_SECS, + PGM_TXW_MAX_RTE, + PGM_PEER_EXPIRY, + PGM_SPMR_EXPIRY, + PGM_RXW_SQNS, + PGM_RXW_SECS, + PGM_RXW_MAX_RTE, + PGM_NAK_BO_IVL, + PGM_NAK_RPT_IVL, + PGM_NAK_RDATA_IVL, + PGM_NAK_DATA_RETRIES, + PGM_NAK_NCF_RETRIES, + PGM_USE_FEC, + PGM_USE_CR, + PGM_USE_PGMCC, + PGM_SEND_ONLY, + PGM_RECV_ONLY, + PGM_PASSIVE, + PGM_ABORT_ON_RESET, + PGM_NOBLOCK, + PGM_SEND_GROUP, + PGM_JOIN_GROUP, + PGM_LEAVE_GROUP, + PGM_BLOCK_SOURCE, + PGM_UNBLOCK_SOURCE, + PGM_JOIN_SOURCE_GROUP, + PGM_LEAVE_SOURCE_GROUP, + PGM_MSFILTER, + PGM_UDP_ENCAP_UCAST_PORT, + PGM_UDP_ENCAP_MCAST_PORT +}; + +/* IO status */ +enum { + PGM_IO_STATUS_ERROR, /* an error occurred */ + PGM_IO_STATUS_NORMAL, /* success */ + PGM_IO_STATUS_RESET, /* session reset */ + PGM_IO_STATUS_FIN, /* session finished */ + PGM_IO_STATUS_EOF, /* socket closed */ + PGM_IO_STATUS_WOULD_BLOCK, /* resource temporarily unavailable */ + PGM_IO_STATUS_RATE_LIMITED, /* would-block on rate limit, check timer */ + PGM_IO_STATUS_TIMER_PENDING, /* would-block with pending timer */ + PGM_IO_STATUS_CONGESTION /* would-block waiting on ACK or timeout */ +}; + +bool pgm_socket (pgm_sock_t**restrict, const sa_family_t, const int, const int, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; +bool pgm_bind (pgm_sock_t*restrict, const struct pgm_sockaddr_t*const restrict, const socklen_t, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; +bool pgm_bind3 (pgm_sock_t*restrict, const struct pgm_sockaddr_t*const restrict, const socklen_t, const struct pgm_interface_req_t*const, const socklen_t, const struct pgm_interface_req_t*const, const socklen_t, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; +bool pgm_connect (pgm_sock_t*restrict, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; +bool pgm_close (pgm_sock_t*, bool); +bool pgm_setsockopt (pgm_sock_t*const restrict, const int, const void*restrict, const socklen_t); +bool pgm_getsockopt (pgm_sock_t*const restrict, const int, void*restrict, socklen_t*restrict); +bool pgm_getaddrinfo (const char*restrict, const struct pgm_addrinfo_t*restrict, struct pgm_addrinfo_t**restrict, pgm_error_t**restrict); +void pgm_freeaddrinfo (struct pgm_addrinfo_t*); +int pgm_send (pgm_sock_t*const restrict, const void*restrict, const size_t, size_t*restrict); +int pgm_sendv (pgm_sock_t*const restrict, const struct pgm_iovec*const restrict, const unsigned, const bool, size_t*restrict); +int pgm_send_skbv (pgm_sock_t*const restrict, struct pgm_sk_buff_t**restrict, const unsigned, const bool, size_t*restrict); +int pgm_recvmsg (pgm_sock_t*const restrict, struct pgm_msgv_t*const restrict, const int, size_t*restrict, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; +int pgm_recvmsgv (pgm_sock_t*const restrict, struct pgm_msgv_t*const restrict, const size_t, const int, size_t*restrict, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; +int pgm_recv (pgm_sock_t*const restrict, void*restrict, const size_t, const int, size_t*const restrict, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; +int pgm_recvfrom (pgm_sock_t*const restrict, void*restrict, const size_t, const int, size_t*restrict, struct pgm_sockaddr_t*restrict, socklen_t*restrict, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; + +bool pgm_getsockname (pgm_sock_t*const restrict, struct pgm_sockaddr_t*restrict, socklen_t*restrict); +int pgm_select_info (pgm_sock_t*const restrict, fd_set*const restrict, fd_set*const restrict, int*const restrict); +#ifdef CONFIG_HAVE_POLL +int pgm_poll_info (pgm_sock_t*const restrict, struct pollfd*const restrict, int*const restrict, const int); +#endif +#ifdef CONFIG_HAVE_EPOLL +int pgm_epoll_ctl (pgm_sock_t*const, const int, const int, const int); +#endif + +PGM_END_DECLS + +#endif /* __PGM_SOCKET_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/time.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/time.h new file mode 100644 index 0000000..2fd3898 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/time.h @@ -0,0 +1,54 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * high resolution timers. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_TIME_H__ +#define __PGM_TIME_H__ + +#include + +PGM_BEGIN_DECLS + +typedef uint64_t pgm_time_t; +typedef void (*pgm_time_since_epoch_func)(const pgm_time_t*const restrict, time_t*restrict); + +#define pgm_to_secs(t) ((uint64_t)( (t) / 1000000UL )) +#define pgm_to_msecs(t) ((uint64_t)( (t) / 1000UL )) +#define pgm_to_usecs(t) ( (t) ) +#define pgm_to_nsecs(t) ((uint64_t)( (t) * 1000UL )) + +#define pgm_to_secsf(t) ( (double)(t) / 1000000.0 ) +#define pgm_to_msecsf(t) ( (double)(t) / 1000.0 ) +#define pgm_to_usecsf(t) ( (double)(t) ) +#define pgm_to_nsecsf(t) ( (double)(t) * 1000.0 ) + +#define pgm_secs(t) ((uint64_t)( (uint64_t)(t) * 1000000UL )) +#define pgm_msecs(t) ((uint64_t)( (uint64_t)(t) * 1000UL )) +#define pgm_usecs(t) ((uint64_t)( (t) )) +#define pgm_nsecs(t) ((uint64_t)( (t) / 1000UL )) + +#define PGM_TIME_FORMAT PRIu64 + +extern pgm_time_since_epoch_func pgm_time_since_epoch; + +PGM_END_DECLS + +#endif /* __PGM_TIME_H__ */ + diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/tsi.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/tsi.h new file mode 100644 index 0000000..c33ea3d --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/tsi.h @@ -0,0 +1,47 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * transport session ID helper functions + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_TSI_H__ +#define __PGM_TSI_H__ + +typedef struct pgm_tsi_t pgm_tsi_t; + +#include +#include + +PGM_BEGIN_DECLS + +/* maximum length of TSI as a string */ +#define PGM_TSISTRLEN (sizeof("000.000.000.000.000.000.00000")) +#define PGM_TSI_INIT { PGM_GSI_INIT, 0 } + +struct pgm_tsi_t { + pgm_gsi_t gsi; /* global session identifier */ + uint16_t sport; /* source port: a random number to help detect session re-starts */ +}; + +char* pgm_tsi_print (const pgm_tsi_t*) PGM_GNUC_WARN_UNUSED_RESULT; +int pgm_tsi_print_r (const pgm_tsi_t*restrict, char*restrict, size_t); +bool pgm_tsi_equal (const void*restrict, const void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; + +PGM_END_DECLS + +#endif /* __PGM_TSI_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/types.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/types.h new file mode 100644 index 0000000..6c1c81b --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/types.h @@ -0,0 +1,56 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Cross-platform data types. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_TYPES_H__ +#define __PGM_TYPES_H__ + +#ifndef _MSC_VER +# include +#endif +#include + +#ifdef _WIN32 +# include +# define sa_family_t ULONG +#endif + +#ifdef _MSC_VER +# include +# define bool BOOL +# define ssize_t SSIZE_T +# define restrict +#elif !defined( __cplusplus) || (__GNUC__ >= 4) +/* g++ v4 handles C99 headers without complaints */ +# include +# include +#else +/* g++ v3 and other ancient compilers */ +# define bool int +# include +#endif + +PGM_BEGIN_DECLS + +/* nc */ + +PGM_END_DECLS + +#endif /* __PGM_TYPES_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/version.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/version.h new file mode 100644 index 0000000..7928450 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/version.h @@ -0,0 +1,41 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * OpenPGM version. + * + * Copyright (c) 2006-2008 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_VERSION_H__ +#define __PGM_VERSION_H__ + +#include + +PGM_BEGIN_DECLS + +extern const unsigned pgm_major_version; +extern const unsigned pgm_minor_version; +extern const unsigned pgm_micro_version; + +extern const char* pgm_build_date; +extern const char* pgm_build_time; +extern const char* pgm_build_system; +extern const char* pgm_build_machine; +extern const char* pgm_build_revision; + +PGM_END_DECLS + +#endif /* __PGM_VERSION_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/winint.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/winint.h new file mode 100644 index 0000000..0205cdf --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/winint.h @@ -0,0 +1,198 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * stdint.h for Win32 & Win64 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_WININT_H__ +#define __PGM_WININT_H__ + +#include + +/* 7.18.1.1 Exact-width integer types */ +typedef signed __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef signed __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef signed __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + +/* 7.18.1.2 Minimum-width integer types */ +typedef int8_t int_least8_t; +typedef uint8_t uint_least8_t; +typedef int16_t int_least16_t; +typedef uint16_t uint_least16_t; +typedef int32_t int_least32_t; +typedef uint32_t uint_least32_t; +typedef int64_t int_least64_t; +typedef uint64_t uint_least64_t; + +/* 7.18.1.3 Fastest minimum-width integer types + * Not actually guaranteed to be fastest for all purposes + * Here we use the exact-width types for 8 and 16-bit ints. + */ +typedef int8_t int_fast8_t; +typedef uint8_t uint_fast8_t; +typedef int16_t int_fast16_t; +typedef uint16_t uint_fast16_t; +typedef int32_t int_fast32_t; +typedef uint32_t uint_fast32_t; +typedef int64_t int_fast64_t; +typedef uint64_t uint_fast64_t; + +/* 7.18.1.5 Greatest-width integer types */ +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + +/* 7.18.2 Limits of specified-width integer types */ +#if !defined ( __cplusplus) || defined (__STDC_LIMIT_MACROS) + +/* 7.18.2.1 Limits of exact-width integer types */ +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +/* 7.18.2.2 Limits of minimum-width integer types */ +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST64_MIN INT64_MIN + +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MAX INT64_MAX + +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +/* 7.18.2.3 Limits of fastest minimum-width integer types */ +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST64_MIN INT64_MIN + +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MAX INT64_MAX + +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +/* 7.18.2.4 Limits of integer types capable of holding + object pointers */ +#ifdef _WIN64 +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif + +/* 7.18.2.5 Limits of greatest-width integer types */ +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +/* 7.18.3 Limits of other integer types */ +#ifdef _WIN64 +# define PTRDIFF_MIN INT64_MIN +# define PTRDIFF_MAX INT64_MAX +#else +# define PTRDIFF_MIN INT32_MIN +# define PTRDIFF_MAX INT32_MAX +#endif + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX +# ifdef _WIN64 +# define SIZE_MAX UINT64_MAX +# else +# define SIZE_MAX UINT32_MAX +# endif +#endif + +#ifndef WCHAR_MIN /* also in wchar.h */ +# define WCHAR_MIN 0U +# define WCHAR_MAX UINT16_MAX +#endif + +/* + * wint_t is unsigned short for compatibility with MS runtime + */ +#define WINT_MIN 0U +#define WINT_MAX UINT16_MAX + +#endif /* !defined ( __cplusplus) || defined __STDC_LIMIT_MACROS */ + + +/* 7.18.4 Macros for integer constants */ +#if !defined ( __cplusplus) || defined (__STDC_CONSTANT_MACROS) + +/* 7.18.4.1 Macros for minimum-width integer constants + + Accoding to Douglas Gwyn : + "This spec was changed in ISO/IEC 9899:1999 TC1; in ISO/IEC + 9899:1999 as initially published, the expansion was required + to be an integer constant of precisely matching type, which + is impossible to accomplish for the shorter types on most + platforms, because C99 provides no standard way to designate + an integer constant with width less than that of type int. + TC1 changed this to require just an integer constant + *expression* with *promoted* type." + + The trick used here is from Clive D W Feather. +*/ + +#define INT8_C(val) (INT_LEAST8_MAX-INT_LEAST8_MAX+(val)) +#define INT16_C(val) (INT_LEAST16_MAX-INT_LEAST16_MAX+(val)) +#define INT32_C(val) (INT_LEAST32_MAX-INT_LEAST32_MAX+(val)) +/* The 'trick' doesn't work in C89 for long long because, without + suffix, (val) will be evaluated as int, not intmax_t */ +#define INT64_C(val) val##LL + +#define UINT8_C(val) (val) +#define UINT16_C(val) (val) +#define UINT32_C(val) (val##U) +#define UINT64_C(val) val##ULL + +/* 7.18.4.2 Macros for greatest-width integer constants */ +#define INTMAX_C(val) val##LL +#define UINTMAX_C(val) val##ULL + +#endif /* !defined ( __cplusplus) || defined __STDC_CONSTANT_MACROS */ + +#endif /* __PGM_WININT_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/wininttypes.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/wininttypes.h new file mode 100644 index 0000000..5daa7f1 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/wininttypes.h @@ -0,0 +1,254 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * inttypes.h for Win32 & Win64 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_WININTTYPES_H__ +#define __PGM_WININTTYPES_H__ + +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) + +/* 7.8.1 Macros for format specifiers + * + * MS runtime does not yet understand C9x standard "ll" + * length specifier. It appears to treat "ll" as "l". + * The non-standard I64 length specifier causes warning in GCC, + * but understood by MS runtime functions. + */ + +/* fprintf macros for signed types */ +#define PRId8 "d" +#define PRId16 "hd" +#define PRId32 "I32d" +#define PRId64 "I64d" + +#define PRIdLEAST8 "d" +#define PRIdLEAST16 "hd" +#define PRIdLEAST32 "I32d" +#define PRIdLEAST64 "I64d" + +#define PRIdFAST8 "d" +#define PRIdFAST16 "hd" +#define PRIdFAST32 "I32d" +#define PRIdFAST64 "I64d" + +#define PRIdMAX "I64d" + +#define PRIi8 "i" +#define PRIi16 "hi" +#define PRIi32 "i" +#define PRIi64 "I64i" + +#define PRIiLEAST8 "i" +#define PRIiLEAST16 "hi" +#define PRIiLEAST32 "I32i" +#define PRIiLEAST64 "I64i" + +#define PRIiFAST8 "i" +#define PRIiFAST16 "hi" +#define PRIiFAST32 "I32i" +#define PRIiFAST64 "I64i" + +#define PRIiMAX "I64i" + +#define PRIo8 "o" +#define PRIo16 "ho" +#define PRIo32 "I32o" +#define PRIo64 "I64o" + +#define PRIoLEAST8 "o" +#define PRIoLEAST16 "ho" +#define PRIoLEAST32 "I32o" +#define PRIoLEAST64 "I64o" + +#define PRIoFAST8 "o" +#define PRIoFAST16 "ho" +#define PRIoFAST32 "o" +#define PRIoFAST64 "I64o" + +#define PRIoMAX "I64o" + +/* fprintf macros for unsigned types */ +#define PRIu8 "u" +#define PRIu16 "hu" +#define PRIu32 "I32u" +#define PRIu64 "I64u" + +#define PRIuLEAST8 "u" +#define PRIuLEAST16 "hu" +#define PRIuLEAST32 "I32u" +#define PRIuLEAST64 "I64u" + +#define PRIuFAST8 "u" +#define PRIuFAST16 "hu" +#define PRIuFAST32 "I32u" +#define PRIuFAST64 "I64u" + +#define PRIuMAX "I64u" + +#define PRIx8 "x" +#define PRIx16 "hx" +#define PRIx32 "I32x" +#define PRIx64 "I64x" + +#define PRIxLEAST8 "x" +#define PRIxLEAST16 "hx" +#define PRIxLEAST32 "I32x" +#define PRIxLEAST64 "I64x" + +#define PRIxFAST8 "x" +#define PRIxFAST16 "hx" +#define PRIxFAST32 "I32x" +#define PRIxFAST64 "I64x" + +#define PRIxMAX "I64x" + +#define PRIX8 "X" +#define PRIX16 "hX" +#define PRIX32 "I32X" +#define PRIX64 "I64X" + +#define PRIXLEAST8 "X" +#define PRIXLEAST16 "hX" +#define PRIXLEAST32 "I32X" +#define PRIXLEAST64 "I64X" + +#define PRIXFAST8 "X" +#define PRIXFAST16 "hX" +#define PRIXFAST32 "I32X" +#define PRIXFAST64 "I64X" + +#define PRIXMAX "I64X" + +/* fscanf macros for signed int types */ + +#define SCNd8 "hhd" +#define SCNd16 "hd" +#define SCNd32 "ld" +#define SCNd64 "I64d" + +#define SCNdLEAST8 "hhd" +#define SCNdLEAST16 "hd" +#define SCNdLEAST32 "ld" +#define SCNdLEAST64 "I64d" + +#define SCNdFAST8 "hhd" +#define SCNdFAST16 "hd" +#define SCNdFAST32 "ld" +#define SCNdFAST64 "I64d" + +#define SCNdMAX "I64d" + +#define SCNi8 "hhi" +#define SCNi16 "hi" +#define SCNi32 "li" +#define SCNi64 "I64i" + +#define SCNiLEAST8 "hhi" +#define SCNiLEAST16 "hi" +#define SCNiLEAST32 "li" +#define SCNiLEAST64 "I64i" + +#define SCNiFAST8 "hhi" +#define SCNiFAST16 "hi" +#define SCNiFAST32 "li" +#define SCNiFAST64 "I64i" + +#define SCNiMAX "I64i" + +#define SCNo8 "hho" +#define SCNo16 "ho" +#define SCNo32 "lo" +#define SCNo64 "I64o" + +#define SCNoLEAST8 "hho" +#define SCNoLEAST16 "ho" +#define SCNoLEAST32 "lo" +#define SCNoLEAST64 "I64o" + +#define SCNoFAST8 "hho" +#define SCNoFAST16 "ho" +#define SCNoFAST32 "lo" +#define SCNoFAST64 "I64o" + +#define SCNoMAX "I64o" + +#define SCNx8 "hhx" +#define SCNx16 "hx" +#define SCNx32 "lx" +#define SCNx64 "I64x" + +#define SCNxLEAST8 "hhx" +#define SCNxLEAST16 "hx" +#define SCNxLEAST32 "lx" +#define SCNxLEAST64 "I64x" + +#define SCNxFAST8 "hhx" +#define SCNxFAST16 "hx" +#define SCNxFAST32 "lx" +#define SCNxFAST64 "I64x" + +#define SCNxMAX "I64x" + +/* fscanf macros for unsigned int types */ + +#define SCNu8 "hhu" +#define SCNu16 "hu" +#define SCNu32 "lu" +#define SCNu64 "I64u" + +#define SCNuLEAST8 "hhu" +#define SCNuLEAST16 "hu" +#define SCNuLEAST32 "lu" +#define SCNuLEAST64 "I64u" + +#define SCNuFAST8 "hhu" +#define SCNuFAST16 "hu" +#define SCNuFAST32 "lu" +#define SCNuFAST64 "I64u" + +#define SCNuMAX "I64u" + +#ifdef _WIN64 +# define PRIdPTR "I64d" +# define PRIiPTR "I64i" +# define PRIoPTR "I64o" +# define PRIuPTR "I64u" +# define PRIxPTR "I64x" +# define PRIXPTR "I64X" +# define SCNdPTR "I64d" +# define SCNiPTR "I64i" +# define SCNoPTR "I64o" +# define SCNxPTR "I64x" +# define SCNuPTR "I64u" +#else +# define PRIdPTR "ld" +# define PRIiPTR "li" +# define PRIoPTR "lo" +# define PRIuPTR "lu" +# define PRIxPTR "lx" +# define PRIXPTR "lX" +# define SCNdPTR "ld" +# define SCNiPTR "li" +# define SCNoPTR "lo" +# define SCNxPTR "lx" +# define SCNuPTR "lu" +#endif + +#endif /* !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) */ + +#endif /* __PGM_WININTTYPES_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/indextoaddr.c b/3rdparty/openpgm-svn-r1085/pgm/indextoaddr.c new file mode 100644 index 0000000..479dffd --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/indextoaddr.c @@ -0,0 +1,98 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable interface index to socket address function. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + + +//#define INDEXTOADDR_DEBUG + + +/* interfaces indexes refer to the link layer, we want to find the internet layer address. + * the big problem is that multiple IPv6 addresses can be bound to one link - called scopes. + * we can just pick the first scope and let IP routing handle the rest. + */ + +bool +pgm_if_indextoaddr ( + const unsigned ifindex, + const sa_family_t iffamily, + const uint32_t ifscope, + struct sockaddr* restrict ifsa, + pgm_error_t** restrict error + ) +{ + pgm_return_val_if_fail (NULL != ifsa, FALSE); + + if (0 == ifindex) /* any interface or address */ + { + ifsa->sa_family = iffamily; + switch (iffamily) { + case AF_INET: + ((struct sockaddr_in*)ifsa)->sin_addr.s_addr = INADDR_ANY; + break; + + case AF_INET6: + ((struct sockaddr_in6*)ifsa)->sin6_addr = in6addr_any; + break; + + default: + pgm_return_val_if_reached (FALSE); + break; + } + return TRUE; + } + + struct pgm_ifaddrs_t *ifap, *ifa; + if (!pgm_getifaddrs (&ifap, error)) { + pgm_prefix_error (error, + _("Enumerating network interfaces: ")); + return FALSE; + } + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) + { + if (NULL == ifa->ifa_addr || + ifa->ifa_addr->sa_family != iffamily) + continue; + + const unsigned i = pgm_if_nametoindex (iffamily, ifa->ifa_name); + pgm_assert (0 != i); + if (i == ifindex) + { + if (ifscope && ifscope != pgm_sockaddr_scope_id (ifa->ifa_addr)) + continue; + memcpy (ifsa, ifa->ifa_addr, pgm_sockaddr_len(ifa->ifa_addr)); + pgm_freeifaddrs (ifap); + return TRUE; + } + } + + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("No matching network interface index: %i"), + ifindex); + pgm_freeifaddrs (ifap); + return FALSE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/indextoaddr_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/indextoaddr_unittest.c new file mode 100644 index 0000000..57222d1 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/indextoaddr_unittest.c @@ -0,0 +1,302 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for portable interface index to socket address function. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* IFF_UP */ +#define _BSD_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* mock state */ + +struct mock_interface_t { + unsigned int index; + char* name; + unsigned int flags; + struct sockaddr_storage addr; + struct sockaddr_storage netmask; +}; + +static GList *mock_interfaces = NULL; + +struct pgm_ifaddrs_t; +struct pgm_error_t; + +static bool mock_pgm_getifaddrs (struct pgm_ifaddrs_t**, struct pgm_error_t**); +static void mock_pgm_freeifaddrs (struct pgm_ifaddrs_t*); +static unsigned mock_pgm_if_nametoindex (const sa_family_t, const char*); + + +#define pgm_getifaddrs mock_pgm_getifaddrs +#define pgm_freeifaddrs mock_pgm_freeifaddrs +#define pgm_if_nametoindex mock_pgm_if_nametoindex + + +#define INDEXTOADDR_DEBUG +#include "indextoaddr.c" + + +static +gpointer +create_interface ( + const unsigned index, + const char* name, + const char* flags + ) +{ + struct mock_interface_t* new_interface; + + g_assert (name); + g_assert (flags); + + new_interface = g_slice_alloc0 (sizeof(struct mock_interface_t)); + new_interface->index = index; + new_interface->name = g_strdup (name); + + struct sockaddr_in* sin = (gpointer)&new_interface->addr; + struct sockaddr_in6* sin6 = (gpointer)&new_interface->addr; + + gchar** tokens = g_strsplit (flags, ",", 0); + for (guint i = 0; tokens[i]; i++) + { + if (strcmp (tokens[i], "up") == 0) + new_interface->flags |= IFF_UP; + else if (strcmp (tokens[i], "down") == 0) + new_interface->flags |= 0; + else if (strcmp (tokens[i], "loop") == 0) + new_interface->flags |= IFF_LOOPBACK; + else if (strcmp (tokens[i], "broadcast") == 0) + new_interface->flags |= IFF_BROADCAST; + else if (strcmp (tokens[i], "multicast") == 0) + new_interface->flags |= IFF_MULTICAST; + else if (strncmp (tokens[i], "ip=", strlen("ip=")) == 0) { + const char* addr = tokens[i] + strlen("ip="); + g_assert (pgm_sockaddr_pton (addr, (struct sockaddr*)&new_interface->addr)); + } + else if (strncmp (tokens[i], "netmask=", strlen("netmask=")) == 0) { + const char* addr = tokens[i] + strlen("netmask="); + g_assert (pgm_sockaddr_pton (addr, (struct sockaddr*)&new_interface->netmask)); + } + else if (strncmp (tokens[i], "scope=", strlen("scope=")) == 0) { + const char* scope = tokens[i] + strlen("scope="); + g_assert (AF_INET6 == ((struct sockaddr*)&new_interface->addr)->sa_family); + ((struct sockaddr_in6*)&new_interface->addr)->sin6_scope_id = atoi (scope); + } + else + g_error ("parsing failed for flag \"%s\"", tokens[i]); + } + + g_strfreev (tokens); + return new_interface; +} + +#define APPEND_INTERFACE(a,b,c) \ + do { \ + gpointer data = create_interface ((a), (b), (c)); \ + g_assert (data); \ + mock_interfaces = g_list_append (mock_interfaces, data); \ + g_assert (mock_interfaces); g_assert (mock_interfaces->data); \ + } while (0) +static +void +mock_setup_net (void) +{ + APPEND_INTERFACE( 1, "lo", "up,loop"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast"); + APPEND_INTERFACE( 3, "eth1", "down,broadcast,multicast"); + APPEND_INTERFACE( 1, "lo", "up,loop,ip=127.0.0.1,netmask=255.0.0.0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=10.6.28.33,netmask=255.255.255.0"); + APPEND_INTERFACE( 1, "lo", "up,loop,ip=::1,netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,scope=0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=2002:dce8:d28e::33,netmask=ffff:ffff:ffff:ffff::0,scope=0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=fe80::214:5eff:febd:6dda,netmask=ffff:ffff:ffff:ffff::0,scope=2"); +} + +static +void +mock_teardown_net (void) +{ + GList* list; + + list = mock_interfaces; + while (list) { + struct mock_interface_t* interface = list->data; + g_free (interface->name); + g_slice_free1 (sizeof(struct mock_interface_t), interface); + list = list->next; + } + g_list_free (mock_interfaces); +} + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + +bool +mock_pgm_getifaddrs ( + struct pgm_ifaddrs_t** ifap, + pgm_error_t** err + ) +{ + if (NULL == ifap) { + return FALSE; + } + + g_debug ("mock_getifaddrs (ifap:%p err:%p)", (gpointer)ifap, (gpointer)err); + + GList* list = mock_interfaces; + int n = g_list_length (list); + struct pgm_ifaddrs_t* ifa = calloc (n, sizeof(struct pgm_ifaddrs_t)); + struct pgm_ifaddrs_t* ift = ifa; + while (list) { + struct mock_interface_t* interface = list->data; + ift->ifa_addr = (gpointer)&interface->addr; + ift->ifa_name = interface->name; + ift->ifa_flags = interface->flags; + ift->ifa_netmask = (gpointer)&interface->netmask; + list = list->next; + if (list) { + ift->ifa_next = ift + 1; + ift = ift->ifa_next; + } + } + + *ifap = ifa; + return TRUE; +} + +static +void +mock_pgm_freeifaddrs ( + struct pgm_ifaddrs_t* ifa + ) +{ + g_debug ("mock_freeifaddrs (ifa:%p)", (gpointer)ifa); + free (ifa); +} + +unsigned +mock_pgm_if_nametoindex ( + const sa_family_t iffamily, + const char* ifname + ) +{ + GList* list = mock_interfaces; + while (list) { + const struct mock_interface_t* interface = list->data; + if (0 == strcmp (ifname, interface->name)) + return interface->index; + list = list->next; + } + return 0; +} + + +/* target: + * bool + * pgm_if_indextoaddr ( + * const unsigned ifindex, + * const sa_family_t iffamily, + * const uint32_t ifscope, + * struct sockaddr* ifsa, + * pgm_error_t** error + * ) + */ + +START_TEST (test_indextoaddr_pass_001) +{ + char saddr[INET6_ADDRSTRLEN]; + struct sockaddr_storage addr; + pgm_error_t* err = NULL; + const unsigned int ifindex = 2; + fail_unless (TRUE == pgm_if_indextoaddr (ifindex, AF_INET, 0, (struct sockaddr*)&addr, &err)); + pgm_sockaddr_ntop ((struct sockaddr*)&addr, saddr, sizeof(saddr)); + g_message ("index:%d -> %s", + ifindex, saddr); + fail_unless (TRUE == pgm_if_indextoaddr (ifindex, AF_INET6, 0, (struct sockaddr*)&addr, &err)); + pgm_sockaddr_ntop ((struct sockaddr*)&addr, saddr, sizeof(saddr)); + g_message ("index:%d -> %s", + ifindex, saddr); +} +END_TEST + +START_TEST (test_indextoaddr_fail_001) +{ + pgm_error_t* err = NULL; + fail_unless (FALSE == pgm_if_indextoaddr (0, 0, 0, NULL, &err)); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_indextoaddr = tcase_create ("init-ctx"); + suite_add_tcase (s, tc_indextoaddr); + tcase_add_checked_fixture (tc_indextoaddr, mock_setup_net, mock_teardown_net); + tcase_add_test (tc_indextoaddr, test_indextoaddr_pass_001); + tcase_add_test (tc_indextoaddr, test_indextoaddr_fail_001); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/indextoname.c b/3rdparty/openpgm-svn-r1085/pgm/indextoname.c new file mode 100644 index 0000000..c4043ef --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/indextoname.c @@ -0,0 +1,52 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Windows interface index to interface name function. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef _WIN32 +# include +# include +#endif +#include + + +//#define INDEXTONAME_DEBUG + + +char* +pgm_if_indextoname ( + unsigned int ifindex, + char* ifname + ) +{ +#ifndef _WIN32 + return if_indextoname (ifindex, ifname); +#else + pgm_return_val_if_fail (NULL != ifname, NULL); + + MIB_IFROW ifRow = { .dwIndex = ifindex }; + const DWORD dwRetval = GetIfEntry (&ifRow); + if (NO_ERROR != dwRetval) + return NULL; + strcpy (ifname, (char*)ifRow.wszName); + return ifname; +#endif /* _WIN32 */ +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/inet_network.c b/3rdparty/openpgm-svn-r1085/pgm/inet_network.c new file mode 100644 index 0000000..8cac34b --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/inet_network.c @@ -0,0 +1,237 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable implementations of inet_network and inet_network6. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + + +//#define INET_NETWORK_DEBUG + +/* locals */ + +static uint32_t cidr_to_netmask (const unsigned) PGM_GNUC_CONST; + + +/* calculate IPv4 netmask from network size, returns address in + * host byte order. + */ + +static +uint32_t +cidr_to_netmask ( + const unsigned cidr + ) +{ + return (cidr == 0) ? 0 : (0xffffffff - (1 << (32 - cidr)) + 1); +} + + +/* Converts a numbers-and-dots notation string into a network number in + * host order. + * Note parameters and return value differs from inet_network(). This + * function will not interpret octal numbers, preceeded with a 0, or + * hexadecimal numbers, preceeded by 0x. + * + * 127 => 127.0.0.0 + * 127.1/8 => 127.0.0.0 -- 127.1.0.0 + * inet_addr() would be 127.0.0.1 + * inet_network() would be 0.0.127.1 + * + * returns 0 on success, returns -1 on invalid address. + */ + +int /* return type to match inet_network() */ +pgm_inet_network ( + const char* restrict s, + struct in_addr* restrict in + ) +{ + pgm_return_val_if_fail (NULL != s, -1); + pgm_return_val_if_fail (NULL != in, -1); + + pgm_debug ("pgm_inet_network (s:\"%s\" in:%p)", + s, (const void*)in); + + const char *p = s; + unsigned val = 0; + int shift = 24; + + in->s_addr = INADDR_ANY; + + while (*p) + { + if (isdigit (*p)) { + val = 10 * val + (*p - '0'); + } else if (*p == '.' || *p == 0) { + if (val > 0xff) { + in->s_addr = INADDR_NONE; + return -1; + } + +//g_trace ("elem %i", val); + + in->s_addr |= val << shift; + val = 0; + shift -= 8; + if (shift < 0 && *p != 0) { + in->s_addr = INADDR_NONE; + return -1; + } + + } else if (*p == '/') { + if (val > 0xff) { + in->s_addr = INADDR_NONE; + return -1; + } +//g_trace ("elem %i", val); + in->s_addr |= val << shift; + p++; val = 0; + while (*p) + { + if (isdigit (*p)) { + val = 10 * val + (*p - '0'); + } else { + in->s_addr = INADDR_NONE; + return -1; + } + p++; + } + if (val == 0 || val > 32) { + in->s_addr = INADDR_NONE; + return -1; + } +//g_trace ("bit mask %i", val); + +/* zero out host bits */ + const struct in_addr netaddr = { .s_addr = cidr_to_netmask (val) }; +#ifdef INET_NETWORK_DEBUG +{ +g_debug ("netaddr %s", inet_ntoa (netaddr)); +} +#endif + in->s_addr &= netaddr.s_addr; + return 0; + + } else if (*p == 'x' || *p == 'X') { /* skip number, e.g. 1.x.x.x */ + if (val > 0) { + in->s_addr = INADDR_NONE; + return -1; + } + + } else { + in->s_addr = INADDR_NONE; + return -1; + } + p++; + } + + in->s_addr |= val << shift; + return 0; +} + +/* Converts a numbers-and-dots notation string into an IPv6 network number. + * + * ::1/128 => 0:0:0:0:0:0:0:1 + * ::1 => 0:0:0:0:0:0:0:1 + * ::1.2.3.4 => 0:0:0:0:1.2.3.4 + * + * returns 0 on success, returns -1 on invalid address. + */ + +int +pgm_inet6_network ( + const char* restrict s, /* NULL terminated */ + struct in6_addr* restrict in6 + ) +{ + pgm_return_val_if_fail (NULL != s, -1); + pgm_return_val_if_fail (NULL != in6, -1); + + pgm_debug ("pgm_inet6_network (s:\"%s\" in6:%p)", + s, (const void*)in6); + +/* inet_pton cannot parse IPv6 addresses with subnet declarations, so + * chop them off. + * + * as we are dealing with network addresses IPv6 zone indices are not important + * so we can use the inet_xtoy functions. + */ + char s2[INET6_ADDRSTRLEN]; + const char *p = s; + char* p2 = s2; + while (*p) { + if (*p == '/') break; + *p2++ = *p++; + } + if (*p == 0) { + if (pgm_inet_pton (AF_INET6, s, in6)) return 0; + pgm_debug ("pgm_inet_pton(AF_INET6) failed on '%s'", s); + memcpy (in6, &in6addr_any, sizeof(in6addr_any)); + return -1; + } + + *p2 = 0; + pgm_debug ("net part %s", s2); + if (!pgm_inet_pton (AF_INET6, s2, in6)) { + pgm_debug ("pgm_inet_pton(AF_INET) failed parsing network part '%s'", s2); + memcpy (in6, &in6addr_any, sizeof(in6addr_any)); + return -1; + } + +#ifdef INET_NETWORK_DEBUG + char sdebug[INET6_ADDRSTRLEN]; + pgm_debug ("IPv6 network address: %s", pgm_inet_ntop(AF_INET6, in6, sdebug, sizeof(sdebug))); +#endif + + p++; + unsigned val = 0; + while (*p) + { + if (isdigit(*p)) { + val = 10 * val + (*p - '0'); + } else { + pgm_debug ("failed parsing subnet size due to character '%c'", *p); + memcpy (in6, &in6addr_any, sizeof(in6addr_any)); + return -1; + } + p++; + } + if (val == 0 || val > 128) { + pgm_debug ("subnet size invalid (%d)", val); + memcpy (in6, &in6addr_any, sizeof(in6addr_any)); + return -1; + } + pgm_debug ("subnet size %i", val); + +/* zero out host bits */ + const unsigned suffix_length = 128 - val; + for (int i = suffix_length, j = 15; i > 0; i -= 8, --j) + { + in6->s6_addr[ j ] &= i >= 8 ? 0x00 : (unsigned)(( 0xffU << i ) & 0xffU ); + } + + pgm_debug ("effective IPv6 network address after subnet mask: %s", pgm_inet_ntop(AF_INET6, in6, s2, sizeof(s2))); + + return 0; +} + +/* eof */ + diff --git a/3rdparty/openpgm-svn-r1085/pgm/inet_network_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/inet_network_unittest.c new file mode 100644 index 0000000..2739215 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/inet_network_unittest.c @@ -0,0 +1,203 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for portable implementations of inet_network and inet_network6. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* mock state */ + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + + +#define INET_NETWORK_DEBUG +#include "inet_network.c" + + +/* target: + * int + * pgm_inet_network ( + * const char* s, + * struct in_addr* in -- in host byte order + * ) + */ + +struct test_case_t { + const char* network; + const char* answer; +}; + +static const struct test_case_t cases_001[] = { + { "127", "127.0.0.0" }, /* different to inet_addr/inet_network */ + { "127/8", "127.0.0.0" }, + { "127.1/8", "127.0.0.0" }, + { "127.1", "127.1.0.0" }, /* different to inet_addr/inet_network */ + { "127.x.x.x", "127.0.0.0" }, + { "127.X.X.X", "127.0.0.0" }, + { "127.0.0.0", "127.0.0.0" }, + { "127.0.0.1/8", "127.0.0.0" }, + { "127.0.0.1/32", "127.0.0.1" }, + { "10.0.0.0/8", "10.0.0.0" }, /* RFC1918 class A */ + { "10.255.255.255/8", "10.0.0.0" }, + { "172.16.0.0/12", "172.16.0.0" }, /* RFC1918 class B */ + { "172.31.255.255/12", "172.16.0.0" }, + { "192.168.0.0/16", "192.168.0.0" }, /* RFC1918 class C */ + { "192.168.255.255/16", "192.168.0.0" }, + { "169.254.0.0/16", "169.254.0.0" }, /* RFC3927 link-local */ + { "192.88.99.0/24", "192.88.99.0" }, /* RFC3068 6to4 relay anycast */ + { "224.0.0.0/4", "224.0.0.0" }, /* RFC3171 multicast */ + { "0.0.0.0", "0.0.0.0" }, + { "255.255.255.255", "255.255.255.255" }, +}; + +START_TEST (test_inet_network_pass_001) +{ + const char* network = cases_001[_i].network; + const char* answer = cases_001[_i].answer; + + struct in_addr host_order, network_order; + fail_unless (0 == pgm_inet_network (network, &host_order)); + network_order.s_addr = g_htonl (host_order.s_addr); + + g_message ("Resolved \"%s\" to \"%s\"", + network, inet_ntoa (network_order)); + +{ +struct in_addr t = { .s_addr = g_htonl (inet_network (network)) }; +g_message ("inet_network (%s) = %s", network, inet_ntoa (t)); +} + + fail_unless (0 == strcmp (answer, inet_ntoa (network_order))); +} +END_TEST + +START_TEST (test_inet_network_fail_001) +{ + fail_unless (-1 == pgm_inet_network (NULL, NULL)); +} +END_TEST + +START_TEST (test_inet_network_fail_002) +{ + const char* network = "192.168.0.1/0"; + + struct in_addr host_order; + fail_unless (-1 == pgm_inet_network (network, &host_order)); +} +END_TEST + +/* target: + * int + * pgm_inet6_network ( + * const char* s, + * struct in6_addr* in6 + * ) + */ + +static const struct test_case_t cases6_001[] = { + { "::1/128", "::1" }, + { "2002:dec8:d28e::36/64", "2002:dec8:d28e::" }, /* 6to4 */ + { "fe80::203:baff:fe4e:6cc8/10", "fe80::" }, /* link-local */ + { "ff02::1/8", "ff00::" }, /* multicast */ + { "fc00:6::61/7", "fc00::" }, /* ULA */ +}; + +START_TEST (test_inet6_network_pass_001) +{ + const char* network = cases6_001[_i].network; + const char* answer = cases6_001[_i].answer; + + char snetwork[INET6_ADDRSTRLEN]; + struct in6_addr addr; + fail_unless (0 == pgm_inet6_network (network, &addr)); + g_message ("Resolved \"%s\" to \"%s\"", + network, pgm_inet_ntop (AF_INET6, &addr, snetwork, sizeof(snetwork))); + + fail_unless (0 == strcmp (answer, snetwork)); +} +END_TEST + +START_TEST (test_inet6_network_fail_001) +{ + fail_unless (-1 == pgm_inet6_network (NULL, NULL)); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_inet_network = tcase_create ("inet-network"); + suite_add_tcase (s, tc_inet_network); + tcase_add_loop_test (tc_inet_network, test_inet_network_pass_001, 0, G_N_ELEMENTS(cases_001)); + tcase_add_test (tc_inet_network, test_inet_network_fail_001); + + TCase* tc_inet6_network = tcase_create ("inet6-network"); + suite_add_tcase (s, tc_inet6_network); + tcase_add_loop_test (tc_inet6_network, test_inet6_network_pass_001, 0, G_N_ELEMENTS(cases6_001)); + tcase_add_test (tc_inet6_network, test_inet6_network_fail_001); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/ip_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/ip_unittest.c new file mode 100644 index 0000000..48bb601 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/ip_unittest.c @@ -0,0 +1,362 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for ip stack. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* getsockopt(3SOCKET) + * level is the protocol number of the protocl that controls the option. + */ +#ifndef SOL_IP +# define SOL_IP IPPROTO_IP +#endif +#ifndef SOL_IPV6 +# define SOL_IPV6 IPPROTO_IPV6 +#endif + +/* mock state */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + + +#define PGM_COMPILATION +#include "impl/sockaddr.h" +#include "impl/indextoaddr.h" +#include "impl/ip.h" + + +/* target: + * testing platform capability to loop send multicast packets to a listening + * receive socket. + */ + +START_TEST (test_multicast_loop_pass_001) +{ + struct sockaddr_in addr; + memset (&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr ("239.192.0.1"); + + int recv_sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + fail_if (-1 == recv_sock, "socket failed"); + struct sockaddr_in recv_addr; + memcpy (&recv_addr, &addr, sizeof(addr)); + recv_addr.sin_port = 7500; + fail_unless (0 == bind (recv_sock, (struct sockaddr*)&recv_addr, pgm_sockaddr_len ((struct sockaddr*)&recv_addr)), "bind failed"); + struct group_req gr; + memset (&gr, 0, sizeof(gr)); + ((struct sockaddr*)&gr.gr_group)->sa_family = addr.sin_family; + ((struct sockaddr_in*)&gr.gr_group)->sin_addr.s_addr = addr.sin_addr.s_addr; + fail_unless (0 == setsockopt (recv_sock, SOL_IP, MCAST_JOIN_GROUP, (const char*)&gr, sizeof(gr)), "setsockopt failed"); + fail_unless (0 == pgm_sockaddr_multicast_loop (recv_sock, AF_INET, FALSE), "multicast_loop failed"); + + int send_sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + fail_if (-1 == send_sock, "socket failed"); + struct sockaddr_in send_addr; + memcpy (&send_addr, &addr, sizeof(addr)); + fail_unless (0 == bind (send_sock, (struct sockaddr*)&send_addr, pgm_sockaddr_len ((struct sockaddr*)&send_addr)), "bind failed"); + struct sockaddr_in if_addr; + fail_unless (TRUE == pgm_if_indextoaddr (0, AF_INET, 0, (struct sockaddr*)&if_addr, NULL), "if_indextoaddr failed"); + fail_unless (0 == pgm_sockaddr_multicast_if (send_sock, (struct sockaddr*)&if_addr, 0), "multicast_if failed"); + fail_unless (0 == pgm_sockaddr_multicast_loop (send_sock, AF_INET, TRUE), "multicast_loop failed"); + + const char data[] = "apple pie"; + addr.sin_port = 7500; + ssize_t bytes_sent = sendto (send_sock, data, sizeof(data), 0, (struct sockaddr*)&addr, pgm_sockaddr_len ((struct sockaddr*)&addr)); + if (-1 == bytes_sent) + g_message ("sendto: %s", strerror (errno)); + fail_unless (sizeof(data) == bytes_sent, "sendto underrun"); + + char recv_data[1024]; + ssize_t bytes_read = recv (recv_sock, recv_data, sizeof(recv_data), MSG_DONTWAIT); + if (-1 == bytes_read) + g_message ("sendto: %s", strerror (errno)); + fail_unless (sizeof(data) == bytes_read, "recv underrun"); + + fail_unless (0 == close (recv_sock), "close failed"); + fail_unless (0 == close (send_sock), "close failed"); +} +END_TEST + +/* target: + * testing whether unicast bind accepts packets to multicast join on a + * different port. + */ + +START_TEST (test_port_bind_pass_001) +{ + struct sockaddr_in addr; + memset (&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr ("239.192.0.1"); + + int recv_sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + fail_if (-1 == recv_sock, "socket failed"); + struct sockaddr_in recv_addr; + memcpy (&recv_addr, &addr, sizeof(addr)); + recv_addr.sin_port = 3056; + fail_unless (0 == bind (recv_sock, (struct sockaddr*)&recv_addr, pgm_sockaddr_len ((struct sockaddr*)&recv_addr)), "bind failed"); + struct group_req gr; + memset (&gr, 0, sizeof(gr)); + ((struct sockaddr*)&gr.gr_group)->sa_family = addr.sin_family; + ((struct sockaddr_in*)&gr.gr_group)->sin_addr.s_addr = addr.sin_addr.s_addr; + ((struct sockaddr_in*)&gr.gr_group)->sin_port = 3055; + fail_unless (0 == setsockopt (recv_sock, SOL_IP, MCAST_JOIN_GROUP, (const char*)&gr, sizeof(gr)), "setsockopt failed"); + fail_unless (0 == pgm_sockaddr_multicast_loop (recv_sock, AF_INET, FALSE), "multicast_loop failed"); + + int send_sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + fail_if (-1 == send_sock, "socket failed"); + struct sockaddr_in send_addr; + memcpy (&send_addr, &addr, sizeof(addr)); + fail_unless (0 == bind (send_sock, (struct sockaddr*)&send_addr, pgm_sockaddr_len ((struct sockaddr*)&send_addr)), "bind failed"); + struct sockaddr_in if_addr; + fail_unless (TRUE == pgm_if_indextoaddr (0, AF_INET, 0, (struct sockaddr*)&if_addr, NULL), "if_indextoaddr failed"); + fail_unless (0 == pgm_sockaddr_multicast_if (send_sock, (struct sockaddr*)&if_addr, 0), "multicast_if failed"); + fail_unless (0 == pgm_sockaddr_multicast_loop (send_sock, AF_INET, TRUE), "multicast_loop failed"); + + const char data[] = "apple pie"; + addr.sin_port = 3056; + ssize_t bytes_sent = sendto (send_sock, data, sizeof(data), 0, (struct sockaddr*)&addr, pgm_sockaddr_len ((struct sockaddr*)&addr)); + if (-1 == bytes_sent) + g_message ("sendto: %s", strerror (errno)); + fail_unless (sizeof(data) == bytes_sent, "sendto underrun"); + + char recv_data[1024]; + ssize_t bytes_read = recv (recv_sock, recv_data, sizeof(recv_data), MSG_DONTWAIT); + if (-1 == bytes_read) + g_message ("recv: %s", strerror (errno)); + if (sizeof(data) != bytes_read) + g_message ("recv returned %d bytes expected %d.", bytes_read, sizeof(data)); + fail_unless (sizeof(data) == bytes_read, "recv underrun"); + + fail_unless (0 == close (recv_sock), "close failed"); + fail_unless (0 == close (send_sock), "close failed"); +} +END_TEST + +/* target: + * test setting hop limit, aka time-to-live. + * + * NB: whilst convenient, we cannot use SOCK_RAW & IPPROTO_UDP on Solaris 10 + * as it crashes the IP stack. + */ + +START_TEST (test_hop_limit_pass_001) +{ + struct sockaddr_in addr; + memset (&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr ("239.192.0.1"); + + int recv_sock = socket (AF_INET, SOCK_RAW, 113); + fail_if (-1 == recv_sock, "socket failed"); + struct sockaddr_in recv_addr; + memcpy (&recv_addr, &addr, sizeof(addr)); + recv_addr.sin_port = 7500; + fail_unless (0 == bind (recv_sock, (struct sockaddr*)&recv_addr, pgm_sockaddr_len ((struct sockaddr*)&recv_addr)), "bind failed"); + struct group_req gr; + memset (&gr, 0, sizeof(gr)); + ((struct sockaddr*)&gr.gr_group)->sa_family = addr.sin_family; + ((struct sockaddr_in*)&gr.gr_group)->sin_addr.s_addr = addr.sin_addr.s_addr; + fail_unless (0 == setsockopt (recv_sock, SOL_IP, MCAST_JOIN_GROUP, (const char*)&gr, sizeof(gr)), "setsockopt failed"); + fail_unless (0 == pgm_sockaddr_multicast_loop (recv_sock, AF_INET, FALSE), "multicast_loop failed"); + fail_unless (0 == pgm_sockaddr_hdrincl (recv_sock, AF_INET, TRUE), "hdrincl failed"); + + int send_sock = socket (AF_INET, SOCK_RAW, 113); + fail_if (-1 == send_sock, "socket failed"); + struct sockaddr_in send_addr; + memcpy (&send_addr, &addr, sizeof(addr)); + fail_unless (0 == bind (send_sock, (struct sockaddr*)&send_addr, pgm_sockaddr_len ((struct sockaddr*)&send_addr)), "bind failed"); + struct sockaddr_in if_addr; + fail_unless (TRUE == pgm_if_indextoaddr (0, AF_INET, 0, (struct sockaddr*)&if_addr, NULL), "if_indextoaddr failed"); + fail_unless (0 == pgm_sockaddr_multicast_if (send_sock, (struct sockaddr*)&if_addr, 0), "multicast_if failed"); + fail_unless (0 == pgm_sockaddr_multicast_loop (send_sock, AF_INET, TRUE), "multicast_loop failed"); + fail_unless (0 == pgm_sockaddr_multicast_hops (send_sock, AF_INET, 16), "multicast_hops failed"); + + const char data[] = "apple pie"; + addr.sin_port = 7500; + ssize_t bytes_sent = sendto (send_sock, data, sizeof(data), 0, (struct sockaddr*)&addr, pgm_sockaddr_len ((struct sockaddr*)&addr)); + if (-1 == bytes_sent) + g_message ("sendto: %s", strerror (errno)); + fail_unless (sizeof(data) == bytes_sent, "sendto underrun"); + + char recv_data[1024]; + ssize_t bytes_read = recv (recv_sock, recv_data, sizeof(recv_data), MSG_DONTWAIT); + if (-1 == bytes_read) + g_message ("recv: %s", strerror (errno)); + const size_t pkt_len = sizeof(struct pgm_ip) + sizeof(data); + if (pkt_len != bytes_read) + g_message ("recv returned %zd bytes expected %zu.", bytes_read, pkt_len); + fail_unless (pkt_len == bytes_read, "recv underrun"); + const struct pgm_ip* iphdr = (void*)recv_data; + fail_unless (4 == iphdr->ip_v, "Incorrect IP version, found %u expecting 4.", iphdr->ip_v); + fail_unless (16 == iphdr->ip_ttl, "hop count mismatch, found %u expecting 16.", iphdr->ip_ttl); + + fail_unless (0 == close (recv_sock), "close failed"); + fail_unless (0 == close (send_sock), "close failed"); +} +END_TEST + +/* target: + * router alert. + */ + +START_TEST (test_router_alert_pass_001) +{ + struct sockaddr_in addr; + memset (&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr ("239.192.0.1"); + + int recv_sock = socket (AF_INET, SOCK_RAW, 113); + fail_if (-1 == recv_sock, "socket failed"); + struct sockaddr_in recv_addr; + memcpy (&recv_addr, &addr, sizeof(addr)); + recv_addr.sin_port = 7500; + fail_unless (0 == bind (recv_sock, (struct sockaddr*)&recv_addr, pgm_sockaddr_len ((struct sockaddr*)&recv_addr)), "bind failed"); + struct group_req gr; + memset (&gr, 0, sizeof(gr)); + ((struct sockaddr*)&gr.gr_group)->sa_family = addr.sin_family; + ((struct sockaddr_in*)&gr.gr_group)->sin_addr.s_addr = addr.sin_addr.s_addr; + fail_unless (0 == setsockopt (recv_sock, SOL_IP, MCAST_JOIN_GROUP, (const char*)&gr, sizeof(gr)), "setsockopt failed"); + fail_unless (0 == pgm_sockaddr_multicast_loop (recv_sock, AF_INET, FALSE), "multicast_loop failed"); + fail_unless (0 == pgm_sockaddr_hdrincl (recv_sock, AF_INET, TRUE), "hdrincl failed"); + + int send_sock = socket (AF_INET, SOCK_RAW, 113); + fail_if (-1 == send_sock, "socket failed"); + struct sockaddr_in send_addr; + memcpy (&send_addr, &addr, sizeof(addr)); + fail_unless (0 == bind (send_sock, (struct sockaddr*)&send_addr, pgm_sockaddr_len ((struct sockaddr*)&send_addr)), "bind failed"); + struct sockaddr_in if_addr; + fail_unless (TRUE == pgm_if_indextoaddr (0, AF_INET, 0, (struct sockaddr*)&if_addr, NULL), "if_indextoaddr failed"); + fail_unless (0 == pgm_sockaddr_multicast_if (send_sock, (struct sockaddr*)&if_addr, 0), "multicast_if failed"); + fail_unless (0 == pgm_sockaddr_multicast_loop (send_sock, AF_INET, TRUE), "multicast_loop failed"); + fail_unless (0 == pgm_sockaddr_router_alert (send_sock, AF_INET, TRUE), "router_alert failed"); + + const char data[] = "apple pie"; + addr.sin_port = 7500; + ssize_t bytes_sent = sendto (send_sock, data, sizeof(data), 0, (struct sockaddr*)&addr, pgm_sockaddr_len ((struct sockaddr*)&addr)); + if (-1 == bytes_sent) + g_message ("sendto: %s", strerror (errno)); + fail_unless (sizeof(data) == bytes_sent, "sendto underrun"); + + char recv_data[1024]; + ssize_t bytes_read = recv (recv_sock, recv_data, sizeof(recv_data), MSG_DONTWAIT); + if (-1 == bytes_read) + g_message ("recv: %s", strerror (errno)); + const size_t ra_iphdr_len = sizeof(uint32_t) + sizeof(struct pgm_ip); + const size_t ra_pkt_len = ra_iphdr_len + sizeof(data); + if (ra_pkt_len != bytes_read) + g_message ("recv returned %zd bytes expected %zu.", bytes_read, ra_pkt_len); + fail_unless (ra_pkt_len == bytes_read, "recv underrun"); + const struct pgm_ip* iphdr = (void*)recv_data; + fail_unless (4 == iphdr->ip_v, "Incorrect IP version, found %u expecting 4.", iphdr->ip_v); + if (ra_iphdr_len != (iphdr->ip_hl << 2)) { + g_message ("IP header length mismatch, found %zu expecting %zu.", + (size_t)(iphdr->ip_hl << 2), ra_iphdr_len); + } + g_message ("IP header length = %zu", (size_t)(iphdr->ip_hl << 2)); + const uint32_t* ipopt = (const void*)&recv_data[ iphdr->ip_hl << 2 ]; + const uint32_t ipopt_ra = ((uint32_t)PGM_IPOPT_RA << 24) | (0x04 << 16); + const uint32_t router_alert = htonl(ipopt_ra); + if (router_alert == *ipopt) { + g_message ("IP option router alert found after IP header length."); + ipopt += sizeof(uint32_t); + } else { + ipopt = (const void*)&recv_data[ sizeof(struct pgm_ip) ]; + fail_unless (router_alert == *ipopt, "IP router alert option not found."); + g_message ("IP option router alert found before end of IP header length."); + } + g_message ("Final IP header length = %zu", (size_t)((const char*)ipopt - (const char*)recv_data)); + + fail_unless (0 == close (recv_sock), "close failed"); + fail_unless (0 == close (send_sock), "close failed"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_multicast_loop = tcase_create ("multicast loop"); + suite_add_tcase (s, tc_multicast_loop); + tcase_add_test (tc_multicast_loop, test_multicast_loop_pass_001); + + TCase* tc_port_bind = tcase_create ("port bind"); + suite_add_tcase (s, tc_port_bind); + tcase_add_test (tc_port_bind, test_port_bind_pass_001); + + TCase* tc_hop_limit = tcase_create ("hop limit"); + suite_add_tcase (s, tc_hop_limit); + tcase_add_test (tc_hop_limit, test_hop_limit_pass_001); + + TCase* tc_router_alert = tcase_create ("router alert"); + suite_add_tcase (s, tc_router_alert); + tcase_add_test (tc_router_alert, test_router_alert_pass_001); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/list.c b/3rdparty/openpgm-svn-r1085/pgm/list.c new file mode 100644 index 0000000..7b22f17 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/list.c @@ -0,0 +1,146 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable doubly-linked list. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + + +//#define LIST_DEBUG + +pgm_list_t* +pgm_list_append ( + pgm_list_t* restrict list, + void* restrict data + ) +{ + pgm_list_t* new_list; + pgm_list_t* last; + + new_list = pgm_new (pgm_list_t, 1); + new_list->data = data; + new_list->next = NULL; + + if (list) + { + last = pgm_list_last (list); + last->next = new_list; + new_list->prev = last; + return list; + } + else + { + new_list->prev = NULL; + return new_list; + } +} + +pgm_list_t* +pgm_list_prepend_link ( + pgm_list_t* restrict list, + pgm_list_t* restrict link_ + ) +{ + pgm_list_t* new_list = link_; + + pgm_return_val_if_fail (NULL != link_, list); + + new_list->next = list; + new_list->prev = NULL; + + if (list) + list->prev = new_list; + return new_list; +} + +static inline +pgm_list_t* +_pgm_list_remove_link ( + pgm_list_t* list, /* list and link_ may be the same */ + pgm_list_t* link_ + ) +{ + if (PGM_LIKELY (NULL != link_)) + { + if (link_->prev) + link_->prev->next = link_->next; + if (link_->next) + link_->next->prev = link_->prev; + + if (link_ == list) + list = list->next; + + link_->next = link_->prev = NULL; + } + return list; +} + +pgm_list_t* +pgm_list_remove_link ( + pgm_list_t* list, /* list and link_ may be the same */ + pgm_list_t* link_ + ) +{ + return _pgm_list_remove_link (list, link_); +} + +pgm_list_t* +pgm_list_delete_link ( + pgm_list_t* list, /* list and link_ may be the same */ + pgm_list_t* link_ + ) +{ + pgm_list_t* new_list = _pgm_list_remove_link (list, link_); + pgm_free (link_); + + return new_list; +} + +/* Has pure attribute as NULL is a valid list + */ + +pgm_list_t* +pgm_list_last ( + pgm_list_t* list + ) +{ + if (PGM_LIKELY (NULL != list)) { + while (list->next) + list = list->next; + } + return list; +} + +unsigned +pgm_list_length ( + pgm_list_t* list + ) +{ + unsigned length = 0; + + while (list) + { + length++; + list = list->next; + } + + return length; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/log.c b/3rdparty/openpgm-svn-r1085/pgm/log.c new file mode 100644 index 0000000..af2aec5 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/log.c @@ -0,0 +1,151 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * basic logging. + * + * Copyright (c) 2006-2007 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#ifndef G_OS_WIN32 +# include +#endif +#include +#include "pgm/log.h" + + +/* globals */ + +#define TIME_FORMAT "%Y-%m-%d %H:%M:%S " + +static int log_timezone PGM_GNUC_READ_MOSTLY = 0; +static char log_hostname[NI_MAXHOST + 1] PGM_GNUC_READ_MOSTLY; + +static void glib_log_handler (const gchar*, GLogLevelFlags, const gchar*, gpointer); +static void pgm_log_handler (const int, const char*, void*); + + +/* calculate time zone offset in seconds + */ + +bool +log_init ( void ) +{ +/* time zone offset */ + time_t t = time(NULL); + struct tm sgmt, *gmt = &sgmt; + *gmt = *gmtime(&t); + struct tm* loc = localtime(&t); + log_timezone = (loc->tm_hour - gmt->tm_hour) * 60 * 60 + + (loc->tm_min - gmt->tm_min) * 60; + int dir = loc->tm_year - gmt->tm_year; + if (!dir) dir = loc->tm_yday - gmt->tm_yday; + log_timezone += dir * 24 * 60 * 60; +// printf ("timezone offset %u seconds.\n", log_timezone); + gethostname (log_hostname, sizeof(log_hostname)); + g_log_set_handler ("Pgm", G_LOG_LEVEL_MASK, glib_log_handler, NULL); + g_log_set_handler ("Pgm-Http", G_LOG_LEVEL_MASK, glib_log_handler, NULL); + g_log_set_handler ("Pgm-Snmp", G_LOG_LEVEL_MASK, glib_log_handler, NULL); + g_log_set_handler (NULL, G_LOG_LEVEL_MASK, glib_log_handler, NULL); + pgm_log_set_handler (pgm_log_handler, NULL); + return 0; +} + +/* log callback + */ +static void +glib_log_handler ( + const gchar* log_domain, + G_GNUC_UNUSED GLogLevelFlags log_level, + const gchar* message, + G_GNUC_UNUSED gpointer unused_data + ) +{ +#ifdef G_OS_UNIX + struct iovec iov[7]; + struct iovec* v = iov; + time_t now; + time (&now); + const struct tm* time_ptr = localtime(&now); + char tbuf[1024]; + strftime(tbuf, sizeof(tbuf), TIME_FORMAT, time_ptr); + v->iov_base = tbuf; + v->iov_len = strlen(tbuf); + v++; + v->iov_base = log_hostname; + v->iov_len = strlen(log_hostname); + v++; + if (log_domain) { + v->iov_base = " "; + v->iov_len = 1; + v++; + v->iov_base = log_domain; + v->iov_len = strlen(log_domain); + v++; + } + v->iov_base = ": "; + v->iov_len = 2; + v++; + v->iov_base = message; + v->iov_len = strlen(message); + v++; + v->iov_base = "\n"; + v->iov_len = 1; + v++; + writev (STDOUT_FILENO, iov, v - iov); +#else + time_t now; + time (&now); + const struct tm* time_ptr = localtime(&now); + char s[1024]; + strftime(s, sizeof(s), TIME_FORMAT, time_ptr); + write (STDOUT_FILENO, s, strlen(s)); + write (STDOUT_FILENO, log_hostname, strlen(log_hostname)); + if (log_domain) { + write (STDOUT_FILENO, " ", 1); + write (STDOUT_FILENO, log_domain, strlen(log_domain)); + } + write (STDOUT_FILENO, ": ", 2); + write (STDOUT_FILENO, message, strlen(message)); + write (STDOUT_FILENO, "\n", 1); +#endif +} + +static void +pgm_log_handler ( + const int pgm_log_level, + const char* message, + G_GNUC_UNUSED void* closure + ) +{ + GLogLevelFlags glib_log_level; + + switch (pgm_log_level) { + case PGM_LOG_LEVEL_DEBUG: glib_log_level = G_LOG_LEVEL_DEBUG; break; + case PGM_LOG_LEVEL_TRACE: glib_log_level = G_LOG_LEVEL_DEBUG; break; + case PGM_LOG_LEVEL_MINOR: glib_log_level = G_LOG_LEVEL_INFO; break; + case PGM_LOG_LEVEL_NORMAL: glib_log_level = G_LOG_LEVEL_MESSAGE; break; + case PGM_LOG_LEVEL_WARNING: glib_log_level = G_LOG_LEVEL_WARNING; break; + case PGM_LOG_LEVEL_ERROR: glib_log_level = G_LOG_LEVEL_CRITICAL; break; + case PGM_LOG_LEVEL_FATAL: glib_log_level = G_LOG_LEVEL_ERROR; break; + } + + g_log ("Pgm", glib_log_level, message, NULL); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/math.c b/3rdparty/openpgm-svn-r1085/pgm/math.c new file mode 100644 index 0000000..c2a1b4e --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/math.c @@ -0,0 +1,75 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable math. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + + +//#define MATH_DEBUG + + +static const unsigned primes[] = +{ + 11, + 19, + 37, + 73, + 109, + 163, + 251, + 367, + 557, + 823, + 1237, + 1861, + 2777, + 4177, + 6247, + 9371, + 14057, + 21089, + 31627, + 47431, + 71143, + 106721, + 160073, + 240101, + 360163, + 540217, + 810343, + 1215497, + 1823231, + 2734867, + 4102283, + 6153409, + 9230113, + 13845163, +}; + +unsigned +pgm_spaced_primes_closest (unsigned num) +{ + for (unsigned i = 0; i < PGM_N_ELEMENTS(primes); i++) + if (primes[i] > num) + return primes[i]; + return primes[PGM_N_ELEMENTS(primes) - 1]; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/md5.c b/3rdparty/openpgm-svn-r1085/pgm/md5.c new file mode 100644 index 0000000..03bad97 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/md5.c @@ -0,0 +1,368 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * MD5 hashing algorithm. + * + * MD5 original source GNU C Library: + * Includes functions to compute MD5 message digest of files or memory blocks + * according to the definition of MD5 in RFC 1321 from April 1992. + * + * Copyright (C) 1995, 1996, 2001, 2003 Free Software Foundation, Inc. + * + * This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + + +//#define MD5_DEBUG + + +/* locals */ + +static void _pgm_md5_process_block (struct pgm_md5_t*restrict, const void*restrict, size_t); +static void* _pgm_md5_read_ctx (const struct pgm_md5_t*, void*restrict); + + +/* This array contains the bytes used to pad the buffer to the next + * 64-byte boundary. (RFC 1321, 3.1: Step 1) */ +static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; + + +#if __BYTE_ORDER == __BIG_ENDIAN +# define SWAP(n) \ + (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) +#else +# define SWAP(n) (n) +#endif + + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ + +void +pgm_md5_init_ctx ( + struct pgm_md5_t* ctx + ) +{ +/* pre-conditions */ + pgm_assert (NULL != ctx); + + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; +} + +/* These are the four functions used in the four steps of the MD5 algorithm + and defined in the RFC 1321. The first function is a little bit optimized + (as found in Colin Plumbs public domain implementation). */ +#define FF(b, c, d) (d ^ (b & (c ^ d))) +#define FG(b, c, d) FF (d, b, c) +#define FH(b, c, d) (b ^ c ^ d) +#define FI(b, c, d) (c ^ (b | ~d)) + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. */ + +static +void +_pgm_md5_process_block ( + struct pgm_md5_t* restrict ctx, + const void* restrict buffer, + size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != buffer); + pgm_assert (len > 0); + pgm_assert (NULL != ctx); + + uint32_t correct_words[16]; + const uint32_t *words = buffer; + const size_t nwords = len / sizeof (uint32_t); + const uint32_t *endp = words + nwords; + uint32_t A = ctx->A; + uint32_t B = ctx->B; + uint32_t C = ctx->C; + uint32_t D = ctx->D; + +/* First increment the byte count. RFC 1321 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. Do a double word increment. */ + ctx->total[0] += len; + if (ctx->total[0] < len) + ++ctx->total[1]; + +/* Process all bytes in the buffer with 64 bytes in each round of + the loop. */ + while (words < endp) + { + uint32_t *cwp = correct_words; + uint32_t A_save = A; + uint32_t B_save = B; + uint32_t C_save = C; + uint32_t D_save = D; + +/* First round: using the given function, the context and a constant + the next context is computed. Because the algorithms processing + unit is a 32-bit word and it is determined to work on words in + little endian byte order we perhaps have to change the byte order + before the computation. To reduce the work for the next steps + we store the swapped words in the array CORRECT_WORDS. */ + +#define OP(a, b, c, d, s, T) \ + do \ + { \ + a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \ + ++words; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + +/* It is unfortunate that C does not provide an operator for + * cyclic rotation. Hope the C compiler is smart enough. */ +#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) + +/* Before we start, one word to the strange constants. + They are defined in RFC 1321 as + + T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64, or + perl -e 'foreach(1..64){printf "0x%08x\n", int (4294967296 * abs (sin $_))}' + */ + +/* Round 1. */ + OP (A, B, C, D, 7, 0xd76aa478); + OP (D, A, B, C, 12, 0xe8c7b756); + OP (C, D, A, B, 17, 0x242070db); + OP (B, C, D, A, 22, 0xc1bdceee); + OP (A, B, C, D, 7, 0xf57c0faf); + OP (D, A, B, C, 12, 0x4787c62a); + OP (C, D, A, B, 17, 0xa8304613); + OP (B, C, D, A, 22, 0xfd469501); + OP (A, B, C, D, 7, 0x698098d8); + OP (D, A, B, C, 12, 0x8b44f7af); + OP (C, D, A, B, 17, 0xffff5bb1); + OP (B, C, D, A, 22, 0x895cd7be); + OP (A, B, C, D, 7, 0x6b901122); + OP (D, A, B, C, 12, 0xfd987193); + OP (C, D, A, B, 17, 0xa679438e); + OP (B, C, D, A, 22, 0x49b40821); + +/* For the second to fourth round we have the possibly swapped words + in CORRECT_WORDS. Redefine the macro to take an additional first + argument specifying the function to use. */ +#undef OP +#define OP(f, a, b, c, d, k, s, T) \ + do \ + { \ + a += f (b, c, d) + correct_words[k] + T; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + +/* Round 2. */ + OP (FG, A, B, C, D, 1, 5, 0xf61e2562); + OP (FG, D, A, B, C, 6, 9, 0xc040b340); + OP (FG, C, D, A, B, 11, 14, 0x265e5a51); + OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); + OP (FG, A, B, C, D, 5, 5, 0xd62f105d); + OP (FG, D, A, B, C, 10, 9, 0x02441453); + OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); + OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); + OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); + OP (FG, D, A, B, C, 14, 9, 0xc33707d6); + OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); + OP (FG, B, C, D, A, 8, 20, 0x455a14ed); + OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); + OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); + OP (FG, C, D, A, B, 7, 14, 0x676f02d9); + OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); + +/* Round 3. */ + OP (FH, A, B, C, D, 5, 4, 0xfffa3942); + OP (FH, D, A, B, C, 8, 11, 0x8771f681); + OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); + OP (FH, B, C, D, A, 14, 23, 0xfde5380c); + OP (FH, A, B, C, D, 1, 4, 0xa4beea44); + OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); + OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); + OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); + OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); + OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); + OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); + OP (FH, B, C, D, A, 6, 23, 0x04881d05); + OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); + OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); + OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); + OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); + +/* Round 4. */ + OP (FI, A, B, C, D, 0, 6, 0xf4292244); + OP (FI, D, A, B, C, 7, 10, 0x432aff97); + OP (FI, C, D, A, B, 14, 15, 0xab9423a7); + OP (FI, B, C, D, A, 5, 21, 0xfc93a039); + OP (FI, A, B, C, D, 12, 6, 0x655b59c3); + OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); + OP (FI, C, D, A, B, 10, 15, 0xffeff47d); + OP (FI, B, C, D, A, 1, 21, 0x85845dd1); + OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); + OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); + OP (FI, C, D, A, B, 6, 15, 0xa3014314); + OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); + OP (FI, A, B, C, D, 4, 6, 0xf7537e82); + OP (FI, D, A, B, C, 11, 10, 0xbd3af235); + OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); + OP (FI, B, C, D, A, 9, 21, 0xeb86d391); + +/* Add the starting values of the context. */ + A += A_save; + B += B_save; + C += C_save; + D += D_save; + } + +/* Put checksum in context given as argument. */ + ctx->A = A; + ctx->B = B; + ctx->C = C; + ctx->D = D; +} + +void +pgm_md5_process_bytes ( + struct pgm_md5_t* restrict ctx, + const void* restrict buffer, + size_t len + ) +{ +/* pre-conditions */ + if (len > 0) { + pgm_assert (NULL != buffer); + } + pgm_assert (NULL != ctx); + + if (len >= 64) + { +#if !_STRING_ARCH_unaligned +/* To check alignment gcc has an appropriate operator. Other + compilers don't. */ +# if __GNUC__ >= 2 +# define UNALIGNED_P(p) (((uintptr_t)p) % __alignof__ (uint32_t) != 0) +# else +# define UNALIGNED_P(p) (((uintptr_t)p) % sizeof(uint32_t) != 0) +# endif + if (UNALIGNED_P (buffer)) + while (len > 64) + { + _pgm_md5_process_block (ctx, memcpy (ctx->buffer, buffer, 64), 64); + buffer = (const char*)buffer + 64; + len -= 64; + } + else +#endif + { + _pgm_md5_process_block (ctx, buffer, len & ~63); + buffer = (const char*)buffer + (len & ~63); + len &= 63; + } + } + +/* Move remaining bytes in internal buffer. */ + if (len > 0) + { + size_t left_over = ctx->buflen; + + memcpy (&ctx->buffer[left_over], buffer, len); + left_over += len; + if (left_over >= 64) + { + _pgm_md5_process_block (ctx, ctx->buffer, 64); + left_over -= 64; + memcpy (ctx->buffer, &ctx->buffer[64], left_over); + } + ctx->buflen = left_over; + } +} + +/* Put result from CTX in first 16 bytes following RESBUF. The result + must be in little endian byte order. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ + +static +void* +_pgm_md5_read_ctx ( + const struct pgm_md5_t* restrict ctx, + void* restrict resbuf + ) +{ +/* pre-conditions */ + pgm_assert (NULL != ctx); + pgm_assert (NULL != resbuf); + + ((uint32_t*)resbuf)[0] = SWAP (ctx->A); + ((uint32_t*)resbuf)[1] = SWAP (ctx->B); + ((uint32_t*)resbuf)[2] = SWAP (ctx->C); + ((uint32_t*)resbuf)[3] = SWAP (ctx->D); + + return resbuf; +} + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ + +void* +pgm_md5_finish_ctx ( + struct pgm_md5_t* restrict ctx, + void* restrict resbuf + ) +{ +/* pre-conditions */ + pgm_assert (NULL != ctx); + pgm_assert (NULL != resbuf); + +/* Take yet unprocessed bytes into account. */ + const uint32_t bytes = ctx->buflen; + size_t pad; + +/* Now count remaining bytes. */ + ctx->total[0] += bytes; + if (ctx->total[0] < bytes) + ++ctx->total[1]; + + pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; + memcpy (&ctx->buffer[bytes], fillbuf, pad); + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + *(uint32_t*) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3); + *(uint32_t*) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) | + (ctx->total[0] >> 29)); + +/* Process last bytes. */ + _pgm_md5_process_block (ctx, ctx->buffer, bytes + pad + 8); + + return _pgm_md5_read_ctx (ctx, resbuf); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/md5_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/md5_unittest.c new file mode 100644 index 0000000..836af1f --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/md5_unittest.c @@ -0,0 +1,189 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for MD5 hashing (not actual algorithm). + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* mock state */ + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + + +#define MD5_DEBUG +#include "md5.c" + + +/* target: + * void + * pgm_md5_init_ctx ( + * struct pgm_md5_t* ctx + * ) + */ + +START_TEST (test_init_ctx_pass_001) +{ + struct pgm_md5_t ctx; + memset (&ctx, 0, sizeof(ctx)); + pgm_md5_init_ctx (&ctx); +} +END_TEST + +START_TEST (test_init_ctx_fail_001) +{ + pgm_md5_init_ctx (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_md5_process_bytes ( + * struct pgm_md5_t* ctx, + * const void* buffer, + * size_t len + * ) + */ + +START_TEST (test_process_bytes_pass_001) +{ + const char buffer[] = "i am not a string."; + struct pgm_md5_t ctx; + memset (&ctx, 0, sizeof(ctx)); + pgm_md5_init_ctx (&ctx); + pgm_md5_process_bytes (&ctx, buffer, sizeof(buffer)); +} +END_TEST + +START_TEST (test_process_bytes_fail_001) +{ + const char buffer[] = "i am not a string."; + pgm_md5_process_bytes (NULL, buffer, sizeof(buffer)); +} +END_TEST + +/* target: + * void* + * pgm_md5_finish_ctx ( + * struct pgm_md5_t* ctx, + * void* resbuf + * ) + */ + +START_TEST (test_finish_ctx_pass_001) +{ + const char* buffer = "i am not a string."; + const char* answer = "ef71-1617-4eef-9737-5e2b-5d7a-d015-b064"; + + char md5[1024]; + char resblock[16]; + struct pgm_md5_t ctx; + memset (&ctx, 0, sizeof(ctx)); + memset (resblock, 0, sizeof(resblock)); + pgm_md5_init_ctx (&ctx); + pgm_md5_process_bytes (&ctx, buffer, strlen(buffer)+1); + pgm_md5_finish_ctx (&ctx, resblock); + sprintf (md5, "%02.2hhx%02.2hhx-%02.2hhx%02.2hhx-%02.2hhx%02.2hhx-%02.2hhx%02.2hhx-%02.2hhx%02.2hhx-%02.2hhx%02.2hhx-%02.2hhx%02.2hhx-%02.2hhx%02.2hhx", + resblock[0], resblock[1], + resblock[2], resblock[3], + resblock[4], resblock[5], + resblock[6], resblock[7], + resblock[8], resblock[9], + resblock[10], resblock[11], + resblock[12], resblock[13], + resblock[14], resblock[15]); + g_message ("md5: %s", md5); + + fail_unless (0 == strcmp (md5, answer), "md5 mismatch"); +} +END_TEST + +START_TEST (test_finish_ctx_fail_001) +{ + char resblock[16]; + pgm_md5_finish_ctx (NULL, resblock); + fail ("reached"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_init_ctx = tcase_create ("init-ctx"); + suite_add_tcase (s, tc_init_ctx); + tcase_add_test (tc_init_ctx, test_init_ctx_pass_001); + tcase_add_test_raise_signal (tc_init_ctx, test_init_ctx_fail_001, SIGABRT); + + TCase* tc_process_bytes = tcase_create ("process_bytes"); + suite_add_tcase (s, tc_process_bytes); + tcase_add_test (tc_process_bytes, test_process_bytes_pass_001); + tcase_add_test_raise_signal (tc_process_bytes, test_process_bytes_fail_001, SIGABRT); + + TCase* tc_finish_ctx = tcase_create ("finish-ctx"); + suite_add_tcase (s, tc_finish_ctx); + tcase_add_test (tc_finish_ctx, test_finish_ctx_pass_001); + tcase_add_test_raise_signal (tc_finish_ctx, test_finish_ctx_fail_001, SIGABRT); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/mem.c b/3rdparty/openpgm-svn-r1085/pgm/mem.c new file mode 100644 index 0000000..85a6dee --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/mem.c @@ -0,0 +1,249 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable fail fast memory allocation. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#ifdef _WIN32 +# define strcasecmp stricmp +#endif +#include +#include + + +//#define MEM_DEBUG + + +/* globals */ + +bool pgm_mem_gc_friendly PGM_GNUC_READ_MOSTLY = FALSE; + + +/* locals */ + +struct pgm_debug_key_t { + const char* key; + unsigned value; +}; +typedef struct pgm_debug_key_t pgm_debug_key_t; + +static volatile uint32_t mem_ref_count = 0; + + +static +bool +debug_key_matches ( + const char* restrict key, + const char* restrict token, + unsigned length + ) +{ + for (; length; length--, key++, token++) + { + const char k = (*key == '_') ? '-' : tolower (*key ); + const char t = (*token == '_') ? '-' : tolower (*token); + if (k != t) + return FALSE; + } + return *key == '\0'; +} + +static +unsigned +pgm_parse_debug_string ( + const char* restrict string, + const pgm_debug_key_t* restrict keys, + const unsigned nkeys + ) +{ + unsigned result = 0; + + if (NULL == string) + return result; + + if (!strcasecmp (string, "all")) + { + for (unsigned i = 0; i < nkeys; i++) + result |= keys[i].value; + } + else if (!strcasecmp (string, "help")) + { + fprintf (stderr, "Supported debug values:"); + for (unsigned i = 0; i < nkeys; i++) + fprintf (stderr, " %s", keys[i].key); + fprintf (stderr, "\n"); + } + else + { + while (string) { + const char* q = strpbrk (string, ":;, \t"); + if (!q) + q = string + strlen (string); + for (unsigned i = 0; i < nkeys; i++) + if (debug_key_matches (keys[i].key, string, q - string)) + result |= keys[i].value; + string = q; + if (*string) + string++; + } + } + return result; +} + +void +pgm_mem_init (void) +{ + static const pgm_debug_key_t keys[] = { + { "gc-friendly", 1 }, + }; + + if (pgm_atomic_exchange_and_add32 (&mem_ref_count, 1) > 0) + return; + + const char *val = getenv ("PGM_DEBUG"); + const unsigned flags = !val ? 0 : pgm_parse_debug_string (val, keys, PGM_N_ELEMENTS (keys)); + if (flags & 1) + pgm_mem_gc_friendly = TRUE; +} + +void +pgm_mem_shutdown (void) +{ + pgm_return_if_fail (pgm_atomic_read32 (&mem_ref_count) > 0); + + if (pgm_atomic_exchange_and_add32 (&mem_ref_count, (uint32_t)-1) != 1) + return; + + /* nop */ +} + +/* malloc wrappers to hard fail */ +void* +pgm_malloc ( + const size_t n_bytes + ) +{ + if (PGM_LIKELY (n_bytes)) + { + void* mem = malloc (n_bytes); + if (mem) + return mem; + + pgm_fatal ("file %s: line %d (%s): failed to allocate %zu bytes", + __FILE__, __LINE__, __PRETTY_FUNCTION__, + n_bytes); + abort (); + } + return NULL; +} + +#define SIZE_OVERFLOWS(a,b) (PGM_UNLIKELY ((a) > SIZE_MAX / (b))) + +void* +pgm_malloc_n ( + const size_t n_blocks, + const size_t block_bytes + ) +{ + if (SIZE_OVERFLOWS (n_blocks, block_bytes)) { + pgm_fatal ("file %s: line %d (%s): overflow allocating %zu*%zu bytes", + __FILE__, __LINE__, __PRETTY_FUNCTION__, + n_blocks, block_bytes); + } + return pgm_malloc (n_blocks * block_bytes); +} + +void* +pgm_malloc0 ( + const size_t n_bytes + ) +{ + if (PGM_LIKELY (n_bytes)) + { + void* mem = calloc (1, n_bytes); + if (mem) + return mem; + + pgm_fatal ("file %s: line %d (%s): failed to allocate %zu bytes", + __FILE__, __LINE__, __PRETTY_FUNCTION__, + n_bytes); + abort (); + } + return NULL; +} + +void* +pgm_malloc0_n ( + const size_t n_blocks, + const size_t block_bytes + ) +{ + if (PGM_LIKELY (n_blocks && block_bytes)) + { + void* mem = calloc (n_blocks, block_bytes); + if (mem) + return mem; + + pgm_fatal ("file %s: line %d (%s): failed to allocate %zu*%zu bytes", + __FILE__, __LINE__, __PRETTY_FUNCTION__, + n_blocks, block_bytes); + abort (); + } + return NULL; +} + +void* +pgm_memdup ( + const void* mem, + const size_t n_bytes + ) +{ + void* new_mem; + + if (PGM_LIKELY (NULL != mem)) + { + new_mem = pgm_malloc (n_bytes); + memcpy (new_mem, mem, n_bytes); + } + else + new_mem = NULL; + + return new_mem; +} + +void* +pgm_realloc ( + void* mem, + const size_t n_bytes + ) +{ + return realloc (mem, n_bytes); +} + +void +pgm_free ( + void* mem + ) +{ + if (PGM_LIKELY (NULL != mem)) + free (mem); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/memcheck b/3rdparty/openpgm-svn-r1085/pgm/memcheck new file mode 100755 index 0000000..fbfe59c --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/memcheck @@ -0,0 +1,13 @@ +#!/bin/sh + +G_SLICE=always-malloc \ +G_DEBUG=gc-friendly \ + valgrind \ + -v \ + --tool=memcheck \ + --leak-check=full \ + --num-callers=40 \ + --gen-suppressions=no \ + --show-reachable=yes \ + --suppressions=valgrind.supp \ + $* diff --git a/3rdparty/openpgm-svn-r1085/pgm/messages.c b/3rdparty/openpgm-svn-r1085/pgm/messages.c new file mode 100644 index 0000000..9fa281b --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/messages.c @@ -0,0 +1,173 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * basic message reporting. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + + +/* globals */ + +/* bit mask for trace role modules */ +int pgm_log_mask PGM_GNUC_READ_MOSTLY = 0xffff; +int pgm_min_log_level PGM_GNUC_READ_MOSTLY = PGM_LOG_LEVEL_NORMAL; + + +/* locals */ + +static const char log_levels[8][6] = { + "Uknown", + "Debug", + "Trace", + "Minor", + "Info", + "Warn", + "Error", + "Fatal" +}; + +static volatile uint32_t messages_ref_count = 0; +static pgm_mutex_t messages_mutex; +static pgm_log_func_t log_handler PGM_GNUC_READ_MOSTLY = NULL; +static void* log_handler_closure PGM_GNUC_READ_MOSTLY = NULL; + +static inline const char* log_level_text (const int) PGM_GNUC_PURE; + + +static inline +const char* +log_level_text ( + const int log_level + ) +{ + switch (log_level) { + default: return log_levels[0]; + case PGM_LOG_LEVEL_DEBUG: return log_levels[1]; + case PGM_LOG_LEVEL_TRACE: return log_levels[2]; + case PGM_LOG_LEVEL_MINOR: return log_levels[3]; + case PGM_LOG_LEVEL_NORMAL: return log_levels[4]; + case PGM_LOG_LEVEL_WARNING: return log_levels[5]; + case PGM_LOG_LEVEL_ERROR: return log_levels[6]; + case PGM_LOG_LEVEL_FATAL: return log_levels[7]; + } +} + +/* reference counted init and shutdown + */ + +void +pgm_messages_init (void) +{ + if (pgm_atomic_exchange_and_add32 (&messages_ref_count, 1) > 0) + return; + + pgm_mutex_init (&messages_mutex); + + const char* log_mask = getenv ("PGM_LOG_MASK"); + if (NULL != log_mask) { + unsigned int value = 0; + if (1 == sscanf (log_mask, "0x%4x", &value)) + pgm_log_mask = value; + } + const char *min_log_level = getenv ("PGM_MIN_LOG_LEVEL"); + if (NULL != min_log_level) { + switch (min_log_level[0]) { + case 'D': pgm_min_log_level = PGM_LOG_LEVEL_DEBUG; break; + case 'T': pgm_min_log_level = PGM_LOG_LEVEL_TRACE; break; + case 'M': pgm_min_log_level = PGM_LOG_LEVEL_MINOR; break; + case 'N': pgm_min_log_level = PGM_LOG_LEVEL_NORMAL; break; + case 'W': pgm_min_log_level = PGM_LOG_LEVEL_WARNING; break; + case 'E': pgm_min_log_level = PGM_LOG_LEVEL_ERROR; break; + case 'F': pgm_min_log_level = PGM_LOG_LEVEL_FATAL; break; + default: break; + } + } +} + +void +pgm_messages_shutdown (void) +{ + pgm_return_if_fail (pgm_atomic_read32 (&messages_ref_count) > 0); + + if (pgm_atomic_exchange_and_add32 (&messages_ref_count, (uint32_t)-1) != 1) + return; + + pgm_mutex_free (&messages_mutex); +} + +/* set application handler for log messages, returns previous value, + * default handler value is NULL. + */ + +pgm_log_func_t +pgm_log_set_handler ( + pgm_log_func_t handler, + void* closure + ) +{ + pgm_log_func_t previous_handler; + pgm_mutex_lock (&messages_mutex); + previous_handler = log_handler; + log_handler = handler; + log_handler_closure = closure; + pgm_mutex_unlock (&messages_mutex); + return previous_handler; +} + +void +pgm__log ( + const int log_level, + const char* format, + ... + ) +{ + va_list args; + + va_start (args, format); + pgm__logv (log_level, format, args); + va_end (args); +} + +void +pgm__logv ( + const int log_level, + const char* format, + va_list args + ) +{ + char tbuf[ 1024 ]; + + pgm_mutex_lock (&messages_mutex); + const int offset = sprintf (tbuf, "%s: ", log_level_text (log_level)); + vsnprintf (tbuf+offset, sizeof(tbuf)-offset, format, args); + tbuf[ sizeof(tbuf) ] = '\0'; + if (log_handler) + log_handler (log_level, tbuf, log_handler_closure); + else { +/* ignore return value */ + write (STDOUT_FILENO, tbuf, strlen (tbuf)); + write (STDOUT_FILENO, "\n", 1); + } + + pgm_mutex_unlock (&messages_mutex); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/mibs/PGM-MIB-petrova-01.txt b/3rdparty/openpgm-svn-r1085/pgm/mibs/PGM-MIB-petrova-01.txt new file mode 100644 index 0000000..28ff72c --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/mibs/PGM-MIB-petrova-01.txt @@ -0,0 +1,5459 @@ +---------------------------------------------------------------- +-- +-- Pragmatic General Multicast (PGM) MIB +-- +---------------------------------------------------------------- +-- +-- +-- Full MIB for the PGM protocol incorporating Network Element +-- (router), source, receiver and DLR functionality +-- +-- extracted from draft-petrova-pgmmib-01.txt + +PGM-MIB DEFINITIONS ::= BEGIN +IMPORTS + OBJECT-TYPE, Counter32, Integer32, Unsigned32, NOTIFICATION-TYPE, + MODULE-IDENTITY, IpAddress, TimeTicks, experimental, BITS + FROM SNMPv2-SMI + MODULE-COMPLIANCE, OBJECT-GROUP, NOTIFICATION-GROUP + FROM SNMPv2-CONF + InterfaceIndex + FROM IF-MIB; + +pgmMIB MODULE-IDENTITY + LAST-UPDATED "200205010000Z" + ORGANIZATION + "Cisco Systems + Tibco Software Inc + Nortel Networks" + CONTACT-INFO + " Richard Edmonstone + redmonst@cisco.com + +44 131 561 3621 + Cisco Systems, Inc. + 170 West Tasman Drive, + San Jose, CA 95134 + USA + + Rajiv Raghunarayan + raraghun@cisco.com + +91 80 532 1300 + Cisco Systems, Inc. + 170 West Tasman Drive, + San Jose, CA 95134 + USA + + Devendra Raut + draut@nortelnetworks.com + (408)495-2859 + Nortel Networks + 4401 Great America Parkway, + Santa Clara, CA 95052 + + Moses Sun + mosun@nortelnetworks.com + (979)694-7156 + Nortel Networks + 4401 Great America Parkway + Santa Clara, CA, + USA + + Todd L. Montgomery + tmontgomery@tibco.com + (304)291-5972 + Tibco Software Inc. + 29W110 Butterfield Rd, Suite 205 + Warrenville, IL 60555 + USA + + Michael Garwood + mgarwood@tibco.com + (630)393-7363 ext.275 + Tibco Software Inc. + 29W110 Butterfield Rd, Suite 205 + Warrenville, IL 60555 + USA + + Luna Petrova + lpetrova@tibco.com + (630)393-7363 ext.330 + Tibco Software Inc. + 29W110 Butterfield Rd, Suite 205 + Warrenville, IL 60555 + USA" + DESCRIPTION + "The MIB module for managing PGM implementations." + REVISION "200205010000Z" + DESCRIPTION + "Rev 2.0: SNMP Notifications added to the MIB." + ::= { experimental 112 } -- assigned by IANA. + +pgm OBJECT IDENTIFIER ::= { pgmMIB 1 } +pgmNetworkElement OBJECT IDENTIFIER ::= { pgm 1 } +pgmSource OBJECT IDENTIFIER ::= { pgm 2 } +pgmReceiver OBJECT IDENTIFIER ::= { pgm 3 } +pgmDLR OBJECT IDENTIFIER ::= { pgm 4 } +pgmNotificationPrefix OBJECT IDENTIFIER ::= { pgmMIB 2 } + +-- PGM Network Element + +pgmNeEnable OBJECT-TYPE + SYNTAX INTEGER { + enable(1), + disable(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Enable/Disable Parameter indicates whether + this PGM operation is enabled or disabled." + DEFVAL { enable } + ::= { pgmNetworkElement 1 } + +pgmNeSessionLifeTime OBJECT-TYPE + SYNTAX Unsigned32(0..2147483647) + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The length of the idle time (seconds) following + which a PGM session will be aged out. An idle PGM + session means there is no SPM message received + from the upstream. + Value of 0 indicates no timeout." + DEFVAL { 300 } + ::= { pgmNetworkElement 2 } + +pgmNeMaxReXmitStates OBJECT-TYPE + SYNTAX Integer32(-2..2147483647) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The Maximum number of retransmission state entries. + The value of -1 means network element has no + limitation. + The value of -2 means not supported by this + implementation." + ::= { pgmNetworkElement 3 } + +pgmNeMaxSessions OBJECT-TYPE + SYNTAX Integer32(-2..2147483647) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The maximum number of state sessions supported. + The value of -1 means network element has no + limitation. + The value of -2 means not supported by this + implementation." + ::= { pgmNetworkElement 4 } + +-- The PGM NE Network Interface + +-- The PGM NE Network Interface tables contain +-- per-interface information about the PGM protocol. +-- The information is grouped into three major categories: +-- fault, configuration and performance management. + +pgmNeInterface OBJECT IDENTIFIER ::= { pgmNetworkElement 100 } + +pgmNeTotalInterfacesNumberOfEntries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of entries in the PGM Interface + table." + ::= { pgmNeInterface 1 } + +-- The PGM NE Network Interface configuration table + +pgmNeIfConfigTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmNeIfConfigEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per interface configuration + information relating to PGM Network Element + operation." + ::= {pgmNeInterface 3} + +pgmNeIfConfigEntry OBJECT-TYPE + SYNTAX PgmNeIfConfigEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per Interface Configuration Information." + INDEX { pgmNeIfConfigIndex } + ::= { pgmNeIfConfigTable 1 } + +PgmNeIfConfigEntry ::= SEQUENCE { + pgmNeIfConfigIndex + InterfaceIndex, + pgmNeIfPgmEnable + INTEGER, + pgmNeIfNakRptInterval + Unsigned32, + pgmNeIfNakRptRate + Unsigned32, + pgmNeIfNakRdataInterval + Unsigned32, + pgmNeIfNakEliminateInterval + Unsigned32 + } + +pgmNeIfConfigIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A unique value for each interface. Its value + ranges between 1 and the value of ifNumber. The + value for each interface must remain constant at + least from one re-initialization of the entity's + network management system to the next + re-initialization." + ::= { pgmNeIfConfigEntry 1 } + +pgmNeIfPgmEnable OBJECT-TYPE + SYNTAX INTEGER { + enable(1), + disable(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Allows PGM to be enabled and disabled per + Network Interface. + + PGM can be enabled or disabled per Network + Interface, only if PGM is enabled for this + Network Element." + ::= { pgmNeIfConfigEntry 2 } + +pgmNeIfNakRptInterval OBJECT-TYPE + SYNTAX Unsigned32(1..4294967295) + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The length of time (milliseconds) for which a + network element will repeat a NAK while waiting + for a corresponding NCF. This interval is counted + down from the transmission of a NAK." + DEFVAL { 100 } + ::= { pgmNeIfConfigEntry 3 } + +pgmNeIfNakRptRate OBJECT-TYPE + SYNTAX Unsigned32(1..4294967295) + UNITS "number of NAKs per second" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The rate at which NAKs are repeated." + DEFVAL { 2 } + ::= { pgmNeIfConfigEntry 4 } + +pgmNeIfNakRdataInterval OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The length of time (milliseconds) for which + a network element will wait for the + corresponding RDATA. This interval is counted + down from the time a matching NCF is received. + This value must be greater than the + pgmNeIfNakEliminateInterval." + DEFVAL { 10000 } + ::= { pgmNeIfConfigEntry 5 } + +pgmNeIfNakEliminateInterval OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The length of time (milliseconds) for which + a network element will eliminate NAKs for + a specific TSI/SQN. This interval is counted + down from the time the first NAK is + established. This value must + be smaller than pgmNeIfNakRdataInterval." + DEFVAL { 5000 } + ::= { pgmNeIfConfigEntry 6 } + +-- The PGM NE Interface performance table. +-- This is primarily statistical information +-- about packets received and sent on the interface + +pgmNeIfPerformanceTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmNeIfPerformanceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per interface performance + information related to PGM Network Element + operation." + ::= {pgmNeInterface 4} + +pgmNeIfPerformanceEntry OBJECT-TYPE + SYNTAX PgmNeIfPerformanceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per Interface Information for Network Elements." + INDEX { pgmNeIfPerformanceIndex } + ::= { pgmNeIfPerformanceTable 1 } + +PgmNeIfPerformanceEntry ::= SEQUENCE { + pgmNeIfPerformanceIndex + InterfaceIndex, + pgmNeIfReXmitStates + Counter32, + pgmNeIfReXmitTimedOut + Counter32, + pgmNeIfInSpms + Counter32, + pgmNeIfOutSpms + Counter32, + pgmNeIfInParitySpms + Counter32, + pgmNeIfOutParitySpms + Counter32, + pgmNeIfInRdata + Counter32, + pgmNeIfOutRdata + Counter32, + pgmNeIfInParityRdata + Counter32, + pgmNeIfOutParityRdata + Counter32, + pgmNeIfInRdataNoSessionErrors + Counter32, + pgmNeIfUniqueNaks + Counter32, + pgmNeIfInNaks + Counter32, + pgmNeIfOutNaks + Counter32, + pgmNeIfUniqueParityNaks + Counter32, + pgmNeIfInParityNaks + Counter32, + pgmNeIfOutParityNaks + Counter32, + pgmNeIfInNakNoSessionErrors + Counter32, + pgmNeIfInNakSeqErrors + Counter32, + pgmNeIfInParityNakTgErrors + Counter32, + pgmNeIfInNnaks + Counter32, + pgmNeIfOutNnaks + Counter32, + pgmNeIfInParityNnaks + Counter32, + pgmNeIfOutParityNnaks + Counter32, + pgmNeIfInNnakNoSessionErrors + Counter32, + pgmNeIfInNcfs + Counter32, + pgmNeIfOutNcfs + Counter32, + pgmNeIfInParityNcfs + Counter32, + pgmNeIfOutParityNcfs + Counter32, + pgmNeIfInNcfNoSessionErrors + Counter32, + pgmNeIfInRedirectNcfs + Counter32, + pgmNeIfMalformed + Counter32, + pgmNeIfSpmFromSource + Counter32, + pgmNeIfSpmBadSqn + Counter32, + pgmNeIfSpmError + Counter32, + pgmNeIfPollRandomIgnore + Counter32, + pgmNeIfPollTsiStateError + Counter32, + pgmNeIfPollParentError + Counter32, + pgmNeIfPollTypeError + Counter32, + pgmNeIfPollError + Counter32, + pgmNeIfPollSuccess + Counter32, + pgmNeIfPollOriginated + Counter32, + pgmNeIfPolrNoState + Counter32, + pgmNeIfPolrError + Counter32, + pgmNeIfPolrParityError + Counter32, + pgmNeIfPolrSuccess + Counter32, + pgmNeIfPolrOriginated + Counter32, + pgmNeIfNcfError + Counter32, + pgmNeIfNcfParityError + Counter32, + pgmNeIfNcfPartialParity + Counter32, + pgmNeIfNcfReceived + Counter32, + pgmNeIfNcfAnticipated + Counter32, + pgmNeIfNcfRedirecting + Counter32, + pgmNeIfNakEliminated + Counter32, + pgmNeIfNakError + Counter32, + pgmNeIfNakParityError + Counter32, + pgmNeIfNNakEliminated + Counter32, + pgmNeIfNNakError + Counter32, + pgmNeIfNNakParityError + Counter32, + pgmNeIfNNakCongestionReports + Counter32, + pgmNeIfNakRetryExpired + Counter32, + pgmNeIfNakRetryExpiredDLR + Counter32, + pgmNeIfNakForwardedDLR + Counter32, + pgmNeIfNakRetransmitted + Counter32, + pgmNeIfRdataEliminatedOIF + Counter32, + pgmNeIfRdataEliminatedSqn + Counter32, + pgmNeIfInRdataFragments + Counter32, + pgmNeIfRdataFragmentsNoSessionErrors + Counter32, + pgmNeIfRdataFragmentsEliminatedOIF + Counter32, + pgmNeIfRdataFragmentsEliminatedSqn + Counter32, + pgmNeIfOutRdataFragments + Counter32 +} + +pgmNeIfPerformanceIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A unique value for each interface. Its value + ranges between 1 and the value of ifNumber. + The value for each interface must remain + constant at least from one re-initialization + of the entity's network management system + to the next re-initialization." + ::= { pgmNeIfPerformanceEntry 1 } + +pgmNeIfReXmitStates OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total retransmit state entries for this + interface." + ::= { pgmNeIfPerformanceEntry 2 } + +pgmNeIfReXmitTimedOut OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of timed-out retransmit state + entries for this interface." + ::= { pgmNeIfPerformanceEntry 3 } + +pgmNeIfInSpms OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of SPMs received on the PGM + interface." + ::= { pgmNeIfPerformanceEntry 4 } + +pgmNeIfOutSpms OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of SPMs sent out from the PGM + interface." + ::= { pgmNeIfPerformanceEntry 5 } + +pgmNeIfInParitySpms OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity SPMs received on the + PGM interface." + ::= { pgmNeIfPerformanceEntry 6 } + +pgmNeIfOutParitySpms OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity SPMs sent out from the + PGM interface." + ::= { pgmNeIfPerformanceEntry 7 } + +pgmNeIfInRdata OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of RDATA received on the PGM + interface." + ::= { pgmNeIfPerformanceEntry 8 } + +pgmNeIfOutRdata OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of RDATA sent out from the + PGM interface." + ::= { pgmNeIfPerformanceEntry 9 } + +pgmNeIfInParityRdata OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity RDATA received on the + PGM interface." + ::= { pgmNeIfPerformanceEntry 10 } + +pgmNeIfOutParityRdata OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity RDATA sent out from + the PGM interface." + ::= { pgmNeIfPerformanceEntry 11 } + +pgmNeIfInRdataNoSessionErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received RDATA discarded because + of no session." + ::= { pgmNeIfPerformanceEntry 12 } + +pgmNeIfUniqueNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of unique NAKs received on + this interface." + ::= { pgmNeIfPerformanceEntry 13 } + +pgmNeIfInNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NAKs received on the PGM + interface." + ::= { pgmNeIfPerformanceEntry 14 } + +pgmNeIfOutNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NAKs sent out from the + PGM interface." + ::= { pgmNeIfPerformanceEntry 15 } + +pgmNeIfUniqueParityNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of unique parity NAKs received + on this interface." + ::= { pgmNeIfPerformanceEntry 16 } + +pgmNeIfInParityNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity NAKs received on the + PGM interface." + ::= { pgmNeIfPerformanceEntry 17 } + +pgmNeIfOutParityNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity NAKs sent out from + the PGM interface." + ::= { pgmNeIfPerformanceEntry 18 } + +pgmNeIfInNakNoSessionErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received NAKs discarded because of + no session." + ::= { pgmNeIfPerformanceEntry 19 } + +pgmNeIfInNakSeqErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received NAKs discarded because + of out of sequence (out of retransmit window)." + ::= { pgmNeIfPerformanceEntry 20 } + +pgmNeIfInParityNakTgErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received parity NAKs discarded + because out of parity TG window." + ::= { pgmNeIfPerformanceEntry 21 } + +pgmNeIfInNnaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NNAKs received on the PGM + interface." + ::= { pgmNeIfPerformanceEntry 22 } + +pgmNeIfOutNnaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NNAKs sent out from the + PGM interface." + ::= { pgmNeIfPerformanceEntry 23 } + +pgmNeIfInParityNnaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity NNAKs received on + the PGM interface." + ::= { pgmNeIfPerformanceEntry 24 } + +pgmNeIfOutParityNnaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity NNAKs sent out from + the PGM interface." + ::= { pgmNeIfPerformanceEntry 25 } + +pgmNeIfInNnakNoSessionErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received NNAKs discarded because + of no session." + ::= { pgmNeIfPerformanceEntry 26 } + +pgmNeIfInNcfs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NCFs received on the PGM + interface." + ::= { pgmNeIfPerformanceEntry 27 } + +pgmNeIfOutNcfs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NCFs sent out from the PGM + interface." + ::= { pgmNeIfPerformanceEntry 28 } + +pgmNeIfInParityNcfs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity NCFs received on the + PGM interface." + ::= { pgmNeIfPerformanceEntry 29 } + +pgmNeIfOutParityNcfs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity NCFs sent out from + the PGM interface." + ::= { pgmNeIfPerformanceEntry 30 } + +pgmNeIfInNcfNoSessionErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received NCFs discarded because + of no session." + ::= { pgmNeIfPerformanceEntry 31 } + +pgmNeIfInRedirectNcfs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of redirected NCFs received on the + PGM interface." + ::= { pgmNeIfPerformanceEntry 32 } + +pgmNeIfMalformed OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of malformed PGM packets." + ::= { pgmNeIfPerformanceEntry 33 } + +pgmNeIfSpmFromSource OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of SPM packets received from source." + ::= { pgmNeIfPerformanceEntry 34 } + +pgmNeIfSpmBadSqn OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of SPM packets discarded due to bad + SQN." + ::= { pgmNeIfPerformanceEntry 35 } + +pgmNeIfSpmError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of SPM packets discarded due to + operational error. Some examples of operational + errors are failure to create TSI state for SPM, + parity SPM for a TSI with no parity." + ::= { pgmNeIfPerformanceEntry 36 } + +pgmNeIfPollRandomIgnore OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of POLL packets not replied due to random + condition failing." + ::= { pgmNeIfPerformanceEntry 37 } + +pgmNeIfPollTsiStateError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of POLL packets discarded due to no + matching TSI state." + ::= { pgmNeIfPerformanceEntry 38 } + +pgmNeIfPollParentError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of POLL packets discarded due to + unknown parent." + ::= { pgmNeIfPerformanceEntry 39 } + +pgmNeIfPollTypeError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of POLL packets discarded due to failed + type matching." + ::= { pgmNeIfPerformanceEntry 40 } + +pgmNeIfPollError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of POLL packets discarded due to + operational error." + ::= { pgmNeIfPerformanceEntry 41 } + +pgmNeIfPollSuccess OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of successfully scheduled POLRs." + ::= { pgmNeIfPerformanceEntry 42 } + +pgmNeIfPollOriginated OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of polls originated on this interface." + ::= { pgmNeIfPerformanceEntry 43 } + +pgmNeIfPolrNoState OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of POLRs discarded due to no matching + state." + ::= { pgmNeIfPerformanceEntry 44 } + +pgmNeIfPolrError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of POLRs discarded due to operational + error." + ::= { pgmNeIfPerformanceEntry 45 } + +pgmNeIfPolrParityError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of parity POLRs received for non-parity + TSI." + ::= { pgmNeIfPerformanceEntry 46 } + +pgmNeIfPolrSuccess OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of POLRs recorded successfully." + ::= { pgmNeIfPerformanceEntry 47 } + +pgmNeIfPolrOriginated OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of POLRs originated by this interface." + ::= { pgmNeIfPerformanceEntry 48 } + +pgmNeIfNcfError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NCFs ignored due to no packet memory, + due to packet processing errors." + ::= { pgmNeIfPerformanceEntry 49 } + +pgmNeIfNcfParityError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NCFs ignored. Incremented when a parity + NCF is received on a session for which no parity + capability has been advertised in the session's + SPMs." + ::= { pgmNeIfPerformanceEntry 50 } + +pgmNeIfNcfPartialParity OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NCFs ignored due to not enough parity + blocks acknowledged." + ::= { pgmNeIfPerformanceEntry 51 } + +pgmNeIfNcfReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NCFs that confirm an outstanding NAK." + ::= { pgmNeIfPerformanceEntry 52 } + +pgmNeIfNcfAnticipated OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NCFs that cause NAK anticipation." + ::= { pgmNeIfPerformanceEntry 53 } + +pgmNeIfNcfRedirecting OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NCFs received as consequence of + redirected NAK." + ::= { pgmNeIfPerformanceEntry 54 } + +pgmNeIfNakEliminated OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAKs eliminated by retransmission + state." + ::= { pgmNeIfPerformanceEntry 55 } + +pgmNeIfNakError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of errors creating retransmission state + or NAK, due to NAK packet processing." + ::= { pgmNeIfPerformanceEntry 56 } + +pgmNeIfNakParityError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAKs ignored, due to no parity + available. Incremented when parity NAK is + received on this session, for which no parity + capability has been advartised." + ::= { pgmNeIfPerformanceEntry 57 } + +pgmNeIfNNakEliminated OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NNAKs eliminated by retransmission + state." + ::= { pgmNeIfPerformanceEntry 58 } + +pgmNeIfNNakError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of errors encountered creating + retransmission state OR nak." + ::= { pgmNeIfPerformanceEntry 59 } + +pgmNeIfNNakParityError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of Null NAKs ignored, due to no parity + available. Incremented when parity NNAK is + received on this session, for which no parity + capability has been advartised." + ::= { pgmNeIfPerformanceEntry 60 } + +pgmNeIfNNakCongestionReports OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAKs forwarded as NNAK as congestion + report only." + ::= { pgmNeIfPerformanceEntry 61 } + +pgmNeIfNakRetryExpired OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NAKs timed out after + retrying." + ::= { pgmNeIfPerformanceEntry 62 } + +pgmNeIfNakRetryExpiredDLR OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAKs unconfirmed by DLR." + ::= { pgmNeIfPerformanceEntry 63 } + +pgmNeIfNakForwardedDLR OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAKs forwarded out this i/f to DLR + with retransmission state." + ::= { pgmNeIfPerformanceEntry 64 } + +pgmNeIfNakRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Total number of NAKs retransmitted out this i/f." + ::= { pgmNeIfPerformanceEntry 65 } + +pgmNeIfRdataEliminatedOIF OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of RDATA packets eliminated by lack of + OIF's." + ::= { pgmNeIfPerformanceEntry 66 } + +pgmNeIfRdataEliminatedSqn OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of RDATA packets eliminated by lack of + SQN." + ::= { pgmNeIfPerformanceEntry 67 } + +pgmNeIfInRdataFragments OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Total number of RDATA fragments received." + ::= { pgmNeIfPerformanceEntry 68 } + +pgmNeIfRdataFragmentsNoSessionErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of RDATA fragments eliminated by lack of + GSI." + ::= { pgmNeIfPerformanceEntry 69 } + +pgmNeIfRdataFragmentsEliminatedOIF OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of RDATA fragments eliminated by lack of + OIFs." + ::= { pgmNeIfPerformanceEntry 70 } + +pgmNeIfRdataFragmentsEliminatedSqn OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of RDATA fragments eliminated by lack of + SQN." + ::= { pgmNeIfPerformanceEntry 71 } + +pgmNeIfOutRdataFragments OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Total number of RDATA fragments forwarded." + ::= { pgmNeIfPerformanceEntry 72 } + +-- +-- PGM Network Element Transport Session Identifier +-- +pgmNeTsi OBJECT IDENTIFIER ::= { pgmNetworkElement 101 } + +pgmNeTotalTsiNumberOfEntries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of sessions in the PGM NE TSI + table." + ::= { pgmNeTsi 1 } + +-- The PGM Transport Session Identifier (TSI) table +-- The TSI information is grouped into three major categories: +-- fault, configuration and performance management. + +-- The PGM NE TSI fault management table +-- This table contains state and some general +-- per TSI information + +pgmNeTsiTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmNeTsiEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per-tsi state information." + ::= {pgmNeTsi 2} + +pgmNeTsiEntry OBJECT-TYPE + SYNTAX PgmNeTsiEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Transport Session is identified by Global ID and + Source Port." + INDEX { pgmNeTsiGlobalId, pgmNeTsiDataSourcePort } + ::= { pgmNeTsiTable 1 } + +PgmNeTsiEntry ::= SEQUENCE { + pgmNeTsiGlobalId + OCTET STRING, + pgmNeTsiDataSourcePort + Unsigned32, + pgmNeTsiStateBits + BITS, + pgmNeTsiDataDestinationPort + Unsigned32, + pgmNeTsiSourceAddress + IpAddress, + pgmNeTsiGroupAddress + IpAddress, + pgmNeTsiUpstreamAddress + IpAddress, + pgmNeTsiUpstreamIfIndex + InterfaceIndex, + pgmNeTsiDlrAddress + IpAddress + } + +pgmNeTsiGlobalId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The Globally unique source identifier for this + transport session." + ::= {pgmNeTsiEntry 1 } + +pgmNeTsiDataSourcePort OBJECT-TYPE + SYNTAX Unsigned32 (0..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Data source port." + ::= {pgmNeTsiEntry 2} + +pgmNeTsiStateBits OBJECT-TYPE + SYNTAX BITS { initialising(0), + spmSqnStateValid(1), + dlrCanProvideParity(2) } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "State associated with the TSI." + ::= {pgmNeTsiEntry 3 } + +pgmNeTsiDataDestinationPort OBJECT-TYPE + SYNTAX Unsigned32 (0..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Data destination port." + ::= {pgmNeTsiEntry 4 } + +pgmNeTsiSourceAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "IP address of the source." + ::= {pgmNeTsiEntry 5 } + +pgmNeTsiGroupAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Multicast group destination address." + ::= {pgmNeTsiEntry 6 } + +pgmNeTsiUpstreamAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The IP address of the upstream PGM neighbouring + element for this TSI." + ::= { pgmNeTsiEntry 7 } + +pgmNeTsiUpstreamIfIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The index of the upstream PGM element for the + entry." + ::= { pgmNeTsiEntry 8 } + +pgmNeTsiDlrAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "IP Address of a known DLR that will be used if + required." + ::= {pgmNeTsiEntry 9 } + + +-- PGM Network Element TSI Configuration Management Table +-- Since the Network Element cannot be configured +-- per TSI, configuration table is not implemented + + +-- PGM Network Element TSI Performance Management Table + +pgmNeTsiPerformanceTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmNeTsiPerformanceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding details of every transport + flow known by the Network Element." + ::= {pgmNeTsi 4} + +pgmNeTsiPerformanceEntry OBJECT-TYPE + SYNTAX PgmNeTsiPerformanceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Transport session description." + INDEX { pgmNeTsiPerformanceGlobalId, + pgmNeTsiPerformanceDataSourcePort } + ::= { pgmNeTsiPerformanceTable 1 } + +PgmNeTsiPerformanceEntry ::= SEQUENCE { + pgmNeTsiPerformanceGlobalId + OCTET STRING, + pgmNeTsiPerformanceDataSourcePort + Unsigned32, + pgmNeTsiSessionTrailEdgeSeq + Counter32, + pgmNeTsiSessionIncrSeq + Counter32, + pgmNeTsiLeadEdgeSeq + Counter32, + pgmNeTsiInSpms + Counter32, + pgmNeTsiOutSpms + Counter32, + pgmNeTsiInParitySpms + Counter32, + pgmNeTsiOutParitySpms + Counter32, + pgmNeTsiTotalReXmitStates + Counter32, + pgmNeTsiTotalReXmitTimedOut + Counter32, + pgmNeTsiInRdata + Counter32, + pgmNeTsiOutRdata + Counter32, + pgmNeTsiInParityRdata + Counter32, + pgmNeTsiOutParityRdata + Counter32, + pgmNeTsiInRdataNoStateErrors + Counter32, + pgmNeTsiUniqueNaks + Counter32, + pgmNeTsiInNaks + Counter32, + pgmNeTsiOutNaks + Counter32, + pgmNeTsiUniqueParityNaks + Counter32, + pgmNeTsiInParityNaks + Counter32, + pgmNeTsiOutParityNaks + Counter32, + pgmNeTsiInNakSeqErrors + Counter32, + pgmNeTsiInNnaks + Counter32, + pgmNeTsiOutNnaks + Counter32, + pgmNeTsiInParityNnaks + Counter32, + pgmNeTsiOutParityNnaks + Counter32, + pgmNeTsiInNcfs + Counter32, + pgmNeTsiOutNcfs + Counter32, + pgmNeTsiInParityNcfs + Counter32, + pgmNeTsiOutParityNcfs + Counter32, + pgmNeTsiSpmSequenceNumber + Unsigned32, + pgmNeTsiTransmissionGroupSize + Unsigned32, + pgmNeTsiTimeout + TimeTicks, + pgmNeTsiLastTtl + Unsigned32, + pgmNeTsiLinkLossRate + Unsigned32, + pgmNeTsiPathLossRate + Unsigned32, + pgmNeTsiReceiverLossRate + Unsigned32, + pgmNeTsiCongestionReportLead + Unsigned32, + pgmNeTsiCongestionReportWorstReceiver + IpAddress +} + +pgmNeTsiPerformanceGlobalId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The Globally unique source identifier for this + transport session." + ::= {pgmNeTsiPerformanceEntry 1 } + +pgmNeTsiPerformanceDataSourcePort OBJECT-TYPE + SYNTAX Unsigned32 (0..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Data source port." + ::= {pgmNeTsiPerformanceEntry 2} + +pgmNeTsiSessionTrailEdgeSeq OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The trailing edge sequence of the transmit + window." + ::= { pgmNeTsiPerformanceEntry 3 } + +pgmNeTsiSessionIncrSeq OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The sequence number defining the leading edge of + the increment window." + ::= { pgmNeTsiPerformanceEntry 4 } + +pgmNeTsiLeadEdgeSeq OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The leading edge sequence of the transmit + window." + ::= { pgmNeTsiPerformanceEntry 5 } + +pgmNeTsiInSpms OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of SPMs received for this + session." + ::= { pgmNeTsiPerformanceEntry 6 } + +pgmNeTsiOutSpms OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of SPMs sent out for this + session." + ::= { pgmNeTsiPerformanceEntry 7 } + +pgmNeTsiInParitySpms OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of Parity SPMs received for + this session." + ::= { pgmNeTsiPerformanceEntry 8 } + +pgmNeTsiOutParitySpms OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of Parity SPMs sent out for + this session." + ::= { pgmNeTsiPerformanceEntry 9 } + +pgmNeTsiTotalReXmitStates OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total retransmit states for this session." + ::= { pgmNeTsiPerformanceEntry 10 } + +pgmNeTsiTotalReXmitTimedOut OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total timed-out retransmit state entries for + this session." + ::= { pgmNeTsiPerformanceEntry 11 } + +pgmNeTsiInRdata OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of RDATAs received for this + session." + ::= { pgmNeTsiPerformanceEntry 12 } + +pgmNeTsiOutRdata OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of RDATAs sent out from this + session." + ::= { pgmNeTsiPerformanceEntry 13 } + +pgmNeTsiInParityRdata OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity RDATAs received for + this session." + ::= { pgmNeTsiPerformanceEntry 14 } + +pgmNeTsiOutParityRdata OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity RDATAs sent out from + this session." + ::= { pgmNeTsiPerformanceEntry 15 } + +pgmNeTsiInRdataNoStateErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of received RDATA discarded + due to no retransmit state." + ::= { pgmNeTsiPerformanceEntry 16 } + +pgmNeTsiUniqueNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of unique NAKs received for + this session." + ::= { pgmNeTsiPerformanceEntry 17 } + +pgmNeTsiInNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NAKs received for this + session." + ::= { pgmNeTsiPerformanceEntry 18 } + +pgmNeTsiOutNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NAKs sent out from this + session." + ::= { pgmNeTsiPerformanceEntry 19 } + +pgmNeTsiUniqueParityNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of unique parity NAKs received + for this session." + ::= { pgmNeTsiPerformanceEntry 20 } + +pgmNeTsiInParityNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity NAKs received for + this session." + ::= { pgmNeTsiPerformanceEntry 21 } + +pgmNeTsiOutParityNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity NAKs sent out from + this session." + ::= { pgmNeTsiPerformanceEntry 22 } + +pgmNeTsiInNakSeqErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of received NAKs discarded + because of out of sequence (out of retransmit + window)." + ::= { pgmNeTsiPerformanceEntry 23 } + +pgmNeTsiInNnaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NNAKs received for this + session." + ::= { pgmNeTsiPerformanceEntry 24 } + +pgmNeTsiOutNnaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NNAKs sent out from this + session." + ::= { pgmNeTsiPerformanceEntry 25 } + +pgmNeTsiInParityNnaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity NNAKs received for + this session." + ::= { pgmNeTsiPerformanceEntry 26 } + +pgmNeTsiOutParityNnaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity NNAKs sent out from + this session." + ::= { pgmNeTsiPerformanceEntry 27 } + +pgmNeTsiInNcfs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NCFs received for this + session." + ::= { pgmNeTsiPerformanceEntry 28 } + +pgmNeTsiOutNcfs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NCFs sent out from this + session." + ::= { pgmNeTsiPerformanceEntry 29 } + +pgmNeTsiInParityNcfs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity NCFs received for + this session." + ::= { pgmNeTsiPerformanceEntry 30 } + +pgmNeTsiOutParityNcfs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of Parity NCFs sent out from + this session." + ::= { pgmNeTsiPerformanceEntry 31 } + +pgmNeTsiSpmSequenceNumber OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Sequence number of the last seen SPM." + ::= {pgmNeTsiPerformanceEntry 32 } + +pgmNeTsiTransmissionGroupSize OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Advertised size of the transmission group for + this transport session." + ::= {pgmNeTsiPerformanceEntry 33 } + +pgmNeTsiTimeout OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Time left for this entry to expire." + ::= {pgmNeTsiPerformanceEntry 34 } + +pgmNeTsiLastTtl OBJECT-TYPE + SYNTAX Unsigned32(1..255) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "IP TTL of last seen valid SPM." + ::= {pgmNeTsiPerformanceEntry 35 } + +pgmNeTsiLinkLossRate OBJECT-TYPE + SYNTAX Unsigned32(0..100) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Worst reported link loss rate for congestion + control. This is reported as a percentage." + ::= {pgmNeTsiPerformanceEntry 36 } + +pgmNeTsiPathLossRate OBJECT-TYPE + SYNTAX Unsigned32 (0..100) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Worst reported path loss rate for congestion + control. This is reported as a percentage." + ::= {pgmNeTsiPerformanceEntry 37 } + +pgmNeTsiReceiverLossRate OBJECT-TYPE + SYNTAX Unsigned32 (0..100) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Worst reported receiver loss rate for congestion + control. This is reported as a percentage." + ::= {pgmNeTsiPerformanceEntry 38 } + +pgmNeTsiCongestionReportLead OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Data lead sequence number associated with the + worst reported receiver loss rate." + ::= {pgmNeTsiPerformanceEntry 39 } + +pgmNeTsiCongestionReportWorstReceiver OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "IP address of the receiver that reported the + worst receiver loss rate." + ::= {pgmNeTsiPerformanceEntry 40 } + +-- The PGM Retransmission table + +-- The PGM Retransmission table contains +-- information about current retransmission requests. +-- This information is held per sequence number, or in +-- the case of FEC, every transmission group, for which +-- retransmission has been requested. + +pgmNeTsiRtxNumberOfEntries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of entries in the retransmission table." + ::= { pgmNeTsi 5 } + +pgmNeTsiRtxTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmNeTsiRtxEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding information for every sequence + number, or in the case of FEC, every + transmission group, for which retransmission has + been requested." + ::= {pgmNeTsi 6 } + +pgmNeTsiRtxEntry OBJECT-TYPE + SYNTAX PgmNeTsiRtxEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per sequence number / transmission group + information." + INDEX { pgmNeTsiGlobalId, + pgmNeTsiDataSourcePort, + pgmNeTsiRtxSequenceNumber, + pgmNeTsiRtxSequenceNumberType } + ::= { pgmNeTsiRtxTable 1 } + +PgmNeTsiRtxEntry ::= SEQUENCE { + pgmNeTsiRtxSequenceNumber + Unsigned32, + pgmNeTsiRtxSequenceNumberType + INTEGER, + pgmNeTsiRtxReqParityTgCount + Counter32, + pgmNeTsiRtxTimeout + TimeTicks, + pgmNeTsiRtxStateBits + BITS +} + +pgmNeTsiRtxSequenceNumber OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "For non-parity retransmission, a sequence number. + For parity retransmission, a transmission group + and packet count." + ::= {pgmNeTsiRtxEntry 1 } + +pgmNeTsiRtxSequenceNumberType OBJECT-TYPE + SYNTAX INTEGER { + selective(1), + tg(2) + } + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Selective Sequence Number and TG Sequence + Number." + ::= {pgmNeTsiRtxEntry 2 } + +pgmNeTsiRtxReqParityTgCount OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Requested number of missing parity packets + of specific Tg. The largest counter of the + received NAK will be stored in this mib. This + variable is valid for parity packets only." + ::= { pgmNeTsiRtxEntry 4 } + +pgmNeTsiRtxTimeout OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "When this state will expire." + ::= {pgmNeTsiRtxEntry 5 } + +pgmNeTsiRtxStateBits OBJECT-TYPE + SYNTAX BITS { + initialising(0), + eliminating(1), + redirecting(2), + stateCreatedByNullNAK(3), + listNAKentry(4), + parityState(5) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "State associated with retransmission entry." + ::= {pgmNeTsiRtxEntry 6 } + +-- The PGM Retransmission interfaces table + +-- The PGM Retransmission interfaces table contains +-- information about what interfaces will be sent +-- retransmitted data for a particular +-- retransmission entry + +pgmNeTsiRtxIfNumberOfEntries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of entries in the retransmission + interfaces table." + ::= { pgmNeTsi 7 } + +pgmNeTsiRtxIfTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmNeTsiRtxIfEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding information of every + interface for which retransmit state for + a particular sequence number or transmission + group has to be sent." + ::= {pgmNeTsi 8} + +pgmNeTsiRtxIfEntry OBJECT-TYPE + SYNTAX PgmNeTsiRtxIfEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Destination interfaces for a particular + retransmit state." + INDEX { pgmNeTsiGlobalId, + pgmNeTsiDataSourcePort, + pgmNeTsiRtxSequenceNumber, + pgmNeTsiRtxSequenceNumberType, + pgmNeTsiRtxIfIndex } + ::= { pgmNeTsiRtxIfTable 1 } + +PgmNeTsiRtxIfEntry ::= SEQUENCE { + pgmNeTsiRtxIfIndex + InterfaceIndex, + pgmNeTsiRtxIfPacketCount + Counter32 +} + +pgmNeTsiRtxIfIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A unique value for each interface. Its value + ranges between 1 and the value of ifNumber. + The value for each interface must remain + constant at least from one re-initialization + of the entity's network management system to + the next re-initialization." + ::= { pgmNeTsiRtxIfEntry 1 } + +pgmNeTsiRtxIfPacketCount OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of repair data packets still to be + retransmitted on this interface. For non-parity + retransmission this will never have a value + greater than 1. For parity retransmission, + any number can be present." + ::= { pgmNeTsiRtxIfEntry 2 } + +-- The PGM Poll Response table + +-- The PGM Poll Response table contains information +-- about PGM parent's of this network element who are +-- currently polling it. + +pgmNeTsiPolrNumberOfEntries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of entries in the poll response table." + ::= { pgmNeTsi 9 } + +pgmNeTsiPolrTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmNeTsiPolrEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding state information about what + PGM parents are polling this Network Element." + ::= { pgmNeTsi 10 } + +pgmNeTsiPolrEntry OBJECT-TYPE + SYNTAX PgmNeTsiPolrEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "State information for a Network Element that + is being polled by its parents" + INDEX { pgmNeTsiGlobalId, + pgmNeTsiDataSourcePort, + pgmNeTsiPolrSource } + ::= { pgmNeTsiPolrTable 1 } + +PgmNeTsiPolrEntry ::= SEQUENCE { + pgmNeTsiPolrSource + IpAddress, + pgmNeTsiPolrSequenceNumber + Unsigned32 +} + +pgmNeTsiPolrSource OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "IP Address of parent who is polling this + device." + ::= { pgmNeTsiPolrEntry 1 } + +pgmNeTsiPolrSequenceNumber OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Sequence number of last POLR from the source." + ::= { pgmNeTsiPolrEntry 2 } + +-- The PGM Poll table + +-- The PGM Poll table contains information related to +-- polling that this Network Element is doing for +-- its children + +pgmNeTsiPollNumberOfEntries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of entries in the poll table." + ::= { pgmNeTsi 11 } + +pgmNeTsiPollTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmNeTsiPollEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding state information related + to polling that this Network Element is doing + for its children." + ::= { pgmNeTsi 12 } + +pgmNeTsiPollEntry OBJECT-TYPE + SYNTAX PgmNeTsiPollEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "State information for a Network Element that + is polling its children." + INDEX { pgmNeTsiGlobalId, + pgmNeTsiDataSourcePort, + pgmNeTsiPollType } + ::= { pgmNeTsiPollTable 1 } + +PgmNeTsiPollEntry ::= SEQUENCE { + pgmNeTsiPollType + INTEGER, + pgmNeTsiPollSequence + Unsigned32, + pgmNeTsiPollChildBackoff + Unsigned32, + pgmNeTsiPollMask + Unsigned32, + pgmNeTsiPollPeriod + Unsigned32, + pgmNeTsiPollCount + Counter32, + pgmNeTsiPollTimeout + TimeTicks +} + +pgmNeTsiPollType OBJECT-TYPE + SYNTAX INTEGER { + general(1), + dlr(2) + } + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Type of Poll." + ::= { pgmNeTsiPollEntry 1 } + +pgmNeTsiPollSequence OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Sequence number of the most recent POLL packet + that we sent." + ::= { pgmNeTsiPollEntry 2 } + +pgmNeTsiPollChildBackoff OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Backoff advertised to be used by child of poll." + ::= { pgmNeTsiPollEntry 3 } + +pgmNeTsiPollMask OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Mask being used in poll." + ::= { pgmNeTsiPollEntry 4 } + +pgmNeTsiPollPeriod OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Period of poll." + ::= { pgmNeTsiPollEntry 5 } + +pgmNeTsiPollCount OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of Poll responses (POLRs) received." + ::= { pgmNeTsiPollEntry 6 } + +pgmNeTsiPollTimeout OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Remaining Time Ticks to next poll." + ::= { pgmNeTsiPollEntry 7 } + + +-- +-- PGM Source +-- + +-- PGM Source general management information + + +pgmSourceSaveDefaults OBJECT-TYPE + SYNTAX INTEGER { initial (1), + save (2), + pending (3), + success (4), + failure (5) } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Flag used to initiate the storing + of all default variable values to + non-volatile storage and to report the + result of the operation. + The following values can only be read, + never written : + initial(1) - returned prior to any requests + for saving the default configuration + pending(3) - saving in progress + success(4) - returned when a save(2) request + is successful + failure(5) - returned when a save(2) request + is unsuccessful + + The following values can only be written, + never read : + save(2) - to indicate that the default + configuration should be saved." + ::= { pgmSource 1 } + +pgmSourceLastUpdateTime OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of TimeTicks since the last update + of the non-volatile storage." + ::= { pgmSource 2 } + +pgmSourceDefaultTtl OBJECT-TYPE + SYNTAX Unsigned32(1..255) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Default TTL used by the PGM Source." + ::= { pgmSource 3 } + +pgmSourceDefaultAdvMode OBJECT-TYPE + SYNTAX INTEGER { data(1), + time(2), + applctrl(3), + other(4) } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Flag to indicate that the transmit window is + advanced with data, by time, under application + control, or any other method." + ::= { pgmSource 4 } + +pgmSourceDefaultLateJoin OBJECT-TYPE + SYNTAX INTEGER { + enable(1), + disable(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Flag to indicate whether or not the sender will + accept late joiners." + ::= { pgmSource 5 } + +pgmSourceDefaultTxwMaxRte OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Maximum transmit rate in bytes/second." + ::= { pgmSource 6 } + +pgmSourceDefaultTxwSecs OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Transmit window size in seconds." + ::= { pgmSource 7 } + +pgmSourceDefaultTxwAdvSecs OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Transmit window advance in seconds. This value + should always be set to a value smaller than + the pgmSourceTxwSecs." + ::= { pgmSource 8 } + +pgmSourceDefaultAdvIvl OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Advance interval in milliseconds. Always a + valid parameter when advancing with time. + Valid only in cases of absence of lost data + when advancing with data." + ::= { pgmSource 9 } + +pgmSourceDefaultSpmIvl OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "SPM interval in milliseconds." + ::= { pgmSource 10 } + +pgmSourceDefaultSpmHeartBeatIvlMin OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "SPM heartbeat interval in milliseconds." + ::= { pgmSource 11 } + +pgmSourceDefaultSpmHeartBeatIvlMax OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Maximum SPM heartbeat interval in milliseconds." + ::= { pgmSource 12 } + +pgmSourceDefaultRdataBackoffIvl OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "RDATA backoff interval in milliseconds." + ::= { pgmSource 13 } + +pgmSourceDefaultFECProactiveParitySize OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Number of proactive parity messages per FEC + block." + ::= { pgmSource 14 } + +pgmSourceDefaultGroupAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The default IP Multicast group address + used by the sender." + ::= { pgmSource 15 } + +pgmSourceUpdateSinceLastSave OBJECT-TYPE + SYNTAX INTEGER + { + notUpdated(1), + updated(2) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Specifies if any of the Source Default + variables have been updated or not, + since the last successful pgmSourceSaveDefaults. + notUpdated - none of the default Source + variables were set after the last + successful save to a non-volatile + storage. + updated - at least one of the default Source + variables were set after the last + successful save to a non-volatile + storage." + ::= { pgmSource 16 } + +-- PGM Source per TSI management information + +pgmSourceTsi OBJECT IDENTIFIER ::= { pgmSource 100 } + +pgmSourceNumberOfEntries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of PGM Source sessions." + ::= { pgmSourceTsi 1 } + +-- PGM Source Fault Management Table + +pgmSourceTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmSourceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per TSI fault + management and general information + related to PGM Source." + ::= {pgmSourceTsi 2} + +pgmSourceEntry OBJECT-TYPE + SYNTAX PgmSourceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per PGM sender information." + INDEX { pgmSourceGlobalId, + pgmSourceSourcePort } + ::= { pgmSourceTable 1 } + +PgmSourceEntry ::= SEQUENCE { + pgmSourceGlobalId + OCTET STRING, + pgmSourceSourcePort + Unsigned32, + pgmSourceSourceAddress + IpAddress, + pgmSourceGroupAddress + IpAddress, + pgmSourceDestPort + Unsigned32, + pgmSourceSourceGsi + OCTET STRING, + pgmSourceSourcePortNumber + Unsigned32 + } + +pgmSourceGlobalId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Globally unique session identifier (GSI)." + ::= { pgmSourceEntry 1 } + +pgmSourceSourcePort OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmSourceEntry 2 } + +pgmSourceSourceAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Source IP address." + ::= { pgmSourceEntry 3 } + +pgmSourceGroupAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "IP Multicast group address used by the + sender." + ::= { pgmSourceEntry 4 } + +pgmSourceDestPort OBJECT-TYPE + SYNTAX Unsigned32 (0..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Destination port number." + ::= { pgmSourceEntry 5 } + +pgmSourceSourceGsi OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Globally unique session identifier (GSI)." + ::= { pgmSourceEntry 6 } + +pgmSourceSourcePortNumber OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmSourceEntry 7 } + + +-- PGM Source Configuration Management Table + +pgmSourceConfigTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmSourceConfigEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per TSI + configuration information + related to the PGM Source." + ::= {pgmSourceTsi 3} + +pgmSourceConfigEntry OBJECT-TYPE + SYNTAX PgmSourceConfigEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per PGM sender information." + INDEX { pgmSourceConfigGlobalId, + pgmSourceConfigSourcePort } + ::= { pgmSourceConfigTable 1 } + +PgmSourceConfigEntry ::= SEQUENCE { + pgmSourceConfigGlobalId + OCTET STRING, + pgmSourceConfigSourcePort + Unsigned32, + pgmSourceTtl + Unsigned32, + pgmSourceAdvMode + INTEGER, + pgmSourceLateJoin + INTEGER, + pgmSourceTxwMaxRte + Unsigned32, + pgmSourceTxwSecs + Unsigned32, + pgmSourceTxwAdvSecs + Unsigned32, + pgmSourceAdvIvl + Unsigned32, + pgmSourceSpmIvl + Unsigned32, + pgmSourceSpmHeartBeatIvlMin + Unsigned32, + pgmSourceSpmHeartBeatIvlMax + Unsigned32, + pgmSourceRdataBackoffIvl + Unsigned32, + pgmSourceFEC + INTEGER, + pgmSourceFECTransmissionGrpSize + Unsigned32, + pgmSourceFECProactiveParitySize + Unsigned32, + pgmSourceSpmPathAddress + IpAddress + } + +pgmSourceConfigGlobalId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Globally unique session identifier (GSI)." + ::= { pgmSourceConfigEntry 1 } + +pgmSourceConfigSourcePort OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmSourceConfigEntry 2 } + +pgmSourceTtl OBJECT-TYPE + SYNTAX Unsigned32(1..255) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "TTL used by sender." + ::= { pgmSourceConfigEntry 3 } + +pgmSourceAdvMode OBJECT-TYPE + SYNTAX INTEGER { data(1), + time(2), + applctrl(3), + other(4) } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Flag to indicate that the transmit window is + advanced with data, by time, under application + control, or any other method." + ::= { pgmSourceConfigEntry 4 } + +pgmSourceLateJoin OBJECT-TYPE + SYNTAX INTEGER { + enable(1), + disable(2) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Flag to indicate whether or not the sender will + accept late joiners." + ::= { pgmSourceConfigEntry 5 } + +pgmSourceTxwMaxRte OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Maximum transmit rate in bytes/second." + ::= { pgmSourceConfigEntry 6 } + +pgmSourceTxwSecs OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Transmit window size in seconds." + ::= { pgmSourceConfigEntry 7 } + +pgmSourceTxwAdvSecs OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Transmit window advance in seconds. This value + should always be set to a value smaller than + the pgmSourceTxwSecs." + ::= { pgmSourceConfigEntry 8 } + +pgmSourceAdvIvl OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Advance interval in milliseconds. Always a + valid parameter when advancing with time. + Valid only in cases of absence of lost data + when advancing with data." + ::= { pgmSourceConfigEntry 9 } + +pgmSourceSpmIvl OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "SPM interval in milliseconds." + ::= { pgmSourceConfigEntry 10 } + +pgmSourceSpmHeartBeatIvlMin OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "SPM heartbeat interval in milliseconds." + ::= { pgmSourceConfigEntry 11 } + +pgmSourceSpmHeartBeatIvlMax OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Maximum SPM heartbeat interval in milliseconds." + ::= { pgmSourceConfigEntry 12 } + +pgmSourceRdataBackoffIvl OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "RDATA backoff interval in milliseconds." + ::= { pgmSourceConfigEntry 13 } + +pgmSourceFEC OBJECT-TYPE + SYNTAX INTEGER { disabled(1), + enabledFixedPacketSize(2), + enabledVariablePacketSize(3) } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Flag to indicate whether or not FEC is enabled + and whether it supports variable or fixed size + messages." + ::= { pgmSourceConfigEntry 14 } + +pgmSourceFECTransmissionGrpSize OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "FEC transmission group size." + ::= { pgmSourceConfigEntry 15 } + +pgmSourceFECProactiveParitySize OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Number of proactive parity messages per FEC + block." + ::= { pgmSourceConfigEntry 16 } + +pgmSourceSpmPathAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Ip Address for the NAKs to be sent, + in case that NE is not set." + ::= { pgmSourceConfigEntry 17 } + +-- PGM Source Performance Management Table + +pgmSourcePerformanceTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmSourcePerformanceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per TSI performance + information related to the PGM Source." + ::= {pgmSourceTsi 4} + +pgmSourcePerformanceEntry OBJECT-TYPE + SYNTAX PgmSourcePerformanceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per PGM sender information." + INDEX { pgmSourcePerformanceGlobalId, + pgmSourcePerformanceSourcePort } + ::= { pgmSourcePerformanceTable 1 } + +PgmSourcePerformanceEntry ::= SEQUENCE { + pgmSourcePerformanceGlobalId + OCTET STRING, + pgmSourcePerformanceSourcePort + Unsigned32, + pgmSourceDataBytesSent + Counter32, + pgmSourceDataMsgsSent + Counter32, + pgmSourceBytesBuffered + Counter32, + pgmSourceMsgsBuffered + Counter32, + pgmSourceBytesRetransmitted + Counter32, + pgmSourceMsgsRetransmitted + Counter32, + pgmSourceBytesSent + Counter32, + pgmSourceRawNaksReceived + Counter32, + pgmSourceNaksIgnored + Counter32, + pgmSourceCksumErrors + Counter32, + pgmSourceMalformedNaks + Counter32, + pgmSourcePacketsDiscarded + Counter32, + pgmSourceNaksRcvd + Counter32, + pgmSourceParityBytesRetransmitted + Counter32, + pgmSourceSelectiveBytesRetransmited + Counter32, + pgmSourceParityMsgsRetransmitted + Counter32, + pgmSourceSelectiveMsgsRetransmitted + Counter32, + pgmSourceBytesAdmit + Counter32, + pgmSourceMsgsAdmit + Counter32, + pgmSourceParityNakPacketsReceived + Counter32, + pgmSourceSelectiveNakPacketsReceived + Counter32, + pgmSourceParityNaksReceived + Counter32, + pgmSourceSelectiveNaksReceived + Counter32, + pgmSourceParityNaksIgnored + Counter32, + pgmSourceSelectiveNaksIgnored + Counter32, + pgmSourceAckErrors + Counter32, + pgmSourcePgmCCAcker + IpAddress, + pgmSourceTransmissionCurrentRate + Counter32, + pgmSourceAckPacketsReceived + Counter32, + pgmSourceNNakPacketsReceived + Counter32, + pgmSourceParityNNakPacketsReceived + Counter32, + pgmSourceSelectiveNNakPacketsReceived + Counter32, + pgmSourceNNaksReceived + Counter32, + pgmSourceParityNNaksReceived + Counter32, + pgmSourceSelectiveNNaksReceived + Counter32, + pgmSourceNNakErrors + Counter32 +} + +pgmSourcePerformanceGlobalId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Globally unique source identifier (GSI)." + ::= { pgmSourcePerformanceEntry 1 } + +pgmSourcePerformanceSourcePort OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmSourcePerformanceEntry 2 } + +pgmSourceDataBytesSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of data bytes sent for this TSI." + ::= { pgmSourcePerformanceEntry 3 } + +pgmSourceDataMsgsSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of data messages sent for this TSI." + ::= { pgmSourcePerformanceEntry 4 } + +pgmSourceBytesBuffered OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes currently buffered for this + TSI." + ::= { pgmSourcePerformanceEntry 5 } + +pgmSourceMsgsBuffered OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of messages currently buffered for + this TSI." + ::= { pgmSourcePerformanceEntry 6 } + +pgmSourceBytesRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes retransmitted for this TSI." + ::= { pgmSourcePerformanceEntry 7 } + +pgmSourceMsgsRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of messages retransmitted for this TSI." + ::= { pgmSourcePerformanceEntry 8 } + +pgmSourceBytesSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of bytes send for this TSI. Includes + IP header and non-data messages." + ::= { pgmSourcePerformanceEntry 9 } + +pgmSourceRawNaksReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Raw number of NAK packets received." + ::= { pgmSourcePerformanceEntry 10 } + +pgmSourceNaksIgnored OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of ignored Naks for this TSI, due to + duplicate NAKs reception." + ::= { pgmSourcePerformanceEntry 11 } + +pgmSourceCksumErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of checksum errors for this TSI." + ::= { pgmSourcePerformanceEntry 12 } + +pgmSourceMalformedNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of malformed NAK packets." + ::= { pgmSourcePerformanceEntry 13 } + +pgmSourcePacketsDiscarded OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of discarded data packets. This counter + is used to count all discarded incoming packets + per TSI in cases of duplicates, header and + packet errors, etc." + ::= { pgmSourcePerformanceEntry 14 } + +pgmSourceNaksRcvd OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of Sequence Numbers NAKed." + ::= { pgmSourcePerformanceEntry 15 } + +pgmSourceParityBytesRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes sent in parity retransmissions." + ::= { pgmSourcePerformanceEntry 16 } + +pgmSourceSelectiveBytesRetransmited OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes sent in selective retransmissions." + ::= { pgmSourcePerformanceEntry 17 } + +pgmSourceParityMsgsRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of parity retransmissions sent." + ::= { pgmSourcePerformanceEntry 18 } + +pgmSourceSelectiveMsgsRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of selective retransmissions sent." + ::= { pgmSourcePerformanceEntry 19 } + +pgmSourceBytesAdmit OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes currently in the rate controled + admit queue. Includes IP header, UDP header if + encapsulated, PGM header, and data." + ::= { pgmSourcePerformanceEntry 20 } + +pgmSourceMsgsAdmit OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of messages currently in the rate controled + admit queue. Includes data messages, retransmissions, + and SPMs." + ::= { pgmSourcePerformanceEntry 21 } + +pgmSourceParityNakPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of parity NAK packets received." + ::= { pgmSourcePerformanceEntry 22 } + +pgmSourceSelectiveNakPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of selective NAK packets received." + ::= { pgmSourcePerformanceEntry 23 } + +pgmSourceParityNaksReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual parity NAKs received." + ::= { pgmSourcePerformanceEntry 24 } + +pgmSourceSelectiveNaksReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual selective NAKs received." + ::= { pgmSourcePerformanceEntry 25 } + +pgmSourceParityNaksIgnored OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual parity NAKs ignored." + ::= { pgmSourcePerformanceEntry 26 } + +pgmSourceSelectiveNaksIgnored OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual selective NAKs ignored." + ::= { pgmSourcePerformanceEntry 27 } + +pgmSourceAckErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of ACK packets received with error in + them, different than checksum error." + ::= { pgmSourcePerformanceEntry 28 } + +pgmSourcePgmCCAcker OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Ip Address of the currently designated pgm + congestion control ACKER." + ::= { pgmSourcePerformanceEntry 29 } + +pgmSourceTransmissionCurrentRate OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Current transmission rate." + ::= { pgmSourcePerformanceEntry 30 } + +pgmSourceAckPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of ACK packets received." + ::= { pgmSourcePerformanceEntry 31 } + +pgmSourceNNakPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of Null NAKs received." + ::= { pgmSourcePerformanceEntry 32 } + +pgmSourceParityNNakPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of parity Null NAK packets received." + ::= { pgmSourcePerformanceEntry 33 } + +pgmSourceSelectiveNNakPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of selective Null NAK packets received." + ::= { pgmSourcePerformanceEntry 34 } + +pgmSourceNNaksReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual NAKs, received in Null + NAK packets." + ::= { pgmSourcePerformanceEntry 35 } + +pgmSourceParityNNaksReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual parity NAKs, + received in Null NAK packets." + ::= { pgmSourcePerformanceEntry 36 } + +pgmSourceSelectiveNNaksReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual NAKs, + received in Null NAK packets." + ::= { pgmSourcePerformanceEntry 37 } + +pgmSourceNNakErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of Null NAK packets received that contain + error, different than checksum error." + ::= { pgmSourcePerformanceEntry 38 } + +-- +-- PGM Receiver +-- + +-- PGM Receiver general management information + +pgmReceiverSaveDefaults OBJECT-TYPE + SYNTAX INTEGER { initial (1), + save (2), + pending (3), + success (4), + failure (5) } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Flag used to initiate the storing + of all default variable values to + non-volatile storage and to report the + result of the operation. + The following values can only be read, + never written : + initial(1) - returned prior to any requests + for saving the default configuration + pending(3) - saving in progress + success(4) - returned when a save(2) request + is successful + failure(5) - returned when a save(2) request + is unsuccessful + + The following values can only be written, + never read : + save(2) - to indicate that the default + configuration should be saved." + ::= { pgmReceiver 1 } + +pgmReceiverLastUpdateTime OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of TimeTicks since the last update + of the non-volatile storage." + ::= { pgmReceiver 2 } + +pgmReceiverDefaultNakBackoffIvl OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "NAK random backoff interval." + ::= { pgmReceiver 3 } + +pgmReceiverDefaultNakRepeatIvl OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "NAK repeat interval." + ::= { pgmReceiver 4 } + +pgmReceiverDefaultNakNcfRetries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Max NAK retries while witing for matching NCF." + ::= { pgmReceiver 5 } + +pgmReceiverDefaultNakRdataIvl OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Default NAK RDATA interval, i.e the amount of + time to cease NAKs for a particular piece of + data after a corresponding NCF has been received." + ::= { pgmReceiver 6 } + +pgmReceiverDefaultNakDataRetries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Max NAK retries while waiting for missing data." + ::= { pgmReceiver 7 } + +pgmReceiverDefaultSendNaks OBJECT-TYPE + SYNTAX INTEGER { + enabled(1), + disabled(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Flag to indicate whether or not receiver should + send NAKs or be totally passive." + ::= { pgmReceiver 8 } + +pgmReceiverDefaultLateJoin OBJECT-TYPE + SYNTAX INTEGER { + enabled(1), + disabled(2) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Flag to indicate whether or not the receiver + should wait for a OPT_JOIN SPM before + attempting to late join." + ::= { pgmReceiver 9 } + +pgmReceiverDefaultNakTtl OBJECT-TYPE + SYNTAX Unsigned32(1..255) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "TTL on NAK packets sent for loss." + ::= { pgmReceiver 10 } + +pgmReceiverDefaultDeliveryOrder OBJECT-TYPE + SYNTAX INTEGER { + unordered(1), + ordered(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Packet Delivery Order for the receiving + application." + ::= { pgmReceiver 11 } + +pgmReceiverDefaultNextPgmHop OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Next hop PGM router address. This option + sets the default address to send NAKs to, + instead of sending to the last hop address." + ::= { pgmReceiver 12 } + +pgmReceiverDefaultGroupAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Default IP Multicast group address + used by the sender." + ::= { pgmReceiver 13 } + +pgmReceiverUpdateSinceLastSave OBJECT-TYPE + SYNTAX INTEGER + { + notUpdated(1), + updated(2) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Specifies if any of the Receiver Default + variables have been updated or not, + since the last successful pgmSourceSaveDefaults. + notUpdated - none of the default Receiver + variables were set after the last + successful save to a non-volatile + storage. + updated - at least one of the default Receiver + variables were set after the last + successful save to a non-volatile + storage." + ::= { pgmReceiver 14 } + +pgmReceiverDefaultNakFailureThresholdTimer OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Timer that defines the default + interval of time during which unrecoverable + lost packets are monitored + for purposes of SNMP trap generation." + ::= { pgmReceiver 15 } + +pgmReceiverDefaultNakFailureThreshold OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The default number of unrecoverable + lost packets within the defined interval + after which an SNMP trap is generated." + ::= { pgmReceiver 16 } + + +-- PGM Receiver per Receiver management information + +pgmReceiverTsi OBJECT IDENTIFIER ::= { pgmReceiver 100 } + +pgmReceiverNumberOfEntries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of PGM Receivers." + ::= { pgmReceiverTsi 1 } + +-- PGM Receiver Fault Management Table + +pgmReceiverTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmReceiverEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per TSI fault + management and general information + related to the PGM Receiver." + ::= {pgmReceiverTsi 2} + +pgmReceiverEntry OBJECT-TYPE + SYNTAX PgmReceiverEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per PGM receiver fault management + and general information." + INDEX { pgmReceiverGlobalId, + pgmReceiverSourcePort, + pgmReceiverInstance } + ::= { pgmReceiverTable 1 } + +PgmReceiverEntry ::= SEQUENCE { + pgmReceiverGlobalId + OCTET STRING, + pgmReceiverSourcePort + Unsigned32, + pgmReceiverInstance + Unsigned32, + pgmReceiverGroupAddress + IpAddress, + pgmReceiverDestPort + Unsigned32, + pgmReceiverSourceAddress + IpAddress, + pgmReceiverLastHop + IpAddress, + pgmReceiverSourceGsi + OCTET STRING, + pgmReceiverSourcePortNumber + Unsigned32, + pgmReceiverUniqueInstance + Unsigned32 + } + +pgmReceiverGlobalId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Globally unique source identifier (GSI)." + ::= { pgmReceiverEntry 1 } + +pgmReceiverSourcePort OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmReceiverEntry 2 } + +pgmReceiverInstance OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Positive number, uniquely identifying + a Receiver." + ::= { pgmReceiverEntry 3 } + +pgmReceiverGroupAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "IP Multicast group address used by the sender." + ::= { pgmReceiverEntry 4 } + +pgmReceiverDestPort OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Destination port number." + ::= { pgmReceiverEntry 5 } + +pgmReceiverSourceAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Source IP address number." + ::= { pgmReceiverEntry 6 } + +pgmReceiverLastHop OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Last hop PGM router address." + ::= { pgmReceiverEntry 7 } + +pgmReceiverSourceGsi OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Globally unique source identifier (GSI)." + ::= { pgmReceiverEntry 8 } + +pgmReceiverSourcePortNumber OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmReceiverEntry 9 } + +pgmReceiverUniqueInstance OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Positive number, uniquely identifying + a Receiver." + ::= { pgmReceiverEntry 10 } + +-- PGM Receiver Configuration Management Table + +pgmReceiverConfigTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmReceiverConfigEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per TSI configuration + management information related + to the PGM Receiver." + ::= {pgmReceiverTsi 3 } + +pgmReceiverConfigEntry OBJECT-TYPE + SYNTAX PgmReceiverConfigEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per PGM receiver configuration management + information." + INDEX { pgmReceiverConfigGlobalId, + pgmReceiverConfigSourcePort, + pgmReceiverConfigInstance } + ::= { pgmReceiverConfigTable 1 } + +PgmReceiverConfigEntry ::= SEQUENCE { + pgmReceiverConfigGlobalId + OCTET STRING, + pgmReceiverConfigSourcePort + Unsigned32, + pgmReceiverConfigInstance + Unsigned32, + pgmReceiverNakBackoffIvl + Unsigned32, + pgmReceiverNakRepeatIvl + Unsigned32, + pgmReceiverNakNcfRetries + Unsigned32, + pgmReceiverNakRdataIvl + Unsigned32, + pgmReceiverNakDataRetries + Unsigned32, + pgmReceiverSendNaks + INTEGER, + pgmReceiverLateJoin + INTEGER, + pgmReceiverNakTtl + Unsigned32, + pgmReceiverDeliveryOrder + INTEGER, + pgmReceiverMcastNaks + INTEGER, + pgmReceiverNakFailureThresholdTimer + Unsigned32, + pgmReceiverNakFailureThreshold + Unsigned32 + } + +pgmReceiverConfigGlobalId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Globally unique source identifier (GSI)." + ::= { pgmReceiverConfigEntry 1 } + +pgmReceiverConfigSourcePort OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmReceiverConfigEntry 2 } + +pgmReceiverConfigInstance OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Positive number, uniquely identifying + a Receiver." + ::= { pgmReceiverConfigEntry 3 } + +pgmReceiverNakBackoffIvl OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "NAK random backoff interval." + ::= { pgmReceiverConfigEntry 4 } + +pgmReceiverNakRepeatIvl OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "NAK repeat interval." + ::= { pgmReceiverConfigEntry 5 } + +pgmReceiverNakNcfRetries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Max NAK retries while witing for matching NCF." + ::= { pgmReceiverConfigEntry 6 } + +pgmReceiverNakRdataIvl OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "NAK RDATA interval." + ::= { pgmReceiverConfigEntry 7 } + +pgmReceiverNakDataRetries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Max NAK retries while waiting for missing data." + ::= { pgmReceiverConfigEntry 8 } + +pgmReceiverSendNaks OBJECT-TYPE + SYNTAX INTEGER { + enabled(1), + disabled(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Flag to indicate whether or not receiver should + send NAKs or be totally passive." + ::= { pgmReceiverConfigEntry 9 } + +pgmReceiverLateJoin OBJECT-TYPE + SYNTAX INTEGER { + enabled(1), + disabled(2) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Flag to indicate whether or not the receiver + should wait for a OPT_JOIN SPM before + attempting to late join." + ::= { pgmReceiverConfigEntry 10 } + +pgmReceiverNakTtl OBJECT-TYPE + SYNTAX Unsigned32(1..255) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "TTL on NAK packets sent for loss." + ::= { pgmReceiverConfigEntry 11 } + +pgmReceiverDeliveryOrder OBJECT-TYPE + SYNTAX INTEGER { + unordered(1), + ordered(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Packet Delivery Order for the receiving + application." + ::= { pgmReceiverConfigEntry 12 } + +pgmReceiverMcastNaks OBJECT-TYPE + SYNTAX INTEGER { + enabled(1), + disabled(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Flag to indicate whether or not receiver should + send multicast NAKs." + ::= { pgmReceiverConfigEntry 13 } + +pgmReceiverNakFailureThresholdTimer OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Timer that defines per receiver + interval of time during which unrecoverable + lost packets are monitored + for purposes of SNMP trap generation." + ::= { pgmReceiverConfigEntry 14 } + +pgmReceiverNakFailureThreshold OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The number of unrecoverable lost packets + within the defined interval + after which an SNMP trap is generated." + ::= { pgmReceiverConfigEntry 15 } + + +-- PGM Receiver Performance Management Table + +pgmReceiverPerformanceTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmReceiverPerformanceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per TSI + performance management information + related to the PGM Receiver." + ::= {pgmReceiverTsi 4} + +pgmReceiverPerformanceEntry OBJECT-TYPE + SYNTAX PgmReceiverPerformanceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per PGM Receiver session performance + management information." + INDEX { pgmReceiverPerformanceGlobalId, + pgmReceiverPerformanceSourcePort, + pgmReceiverPerformanceInstance } + ::= { pgmReceiverPerformanceTable 1 } + +PgmReceiverPerformanceEntry ::= SEQUENCE { + pgmReceiverPerformanceGlobalId + OCTET STRING, + pgmReceiverPerformanceSourcePort + Unsigned32, + pgmReceiverPerformanceInstance + Unsigned32, + pgmReceiverDataBytesReceived + Counter32, + pgmReceiverDataMsgsReceived + Counter32, + pgmReceiverNaksSent + Counter32, + pgmReceiverNaksRetransmitted + Counter32, + pgmReceiverNakFailures + Counter32, + pgmReceiverBytesReceived + Counter32, + pgmReceiverNaksSuppressed + Counter32, + pgmReceiverCksumErrors + Counter32, + pgmReceiverMalformedSpms + Counter32, + pgmReceiverMalformedOdata + Counter32, + pgmReceiverMalformedRdata + Counter32, + pgmReceiverMalformedNcfs + Counter32, + pgmReceiverPacketsDiscarded + Counter32, + pgmReceiverLosses + Counter32, + pgmReceiverBytesDeliveredToApp + Counter32, + pgmReceiverMsgsDeliveredToApp + Counter32, + pgmReceiverDupSpms + Counter32, + pgmReceiverDupDatas + Counter32, + pgmReceiverDupParities + Counter32, + pgmReceiverNakPacketsSent + Counter32, + pgmReceiverParityNakPacketsSent + Counter32, + pgmReceiverSelectiveNakPacketsSent + Counter32, + pgmReceiverParityNaksSent + Counter32, + pgmReceiverSelectiveNaksSent + Counter32, + pgmReceiverParityNaksRetransmitted + Counter32, + pgmReceiverSelectiveNaksRetransmitted + Counter32, + pgmReceiverNaksFailed + Counter32, + pgmReceiverParityNaksFailed + Counter32, + pgmReceiverSelectiveNaksFailed + Counter32, + pgmReceiverNaksFailedRxwAdvanced + Counter32, + pgmReceiverNaksFaledNcfRetriesExceeded + Counter32, + pgmReceiverNaksFailedDataRetriesExceeded + Counter32, + pgmReceiverNaksFailedGenExpired + Counter32, + pgmReceiverNakFailuresDelivered + Counter32, + pgmReceiverParityNaksSuppressed + Counter32, + pgmReceiverSelectiveNaksSuppressed + Counter32, + pgmReceiverNakErrors + Counter32, + pgmReceiverOutstandingParityNaks + Counter32, + pgmReceiverOutstandingSelectiveNaks + Counter32, + pgmReceiverLastActivity + Counter32, + pgmReceiverNakSvcTimeMin + Counter32, + pgmReceiverNakSvcTimeMean + Counter32, + pgmReceiverNakSvcTimeMax + Counter32, + pgmReceiverNakFailTimeMin + Counter32, + pgmReceiverNakFailTimeMean + Counter32, + pgmReceiverNakFailTimeMax + Counter32, + pgmReceiverNakTransmitMin + Counter32, + pgmReceiverNakTransmitMean + Counter32, + pgmReceiverNakTransmitMax + Counter32, + pgmReceiverAcksSent + Counter32, + pgmReceiverRxwTrail + Counter32, + pgmReceiverRxwLead + Counter32, + pgmReceiverNakFailuresLastInterval + Counter32, + pgmReceiverLastIntervalNakFailures + Counter32 +} + +pgmReceiverPerformanceGlobalId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Globally unique source identifier (GSI)." + ::= { pgmReceiverPerformanceEntry 1 } + +pgmReceiverPerformanceSourcePort OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmReceiverPerformanceEntry 2 } + +pgmReceiverPerformanceInstance OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Positive number, uniquely identifying + a Receiver." + ::= { pgmReceiverPerformanceEntry 3 } + +pgmReceiverDataBytesReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of data bytes received for this PGM + Receiver session." + ::= { pgmReceiverPerformanceEntry 4 } + +pgmReceiverDataMsgsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of data messages received for this + PGM Receiver session." + ::= { pgmReceiverPerformanceEntry 5 } + +pgmReceiverNaksSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAKs sent for this session." + ::= { pgmReceiverPerformanceEntry 6 } + +pgmReceiverNaksRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAKs retransmitted for this + session." + ::= { pgmReceiverPerformanceEntry 7 } + +pgmReceiverNakFailures OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAK failures for this session. + This counter represents the number of + unrecoverable/unrepairable data packets." + ::= { pgmReceiverPerformanceEntry 8 } + +pgmReceiverBytesReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes received for this session. + It counts all bytes received, including IP + and PGM header and non-data messages." + ::= { pgmReceiverPerformanceEntry 9 } + +pgmReceiverNaksSuppressed OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of suppressed NAKs." + ::= { pgmReceiverPerformanceEntry 10 } + +pgmReceiverCksumErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of checksum errors for this session." + ::= { pgmReceiverPerformanceEntry 11 } + +pgmReceiverMalformedSpms OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of malformed SPMs for this session." + ::= { pgmReceiverPerformanceEntry 12 } + +pgmReceiverMalformedOdata OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of malformed ODATA packets for this + session." + ::= { pgmReceiverPerformanceEntry 13 } + +pgmReceiverMalformedRdata OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of malformed RDATA packets for this + session." + ::= { pgmReceiverPerformanceEntry 14 } + +pgmReceiverMalformedNcfs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of malformed NCF packets for this + session." + ::= { pgmReceiverPerformanceEntry 15 } + +pgmReceiverPacketsDiscarded OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of discarded packets for this + session." + ::= { pgmReceiverPerformanceEntry 16 } + +pgmReceiverLosses OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of detected missed packets for + this session. This counter is incremented + every time a Receiver detects a missing + packet." + ::= { pgmReceiverPerformanceEntry 17 } + +pgmReceiverBytesDeliveredToApp OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes, delivered to the + application." + ::= { pgmReceiverPerformanceEntry 18 } + +pgmReceiverMsgsDeliveredToApp OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of messages, delivered to the + application." + ::= { pgmReceiverPerformanceEntry 19 } + +pgmReceiverDupSpms OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of duplicate SPMs." + ::= { pgmReceiverPerformanceEntry 20 } + +pgmReceiverDupDatas OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of duplicate RDATA/ODATA." + ::= { pgmReceiverPerformanceEntry 21 } + +pgmReceiverDupParities OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of duplicate parities seen." + ::= { pgmReceiverPerformanceEntry 22 } + +pgmReceiverNakPacketsSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAK packets sent. + Includes parity and selective." + ::= { pgmReceiverPerformanceEntry 23 } + +pgmReceiverParityNakPacketsSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of parity NAK packets sent." + ::= { pgmReceiverPerformanceEntry 24 } + +pgmReceiverSelectiveNakPacketsSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of selective NAK packets sent." + ::= { pgmReceiverPerformanceEntry 25 } + +pgmReceiverParityNaksSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual parity NAK packets sent." + ::= { pgmReceiverPerformanceEntry 26 } + +pgmReceiverSelectiveNaksSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual selective NAK packets sent." + ::= { pgmReceiverPerformanceEntry 27 } + +pgmReceiverParityNaksRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual parity NAKs retransmitted." + ::= { pgmReceiverPerformanceEntry 28 } + +pgmReceiverSelectiveNaksRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual selective NAKs retransmitted." + ::= { pgmReceiverPerformanceEntry 29 } + +pgmReceiverNaksFailed OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual NAKs that failed." + ::= { pgmReceiverPerformanceEntry 30 } + +pgmReceiverParityNaksFailed OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual parity NAKs that failed." + ::= { pgmReceiverPerformanceEntry 31 } + +pgmReceiverSelectiveNaksFailed OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual selective NAKs that failed." + ::= { pgmReceiverPerformanceEntry 32 } + +pgmReceiverNaksFailedRxwAdvanced OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual NAKs that failed, due to the + window being advanced over them." + ::= { pgmReceiverPerformanceEntry 33 } + +pgmReceiverNaksFaledNcfRetriesExceeded OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual NAKs that failed, due to ncf + retry limit exceeded." + ::= { pgmReceiverPerformanceEntry 34 } + +pgmReceiverNaksFailedDataRetriesExceeded OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual NAKs that failed, due to data + retry limit exceeded." + ::= { pgmReceiverPerformanceEntry 35 } + +pgmReceiverNaksFailedGenExpired OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual NAKs that failed, due to NAK + generation interval expiring before it + could be repaired." + ::= { pgmReceiverPerformanceEntry 36 } + +pgmReceiverNakFailuresDelivered OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAK failures delivered to application." + ::= { pgmReceiverPerformanceEntry 37 } + +pgmReceiverParityNaksSuppressed OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual parity NAKs that were + suppressed from being sent due to reception + of an NCF or ODATA/RDATA for the loss." + ::= { pgmReceiverPerformanceEntry 38 } + +pgmReceiverSelectiveNaksSuppressed OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual selective NAKs that were + suppressed from being sent due to reception + of an NCF or ODATA/RDATA for the loss." + ::= { pgmReceiverPerformanceEntry 39 } + +pgmReceiverNakErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAK packets, that contained + errors in them." + ::= { pgmReceiverPerformanceEntry 40 } + +pgmReceiverOutstandingParityNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Current number of outstanding individual parity + NAKs that are waiting to be repaired." + ::= { pgmReceiverPerformanceEntry 41 } + +pgmReceiverOutstandingSelectiveNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Current number of outstanding individual selective + NAKs that are waiting to be repaired." + ::= { pgmReceiverPerformanceEntry 42 } + +pgmReceiverLastActivity OBJECT-TYPE + SYNTAX Counter32 + UNITS "seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Last time activity was observed from + the Source. In seconds since the epoch, + January 1, 1970." + ::= { pgmReceiverPerformanceEntry 43 } + +pgmReceiverNakSvcTimeMin OBJECT-TYPE + SYNTAX Counter32 + UNITS "miliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The min time that it took for a loss + to be repaired." + ::= { pgmReceiverPerformanceEntry 44 } + +pgmReceiverNakSvcTimeMean OBJECT-TYPE + SYNTAX Counter32 + UNITS "miliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The mean time that it took for all losses + to be repaired." + ::= { pgmReceiverPerformanceEntry 45 } + +pgmReceiverNakSvcTimeMax OBJECT-TYPE + SYNTAX Counter32 + UNITS "miliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The max time that it took for a loss + to be repaired." + ::= { pgmReceiverPerformanceEntry 46 } + +pgmReceiverNakFailTimeMin OBJECT-TYPE + SYNTAX Counter32 + UNITS "miliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The min time it took for a loss + to be considered unrecoverable." + ::= { pgmReceiverPerformanceEntry 47 } + +pgmReceiverNakFailTimeMean OBJECT-TYPE + SYNTAX Counter32 + UNITS "miliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The mean time it took for all losses + to be considered unrecoverable." + ::= { pgmReceiverPerformanceEntry 48 } + +pgmReceiverNakFailTimeMax OBJECT-TYPE + SYNTAX Counter32 + UNITS "miliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The max time it took for a loss + to be considered unrecoverable." + ::= { pgmReceiverPerformanceEntry 49 } + +pgmReceiverNakTransmitMin OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The min number of times an individual NAK + needed to be retransmitted before it was repaired." + ::= { pgmReceiverPerformanceEntry 50 } + +pgmReceiverNakTransmitMean OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The mean number of times an individual NAK + needed to be retransmitted before it was repaired." + ::= { pgmReceiverPerformanceEntry 51 } + +pgmReceiverNakTransmitMax OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The max number of times an individual NAK + needed to be retransmitted before it was repaired." + ::= { pgmReceiverPerformanceEntry 52 } + +pgmReceiverAcksSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of ACKs sent from the congestion + control operation." + ::= { pgmReceiverPerformanceEntry 53 } + +pgmReceiverRxwTrail OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Sequence number of the trailing edge of + the transmission window as is being advertised + by the sender." + ::= { pgmReceiverPerformanceEntry 54 } + +pgmReceiverRxwLead OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Sequence number of the leading edge of + the transmission window as is being advertised + by the sender." + ::= { pgmReceiverPerformanceEntry 55 } + +pgmReceiverNakFailuresLastInterval OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The actual number of seconds since the + last pgmReceiverLastIntervalNakFailures + counter reset due to number of nak failures + threshold exceeded." + ::= { pgmReceiverPerformanceEntry 56 } + +pgmReceiverLastIntervalNakFailures OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of actual unrecoverable failures for + the requested threshold interval for this session." + ::= { pgmReceiverPerformanceEntry 57 } + +-- +-- Designated Local Repairer (DLR) +-- + +-- Designated Local Repairer (DLR) Default Configuration + +pgmDlrSaveDefaults OBJECT-TYPE + SYNTAX INTEGER { initial (1), + save (2), + pending (3), + success (4), + failure (5) } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Flag used to initiate the storing + of all default variable values to + non-volatile storage and to report the + result of the operation. + The following values can only be read, + never written : + initial(1) - returned prior to any requests + for saving the default configuration + pending(3) - saving in progress + success(4) - returned when a save(2) request + is successful + failure(5) - returned when a save(2) request + is unsuccessful + + The following values can only be written, + never read : + save(2) - to indicate that the default + configuration should be saved." + ::= { pgmDLR 1 } + +pgmDlrLastUpdateTime OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of TimeTicks since the last update + of the non-volatile storage." + ::= { pgmDLR 2 } + +pgmDlrGroupAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Multicast group address to listen for traffic + on." + ::= { pgmDLR 3 } + +pgmDlrCacheRtx OBJECT-TYPE + SYNTAX INTEGER + { + cacheOFF(1), + cacheON(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Specifies if the NE should also cache data for + retransmission or simply suppress duplicate + NAKs and forward the NAKs to it's parent NE or + sender." + ::= { pgmDLR 4 } + +pgmDlrActivityIvl OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Specifies the delay between activity checks + for specific PGM sessions." + ::= { pgmDLR 5 } + +pgmDlrMaxRate OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Specifies the maximum rate (in bps) for + retransmissions." + ::= { pgmDLR 6 } + +pgmDlrParentNeAddr OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Ip Address of the NE to send all NAKs to." + ::= { pgmDLR 7 } + +pgmDlrUpdateSinceLastSave OBJECT-TYPE + SYNTAX INTEGER + { + notUpdated(1), + updated(2) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Specifies if any of the Dlr Default + variables have been updated or not, + since the last successful pgmDlrSaveDefaults. + notUpdated - none of the default Dlr + variables were set after the last + successful save to a non-volatile + storage. + updated - at least one of the default Dlr + variables were set after the last + successful save to a non-volatile + storage." + ::= { pgmDLR 8 } + + +-- +-- PGM DLR Source/Re-transmitter Sessions +-- +pgmDlrSource OBJECT IDENTIFIER ::= { pgmDLR 100 } + +pgmDlrSourceNumberOfEntries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of PGM Source sessions for + the PGM DLR." + ::= { pgmDlrSource 1 } + +-- PGM Dlr Source Fault Management Table + +pgmDlrSourceTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmDlrSourceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per TSI fault + management and general information + related to the PGM DLR Source sessions." + ::= {pgmDlrSource 2} + +pgmDlrSourceEntry OBJECT-TYPE + SYNTAX PgmDlrSourceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per PGM DLR Source sessions fault + management information." + INDEX { pgmDlrSourceGlobalId, + pgmDlrSourceSourcePort } + ::= { pgmDlrSourceTable 1 } + +PgmDlrSourceEntry ::= SEQUENCE { + pgmDlrSourceGlobalId + OCTET STRING, + pgmDlrSourceSourcePort + Unsigned32, + pgmDlrSourceGroupAddress + IpAddress, + pgmDlrSourceSourceGsi + OCTET STRING, + pgmDlrSourceSourcePortNumber + Unsigned32 + } + +pgmDlrSourceGlobalId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Globally unique source identifier (GSI)." + ::= { pgmDlrSourceEntry 1 } + +pgmDlrSourceSourcePort OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmDlrSourceEntry 2 } + +pgmDlrSourceGroupAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Multicast group interface address + to send multicast packets on." + ::= { pgmDlrSourceEntry 3 } + +pgmDlrSourceSourceGsi OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Globally unique source identifier (GSI)." + ::= { pgmDlrSourceEntry 4 } + +pgmDlrSourceSourcePortNumber OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmDlrSourceEntry 5 } + +-- PGM DLR Source Configuration Management Table + +pgmDlrSourceConfigTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmDlrSourceConfigEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per TSI configuration + management information related to the + PGM DLR Source sessions." + ::= {pgmDlrSource 3} + +pgmDlrSourceConfigEntry OBJECT-TYPE + SYNTAX PgmDlrSourceConfigEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per PGM DLR Source sessions configuration + management information." + INDEX { pgmDlrSourceConfigGlobalId, + pgmDlrSourceConfigSourcePort } + ::= { pgmDlrSourceConfigTable 1 } + +PgmDlrSourceConfigEntry ::= SEQUENCE { + pgmDlrSourceConfigGlobalId + OCTET STRING, + pgmDlrSourceConfigSourcePort + Unsigned32, + pgmDlrSourceGroupTtl + Unsigned32, + pgmDlrSourceRdataBackoffIvl + Unsigned32 + } + +pgmDlrSourceConfigGlobalId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Globally unique source identifier (GSI)." + ::= { pgmDlrSourceConfigEntry 1 } + +pgmDlrSourceConfigSourcePort OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmDlrSourceConfigEntry 2 } + +pgmDlrSourceGroupTtl OBJECT-TYPE + SYNTAX Unsigned32(1..255) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "This option sets the default TTL to use for + multicast packets. " + ::= { pgmDlrSourceConfigEntry 3 } + +pgmDlrSourceRdataBackoffIvl OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "This option sets the default RDATA backoff + interval. The value is expressed in milliseconds. + The value of 0 indicates no backoff." + ::= { pgmDlrSourceConfigEntry 4 } + + +-- PGM DLR Source Performance Management Table + +pgmDlrSourcePerformanceTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmDlrSourcePerformanceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per TSI performance + management information related to the + PGM DLR Source sessions." + ::= {pgmDlrSource 4} + +pgmDlrSourcePerformanceEntry OBJECT-TYPE + SYNTAX PgmDlrSourcePerformanceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per PGM DLR Source performance management + information." + INDEX { pgmDlrSourcePerformanceGlobalId, + pgmDlrSourcePerformanceSourcePort } + ::= { pgmDlrSourcePerformanceTable 1 } + +PgmDlrSourcePerformanceEntry ::= SEQUENCE { + pgmDlrSourcePerformanceGlobalId + OCTET STRING, + pgmDlrSourcePerformanceSourcePort + Unsigned32, + pgmDlrSourceRdataMsgsSent + Counter32, + pgmDlrSourceRdataBytesSent + Counter32, + pgmDlrSourceBytesSent + Counter32, + pgmDlrSourceNaksRcvd + Counter32, + pgmDlrSourceNaksIgnored + Counter32, + pgmDlrSourceNakErrors + Counter32, + pgmDlrSourceDiscards + Counter32, + pgmDlrSourceCksumErrors + Counter32, + pgmDlrSourceNNaksSent + Counter32, + pgmDlrSourceBytesBuffered + Counter32, + pgmDlrSourceMsgsBuffered + Counter32, + pgmDlrSourceParityBytesRetransmitted + Counter32, + pgmDlrSourceSelectiveBytesRetransmited + Counter32, + pgmDlrSourceParityMsgsRetransmitted + Counter32, + pgmDlrSourceSelectiveMsgsRetransmitted + Counter32, + pgmDlrSourceBytesAdmit + Counter32, + pgmDlrSourceMsgsAdmit + Counter32, + pgmDlrSourceNakPacketsReceived + Counter32, + pgmDlrSourceParityNakPacketsReceived + Counter32, + pgmDlrSourceSelectiveNakPacketsReceived + Counter32, + pgmDlrSourceParityNaksReceived + Counter32, + pgmDlrSourceSelectiveNaksReceived + Counter32, + pgmDlrSourceParityNaksIgnored + Counter32, + pgmDlrSourceSelectiveNaksIgnored + Counter32, + pgmDlrSourceAckErrors + Counter32, + pgmDlrSourceNNakErrors + Counter32, + pgmDlrSourceAckPacketsReceived + Counter32, + pgmDlrSourceNNakPacketsReceived + Counter32, + pgmDlrSourceParityNNakPacketsReceived + Counter32, + pgmDlrSourceSelectiveNNakPacketsReceived + Counter32, + pgmDlrSourceNNaksReceived + Counter32, + pgmDlrSourceParityNNaksReceived + Counter32, + pgmDlrSourceSelectiveNNaksReceived + Counter32 + } + +pgmDlrSourcePerformanceGlobalId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Globally unique source identifier (GSI)." + ::= { pgmDlrSourcePerformanceEntry 1 } + +pgmDlrSourcePerformanceSourcePort OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmDlrSourcePerformanceEntry 2 } + +pgmDlrSourceRdataMsgsSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of Repair Data (RDATA) packets sent for + this PGM DLR." + ::= { pgmDlrSourcePerformanceEntry 3 } + +pgmDlrSourceRdataBytesSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of RDATA bytes sent." + ::= { pgmDlrSourcePerformanceEntry 4 } + +pgmDlrSourceBytesSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes sent. This includes IP and + PGM header and non-data msgs." + ::= { pgmDlrSourcePerformanceEntry 5 } + +pgmDlrSourceNaksRcvd OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAKs received on this TSI." + ::= { pgmDlrSourcePerformanceEntry 6 } + +pgmDlrSourceNaksIgnored OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAKs ignored on this TSI, due to + duplicates." + ::= { pgmDlrSourcePerformanceEntry 7 } + +pgmDlrSourceNakErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of malformed NAKs on this TSI." + ::= { pgmDlrSourcePerformanceEntry 8 } + +pgmDlrSourceDiscards OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of discarded packets on this TSI. + This counter is used to count all discarded + incoming packets per TSI in cases of + duplicates, header and packet errors, etc." + ::= { pgmDlrSourcePerformanceEntry 9 } + +pgmDlrSourceCksumErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of checksum errors on this TSI." + ::= { pgmDlrSourcePerformanceEntry 10 } + +pgmDlrSourceNNaksSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of Null NAKs (in number of packets) + sent by this PGM DLR session." + ::= { pgmDlrSourcePerformanceEntry 11 } + +pgmDlrSourceBytesBuffered OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes currently buffered for this + TSI." + ::= { pgmDlrSourcePerformanceEntry 12 } + +pgmDlrSourceMsgsBuffered OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of messages currently buffered for + this TSI." + ::= { pgmDlrSourcePerformanceEntry 13 } + +pgmDlrSourceParityBytesRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes sent in parity retransmissions." + ::= { pgmDlrSourcePerformanceEntry 14 } + +pgmDlrSourceSelectiveBytesRetransmited OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes sent in selective retransmissions." + ::= { pgmDlrSourcePerformanceEntry 15 } + +pgmDlrSourceParityMsgsRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of parity retransmissions sent." + ::= { pgmDlrSourcePerformanceEntry 16 } + +pgmDlrSourceSelectiveMsgsRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of selective retransmissions sent." + ::= { pgmDlrSourcePerformanceEntry 17 } + +pgmDlrSourceBytesAdmit OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes currently in the rate controled + admit queue. Includes IP header, UDP header if + encapsulated, PGM header, and data." + ::= { pgmDlrSourcePerformanceEntry 18 } + +pgmDlrSourceMsgsAdmit OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of messages currently in the rate controled + admit queue. Includes data messages, retransmissions, + and SPMs." + ::= { pgmDlrSourcePerformanceEntry 19 } + +pgmDlrSourceNakPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAK packets received." + ::= { pgmDlrSourcePerformanceEntry 20 } + +pgmDlrSourceParityNakPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of parity NAK packets received." + ::= { pgmDlrSourcePerformanceEntry 21 } + +pgmDlrSourceSelectiveNakPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of selective NAK packets received." + ::= { pgmDlrSourcePerformanceEntry 22 } + +pgmDlrSourceParityNaksReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual parity NAKs received." + ::= { pgmDlrSourcePerformanceEntry 23 } + +pgmDlrSourceSelectiveNaksReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual selective NAKs received." + ::= { pgmDlrSourcePerformanceEntry 24 } + +pgmDlrSourceParityNaksIgnored OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual parity NAKs ignored." + ::= { pgmDlrSourcePerformanceEntry 25 } + +pgmDlrSourceSelectiveNaksIgnored OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual selective NAKs ignored." + ::= { pgmDlrSourcePerformanceEntry 26 } + +pgmDlrSourceAckErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of ACK packets received with error in + them, different than checksum error." + ::= { pgmDlrSourcePerformanceEntry 27 } + +pgmDlrSourceNNakErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of Null NAK packets received that contain + error, rSifferent than checksum error." + ::= { pgmDlrSourcePerformanceEntry 28 } + +pgmDlrSourceAckPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of ACK packets received." + ::= { pgmDlrSourcePerformanceEntry 29 } + +pgmDlrSourceNNakPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of Null NAKs received." + ::= { pgmDlrSourcePerformanceEntry 30 } + +pgmDlrSourceParityNNakPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of parity Null NAK packets received." + ::= { pgmDlrSourcePerformanceEntry 31 } + +pgmDlrSourceSelectiveNNakPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of selective Null NAK packets received." + ::= { pgmDlrSourcePerformanceEntry 32 } + +pgmDlrSourceNNaksReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual NAKs, received in Null + NAK packets." + ::= { pgmDlrSourcePerformanceEntry 33 } + +pgmDlrSourceParityNNaksReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual parity NAKs, + received in Null NAK packets." + ::= { pgmDlrSourcePerformanceEntry 34 } + +pgmDlrSourceSelectiveNNaksReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual NAKs, + received in Null NAK packets." + ::= { pgmDlrSourcePerformanceEntry 35 } + +-- Notifications + +pgmNotifications OBJECT IDENTIFIER ::= + { pgmNotificationPrefix 0 } + +pgmStart NOTIFICATION-TYPE + STATUS current + DESCRIPTION + "This trap is sent when the pgm snmp agent starts" + ::= { pgmNotifications 1 } + +pgmStop NOTIFICATION-TYPE + STATUS current + DESCRIPTION + "This trap is sent when the pgm snmp agent terminates" + ::= { pgmNotifications 2 } + +-- PGM Source Specific Traps + +pgmNewSourceTrap NOTIFICATION-TYPE + OBJECTS { + pgmSourceSourceGsi, + pgmSourceSourcePortNumber + } + STATUS current + DESCRIPTION + "New Source Session created." + ::= { pgmNotifications 3 } + +pgmClosedSourceTrap NOTIFICATION-TYPE + OBJECTS { + pgmSourceSourceGsi, + pgmSourceSourcePortNumber + } + STATUS current + DESCRIPTION + "Source Session closed." + ::= { pgmNotifications 4 } + +-- PGM Receiver Specific Traps + +pgmNewReceiverTrap NOTIFICATION-TYPE + OBJECTS { + pgmReceiverSourceGsi, + pgmReceiverSourcePortNumber, + pgmReceiverUniqueInstance + } + STATUS current + DESCRIPTION + "New Receiver Session created. + This trap is optional." + ::= { pgmNotifications 5 } + +pgmClosedReceiverTrap NOTIFICATION-TYPE + OBJECTS { + pgmReceiverSourceGsi, + pgmReceiverSourcePortNumber, + pgmReceiverUniqueInstance + } + STATUS current + DESCRIPTION + "Receiver Session closed. + This trap is optional." + ::= { pgmNotifications 6 } + +pgmNakFailuresTrap NOTIFICATION-TYPE + OBJECTS { + pgmReceiverSourceGsi, + pgmReceiverSourcePortNumber, + pgmReceiverUniqueInstance, + pgmReceiverNakFailureThresholdTimer, + pgmReceiverNakFailureThreshold, + pgmReceiverNakFailuresLastInterval, + pgmReceiverLastIntervalNakFailures + } + STATUS current + DESCRIPTION + "The number of unrecovered lost packets + exceeded the threshold limit for the + corresponding threshold interval." + ::= { pgmNotifications 7 } + +-- PGM Dlr Source Specific Traps + +pgmNewDlrSourceTrap NOTIFICATION-TYPE + OBJECTS { + pgmDlrSourceSourceGsi, + pgmDlrSourceSourcePortNumber + } + STATUS current + DESCRIPTION + "New Dlr Source Session created." + ::= { pgmNotifications 8 } + +pgmClosedDlrSourceTrap NOTIFICATION-TYPE + OBJECTS { + pgmDlrSourceSourceGsi, + pgmDlrSourceSourcePortNumber + } + STATUS current + DESCRIPTION + "Dlr Source Session closed." + ::= { pgmNotifications 9 } + +-- Conformance information + +pgmMIBConformance OBJECT IDENTIFIER ::= { pgmMIB 3 } +pgmMIBCompliances OBJECT IDENTIFIER ::= { pgmMIBConformance 1 } +pgmMIBGroups OBJECT IDENTIFIER ::= { pgmMIBConformance 2 } + +-- Compliance statements + +pgmNetworkElementMIBCompliance MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement for devices running as PGM + Network Elements." + MODULE -- this module + MANDATORY-GROUPS { pgmNetworkElementMIBGroup } + + ::= { pgmMIBCompliances 1 } + +pgmSourceMIBCompliance MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement for devices running as PGM + sources." + MODULE -- this module + MANDATORY-GROUPS { pgmSourceMIBGroup } + + ::= { pgmMIBCompliances 2 } + +pgmReceiverMIBCompliance MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement for devices running as PGM + receivers." + MODULE -- this module + MANDATORY-GROUPS { pgmReceiverMIBGroup } + + ::= { pgmMIBCompliances 3 } + +pgmDLRMIBCompliance MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement for devices running as PGM + designated local repairers (DLR)." + MODULE -- this module + MANDATORY-GROUPS { pgmDLRMIBGroup, + pgmReceiverMIBGroup } + + ::= { pgmMIBCompliances 4 } + +pgmTrapsMIBCompliance MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement for PGM traps." + MODULE -- this module + MANDATORY-GROUPS { pgmTrapsMIBGroup, + pgmTrapsSourceMIBGroup, + pgmTrapsReceiverMIBGroup, + pgmTrapsDlrSourceMIBGroup } + ::= { pgmMIBCompliances 5 } + +-- Units of conformance + +pgmNetworkElementMIBGroup OBJECT-GROUP + OBJECTS { pgmNeEnable, + pgmNeSessionLifeTime, + pgmNeMaxReXmitStates, + pgmNeMaxSessions, + pgmNeTotalInterfacesNumberOfEntries, + pgmNeIfPgmEnable, + pgmNeIfNakRptInterval, + pgmNeIfNakRptRate, + pgmNeIfNakRdataInterval, + pgmNeIfNakEliminateInterval, + pgmNeIfReXmitStates, + pgmNeIfReXmitTimedOut, + pgmNeIfInSpms, + pgmNeIfOutSpms, + pgmNeIfInParitySpms, + pgmNeIfOutParitySpms, + pgmNeIfInRdata, + pgmNeIfOutRdata, + pgmNeIfInParityRdata, + pgmNeIfOutParityRdata, + pgmNeIfInRdataNoSessionErrors, + pgmNeIfUniqueNaks, + pgmNeIfInNaks, + pgmNeIfOutNaks, + pgmNeIfUniqueParityNaks, + pgmNeIfInParityNaks, + pgmNeIfOutParityNaks, + pgmNeIfInNakNoSessionErrors, + pgmNeIfInNakSeqErrors, + pgmNeIfInParityNakTgErrors, + pgmNeIfInNnaks, + pgmNeIfOutNnaks, + pgmNeIfInParityNnaks, + pgmNeIfOutParityNnaks, + pgmNeIfInNnakNoSessionErrors, + pgmNeIfInNcfs, + pgmNeIfOutNcfs, + pgmNeIfInParityNcfs, + pgmNeIfOutParityNcfs, + pgmNeIfInNcfNoSessionErrors, + pgmNeIfInRedirectNcfs, + pgmNeIfMalformed, + pgmNeIfSpmFromSource, + pgmNeIfSpmBadSqn, + pgmNeIfSpmError, + pgmNeIfPollRandomIgnore, + pgmNeIfPollTsiStateError, + pgmNeIfPollParentError, + pgmNeIfPollTypeError, + pgmNeIfPollError, + pgmNeIfPollSuccess, + pgmNeIfPollOriginated, + pgmNeIfPolrNoState, + pgmNeIfPolrError, + pgmNeIfPolrParityError, + pgmNeIfPolrSuccess, + pgmNeIfPolrOriginated, + pgmNeIfNcfError, + pgmNeIfNcfParityError, + pgmNeIfNcfPartialParity, + pgmNeIfNcfReceived, + pgmNeIfNcfAnticipated, + pgmNeIfNcfRedirecting, + pgmNeIfNakEliminated, + pgmNeIfNakError, + pgmNeIfNakParityError, + pgmNeIfNNakEliminated, + pgmNeIfNNakError, + pgmNeIfNNakParityError, + pgmNeIfNNakCongestionReports, + pgmNeIfNakRetryExpired, + pgmNeIfNakRetryExpiredDLR, + pgmNeIfNakForwardedDLR, + pgmNeIfNakRetransmitted, + pgmNeIfRdataEliminatedOIF, + pgmNeIfRdataEliminatedSqn, + pgmNeIfInRdataFragments, + pgmNeIfRdataFragmentsNoSessionErrors, + pgmNeIfRdataFragmentsEliminatedOIF, + pgmNeIfRdataFragmentsEliminatedSqn, + pgmNeIfOutRdataFragments, + pgmNeTotalTsiNumberOfEntries, + pgmNeTsiStateBits, + pgmNeTsiDataDestinationPort, + pgmNeTsiSourceAddress, + pgmNeTsiGroupAddress, + pgmNeTsiUpstreamAddress, + pgmNeTsiUpstreamIfIndex, + pgmNeTsiDlrAddress, + pgmNeTsiSessionTrailEdgeSeq, + pgmNeTsiSessionIncrSeq, + pgmNeTsiLeadEdgeSeq, + pgmNeTsiInSpms, + pgmNeTsiOutSpms, + pgmNeTsiInParitySpms, + pgmNeTsiOutParitySpms, + pgmNeTsiTotalReXmitStates, + pgmNeTsiTotalReXmitTimedOut, + pgmNeTsiInRdata, + pgmNeTsiOutRdata, + pgmNeTsiInParityRdata, + pgmNeTsiOutParityRdata, + pgmNeTsiInRdataNoStateErrors, + pgmNeTsiUniqueNaks, + pgmNeTsiInNaks, + pgmNeTsiOutNaks, + pgmNeTsiUniqueParityNaks, + pgmNeTsiInParityNaks, + pgmNeTsiOutParityNaks, + pgmNeTsiInNakSeqErrors, + pgmNeTsiInNnaks, + pgmNeTsiOutNnaks, + pgmNeTsiInParityNnaks, + pgmNeTsiOutParityNnaks, + pgmNeTsiInNcfs, + pgmNeTsiOutNcfs, + pgmNeTsiInParityNcfs, + pgmNeTsiOutParityNcfs, + pgmNeTsiSpmSequenceNumber, + pgmNeTsiTransmissionGroupSize, + pgmNeTsiTimeout, + pgmNeTsiLastTtl, + pgmNeTsiLinkLossRate, + pgmNeTsiPathLossRate, + pgmNeTsiReceiverLossRate, + pgmNeTsiCongestionReportLead, + pgmNeTsiCongestionReportWorstReceiver, + pgmNeTsiRtxNumberOfEntries, + pgmNeTsiRtxReqParityTgCount, + pgmNeTsiRtxTimeout, + pgmNeTsiRtxStateBits, + pgmNeTsiRtxIfNumberOfEntries, + pgmNeTsiRtxIfPacketCount, + pgmNeTsiPolrNumberOfEntries, + pgmNeTsiPolrSequenceNumber, + pgmNeTsiPollNumberOfEntries, + pgmNeTsiPollSequence, + pgmNeTsiPollChildBackoff, + pgmNeTsiPollMask, + pgmNeTsiPollPeriod, + pgmNeTsiPollCount, + pgmNeTsiPollTimeout } + STATUS current + DESCRIPTION + "A collection of objects to support + management of PGM Network Elements." + ::= { pgmMIBGroups 1 } + +pgmSourceMIBGroup OBJECT-GROUP + OBJECTS { pgmSourceSaveDefaults, + pgmSourceLastUpdateTime, + pgmSourceDefaultTtl, + pgmSourceDefaultAdvMode, + pgmSourceDefaultLateJoin, + pgmSourceDefaultTxwMaxRte, + pgmSourceDefaultTxwSecs, + pgmSourceDefaultTxwAdvSecs, + pgmSourceDefaultAdvIvl, + pgmSourceDefaultSpmIvl, + pgmSourceDefaultSpmHeartBeatIvlMin, + pgmSourceDefaultSpmHeartBeatIvlMax, + pgmSourceDefaultRdataBackoffIvl, + pgmSourceDefaultFECProactiveParitySize, + pgmSourceDefaultGroupAddress, + pgmSourceUpdateSinceLastSave, + pgmSourceNumberOfEntries, + pgmSourceSourceAddress, + pgmSourceGroupAddress, + pgmSourceDestPort, + pgmSourceSourceGsi, + pgmSourceSourcePortNumber, + pgmSourceTtl, + pgmSourceAdvMode, + pgmSourceLateJoin, + pgmSourceTxwMaxRte, + pgmSourceTxwSecs, + pgmSourceTxwAdvSecs, + pgmSourceAdvIvl, + pgmSourceSpmIvl, + pgmSourceSpmHeartBeatIvlMin, + pgmSourceSpmHeartBeatIvlMax, + pgmSourceRdataBackoffIvl, + pgmSourceFEC, + pgmSourceFECTransmissionGrpSize, + pgmSourceFECProactiveParitySize, + pgmSourceSpmPathAddress, + pgmSourceDataBytesSent, + pgmSourceDataMsgsSent, + pgmSourceBytesBuffered, + pgmSourceMsgsBuffered, + pgmSourceBytesRetransmitted, + pgmSourceMsgsRetransmitted, + pgmSourceBytesSent, + pgmSourceRawNaksReceived, + pgmSourceNaksIgnored, + pgmSourceCksumErrors, + pgmSourceMalformedNaks, + pgmSourcePacketsDiscarded, + pgmSourceNaksRcvd, + pgmSourceParityBytesRetransmitted, + pgmSourceSelectiveBytesRetransmited, + pgmSourceParityMsgsRetransmitted, + pgmSourceSelectiveMsgsRetransmitted, + pgmSourceBytesAdmit, + pgmSourceMsgsAdmit, + pgmSourceParityNakPacketsReceived, + pgmSourceSelectiveNakPacketsReceived, + pgmSourceParityNaksReceived, + pgmSourceSelectiveNaksReceived, + pgmSourceParityNaksIgnored, + pgmSourceSelectiveNaksIgnored, + pgmSourceAckErrors, + pgmSourcePgmCCAcker, + pgmSourceTransmissionCurrentRate, + pgmSourceAckPacketsReceived, + pgmSourceNNakPacketsReceived, + pgmSourceParityNNakPacketsReceived, + pgmSourceSelectiveNNakPacketsReceived, + pgmSourceNNaksReceived, + pgmSourceParityNNaksReceived, + pgmSourceSelectiveNNaksReceived, + pgmSourceNNakErrors } + STATUS current + DESCRIPTION + "A collection of objects to support management of + PGM sources." + ::= { pgmMIBGroups 2 } + +pgmReceiverMIBGroup OBJECT-GROUP + OBJECTS { pgmReceiverSaveDefaults, + pgmReceiverLastUpdateTime, + pgmReceiverDefaultNakBackoffIvl, + pgmReceiverDefaultNakRepeatIvl, + pgmReceiverDefaultNakNcfRetries, + pgmReceiverDefaultNakRdataIvl, + pgmReceiverDefaultNakDataRetries, + pgmReceiverDefaultSendNaks, + pgmReceiverDefaultLateJoin, + pgmReceiverDefaultNakTtl, + pgmReceiverDefaultDeliveryOrder, + pgmReceiverDefaultNextPgmHop, + pgmReceiverDefaultGroupAddress, + pgmReceiverUpdateSinceLastSave, + pgmReceiverDefaultNakFailureThresholdTimer, + pgmReceiverDefaultNakFailureThreshold, + pgmReceiverNumberOfEntries, + pgmReceiverGroupAddress, + pgmReceiverDestPort, + pgmReceiverSourceAddress, + pgmReceiverLastHop, + pgmReceiverSourceGsi, + pgmReceiverSourcePortNumber, + pgmReceiverUniqueInstance, + pgmReceiverNakBackoffIvl, + pgmReceiverNakRepeatIvl, + pgmReceiverNakNcfRetries, + pgmReceiverNakRdataIvl, + pgmReceiverNakDataRetries, + pgmReceiverSendNaks, + pgmReceiverLateJoin, + pgmReceiverNakTtl, + pgmReceiverDeliveryOrder, + pgmReceiverMcastNaks, + pgmReceiverNakFailureThresholdTimer, + pgmReceiverNakFailureThreshold, + pgmReceiverDataBytesReceived, + pgmReceiverDataMsgsReceived, + pgmReceiverNaksSent, + pgmReceiverNaksRetransmitted, + pgmReceiverNakFailures, + pgmReceiverBytesReceived, + pgmReceiverNaksSuppressed, + pgmReceiverCksumErrors, + pgmReceiverMalformedSpms, + pgmReceiverMalformedOdata, + pgmReceiverMalformedRdata, + pgmReceiverMalformedNcfs, + pgmReceiverPacketsDiscarded, + pgmReceiverLosses, + pgmReceiverBytesDeliveredToApp, + pgmReceiverMsgsDeliveredToApp, + pgmReceiverDupSpms, + pgmReceiverDupDatas, + pgmReceiverDupParities, + pgmReceiverNakPacketsSent, + pgmReceiverParityNakPacketsSent, + pgmReceiverSelectiveNakPacketsSent, + pgmReceiverParityNaksSent, + pgmReceiverSelectiveNaksSent, + pgmReceiverParityNaksRetransmitted, + pgmReceiverSelectiveNaksRetransmitted, + pgmReceiverNaksFailed, + pgmReceiverParityNaksFailed, + pgmReceiverSelectiveNaksFailed, + pgmReceiverNaksFailedRxwAdvanced, + pgmReceiverNaksFaledNcfRetriesExceeded, + pgmReceiverNaksFailedDataRetriesExceeded, + pgmReceiverNaksFailedGenExpired, + pgmReceiverNakFailuresDelivered, + pgmReceiverParityNaksSuppressed, + pgmReceiverSelectiveNaksSuppressed, + pgmReceiverNakErrors, + pgmReceiverOutstandingParityNaks, + pgmReceiverOutstandingSelectiveNaks, + pgmReceiverLastActivity, + pgmReceiverNakSvcTimeMin, + pgmReceiverNakSvcTimeMean, + pgmReceiverNakSvcTimeMax, + pgmReceiverNakFailTimeMin, + pgmReceiverNakFailTimeMean, + pgmReceiverNakFailTimeMax, + pgmReceiverNakTransmitMin, + pgmReceiverNakTransmitMean, + pgmReceiverNakTransmitMax, + pgmReceiverAcksSent, + pgmReceiverRxwTrail, + pgmReceiverRxwLead, + pgmReceiverNakFailuresLastInterval, + pgmReceiverLastIntervalNakFailures } + STATUS current + DESCRIPTION + "A collection of objects to support management of + PGM receivers." + ::= { pgmMIBGroups 3 } + +pgmDLRMIBGroup OBJECT-GROUP + OBJECTS { pgmDlrSaveDefaults, + pgmDlrLastUpdateTime, + pgmDlrGroupAddress, + pgmDlrCacheRtx, + pgmDlrActivityIvl, + pgmDlrMaxRate, + pgmDlrParentNeAddr, + pgmDlrUpdateSinceLastSave, + pgmDlrSourceNumberOfEntries, + pgmDlrSourceGroupAddress, + pgmDlrSourceSourceGsi, + pgmDlrSourceSourcePortNumber, + pgmDlrSourceGroupTtl, + pgmDlrSourceRdataBackoffIvl, + pgmDlrSourceRdataMsgsSent, + pgmDlrSourceRdataBytesSent, + pgmDlrSourceBytesSent, + pgmDlrSourceNaksRcvd, + pgmDlrSourceNaksIgnored, + pgmDlrSourceNakErrors, + pgmDlrSourceDiscards, + pgmDlrSourceCksumErrors, + pgmDlrSourceNNaksSent, + pgmDlrSourceBytesBuffered, + pgmDlrSourceMsgsBuffered, + pgmDlrSourceParityBytesRetransmitted, + pgmDlrSourceSelectiveBytesRetransmited, + pgmDlrSourceParityMsgsRetransmitted, + pgmDlrSourceSelectiveMsgsRetransmitted, + pgmDlrSourceBytesAdmit, + pgmDlrSourceMsgsAdmit, + pgmDlrSourceNakPacketsReceived, + pgmDlrSourceParityNakPacketsReceived, + pgmDlrSourceSelectiveNakPacketsReceived, + pgmDlrSourceParityNaksReceived, + pgmDlrSourceSelectiveNaksReceived, + pgmDlrSourceParityNaksIgnored, + pgmDlrSourceSelectiveNaksIgnored, + pgmDlrSourceAckErrors, + pgmDlrSourceNNakErrors, + pgmDlrSourceAckPacketsReceived, + pgmDlrSourceNNakPacketsReceived, + pgmDlrSourceParityNNakPacketsReceived, + pgmDlrSourceSelectiveNNakPacketsReceived, + pgmDlrSourceNNaksReceived, + pgmDlrSourceParityNNaksReceived, + pgmDlrSourceSelectiveNNaksReceived } + STATUS current + DESCRIPTION + "A collection of objects to support management of + PGM designated local repairers (DLR)." + ::= { pgmMIBGroups 4 } + +pgmTrapsMIBGroup NOTIFICATION-GROUP + NOTIFICATIONS { pgmStart, + pgmStop } + STATUS current + DESCRIPTION + "A collection of objects to support pgm + specific traps." + ::= { pgmMIBGroups 5 } + +pgmTrapsSourceMIBGroup NOTIFICATION-GROUP + NOTIFICATIONS { pgmNewSourceTrap, + pgmClosedSourceTrap } + STATUS current + DESCRIPTION + "A collection of objects to support pgm + source specific traps." + ::= { pgmMIBGroups 6 } + +pgmTrapsReceiverMIBGroup NOTIFICATION-GROUP + NOTIFICATIONS { pgmNewReceiverTrap, + pgmClosedReceiverTrap, + pgmNakFailuresTrap } + STATUS current + DESCRIPTION + "A collection of objects to support pgm + receiver specific traps." + ::= { pgmMIBGroups 7 } + +pgmTrapsDlrSourceMIBGroup NOTIFICATION-GROUP + NOTIFICATIONS { pgmNewDlrSourceTrap, + pgmClosedDlrSourceTrap } + STATUS current + DESCRIPTION + "A collection of objects to support pgm + dlr source specific traps." + ::= { pgmMIBGroups 8 } + + +END + diff --git a/3rdparty/openpgm-svn-r1085/pgm/mld-semantics.txt b/3rdparty/openpgm-svn-r1085/pgm/mld-semantics.txt new file mode 100644 index 0000000..135400d --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/mld-semantics.txt @@ -0,0 +1,52 @@ + previous request following request return + ----------------- ----------------- ----------- + MCAST_JOIN_GROUP MCAST_JOIN_GROUP EADDRINUSE + MCAST_JOIN_GROUP MCAST_LEAVE_GROUP 0 + MCAST_JOIN_GROUP MCAST_JOIN_SOURCE_GROUP EINVAL + MCAST_JOIN_GROUP MCAST_LEAVE_SOURCE_GROUP EINVAL + MCAST_JOIN_GROUP MCAST_BLOCK_SOURCE 0 + MCAST_JOIN_SOURCE_GROUP MCAST_JOIN_GROUP EADDRINUSE + MCAST_JOIN_SOURCE_GROUP MCAST_LEAVE_GROUP 0 + MCAST_JOIN_SOURCE_GROUP MCAST_JOIN_SOURCE_GROUP (*1) + MCAST_JOIN_SOURCE_GROUP MCAST_LEAVE_SOURCE_GROUP (*2) + MCAST_JOIN_SOURCE_GROUP MCAST_BLOCK_SOURCE EINVAL + MCAST_JOIN_SOURCE_GROUP MCAST_UNBLOCK_SOURCE EINVAL + MCAST_BLOCK_SOURCE MCAST_JOIN_GROUP EADDRINUSE + MCAST_BLOCK_SOURCE MCAST_LEAVE_GROUP 0 + MCAST_BLOCK_SOURCE MCAST_JOIN_SOURCE_GROUP EINVAL + MCAST_BLOCK_SOURCE MCAST_LEAVE_SOURCE_GROUP EINVAL + MCAST_BLOCK_SOURCE MCAST_BLOCK_SOURCE (*1) + MCAST_BLOCK_SOURCE MCAST_UNBLOCK_SOURCE (*2) + +(*1) EADDRNOTAVAIL if source address is same of filtered one. Otherwise 0. +(*2) EADDRNOTAVAIL if source address is not same of filtered one. Otherwise 0. + + +http://planete.inria.fr/Hitoshi.Asaeda/mldv2/README.txt + + +The following steps apply for any-source applications: + + Use MCAST_JOIN_GROUP to join a group. + Use MCAST_BLOCK_SOURCE to turn off a given source, if required. + Use MCAST_UNBLOCK_SOURCE to re-allow a blocked source, if required. + Use MCAST_LEAVE_GROUP to leave the group. + +The following steps apply for controlled-source applications: + + Use MCAST_JOIN_SOURCE_GROUP to join each group/source pair. + Use MCAST_LEAVE_SOURCE_GROUP to leave each group/source, or use MCAST_LEAVE_GROUP to leave all sources, if the same group address is used by all sources. + +The following steps apply for any-source applications: + + Use IP_ADD_MEMBERSHIP to join a group (IPV6_ADD_MEMBERSHIP for IPv6). + Use IP_BLOCK_SOURCE to turn off a given source, if required. + Use IP_UNBLOCK_SOURCE to re-allow a blocked source, if required. + Use IP_DROP_MEMBERSHIP to leave the group (IPV6_DROP_MEMBERSHIP for IPv6). + +The following steps apply for controlled-source applications: + + Use IP_ADD_SOURCE_MEMBERSHIP to join each group/source pair. + Use IP_DROP_SOURCE_MEMBERSHIP to leave each group/source, or use IP_DROP_MEMBERSHIP to leave all sources, if the same group address is used by all sources. + +http://msdn.microsoft.com/en-us/library/ms738558(VS.85).aspx diff --git a/3rdparty/openpgm-svn-r1085/pgm/msfec.txt b/3rdparty/openpgm-svn-r1085/pgm/msfec.txt new file mode 100644 index 0000000..4b2c23a --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/msfec.txt @@ -0,0 +1,33 @@ +FEC parameters for Microsoft's PGM stack + + +FECBlockSize (n) [FECGroupSize+1, 255] +Maximum number of packets that can be sent for any group, including original data and parity packets. Maximum and default value is 255. + +FECProActivePackets +Number of packets to send proactively with each group. Use this option when the network is dispersed, and upstream NAK requests are expensive. + +FECGroupSize (k) [2, 128] +Number of packets to be treated as one group for the purpose of computing parity packets. Group size must be a power of two. In lossy networks, keep the group size relatively small. + +fFECOnDemandParityEnabled +Specifies whether the sender is enabled for sending parity repair packets. When TRUE, receivers should only request parity repair packets. + + +Reed Solomon codes: + + encode/decode time (us) +RS(255, 2) 4/6 +RS(255, 4) 7/10 +RS(255, 8) 14/18 +RS(255, 16) 29/34 +RS(255, 32) 57/64 +RS(255, 64) 119/134 +RS(255, 128) 236/fail(278) + +reference platform: Intel Xeon CPU 3.20Ghz + + +Implementation exact copy of Luigi Rizzo FEC code as demonstrated in RMDP: + +http://info.iet.unipi.it/~luigi/fec.html diff --git a/3rdparty/openpgm-svn-r1085/pgm/nametoindex.c b/3rdparty/openpgm-svn-r1085/pgm/nametoindex.c new file mode 100644 index 0000000..28444d1 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/nametoindex.c @@ -0,0 +1,249 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Windows interface name to interface index function. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef _WIN32 +# include +# include +#endif +#include +#include + + +//#define NAMETOINDEX_DEBUG + +#define MAX_TRIES 3 +#define DEFAULT_BUFFER_SIZE 4096 + + +#ifdef _WIN32 +static inline +void* +_pgm_heap_alloc ( + const size_t n_bytes + ) +{ +# ifdef CONFIG_USE_HEAPALLOC + return HeapAlloc (GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, n_bytes); +# else + return pgm_malloc (n_bytes); +# endif +} + +static inline +void +_pgm_heap_free ( + void* mem + ) +{ +# ifdef CONFIG_USE_HEAPALLOC + HeapFree (GetProcessHeap(), 0, mem); +# else + pgm_free (mem); +# endif +} + +/* Retrieve adapter index via name. + * Wine edition: First try GetAdapterIndex() then fallback to enumerating + * adapters via GetAdaptersInfo(). + * + * On error returns zero, no errors are defined. + */ + +static +unsigned /* type matching if_nametoindex() */ +_pgm_getadaptersinfo_nametoindex ( + const sa_family_t iffamily, + const char* ifname + ) +{ + pgm_return_val_if_fail (NULL != ifname, 0); + + pgm_assert (AF_INET6 != iffamily); + + DWORD dwRet, ifIndex; + ULONG ulOutBufLen = DEFAULT_BUFFER_SIZE; + PIP_ADAPTER_INFO pAdapterInfo = NULL; + PIP_ADAPTER_INFO pAdapter = NULL; + +/* loop to handle interfaces coming online causing a buffer overflow + * between first call to list buffer length and second call to enumerate. + */ + for (unsigned i = MAX_TRIES; i; i--) + { + pgm_debug ("IP_ADAPTER_INFO buffer length %lu bytes.", ulOutBufLen); + pAdapterInfo = (IP_ADAPTER_INFO*)_pgm_heap_alloc (ulOutBufLen); + dwRet = GetAdaptersInfo (pAdapterInfo, &ulOutBufLen); + if (ERROR_BUFFER_OVERFLOW == dwRet) { + _pgm_heap_free (pAdapterInfo); + pAdapterInfo = NULL; + } else { + break; + } + } + + switch (dwRet) { + case ERROR_SUCCESS: /* NO_ERROR */ + break; + case ERROR_BUFFER_OVERFLOW: + pgm_warn (_("GetAdaptersInfo repeatedly failed with ERROR_BUFFER_OVERFLOW.")); + if (pAdapterInfo) + _pgm_heap_free (pAdapterInfo); + return 0; + default: + pgm_warn (_("GetAdaptersInfo failed")); + if (pAdapterInfo) + _pgm_heap_free (pAdapterInfo); + return 0; + } + + for (pAdapter = pAdapterInfo; + pAdapter; + pAdapter = pAdapter->Next) + { + for (IP_ADDR_STRING *pIPAddr = &pAdapter->IpAddressList; + pIPAddr; + pIPAddr = pIPAddr->Next) + { +/* skip null adapters */ + if (strlen (pIPAddr->IpAddress.String) == 0) + continue; + + if (0 == strncmp (ifname, pAdapter->AdapterName, IF_NAMESIZE)) { + ifIndex = pAdapter->Index; + _pgm_heap_free (pAdapterInfo); + return ifIndex; + } + } + } + + if (pAdapterInfo) + _pgm_heap_free (pAdapterInfo); + return 0; +} + +/* Retrieve adapter index via name. + * Windows edition: First try GetAdapterIndex() then fallback to enumerating + * adapters via GetAdaptersAddresses(). + * + * On error returns zero, no errors are defined. + */ + +static +unsigned /* type matching if_nametoindex() */ +_pgm_getadaptersaddresses_nametoindex ( + const sa_family_t iffamily, + const char* ifname + ) +{ + pgm_return_val_if_fail (NULL != ifname, 0); + + ULONG ifIndex; + DWORD dwSize = DEFAULT_BUFFER_SIZE, dwRet; + IP_ADAPTER_ADDRESSES *pAdapterAddresses = NULL, *adapter; + +/* first see if GetAdapterIndex is working + */ + dwRet = GetAdapterIndex ((const LPWSTR)ifname, &ifIndex); + if (NO_ERROR == dwRet) + return ifIndex; + +/* fallback to finding index via iterating adapter list */ + +/* loop to handle interfaces coming online causing a buffer overflow + * between first call to list buffer length and second call to enumerate. + */ + for (unsigned i = MAX_TRIES; i; i--) + { + pAdapterAddresses = (IP_ADAPTER_ADDRESSES*)_pgm_heap_alloc (dwSize); + dwRet = GetAdaptersAddresses (AF_UNSPEC, + GAA_FLAG_SKIP_ANYCAST | + GAA_FLAG_SKIP_DNS_SERVER | + GAA_FLAG_SKIP_FRIENDLY_NAME | + GAA_FLAG_SKIP_MULTICAST, + NULL, + pAdapterAddresses, + &dwSize); + if (ERROR_BUFFER_OVERFLOW == dwRet) { + _pgm_heap_free (pAdapterAddresses); + pAdapterAddresses = NULL; + } else { + break; + } + } + + switch (dwRet) { + case ERROR_SUCCESS: + break; + case ERROR_BUFFER_OVERFLOW: + pgm_warn (_("GetAdaptersAddresses repeatedly failed with ERROR_BUFFER_OVERFLOW")); + if (pAdapterAddresses) + _pgm_heap_free (pAdapterAddresses); + return 0; + default: + pgm_warn (_("GetAdaptersAddresses failed")); + if (pAdapterAddresses) + _pgm_heap_free (pAdapterAddresses); + return 0; + } + + for (adapter = pAdapterAddresses; + adapter; + adapter = adapter->Next) + { + if (0 == strcmp (ifname, adapter->AdapterName)) { + ifIndex = AF_INET6 == iffamily ? adapter->Ipv6IfIndex : adapter->IfIndex; + _pgm_heap_free (pAdapterAddresses); + return ifIndex; + } + } + + if (pAdapterAddresses) + _pgm_heap_free (pAdapterAddresses); + return 0; +} +#endif /* _WIN32 */ + +/* Retrieve interface index for a specified adapter name. + * On error returns zero, no errors are defined. + */ + +unsigned /* type matching if_nametoindex() */ +pgm_if_nametoindex ( +#ifndef _WIN32 + PGM_GNUC_UNUSED const sa_family_t iffamily, +#else + const sa_family_t iffamily, +#endif + const char* ifname + ) +{ + pgm_return_val_if_fail (NULL != ifname, 0); + +#ifndef _WIN32 + return if_nametoindex (ifname); +#elif defined(CONFIG_TARGET_WINE) + return _pgm_getadaptersinfo_nametoindex (iffamily, ifname); +#else + return _pgm_getadaptersaddresses_nametoindex (iffamily, ifname); +#endif +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/net-snmp.txt b/3rdparty/openpgm-svn-r1085/pgm/net-snmp.txt new file mode 100644 index 0000000..549bcc2 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/net-snmp.txt @@ -0,0 +1,34 @@ +net-snmp is hard coded on mib location, only the mib file names can +be specified outside of snmptranslate. + +$ mkdir -p ~/.snmp/mibs +$ cp mibs/PGM-MIB-petrova-01.txt ~/.snmp/mibs + +Basic test: + +$ snmptranslate -m ALL -IR pgmMIB +PGM-MIB::pgmMIB + +Display full pretty tree: + +$ snmptranslate -m ALL -Tp -IR pgmMIB ++--pgmMIB(112) + | + +--pgm(1) + | | + | +--pgmNetworkElement(1) + | | | +... + + +Now the framework tool can be used: + +$ env MIBS="+ALL" mib2c pgmMIB + +... + +To run with SNMP install snmpd, enable "master agentx" and walk: + +$ env MIBS="+ALL" snmpwalk -v 1 -c public localhost pgmMIB +End of MIB + diff --git a/3rdparty/openpgm-svn-r1085/pgm/net.c b/3rdparty/openpgm-svn-r1085/pgm/net.c new file mode 100644 index 0000000..5b2f9e9 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/net.c @@ -0,0 +1,175 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * network send wrapper. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#ifdef CONFIG_HAVE_POLL +# include +#endif +#ifndef _WIN32 +# include +# include +# include +#endif +#include +#include +#include +#include + + +#define NET_DEBUG + + +#if !defined(ENETUNREACH) && defined(WSAENETUNREACH) +# define ENETUNREACH WSAENETUNREACH +#endif +#if !defined(EHOSTUNREACH) && defined(WSAEHOSTUNREACH) +# define EHOSTUNREACH WSAEHOSTUNREACH +#endif +#if !defined(ENOBUFS) && defined(WSAENOBUFS) +# define ENOBUFS WSAENOBUFS +#endif + + +/* locked and rate regulated sendto + * + * on success, returns number of bytes sent. on error, -1 is returned, and + * errno set appropriately. + */ + +ssize_t +pgm_sendto ( + pgm_sock_t* sock, + bool use_rate_limit, + bool use_router_alert, + const void* restrict buf, + size_t len, + const struct sockaddr* restrict to, + socklen_t tolen + ) +{ + pgm_assert( NULL != sock ); + pgm_assert( NULL != buf ); + pgm_assert( len > 0 ); + pgm_assert( NULL != to ); + pgm_assert( tolen > 0 ); + +#ifdef NET_DEBUG + char saddr[INET_ADDRSTRLEN]; + pgm_sockaddr_ntop (to, saddr, sizeof(saddr)); + pgm_debug ("pgm_sendto (sock:%p use_rate_limit:%s use_router_alert:%s buf:%p len:%zu to:%s [toport:%d] tolen:%d)", + (const void*)sock, + use_rate_limit ? "TRUE" : "FALSE", + use_router_alert ? "TRUE" : "FALSE", + (const void*)buf, + len, + saddr, + ntohs (((const struct sockaddr_in*)to)->sin_port), + (int)tolen); +#endif + + const int send_sock = use_router_alert ? sock->send_with_router_alert_sock : sock->send_sock; + + if (use_rate_limit && + !pgm_rate_check (&sock->rate_control, len, sock->is_nonblocking)) + { + errno = ENOBUFS; + return (const ssize_t)-1; + } + + if (!use_router_alert && sock->can_send_data) + pgm_mutex_lock (&sock->send_mutex); + + ssize_t sent = sendto (send_sock, buf, len, 0, to, (socklen_t)tolen); + pgm_debug ("sendto returned %zd", sent); + if (sent < 0) { + int save_errno = pgm_sock_errno(); + if (PGM_UNLIKELY(errno != ENETUNREACH && /* Network is unreachable */ + errno != EHOSTUNREACH && /* No route to host */ + errno != EAGAIN)) /* would block on non-blocking send */ + { +#ifdef CONFIG_HAVE_POLL +/* poll for cleared socket */ + struct pollfd p = { + .fd = send_sock, + .events = POLLOUT, + .revents = 0 + }; + const int ready = poll (&p, 1, 500 /* ms */); +#else + fd_set writefds; + FD_ZERO(&writefds); + FD_SET(send_sock, &writefds); + struct timeval tv = { + .tv_sec = 0, + .tv_usec = 500 /* ms */ * 1000 + }; + const int ready = select (1, NULL, &writefds, NULL, &tv); +#endif /* CONFIG_HAVE_POLL */ + if (ready > 0) + { + sent = sendto (send_sock, buf, len, 0, to, (socklen_t)tolen); + if ( sent < 0 ) + { + save_errno = pgm_sock_errno(); + pgm_warn (_("sendto() %s failed: %s"), + inet_ntoa( ((const struct sockaddr_in*)to)->sin_addr ), + pgm_sock_strerror (save_errno)); + } + } + else if (ready == 0) + { + pgm_warn (_("sendto() %s failed: socket timeout."), + inet_ntoa( ((const struct sockaddr_in*)to)->sin_addr )); + } + else + { + save_errno = pgm_sock_errno(); + pgm_warn (_("blocked socket failed: %s"), + pgm_sock_strerror (save_errno)); + } + } + } + + if (!use_router_alert && sock->can_send_data) + pgm_mutex_unlock (&sock->send_mutex); + return sent; +} + +/* socket helper, for setting pipe ends non-blocking + * + * on success, returns 0. on error, returns -1, and sets errno appropriately. + */ + +int +pgm_set_nonblocking ( + int fd[2] + ) +{ +/* pre-conditions */ + pgm_assert (fd[0]); + pgm_assert (fd[1]); + + pgm_sockaddr_nonblocking (fd[0], TRUE); + pgm_sockaddr_nonblocking (fd[1], TRUE); + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/net_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/net_unittest.c new file mode 100644 index 0000000..fc684ca --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/net_unittest.c @@ -0,0 +1,375 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for network send wrapper. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include + + +/* mock state */ + +#define pgm_rate_check mock_pgm_rate_check +#define sendto mock_sendto +#define poll mock_poll +#define select mock_select +#define fcntl mock_fcntl + +#define NET_DEBUG +#include "net.c" + + +static +pgm_sock_t* +generate_sock (void) +{ + pgm_sock_t* sock = g_malloc0 (sizeof(pgm_sock_t)); + return sock; +} + +static +char* +flags_string ( + int flags + ) +{ + static char s[1024]; + + s[0] = '\0'; + if (flags & MSG_OOB) + strcat (s, "MSG_OOB"); +#define MSG(flag) \ + do { \ + if (flags & flag) { \ + strcat (s, s[0] ? ("|" #flag) : (#flag)); \ + } \ + } while (0) +#ifdef MSG_PEEK + MSG(MSG_PEEK); +#endif +#ifdef MSG_DONTROUTE + MSG(MSG_DONTROUTE); +#endif +#ifdef MSG_CTRUNC + MSG(MSG_CTRUNC); +#endif +#ifdef MSG_PROXY + MSG(MSG_PROXY); +#endif +#ifdef MSG_TRUNC + MSG(MSG_TRUNC); +#endif +#ifdef MSG_DONTWAIT + MSG(MSG_DONTWAIT); +#endif +#ifdef MSG_EOR + MSG(MSG_EOR); +#endif +#ifdef MSG_WAITALL + MSG(MSG_WAITALL); +#endif +#ifdef MSG_FIN + MSG(MSG_FIN); +#endif +#ifdef MSG_SYN + MSG(MSG_SYN); +#endif +#ifdef MSG_CONFIRM + MSG(MSG_CONFIRM); +#endif +#ifdef MSG_RST + MSG(MSG_RST); +#endif +#ifdef MSG_ERRQUEUE + MSG(MSG_ERRQUEUE); +#endif +#ifdef MSG_NOSIGNAL + MSG(MSG_NOSIGNAL); +#endif +#ifdef MSG_MORE + MSG(MSG_MORE); +#endif +#ifdef MSG_CMSG_CLOEXEC + MSG(MSG_CMSG_CLOEXEC); +#endif + if (!s[0]) { + if (flags) + sprintf (s, "0x%x", flags); + else + strcpy (s, "0"); + } + return s; +} + + +/* mock functions for external references */ + +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return 0; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_rate_check ( + pgm_rate_t* bucket, + const size_t data_size, + const bool is_nonblocking + ) +{ + g_debug ("mock_pgm_rate_check (bucket:%p data-size:%zu is-nonblocking:%s)", + (gpointer)bucket, data_size, is_nonblocking ? "TRUE" : "FALSE"); + return TRUE; +} + +ssize_t +mock_sendto ( + int s, + const void* buf, + size_t len, + int flags, + const struct sockaddr* to, + socklen_t tolen + ) +{ + char saddr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop (to, saddr, sizeof(saddr)); + g_debug ("mock_sendto (s:%i buf:%p len:%d flags:%s to:%s tolen:%d)", + s, buf, len, flags_string (flags), saddr, tolen); + return len; +} + +#ifdef CONFIG_HAVE_POLL +int +mock_poll ( + struct pollfd* fds, + nfds_t nfds, + int timeout + ) +{ + g_debug ("mock_poll (fds:%p nfds:%d timeout:%d)", + (gpointer)fds, (int)nfds, timeout); + return 0; +} +#else +int +mock_select ( + int nfds, + fd_set* readfds, + fd_set* writefds, + fd_set* exceptfds, + struct timeval* timeout + ) +{ + g_debug ("mock_select (nfds:%d readfds:%p writefds:%p exceptfds:%p timeout:%p)", + nfds, (gpointer)readfds, (gpointer)writefds, (gpointer)exceptfds, (gpointer)timeout); + return 0; +} +#endif + +int +mock_fcntl ( + int fd, + int cmd, + ... + ) +{ + long arg; + va_list args; + if (F_GETFL == cmd) { + g_debug ("mock_fcntl (fd:%d cmd:F_GETFL)", fd); + return 0; + } + if (F_SETFL == cmd) { + va_start (args, cmd); + arg = va_arg (args, long); + va_end (args); + g_debug ("mock_fcntl (fd:%d cmd:F_SETFL arg:%ld)", fd, arg); + return arg; + } + g_assert_not_reached(); +} + + +/* target: + * ssize_t + * pgm_sendto ( + * pgm_sock_t* sock, + * bool use_rate_limit, + * bool use_router_alert, + * const void* buf, + * size_t len, + * const struct sockaddr* to, + * socklen_t tolen + * ) + */ + +START_TEST (test_sendto_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + const char* buf = "i am not a string"; + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr ("172.12.90.1") + }; + gssize len = pgm_sendto (sock, FALSE, FALSE, buf, sizeof(buf), (struct sockaddr*)&addr, sizeof(addr)); + fail_unless (sizeof(buf) == len, "sendto underrun"); +} +END_TEST + +START_TEST (test_sendto_fail_001) +{ + const char* buf = "i am not a string"; + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr ("172.12.90.1") + }; + gssize len = pgm_sendto (NULL, FALSE, FALSE, buf, sizeof(buf), (struct sockaddr*)&addr, sizeof(addr)); + fail ("reached"); +} +END_TEST + +START_TEST (test_sendto_fail_002) +{ + pgm_sock_t* sock = generate_sock (); + const char* buf = "i am not a string"; + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr ("172.12.90.1") + }; + gssize len = pgm_sendto (sock, FALSE, FALSE, NULL, sizeof(buf), (struct sockaddr*)&addr, sizeof(addr)); + fail ("reached"); +} +END_TEST + +START_TEST (test_sendto_fail_003) +{ + pgm_sock_t* sock = generate_sock (); + const char* buf = "i am not a string"; + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr ("172.12.90.1") + }; + gssize len = pgm_sendto (sock, FALSE, FALSE, buf, 0, (struct sockaddr*)&addr, sizeof(addr)); + fail ("reached"); +} +END_TEST + +START_TEST (test_sendto_fail_004) +{ + pgm_sock_t* sock = generate_sock (); + const char* buf = "i am not a string"; + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr ("172.12.90.1") + }; + gssize len = pgm_sendto (sock, FALSE, FALSE, buf, sizeof(buf), NULL, sizeof(addr)); + fail ("reached"); +} +END_TEST + +START_TEST (test_sendto_fail_005) +{ + pgm_sock_t* sock = generate_sock (); + const char* buf = "i am not a string"; + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr ("172.12.90.1") + }; + gssize len = pgm_sendto (sock, FALSE, FALSE, buf, sizeof(buf), (struct sockaddr*)&addr, 0); + fail ("reached"); +} +END_TEST + +/* target: + * int + * pgm_set_nonblocking ( + * int filedes[2] + * ) + */ + +START_TEST (test_set_nonblocking_pass_001) +{ + int filedes[2] = { fileno (stdout), fileno (stderr) }; + int retval = pgm_set_nonblocking (filedes); +} +END_TEST + +START_TEST (test_set_nonblocking_fail_001) +{ + int filedes[2] = { 0, 0 }; + int retval = pgm_set_nonblocking (filedes); + fail ("reached"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_sendto = tcase_create ("sendto"); + suite_add_tcase (s, tc_sendto); + tcase_add_test (tc_sendto, test_sendto_pass_001); + tcase_add_test_raise_signal (tc_sendto, test_sendto_fail_001, SIGABRT); + tcase_add_test_raise_signal (tc_sendto, test_sendto_fail_002, SIGABRT); + tcase_add_test_raise_signal (tc_sendto, test_sendto_fail_003, SIGABRT); + tcase_add_test_raise_signal (tc_sendto, test_sendto_fail_004, SIGABRT); + tcase_add_test_raise_signal (tc_sendto, test_sendto_fail_005, SIGABRT); + + TCase* tc_set_nonblocking = tcase_create ("set-nonblocking"); + suite_add_tcase (s, tc_set_nonblocking); + tcase_add_test (tc_set_nonblocking, test_set_nonblocking_pass_001); + tcase_add_test_raise_signal (tc_set_nonblocking, test_set_nonblocking_fail_001, SIGABRT); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/options.txt b/3rdparty/openpgm-svn-r1085/pgm/options.txt new file mode 100644 index 0000000..cb01da0 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/options.txt @@ -0,0 +1,158 @@ + OPT_LENGTH 0x00 - Option's Length + +pgm_opt_header +pgm_opt_length + +first option, always present. + +-------------------------------------------------------------------------------- + + OPT_FRAGMENT 0x01 - Fragmentation + +pgm_opt_header +pgm_opt_fragment + +may be present for odata, rdata. 'MAY' exist for others, although a bit strange. + +-------------------------------------------------------------------------------- + + OPT_NAK_LIST 0x02 - List of NAK entries + +pgm_opt_header +pgm_opt_nak_list + +may be present for naks. + +-------------------------------------------------------------------------------- + + OPT_JOIN 0x03 - Late Joining + +pgm_opt_header +pgm_opt_join + +may be present for odata, rdata, spm. + +requires SPM to learn NLA already so not overly useful with odata/rdata, could be +used with video streaming to last i-frame data sequence number. + +-------------------------------------------------------------------------------- + + OPT_REDIRECT 0x07 - Redirect + +pgm_opt_header +pgm_opt_redirect +pgm_opt_redirect6 + +should be present for polrs from a dlr. + +-------------------------------------------------------------------------------- + + OPT_SYN 0x0D - Synchronization + +pgm_opt_header +pgm_opt_syn + +must only appear with odata or rdata. + +-------------------------------------------------------------------------------- + + OPT_FIN 0x0E - Session Fin receivers, conventional + feedbackish + +pgm_opt_header +opt_opt_fin + +may be present for odata, rdata, must appear in following spms. + +-------------------------------------------------------------------------------- + + OPT_RST 0x0F - Session Reset + +pgm_opt_header +pgm_opt_rst + +must only appear in spms. not many 'unrecoverable error conditions' exist though. + +-------------------------------------------------------------------------------- + + OPT_PARITY + +must appear in odata or rdata to indicate pro-active or on-demand parity data, +nak to request parity repair data, ncf to confirm parity nak. + + + OPT_VAR_PKTLEN + +may be present in odata or data to indicate variable size packets. + + + OPT_PARITY_PRM 0x08 - Forward Error Correction Parameters + +pgm_opt_header +pgm_opt_parity_prm + +appended to spms to inform of pro-active or on-demand parity. + +-------------------------------------------------------------------------------- + + OPT_PARITY_GRP 0x09 - Forward Error Correction Group Number + +pgm_opt_parity_grp + +appended to odata and rdata parity packets. + +-------------------------------------------------------------------------------- + + OPT_CURR_TGSIZE 0x0A - Forward Error Correction Group Size + +pgm_opt_curr_tgsize + +must appear in last odata or rdata packet of variable transmission group, may +appear in spms. + +-------------------------------------------------------------------------------- + + OPT_CR 0x10 - Congestion Report + +pgm_opt_header +pgm_opt_cr + +-------------------------------------------------------------------------------- + + OPT_CRQST 0x11 - Congestion Report Request + +pgm_opt_header +pgm_opt_crqst + +-------------------------------------------------------------------------------- + + OPT_NAK_BO_IVL 0x04 - NAK Back-Off Interval + +pgm_opt_header +pgm_opt_nak_bo_ivl + +-------------------------------------------------------------------------------- + + OPT_NAK_BO_RNG 0x05 - NAK Back-Off Range + +pgm_opt_header +pgm_opt_nak_bo_rng + +-------------------------------------------------------------------------------- + + OPT_NBR_UNREACH 0x0B - Neighbor Unreachable + +pgm_opt_header +pgm_opt_nbr_unreach + +-------------------------------------------------------------------------------- + + OPT_PATH_NLA 0x0C - Path NLA + +pgm_opt_header +pgm_opt_path_nla +pgm_opt6_path_nla + +-------------------------------------------------------------------------------- + + OPT_INVALID 0x7F - Option invalidated diff --git a/3rdparty/openpgm-svn-r1085/pgm/packet_parse.c b/3rdparty/openpgm-svn-r1085/pgm/packet_parse.c new file mode 100644 index 0000000..0fad8ca --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/packet_parse.c @@ -0,0 +1,615 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM packet formats, RFC 3208. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include + + +//#define PACKET_DEBUG + +#ifndef PACKET_DEBUG +# define PGM_DISABLE_ASSERT +#endif + + +/* locals */ + +static bool pgm_parse (struct pgm_sk_buff_t*const restrict, pgm_error_t**restrict); + + +/* Parse a raw-IP packet for IP and PGM header and any payload. + */ + +#define PGM_MIN_SIZE ( \ + sizeof(struct pgm_ip) + /* IPv4 header */ \ + sizeof(struct pgm_header) /* PGM header */ \ + ) + +bool +pgm_parse_raw ( + struct pgm_sk_buff_t* const restrict skb, /* data will be modified */ + struct sockaddr* const restrict dst, + pgm_error_t** restrict error + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + pgm_assert (NULL != dst); + + pgm_debug ("pgm_parse_raw (skb:%p dst:%p error:%p)", + (const void*)skb, (const void*)dst, (const void*)error); + +/* minimum size should be IPv4 header plus PGM header, check IP version later */ + if (PGM_UNLIKELY(skb->len < PGM_MIN_SIZE)) + { + pgm_set_error (error, + PGM_ERROR_DOMAIN_PACKET, + PGM_ERROR_BOUNDS, + _("IP packet too small at %" PRIu16 " bytes, expecting at least %" PRIu16 " bytes."), + skb->len, (uint16_t)PGM_MIN_SIZE); + return FALSE; + } + +/* IP packet header: IPv4 + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |Version| HL | ToS | Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Fragment ID |R|D|M| Fragment Offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | Protocol | Checksum | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Source IP Address | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Destination IP Address | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | IP Options when present ... + * +-+-+-+-+-+-+-+-+-+-+-+-+ ... + * | Data ... + * +-+-+- ... + * + * IPv6 + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |Version| Traffic Class | Flow Label | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Payload Length | Next Header | Hop Limit | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * | Source IP Address | + * | | + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * | Destination IP Address | + * | | + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | IP Options when present ... + * +-+-+-+-+-+-+-+-+-+-+-+-+ ... + * | Data ... + * +-+-+- ... + * + */ + +/* decode IP header */ + const struct pgm_ip* ip = (struct pgm_ip*)skb->data; + switch (ip->ip_v) { + case 4: { + struct sockaddr_in* sin = (struct sockaddr_in*)dst; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = ip->ip_dst.s_addr; + break; + } + + case 6: + pgm_set_error (error, + PGM_ERROR_DOMAIN_PACKET, + PGM_ERROR_AFNOSUPPORT, + _("IPv6 is not supported for raw IP header parsing.")); + return FALSE; + + default: + pgm_set_error (error, + PGM_ERROR_DOMAIN_PACKET, + PGM_ERROR_AFNOSUPPORT, + _("IP header reports an invalid version %d."), + ip->ip_v); + return FALSE; + } + + const size_t ip_header_length = ip->ip_hl * 4; /* IP header length in 32bit octets */ + if (PGM_UNLIKELY(ip_header_length < sizeof(struct pgm_ip))) + { + pgm_set_error (error, + PGM_ERROR_DOMAIN_PACKET, + PGM_ERROR_BOUNDS, + _("IP header reports an invalid header length %zu bytes."), + ip_header_length); + return FALSE; + } + +#ifndef CONFIG_HOST_ORDER_IP_LEN + size_t packet_length = ntohs (ip->ip_len); /* total packet length */ +#else + size_t packet_length = ip->ip_len; /* total packet length */ +#endif + + +/* ip_len can equal packet_length - ip_header_length in FreeBSD/NetBSD + * Stevens/Fenner/Rudolph, Unix Network Programming Vol.1, p.739 + * + * RFC3828 allows partial packets such that len < packet_length with UDP lite + */ + if (skb->len == packet_length + ip_header_length) { + packet_length += ip_header_length; + } + + if (PGM_UNLIKELY(skb->len < packet_length)) { /* redundant: often handled in kernel */ + pgm_set_error (error, + PGM_ERROR_DOMAIN_PACKET, + PGM_ERROR_BOUNDS, + _("IP packet received at %" PRIu16 " bytes whilst IP header reports %zu bytes."), + skb->len, packet_length); + return FALSE; + } + +/* packets that fail checksum will generally not be passed upstream except with rfc3828 + */ +#if PGM_CHECK_IN_CKSUM + const uint16_t sum = in_cksum (data, packet_length, 0); + if (PGM_UNLIKELY(0 != sum)) { + const uint16_t ip_sum = ntohs (ip->ip_sum); + pgm_set_error (error, + PGM_ERROR_DOMAIN_PACKET, + PGM_ERROR_CKSUM, + _("IP packet checksum mismatch, reported 0x%x whilst calculated 0x%x."), + ip_sum, sum); + return FALSE; + } +#endif + +/* fragmentation offset, bit 0: 0, bit 1: do-not-fragment, bit 2: more-fragments */ +#ifndef CONFIG_HOST_ORDER_IP_OFF + const uint16_t offset = ntohs (ip->ip_off); +#else + const uint16_t offset = ip->ip_off; +#endif + if (PGM_UNLIKELY((offset & 0x1fff) != 0)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_PACKET, + PGM_ERROR_PROTO, + _("IP header reports packet fragmentation, offset %u."), + offset & 0x1fff); + return FALSE; + } + +/* PGM payload, header looks as follows: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Source Port | Destination Port | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Options | Checksum | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Global Source ID ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ... Global Source ID | TSDU Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type specific data ... + * +-+-+-+-+-+-+-+-+-+- ... + */ + + skb->pgm_header = (void*)( (char*)skb->data + ip_header_length ); + +/* advance DATA pointer to PGM packet */ + skb->data = skb->pgm_header; + skb->len -= ip_header_length; + return pgm_parse (skb, error); +} + +bool +pgm_parse_udp_encap ( + struct pgm_sk_buff_t* restrict skb, /* will be modified */ + pgm_error_t** restrict error + ) +{ + pgm_assert (NULL != skb); + + if (PGM_UNLIKELY(skb->len < sizeof(struct pgm_header))) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_PACKET, + PGM_ERROR_BOUNDS, + _("UDP payload too small for PGM packet at %" PRIu16 " bytes, expecting at least %zu bytes."), + skb->len, sizeof(struct pgm_header)); + return FALSE; + } + +/* DATA payload is PGM packet, no headers */ + skb->pgm_header = skb->data; + return pgm_parse (skb, error); +} + +/* will modify packet contents to calculate and check PGM checksum + */ +static +bool +pgm_parse ( + struct pgm_sk_buff_t* const restrict skb, /* will be modified to calculate checksum */ + pgm_error_t** restrict error + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + +/* pgm_checksum == 0 means no transmitted checksum */ + if (skb->pgm_header->pgm_checksum) + { + const uint16_t sum = skb->pgm_header->pgm_checksum; + skb->pgm_header->pgm_checksum = 0; + const uint16_t pgm_sum = pgm_csum_fold (pgm_csum_partial ((const char*)skb->pgm_header, skb->len, 0)); + skb->pgm_header->pgm_checksum = sum; + if (PGM_UNLIKELY(pgm_sum != sum)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_PACKET, + PGM_ERROR_CKSUM, + _("PGM packet checksum mismatch, reported 0x%x whilst calculated 0x%x."), + pgm_sum, sum); + return FALSE; + } + } else { + if (PGM_ODATA == skb->pgm_header->pgm_type || + PGM_RDATA == skb->pgm_header->pgm_type) + { + pgm_set_error (error, + PGM_ERROR_DOMAIN_PACKET, + PGM_ERROR_PROTO, + _("PGM checksum missing whilst mandatory for %cDATA packets."), + PGM_ODATA == skb->pgm_header->pgm_type ? 'O' : 'R'); + return FALSE; + } + pgm_debug ("No PGM checksum :O"); + } + +/* copy packets source transport identifier */ + memcpy (&skb->tsi.gsi, skb->pgm_header->pgm_gsi, sizeof(pgm_gsi_t)); + skb->tsi.sport = skb->pgm_header->pgm_sport; + return TRUE; +} + +/* 8.1. Source Path Messages (SPM) + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SPM's Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Trailing Edge Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Leading Edge Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Path NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * NLA = Network Layer Address + * NLA AFI = NLA Address Family Indicator: rfc 1700 (ADDRESS FAMILY NUMBERS) + * => Path NLA = IP address of last network element + */ + +#define PGM_MIN_SPM_SIZE ( sizeof(struct pgm_spm) ) + +bool +pgm_verify_spm ( + const struct pgm_sk_buff_t* const skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + + const struct pgm_spm* spm = (const struct pgm_spm*)skb->data; + switch (ntohs (spm->spm_nla_afi)) { +/* truncated packet */ + case AFI_IP6: + if (PGM_UNLIKELY(skb->len < sizeof(struct pgm_spm6))) + return FALSE; + break; + case AFI_IP: + if (PGM_UNLIKELY(skb->len < sizeof(struct pgm_spm))) + return FALSE; + break; + + default: + return FALSE; + } + + return TRUE; +} + +/* 14.7.1. Poll Request + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLL's Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLL's Round | POLL's Sub-type | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Path NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | POLL's Back-off Interval | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Random String | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Matching Bit-Mask | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Sent to ODATA multicast group with IP Router Alert option. + */ + +#define PGM_MIN_POLL_SIZE ( sizeof(struct pgm_poll) ) + +bool +pgm_verify_poll ( + const struct pgm_sk_buff_t* const skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + + const struct pgm_poll* poll4 = (const struct pgm_poll*)skb->data; + switch (ntohs (poll4->poll_nla_afi)) { +/* truncated packet */ + case AFI_IP6: + if (PGM_UNLIKELY(skb->len < sizeof(struct pgm_poll6))) + return FALSE; + break; + case AFI_IP: + if (PGM_UNLIKELY(skb->len < sizeof(struct pgm_poll))) + return FALSE; + break; + + default: + return FALSE; + } + + return TRUE; +} + +/* 14.7.2. Poll Response + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLR's Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLR's Round | reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +bool +pgm_verify_polr ( + const struct pgm_sk_buff_t* const skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + +/* truncated packet */ + if (PGM_UNLIKELY(skb->len < sizeof(struct pgm_polr))) + return FALSE; + return TRUE; +} + +/* 8.2. Data Packet + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Data Packet Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Trailing Edge Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Data ... + * +-+-+- ... + */ + +/* no verification api */ + +/* 8.3. NAK + * + * Technically the AFI of the source and multicast group can be different + * but that would be very wibbly wobbly. One example is using a local DLR + * with a IPv4 address to reduce NAK cost for recovery on wide IPv6 + * distribution. + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Requested Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Source NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Multicast Group NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | Option Extensions when present ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... + */ + +#define PGM_MIN_NAK_SIZE ( sizeof(struct pgm_nak) ) + +bool +pgm_verify_nak ( + const struct pgm_sk_buff_t* const skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + + pgm_debug ("pgm_verify_nak (skb:%p)", (const void*)skb); + +/* truncated packet */ + if (PGM_UNLIKELY(skb->len < PGM_MIN_NAK_SIZE)) + return FALSE; + + const struct pgm_nak* nak = (struct pgm_nak*)skb->data; + const uint16_t nak_src_nla_afi = ntohs (nak->nak_src_nla_afi); + uint16_t nak_grp_nla_afi = 0; + +/* check source NLA: unicast address of the ODATA sender */ + switch (nak_src_nla_afi) { + case AFI_IP: + nak_grp_nla_afi = ntohs (nak->nak_grp_nla_afi); + break; + + case AFI_IP6: + nak_grp_nla_afi = ntohs (((const struct pgm_nak6*)nak)->nak6_grp_nla_afi); + break; + + default: + return FALSE; + } + +/* check multicast group NLA */ + switch (nak_grp_nla_afi) { + case AFI_IP6: + switch (nak_src_nla_afi) { +/* IPv4 + IPv6 NLA */ + case AFI_IP: + if (PGM_UNLIKELY(skb->len < ( sizeof(struct pgm_nak) + sizeof(struct in6_addr) - sizeof(struct in_addr) ))) + return FALSE; + break; + +/* IPv6 + IPv6 NLA */ + case AFI_IP6: + if (PGM_UNLIKELY(skb->len < sizeof(struct pgm_nak6))) + return FALSE; + break; + } + + case AFI_IP: + break; + + default: + return FALSE; + } + + return TRUE; +} + +/* 8.3. N-NAK + */ + +bool +pgm_verify_nnak ( + const struct pgm_sk_buff_t* const skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + + return pgm_verify_nak (skb); +} + +/* 8.3. NCF + */ + +bool +pgm_verify_ncf ( + const struct pgm_sk_buff_t* const skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + + return pgm_verify_nak (skb); +} + +/* 13.6. SPM Request + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... + */ + +bool +pgm_verify_spmr ( + PGM_GNUC_UNUSED const struct pgm_sk_buff_t* skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + + return TRUE; +} + +/* PGMCC: ACK + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RX_MAX | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Received Packet Bitmap | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... + */ + +#define PGM_MIN_ACK_SIZE ( sizeof(struct pgm_ack) ) + +bool +pgm_verify_ack ( + PGM_GNUC_UNUSED const struct pgm_sk_buff_t* skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + + return TRUE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/packet_parse_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/packet_parse_unittest.c new file mode 100644 index 0000000..94b06d2 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/packet_parse_unittest.c @@ -0,0 +1,382 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for PGM packet handling. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* mock state */ + +#define PACKET_DEBUG +#include "packet_parse.c" + + +static +struct pgm_sk_buff_t* +generate_raw_pgm (void) +{ + const char source[] = "i am not a string"; + const guint source_len = sizeof(source); + struct pgm_sk_buff_t* skb; + GError* err = NULL; + + skb = pgm_alloc_skb (1500); + skb->sock = (pgm_sock_t*)0x1; + skb->tstamp = 0x1; + skb->data = skb->head; + skb->len = sizeof(struct pgm_ip) + sizeof(struct pgm_header) + sizeof(struct pgm_data) + source_len; + skb->tail = (guint8*)skb->data + skb->len; + +/* add IP header */ + struct pgm_ip* iphdr = skb->data; + iphdr->ip_hl = sizeof(struct pgm_ip) / 4; + iphdr->ip_v = 4; + iphdr->ip_tos = 0; + iphdr->ip_len = g_htons (skb->len); + iphdr->ip_id = 0; + iphdr->ip_off = 0; + iphdr->ip_ttl = 16; + iphdr->ip_p = IPPROTO_PGM; + iphdr->ip_sum = 0; + iphdr->ip_src.s_addr = inet_addr ("127.0.0.1"); + iphdr->ip_dst.s_addr = inet_addr ("127.0.0.2"); + +/* add PGM header */ + struct pgm_header* pgmhdr = (gpointer)(iphdr + 1); + pgmhdr->pgm_sport = g_htons ((guint16)1000); + pgmhdr->pgm_dport = g_htons ((guint16)7500); + pgmhdr->pgm_type = PGM_ODATA; + pgmhdr->pgm_options = 0; + pgmhdr->pgm_gsi[0] = 1; + pgmhdr->pgm_gsi[1] = 2; + pgmhdr->pgm_gsi[2] = 3; + pgmhdr->pgm_gsi[3] = 4; + pgmhdr->pgm_gsi[4] = 5; + pgmhdr->pgm_gsi[5] = 6; + pgmhdr->pgm_tsdu_length = g_htons (source_len); + +/* add ODATA header */ + struct pgm_data* datahdr = (gpointer)(pgmhdr + 1); + datahdr->data_sqn = g_htonl ((guint32)0); + datahdr->data_trail = g_htonl ((guint32)-1); + +/* add payload */ + gpointer data = (gpointer)(datahdr + 1); + memcpy (data, source, source_len); + +/* finally PGM checksum */ + pgmhdr->pgm_checksum = 0; + pgmhdr->pgm_checksum = pgm_csum_fold (pgm_csum_partial (pgmhdr, sizeof(struct pgm_header) + sizeof(struct pgm_data) + source_len, 0)); + +/* and IP checksum */ + iphdr->ip_sum = pgm_inet_checksum (skb->head, skb->len, 0); + + return skb; +} + +static +struct pgm_sk_buff_t* +generate_udp_encap_pgm (void) +{ + const char source[] = "i am not a string"; + const guint source_len = sizeof(source); + struct pgm_sk_buff_t* skb; + GError* err = NULL; + + skb = pgm_alloc_skb (1500); + skb->sock = (pgm_sock_t*)0x1; + skb->tstamp = 0x1; + skb->data = skb->head; + skb->len = sizeof(struct pgm_header) + sizeof(struct pgm_data) + source_len; + skb->tail = (guint8*)skb->data + skb->len; + +/* add PGM header */ + struct pgm_header* pgmhdr = skb->head; + pgmhdr->pgm_sport = g_htons ((guint16)1000); + pgmhdr->pgm_dport = g_htons ((guint16)7500); + pgmhdr->pgm_type = PGM_ODATA; + pgmhdr->pgm_options = 0; + pgmhdr->pgm_gsi[0] = 1; + pgmhdr->pgm_gsi[1] = 2; + pgmhdr->pgm_gsi[2] = 3; + pgmhdr->pgm_gsi[3] = 4; + pgmhdr->pgm_gsi[4] = 5; + pgmhdr->pgm_gsi[5] = 6; + pgmhdr->pgm_tsdu_length = g_htons (source_len); + +/* add ODATA header */ + struct pgm_data* datahdr = (gpointer)(pgmhdr + 1); + datahdr->data_sqn = g_htonl ((guint32)0); + datahdr->data_trail = g_htonl ((guint32)-1); + +/* add payload */ + gpointer data = (gpointer)(datahdr + 1); + memcpy (data, source, source_len); + +/* finally PGM checksum */ + pgmhdr->pgm_checksum = 0; + pgmhdr->pgm_checksum = pgm_csum_fold (pgm_csum_partial (pgmhdr, sizeof(struct pgm_header) + sizeof(struct pgm_data) + source_len, 0)); + + return skb; +} + +/* mock functions for external references */ + +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return 0; +} + + +/* target: + * bool + * pgm_parse_raw ( + * struct pgm_sk_buff_t* const skb, + * struct sockaddr* const addr, + * pgm_error_t** error + * ) + */ + +START_TEST (test_parse_raw_pass_001) +{ + struct sockaddr_storage addr; + pgm_error_t* err = NULL; + struct pgm_sk_buff_t* skb = generate_raw_pgm (); + gboolean success = pgm_parse_raw (skb, (struct sockaddr*)&addr, &err); + if (!success && err) { + g_error ("Parsing raw packet: %s", err->message); + } + fail_unless (TRUE == success, "parse_raw failed"); + char saddr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&addr, saddr, sizeof(saddr)); + g_message ("Decoded destination NLA: %s", saddr); +} +END_TEST + +START_TEST (test_parse_raw_fail_001) +{ + struct sockaddr_storage addr; + pgm_error_t* err = NULL; + pgm_parse_raw (NULL, (struct sockaddr*)&addr, &err); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_parse_udp_encap ( + * struct pgm_sk_buff_t* const skb, + * pgm_error_t** error + * ) + */ + +START_TEST (test_parse_udp_encap_pass_001) +{ + pgm_error_t* err = NULL; + struct pgm_sk_buff_t* skb = generate_udp_encap_pgm (); + gboolean success = pgm_parse_udp_encap (skb, &err); + if (!success && err) { + g_error ("Parsing UDP encapsulated packet: %s", err->message); + } + fail_unless (TRUE == success, "parse_udp_encap failed"); +} +END_TEST + +START_TEST (test_parse_udp_encap_fail_001) +{ + pgm_error_t* err = NULL; + pgm_parse_udp_encap (NULL, &err); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_verify_spm ( + * struct pgm_sk_buff_t* const skb + * ) + */ + +START_TEST (test_verify_spm_pass_001) +{ +} +END_TEST + +START_TEST (test_verify_spm_fail_001) +{ + pgm_verify_spm (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_verify_spmr ( + * struct pgm_sk_buff_t* const skb + * ) + */ + +START_TEST (test_verify_spmr_pass_001) +{ +} +END_TEST + +START_TEST (test_verify_spmr_fail_001) +{ + pgm_verify_spmr (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_verify_nak ( + * struct pgm_sk_buff_t* const skb + * ) + */ + +START_TEST (test_verify_nak_pass_001) +{ +} +END_TEST + +START_TEST (test_verify_nak_fail_001) +{ + pgm_verify_nak (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_verify_nnak ( + * struct pgm_sk_buff_t* const skb + * ) + */ + +START_TEST (test_verify_nnak_pass_001) +{ +} +END_TEST + +START_TEST (test_verify_nnak_fail_001) +{ + pgm_verify_nnak (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_verify_ncf ( + * struct pgm_sk_buff_t* const skb + * ) + */ + +START_TEST (test_verify_ncf_pass_001) +{ +} +END_TEST + +START_TEST (test_verify_ncf_fail_001) +{ + pgm_verify_ncf (NULL); + fail ("reached"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_parse_raw = tcase_create ("parse-raw"); + suite_add_tcase (s, tc_parse_raw); + tcase_add_test (tc_parse_raw, test_parse_raw_pass_001); + tcase_add_test_raise_signal (tc_parse_raw, test_parse_raw_fail_001, SIGABRT); + + TCase* tc_parse_udp_encap = tcase_create ("parse-udp-encap"); + suite_add_tcase (s, tc_parse_udp_encap); + tcase_add_test (tc_parse_udp_encap, test_parse_udp_encap_pass_001); + tcase_add_test_raise_signal (tc_parse_udp_encap, test_parse_udp_encap_fail_001, SIGABRT); + + TCase* tc_verify_spm = tcase_create ("verify-spm"); + suite_add_tcase (s, tc_verify_spm); + tcase_add_test (tc_verify_spm, test_verify_spm_pass_001); + tcase_add_test_raise_signal (tc_verify_spm, test_verify_spm_fail_001, SIGABRT); + + TCase* tc_verify_spmr = tcase_create ("verify-spmr"); + suite_add_tcase (s, tc_verify_spmr); + tcase_add_test (tc_verify_spmr, test_verify_spmr_pass_001); + tcase_add_test_raise_signal (tc_verify_spmr, test_verify_spmr_fail_001, SIGABRT); + + TCase* tc_verify_nak = tcase_create ("verify-nak"); + suite_add_tcase (s, tc_verify_nak); + tcase_add_test (tc_verify_nak, test_verify_nak_pass_001); + tcase_add_test_raise_signal (tc_verify_nak, test_verify_nak_fail_001, SIGABRT); + + TCase* tc_verify_nnak = tcase_create ("verify-nnak"); + suite_add_tcase (s, tc_verify_nnak); + tcase_add_test (tc_verify_nnak, test_verify_nnak_pass_001); + tcase_add_test_raise_signal (tc_verify_nnak, test_verify_nnak_fail_001, SIGABRT); + + TCase* tc_verify_ncf = tcase_create ("verify-ncf"); + suite_add_tcase (s, tc_verify_ncf); + tcase_add_test (tc_verify_ncf, test_verify_ncf_pass_001); + tcase_add_test_raise_signal (tc_verify_ncf, test_verify_ncf_fail_001, SIGABRT); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/packet_test.c b/3rdparty/openpgm-svn-r1085/pgm/packet_test.c new file mode 100644 index 0000000..6ac469f --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/packet_test.c @@ -0,0 +1,1158 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM packet formats, RFC 3208. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __STDC_FORMAT_MACROS +#include +#include +#include +#ifndef _WIN32 +# include +# include +# include +# include +#endif +#include +#include +#include + + +//#define PACKET_DEBUG + + +static bool pgm_print_spm (const struct pgm_header* const, const void*, const size_t); +static bool pgm_print_poll (const struct pgm_header* const, const void*, const size_t); +static bool pgm_print_polr (const struct pgm_header* const, const void*, const size_t); +static bool pgm_print_odata (const struct pgm_header* const, const void*, const size_t); +static bool pgm_print_rdata (const struct pgm_header* const, const void*, const size_t); +static bool pgm_print_nak (const struct pgm_header* const, const void*, const size_t); +static bool pgm_print_nnak (const struct pgm_header* const, const void*, const size_t); +static bool pgm_print_ncf (const struct pgm_header* const, const void*, const size_t); +static bool pgm_print_spmr (const struct pgm_header* const, const void*, const size_t); +static bool pgm_print_ack (const struct pgm_header* const, const void*, const size_t); +static ssize_t pgm_print_options (const void*, size_t); + +bool +pgm_print_packet ( + const void* data, + size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != data); + pgm_assert (len > 0); + +/* minimum size should be IP header plus PGM header */ + if (len < (sizeof(struct pgm_ip) + sizeof(struct pgm_header))) + { + printf ("Packet size too small: %zu bytes, expecting at least %zu bytes.\n", + len, sizeof(struct pgm_ip) + sizeof(struct pgm_header)); + return FALSE; + } + +/* decode IP header */ + const struct pgm_ip* ip = (const struct pgm_ip*)data; + if (ip->ip_v != 4) /* IP version, 4 or 6 */ + { + puts ("not IP4 packet :/"); /* v6 not currently handled */ + return FALSE; + } + printf ("IP "); + + const size_t ip_header_length = ip->ip_hl * 4; /* IP header length in 32bit octets */ + if (ip_header_length < sizeof(struct pgm_ip)) + { + puts ("bad IP header length :("); + return FALSE; + } + + size_t packet_length = ntohs(ip->ip_len); /* total packet length */ + +/* ip_len can equal packet_length - ip_header_length in FreeBSD/NetBSD + * Stevens/Fenner/Rudolph, Unix Network Programming Vol.1, p.739 + * + * RFC3828 allows partial packets such that len < packet_length with UDP lite + */ + if (len == packet_length + ip_header_length) { + packet_length += ip_header_length; + } + + if (len < packet_length) { /* redundant: often handled in kernel */ + puts ("truncated IP packet"); + return FALSE; + } + +/* TCP Segmentation Offload (TSO) might have zero length here */ + if (packet_length < ip_header_length) { + puts ("bad length :("); + return FALSE; + } + + const uint16_t offset = ntohs(ip->ip_off); + +/* 3 bits routing priority, 4 bits type of service: delay, throughput, reliability, cost */ + printf ("(tos 0x%x", (int)ip->ip_tos); + switch (ip->ip_tos & 0x3) + { + case 1: printf (",ECT(1)"); break; + case 2: printf (",ECT(0)"); break; + case 3: printf (",CE"); break; + default: break; + } + +/* time to live */ + if (ip->ip_ttl >= 1) printf (", ttl %u", ip->ip_ttl); + +/* fragmentation */ +#define IP_RDF 0x8000 +#define IP_DF 0x4000 +#define IP_MF 0x2000 +#define IP_OFFMASK 0x1fff + + printf (", id %u, offset %u, flags [%s%s]", + ntohs(ip->ip_id), + (offset & 0x1fff) * 8, + ((offset & IP_DF) ? "DF" : ""), + ((offset & IP_MF) ? "+" : "")); + printf (", length %zu", packet_length); + +/* IP options */ + if ((ip_header_length - sizeof(struct pgm_ip)) > 0) { + printf (", options ("); + pgm_ipopt_print((const void*)(ip + 1), ip_header_length - sizeof(struct pgm_ip)); + printf (" )"); + } + +/* packets that fail checksum will generally not be passed upstream except with rfc3828 + */ + const uint16_t ip_sum = pgm_inet_checksum(data, packet_length, 0); + if (ip_sum != 0) { + const uint16_t encoded_ip_sum = ntohs(ip->ip_sum); + printf (", bad cksum! %i", encoded_ip_sum); + } + + printf (") "); + +/* fragmentation offset, bit 0: 0, bit 1: do-not-fragment, bit 2: more-fragments */ + if ((offset & 0x1fff) != 0) { + puts ("fragmented packet :/"); + return FALSE; + } + +/* PGM payload, header looks as follows: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Source Port | Destination Port | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Options | Checksum | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Global Source ID ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ... Global Source ID | TSDU Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type specific data ... + * +-+-+-+-+-+-+-+-+-+- ... + */ + const struct pgm_header* pgm_header = (const struct pgm_header*)((const char*)data + ip_header_length); + const size_t pgm_length = packet_length - ip_header_length; + + if (pgm_length < sizeof(pgm_header)) { + puts ("bad packet size :("); + return FALSE; + } + + printf ("%s.%s > ", + pgm_gethostbyaddr((const struct in_addr*)&ip->ip_src), pgm_udpport_string(pgm_header->pgm_sport)); + printf ("%s.%s: PGM\n", + pgm_gethostbyaddr((const struct in_addr*)&ip->ip_dst), pgm_udpport_string(pgm_header->pgm_dport)); + + printf ("type: %s [%i] (version=%i, reserved=%i)\n" + "options: extensions=%s, network-significant=%s, parity packet=%s (variable size=%s)\n" + "global source id: %i.%i.%i.%i.%i.%i\n" + "tsdu length: %i\n", + + /* packet type */ /* packet version */ /* reserved = 0x0 */ + pgm_type_string(pgm_header->pgm_type & 0xf), + (pgm_header->pgm_type & 0xf), ((pgm_header->pgm_type & 0xc0) >> 6), ((pgm_header->pgm_type & 0x30) >> 4), + +/* bit 0 set => one or more option extensions are present */ + ((pgm_header->pgm_options & (0x1 << 7)) ? "true" : "false"), +/* bit 1 set => one or more options are network-significant */ + ((pgm_header->pgm_options & (0x1 << 6)) ? "true" : "false"), +/* bit 7 set => parity packet (OPT_PARITY) */ + ((pgm_header->pgm_options & (0x1 << 0)) ? "true" : "false"), +/* bit 6 set => parity packet for variable packet sizes (OPT_VAR_PKTLEN) */ + ((pgm_header->pgm_options & (0x1 << 1)) ? "true" : "false"), + + pgm_header->pgm_gsi[0], pgm_header->pgm_gsi[1], pgm_header->pgm_gsi[2], pgm_header->pgm_gsi[3], pgm_header->pgm_gsi[4], pgm_header->pgm_gsi[5], + ntohs(pgm_header->pgm_tsdu_length)); + + if (pgm_header->pgm_checksum) + { +#if 0 + const uint16_t encoded_pgm_sum = pgm_header->pgm_checksum; +/* requires modification of data buffer */ + pgm_header->pgm_checksum = 0; + const uint16_t pgm_sum = pgm_csum_fold (pgm_csum_partial((const char*)pgm_header, pgm_length, 0)); + if (pgm_sum != encoded_pgm_sum) { + printf ("PGM checksum incorrect, packet %x calculated %x :(\n", encoded_pgm_sum, pgm_sum); + return FALSE; + } +#endif + } else { + puts ("No PGM checksum :O"); + } + +/* now decode PGM packet types */ + const void* pgm_data = pgm_header + 1; + const size_t pgm_data_length = pgm_length - sizeof(pgm_header); /* can equal zero for SPMR's */ + + bool err = FALSE; + switch (pgm_header->pgm_type) { + case PGM_SPM: err = pgm_print_spm (pgm_header, pgm_data, pgm_data_length); break; + case PGM_POLL: err = pgm_print_poll (pgm_header, pgm_data, pgm_data_length); break; + case PGM_POLR: err = pgm_print_polr (pgm_header, pgm_data, pgm_data_length); break; + case PGM_ODATA: err = pgm_print_odata (pgm_header, pgm_data, pgm_data_length); break; + case PGM_RDATA: err = pgm_print_rdata (pgm_header, pgm_data, pgm_data_length); break; + case PGM_NAK: err = pgm_print_nak (pgm_header, pgm_data, pgm_data_length); break; + case PGM_NNAK: err = pgm_print_nnak (pgm_header, pgm_data, pgm_data_length); break; + case PGM_NCF: err = pgm_print_ncf (pgm_header, pgm_data, pgm_data_length); break; + case PGM_SPMR: err = pgm_print_spmr (pgm_header, pgm_data, pgm_data_length); break; + case PGM_ACK: err = pgm_print_ack (pgm_header, pgm_data, pgm_data_length); break; + default: puts ("unknown packet type :("); break; + } + + return err; +} + +/* 8.1. Source Path Messages (SPM) + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SPM's Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Trailing Edge Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Leading Edge Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Path NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * NLA = Network Layer Address + * NLA AFI = NLA Address Family Indicator: rfc 1700 (ADDRESS FAMILY NUMBERS) + * => Path NLA = IP address of last network element + */ + +#define PGM_MIN_SPM_SIZE ( sizeof(struct pgm_spm) ) + +static +bool +pgm_print_spm ( + const struct pgm_header* const header, + const void* data, + const size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != header); + pgm_assert (NULL != data); + pgm_assert (len > 0); + + printf ("SPM: "); + + if (len < PGM_MIN_SPM_SIZE) { + puts ("packet truncated :("); + return FALSE; + } + + const struct pgm_spm * spm = (const struct pgm_spm *)data; + const struct pgm_spm6* spm6 = (const struct pgm_spm6*)data; + const uint16_t spm_nla_afi = ntohs (spm->spm_nla_afi); + + printf ("sqn %" PRIu32 " trail %" PRIu32 "lu lead %" PRIu32 "lu nla-afi %u ", + ntohl(spm->spm_sqn), + ntohl(spm->spm_trail), + ntohl(spm->spm_lead), + spm_nla_afi); /* address family indicator */ + + char s[INET6_ADDRSTRLEN]; + const void* pgm_opt; + size_t pgm_opt_len; + switch (spm_nla_afi) { + case AFI_IP: + pgm_inet_ntop (AF_INET, &spm->spm_nla, s, sizeof(s)); + pgm_opt = (const uint8_t*)data + sizeof(struct pgm_spm); + pgm_opt_len = len - sizeof(struct pgm_spm); + break; + + case AFI_IP6: + if (len < sizeof (struct pgm_spm6)) { + puts ("packet truncated :("); + return FALSE; + } + + pgm_inet_ntop (AF_INET6, &spm6->spm6_nla, s, sizeof(s)); + pgm_opt = (const uint8_t*)data + sizeof(struct pgm_spm6); + pgm_opt_len = len - sizeof(struct pgm_spm6); + break; + + default: + printf ("unsupported afi"); + return FALSE; + } + + printf ("%s", s); + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT && + pgm_print_options (pgm_opt, pgm_opt_len) < 0 ) + { + return FALSE; + } + + printf ("\n"); + return TRUE; +} + +/* 14.7.1. Poll Request + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLL's Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLL's Round | POLL's Sub-type | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Path NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | POLL's Back-off Interval | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Random String | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Matching Bit-Mask | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Sent to ODATA multicast group with IP Router Alert option. + */ + +#define PGM_MIN_POLL_SIZE ( sizeof(struct pgm_poll) ) + +static +bool +pgm_print_poll ( + const struct pgm_header* const header, + const void* data, + const size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != header); + pgm_assert (NULL != data); + pgm_assert (len > 0); + + printf ("POLL: "); + + if (len < PGM_MIN_POLL_SIZE) { + puts ("packet truncated :("); + return FALSE; + } + + const struct pgm_poll * poll4 = (const struct pgm_poll *)data; + const struct pgm_poll6* poll6 = (const struct pgm_poll6*)data; + const uint16_t poll_nla_afi = ntohs (poll4->poll_nla_afi); + + printf ("sqn %" PRIu32 " round %u sub-type %u nla-afi %u ", + ntohl(poll4->poll_sqn), + ntohs(poll4->poll_round), + ntohs(poll4->poll_s_type), + poll_nla_afi); /* address family indicator */ + + char s[INET6_ADDRSTRLEN]; + const void* pgm_opt; + size_t pgm_opt_len; + switch (poll_nla_afi) { + case AFI_IP: + pgm_inet_ntop (AF_INET, &poll4->poll_nla, s, sizeof(s)); + pgm_opt = (const uint8_t*)data + sizeof(struct pgm_poll); + pgm_opt_len = len - sizeof(struct pgm_poll); + printf ("%s", s); + +/* back-off interval in microseconds */ + printf (" bo_ivl %u", poll4->poll_bo_ivl); + +/* random string */ + printf (" rand [%c%c%c%c]", + isprint (poll4->poll_rand[0]) ? poll4->poll_rand[0] : '.', + isprint (poll4->poll_rand[1]) ? poll4->poll_rand[1] : '.', + isprint (poll4->poll_rand[2]) ? poll4->poll_rand[2] : '.', + isprint (poll4->poll_rand[3]) ? poll4->poll_rand[3] : '.' ); + +/* matching bit-mask */ + printf (" mask 0x%x", poll4->poll_mask); + break; + + case AFI_IP6: + if (len < sizeof (struct pgm_poll6)) { + puts ("packet truncated :("); + return FALSE; + } + + pgm_inet_ntop (AF_INET6, &poll6->poll6_nla, s, sizeof (s)); + pgm_opt = (const uint8_t*)data + sizeof(struct pgm_poll6); + pgm_opt_len = len - sizeof(struct pgm_poll6); + printf ("%s", s); + +/* back-off interval in microseconds */ + printf (" bo_ivl %u", poll6->poll6_bo_ivl); + +/* random string */ + printf (" rand [%c%c%c%c]", + isprint (poll6->poll6_rand[0]) ? poll6->poll6_rand[0] : '.', + isprint (poll6->poll6_rand[1]) ? poll6->poll6_rand[1] : '.', + isprint (poll6->poll6_rand[2]) ? poll6->poll6_rand[2] : '.', + isprint (poll6->poll6_rand[3]) ? poll6->poll6_rand[3] : '.' ); + +/* matching bit-mask */ + printf (" mask 0x%x", poll6->poll6_mask); + break; + + default: + printf ("unsupported afi"); + return FALSE; + } + + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT && + pgm_print_options (pgm_opt, pgm_opt_len) < 0 ) + { + return FALSE; + } + + printf ("\n"); + return TRUE; +} + +/* 14.7.2. Poll Response + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLR's Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLR's Round | reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +static +bool +pgm_print_polr ( + const struct pgm_header* const header, + const void* data, + const size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != header); + pgm_assert (NULL != data); + pgm_assert (len > 0); + + printf ("POLR: "); + + if (len < sizeof(struct pgm_polr)) { + puts ("packet truncated :("); + return FALSE; + } + + const struct pgm_polr* polr = (const struct pgm_polr*)data; + + printf("sqn %" PRIu32 " round %u", + ntohl(polr->polr_sqn), + ntohs(polr->polr_round)); + + const void* pgm_opt = (const uint8_t*)data + sizeof(struct pgm_polr); + size_t pgm_opt_len = len - sizeof(struct pgm_polr); + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT && + pgm_print_options (pgm_opt, pgm_opt_len) < 0 ) + { + return FALSE; + } + + printf ("\n"); + return TRUE; +} + +/* 8.2. Data Packet + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Data Packet Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Trailing Edge Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Data ... + * +-+-+- ... + */ + +static +bool +pgm_print_odata ( + const struct pgm_header* const header, + const void* data, + const size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != header); + pgm_assert (NULL != data); + pgm_assert (len > 0); + + printf ("ODATA: "); + + if (len < sizeof(struct pgm_data)) { + puts ("packet truncated :("); + return FALSE; + } + + const struct pgm_data* odata = (const struct pgm_data*)data; + + printf ("sqn %" PRIu32 " trail %" PRIu32 " [", + ntohl(odata->data_sqn), + ntohl(odata->data_trail)); + +/* option extensions */ + const void* pgm_opt = (const uint8_t*)data + sizeof(struct pgm_data); + size_t pgm_opt_len = len - sizeof(struct pgm_data); + const char* payload = pgm_opt; + + if (header->pgm_options & PGM_OPT_PRESENT) { + const ssize_t opt_len = pgm_print_options (pgm_opt, pgm_opt_len); + if (opt_len < 0) + return FALSE; + payload += opt_len; + } + +/* data */ + const char* end = payload + ntohs (header->pgm_tsdu_length); + while (payload < end) { + if (isprint (*payload)) + putchar (*payload); + else + putchar ('.'); + payload++; + } + + printf ("]\n"); + return TRUE; +} + +/* 8.2. Repair Data + */ + +static +bool +pgm_print_rdata ( + const struct pgm_header* const header, + const void* data, + const size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != header); + pgm_assert (NULL != data); + pgm_assert (len > 0); + + printf ("RDATA: "); + + if (len < sizeof(struct pgm_data)) { + puts ("packet truncated :("); + return FALSE; + } + + const struct pgm_data* rdata = (const struct pgm_data*)data; + + printf ("sqn %" PRIu32 " trail %" PRIu32 " [", + ntohl (rdata->data_sqn), + ntohl (rdata->data_trail)); + +/* option extensions */ + const void* pgm_opt = (const uint8_t*)data + sizeof(struct pgm_data); + size_t pgm_opt_len = len - sizeof(struct pgm_data); + const char* payload = pgm_opt; + + if (header->pgm_options & PGM_OPT_PRESENT) { + const ssize_t opt_len = pgm_print_options (pgm_opt, pgm_opt_len); + if (opt_len < 0) + return FALSE; + payload += opt_len; + } + +/* data */ + const char* end = payload + ntohs (header->pgm_tsdu_length); + while (payload < end) { + if (isprint (*payload)) + putchar (*payload); + else + putchar ('.'); + payload++; + } + + printf ("]\n"); + return TRUE; +} + +/* 8.3. NAK + * + * Technically the AFI of the source and multicast group can be different + * but that would be very wibbly wobbly. One example is using a local DLR + * with a IPv4 address to reduce NAK cost for recovery on wide IPv6 + * distribution. + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Requested Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Source NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Multicast Group NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | Option Extensions when present ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... + */ + +#define PGM_MIN_NAK_SIZE ( sizeof(struct pgm_nak) ) + +static +bool +pgm_print_nak ( + const struct pgm_header* const header, + const void* data, + const size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != header); + pgm_assert (NULL != data); + pgm_assert (len > 0); + + printf ("NAK: "); + + if (len < PGM_MIN_NAK_SIZE) { + puts ("packet truncated :("); + return FALSE; + } + + const struct pgm_nak * nak = (const struct pgm_nak *)data; + const struct pgm_nak6* nak6 = (const struct pgm_nak6*)data; + const uint16_t nak_src_nla_afi = ntohs (nak->nak_src_nla_afi); + + printf ("sqn %" PRIu32 " src ", + ntohl(nak->nak_sqn)); + + char s[INET6_ADDRSTRLEN]; + const void* pgm_opt; + size_t pgm_opt_len; + +/* source nla */ + switch (nak_src_nla_afi) { + case AFI_IP: { + const uint16_t nak_grp_nla_afi = ntohs (nak->nak_grp_nla_afi); + if (nak_src_nla_afi != nak_grp_nla_afi) { + puts ("different source & group afi very wibbly wobbly :("); + return FALSE; + } + + pgm_inet_ntop (AF_INET, &nak->nak_src_nla, s, sizeof(s)); + pgm_opt = (const uint8_t*)data + sizeof(struct pgm_nak); + pgm_opt_len = len - sizeof(struct pgm_nak); + printf ("%s grp ", s); + + pgm_inet_ntop (AF_INET, &nak->nak_grp_nla, s, sizeof(s)); + printf ("%s", s); + break; + } + + case AFI_IP6: { + if (len < sizeof (struct pgm_nak6)) { + puts ("packet truncated :("); + return FALSE; + } + + const uint16_t nak_grp_nla_afi = ntohs (nak6->nak6_grp_nla_afi); + if (nak_src_nla_afi != nak_grp_nla_afi) { + puts ("different source & group afi very wibbly wobbly :("); + return FALSE; + } + + pgm_inet_ntop (AF_INET6, &nak6->nak6_src_nla, s, sizeof(s)); + pgm_opt = (const uint8_t*)data + sizeof(struct pgm_nak6); + pgm_opt_len = len - sizeof(struct pgm_nak6); + printf ("%s grp ", s); + + pgm_inet_ntop (AF_INET6, &nak6->nak6_grp_nla, s, sizeof(s)); + printf ("%s", s); + break; + } + + default: + puts ("unsupported afi"); + return FALSE; + } + + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT && + pgm_print_options (pgm_opt, pgm_opt_len) < 0 ) + { + return FALSE; + } + + printf ("\n"); + return TRUE; +} + +/* 8.3. N-NAK + */ + +static +bool +pgm_print_nnak ( + PGM_GNUC_UNUSED const struct pgm_header* const header, + PGM_GNUC_UNUSED const void* data, + const size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != header); + pgm_assert (NULL != data); + pgm_assert (len > 0); + + printf ("N-NAK: "); + + if (len < sizeof(struct pgm_nak)) { + puts ("packet truncated :("); + return FALSE; + } + +// struct pgm_nak* nnak = (struct pgm_nak*)data; + + return TRUE; +} + +/* 8.3. NCF + */ + +bool +pgm_print_ncf ( + PGM_GNUC_UNUSED const struct pgm_header* const header, + PGM_GNUC_UNUSED const void* data, + const size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != header); + pgm_assert (NULL != data); + pgm_assert (len > 0); + + printf ("NCF: "); + + if (len < sizeof(struct pgm_nak)) { + puts ("packet truncated :("); + return FALSE; + } + +// struct pgm_nak* ncf = (struct pgm_nak*)data; + + return TRUE; +} + +/* 13.6. SPM Request + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... + */ + +static +bool +pgm_print_spmr ( + const struct pgm_header* const header, + const void* data, + const size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != header); + pgm_assert (NULL != data); + pgm_assert (len > 0); + + printf ("SPMR: "); + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT && + pgm_print_options (data, len) < 0 ) + { + return FALSE; + } + + printf ("\n"); + return TRUE; +} + +/* PGMCC: ACK + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RX_MAX | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Received Packet Bitmap | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... + */ + +static +bool +pgm_print_ack ( + const struct pgm_header* const header, + const void* data, + const size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != header); + pgm_assert (NULL != data); + pgm_assert (len > 0); + + printf ("ACK: "); + + const struct pgm_ack* ack = (const struct pgm_ack*)data; + char bitmap[33]; + + for (unsigned i = 31; i; i--) + bitmap[i] = (ack->ack_bitmap & (1 << i)) ? '1' : '0'; + bitmap[32] = '\0'; + + printf ("rx_max %" PRIu32 " bitmap [%s] ", + ntohl(ack->ack_rx_max), bitmap); + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT && + pgm_print_options (data, len) < 0 ) + { + return FALSE; + } + + printf ("\n"); + return TRUE; +} + + +/* Parse PGM options fields, alters contents of packet. + * + * returns -1 on failure, or total length in octets of the option fields + */ + +static +ssize_t +pgm_print_options ( + const void* data, + size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != data); + pgm_assert (len > 0); + + printf (" OPTIONS:"); + if (len < sizeof(struct pgm_opt_length)) { + puts (" packet truncated :("); + return -1; + } + + const struct pgm_opt_length* opt_len = (const struct pgm_opt_length*)data; + if (opt_len->opt_length != sizeof(struct pgm_opt_length)) { + printf (" bad opt_length length %u\n", (unsigned)opt_len->opt_length); + return -1; + } + + uint16_t opt_total_length = ntohs (opt_len->opt_total_length); + printf (" total len %u ", opt_total_length); + if (opt_total_length < (sizeof(struct pgm_opt_length) + sizeof(struct pgm_opt_header)) || + opt_total_length > len) + { + puts ("bad total length"); + return -1; + } + +/* total length includes opt_length option */ + opt_total_length -= sizeof(struct pgm_opt_length); + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)(opt_len + 1); + +/* iterate through options (max 16) */ + unsigned count = 16; + while (opt_total_length && count) + { + if (opt_total_length < sizeof(struct pgm_opt_header) || + opt_header->opt_length > opt_total_length) + { + puts ("short on option data :o"); + return -1; + } + + if (opt_header->opt_type & PGM_OPT_END) { + printf ("OPT_END+"); + } + + switch (opt_header->opt_type & PGM_OPT_MASK) { + case PGM_OPT_FRAGMENT: + printf ("OPT_FRAGMENT "); + break; + + case PGM_OPT_NAK_LIST: + printf ("OPT_NAK_LIST "); + break; + + case PGM_OPT_JOIN: + printf ("OPT_JOIN "); + break; + + case PGM_OPT_REDIRECT: + printf ("OPT_REDIRECT "); + break; + + case PGM_OPT_SYN: + printf ("OPT_SYN "); + break; + + case PGM_OPT_FIN: + printf ("OPT_FIN "); + break; + + case PGM_OPT_RST: + printf ("OPT_RST "); + break; + + case PGM_OPT_PARITY_PRM: + printf ("OPT_PARITY_PRM "); + break; + + case PGM_OPT_CURR_TGSIZE: + printf ("OPT_CURR_TGSIZE "); + break; + + case PGM_OPT_CR: + printf ("OPT_CR "); + break; + + case PGM_OPT_CRQST: + printf ("OPT_CRQST "); + break; + + case PGM_OPT_PGMCC_DATA: + printf ("OPT_PGMCC_DATA "); + break; + + case PGM_OPT_PGMCC_FEEDBACK: + printf ("OPT_PGMCC_FEEDBACK "); + break; + + case PGM_OPT_NAK_BO_IVL: + printf ("OPT_NAK_BO_IVL "); + break; + + case PGM_OPT_NAK_BO_RNG: + printf ("OPT_NAK_BO_RNG "); + break; + + case PGM_OPT_NBR_UNREACH: + printf ("OPT_NBR_UNREACH "); + break; + + case PGM_OPT_PATH_NLA: + printf ("OPT_PATH_NLA "); + break; + + default: + printf ("OPT-%u{%u} ", opt_header->opt_type & PGM_OPT_MASK, opt_header->opt_length); + break; + } + + opt_total_length -= opt_header->opt_length; + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); + + count--; + } + + if (!count) { + puts ("too many options found"); + return -1; + } + + return ((const uint8_t*)opt_header - (const uint8_t*)data); +} + +const char* +pgm_type_string ( + uint8_t type + ) +{ + const char* c; + + switch (type) { + case PGM_SPM: c = "PGM_SPM"; break; + case PGM_POLL: c = "PGM_POLL"; break; + case PGM_POLR: c = "PGM_POLR"; break; + case PGM_ODATA: c = "PGM_ODATA"; break; + case PGM_RDATA: c = "PGM_RDATA"; break; + case PGM_NAK: c = "PGM_NAK"; break; + case PGM_NNAK: c = "PGM_NNAK"; break; + case PGM_NCF: c = "PGM_NCF"; break; + case PGM_SPMR: c = "PGM_SPMR"; break; + case PGM_ACK: c = "PGM_ACK"; break; + default: c = "(unknown)"; break; + } + + return c; +} + +const char* +pgm_udpport_string ( + uint16_t port + ) +{ + static pgm_hashtable_t *services = NULL; + + if (!services) { + services = pgm_hashtable_new (pgm_int_hash, pgm_int_equal); + } + + const int hash_key = port; + void* service_string = pgm_hashtable_lookup (services, &hash_key); + if (service_string != NULL) { + return service_string; + } + + struct servent* se = getservbyport (port, "udp"); + if (se == NULL) { + char buf[sizeof("00000")]; + snprintf(buf, sizeof(buf), "%u", ntohs(port)); + service_string = pgm_strdup(buf); + } else { + service_string = pgm_strdup(se->s_name); + } + pgm_hashtable_insert (services, &hash_key, service_string); + return service_string; +} + +const char* +pgm_gethostbyaddr ( + const struct in_addr* ap + ) +{ + static pgm_hashtable_t *hosts = NULL; + + if (!hosts) { + hosts = pgm_hashtable_new (pgm_str_hash, pgm_int_equal); + } + + const int hash_key = (int)ap->s_addr; + void* host_string = pgm_hashtable_lookup (hosts, &hash_key); + if (host_string != NULL) { + return host_string; + } + + struct hostent* he = gethostbyaddr((const char*)ap, sizeof(struct in_addr), AF_INET); + if (he == NULL) { + struct in_addr in; + memcpy (&in, ap, sizeof(in)); + host_string = pgm_strdup(inet_ntoa(in)); + } else { + host_string = pgm_strdup(he->h_name); + } + pgm_hashtable_insert (hosts, &hash_key, host_string); + return host_string; +} + +void +pgm_ipopt_print ( + const void* ipopt, + size_t length + ) +{ +/* pre-conditions */ + pgm_assert (NULL != ipopt); + + const char* op = ipopt; + + while (length) + { + char len = (*op == PGM_IPOPT_NOP || *op == PGM_IPOPT_EOL) ? 1 : op[1]; + switch (*op) { + case PGM_IPOPT_EOL: printf(" eol"); break; + case PGM_IPOPT_NOP: printf(" nop"); break; + case PGM_IPOPT_RR: printf(" rr"); break; /* 1 route */ + case PGM_IPOPT_TS: printf(" ts"); break; /* 1 TS */ +#if 0 + case PGM_IPOPT_SECURITY: printf(" sec-level"); break; + case PGM_IPOPT_LSRR: printf(" lsrr"); break; /* 1 route */ + case PGM_IPOPT_SATID: printf(" satid"); break; + case PGM_IPOPT_SSRR: printf(" ssrr"); break; /* 1 route */ +#endif + default: printf(" %x{%d}", (int)*op, (int)len); break; + } + + if (!len) { + puts ("invalid IP opt length"); + return; + } + + op += len; + length -= len; + } +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/packet_test_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/packet_test_unittest.c new file mode 100644 index 0000000..7edefbb --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/packet_test_unittest.c @@ -0,0 +1,169 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for PGM packet handling. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include + + +/* mock state */ + +#define PACKET_DEBUG +#include "packet_test.c" + + +static +struct pgm_sk_buff_t* +generate_raw_pgm (void) +{ + const char source[] = "i am not a string"; + const guint source_len = sizeof(source); + struct pgm_sk_buff_t* skb; + GError* err = NULL; + + skb = pgm_alloc_skb (1500); + skb->sock = (pgm_sock_t*)0x1; + skb->tstamp = 0x1; + skb->data = skb->head; + skb->len = sizeof(struct pgm_ip) + sizeof(struct pgm_header) + sizeof(struct pgm_data) + source_len; + skb->tail = (guint8*)skb->data + skb->len; + +/* add IP header */ + struct pgm_ip* iphdr = skb->data; + iphdr->ip_hl = sizeof(struct pgm_ip) / 4; + iphdr->ip_v = 4; + iphdr->ip_tos = 0; + iphdr->ip_len = g_htons (skb->len); + iphdr->ip_id = 0; + iphdr->ip_off = 0; + iphdr->ip_ttl = 16; + iphdr->ip_p = IPPROTO_PGM; + iphdr->ip_sum = 0; + iphdr->ip_src.s_addr = inet_addr ("127.0.0.1"); + iphdr->ip_dst.s_addr = inet_addr ("127.0.0.2"); + +/* add PGM header */ + struct pgm_header* pgmhdr = (gpointer)(iphdr + 1); + pgmhdr->pgm_sport = g_htons ((guint16)1000); + pgmhdr->pgm_dport = g_htons ((guint16)7500); + pgmhdr->pgm_type = PGM_ODATA; + pgmhdr->pgm_options = 0; + pgmhdr->pgm_gsi[0] = 1; + pgmhdr->pgm_gsi[1] = 2; + pgmhdr->pgm_gsi[2] = 3; + pgmhdr->pgm_gsi[3] = 4; + pgmhdr->pgm_gsi[4] = 5; + pgmhdr->pgm_gsi[5] = 6; + pgmhdr->pgm_tsdu_length = g_htons (source_len); + +/* add ODATA header */ + struct pgm_data* datahdr = (gpointer)(pgmhdr + 1); + datahdr->data_sqn = g_htonl ((guint32)0); + datahdr->data_trail = g_htonl ((guint32)-1); + +/* add payload */ + gpointer data = (gpointer)(datahdr + 1); + memcpy (data, source, source_len); + +/* finally PGM checksum */ + pgmhdr->pgm_checksum = 0; + pgmhdr->pgm_checksum = pgm_csum_fold (pgm_csum_partial (pgmhdr, sizeof(struct pgm_header) + sizeof(struct pgm_data) + source_len, 0)); + +/* and IP checksum */ + iphdr->ip_sum = pgm_inet_checksum (skb->head, skb->len, 0); + + return skb; +} + + +/* mock functions for external references */ + +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return 0; +} + + +/* target: + * gboolean + * pgm_print_packet ( + * gpointer data, + * gsize len + * ) + */ + +START_TEST (test_print_packet_pass_001) +{ + struct pgm_sk_buff_t* skb = generate_raw_pgm (); + pgm_print_packet (skb->head, skb->len); +} +END_TEST + +START_TEST (test_print_packet_fail_001) +{ + pgm_print_packet (NULL, 0); + fail ("reached"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_print_packet = tcase_create ("print-packet"); + suite_add_tcase (s, tc_print_packet); + tcase_add_test (tc_print_packet, test_print_packet_pass_001); + tcase_add_test_raise_signal (tc_print_packet, test_print_packet_fail_001, SIGABRT); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/pgmMIB.c b/3rdparty/openpgm-svn-r1085/pgm/pgmMIB.c new file mode 100644 index 0000000..1226c50 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/pgmMIB.c @@ -0,0 +1,3212 @@ +/* + * Note: this file originally auto-generated by mib2c using + * : mib2c.notify.conf,v 5.3 2004/04/15 12:29:19 dts12 Exp $ + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "pgm/snmp.h" +#include "impl/pgmMIB.h" +#include "impl/pgmMIB_columns.h" +#include "impl/pgmMIB_enums.h" + + +//#define PGMMIB_DEBUG + + +/* locals */ + +struct pgm_snmp_context_t { + pgm_slist_t* list; + pgm_list_t* node; + int index; /* table index */ + unsigned instance; /* unique number per node */ +}; + +typedef struct pgm_snmp_context_t pgm_snmp_context_t; + +static const oid snmptrap_oid[] = {1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0}; + + +/* functions */ + +static int initialize_table_pgmSourceTable(void); +static Netsnmp_Node_Handler pgmSourceTable_handler; +static Netsnmp_First_Data_Point pgmSourceTable_get_first_data_point; +static Netsnmp_Next_Data_Point pgmSourceTable_get_next_data_point; +static Netsnmp_Free_Loop_Context pgmSourceTable_free_loop_context; + +static int initialize_table_pgmSourceConfigTable(void); +static Netsnmp_Node_Handler pgmSourceConfigTable_handler; +static Netsnmp_First_Data_Point pgmSourceConfigTable_get_first_data_point; +static Netsnmp_Next_Data_Point pgmSourceConfigTable_get_next_data_point; +static Netsnmp_Free_Loop_Context pgmSourceConfigTable_free_loop_context; + +static int initialize_table_pgmSourcePerformanceTable(void); +static Netsnmp_Node_Handler pgmSourcePerformanceTable_handler; +static Netsnmp_First_Data_Point pgmSourcePerformanceTable_get_first_data_point; +static Netsnmp_Next_Data_Point pgmSourcePerformanceTable_get_next_data_point; +static Netsnmp_Free_Loop_Context pgmSourcePerformanceTable_free_loop_context; + +static int initialize_table_pgmReceiverTable(void); +static Netsnmp_Node_Handler pgmReceiverTable_handler; +static Netsnmp_First_Data_Point pgmReceiverTable_get_first_data_point; +static Netsnmp_Next_Data_Point pgmReceiverTable_get_next_data_point; +static Netsnmp_Free_Loop_Context pgmReceiverTable_free_loop_context; + +static int initialize_table_pgmReceiverConfigTable(void); +static Netsnmp_Node_Handler pgmReceiverConfigTable_handler; +static Netsnmp_First_Data_Point pgmReceiverConfigTable_get_first_data_point; +static Netsnmp_Next_Data_Point pgmReceiverConfigTable_get_next_data_point; +static Netsnmp_Free_Loop_Context pgmReceiverConfigTable_free_loop_context; + +static int initialize_table_pgmReceiverPerformanceTable(void); +static Netsnmp_Node_Handler pgmReceiverPerformanceTable_handler; +static Netsnmp_First_Data_Point pgmReceiverPerformanceTable_get_first_data_point; +static Netsnmp_Next_Data_Point pgmReceiverPerformanceTable_get_next_data_point; +static Netsnmp_Free_Loop_Context pgmReceiverPerformanceTable_free_loop_context; + + +bool +pgm_mib_init ( + pgm_error_t** error + ) +{ + if (MIB_REGISTERED_OK != initialize_table_pgmSourceTable()) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SNMP, + PGM_ERROR_FAILED, + _("pgmSourceTable registration: see SNMP log for further details.")); + return FALSE; + } + if (MIB_REGISTERED_OK != initialize_table_pgmSourceConfigTable()) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SNMP, + PGM_ERROR_FAILED, + _("pgmSourceConfigTable registration: see SNMP log for further details.")); + return FALSE; + } + if (MIB_REGISTERED_OK != initialize_table_pgmSourcePerformanceTable()) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SNMP, + PGM_ERROR_FAILED, + _("pgmSourcePerformanceTable registration: see SNMP log for further details.")); + return FALSE; + } + if (MIB_REGISTERED_OK != initialize_table_pgmReceiverTable()) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SNMP, + PGM_ERROR_FAILED, + _("pgmReceiverTable registration: see SNMP log for further details.")); + return FALSE; + } + if (MIB_REGISTERED_OK != initialize_table_pgmReceiverConfigTable()) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SNMP, + PGM_ERROR_FAILED, + _("pgmReceiverConfigTable registration: see SNMP log for further details.")); + return FALSE; + } + if (MIB_REGISTERED_OK != initialize_table_pgmReceiverPerformanceTable()) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SNMP, + PGM_ERROR_FAILED, + _("pgmReceiverPerformanceTable registration: see SNMP log for further details.")); + return FALSE; + } + + return TRUE; +} + +/* + * pgmSourceTable + * + * returns MIB_REGISTERED_OK on success, failures include: + * MIB_REGISTRATION_FAILED + * MIB_DUPLICATE_REGISTRATION + * SNMPERR_GENERR + */ + +static +int +initialize_table_pgmSourceTable (void) +{ + pgm_debug ("initialize_table_pgmSourceTable ()"); + + static const oid pgmSourceTable_oid[] = {1,3,6,1,3,112,1,2,100,2}; + netsnmp_table_registration_info* table_info = NULL; + netsnmp_iterator_info* iinfo = NULL; + netsnmp_handler_registration* reg = NULL; + + reg = netsnmp_create_handler_registration ("pgmSourceTable", pgmSourceTable_handler, + pgmSourceTable_oid, OID_LENGTH( pgmSourceTable_oid ), + HANDLER_CAN_RONLY); + if (!reg) + goto error; + + table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); + if (!table_info) + goto error; + + table_info->min_column = COLUMN_PGMSOURCESOURCEADDRESS; + table_info->max_column = COLUMN_PGMSOURCESOURCEPORTNUMBER; + + netsnmp_table_helper_add_indexes (table_info, + ASN_OCTET_STR, /* index: pgmSourceGlobalId */ + ASN_UNSIGNED, /* index: pgmSourceSourcePort */ + 0); + + iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info ); + if (!iinfo) + goto error; + + iinfo->get_first_data_point = pgmSourceTable_get_first_data_point; + iinfo->get_next_data_point = pgmSourceTable_get_next_data_point; + iinfo->free_loop_context_at_end = pgmSourceTable_free_loop_context; + iinfo->table_reginfo = table_info; + + return netsnmp_register_table_iterator (reg, iinfo); + +error: + if (table_info && table_info->indexes) /* table_data_free_func() is internal */ + snmp_free_var (table_info->indexes); + SNMP_FREE( table_info ); + SNMP_FREE( iinfo ); + netsnmp_handler_registration_free (reg); + + return -1; +} + +/* called for first row of data in SNMP table + * + * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context, + * optionally returns my_data_context. + * + * returns answer or NULL + */ + +static +netsnmp_variable_list* +pgmSourceTable_get_first_data_point( + void** my_loop_context, /* valid through one query of multiple "data points" */ + void** my_data_context, /* answer blob which is passed to handler() */ + netsnmp_variable_list* put_index_data, /* answer */ + netsnmp_iterator_info* mydata /* iinfo on init() */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmSourceTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_rwlock_reader_lock (&pgm_sock_list_lock); + + if (!pgm_sock_list) { + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + return NULL; + } + +/* create our own context for this SNMP loop */ + pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1); + context->list = pgm_sock_list; + *my_loop_context = context; + +/* pass on for generic row access */ + return pgmSourceTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata); +} + +static +netsnmp_variable_list* +pgmSourceTable_get_next_data_point( + void** my_loop_context, + void** my_data_context, + netsnmp_variable_list* put_index_data, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmSourceTable_get_next_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context; + netsnmp_variable_list *idx = put_index_data; + + if (!context->list) + return NULL; + + pgm_sock_t* sock = context->list->data; + +/* pgmSourceGlobalId */ + char gsi[ PGM_GSISTRLEN ]; + pgm_gsi_print_r (&sock->tsi.gsi, gsi, sizeof(gsi)); + snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi)); + idx = idx->next_variable; + +/* pgmSourceSourcePort */ + const unsigned sport = ntohs (sock->tsi.sport); + snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport)); + + *my_data_context = sock; + context->list = context->list->next; + + return put_index_data; +} + +static +void +pgmSourceTable_free_loop_context ( + void* my_loop_context, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmSourceTable_free_loop_context (my_loop_context:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context; + pgm_free (context); + my_loop_context = NULL; + + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); +} + +static +int +pgmSourceTable_handler ( + netsnmp_mib_handler* handler, + netsnmp_handler_registration* reginfo, + netsnmp_agent_request_info* reqinfo, + netsnmp_request_info* requests + ) +{ +/* pre-conditions */ + pgm_assert (NULL != handler); + pgm_assert (NULL != reginfo); + pgm_assert (NULL != reqinfo); + pgm_assert (NULL != requests); + + pgm_debug ("pgmSourceTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)", + (const void*)handler, + (const void*)reginfo, + (const void*)reqinfo, + (const void*)requests); + + switch (reqinfo->mode) { + +/* Read-support (also covers GetNext requests) */ + + case MODE_GET: + for (netsnmp_request_info* request = requests; + request; + request = request->next) + { + const pgm_sock_t* sock = (pgm_sock_t*)netsnmp_extract_iterator_context (request); + + if (!sock) { + netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE); + continue; + } + + netsnmp_variable_list *var = request->requestvb; + netsnmp_table_request_info* table_info = netsnmp_extract_table_info (request); + + if (!table_info) { + snmp_log (LOG_ERR, "pgmSourceTable_handler: empty table request info.\n"); + continue; + } + + switch (table_info->colnum) { + + case COLUMN_PGMSOURCESOURCEADDRESS: + { + struct sockaddr_in s4; + if (AF_INET == sock->send_gsr.gsr_source.ss_family) + memcpy (&s4, &sock->send_gsr.gsr_source, sizeof(s4)); + else + memset (&s4, 0, sizeof(s4)); + snmp_set_var_typed_value (var, ASN_IPADDRESS, + (const u_char*)&s4.sin_addr.s_addr, + sizeof(struct in_addr) ); + } + break; + + case COLUMN_PGMSOURCEGROUPADDRESS: + { + struct sockaddr_in s4; + if (AF_INET == sock->send_gsr.gsr_group.ss_family) + memcpy (&s4, &sock->send_gsr.gsr_group, sizeof(s4)); + else + memset (&s4, 0, sizeof(s4)); + snmp_set_var_typed_value (var, ASN_IPADDRESS, + (const u_char*)&s4.sin_addr.s_addr, + sizeof(struct in_addr) ); + } + break; + + case COLUMN_PGMSOURCEDESTPORT: + { + const unsigned dport = ntohs (sock->dport); + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&dport, sizeof(dport) ); + } + break; + +/* copy index[0] */ + case COLUMN_PGMSOURCESOURCEGSI: + snmp_set_var_typed_value (var, ASN_OCTET_STR, + (const u_char*)table_info->indexes->val.string, + table_info->indexes->val_len); + break; + +/* copy index[1] */ + case COLUMN_PGMSOURCESOURCEPORTNUMBER: + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)table_info->indexes->next_variable->val.integer, + table_info->indexes->next_variable->val_len); + + break; + + default: + snmp_log (LOG_ERR, "pgmSourceTable_handler: unknown column.\n"); + break; + } + } + break; + + case MODE_SET_RESERVE1: + default: + snmp_log (LOG_ERR, "pgmSourceTable_handler: unsupported mode.\n"); + break; + + } + + return SNMP_ERR_NOERROR; +} + +/* + * pgmSourceConfigTable + * + */ + +static +int +initialize_table_pgmSourceConfigTable(void) +{ + pgm_debug ("initialize_table_pgmSourceConfigTable ()"); + + static const oid pgmSourceConfigTable_oid[] = {1,3,6,1,3,112,1,2,100,3}; + netsnmp_table_registration_info* table_info = NULL; + netsnmp_iterator_info* iinfo = NULL; + netsnmp_handler_registration* reg = NULL; + + reg = netsnmp_create_handler_registration ("pgmSourceConfigTable", pgmSourceConfigTable_handler, + pgmSourceConfigTable_oid, OID_LENGTH( pgmSourceConfigTable_oid ), + HANDLER_CAN_RONLY); + if (!reg) + goto error; + + table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); + if (!table_info) + goto error; + + table_info->min_column = COLUMN_PGMSOURCETTL; + table_info->max_column = COLUMN_PGMSOURCESPMPATHADDRESS; + + netsnmp_table_helper_add_indexes (table_info, + ASN_OCTET_STR, /* index: pgmSourceConfigGlobalId */ + ASN_UNSIGNED, /* index: pgmSourceConfigSourcePort */ + 0); + + iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info ); + if (!iinfo) + goto error; + + iinfo->get_first_data_point = pgmSourceConfigTable_get_first_data_point; + iinfo->get_next_data_point = pgmSourceConfigTable_get_next_data_point; + iinfo->free_loop_context_at_end = pgmSourceConfigTable_free_loop_context; + iinfo->table_reginfo = table_info; + + return netsnmp_register_table_iterator (reg, iinfo); + +error: + if (table_info && table_info->indexes) /* table_data_free_func() is internal */ + snmp_free_var (table_info->indexes); + SNMP_FREE( table_info ); + SNMP_FREE( iinfo ); + netsnmp_handler_registration_free (reg); + + return -1; +} + +/* called for first row of data in SNMP table + * + * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context, + * optionally returns my_data_context. + * + * returns answer or NULL + */ + +static +netsnmp_variable_list* +pgmSourceConfigTable_get_first_data_point( + void** my_loop_context, /* valid through one query of multiple "data points" */ + void** my_data_context, /* answer blob which is passed to handler() */ + netsnmp_variable_list* put_index_data, /* answer */ + netsnmp_iterator_info* mydata /* iinfo on init() */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmSourceConfigTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_rwlock_reader_lock (&pgm_sock_list_lock); + + if (!pgm_sock_list) { + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + return NULL; + } + +/* create our own context for this SNMP loop */ + pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1); + context->list = pgm_sock_list; + *my_loop_context = context; + +/* pass on for generic row access */ + return pgmSourceConfigTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata); +} + +static +netsnmp_variable_list* +pgmSourceConfigTable_get_next_data_point( + void** my_loop_context, + void** my_data_context, + netsnmp_variable_list* put_index_data, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmSourceConfigTable_get_next_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context; + netsnmp_variable_list *idx = put_index_data; + + if (!context->list) + return NULL; + + pgm_sock_t* sock = context->list->data; + +/* pgmSourceGlobalId */ + char gsi[ PGM_GSISTRLEN ]; + pgm_gsi_print_r (&sock->tsi.gsi, gsi, sizeof(gsi)); + snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi)); + idx = idx->next_variable; + +/* pgmSourceSourcePort */ + const unsigned sport = ntohs (sock->tsi.sport); + snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport)); + + *my_data_context = sock; + context->list = context->list->next; + + return put_index_data; +} + +static +void +pgmSourceConfigTable_free_loop_context ( + void* my_loop_context, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmSourceConfigTable_free_loop_context (my_loop_context:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context; + pgm_free (context); + my_loop_context = NULL; + + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); +} + +static +int +pgmSourceConfigTable_handler ( + netsnmp_mib_handler* handler, + netsnmp_handler_registration* reginfo, + netsnmp_agent_request_info* reqinfo, + netsnmp_request_info* requests + ) +{ +/* pre-conditions */ + pgm_assert (NULL != handler); + pgm_assert (NULL != reginfo); + pgm_assert (NULL != reqinfo); + pgm_assert (NULL != requests); + + pgm_debug ("pgmSourceConfigTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)", + (const void*)handler, + (const void*)reginfo, + (const void*)reqinfo, + (const void*)requests); + + switch (reqinfo->mode) { + +/* Read-support (also covers GetNext requests) */ + + case MODE_GET: + for (netsnmp_request_info* request = requests; + request; + request = request->next) + { + const pgm_sock_t* sock = (pgm_sock_t*)netsnmp_extract_iterator_context (request); + + if (!sock) { + netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE); + continue; + } + + netsnmp_variable_list *var = request->requestvb; + netsnmp_table_request_info* table_info = netsnmp_extract_table_info (request); + + if (!table_info) { + snmp_log (LOG_ERR, "pgmSourceTable_handler: empty table request info.\n"); + continue; + } + + switch (table_info->colnum) { + + case COLUMN_PGMSOURCETTL: + { + const unsigned hops = sock->hops; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&hops, sizeof(hops) ); + } + break; + + case COLUMN_PGMSOURCEADVMODE: + { + const unsigned adv_mode = 0 == sock->adv_mode ? PGMSOURCEADVMODE_TIME : PGMSOURCEADVMODE_DATA; + snmp_set_var_typed_value (var, ASN_INTEGER, + (const u_char*)&adv_mode, sizeof(adv_mode) ); + } + break; + +/* FIXED: pgmSourceLateJoin = disable(2) */ + case COLUMN_PGMSOURCELATEJOIN: + { + const unsigned late_join = PGMSOURCELATEJOIN_DISABLE; + snmp_set_var_typed_value (var, ASN_INTEGER, + (const u_char*)&late_join, sizeof(late_join) ); + } + break; + + case COLUMN_PGMSOURCETXWMAXRTE: + { + const unsigned txw_max_rte = sock->txw_max_rte; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&txw_max_rte, sizeof(txw_max_rte) ); + } + break; + + case COLUMN_PGMSOURCETXWSECS: + { + const unsigned txw_secs = sock->txw_secs; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&txw_secs, sizeof(txw_secs) ); + } + break; + +/* FIXED: TXW_ADV_SECS = 0 */ + case COLUMN_PGMSOURCETXWADVSECS: + { + const unsigned txw_adv_secs = 0; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&txw_adv_secs, sizeof(txw_adv_secs) ); + } + break; + +/* FIXED: pgmSourceAdvIvl = TXW_ADV_SECS * 1000 = 0 */ + case COLUMN_PGMSOURCEADVIVL: + { + const unsigned adv_ivl = 0; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&adv_ivl, sizeof(adv_ivl) ); + } + break; + + case COLUMN_PGMSOURCESPMIVL: + { + const unsigned spm_ivl = pgm_to_msecs (sock->spm_ambient_interval); + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&spm_ivl, sizeof(spm_ivl) ); + } + break; + +/* TODO: IHB_MIN */ + case COLUMN_PGMSOURCESPMHEARTBEATIVLMIN: + { + const unsigned ihb_min = 0; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&ihb_min, sizeof(ihb_min) ); + } + break; + +/* TODO: IHB_MAX */ + case COLUMN_PGMSOURCESPMHEARTBEATIVLMAX: + { + const unsigned ihb_max = 0; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&ihb_max, sizeof(ihb_max) ); + } + break; + +/* NAK_BO_IVL */ + case COLUMN_PGMSOURCERDATABACKOFFIVL: + { + const unsigned nak_bo_ivl = pgm_to_msecs (sock->nak_bo_ivl); + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&nak_bo_ivl, sizeof(nak_bo_ivl) ); + } + break; + +/* FIXED: pgmSourceFEC = disabled(1) */ + case COLUMN_PGMSOURCEFEC: + { + const unsigned fec = (sock->use_ondemand_parity || sock->use_proactive_parity) ? 1 : 0; + snmp_set_var_typed_value (var, ASN_INTEGER, + (const u_char*)&fec, sizeof(fec) ); + } + break; + +/* FIXED: pgmSourceFECTransmissionGrpSize = 0 */ + case COLUMN_PGMSOURCEFECTRANSMISSIONGRPSIZE: + { + const unsigned fec_tgs = sock->rs_k; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&fec_tgs, sizeof(fec_tgs) ); + } + break; + +/* FIXED: pgmSourceFECProactiveParitySize = 0 */ + case COLUMN_PGMSOURCEFECPROACTIVEPARITYSIZE: + { + const unsigned fec_paps = sock->rs_proactive_h; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&fec_paps, sizeof(fec_paps) ); + } + break; + +/* IPv6 not supported */ + case COLUMN_PGMSOURCESPMPATHADDRESS: + { + struct sockaddr_in s4; + if (AF_INET == sock->recv_gsr[0].gsr_source.ss_family) + memcpy (&s4, &sock->recv_gsr[0].gsr_source, sizeof(s4)); + else + memset (&s4, 0, sizeof(s4)); + snmp_set_var_typed_value (var, ASN_IPADDRESS, + (const u_char*)&s4.sin_addr.s_addr, + sizeof(struct in_addr) ); + } + break; + + default: + snmp_log (LOG_ERR, "pgmSourceConfigTable_handler: unknown column.\n"); + break; + } + } + break; + + case MODE_SET_RESERVE1: + default: + snmp_log (LOG_ERR, "pgmSourceConfigTable_handler: unsupported mode.\n"); + break; + + } + + return SNMP_ERR_NOERROR; +} + +/* + * pgmSourcePerformanceTable + */ + +static +int +initialize_table_pgmSourcePerformanceTable (void) +{ + pgm_debug ("initialize_table_pgmSourcePerformanceTable ()"); + + static const oid pgmSourcePerformanceTable_oid[] = {1,3,6,1,3,112,1,2,100,4}; + netsnmp_table_registration_info* table_info = NULL; + netsnmp_iterator_info* iinfo = NULL; + netsnmp_handler_registration* reg = NULL; + + reg = netsnmp_create_handler_registration ("pgmSourcePerformanceTable", pgmSourcePerformanceTable_handler, + pgmSourcePerformanceTable_oid, OID_LENGTH( pgmSourcePerformanceTable_oid ), + HANDLER_CAN_RONLY); + if (!reg) + goto error; + + table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); + if (!table_info) + goto error; + + table_info->min_column = COLUMN_PGMSOURCEDATABYTESSENT; + table_info->max_column = COLUMN_PGMSOURCENNAKERRORS; + + netsnmp_table_helper_add_indexes (table_info, + ASN_OCTET_STR, /* index: pgmSourceGlobalId */ + ASN_UNSIGNED, /* index: pgmSourceSourcePort */ + 0); + + iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info ); + if (!iinfo) + goto error; + + iinfo->get_first_data_point = pgmSourcePerformanceTable_get_first_data_point; + iinfo->get_next_data_point = pgmSourcePerformanceTable_get_next_data_point; + iinfo->free_loop_context_at_end = pgmSourcePerformanceTable_free_loop_context; + iinfo->table_reginfo = table_info; + + return netsnmp_register_table_iterator (reg, iinfo); + +error: + if (table_info && table_info->indexes) /* table_data_free_func() is internal */ + snmp_free_var (table_info->indexes); + SNMP_FREE( table_info ); + SNMP_FREE( iinfo ); + netsnmp_handler_registration_free (reg); + + return -1; +} + +/* called for first row of data in SNMP table + * + * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context, + * optionally returns my_data_context. + * + * returns answer or NULL + */ + +static +netsnmp_variable_list* +pgmSourcePerformanceTable_get_first_data_point ( + void** my_loop_context, /* valid through one query of multiple "data points" */ + void** my_data_context, /* answer blob which is passed to handler() */ + netsnmp_variable_list* put_index_data, /* answer */ + netsnmp_iterator_info* mydata /* iinfo on init() */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmSourcePerformanceTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_rwlock_reader_lock (&pgm_sock_list_lock); + + if (!pgm_sock_list) { + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + return NULL; + } + +/* create our own context for this SNMP loop */ + pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1); + context->list = pgm_sock_list; + *my_loop_context = context; + +/* pass on for generic row access */ + return pgmSourcePerformanceTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata); +} + +static +netsnmp_variable_list* +pgmSourcePerformanceTable_get_next_data_point ( + void** my_loop_context, + void** my_data_context, + netsnmp_variable_list* put_index_data, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmSourcePerformanceTable_get_next_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context; + netsnmp_variable_list *idx = put_index_data; + + if (!context->list) + return NULL; + + pgm_sock_t* sock = context->list->data; + +/* pgmSourceGlobalId */ + char gsi[ PGM_GSISTRLEN ]; + pgm_gsi_print_r (&sock->tsi.gsi, gsi, sizeof(gsi)); + snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi)); + idx = idx->next_variable; + +/* pgmSourceSourcePort */ + const unsigned sport = ntohs (sock->tsi.sport); + snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport)); + + *my_data_context = sock; + context->list = context->list->next; + + return put_index_data; +} + +static +void +pgmSourcePerformanceTable_free_loop_context ( + void* my_loop_context, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmPerformanceSourceTable_free_loop_context (my_loop_context:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context; + pgm_free (context); + my_loop_context = NULL; + + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); +} + +static +int +pgmSourcePerformanceTable_handler ( + netsnmp_mib_handler* handler, + netsnmp_handler_registration* reginfo, + netsnmp_agent_request_info* reqinfo, + netsnmp_request_info* requests + ) +{ +/* pre-conditions */ + pgm_assert (NULL != handler); + pgm_assert (NULL != reginfo); + pgm_assert (NULL != reqinfo); + pgm_assert (NULL != requests); + + pgm_debug ("pgmSourcePerformanceTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)", + (const void*)handler, + (const void*)reginfo, + (const void*)reqinfo, + (const void*)requests); + + switch (reqinfo->mode) { + +/* Read-support (also covers GetNext requests) */ + + case MODE_GET: + for (netsnmp_request_info* request = requests; + request; + request = request->next) + { + const pgm_sock_t* sock = (pgm_sock_t*)netsnmp_extract_iterator_context (request); + + if (!sock) { + netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE); + continue; + } + + const pgm_txw_t* window = (const pgm_txw_t*)sock->window; + + netsnmp_variable_list *var = request->requestvb; + netsnmp_table_request_info* table_info = netsnmp_extract_table_info (request); + + if (!table_info) { + snmp_log (LOG_ERR, "pgmSourceTable_handler: empty table request info.\n"); + continue; + } + + switch (table_info->colnum) { + + case COLUMN_PGMSOURCEDATABYTESSENT: + { + const unsigned data_bytes = sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&data_bytes, sizeof(data_bytes) ); + } + break; + + case COLUMN_PGMSOURCEDATAMSGSSENT: + { + const unsigned data_msgs = sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&data_msgs, sizeof(data_msgs) ); + } + break; + + case COLUMN_PGMSOURCEBYTESBUFFERED: + { + const unsigned bytes_buffered = sock->can_send_data ? pgm_txw_size (window) : 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&bytes_buffered, sizeof(bytes_buffered) ); + } + break; + + case COLUMN_PGMSOURCEMSGSBUFFERED: + { + const unsigned msgs_buffered = sock->can_send_data ? pgm_txw_length (window) : 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&msgs_buffered, sizeof(msgs_buffered) ); + } + break; + +/* PGM_PC_SOURCE_SELECTIVE_BYTES_RETRANSMITTED + COLUMN_PGMSOURCEPARITYBYTESRETRANSMITTED */ + case COLUMN_PGMSOURCEBYTESRETRANSMITTED: + { + const unsigned bytes_resent = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_BYTES_RETRANSMITTED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&bytes_resent, sizeof(bytes_resent) ); + } + break; + +/* PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED + COLUMN_PGMSOURCEPARITYMSGSRETRANSMITTED */ + case COLUMN_PGMSOURCEMSGSRETRANSMITTED: + { + const unsigned msgs_resent = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&msgs_resent, sizeof(msgs_resent) ); + } + break; + + case COLUMN_PGMSOURCEBYTESSENT: + { + const unsigned bytes_sent = sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&bytes_sent, sizeof(bytes_sent) ); + } + break; + +/* COLUMN_PGMSOURCEPARITYNAKPACKETSRECEIVED + COLUMN_PGMSOURCESELECTIVENAKPACKETSRECEIVED */ + case COLUMN_PGMSOURCERAWNAKSRECEIVED: + { + const unsigned nak_packets = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&nak_packets, sizeof(nak_packets) ); + } + break; + +/* PGM_PC_SOURCE_SELECTIVE_NAKS_IGNORED + COLUMN_PGMSOURCEPARITYNAKSIGNORED */ + case COLUMN_PGMSOURCENAKSIGNORED: + { + const unsigned naks_ignored = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_IGNORED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&naks_ignored, sizeof(naks_ignored) ); + } + break; + + case COLUMN_PGMSOURCECKSUMERRORS: + { + const unsigned cksum_errors = sock->cumulative_stats[PGM_PC_SOURCE_CKSUM_ERRORS]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&cksum_errors, sizeof(cksum_errors) ); + } + break; + + case COLUMN_PGMSOURCEMALFORMEDNAKS: + { + const unsigned malformed_naks = sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&malformed_naks, sizeof(malformed_naks) ); + } + break; + + case COLUMN_PGMSOURCEPACKETSDISCARDED: + { + const unsigned packets_discarded = sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&packets_discarded, sizeof(packets_discarded) ); + } + break; + +/* PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED + COLUMN_PGMSOURCEPARITYNAKSRECEIVED */ + case COLUMN_PGMSOURCENAKSRCVD: + { + const unsigned naks_received = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&naks_received, sizeof(naks_received) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMSOURCEPARITYBYTESRETRANSMITTED: + { + const unsigned parity_bytes_resent = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&parity_bytes_resent, sizeof(parity_bytes_resent) ); + } + break; + + case COLUMN_PGMSOURCESELECTIVEBYTESRETRANSMITED: + { + const unsigned selective_bytes_resent = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_BYTES_RETRANSMITTED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&selective_bytes_resent, sizeof(selective_bytes_resent) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMSOURCEPARITYMSGSRETRANSMITTED: + { + const unsigned parity_msgs_resent = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&parity_msgs_resent, sizeof(parity_msgs_resent) ); + } + break; + + case COLUMN_PGMSOURCESELECTIVEMSGSRETRANSMITTED: + { + const unsigned selective_msgs_resent = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&selective_msgs_resent, sizeof(selective_msgs_resent) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMSOURCEBYTESADMIT: + { + const unsigned bytes_admit = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&bytes_admit, sizeof(bytes_admit) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMSOURCEMSGSADMIT: + { + const unsigned msgs_admit = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&msgs_admit, sizeof(msgs_admit) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMSOURCEPARITYNAKPACKETSRECEIVED: + { + const unsigned parity_nak_packets = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&parity_nak_packets, sizeof(parity_nak_packets) ); + } + break; + + case COLUMN_PGMSOURCESELECTIVENAKPACKETSRECEIVED: + { + const unsigned selective_nak_packets = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&selective_nak_packets, sizeof(selective_nak_packets) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMSOURCEPARITYNAKSRECEIVED: + { + const unsigned parity_naks = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&parity_naks, sizeof(parity_naks) ); + } + break; + + case COLUMN_PGMSOURCESELECTIVENAKSRECEIVED: + { + const unsigned selective_naks = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&selective_naks, sizeof(selective_naks) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMSOURCEPARITYNAKSIGNORED: + { + const unsigned parity_naks_ignored = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&parity_naks_ignored, sizeof(parity_naks_ignored) ); + } + break; + + case COLUMN_PGMSOURCESELECTIVENAKSIGNORED: + { + const unsigned selective_naks_ignored = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_IGNORED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&selective_naks_ignored, sizeof(selective_naks_ignored) ); + } + break; + + case COLUMN_PGMSOURCEACKERRORS: + { + const unsigned ack_errors = sock->cumulative_stats[PGM_PC_SOURCE_ACK_ERRORS];; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&ack_errors, sizeof(ack_errors) ); + } + break; + + case COLUMN_PGMSOURCEPGMCCACKER: + { + struct sockaddr_in s4; + if (AF_INET == sock->acker_nla.ss_family) + memcpy (&s4, &sock->acker_nla, sizeof(s4)); + else + memset (&s4, 0, sizeof(s4)); + snmp_set_var_typed_value (var, ASN_IPADDRESS, + (const u_char*)&s4.sin_addr.s_addr, + sizeof(struct in_addr) ); + } + break; + + case COLUMN_PGMSOURCETRANSMISSIONCURRENTRATE: + { + const unsigned tx_current_rate = sock->cumulative_stats[PGM_PC_SOURCE_TRANSMISSION_CURRENT_RATE]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&tx_current_rate, sizeof(tx_current_rate) ); + } + break; + + case COLUMN_PGMSOURCEACKPACKETSRECEIVED: + { + const unsigned ack_packets = sock->cumulative_stats[PGM_PC_SOURCE_ACK_PACKETS_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&ack_packets, sizeof(ack_packets) ); + } + break; + +/* COLUMN_PGMSOURCEPARITYNNAKPACKETSRECEIVED + COLUMN_PGMSOURCESELECTIVENNAKPACKETSRECEIVED */ + case COLUMN_PGMSOURCENNAKPACKETSRECEIVED: + { + const unsigned nnak_packets = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAK_PACKETS_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&nnak_packets, sizeof(nnak_packets) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMSOURCEPARITYNNAKPACKETSRECEIVED: + { + const unsigned parity_nnak_packets = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&parity_nnak_packets, sizeof(parity_nnak_packets) ); + } + break; + + case COLUMN_PGMSOURCESELECTIVENNAKPACKETSRECEIVED: + { + const unsigned selective_nnak_packets = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAK_PACKETS_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&selective_nnak_packets, sizeof(selective_nnak_packets) ); + } + break; + +/* COLUMN_PGMSOURCEPARITYNNAKSRECEIVED + COLUMN_PGMSOURCESELECTIVENNAKSRECEIVED */ + case COLUMN_PGMSOURCENNAKSRECEIVED: + { + const unsigned nnaks_received = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAKS_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&nnaks_received, sizeof(nnaks_received) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMSOURCEPARITYNNAKSRECEIVED: + { + const unsigned parity_nnaks = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&parity_nnaks, sizeof(parity_nnaks) ); + } + break; + + case COLUMN_PGMSOURCESELECTIVENNAKSRECEIVED: + { + const unsigned selective_nnaks = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAKS_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&selective_nnaks, sizeof(selective_nnaks) ); + } + break; + + case COLUMN_PGMSOURCENNAKERRORS: + { + const unsigned malformed_nnaks = sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&malformed_nnaks, sizeof(malformed_nnaks) ); + } + break; + + default: + snmp_log (LOG_ERR, "pgmSourcePerformanceTable_handler: unknown column.\n"); + break; + } + } + break; + + case MODE_SET_RESERVE1: + default: + snmp_log (LOG_ERR, "pgmSourcePerformanceTable_handler: unsupported mode.\n"); + break; + + } + + return SNMP_ERR_NOERROR; +} + +/* + * pgmReceiverTable + */ + +static +int +initialize_table_pgmReceiverTable(void) +{ + pgm_debug ("initialize_table_pgmReceiverTable ()"); + + static const oid pgmReceiverTable_oid[] = {1,3,6,1,3,112,1,3,100,2}; + netsnmp_table_registration_info* table_info = NULL; + netsnmp_iterator_info* iinfo = NULL; + netsnmp_handler_registration* reg = NULL; + + reg = netsnmp_create_handler_registration ("pgmReceiverTable", pgmReceiverTable_handler, + pgmReceiverTable_oid, OID_LENGTH( pgmReceiverTable_oid ), + HANDLER_CAN_RONLY); + if (!reg) + goto error; + + table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); + if (!table_info) + goto error; + + table_info->min_column = COLUMN_PGMRECEIVERGROUPADDRESS; + table_info->max_column = COLUMN_PGMRECEIVERUNIQUEINSTANCE; + + netsnmp_table_helper_add_indexes (table_info, + ASN_OCTET_STR, /* index: pgmReceiverGlobalId */ + ASN_UNSIGNED, /* index: pgmReceiverSourcePort */ + ASN_UNSIGNED, /* index: pgmReceiverInstance */ + 0); + + iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info ); + if (!iinfo) + goto error; + + iinfo->get_first_data_point = pgmReceiverTable_get_first_data_point; + iinfo->get_next_data_point = pgmReceiverTable_get_next_data_point; + iinfo->free_loop_context_at_end = pgmReceiverTable_free_loop_context; + iinfo->table_reginfo = table_info; + + return netsnmp_register_table_iterator (reg, iinfo); + +error: + if (table_info && table_info->indexes) /* table_data_free_func() is internal */ + snmp_free_var (table_info->indexes); + SNMP_FREE( table_info ); + SNMP_FREE( iinfo ); + netsnmp_handler_registration_free (reg); + + return -1; +} + +/* called for first row of data in SNMP table + * + * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context, + * optionally returns my_data_context. + * + * returns answer or NULL + */ + +static +netsnmp_variable_list* +pgmReceiverTable_get_first_data_point ( + void** my_loop_context, /* valid through one query of multiple "data points" */ + void** my_data_context, /* answer blob which is passed to handler() */ + netsnmp_variable_list* put_index_data, /* answer */ + netsnmp_iterator_info* mydata /* iinfo on init() */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmReceiverTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_rwlock_reader_lock (&pgm_sock_list_lock); + + if (!pgm_sock_list) { + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + return NULL; + } + +/* create our own context for this SNMP loop */ + pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1); + +/* hunt to find first node, through all socks */ + for (context->list = pgm_sock_list; + context->list; + context->list = context->list->next) + { +/* and through all peers for each sock */ + pgm_sock_t* sock = (pgm_sock_t*)context->list->data; + pgm_rwlock_reader_lock (&sock->peers_lock); + context->node = sock->peers_list; + if (context->node) { +/* maintain this sock's peers lock */ + break; + } + + pgm_rwlock_reader_unlock (&sock->peers_lock); + } + +/* no node found */ + if (!context->node) { + pgm_free (context); + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + return NULL; + } + + *my_loop_context = context; + +/* pass on for generic row access */ + return pgmReceiverTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata); +} + +static +netsnmp_variable_list* +pgmReceiverTable_get_next_data_point ( + void** my_loop_context, + void** my_data_context, + netsnmp_variable_list* put_index_data, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmReceiverTable_get_next_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context; + netsnmp_variable_list *idx = put_index_data; + + if (!context->list) + return NULL; + + pgm_sock_t* sock = context->list->data; + + if (!context->node) + return NULL; + + pgm_peer_t* peer = context->node->data; + +/* pgmReceiverGlobalId */ + char gsi[ PGM_GSISTRLEN ]; + pgm_gsi_print_r (&peer->tsi.gsi, gsi, sizeof(gsi)); + snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi)); + idx = idx->next_variable; + +/* pgmReceiverSourcePort */ + const unsigned sport = ntohs (peer->tsi.sport); + snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport)); + idx = idx->next_variable; + +/* pgmReceiverInstance */ + const unsigned instance = context->instance++; + snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&instance, sizeof(instance)); + +/* set data context to pass to handler callback */ + *my_data_context = peer; + +/* hunt for next valid node */ + if (context->node->next) { + context->node = context->node->next; + } else { + context->node = NULL; + while (context->list->next) + { + pgm_rwlock_reader_unlock (&sock->peers_lock); + context->list = context->list->next; + sock = context->list->data; + pgm_rwlock_reader_lock (&sock->peers_lock); + context->node = sock->peers_list; + if (context->node) { +/* keep lock */ + break; + } + } + } + + return put_index_data; +} + +static +void +pgmReceiverTable_free_loop_context ( + void* my_loop_context, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmReceiverTable_free_loop_context (my_loop_context:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context; + +/* check for intra-peer state */ + if (context->list) { + pgm_sock_t* sock = context->list->data; + pgm_rwlock_reader_unlock (&sock->peers_lock); + } + + pgm_free (context); + my_loop_context = NULL; + + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); +} + +static +int +pgmReceiverTable_handler ( + netsnmp_mib_handler* handler, + netsnmp_handler_registration* reginfo, + netsnmp_agent_request_info* reqinfo, + netsnmp_request_info* requests + ) +{ +/* pre-conditions */ + pgm_assert (NULL != handler); + pgm_assert (NULL != reginfo); + pgm_assert (NULL != reqinfo); + pgm_assert (NULL != requests); + + pgm_debug ("pgmReceiverTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)", + (const void*)handler, + (const void*)reginfo, + (const void*)reqinfo, + (const void*)requests); + + switch (reqinfo->mode) { + +/* Read-support (also covers GetNext requests) */ + + case MODE_GET: + for (netsnmp_request_info* request = requests; + request; + request = request->next) + { + const pgm_peer_t* peer = (pgm_peer_t*)netsnmp_extract_iterator_context (request); + + if (!peer) { + netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE); + continue; + } + + netsnmp_variable_list *var = request->requestvb; + netsnmp_table_request_info* table_info = netsnmp_extract_table_info(request); + + if (table_info == NULL) { + snmp_log (LOG_ERR, "pgmReceiverTable_handler: empty table request info.\n"); + continue; + } + + switch (table_info->colnum) { + + case COLUMN_PGMRECEIVERGROUPADDRESS: + { + struct sockaddr_in s4; + if (AF_INET == peer->group_nla.ss_family) + memcpy (&s4, &peer->group_nla, sizeof(s4)); + else + memset (&s4, 0, sizeof(s4)); + snmp_set_var_typed_value (var, ASN_IPADDRESS, + (const u_char*)&s4.sin_addr.s_addr, + sizeof(struct in_addr) ); + } + break; + +/* by definition same as sock */ + case COLUMN_PGMRECEIVERDESTPORT: + { + const unsigned dport = ntohs (peer->sock->dport); + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&dport, sizeof(dport) ); + } + break; + + case COLUMN_PGMRECEIVERSOURCEADDRESS: + { + struct sockaddr_in s4; + if (AF_INET == peer->nla.ss_family) + memcpy (&s4, &peer->nla, sizeof(s4)); + else + memset (&s4, 0, sizeof(s4)); + snmp_set_var_typed_value (var, ASN_IPADDRESS, + (const u_char*)&s4.sin_addr.s_addr, + sizeof(struct in_addr) ); + } + break; + + case COLUMN_PGMRECEIVERLASTHOP: + { + struct sockaddr_in s4; + if (AF_INET == peer->local_nla.ss_family) + memcpy (&s4, &peer->local_nla, sizeof(s4)); + else + memset (&s4, 0, sizeof(s4)); + snmp_set_var_typed_value (var, ASN_IPADDRESS, + (const u_char*)&s4.sin_addr.s_addr, + sizeof(struct in_addr) ); + } + break; + +/* copy index[0] */ + case COLUMN_PGMRECEIVERSOURCEGSI: + snmp_set_var_typed_value (var, ASN_OCTET_STR, + (const u_char*)table_info->indexes->val.string, + table_info->indexes->val_len); + break; + +/* copy index[1] */ + case COLUMN_PGMRECEIVERSOURCEPORTNUMBER: + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)table_info->indexes->next_variable->val.integer, + table_info->indexes->next_variable->val_len); + break; + +/* copy index[2] */ + case COLUMN_PGMRECEIVERUNIQUEINSTANCE: + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)table_info->indexes->next_variable->next_variable->val.integer, + table_info->indexes->next_variable->next_variable->val_len); + break; + + default: + snmp_log (LOG_ERR, "pgmReceiverTable_handler: unknown column.\n"); + break; + } + } + break; + + case MODE_SET_RESERVE1: + default: + snmp_log (LOG_ERR, "pgmReceiverTable_handler: unsupported mode.\n"); + break; + + } + + return SNMP_ERR_NOERROR; +} + +/* + * pgmReceiverConfigTable + * + */ + +static +int +initialize_table_pgmReceiverConfigTable(void) +{ + pgm_debug ("initialize_table_pgmReceiverConfigTable ()"); + + static const oid pgmReceiverConfigTable_oid[] = {1,3,6,1,3,112,1,3,100,3}; + netsnmp_table_registration_info* table_info = NULL; + netsnmp_iterator_info* iinfo = NULL; + netsnmp_handler_registration* reg = NULL; + + reg = netsnmp_create_handler_registration ("pgmReceiverConfigTable", pgmReceiverConfigTable_handler, + pgmReceiverConfigTable_oid, OID_LENGTH(pgmReceiverConfigTable_oid), + HANDLER_CAN_RONLY); + if (!reg) + goto error; + + table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); + if (!table_info) + goto error; + + table_info->min_column = COLUMN_PGMRECEIVERNAKBACKOFFIVL; + table_info->max_column = COLUMN_PGMRECEIVERNAKFAILURETHRESHOLD; + + netsnmp_table_helper_add_indexes (table_info, + ASN_OCTET_STR, /* index: pgmReceiverConfigGlobalId */ + ASN_UNSIGNED, /* index: pgmReceiverConfigSourcePort */ + ASN_UNSIGNED, /* index: pgmReceiverInstance */ + 0); + + iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info ); + if (!iinfo) + goto error; + + iinfo->get_first_data_point = pgmReceiverConfigTable_get_first_data_point; + iinfo->get_next_data_point = pgmReceiverConfigTable_get_next_data_point; + iinfo->free_loop_context_at_end = pgmReceiverConfigTable_free_loop_context; + iinfo->table_reginfo = table_info; + + return netsnmp_register_table_iterator (reg, iinfo); + +error: + if (table_info && table_info->indexes) /* table_data_free_func() is internal */ + snmp_free_var (table_info->indexes); + SNMP_FREE( table_info ); + SNMP_FREE( iinfo ); + netsnmp_handler_registration_free (reg); + + return -1; +} + +/* called for first row of data in SNMP table + * + * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context, + * optionally returns my_data_context. + * + * returns answer or NULL + */ + +static +netsnmp_variable_list* +pgmReceiverConfigTable_get_first_data_point( + void** my_loop_context, /* valid through one query of multiple "data points" */ + void** my_data_context, /* answer blob which is passed to handler() */ + netsnmp_variable_list* put_index_data, /* answer */ + netsnmp_iterator_info* mydata /* iinfo on init() */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmReceiverConfigTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_rwlock_reader_lock (&pgm_sock_list_lock); + + if (!pgm_sock_list) { + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + return NULL; + } + +/* create our own context for this SNMP loop */ + pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1); + +/* hunt to find first node, through all socks */ + for (context->list = pgm_sock_list; + context->list; + context->list = context->list->next) + { +/* and through all peers for each sock */ + pgm_sock_t* sock = (pgm_sock_t*)context->list->data; + pgm_rwlock_reader_lock (&sock->peers_lock); + context->node = sock->peers_list; + if (context->node) + break; + + pgm_rwlock_reader_unlock (&sock->peers_lock); + } + +/* no node found */ + if (!context->node) { + pgm_free (context); + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + return NULL; + } + + *my_loop_context = context; + +/* pass on for generic row access */ + return pgmReceiverConfigTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata); +} + +static +netsnmp_variable_list* +pgmReceiverConfigTable_get_next_data_point( + void** my_loop_context, + void** my_data_context, + netsnmp_variable_list* put_index_data, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmReceiverConfigTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context; + netsnmp_variable_list *idx = put_index_data; + + if (!context->list) + return NULL; + + pgm_sock_t* sock = context->list->data; + + if (!context->node) + return NULL; + + pgm_peer_t* peer = context->node->data; + +/* pgmReceiverGlobalId */ + char gsi[ PGM_GSISTRLEN ]; + pgm_gsi_print_r (&peer->tsi.gsi, gsi, sizeof(gsi)); + snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi)); + idx = idx->next_variable; + +/* pgmReceiverSourcePort */ + const unsigned sport = ntohs (peer->tsi.sport); + snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport)); + idx = idx->next_variable; + +/* pgmReceiverInstance */ + const unsigned instance = context->instance++; + snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&instance, sizeof(instance)); + +/* set data context to pass to handler callback */ + *my_data_context = peer; + +/* hunt for next valid node */ + if (context->node->next) { + context->node = context->node->next; + } else { + context->node = NULL; + while (context->list->next) + { + pgm_rwlock_reader_unlock (&sock->peers_lock); + context->list = context->list->next; + sock = context->list->data; + pgm_rwlock_reader_lock (&sock->peers_lock); + context->node = sock->peers_list; + if (context->node) { +/* keep lock */ + break; + } + } + } + + return put_index_data; +} + +static +void +pgmReceiverConfigTable_free_loop_context ( + void* my_loop_context, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmReceiverConfigTable_free_loop_context (my_loop_context:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context; + +/* check for intra-peer state */ + if (context->list) { + pgm_sock_t* sock = context->list->data; + pgm_rwlock_reader_unlock (&sock->peers_lock); + } + + pgm_free (context); + my_loop_context = NULL; + + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); +} + +static +int +pgmReceiverConfigTable_handler ( + netsnmp_mib_handler* handler, + netsnmp_handler_registration* reginfo, + netsnmp_agent_request_info* reqinfo, + netsnmp_request_info* requests + ) +{ +/* pre-conditions */ + pgm_assert (NULL != handler); + pgm_assert (NULL != reginfo); + pgm_assert (NULL != reqinfo); + pgm_assert (NULL != requests); + + pgm_debug ("pgmReceiverConfigTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)", + (const void*)handler, + (const void*)reginfo, + (const void*)reqinfo, + (const void*)requests); + + switch (reqinfo->mode) { + +/* Read-support (also covers GetNext requests) */ + + case MODE_GET: + for (netsnmp_request_info* request = requests; + request; + request = request->next) + { + const pgm_peer_t* peer = (pgm_peer_t*)netsnmp_extract_iterator_context(request); + + if (peer == NULL) { + netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE); + continue; + } + + netsnmp_variable_list *var = request->requestvb; + netsnmp_table_request_info* table_info = netsnmp_extract_table_info(request); + + if (table_info == NULL) { + snmp_log (LOG_ERR, "pgmReceiverTable_handler: empty table request info.\n"); + continue; + } + + switch (table_info->colnum) { + +/* nak_bo_ivl from sock */ + case COLUMN_PGMRECEIVERNAKBACKOFFIVL: + { + const unsigned nak_bo_ivl = peer->sock->nak_bo_ivl; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&nak_bo_ivl, sizeof(nak_bo_ivl) ); + } + break; + +/* nak_rpt_ivl from sock */ + case COLUMN_PGMRECEIVERNAKREPEATIVL: + { + const unsigned nak_rpt_ivl = peer->sock->nak_rpt_ivl; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&nak_rpt_ivl, sizeof(nak_rpt_ivl) ); + } + break; + +/* nak_ncf_retries from sock */ + case COLUMN_PGMRECEIVERNAKNCFRETRIES: + { + const unsigned nak_ncf_retries = peer->sock->nak_ncf_retries; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&nak_ncf_retries, sizeof(nak_ncf_retries) ); + } + break; + +/* nak_rdata_ivl from sock */ + case COLUMN_PGMRECEIVERNAKRDATAIVL: + { + const unsigned nak_rdata_ivl = peer->sock->nak_rdata_ivl; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&nak_rdata_ivl, sizeof(nak_rdata_ivl) ); + } + break; + +/* nak_data_retries from sock */ + case COLUMN_PGMRECEIVERNAKDATARETRIES: + { + const unsigned nak_data_retries = peer->sock->nak_data_retries; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&nak_data_retries, sizeof(nak_data_retries) ); + } + break; + +/* FIXED: pgmReceiverSendNaks = enabled(1) */ + case COLUMN_PGMRECEIVERSENDNAKS: + { + const unsigned send_naks = PGMRECEIVERSENDNAKS_ENABLED; + snmp_set_var_typed_value (var, ASN_INTEGER, + (const u_char*)&send_naks, sizeof(send_naks) ); + } + break; + +/* FIXED: pgmReceiverLateJoin = disabled(2) */ + case COLUMN_PGMRECEIVERLATEJOIN: + { + const unsigned late_join = PGMRECEIVERLATEJOIN_DISABLED; + snmp_set_var_typed_value (var, ASN_INTEGER, + (const u_char*)&late_join, sizeof(late_join) ); + } + break; + +/* FIXED: 1 for multicast */ + case COLUMN_PGMRECEIVERNAKTTL: + { + const unsigned nak_hops = 1; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&nak_hops, sizeof(nak_hops) ); + } + break; + +/* FIXED: pgmReceiverDeliveryOrder = ordered(2) */ + case COLUMN_PGMRECEIVERDELIVERYORDER: + { + const unsigned delivery_order = PGMRECEIVERDELIVERYORDER_ORDERED; + snmp_set_var_typed_value (var, ASN_INTEGER, + (const u_char*)&delivery_order, sizeof(delivery_order) ); + } + break; + +/* FIXED: pgmReceiverMcastNaks = disabled(2) */ + case COLUMN_PGMRECEIVERMCASTNAKS: + { + const unsigned mcast_naks = PGMRECEIVERMCASTNAKS_DISABLED; + snmp_set_var_typed_value (var, ASN_INTEGER, + (const u_char*)&mcast_naks, sizeof(mcast_naks) ); + } + break; + +/* TODO: traps */ + case COLUMN_PGMRECEIVERNAKFAILURETHRESHOLDTIMER: + case COLUMN_PGMRECEIVERNAKFAILURETHRESHOLD: + { + const unsigned threshold = 0; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&threshold, sizeof(threshold) ); + } + break; + + default: + snmp_log (LOG_ERR, "pgmReceiverTable_handler: unknown column.\n"); + break; + } + } + break; + + case MODE_SET_RESERVE1: + default: + snmp_log (LOG_ERR, "pgmReceiverTable_handler: unsupported mode.\n"); + break; + + } + + return SNMP_ERR_NOERROR; +} + +/* + * pgmReceiverPerformanceTable + */ + +static +int +initialize_table_pgmReceiverPerformanceTable (void) +{ + pgm_debug ("initialize_table_pgmReceiverPerformanceTable ()"); + + static const oid pgmReceiverPerformanceTable_oid[] = {1,3,6,1,3,112,1,3,100,4}; + netsnmp_table_registration_info* table_info = NULL; + netsnmp_iterator_info* iinfo = NULL; + netsnmp_handler_registration* reg = NULL; + + reg = netsnmp_create_handler_registration ("pgmReceiverPerformanceTable", pgmReceiverPerformanceTable_handler, + pgmReceiverPerformanceTable_oid, OID_LENGTH( pgmReceiverPerformanceTable_oid ), + HANDLER_CAN_RONLY); + if (!reg) + goto error; + + table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); + if (!table_info) + goto error; + + table_info->min_column = COLUMN_PGMRECEIVERDATABYTESRECEIVED; + table_info->max_column = COLUMN_PGMRECEIVERLASTINTERVALNAKFAILURES; + + netsnmp_table_helper_add_indexes (table_info, + ASN_OCTET_STR, /* index: pgmReceiverGlobalId */ + ASN_UNSIGNED, /* index: pgmReceiverSourcePort */ + ASN_UNSIGNED, /* index: pgmReceiverInstance */ + 0); + + iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info ); + if (!iinfo) + goto error; + + iinfo->get_first_data_point = pgmReceiverPerformanceTable_get_first_data_point; + iinfo->get_next_data_point = pgmReceiverPerformanceTable_get_next_data_point; + iinfo->free_loop_context_at_end = pgmReceiverPerformanceTable_free_loop_context; + iinfo->table_reginfo = table_info; + + return netsnmp_register_table_iterator (reg, iinfo); + +error: + if (table_info && table_info->indexes) /* table_data_free_func() is internal */ + snmp_free_var (table_info->indexes); + SNMP_FREE( table_info ); + SNMP_FREE( iinfo ); + netsnmp_handler_registration_free (reg); + + return -1; +} + +/* called for first row of data in SNMP table + * + * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context, + * optionally returns my_data_context. + * + * returns answer or NULL + */ + +static +netsnmp_variable_list* +pgmReceiverPerformanceTable_get_first_data_point ( + void** my_loop_context, /* valid through one query of multiple "data points" */ + void** my_data_context, /* answer blob which is passed to handler() */ + netsnmp_variable_list* put_index_data, /* answer */ + netsnmp_iterator_info* mydata /* iinfo on init() */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmReceiverPerformanceTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_rwlock_reader_lock (&pgm_sock_list_lock); + + if (!pgm_sock_list) { + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + return NULL; + } + +/* create our own context for this SNMP loop */ + pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1); + +/* hunt to find first node, through all socks */ + for (context->list = pgm_sock_list; + context->list; + context->list = context->list->next) + { +/* and through all peers for each sock */ + pgm_sock_t* sock = (pgm_sock_t*)context->list->data; + pgm_rwlock_reader_lock (&sock->peers_lock); + context->node = sock->peers_list; + if (context->node) + break; + + pgm_rwlock_reader_unlock (&sock->peers_lock); + } + +/* no node found */ + if (!context->node) { + pgm_free (context); + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + return NULL; + } + + *my_loop_context = context; + +/* pass on for generic row access */ + return pgmReceiverPerformanceTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata); +} + +static +netsnmp_variable_list* +pgmReceiverPerformanceTable_get_next_data_point ( + void** my_loop_context, + void** my_data_context, + netsnmp_variable_list* put_index_data, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmReceiverPerformanceTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context; + netsnmp_variable_list *idx = put_index_data; + + if (!context->list) + return NULL; + + pgm_sock_t* sock = context->list->data; + + if (!context->node) + return NULL; + + pgm_peer_t* peer = context->node->data; + +/* pgmReceiverGlobalId */ + char gsi[ PGM_GSISTRLEN ]; + pgm_gsi_print_r (&peer->tsi.gsi, gsi, sizeof(gsi)); + snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi)); + idx = idx->next_variable; + +/* pgmReceiverSourcePort */ + const unsigned sport = ntohs (peer->tsi.sport); + snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport)); + idx = idx->next_variable; + +/* pgmReceiverInstance */ + const unsigned instance = context->instance++; + snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&instance, sizeof(instance)); + +/* set data context to pass to handler callback */ + *my_data_context = peer; + +/* hunt for next valid node */ + if (context->node->next) { + context->node = context->node->next; + } else { + context->node = NULL; + while (context->list->next) + { + pgm_rwlock_reader_unlock (&sock->peers_lock); + context->list = context->list->next; + sock = context->list->data; + pgm_rwlock_reader_lock (&sock->peers_lock); + context->node = sock->peers_list; + + if (context->node) + break; + } + } + + return put_index_data; +} + +static +void +pgmReceiverPerformanceTable_free_loop_context ( + void* my_loop_context, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmReceiverPerformanceTable_free_loop_context (my_loop_context:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context; + +/* check for intra-peer state */ + if (context->list) { + pgm_sock_t* sock = context->list->data; + pgm_rwlock_reader_unlock (&sock->peers_lock); + } + + pgm_free (context); + my_loop_context = NULL; + + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); +} + +static +int +pgmReceiverPerformanceTable_handler ( + netsnmp_mib_handler* handler, + netsnmp_handler_registration* reginfo, + netsnmp_agent_request_info* reqinfo, + netsnmp_request_info* requests + ) +{ +/* pre-conditions */ + pgm_assert (NULL != handler); + pgm_assert (NULL != reginfo); + pgm_assert (NULL != reqinfo); + pgm_assert (NULL != requests); + + pgm_debug ("pgmReceiverPerformanceTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)", + (const void*)handler, + (const void*)reginfo, + (const void*)reqinfo, + (const void*)requests); + + switch (reqinfo->mode) { + +/* Read-support (also covers GetNext requests) */ + + case MODE_GET: + for (netsnmp_request_info* request = requests; + request; + request = request->next) + { + const pgm_peer_t* peer = (pgm_peer_t*)netsnmp_extract_iterator_context (request); + + if (!peer) { + netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE); + continue; + } + + const pgm_rxw_t* window = (const pgm_rxw_t*)peer->window; + + netsnmp_variable_list *var = request->requestvb; + netsnmp_table_request_info* table_info = netsnmp_extract_table_info (request); + + if (!table_info) { + snmp_log (LOG_ERR, "pgmReceiverTable_handler: empty table request info.\n"); + continue; + } + + switch (table_info->colnum) { + + case COLUMN_PGMRECEIVERDATABYTESRECEIVED: + { + const unsigned data_bytes = peer->cumulative_stats[PGM_PC_RECEIVER_DATA_BYTES_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&data_bytes, sizeof(data_bytes) ); + } + break; + + case COLUMN_PGMRECEIVERDATAMSGSRECEIVED: + { + const unsigned data_msgs = peer->cumulative_stats[PGM_PC_RECEIVER_DATA_MSGS_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&data_msgs, sizeof(data_msgs) ); + } + break; + +/* total */ + case COLUMN_PGMRECEIVERNAKSSENT: + { + const unsigned naks_sent = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SENT]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&naks_sent, sizeof(naks_sent) ); + } + break; + +/* total */ + case COLUMN_PGMRECEIVERNAKSRETRANSMITTED: + { + const unsigned naks_resent = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_RETRANSMITTED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&naks_resent, sizeof(naks_resent) ); + } + break; + +/* total */ + case COLUMN_PGMRECEIVERNAKFAILURES: + { + const unsigned nak_failures = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_FAILED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&nak_failures, sizeof(nak_failures) ); + } + break; + + case COLUMN_PGMRECEIVERBYTESRECEIVED: + { + const unsigned bytes_received = peer->cumulative_stats[PGM_PC_RECEIVER_BYTES_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&bytes_received, sizeof(bytes_received) ); + } + break; + +/* total */ + case COLUMN_PGMRECEIVERNAKSSUPPRESSED: + { + const unsigned naks_suppressed = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&naks_suppressed, sizeof(naks_suppressed) ); + } + break; + +/* bogus: same as source checksum errors */ + case COLUMN_PGMRECEIVERCKSUMERRORS: + { + const unsigned cksum_errors = peer->sock->cumulative_stats[PGM_PC_SOURCE_CKSUM_ERRORS]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&cksum_errors, sizeof(cksum_errors) ); + } + break; + + case COLUMN_PGMRECEIVERMALFORMEDSPMS: + { + const unsigned malformed_spms = peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_SPMS]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&malformed_spms, sizeof(malformed_spms) ); + } + break; + + case COLUMN_PGMRECEIVERMALFORMEDODATA: + { + const unsigned malformed_odata = peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_ODATA]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&malformed_odata, sizeof(malformed_odata) ); + } + break; + + case COLUMN_PGMRECEIVERMALFORMEDRDATA: + { + const unsigned malformed_rdata = peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_RDATA]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&malformed_rdata, sizeof(malformed_rdata) ); + } + break; + + case COLUMN_PGMRECEIVERMALFORMEDNCFS: + { + const unsigned malformed_ncfs = peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_NCFS]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&malformed_ncfs, sizeof(malformed_ncfs) ); + } + break; + + case COLUMN_PGMRECEIVERPACKETSDISCARDED: + { + const unsigned packets_discarded = peer->cumulative_stats[PGM_PC_RECEIVER_PACKETS_DISCARDED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&packets_discarded, sizeof(packets_discarded) ); + } + break; + + case COLUMN_PGMRECEIVERLOSSES: + { + const unsigned losses = window->cumulative_losses; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&losses, sizeof(losses) ); + } + break; + + case COLUMN_PGMRECEIVERBYTESDELIVEREDTOAPP: + { + const unsigned bytes_delivered =window->bytes_delivered; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&bytes_delivered, sizeof(bytes_delivered) ); + } + break; + + case COLUMN_PGMRECEIVERMSGSDELIVEREDTOAPP: + { + const unsigned msgs_delivered = window->msgs_delivered; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&msgs_delivered, sizeof(msgs_delivered) ); + } + break; + + case COLUMN_PGMRECEIVERDUPSPMS: + { + const unsigned dup_spms = peer->cumulative_stats[PGM_PC_RECEIVER_DUP_SPMS]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&dup_spms, sizeof(dup_spms) ); + } + break; + + case COLUMN_PGMRECEIVERDUPDATAS: + { + const unsigned dup_data = peer->cumulative_stats[PGM_PC_RECEIVER_DUP_DATAS]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&dup_data, sizeof(dup_data) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMRECEIVERDUPPARITIES: + { + const unsigned dup_parity = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&dup_parity, sizeof(dup_parity) ); + } + break; + +/* COLUMN_PGMRECEIVERPARITYNAKPACKETSSENT + COLUMN_PGMRECEIVERSELECTIVENAKPACKETSSENT */ + case COLUMN_PGMRECEIVERNAKPACKETSSENT: + { + const unsigned nak_packets = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAK_PACKETS_SENT]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&nak_packets, sizeof(nak_packets) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMRECEIVERPARITYNAKPACKETSSENT: + { + const unsigned parity_naks = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&parity_naks, sizeof(parity_naks) ); + } + break; + + case COLUMN_PGMRECEIVERSELECTIVENAKPACKETSSENT: + { + const unsigned nak_packets = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAK_PACKETS_SENT]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&nak_packets, sizeof(nak_packets) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMRECEIVERPARITYNAKSSENT: + { + const unsigned parity_naks = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&parity_naks, sizeof(parity_naks) ); + } + break; + + case COLUMN_PGMRECEIVERSELECTIVENAKSSENT: + { + const unsigned naks_sent = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SENT]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&naks_sent, sizeof(naks_sent) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMRECEIVERPARITYNAKSRETRANSMITTED: + { + const unsigned parity_resent = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&parity_resent, sizeof(parity_resent) ); + } + break; + + case COLUMN_PGMRECEIVERSELECTIVENAKSRETRANSMITTED: + { + const unsigned naks_resent = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_RETRANSMITTED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&naks_resent, sizeof(naks_resent) ); + } + break; + +/* COLUMN_PGMRECEIVERPARITYNAKSFAILED + COLUMN_PGMRECEIVERSELECTIVENAKSFAILED */ + case COLUMN_PGMRECEIVERNAKSFAILED: + { + const unsigned naks_failed = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_FAILED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&naks_failed, sizeof(naks_failed) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMRECEIVERPARITYNAKSFAILED: + { + const unsigned parity_failed = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&parity_failed, sizeof(parity_failed) ); + } + break; + + case COLUMN_PGMRECEIVERSELECTIVENAKSFAILED: + { + const unsigned naks_failed = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_FAILED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&naks_failed, sizeof(naks_failed) ); + } + break; + + case COLUMN_PGMRECEIVERNAKSFAILEDRXWADVANCED: + { + const unsigned rxw_failed = peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_RXW_ADVANCED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&rxw_failed, sizeof(rxw_failed) ); + } + break; + + case COLUMN_PGMRECEIVERNAKSFALEDNCFRETRIESEXCEEDED: + { + const unsigned ncf_retries = peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_NCF_RETRIES_EXCEEDED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&ncf_retries, sizeof(ncf_retries) ); + } + break; + + case COLUMN_PGMRECEIVERNAKSFAILEDDATARETRIESEXCEEDED: + { + const unsigned data_retries = peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_DATA_RETRIES_EXCEEDED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&data_retries, sizeof(data_retries) ); + } + break; + +/* FIXED: 0 - absolutely no idea what this means */ + case COLUMN_PGMRECEIVERNAKSFAILEDGENEXPIRED: + { + const unsigned happy_pandas = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&happy_pandas, sizeof(happy_pandas) ); + } + break; + + case COLUMN_PGMRECEIVERNAKFAILURESDELIVERED: + { + const unsigned delivered = peer->cumulative_stats[PGM_PC_RECEIVER_NAK_FAILURES_DELIVERED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&delivered, sizeof(delivered) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMRECEIVERPARITYNAKSSUPPRESSED: + { + const unsigned suppressed = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&suppressed, sizeof(suppressed) ); + } + break; + + case COLUMN_PGMRECEIVERSELECTIVENAKSSUPPRESSED: + { + const unsigned suppressed = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&suppressed, sizeof(suppressed) ); + } + break; + + case COLUMN_PGMRECEIVERNAKERRORS: + { + const unsigned malformed_naks = peer->cumulative_stats[PGM_PC_RECEIVER_NAK_ERRORS]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&malformed_naks, sizeof(malformed_naks) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMRECEIVEROUTSTANDINGPARITYNAKS: + { + const unsigned outstanding_parity = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&outstanding_parity, sizeof(outstanding_parity) ); + } + break; + + case COLUMN_PGMRECEIVEROUTSTANDINGSELECTIVENAKS: + { + const unsigned outstanding_selective = window->nak_backoff_queue.length + + window->wait_ncf_queue.length + + window->wait_data_queue.length; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&outstanding_selective, sizeof(outstanding_selective) ); + } + break; + + case COLUMN_PGMRECEIVERLASTACTIVITY: + { + union { + unsigned uint_value; + time_t time_t_value; + } last_activity; + pgm_time_since_epoch (&peer->last_packet, &last_activity.time_t_value); + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&last_activity.uint_value, sizeof(last_activity.uint_value) ); + } + break; + + case COLUMN_PGMRECEIVERNAKSVCTIMEMIN: + { + const unsigned min_repair_time = window->min_fill_time; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&min_repair_time, sizeof(min_repair_time) ); + } + break; + + case COLUMN_PGMRECEIVERNAKSVCTIMEMEAN: + { + const unsigned mean_repair_time = peer->cumulative_stats[PGM_PC_RECEIVER_NAK_SVC_TIME_MEAN]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&mean_repair_time, sizeof(mean_repair_time) ); + } + break; + + case COLUMN_PGMRECEIVERNAKSVCTIMEMAX: + { + const unsigned max_repair_time = window->max_fill_time; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&max_repair_time, sizeof(max_repair_time) ); + } + break; + + case COLUMN_PGMRECEIVERNAKFAILTIMEMIN: + { + const unsigned min_fail_time = peer->min_fail_time; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&min_fail_time, sizeof(min_fail_time) ); + } + break; + + case COLUMN_PGMRECEIVERNAKFAILTIMEMEAN: + { + const unsigned mean_fail_time = peer->cumulative_stats[PGM_PC_RECEIVER_NAK_FAIL_TIME_MEAN]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&mean_fail_time, sizeof(mean_fail_time) ); + } + break; + + case COLUMN_PGMRECEIVERNAKFAILTIMEMAX: + { + const unsigned max_fail_time = peer->max_fail_time; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&max_fail_time, sizeof(max_fail_time) ); + } + break; + + case COLUMN_PGMRECEIVERNAKTRANSMITMIN: + { + const unsigned min_transmit_count = window->min_nak_transmit_count; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&min_transmit_count, sizeof(min_transmit_count) ); + } + break; + + case COLUMN_PGMRECEIVERNAKTRANSMITMEAN: + { + const unsigned mean_transmit_count = peer->cumulative_stats[PGM_PC_RECEIVER_TRANSMIT_MEAN]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&mean_transmit_count, sizeof(mean_transmit_count) ); + } + break; + + case COLUMN_PGMRECEIVERNAKTRANSMITMAX: + { + const unsigned max_transmit_count = window->max_nak_transmit_count; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&max_transmit_count, sizeof(max_transmit_count) ); + } + break; + + case COLUMN_PGMRECEIVERACKSSENT: + { + const unsigned acks_sent = peer->cumulative_stats[PGM_PC_RECEIVER_ACKS_SENT]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&acks_sent, sizeof(acks_sent) ); + } + break; + + case COLUMN_PGMRECEIVERRXWTRAIL: + { + const unsigned rxw_trail = window->rxw_trail; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&rxw_trail, sizeof(rxw_trail) ); + } + break; + + case COLUMN_PGMRECEIVERRXWLEAD: + { + const unsigned rxw_lead = window->lead; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&rxw_lead, sizeof(rxw_lead) ); + } + break; + +/* TODO: traps */ + case COLUMN_PGMRECEIVERNAKFAILURESLASTINTERVAL: + case COLUMN_PGMRECEIVERLASTINTERVALNAKFAILURES: + { + const unsigned failures = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&failures, sizeof(failures) ); + } + break; + + default: + snmp_log (LOG_ERR, "pgmReceiverTable_handler: unknown column.\n"); + break; + } + } + break; + + case MODE_SET_RESERVE1: + default: + snmp_log (LOG_ERR, "pgmReceiverTable_handler: unsupported mode.\n"); + break; + + } + + return SNMP_ERR_NOERROR; +} + +/* + * SNMP TRAPS + */ + +int +send_pgmStart_trap (void) +{ + pgm_debug ("send_pgmStart_trap ()"); + + netsnmp_variable_list *var_list = NULL; + static const oid pgmStart_oid[] = { 1,3,6,1,3,112,2,0,1 }; + +/* + * Set the snmpTrapOid.0 value + */ + snmp_varlist_add_variable (&var_list, + snmptrap_oid, OID_LENGTH( snmptrap_oid ), + ASN_OBJECT_ID, + (const u_char*)pgmStart_oid, sizeof(pgmStart_oid)); +/* + * Add any extra (optional) objects here + */ + +/* + * Send the trap to the list of configured destinations + * and clean up + */ + send_v2trap (var_list); + snmp_free_varbind (var_list); + return SNMP_ERR_NOERROR; +} + +int +send_pgmStop_trap (void) +{ + pgm_debug ("send_pgmStop_trap ()"); + + netsnmp_variable_list *var_list = NULL; + static const oid pgmStop_oid[] = { 1,3,6,1,3,112,2,0,2 }; + +/* + * Set the snmpTrapOid.0 value + */ + snmp_varlist_add_variable (&var_list, + snmptrap_oid, OID_LENGTH(snmptrap_oid), + ASN_OBJECT_ID, + (const u_char*)pgmStop_oid, sizeof(pgmStop_oid)); + + +/* + * Add any extra (optional) objects here + */ + +/* + * Send the trap to the list of configured destinations + * and clean up + */ + send_v2trap (var_list); + snmp_free_varbind (var_list); + return SNMP_ERR_NOERROR; +} + +int +send_pgmNewSourceTrap_trap (void) +{ + pgm_debug ("send_pgmNewSourceTrap_trap ()"); + + netsnmp_variable_list *var_list = NULL; + static const oid pgmNewSourceTrap_oid[] = { 1,3,6,1,3,112,2,0,3 }; + static const oid pgmSourceSourceGsi_oid[] = { 1,3,6,1,3,112,1,2,100,2,1,6, /* insert index here */ }; + static const oid pgmSourceSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,2,100,2,1,7, /* insert index here */ }; + +/* + * Set the snmpTrapOid.0 value + */ + snmp_varlist_add_variable (&var_list, + snmptrap_oid, OID_LENGTH(snmptrap_oid), + ASN_OBJECT_ID, + (const u_char*)pgmNewSourceTrap_oid, sizeof(pgmNewSourceTrap_oid)); +/* + * Add any objects from the trap definition + */ + snmp_varlist_add_variable (&var_list, + pgmSourceSourceGsi_oid, OID_LENGTH(pgmSourceSourceGsi_oid), + ASN_OCTET_STR, +/* Set an appropriate value for pgmSourceSourceGsi */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmSourceSourcePortNumber_oid, OID_LENGTH(pgmSourceSourcePortNumber_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmSourceSourcePortNumber */ + NULL, 0); +/* + * Add any extra (optional) objects here + */ + +/* + * Send the trap to the list of configured destinations + * and clean up + */ + send_v2trap (var_list); + snmp_free_varbind (var_list); + return SNMP_ERR_NOERROR; +} + +int +send_pgmClosedSourceTrap_trap (void) +{ + pgm_debug ("send_pgmClosedSourceTrap_trap ()"); + + netsnmp_variable_list *var_list = NULL; + static const oid pgmClosedSourceTrap_oid[] = { 1,3,6,1,3,112,2,0,4 }; + static const oid pgmSourceSourceGsi_oid[] = { 1,3,6,1,3,112,1,2,100,2,1,6, /* insert index here */ }; + static const oid pgmSourceSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,2,100,2,1,7, /* insert index here */ }; + +/* + * Set the snmpTrapOid.0 value + */ + snmp_varlist_add_variable (&var_list, + snmptrap_oid, OID_LENGTH(snmptrap_oid), + ASN_OBJECT_ID, + (const u_char*)pgmClosedSourceTrap_oid, sizeof(pgmClosedSourceTrap_oid)); +/* + * Add any objects from the trap definition + */ + snmp_varlist_add_variable (&var_list, + pgmSourceSourceGsi_oid, OID_LENGTH(pgmSourceSourceGsi_oid), + ASN_OCTET_STR, +/* Set an appropriate value for pgmSourceSourceGsi */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmSourceSourcePortNumber_oid, OID_LENGTH(pgmSourceSourcePortNumber_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmSourceSourcePortNumber */ + NULL, 0); +/* + * Add any extra (optional) objects here + */ + +/* + * Send the trap to the list of configured destinations + * and clean up + */ + send_v2trap (var_list); + snmp_free_varbind (var_list); + return SNMP_ERR_NOERROR; +} + +int +send_pgmNewReceiverTrap_trap (void) +{ + pgm_debug ("send_pgmNewReceiverTrap_trap ()"); + + netsnmp_variable_list *var_list = NULL; + static const oid pgmNewReceiverTrap_oid[] = { 1,3,6,1,3,112,2,0,5 }; + static const oid pgmReceiverSourceGsi_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,8, /* insert index here */ }; + static const oid pgmReceiverSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,9, /* insert index here */ }; + static const oid pgmReceiverUniqueInstance_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,10, /* insert index here */ }; + +/* + * Set the snmpTrapOid.0 value + */ + snmp_varlist_add_variable (&var_list, + snmptrap_oid, OID_LENGTH(snmptrap_oid), + ASN_OBJECT_ID, + (const u_char*)pgmNewReceiverTrap_oid, sizeof(pgmNewReceiverTrap_oid)); +/* + * Add any objects from the trap definition + */ + snmp_varlist_add_variable (&var_list, + pgmReceiverSourceGsi_oid, OID_LENGTH(pgmReceiverSourceGsi_oid), + ASN_OCTET_STR, +/* Set an appropriate value for pgmReceiverSourceGsi */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmReceiverSourcePortNumber_oid, OID_LENGTH(pgmReceiverSourcePortNumber_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmReceiverSourcePortNumber */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmReceiverUniqueInstance_oid, OID_LENGTH(pgmReceiverUniqueInstance_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmReceiverUniqueInstance */ + NULL, 0); +/* + * Add any extra (optional) objects here + */ + +/* + * Send the trap to the list of configured destinations + * and clean up + */ + send_v2trap (var_list); + snmp_free_varbind (var_list); + return SNMP_ERR_NOERROR; +} + +int +send_pgmClosedReceiverTrap_trap (void) +{ + pgm_debug ("send_pgmClosedReceiverTrap_trap ()"); + + netsnmp_variable_list *var_list = NULL; + static const oid pgmClosedReceiverTrap_oid[] = { 1,3,6,1,3,112,2,0,6 }; + static const oid pgmReceiverSourceGsi_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,8, /* insert index here */ }; + static const oid pgmReceiverSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,9, /* insert index here */ }; + static const oid pgmReceiverUniqueInstance_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,10, /* insert index here */ }; + +/* + * Set the snmpTrapOid.0 value + */ + snmp_varlist_add_variable (&var_list, + snmptrap_oid, OID_LENGTH(snmptrap_oid), + ASN_OBJECT_ID, + (const u_char*)pgmClosedReceiverTrap_oid, sizeof(pgmClosedReceiverTrap_oid)); +/* + * Add any objects from the trap definition + */ + snmp_varlist_add_variable (&var_list, + pgmReceiverSourceGsi_oid, OID_LENGTH(pgmReceiverSourceGsi_oid), + ASN_OCTET_STR, +/* Set an appropriate value for pgmReceiverSourceGsi */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmReceiverSourcePortNumber_oid, OID_LENGTH(pgmReceiverSourcePortNumber_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmReceiverSourcePortNumber */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmReceiverUniqueInstance_oid, OID_LENGTH(pgmReceiverUniqueInstance_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmReceiverUniqueInstance */ + NULL, 0); +/* + * Add any extra (optional) objects here + */ + +/* + * Send the trap to the list of configured destinations + * and clean up + */ + send_v2trap (var_list); + snmp_free_varbind (var_list); + return SNMP_ERR_NOERROR; +} + +int +send_pgmNakFailuresTrap_trap (void) +{ + pgm_debug ("send_pgmNakFailuresTrap_trap ()"); + + netsnmp_variable_list *var_list = NULL; + static const oid pgmNakFailuresTrap_oid[] = { 1,3,6,1,3,112,2,0,7 }; + static const oid pgmReceiverSourceGsi_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,8, /* insert index here */ }; + static const oid pgmReceiverSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,9, /* insert index here */ }; + static const oid pgmReceiverUniqueInstance_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,10, /* insert index here */ }; + static const oid pgmReceiverNakFailureThresholdTimer_oid[] = { 1,3,6,1,3,112,1,3,100,3,1,14, /* insert index here */ }; + static const oid pgmReceiverNakFailureThreshold_oid[] = { 1,3,6,1,3,112,1,3,100,3,1,15, /* insert index here */ }; + static const oid pgmReceiverNakFailuresLastInterval_oid[] = { 1,3,6,1,3,112,1,3,100,4,1,56, /* insert index here */ }; + static const oid pgmReceiverLastIntervalNakFailures_oid[] = { 1,3,6,1,3,112,1,3,100,4,1,57, /* insert index here */ }; + +/* + * Set the snmpTrapOid.0 value + */ + snmp_varlist_add_variable (&var_list, + snmptrap_oid, OID_LENGTH(snmptrap_oid), + ASN_OBJECT_ID, + (const u_char*)pgmNakFailuresTrap_oid, sizeof(pgmNakFailuresTrap_oid)); +/* + * Add any objects from the trap definition + */ + snmp_varlist_add_variable (&var_list, + pgmReceiverSourceGsi_oid, OID_LENGTH(pgmReceiverSourceGsi_oid), + ASN_OCTET_STR, +/* Set an appropriate value for pgmReceiverSourceGsi */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmReceiverSourcePortNumber_oid, OID_LENGTH(pgmReceiverSourcePortNumber_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmReceiverSourcePortNumber */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmReceiverUniqueInstance_oid, OID_LENGTH(pgmReceiverUniqueInstance_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmReceiverUniqueInstance */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmReceiverNakFailureThresholdTimer_oid, OID_LENGTH(pgmReceiverNakFailureThresholdTimer_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmReceiverNakFailureThresholdTimer */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmReceiverNakFailureThreshold_oid, OID_LENGTH(pgmReceiverNakFailureThreshold_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmReceiverNakFailureThreshold */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmReceiverNakFailuresLastInterval_oid, OID_LENGTH(pgmReceiverNakFailuresLastInterval_oid), + ASN_COUNTER, +/* Set an appropriate value for pgmReceiverNakFailuresLastInterval */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmReceiverLastIntervalNakFailures_oid, OID_LENGTH(pgmReceiverLastIntervalNakFailures_oid), + ASN_COUNTER, +/* Set an appropriate value for pgmReceiverLastIntervalNakFailures */ + NULL, 0); +/* + * Add any extra (optional) objects here + */ + +/* + * Send the trap to the list of configured destinations + * and clean up + */ + send_v2trap (var_list); + snmp_free_varbind (var_list); + return SNMP_ERR_NOERROR; +} + +int +send_pgmNewDlrSourceTrap_trap (void) +{ + pgm_debug ("send_pgmNewDlrSourceTrap_trap ()"); + + netsnmp_variable_list *var_list = NULL; + static const oid pgmNewDlrSourceTrap_oid[] = { 1,3,6,1,3,112,2,0,8 }; + static const oid pgmDlrSourceSourceGsi_oid[] = { 1,3,6,1,3,112,1,4,100,2,1,4, /* insert index here */ }; + static const oid pgmDlrSourceSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,4,100,2,1,5, /* insert index here */ }; + +/* + * Set the snmpTrapOid.0 value + */ + snmp_varlist_add_variable (&var_list, + snmptrap_oid, OID_LENGTH(snmptrap_oid), + ASN_OBJECT_ID, + (const u_char*)pgmNewDlrSourceTrap_oid, sizeof(pgmNewDlrSourceTrap_oid)); +/* + * Add any objects from the trap definition + */ + snmp_varlist_add_variable (&var_list, + pgmDlrSourceSourceGsi_oid, OID_LENGTH(pgmDlrSourceSourceGsi_oid), + ASN_OCTET_STR, +/* Set an appropriate value for pgmDlrSourceSourceGsi */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmDlrSourceSourcePortNumber_oid, OID_LENGTH(pgmDlrSourceSourcePortNumber_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmDlrSourceSourcePortNumber */ + NULL, 0); +/* + * Add any extra (optional) objects here + */ + +/* + * Send the trap to the list of configured destinations + * and clean up + */ + send_v2trap (var_list); + snmp_free_varbind (var_list); + return SNMP_ERR_NOERROR; +} + +int +send_pgmClosedDlrSourceTrap_trap (void) +{ + pgm_debug ("send_pgmClosedDlrSourceTrap_trap ()"); + + netsnmp_variable_list *var_list = NULL; + static const oid pgmClosedDlrSourceTrap_oid[] = { 1,3,6,1,3,112,2,0,9 }; + static const oid pgmDlrSourceSourceGsi_oid[] = { 1,3,6,1,3,112,1,4,100,2,1,4, /* insert index here */ }; + static const oid pgmDlrSourceSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,4,100,2,1,5, /* insert index here */ }; + +/* + * Set the snmpTrapOid.0 value + */ + snmp_varlist_add_variable (&var_list, + snmptrap_oid, OID_LENGTH(snmptrap_oid), + ASN_OBJECT_ID, + (const u_char*)pgmClosedDlrSourceTrap_oid, sizeof(pgmClosedDlrSourceTrap_oid)); + +/* + * Add any objects from the trap definition + */ + snmp_varlist_add_variable (&var_list, + pgmDlrSourceSourceGsi_oid, OID_LENGTH(pgmDlrSourceSourceGsi_oid), + ASN_OCTET_STR, +/* Set an appropriate value for pgmDlrSourceSourceGsi */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmDlrSourceSourcePortNumber_oid, OID_LENGTH(pgmDlrSourceSourcePortNumber_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmDlrSourceSourcePortNumber */ + NULL, 0); +/* + * Add any extra (optional) objects here + */ + +/* + * Send the trap to the list of configured destinations + * and clean up + */ + send_v2trap (var_list); + snmp_free_varbind (var_list); + return SNMP_ERR_NOERROR; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/pgmMIB_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/pgmMIB_unittest.c new file mode 100644 index 0000000..4cb4672 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/pgmMIB_unittest.c @@ -0,0 +1,257 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for PGM MIB routines. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include + + +/* mock state */ + +static GStaticRWLock mock_pgm_transport_list_lock = G_STATIC_RW_LOCK_INIT; +static GSList* mock_pgm_transport_list = NULL; + + +/* mock functions for external references */ + +static +netsnmp_handler_registration* +mock_netsnmp_create_handler_registration ( + const char* name, + Netsnmp_Node_Handler* handler_access_method, + oid* reg_oid, + size_t reg_oid_len, + int modes + ) +{ + netsnmp_handler_registration* handler = g_malloc0 (sizeof(netsnmp_handler_registration)); + return handler; +} + +static +void +mock_netsnmp_handler_registration_free ( + netsnmp_handler_registration* handler + ) +{ + g_assert (NULL != handler); + g_free (handler); +} + +static +void +mock_netsnmp_table_helper_add_indexes ( + netsnmp_table_registration_info* tinfo, + ... + ) +{ +} + +static +int +mock_netsnmp_register_table_iterator ( + netsnmp_handler_registration* reginfo, + netsnmp_iterator_info* iinfo + ) +{ + return MIB_REGISTERED_OK; +} + +static +int +mock_netsnmp_set_request_error ( + netsnmp_agent_request_info* reqinfo, + netsnmp_request_info* request, + int error_value + ) +{ + return 0; +} + +static +void* +mock_netsnmp_extract_iterator_context ( + netsnmp_request_info* reqinfo + ) +{ + return (void*)0x1; +} + +static +netsnmp_table_request_info* +mock_netsnmp_extract_table_info ( + netsnmp_request_info* reqinfo + ) +{ + return NULL; +} + +static +int +mock_snmp_set_var_typed_value ( + netsnmp_variable_list* newvar, + u_char type, + const u_char* val_str, + size_t val_len + ) +{ + return 0; +} + +static +netsnmp_variable_list* +mock_snmp_varlist_add_variable ( + netsnmp_variable_list** varlist, + const oid* oid, + size_t name_length, + u_char type, + const u_char* value, + size_t len + ) +{ + return NULL; +} + +static +void +mock_snmp_free_varbind ( + netsnmp_variable_list* var + ) +{ +} + +static +void +mock_snmp_free_var ( + netsnmp_variable_list* var + ) +{ +} + +static +int +mock_snmp_log ( + int priority, + const char* format, + ... + ) +{ + return 0; +} + +static +void +mock_send_v2trap ( + netsnmp_variable_list* var + ) +{ +} + +/** time module */ + +static +void +mock_pgm_time_since_epoch ( + pgm_time_t* pgm_time_t_time, + time_t* time_t_time + ) +{ + *time_t_time = pgm_to_secs (*pgm_time_t_time + 0); +} + + +#define netsnmp_create_handler_registration mock_netsnmp_create_handler_registration +#define netsnmp_handler_registration_free mock_netsnmp_handler_registration_free +#define netsnmp_table_helper_add_indexes mock_netsnmp_table_helper_add_indexes +#define netsnmp_register_table_iterator mock_netsnmp_register_table_iterator +#define netsnmp_set_request_error mock_netsnmp_set_request_error +#define netsnmp_extract_iterator_context mock_netsnmp_extract_iterator_context +#define netsnmp_extract_table_info mock_netsnmp_extract_table_info +#define snmp_set_var_typed_value mock_snmp_set_var_typed_value +#define snmp_varlist_add_variable mock_snmp_varlist_add_variable +#define snmp_free_varbind mock_snmp_free_varbind +#define snmp_free_var mock_snmp_free_var +#define snmp_log mock_snmp_log +#define send_v2trap mock_send_v2trap +#define pgm_transport_list mock_pgm_transport_list +#define pgm_transport_list_lock mock_pgm_transport_list_lock +#define pgm_time_since_epoch mock_pgm_time_since_epoch + +#define PGMMIB_DEBUG +#include "pgmMIB.c" + + +/* target: + * gboolean + * pgm_mib_init ( + * GError** error + * ) + */ + +START_TEST (test_init_pass_001) +{ + GError* err = NULL; + fail_unless (TRUE == pgm_mib_init (&err)); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_init = tcase_create ("init"); + suite_add_tcase (s, tc_init); + tcase_add_test (tc_init, test_init_pass_001); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/plan.txt b/3rdparty/openpgm-svn-r1085/pgm/plan.txt new file mode 100644 index 0000000..b1747ae --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/plan.txt @@ -0,0 +1,238 @@ +pgmdump +------- + +View all packets like tcpdump, but updated to full spec and allow dump of payload. + + +pgmtop +------ + +Dump realtime packet statistics in a ncurses display, mix of top/htop/netop. + + +basic_send +---------- + +Send an ODATA packet and terminate. + +Accept string payload and network parameters on command line. + +Send to multicast or send to unicast AFI. + +IPv4/6. + +Define optional session start, late join tags. + + +spm_idle +-------- + +Idle in an event loop sending out SPM packets. + + +stream_send +----------- + +Send a constant stream of ODATA and SPM packets. + + +basic_http +---------- + +Simple embedded web server + + +basic_recv +---------- + +Listen to packets indicating data loss, view details through web interface. + + +basic_container +--------------- + +Test performance of glib containers for fast allocating for a dynamic transmit window. + + +basic_txw +--------- + +Test performance of a basic transmit window implementation. + + +nak_txw +------- + +Test performance of random access to packets inside the window. + + +stream_send_with_nak +-------------------- + +Respond to NAK's with RDATA. + + +basic_recv_with_nak +------------------- + +Listen to packets and send NAK's to rebuild data. + + +dumpif +------ + +Display all IP based interfaces and basic details. + + +testif +------ + +Test various combinations of network specification. + + +sw_calc +------- + +Basic calculation tests of wrap-around sliding windows and a leading edge. + + +basic_recv_with_rxw +------------------- + +Listen to packets buffered with a receive window. + + +test_cpu_timers +--------------- + +Calculate drift between processors, cores, and hyper-threads. + + +pgmsend +-------- + +basic_send updated to use transmit window. + + +pgmrecv +-------- + +basic_recv_with_rxw without web interface, primary displays messages from pgmsend. + + +syncrecv +-------- + +pgmrecv implemented outside GLib with a synchronous coding paradigm. + + +pgmping +------- + +Dual mode: one to send fixed messages like pgmsend and listen for response, two to listen for +messages and reply. + + +block_send +---------- + +Send APDUs over ODATA. + + +(pgmrecv can receive APDUs) + +test_rs +------- + +Test 8-bit symbol Reed Solomon encoding and decoding with errors & erasures. + +test_fec +-------- + +Test fec creation and recovery. + + +send_with_fec +-------------------- + +Send APDUs over ODATA with FEC. + + + +Scenarios to reproduce +********************** + +- Packet loss in stream causing NAK generation. +- Link saturation in sending causing API feedback. +- Link peak stable speed. +- Maxium NAK generation to determine NCF/RDATA throughput. +- Corrupt packets with invalid IP checksum (? generate IP HDR in sender) +- Corrupt packets with invalid PGM checksum. +- Invalid packet values. +- NAK to NCF latency. +- NAK to RDATA latency. +- Publish bandwidth: total, per packet type, payload, per recipient (?) +- Subscribe bandwidth: total, per packet type, payload, per publisher (?) +- Restarting a session with similar or dissimilar sequence numbering. + +Outstanding questions +********************* + +- Is it faster to use chunks containing multiple packets instead of one MTU + per packet. Does aligning with system page size matter? +- Can g_timers be dropped easily for gettimeofday and avoid floating point math? Possible + to pass timer upstream with contiguous data for easy access. +- Can time evaluation be dropped to at most once per main loop event? +- Does code work 32 bit and is it optimal? +- Should trash stacks be monitored and controlled externally? For example, clearing + up after bursts or administrative control. +- Should trash stacks have a upper limit to then free to the slice allocator? +- Should lost packets be managed as ranges or individual sequence numbers, how + does each method affect performance? + +* The initial draft of PGM included OPT_RANGE option for NAKs to specify a range of lost + packets, this was replaced in the final draft with NAK lists. Some research hints that + ranges are suitable: + + http://www.isoc.org/inet2001/CD_proceedings/T54/T54.htm + http://tools.ietf.org/html/draft-speakman-pgm-spec-01 + +- Are place holders necessary? Can state timeouts be managed without a per sequence number + object? For example by the next data object, or an extra object for an ncf extended window: + note that nak packet generation should easily dwarfs time spent unless advantage is taken + of the additional 62 naks in a opt_nak_list. Caution has to be taken with the cost of + splitting a range when a packet is inserted in the middle, although idealy it should + be sequential from the trailing edge. +- Is it better to have send sockets per transport, or shared, bound to each interface? +- Cost of sharing state helper lists between receive windows? On culling a peer the lists + have to be purged. Saves iterating the hash list of receivers. +- Encapsulated UDP lists two ports, 3305 for broadcast, 3306 for unicast, how is this + supposed to map to regular PGM, and why the split? + +Basic TODO list +*************** + +- Ensure standardised error handling and status reporting. +- Implement mechanism for data-loss feedback. +- OPT_SYN & OPT_FIN on sending side. +- Shared trash stacks between multiple receive windows (contexts). +- Shared trash stacks between transmit and receive windows. +- FEC: compatibility with SmartPGM FEC, MS FEC? +- NAK backoff learning. +- Full conformance testing. (nak backoffs remaining) +- Unit testing. +- System testing with valgrind. +- Performance testing with oprofile. +- Basic DLR. +- Implement PGM sender (only) support thread? +- eventfd instead of pipes for recent Linux kernels. + +Optionals +********* + +- Some form of broadcast statistics for passive monitoring. +- (fix) BCH Reed-Solomon codec as possibly faster alternative, albeit incompatible with Microsoft. +- Recommendations as per the RMT working group of the IETF for AL-FEC codecs: Raptor codes, LDPC- + staircase and LDPC-triangle codes. +- XDR based messaging format as example of binary encoded messaging. + diff --git a/3rdparty/openpgm-svn-r1085/pgm/queue.c b/3rdparty/openpgm-svn-r1085/pgm/queue.c new file mode 100644 index 0000000..351c7ef --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/queue.c @@ -0,0 +1,110 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable double-ended queue. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + + +//#define QUEUE_DEBUG + +bool +pgm_queue_is_empty ( + const pgm_queue_t*const queue + ) +{ + pgm_return_val_if_fail (queue != NULL, TRUE); + + return queue->head == NULL; +} + +void +pgm_queue_push_head_link ( + pgm_queue_t* restrict queue, + pgm_list_t* restrict head_link + ) +{ + pgm_return_if_fail (queue != NULL); + pgm_return_if_fail (head_link != NULL); + pgm_return_if_fail (head_link->prev == NULL); + pgm_return_if_fail (head_link->next == NULL); + + head_link->next = queue->head; + if (queue->head) + queue->head->prev = head_link; + else + queue->tail = head_link; + queue->head = head_link; + queue->length++; +} + +pgm_list_t* +pgm_queue_pop_tail_link ( + pgm_queue_t* queue + ) +{ + pgm_return_val_if_fail (queue != NULL, NULL); + + if (queue->tail) + { + pgm_list_t *node = queue->tail; + + queue->tail = node->prev; + if (queue->tail) + { + queue->tail->next = NULL; + node->prev = NULL; + } + else + queue->head = NULL; + queue->length--; + + return node; + } + + return NULL; +} + +pgm_list_t* +pgm_queue_peek_tail_link ( + pgm_queue_t* queue + ) +{ + pgm_return_val_if_fail (queue != NULL, NULL); + + return queue->tail; +} + +void +pgm_queue_unlink ( + pgm_queue_t* restrict queue, + pgm_list_t* restrict target_link + ) +{ + pgm_return_if_fail (queue != NULL); + pgm_return_if_fail (target_link != NULL); + + if (target_link == queue->tail) + queue->tail = queue->tail->prev; + + queue->head = pgm_list_remove_link (queue->head, target_link); + queue->length--; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/rand.c b/3rdparty/openpgm-svn-r1085/pgm/rand.c new file mode 100644 index 0000000..91b71eb --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/rand.c @@ -0,0 +1,137 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable weak pseudo-random generator. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _WIN32 +# include +# include +#endif +#include + + +//#define RAND_DEBUG + + +/* locals */ + +static pgm_rand_t global_rand = { .seed = 0 }; +static volatile uint32_t rand_ref_count = 0; +static pgm_mutex_t rand_mutex; + + +void +pgm_rand_init (void) +{ + if (pgm_atomic_exchange_and_add32 (&rand_ref_count, 1) > 0) + return; + + pgm_mutex_init (&rand_mutex); +} + +void +pgm_rand_shutdown (void) +{ + pgm_return_if_fail (pgm_atomic_read32 (&rand_ref_count) > 0); + + if (pgm_atomic_exchange_and_add32 (&rand_ref_count, (uint32_t)-1) != 1) + return; + + pgm_mutex_free (&rand_mutex); +} + +void +pgm_rand_create ( + pgm_rand_t* new_rand + ) +{ +/* pre-conditions */ + pgm_assert (NULL != new_rand); + +#ifndef _WIN32 +/* attempt to read seed from kernel + */ + FILE* fp; + do { + fp = fopen ("/dev/urandom", "rb"); + } while (PGM_UNLIKELY(EINTR == errno)); + if (fp) { + size_t items_read; + do { + items_read = fread (&new_rand->seed, sizeof(new_rand->seed), 1, fp); + } while (PGM_UNLIKELY(EINTR == errno)); + fclose (fp); + if (1 == items_read) + return; + } +#endif /* !_WIN32 */ + const pgm_time_t now = pgm_time_update_now(); + new_rand->seed = (uint32_t)pgm_to_msecs (now); +} + +/* derived from POSIX.1-2001 example implementation of rand() + */ + +uint32_t +pgm_rand_int ( + pgm_rand_t* r + ) +{ +/* pre-conditions */ + pgm_assert (NULL != r); + + r->seed = r->seed * 1103515245 + 12345; + return r->seed; +} + +int32_t +pgm_rand_int_range ( + pgm_rand_t* r, + int32_t begin, + int32_t end + ) +{ +/* pre-conditions */ + pgm_assert (NULL != r); + + return begin + pgm_rand_int (r) % (end - begin); +} + +uint32_t +pgm_random_int (void) +{ + pgm_mutex_lock (&rand_mutex); + if (PGM_UNLIKELY(!global_rand.seed)) + pgm_rand_create (&global_rand); + const uint32_t rand_value = pgm_rand_int (&global_rand); + pgm_mutex_unlock (&rand_mutex); + return rand_value; +} + +int32_t +pgm_random_int_range ( + int32_t begin, + int32_t end + ) +{ + const uint32_t rand_value = pgm_random_int(); + return begin + rand_value % (end - begin); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/rate_control.c b/3rdparty/openpgm-svn-r1085/pgm/rate_control.c new file mode 100644 index 0000000..2baceeb --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/rate_control.c @@ -0,0 +1,158 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Rate regulation. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + + +/* create machinery for rate regulation. + * the rate_per_sec is ammortized over millisecond time periods. + * + * NB: bucket MUST be memset 0 before calling. + */ + +void +pgm_rate_create ( + pgm_rate_t* bucket, + const ssize_t rate_per_sec, /* 0 = disable */ + const size_t iphdr_len, + const uint16_t max_tpdu + ) +{ +/* pre-conditions */ + pgm_assert (NULL != bucket); + pgm_assert (rate_per_sec >= max_tpdu); + + bucket->rate_per_sec = rate_per_sec; + bucket->iphdr_len = iphdr_len; + bucket->last_rate_check = pgm_time_update_now (); +/* pre-fill bucket */ + if ((rate_per_sec / 1000) >= max_tpdu) { + bucket->rate_per_msec = bucket->rate_per_sec / 1000; + bucket->rate_limit = bucket->rate_per_msec; + } else { + bucket->rate_limit = bucket->rate_per_sec; + } + pgm_spinlock_init (&bucket->spinlock); +} + +void +pgm_rate_destroy ( + pgm_rate_t* bucket + ) +{ +/* pre-conditions */ + pgm_assert (NULL != bucket); + + pgm_spinlock_free (&bucket->spinlock); +} + +/* check bit bucket whether an operation can proceed or should wait. + * + * returns TRUE when leaky bucket permits unless non-blocking flag is set. + * returns FALSE if operation should block and non-blocking flag is set. + */ + +bool +pgm_rate_check ( + pgm_rate_t* bucket, + const size_t data_size, + const bool is_nonblocking + ) +{ + int new_rate_limit; + +/* pre-conditions */ + pgm_assert (NULL != bucket); + pgm_assert (data_size > 0); + + if (0 == bucket->rate_per_sec) + return TRUE; + + pgm_spinlock_lock (&bucket->spinlock); + pgm_time_t now = pgm_time_update_now(); + pgm_time_t time_since_last_rate_check = now - bucket->last_rate_check; + + if (bucket->rate_per_msec) + { + if (time_since_last_rate_check > pgm_msecs(1)) + new_rate_limit = bucket->rate_per_msec; + else { + new_rate_limit = bucket->rate_limit + ((bucket->rate_per_msec * time_since_last_rate_check) / 1000UL); + if (new_rate_limit > bucket->rate_per_msec) + new_rate_limit = bucket->rate_per_msec; + } + } + else + { + if (time_since_last_rate_check > pgm_secs(1)) + new_rate_limit = bucket->rate_per_sec; + else { + new_rate_limit = bucket->rate_limit + ((bucket->rate_per_sec * time_since_last_rate_check) / 1000000UL); + if (new_rate_limit > bucket->rate_per_sec) + new_rate_limit = bucket->rate_per_sec; + } + } + + new_rate_limit -= ( bucket->iphdr_len + data_size ); + if (is_nonblocking && new_rate_limit < 0) { + pgm_spinlock_unlock (&bucket->spinlock); + return FALSE; + } + + bucket->rate_limit = new_rate_limit; + bucket->last_rate_check = now; + if (bucket->rate_limit < 0) { + int sleep_amount; + do { + pgm_thread_yield(); + now = pgm_time_update_now(); + time_since_last_rate_check = now - bucket->last_rate_check; + sleep_amount = pgm_to_secs (bucket->rate_per_sec * time_since_last_rate_check); + } while (sleep_amount + bucket->rate_limit < 0); + bucket->rate_limit += sleep_amount; + bucket->last_rate_check = now; + } + pgm_spinlock_unlock (&bucket->spinlock); + return TRUE; +} + +pgm_time_t +pgm_rate_remaining ( + pgm_rate_t* bucket, + const size_t n + ) +{ +/* pre-conditions */ + pgm_assert (NULL != bucket); + + if (PGM_UNLIKELY(0 == bucket->rate_per_sec)) + return 0; + + pgm_spinlock_lock (&bucket->spinlock); + const pgm_time_t now = pgm_time_update_now(); + const pgm_time_t time_since_last_rate_check = now - bucket->last_rate_check; + const int bucket_bytes = bucket->rate_limit + pgm_to_secs (bucket->rate_per_sec * time_since_last_rate_check) - n; + pgm_spinlock_unlock (&bucket->spinlock); + + return bucket_bytes >= 0 ? 0 : (bucket->rate_per_sec / -bucket_bytes); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/rate_control_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/rate_control_unittest.c new file mode 100644 index 0000000..7da5128 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/rate_control_unittest.c @@ -0,0 +1,241 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for rate regulation. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include +#include +#include + + +/* mock state */ + + +#define pgm_time_now mock_pgm_time_now +#define pgm_time_update_now mock_pgm_time_update_now + +#define RATE_CONTROL_DEBUG +#include "rate_control.c" + +static pgm_time_t mock_pgm_time_now = 0x1; +static pgm_time_t _mock_pgm_time_update_now (void); +pgm_time_update_func mock_pgm_time_update_now = _mock_pgm_time_update_now; + + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + +static +pgm_time_t +_mock_pgm_time_update_now (void) +{ + g_debug ("mock_pgm_time_now: %" PGM_TIME_FORMAT, mock_pgm_time_now); + return mock_pgm_time_now; +} + + +/* target: + * void + * pgm_rate_create ( + * pgm_rate_t* bucket_, + * const ssize_t rate_per_sec, + * const size_t iphdr_len, + * const uint16_t max_tpdu + * ) + */ + +START_TEST (test_create_pass_001) +{ + pgm_rate_t rate; + memset (&rate, 0, sizeof(rate)); + pgm_rate_create (&rate, 100*1000, 10, 1500); +} +END_TEST + +START_TEST (test_create_fail_001) +{ + pgm_rate_create (NULL, 0, 0, 1500); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_rate_destroy ( + * pgm_rate_t* bucket + * ) + */ + +START_TEST (test_destroy_pass_001) +{ + pgm_rate_t rate; + memset (&rate, 0, sizeof(rate)); + pgm_rate_create (&rate, 100*1000, 10, 1500); + pgm_rate_destroy (&rate); +} +END_TEST + +START_TEST (test_destroy_fail_001) +{ + pgm_rate_destroy (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_rate_check ( + * pgm_rate_t* bucket, + * const size_t data_size, + * const bool is_nonblocking + * ) + * + * 001: should use seconds resolution to allow 2 packets through then fault. + */ + +START_TEST (test_check_pass_001) +{ + pgm_rate_t rate; + memset (&rate, 0, sizeof(rate)); + pgm_rate_create (&rate, 2*1010, 10, 1500); + mock_pgm_time_now += pgm_secs(2); + fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + pgm_rate_destroy (&rate); +} +END_TEST + +START_TEST (test_check_fail_001) +{ + pgm_rate_check (NULL, 1000, FALSE); + fail ("reached"); +} +END_TEST + +/* 002: assert that only one packet should pass through small bucket + */ + +START_TEST (test_check_pass_002) +{ + pgm_rate_t rate; + memset (&rate, 0, sizeof(rate)); + pgm_rate_create (&rate, 2*900, 10, 1500); + mock_pgm_time_now += pgm_secs(2); + fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + pgm_rate_destroy (&rate); +} +END_TEST + +/* 003: millisecond resolution should initiate millisecond fills. + */ + +START_TEST (test_check_pass_003) +{ + pgm_rate_t rate; + memset (&rate, 0, sizeof(rate)); + pgm_rate_create (&rate, 2*1010*1000, 10, 1500); + mock_pgm_time_now += pgm_secs(2); + fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); +/* duplicate check at same time point */ + fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); +/* advance time causing a millisecond fill to occur */ + mock_pgm_time_now += pgm_msecs(1); + fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); +/* advance time to fill bucket enough for only one packet */ + mock_pgm_time_now += pgm_usecs(500); + fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); +/* advance time to fill the bucket a little but not enough for one packet */ + mock_pgm_time_now += pgm_usecs(100); + fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); +/* advance time a lot, should be limited to millisecond fill rate */ + mock_pgm_time_now += pgm_secs(10); + fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + pgm_rate_destroy (&rate); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_create = tcase_create ("create"); + suite_add_tcase (s, tc_create); + tcase_add_test (tc_create, test_create_pass_001); + tcase_add_test_raise_signal (tc_create, test_create_fail_001, SIGABRT); + + TCase* tc_destroy = tcase_create ("destroy"); + suite_add_tcase (s, tc_destroy); + tcase_add_test (tc_destroy, test_destroy_pass_001); + tcase_add_test_raise_signal (tc_destroy, test_destroy_fail_001, SIGABRT); + + TCase* tc_check = tcase_create ("check"); + suite_add_tcase (s, tc_check); + tcase_add_test (tc_check, test_check_pass_001); + tcase_add_test (tc_check, test_check_pass_002); + tcase_add_test (tc_check, test_check_pass_003); + tcase_add_test_raise_signal (tc_check, test_check_fail_001, SIGABRT); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/receiver.c b/3rdparty/openpgm-svn-r1085/pgm/receiver.c new file mode 100644 index 0000000..8f26353 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/receiver.c @@ -0,0 +1,2268 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM receiver socket. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +//#define RECEIVER_DEBUG +//#define SPM_DEBUG + +#ifndef RECEIVER_DEBUG +# define PGM_DISABLE_ASSERT +#endif + +#if !defined(ENOBUFS) && defined(WSAENOBUFS) +# define ENOBUFS WSAENOBUFS +#endif +#if !defined(ECONNRESET) && defined(WSAECONNRESET) +# define ECONNRESET WSAECONNRESET +#endif + +static bool send_spmr (pgm_sock_t*const restrict, pgm_peer_t*const restrict); +static bool send_nak (pgm_sock_t*const restrict, pgm_peer_t*const restrict, const uint32_t); +static bool send_parity_nak (pgm_sock_t*const restrict, pgm_peer_t*const restrict, const unsigned, const unsigned); +static bool send_nak_list (pgm_sock_t*const restrict, pgm_peer_t*const restrict, const struct pgm_sqn_list_t*const restrict); +static bool nak_rb_state (pgm_peer_t*, const pgm_time_t); +static void nak_rpt_state (pgm_peer_t*, const pgm_time_t); +static void nak_rdata_state (pgm_peer_t*, const pgm_time_t); +static inline pgm_peer_t* _pgm_peer_ref (pgm_peer_t*); +static bool on_general_poll (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict); +static bool on_dlr_poll (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict); + + +/* helpers for pgm_peer_t */ +static inline +pgm_time_t +next_ack_rb_expiry ( + const pgm_rxw_t* window + ) +{ + pgm_assert (NULL != window); + pgm_assert (NULL != window->ack_backoff_queue.tail); + + const struct pgm_peer_t* peer = (const struct pgm_peer_t*)window->ack_backoff_queue.tail; + return peer->ack_rb_expiry; +} + +static inline +pgm_time_t +next_nak_rb_expiry ( + const pgm_rxw_t* window + ) +{ + pgm_assert (NULL != window); + pgm_assert (NULL != window->nak_backoff_queue.tail); + + const struct pgm_sk_buff_t* skb = (const struct pgm_sk_buff_t*)window->nak_backoff_queue.tail; + const pgm_rxw_state_t* state = (const pgm_rxw_state_t*)&skb->cb; + return state->timer_expiry; +} + +static inline +pgm_time_t +next_nak_rpt_expiry ( + const pgm_rxw_t* window + ) +{ + pgm_assert (NULL != window); + pgm_assert (NULL != window->wait_ncf_queue.tail); + + const struct pgm_sk_buff_t* skb = (const struct pgm_sk_buff_t*)window->wait_ncf_queue.tail; + const pgm_rxw_state_t* state = (const pgm_rxw_state_t*)&skb->cb; + return state->timer_expiry; +} + +static inline +pgm_time_t +next_nak_rdata_expiry ( + const pgm_rxw_t* window + ) +{ + pgm_assert (NULL != window); + pgm_assert (NULL != window->wait_data_queue.tail); + + const struct pgm_sk_buff_t* skb = (const struct pgm_sk_buff_t*)window->wait_data_queue.tail; + const pgm_rxw_state_t* state = (const pgm_rxw_state_t*)&skb->cb; + return state->timer_expiry; +} + +/* calculate ACK_RB_IVL. + */ +static inline +uint32_t +ack_rb_ivl ( + pgm_sock_t* sock + ) /* not const as rand() updates the seed */ +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert_cmpuint (sock->ack_bo_ivl, >, 1); + + return pgm_rand_int_range (&sock->rand_, 1 /* us */, sock->ack_bo_ivl); +} + +/* calculate NAK_RB_IVL as random time interval 1 - NAK_BO_IVL. + */ +static inline +uint32_t +nak_rb_ivl ( + pgm_sock_t* sock + ) /* not const as rand() updates the seed */ +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert_cmpuint (sock->nak_bo_ivl, >, 1); + + return pgm_rand_int_range (&sock->rand_, 1 /* us */, sock->nak_bo_ivl); +} + +/* mark sequence as recovery failed. + */ + +static +void +cancel_skb ( + pgm_sock_t* restrict sock, + pgm_peer_t* restrict peer, + const struct pgm_sk_buff_t* restrict skb, + const pgm_time_t now + ) +{ + pgm_assert (NULL != sock); + pgm_assert (NULL != peer); + pgm_assert (NULL != skb); + pgm_assert_cmpuint (now, >=, skb->tstamp); + + pgm_trace (PGM_LOG_ROLE_RX_WINDOW, _("Lost data #%u due to cancellation."), skb->sequence); + + const uint32_t fail_time = now - skb->tstamp; + if (!peer->max_fail_time) + peer->max_fail_time = peer->min_fail_time = fail_time; + else if (fail_time > peer->max_fail_time) + peer->max_fail_time = fail_time; + else if (fail_time < peer->min_fail_time) + peer->min_fail_time = fail_time; + + pgm_rxw_lost (peer->window, skb->sequence); + PGM_HISTOGRAM_TIMES("Rx.FailTime", fail_time); + +/* mark receiver window for flushing on next recv() */ + pgm_peer_set_pending (sock, peer); +} + +/* check whether this receiver is the designated acker for the source + */ + +static inline +bool +_pgm_is_acker ( + const pgm_peer_t* restrict peer, + const struct pgm_sk_buff_t* restrict skb + ) +{ + struct sockaddr_storage acker_nla; + +/* pre-conditions */ + pgm_assert (NULL != peer); + pgm_assert (NULL != skb); + pgm_assert (NULL != skb->pgm_opt_pgmcc_data); + + pgm_nla_to_sockaddr (&skb->pgm_opt_pgmcc_data->opt_nla_afi, (struct sockaddr*)&acker_nla); + return (0 == pgm_sockaddr_cmp ((struct sockaddr*)&acker_nla, (struct sockaddr*)&peer->sock->send_addr)); +} + +/* is the source holding an acker election + */ + +static inline +bool +_pgm_is_acker_election ( + const struct pgm_sk_buff_t* restrict skb + ) +{ + pgm_assert (NULL != skb); + pgm_assert (NULL != skb->pgm_opt_pgmcc_data); + + const unsigned acker_afi = ntohs (skb->pgm_opt_pgmcc_data->opt_nla_afi); + switch (acker_afi) { + case AFI_IP: + if (INADDR_ANY == skb->pgm_opt_pgmcc_data->opt_nla.s_addr) + return TRUE; + break; + + case AFI_IP6: + if (0 == memcmp (&skb->pgm_opt_pgmcc_data->opt_nla, &in6addr_any, sizeof(in6addr_any))) + return TRUE; + break; + + default: break; + } + + return FALSE; +} + +/* add state for an ACK on a data packet. + */ + +static inline +void +_pgm_add_ack ( + pgm_peer_t* const restrict peer, + const pgm_time_t ack_rb_expiry + ) +{ + peer->ack_rb_expiry = ack_rb_expiry; + pgm_queue_push_head_link (&peer->window->ack_backoff_queue, &peer->ack_link); +} + +/* remove outstanding ACK + */ + +static inline +void +_pgm_remove_ack ( + pgm_peer_t* const restrict peer + ) +{ + pgm_assert (!pgm_queue_is_empty (&peer->window->ack_backoff_queue)); + pgm_queue_unlink (&peer->window->ack_backoff_queue, &peer->ack_link); + peer->ack_rb_expiry = 0; +} + +/* increase reference count for peer object + * + * on success, returns peer object. + */ + +static inline +pgm_peer_t* +_pgm_peer_ref ( + pgm_peer_t* peer + ) +{ +/* pre-conditions */ + pgm_assert (NULL != peer); + + pgm_atomic_inc32 (&peer->ref_count); + return peer; +} + +/* decrease reference count of peer object, destroying on last reference. + */ + +void +pgm_peer_unref ( + pgm_peer_t* peer + ) +{ +/* pre-conditions */ + pgm_assert (NULL != peer); + + if (pgm_atomic_exchange_and_add32 (&peer->ref_count, (uint32_t)-1) != 1) + return; + +/* receive window */ + pgm_rxw_destroy (peer->window); + peer->window = NULL; + +/* object */ + pgm_free (peer); + peer = NULL; +} + +/* find PGM options in received SKB. + * + * returns TRUE if opt_fragment is found, otherwise FALSE is returned. + */ + +static +bool +get_pgm_options ( + struct pgm_sk_buff_t* const skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + pgm_assert (NULL != skb->pgm_data); + + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(skb->pgm_data + 1); + bool found_opt = FALSE; + + pgm_assert (opt_header->opt_type == PGM_OPT_LENGTH); + pgm_assert (opt_header->opt_length == sizeof(struct pgm_opt_length)); + + pgm_debug ("get_pgm_options (skb:%p)", + (const void*)skb); + + skb->pgm_opt_fragment = NULL; + skb->pgm_opt_pgmcc_data = NULL; + +/* always at least two options, first is always opt_length */ + do { + opt_header = (struct pgm_opt_header*)((char*)opt_header + opt_header->opt_length); +/* option overflow */ + if (PGM_UNLIKELY((char*)opt_header > (char*)skb->data)) + break; + + switch (opt_header->opt_type & PGM_OPT_MASK) { + case PGM_OPT_FRAGMENT: + skb->pgm_opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); + found_opt = TRUE; + break; + + case PGM_OPT_PGMCC_DATA: + skb->pgm_opt_pgmcc_data = (struct pgm_opt_pgmcc_data*)(opt_header + 1); + found_opt = TRUE; + break; + + default: break; + } + + } while (!(opt_header->opt_type & PGM_OPT_END)); + return found_opt; +} + +/* a peer in the context of the sock is another party on the network sending PGM + * packets. for each peer we need a receive window and network layer address (nla) to + * which nak requests can be forwarded to. + * + * on success, returns new peer object. + */ + +pgm_peer_t* +pgm_new_peer ( + pgm_sock_t* const restrict sock, + const pgm_tsi_t* const restrict tsi, + const struct sockaddr* const restrict src_addr, + const socklen_t src_addrlen, + const struct sockaddr* const restrict dst_addr, + const socklen_t dst_addrlen, + const pgm_time_t now + ) +{ + pgm_peer_t* peer; + +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != src_addr); + pgm_assert (src_addrlen > 0); + pgm_assert (NULL != dst_addr); + pgm_assert (dst_addrlen > 0); + +#ifdef PGM_DEBUG + char saddr[INET6_ADDRSTRLEN], daddr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop (src_addr, saddr, sizeof(saddr)); + pgm_sockaddr_ntop (dst_addr, daddr, sizeof(daddr)); + pgm_debug ("pgm_new_peer (sock:%p tsi:%s src-addr:%s src-addrlen:%u dst-addr:%s dst-addrlen:%u)", + (void*)sock, pgm_tsi_print (tsi), saddr, (unsigned)src_addrlen, daddr, (unsigned)dst_addrlen); +#endif + + peer = pgm_new0 (pgm_peer_t, 1); + peer->expiry = now + sock->peer_expiry; + peer->sock = sock; + memcpy (&peer->tsi, tsi, sizeof(pgm_tsi_t)); + memcpy (&peer->group_nla, dst_addr, dst_addrlen); + memcpy (&peer->local_nla, src_addr, src_addrlen); +/* port at same location for sin/sin6 */ + ((struct sockaddr_in*)&peer->local_nla)->sin_port = htons (sock->udp_encap_ucast_port); + ((struct sockaddr_in*)&peer->nla)->sin_port = htons (sock->udp_encap_ucast_port); + +/* lock on rx window */ + peer->window = pgm_rxw_create (&peer->tsi, + sock->max_tpdu, + sock->rxw_sqns, + sock->rxw_secs, + sock->rxw_max_rte, + sock->ack_c_p); + peer->spmr_expiry = now + sock->spmr_expiry; + +/* add peer to hash table and linked list */ + pgm_rwlock_writer_lock (&sock->peers_lock); + pgm_peer_t* entry = _pgm_peer_ref (peer); + pgm_hashtable_insert (sock->peers_hashtable, &peer->tsi, entry); + peer->peers_link.data = peer; + sock->peers_list = pgm_list_prepend_link (sock->peers_list, &peer->peers_link); + pgm_rwlock_writer_unlock (&sock->peers_lock); + + pgm_timer_lock (sock); + if (pgm_time_after( sock->next_poll, peer->spmr_expiry )) + sock->next_poll = peer->spmr_expiry; + pgm_timer_unlock (sock); + return peer; +} + +/* copy any contiguous buffers in the peer list to the provided + * message vector. + * returns -ENOBUFS if the vector is full, returns -ECONNRESET if + * data loss is detected, returns 0 when all peers flushed. + */ + +int +pgm_flush_peers_pending ( + pgm_sock_t* const restrict sock, + struct pgm_msgv_t** restrict pmsg, + const struct pgm_msgv_t* const msg_end, /* at least pmsg + 1, same object */ + size_t* const restrict bytes_read, /* added to, not set */ + unsigned* const restrict data_read + ) +{ + int retval = 0; + +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != pmsg); + pgm_assert (NULL != *pmsg); + pgm_assert (NULL != msg_end); + pgm_assert (NULL != bytes_read); + pgm_assert (NULL != data_read); + + pgm_debug ("pgm_flush_peers_pending (sock:%p pmsg:%p msg-end:%p bytes-read:%p data-read:%p)", + (const void*)sock, (const void*)pmsg, (const void*)msg_end, (const void*)bytes_read, (const void*)data_read); + + while (sock->peers_pending) + { + pgm_peer_t* peer = sock->peers_pending->data; + if (peer->last_commit && peer->last_commit < sock->last_commit) + pgm_rxw_remove_commit (peer->window); + const ssize_t peer_bytes = pgm_rxw_readv (peer->window, pmsg, msg_end - *pmsg + 1); + + if (peer->last_cumulative_losses != ((pgm_rxw_t*)peer->window)->cumulative_losses) + { + sock->is_reset = TRUE; + peer->lost_count = ((pgm_rxw_t*)peer->window)->cumulative_losses - peer->last_cumulative_losses; + peer->last_cumulative_losses = ((pgm_rxw_t*)peer->window)->cumulative_losses; + } + + if (peer_bytes >= 0) + { + (*bytes_read) += peer_bytes; + (*data_read) ++; + peer->last_commit = sock->last_commit; + if (*pmsg > msg_end) { /* commit full */ + retval = -ENOBUFS; + break; + } + } else + peer->last_commit = 0; + if (PGM_UNLIKELY(sock->is_reset)) { + retval = -ECONNRESET; + break; + } +/* clear this reference and move to next */ + sock->peers_pending = pgm_slist_remove_first (sock->peers_pending); + } + + return retval; +} + +/* edge trigerred has receiver pending events + */ + +bool +pgm_peer_has_pending ( + pgm_peer_t* const peer + ) +{ +/* pre-conditions */ + pgm_assert (NULL != peer); + + if (NULL == peer->pending_link.data && ((pgm_rxw_t*)peer->window)->has_event) { + ((pgm_rxw_t*)peer->window)->has_event = 0; + return TRUE; + } + return FALSE; +} + +/* set receiver in pending event queue + */ + +void +pgm_peer_set_pending ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict peer + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != peer); + + if (peer->pending_link.data) return; + peer->pending_link.data = peer; + sock->peers_pending = pgm_slist_prepend_link (sock->peers_pending, &peer->pending_link); +} + +/* Create a new error SKB detailing data loss. + */ + +void +pgm_set_reset_error ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict source, + struct pgm_msgv_t* const restrict msgv + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != source); + pgm_assert (NULL != msgv); + + struct pgm_sk_buff_t* error_skb = pgm_alloc_skb (0); + error_skb->sock = sock; + error_skb->tstamp = pgm_time_update_now (); + memcpy (&error_skb->tsi, &source->tsi, sizeof(pgm_tsi_t)); + error_skb->sequence = source->lost_count; + msgv->msgv_skb[0] = error_skb; + msgv->msgv_len = 1; +} + +/* SPM indicate start of a session, continued presence of a session, or flushing final packets + * of a session. + * + * returns TRUE on valid packet, FALSE on invalid packet or duplicate SPM sequence number. + */ + +bool +pgm_on_spm ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict source, + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != source); + pgm_assert (NULL != skb); + + pgm_debug("pgm_on_spm (sock:%p source:%p skb:%p)", + (const void*)sock, (const void*)source, (const void*)skb); + + if (PGM_UNLIKELY(!pgm_verify_spm (skb))) { + pgm_trace(PGM_LOG_ROLE_NETWORK,_("Discarded invalid SPM.")); + source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_SPMS]++; + return FALSE; + } + + const struct pgm_spm* spm = (struct pgm_spm*) skb->data; + const struct pgm_spm6* spm6 = (struct pgm_spm6*)skb->data; + const uint32_t spm_sqn = ntohl (spm->spm_sqn); + +/* check for advancing sequence number, or first SPM */ + if (PGM_LIKELY(pgm_uint32_gte (spm_sqn, source->spm_sqn))) + { +/* copy NLA for replies */ + pgm_nla_to_sockaddr (&spm->spm_nla_afi, (struct sockaddr*)&source->nla); + +/* save sequence number */ + source->spm_sqn = spm_sqn; + +/* update receive window */ + const pgm_time_t nak_rb_expiry = skb->tstamp + nak_rb_ivl (sock); + const unsigned naks = pgm_rxw_update (source->window, + ntohl (spm->spm_lead), + ntohl (spm->spm_trail), + skb->tstamp, + nak_rb_expiry); + if (naks) { + pgm_timer_lock (sock); + if (pgm_time_after (sock->next_poll, nak_rb_expiry)) + sock->next_poll = nak_rb_expiry; + pgm_timer_unlock (sock); + } + +/* mark receiver window for flushing on next recv() */ + const pgm_rxw_t* window = source->window; + if (window->cumulative_losses != source->last_cumulative_losses && + !source->pending_link.data) + { + sock->is_reset = TRUE; + source->lost_count = window->cumulative_losses - source->last_cumulative_losses; + source->last_cumulative_losses = window->cumulative_losses; + pgm_peer_set_pending (sock, source); + } + } + else + { /* does not advance SPM sequence number */ + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded duplicate SPM.")); + source->cumulative_stats[PGM_PC_RECEIVER_DUP_SPMS]++; + return FALSE; + } + +/* check whether peer can generate parity packets */ + if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) + { + const struct pgm_opt_length* opt_len = (AF_INET6 == source->nla.ss_family) ? + (const struct pgm_opt_length*)(spm6 + 1) : + (const struct pgm_opt_length*)(spm + 1); + if (PGM_UNLIKELY(opt_len->opt_type != PGM_OPT_LENGTH)) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed SPM.")); + source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_SPMS]++; + return FALSE; + } + if (PGM_UNLIKELY(opt_len->opt_length != sizeof(struct pgm_opt_length))) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed SPM.")); + source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_SPMS]++; + return FALSE; + } +/* TODO: check for > 16 options & past packet end */ + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; + do { + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); + if ((opt_header->opt_type & PGM_OPT_MASK) == PGM_OPT_PARITY_PRM) + { + const struct pgm_opt_parity_prm* opt_parity_prm = (const struct pgm_opt_parity_prm*)(opt_header + 1); + if (PGM_UNLIKELY((opt_parity_prm->opt_reserved & PGM_PARITY_PRM_MASK) == 0)) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed SPM.")); + source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_SPMS]++; + return FALSE; + } + + const uint32_t parity_prm_tgs = ntohl (opt_parity_prm->parity_prm_tgs); + if (PGM_UNLIKELY(parity_prm_tgs < 2 || parity_prm_tgs > 128)) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed SPM.")); + source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_SPMS]++; + return FALSE; + } + + source->has_proactive_parity = opt_parity_prm->opt_reserved & PGM_PARITY_PRM_PRO; + source->has_ondemand_parity = opt_parity_prm->opt_reserved & PGM_PARITY_PRM_OND; + if (source->has_proactive_parity || source->has_ondemand_parity) { + source->is_fec_enabled = 1; + pgm_rxw_update_fec (source->window, parity_prm_tgs); + } + } + } while (!(opt_header->opt_type & PGM_OPT_END)); + } + +/* either way bump expiration timer */ + source->expiry = skb->tstamp + sock->peer_expiry; + source->spmr_expiry = 0; + if (source->spmr_tstamp > 0) { + PGM_HISTOGRAM_TIMES("Rx.SpmRequestResponseTime", skb->tstamp - source->spmr_tstamp); + source->spmr_tstamp = 0; + } + return TRUE; +} + +/* Multicast peer-to-peer NAK handling, pretty much the same as a NCF but different direction + * + * if NAK is valid, returns TRUE. on error, FALSE is returned. + */ + +bool +pgm_on_peer_nak ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict peer, + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != peer); + pgm_assert (NULL != skb); + + pgm_debug ("pgm_on_peer_nak (sock:%p peer:%p skb:%p)", + (const void*)sock, (const void*)peer, (const void*)skb); + + if (PGM_UNLIKELY(!pgm_verify_nak (skb))) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded invalid multicast NAK.")); + peer->cumulative_stats[PGM_PC_RECEIVER_NAK_ERRORS]++; + return FALSE; + } + + const struct pgm_nak* nak = (struct pgm_nak*) skb->data; + const struct pgm_nak6* nak6 = (struct pgm_nak6*)skb->data; + +/* NAK_SRC_NLA must not contain our sock unicast NLA */ + struct sockaddr_storage nak_src_nla; + pgm_nla_to_sockaddr (&nak->nak_src_nla_afi, (struct sockaddr*)&nak_src_nla); + if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&nak_src_nla, (struct sockaddr*)&sock->send_addr) == 0)) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded multicast NAK on NLA mismatch.")); + return FALSE; + } + +/* NAK_GRP_NLA contains one of our sock receive multicast groups: the sources send multicast group */ + struct sockaddr_storage nak_grp_nla; + pgm_nla_to_sockaddr ((AF_INET6 == nak_src_nla.ss_family) ? &nak6->nak6_grp_nla_afi : &nak->nak_grp_nla_afi, (struct sockaddr*)&nak_grp_nla); + bool found = FALSE; + for (unsigned i = 0; i < sock->recv_gsr_len; i++) + { + if (pgm_sockaddr_cmp ((struct sockaddr*)&nak_grp_nla, (struct sockaddr*)&sock->recv_gsr[i].gsr_group) == 0) + { + found = TRUE; + break; + } + } + + if (PGM_UNLIKELY(!found)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded multicast NAK on multicast group mismatch.")); + return FALSE; + } + +/* handle as NCF */ + int status = pgm_rxw_confirm (peer->window, + ntohl (nak->nak_sqn), + skb->tstamp, + skb->tstamp + sock->nak_rdata_ivl, + skb->tstamp + nak_rb_ivl(sock)); + if (PGM_RXW_UPDATED == status || PGM_RXW_APPENDED == status) + peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED]++; + +/* check NAK list */ + const uint32_t* nak_list = NULL; + unsigned nak_list_len = 0; + if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) + { + const struct pgm_opt_length* opt_len = (AF_INET6 == nak_src_nla.ss_family) ? + (const struct pgm_opt_length*)(nak6 + 1) : + (const struct pgm_opt_length*)(nak + 1); + if (PGM_UNLIKELY(opt_len->opt_type != PGM_OPT_LENGTH)) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed multicast NAK.")); + peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_NCFS]++; + return FALSE; + } + if (PGM_UNLIKELY(opt_len->opt_length != sizeof(struct pgm_opt_length))) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed multicast NAK.")); + peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_NCFS]++; + return FALSE; + } +/* TODO: check for > 16 options & past packet end */ + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; + do { + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); + if ((opt_header->opt_type & PGM_OPT_MASK) == PGM_OPT_NAK_LIST) + { + nak_list = ((const struct pgm_opt_nak_list*)(opt_header + 1))->opt_sqn; + nak_list_len = ( opt_header->opt_length - sizeof(struct pgm_opt_header) - sizeof(uint8_t) ) / sizeof(uint32_t); + break; + } + } while (!(opt_header->opt_type & PGM_OPT_END)); + } + + while (nak_list_len) { + status = pgm_rxw_confirm (peer->window, + ntohl (*nak_list), + skb->tstamp, + skb->tstamp + sock->nak_rdata_ivl, + skb->tstamp + nak_rb_ivl(sock)); + if (PGM_RXW_UPDATED == status || PGM_RXW_APPENDED == status) + peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED]++; + nak_list++; + nak_list_len--; + } + +/* mark receiver window for flushing on next recv() */ + const pgm_rxw_t* window = peer->window; + if (window->cumulative_losses != peer->last_cumulative_losses && + !peer->pending_link.data) + { + sock->is_reset = TRUE; + peer->lost_count = window->cumulative_losses - peer->last_cumulative_losses; + peer->last_cumulative_losses = window->cumulative_losses; + pgm_peer_set_pending (sock, peer); + } + return TRUE; +} + +/* NCF confirming receipt of a NAK from this sock or another on the LAN segment. + * + * Packet contents will match exactly the sent NAK, although not really that helpful. + * + * if NCF is valid, returns TRUE. on error, FALSE is returned. + */ + +bool +pgm_on_ncf ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict source, + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != source); + pgm_assert (NULL != skb); + + pgm_debug ("pgm_on_ncf (sock:%p source:%p skb:%p)", + (const void*)sock, (const void*)source, (const void*)skb); + + if (PGM_UNLIKELY(!pgm_verify_ncf (skb))) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded invalid NCF.")); + source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_NCFS]++; + return FALSE; + } + + const struct pgm_nak* ncf = (struct pgm_nak*) skb->data; + const struct pgm_nak6* ncf6 = (struct pgm_nak6*)skb->data; + +/* NCF_SRC_NLA may contain our sock unicast NLA, we don't really care */ + struct sockaddr_storage ncf_src_nla; + pgm_nla_to_sockaddr (&ncf->nak_src_nla_afi, (struct sockaddr*)&ncf_src_nla); + +#if 0 + if (PGM(pgm_sockaddr_cmp ((struct sockaddr*)&ncf_src_nla, (struct sockaddr*)&sock->send_addr) != 0)) { + g_trace ("INFO", "Discarded NCF on NLA mismatch."); + peer->cumulative_stats[PGM_PC_RECEIVER_PACKETS_DISCARDED]++; + return FALSE; + } +#endif + +/* NCF_GRP_NLA contains our sock multicast group */ + struct sockaddr_storage ncf_grp_nla; + pgm_nla_to_sockaddr ((AF_INET6 == ncf_src_nla.ss_family) ? &ncf6->nak6_grp_nla_afi : &ncf->nak_grp_nla_afi, (struct sockaddr*)&ncf_grp_nla); + if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&ncf_grp_nla, (struct sockaddr*)&sock->send_gsr.gsr_group) != 0)) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded NCF on multicast group mismatch.")); + return FALSE; + } + + const pgm_time_t ncf_rdata_ivl = skb->tstamp + sock->nak_rdata_ivl; + const pgm_time_t ncf_rb_ivl = skb->tstamp + nak_rb_ivl(sock); + int status = pgm_rxw_confirm (source->window, + ntohl (ncf->nak_sqn), + skb->tstamp, + ncf_rdata_ivl, + ncf_rb_ivl); + if (PGM_RXW_UPDATED == status || PGM_RXW_APPENDED == status) + { + const pgm_time_t ncf_ivl = (PGM_RXW_APPENDED == status) ? ncf_rb_ivl : ncf_rdata_ivl; + pgm_timer_lock (sock); + if (pgm_time_after (sock->next_poll, ncf_ivl)) { + sock->next_poll = ncf_ivl; + } + pgm_timer_unlock (sock); + source->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED]++; + } + +/* check NCF list */ + const uint32_t* ncf_list = NULL; + unsigned ncf_list_len = 0; + if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) + { + const struct pgm_opt_length* opt_len = (AF_INET6 == ncf_src_nla.ss_family) ? + (const struct pgm_opt_length*)(ncf6 + 1) : + (const struct pgm_opt_length*)(ncf + 1); + if (PGM_UNLIKELY(opt_len->opt_type != PGM_OPT_LENGTH)) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed NCF.")); + source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_NCFS]++; + return FALSE; + } + if (PGM_UNLIKELY(opt_len->opt_length != sizeof(struct pgm_opt_length))) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed NCF.")); + source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_NCFS]++; + return FALSE; + } +/* TODO: check for > 16 options & past packet end */ + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; + do { + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); + if ((opt_header->opt_type & PGM_OPT_MASK) == PGM_OPT_NAK_LIST) + { + ncf_list = ((const struct pgm_opt_nak_list*)(opt_header + 1))->opt_sqn; + ncf_list_len = ( opt_header->opt_length - sizeof(struct pgm_opt_header) - sizeof(uint8_t) ) / sizeof(uint32_t); + break; + } + } while (!(opt_header->opt_type & PGM_OPT_END)); + } + + pgm_debug ("NCF contains 1+%d sequence numbers.", ncf_list_len); + while (ncf_list_len) + { + status = pgm_rxw_confirm (source->window, + ntohl (*ncf_list), + skb->tstamp, + ncf_rdata_ivl, + ncf_rb_ivl); + if (PGM_RXW_UPDATED == status || PGM_RXW_APPENDED == status) + source->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED]++; + ncf_list++; + ncf_list_len--; + } + +/* mark receiver window for flushing on next recv() */ + const pgm_rxw_t* window = source->window; + if (window->cumulative_losses != source->last_cumulative_losses && + !source->pending_link.data) + { + sock->is_reset = TRUE; + source->lost_count = window->cumulative_losses - source->last_cumulative_losses; + source->last_cumulative_losses = window->cumulative_losses; + pgm_peer_set_pending (sock, source); + } + return TRUE; +} + +/* send SPM-request to a new peer, this packet type has no contents + * + * on success, TRUE is returned, if operation would block FALSE is + * returned. + */ + +static +bool +send_spmr ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict source + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != source); + + pgm_debug ("send_spmr (sock:%p source:%p)", + (const void*)sock, (const void*)source); + + const size_t tpdu_length = sizeof(struct pgm_header); + char buf[ tpdu_length ]; + struct pgm_header* header = (struct pgm_header*)buf; + memcpy (header->pgm_gsi, &source->tsi.gsi, sizeof(pgm_gsi_t)); +/* dport & sport reversed communicating upstream */ + header->pgm_sport = sock->dport; + header->pgm_dport = source->tsi.sport; + header->pgm_type = PGM_SPMR; + header->pgm_options = 0; + header->pgm_tsdu_length = 0; + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + +/* send multicast SPMR TTL 1 */ + pgm_sockaddr_multicast_hops (sock->send_sock, sock->send_gsr.gsr_group.ss_family, 1); + ssize_t sent = pgm_sendto (sock, + FALSE, /* not rate limited */ + FALSE, /* regular socket */ + header, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len ((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) + return FALSE; + +/* send unicast SPMR with regular TTL */ + pgm_sockaddr_multicast_hops (sock->send_sock, sock->send_gsr.gsr_group.ss_family, sock->hops); + sent = pgm_sendto (sock, + FALSE, + FALSE, + header, + tpdu_length, + (struct sockaddr*)&source->local_nla, + pgm_sockaddr_len ((struct sockaddr*)&source->local_nla)); + if (sent < 0 && EAGAIN == errno) + return FALSE; + + sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT] += tpdu_length * 2; + return TRUE; +} + +/* send selective NAK for one sequence number. + * + * on success, TRUE is returned, returns FALSE if would block on operation. + */ + +static +bool +send_nak ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict source, + const uint32_t sequence + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != source); + + pgm_debug ("send_nak (sock:%p peer:%p sequence:%" PRIu32 ")", + (void*)sock, (void*)source, sequence); + + size_t tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_nak); + if (AF_INET6 == source->nla.ss_family) + tpdu_length += sizeof(struct pgm_nak6) - sizeof(struct pgm_nak); + char buf[ tpdu_length ]; + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_nak* nak = (struct pgm_nak* )(header + 1); + struct pgm_nak6* nak6 = (struct pgm_nak6*)(header + 1); + memcpy (header->pgm_gsi, &source->tsi.gsi, sizeof(pgm_gsi_t)); + +/* dport & sport swap over for a nak */ + header->pgm_sport = sock->dport; + header->pgm_dport = source->tsi.sport; + header->pgm_type = PGM_NAK; + header->pgm_options = 0; + header->pgm_tsdu_length = 0; + +/* NAK */ + nak->nak_sqn = htonl (sequence); + +/* source nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&source->nla, (char*)&nak->nak_src_nla_afi); + +/* group nla: we match the NAK NLA to the same as advertised by the source, we might + * be listening to multiple multicast groups + */ + pgm_sockaddr_to_nla ((struct sockaddr*)&source->group_nla, + (AF_INET6 == source->nla.ss_family) ? (char*)&nak6->nak6_grp_nla_afi : (char*)&nak->nak_grp_nla_afi); + + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + + const ssize_t sent = pgm_sendto (sock, + FALSE, /* not rate limited */ + TRUE, /* with router alert */ + header, + tpdu_length, + (struct sockaddr*)&source->nla, + pgm_sockaddr_len((struct sockaddr*)&source->nla)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) + return FALSE; + + source->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAK_PACKETS_SENT]++; + source->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SENT]++; + return TRUE; +} + +/* Send a parity NAK requesting on-demand parity packet generation. + * + * on success, TRUE is returned, returns FALSE if operation would block. + */ + +static +bool +send_parity_nak ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict source, + const uint32_t nak_tg_sqn, /* transmission group (shifted) */ + const uint32_t nak_pkt_cnt /* count of parity packets to request */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != source); + pgm_assert (nak_pkt_cnt > 0); + + pgm_debug ("send_parity_nak (sock:%p source:%p nak-tg-sqn:%" PRIu32 " nak-pkt-cnt:%" PRIu32 ")", + (void*)sock, (void*)source, nak_tg_sqn, nak_pkt_cnt); + + size_t tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_nak); + if (AF_INET6 == source->nla.ss_family) + tpdu_length += sizeof(struct pgm_nak6) - sizeof(struct pgm_nak); + char buf[ tpdu_length ]; + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_nak* nak = (struct pgm_nak* )(header + 1); + struct pgm_nak6* nak6 = (struct pgm_nak6*)(header + 1); + memcpy (header->pgm_gsi, &source->tsi.gsi, sizeof(pgm_gsi_t)); + +/* dport & sport swap over for a nak */ + header->pgm_sport = sock->dport; + header->pgm_dport = source->tsi.sport; + header->pgm_type = PGM_NAK; + header->pgm_options = PGM_OPT_PARITY; /* this is a parity packet */ + header->pgm_tsdu_length = 0; + +/* NAK */ + nak->nak_sqn = htonl (nak_tg_sqn | (nak_pkt_cnt - 1) ); + +/* source nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&source->nla, (char*)&nak->nak_src_nla_afi); + +/* group nla: we match the NAK NLA to the same as advertised by the source, we might + * be listening to multiple multicast groups + */ + pgm_sockaddr_to_nla ((struct sockaddr*)&source->group_nla, + (AF_INET6 == source->nla.ss_family) ? (char*)&nak6->nak6_grp_nla_afi : (char*)&nak->nak_grp_nla_afi ); + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + + const ssize_t sent = pgm_sendto (sock, + FALSE, /* not rate limited */ + TRUE, /* with router alert */ + header, + tpdu_length, + (struct sockaddr*)&source->nla, + pgm_sockaddr_len((struct sockaddr*)&source->nla)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) + return FALSE; + + source->cumulative_stats[PGM_PC_RECEIVER_PARITY_NAK_PACKETS_SENT]++; + source->cumulative_stats[PGM_PC_RECEIVER_PARITY_NAKS_SENT]++; + return TRUE; +} + +/* A NAK packet with a OPT_NAK_LIST option extension + * + * on success, TRUE is returned. on error, FALSE is returned. + */ + +static +bool +send_nak_list ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict source, + const struct pgm_sqn_list_t* const restrict sqn_list + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != source); + pgm_assert (NULL != sqn_list); + pgm_assert_cmpuint (sqn_list->len, >, 1); + pgm_assert_cmpuint (sqn_list->len, <=, 63); + +#ifdef RECEIVER_DEBUG + char list[1024]; + sprintf (list, "%" PRIu32, sqn_list->sqn[0]); + for (unsigned i = 1; i < sqn_list->len; i++) { + char sequence[2 + strlen("4294967295")]; + sprintf (sequence, " %" PRIu32, sqn_list->sqn[i]); + strcat (list, sequence); + } + pgm_debug("send_nak_list (sock:%p source:%p sqn-list:[%s])", + (const void*)sock, (const void*)source, list); +#endif + + size_t tpdu_length = sizeof(struct pgm_header) + + sizeof(struct pgm_nak) + + sizeof(struct pgm_opt_length) + /* includes header */ + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(uint32_t) ); + if (AF_INET6 == source->nla.ss_family) + tpdu_length += sizeof(struct pgm_nak6) - sizeof(struct pgm_nak); + char buf[ tpdu_length ]; + if (PGM_UNLIKELY(pgm_mem_gc_friendly)) + memset (buf, 0, tpdu_length); + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_nak* nak = (struct pgm_nak* )(header + 1); + struct pgm_nak6* nak6 = (struct pgm_nak6*)(header + 1); + memcpy (header->pgm_gsi, &source->tsi.gsi, sizeof(pgm_gsi_t)); + +/* dport & sport swap over for a nak */ + header->pgm_sport = sock->dport; + header->pgm_dport = source->tsi.sport; + header->pgm_type = PGM_NAK; + header->pgm_options = PGM_OPT_PRESENT | PGM_OPT_NETWORK; + header->pgm_tsdu_length = 0; + +/* NAK */ + nak->nak_sqn = htonl (sqn_list->sqn[0]); + +/* source nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&source->nla, (char*)&nak->nak_src_nla_afi); + +/* group nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&source->group_nla, + (AF_INET6 == source->nla.ss_family) ? (char*)&nak6->nak6_grp_nla_afi : (char*)&nak->nak_grp_nla_afi); + +/* OPT_NAK_LIST */ + struct pgm_opt_length* opt_len = (AF_INET6 == source->nla.ss_family) ? (struct pgm_opt_length*)(nak6 + 1) : (struct pgm_opt_length*)(nak + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(uint32_t) ) ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_NAK_LIST | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(uint32_t) ); + struct pgm_opt_nak_list* opt_nak_list = (struct pgm_opt_nak_list*)(opt_header + 1); + opt_nak_list->opt_reserved = 0; + + for (unsigned i = 1; i < sqn_list->len; i++) + opt_nak_list->opt_sqn[i-1] = htonl (sqn_list->sqn[i]); + + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + + const ssize_t sent = pgm_sendto (sock, + FALSE, /* not rate limited */ + FALSE, /* regular socket */ + header, + tpdu_length, + (struct sockaddr*)&source->nla, + pgm_sockaddr_len((struct sockaddr*)&source->nla)); + if ( sent != (ssize_t)tpdu_length ) + return FALSE; + + source->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAK_PACKETS_SENT]++; + source->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SENT] += 1 + sqn_list->len; + return TRUE; +} + +/* send ACK upstream to source + * + * on success, TRUE is returned. on error, FALSE is returned. + */ + +static +bool +send_ack ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict source, + const pgm_time_t now + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != source); + + pgm_debug ("send_ack (sock:%p source:%p now:%" PGM_TIME_FORMAT ")", + (const void*)sock, (const void*)source, now); + + size_t tpdu_length = sizeof(struct pgm_header) + + sizeof(struct pgm_ack) + + sizeof(struct pgm_opt_length) + /* includes header */ + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_pgmcc_feedback); + if (AF_INET6 == sock->send_addr.ss_family) + tpdu_length += sizeof(struct pgm_opt6_pgmcc_feedback) - sizeof(struct pgm_opt_pgmcc_feedback); + char buf[ tpdu_length ]; + if (PGM_UNLIKELY(pgm_mem_gc_friendly)) + memset (buf, 0, tpdu_length); + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_ack* ack = (struct pgm_ack*)(header + 1); + memcpy (header->pgm_gsi, &source->tsi.gsi, sizeof(pgm_gsi_t)); + +/* dport & sport swap over for an ack */ + header->pgm_sport = sock->dport; + header->pgm_dport = source->tsi.sport; + header->pgm_type = PGM_ACK; + header->pgm_options = PGM_OPT_PRESENT; + header->pgm_tsdu_length = 0; + +/* ACK */ + ack->ack_rx_max = htonl (pgm_rxw_lead (source->window)); + ack->ack_bitmap = htonl (source->window->bitmap); + +/* OPT_PGMCC_FEEDBACK */ + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(ack + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + (AF_INET6 == sock->send_addr.ss_family) ? + sizeof(struct pgm_opt6_pgmcc_feedback) : + sizeof(struct pgm_opt_pgmcc_feedback) ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_PGMCC_FEEDBACK | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + ( (AF_INET6 == sock->send_addr.ss_family) ? + sizeof(struct pgm_opt6_pgmcc_feedback) : + sizeof(struct pgm_opt_pgmcc_feedback) ); + struct pgm_opt_pgmcc_feedback* opt_pgmcc_feedback = (struct pgm_opt_pgmcc_feedback*)(opt_header + 1); + opt_pgmcc_feedback->opt_reserved = 0; + + const uint32_t t = source->ack_last_tstamp + pgm_to_msecs( now - source->last_data_tstamp ); + opt_pgmcc_feedback->opt_tstamp = htonl (t); + pgm_sockaddr_to_nla ((struct sockaddr*)&sock->send_addr, (char*)&opt_pgmcc_feedback->opt_nla_afi); + opt_pgmcc_feedback->opt_loss_rate = htonl (source->window->data_loss); + + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + + const ssize_t sent = pgm_sendto (sock, + FALSE, /* not rate limited */ + FALSE, /* regular socket */ + header, + tpdu_length, + (struct sockaddr*)&source->nla, + pgm_sockaddr_len((struct sockaddr*)&source->nla)); + if ( sent != (ssize_t)tpdu_length ) + return FALSE; + + source->cumulative_stats[PGM_PC_RECEIVER_ACKS_SENT]++; + return TRUE; +} + +/* check all receiver windows for ACKer elections, on expiration send an ACK. + * + * returns TRUE on success, returns FALSE if operation would block. + */ + +static +bool +ack_rb_state ( + pgm_peer_t* peer, + const pgm_time_t now + ) +{ +/* pre-conditions */ + pgm_assert (NULL != peer); + + pgm_debug ("ack_rb_state (peer:%p now:%" PGM_TIME_FORMAT ")", + (const void*)peer, now); + + pgm_rxw_t* window = peer->window; + pgm_sock_t* sock = peer->sock; + pgm_list_t* list; + + list = window->ack_backoff_queue.tail; + if (!list) { + pgm_assert (window->ack_backoff_queue.head == NULL); + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Backoff queue is empty in ack_rb_state.")); + return TRUE; + } else { + pgm_assert (window->ack_backoff_queue.head != NULL); + } + +/* have not learned this peers NLA */ + const bool is_valid_nla = (0 != peer->nla.ss_family); + + while (list) + { + pgm_list_t* next_list_el = list->prev; + +/* check for ACK backoff expiration */ + if (pgm_time_after_eq(now, peer->ack_rb_expiry)) + { +/* unreliable delivery */ + _pgm_remove_ack (peer); + + if (PGM_UNLIKELY(!is_valid_nla)) { + pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Unable to send ACK due to unknown NLA.")); + list = next_list_el; + continue; + } + + pgm_assert (!pgm_sockaddr_is_addr_unspecified ((struct sockaddr*)&peer->nla)); + + if (!send_ack (sock, peer, now)) + return FALSE; + } + else + { /* packet expires some time later */ + break; + } + + list = next_list_el; + } + + if (window->ack_backoff_queue.length == 0) + { + pgm_assert ((struct rxw_packet*)window->ack_backoff_queue.head == NULL); + pgm_assert ((struct rxw_packet*)window->ack_backoff_queue.tail == NULL); + } + else + { + pgm_assert ((struct rxw_packet*)window->ack_backoff_queue.head != NULL); + pgm_assert ((struct rxw_packet*)window->ack_backoff_queue.tail != NULL); + } + + if (window->ack_backoff_queue.tail) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Next expiry set in %f seconds."), + pgm_to_secsf(next_ack_rb_expiry(window) - now)); + } + else + { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("ACK backoff queue empty.")); + } + return TRUE; +} + +/* check all receiver windows for packets in BACK-OFF_STATE, on expiration send a NAK. + * update sock::next_nak_rb_timestamp for next expiration time. + * + * peer object is locked before entry. + * + * returns TRUE on success, returns FALSE if operation would block. + */ + +static +bool +nak_rb_state ( + pgm_peer_t* peer, + const pgm_time_t now + ) +{ +/* pre-conditions */ + pgm_assert (NULL != peer); + + pgm_debug ("nak_rb_state (peer:%p now:%" PGM_TIME_FORMAT ")", + (const void*)peer, now); + + pgm_rxw_t* window = peer->window; + pgm_sock_t* sock = peer->sock; + pgm_list_t* list; + struct pgm_sqn_list_t nak_list = { .len = 0 }; + +/* send all NAKs first, lack of data is blocking contiguous processing and its + * better to get the notification out a.s.a.p. even though it might be waiting + * in a kernel queue. + * + * alternative: after each packet check for incoming data and return to the + * event loop. bias for shorter loops as retry count increases. + */ + list = window->nak_backoff_queue.tail; + if (!list) { + pgm_assert (window->nak_backoff_queue.head == NULL); + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Backoff queue is empty in nak_rb_state.")); + return TRUE; + } else { + pgm_assert (window->nak_backoff_queue.head != NULL); + } + + unsigned dropped_invalid = 0; + +/* have not learned this peers NLA */ + const bool is_valid_nla = 0 != peer->nla.ss_family; + +/* TODO: process BOTH selective and parity NAKs? */ + +/* calculate current transmission group for parity enabled peers */ + if (peer->has_ondemand_parity) + { + const uint32_t tg_sqn_mask = 0xffffffff << window->tg_sqn_shift; + +/* NAKs only generated previous to current transmission group */ + const uint32_t current_tg_sqn = window->lead & tg_sqn_mask; + + uint32_t nak_tg_sqn = 0; + uint32_t nak_pkt_cnt = 0; + +/* parity NAK generation */ + + while (list) + { + pgm_list_t* next_list_el = list->prev; + struct pgm_sk_buff_t* skb = (struct pgm_sk_buff_t*)list; + pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; + +/* check this packet for state expiration */ + if (pgm_time_after_eq (now, state->timer_expiry)) + { + if (PGM_UNLIKELY(!is_valid_nla)) { + dropped_invalid++; + pgm_rxw_lost (window, skb->sequence); +/* mark receiver window for flushing on next recv() */ + pgm_peer_set_pending (sock, peer); + list = next_list_el; + continue; + } + +/* TODO: parity nak lists */ + const uint32_t tg_sqn = skb->sequence & tg_sqn_mask; + if ( ( nak_pkt_cnt && tg_sqn == nak_tg_sqn ) || + ( !nak_pkt_cnt && tg_sqn != current_tg_sqn ) ) + { + pgm_rxw_state (window, skb, PGM_PKT_STATE_WAIT_NCF); + + if (!nak_pkt_cnt++) + nak_tg_sqn = tg_sqn; + state->nak_transmit_count++; + +#ifdef PGM_ABSOLUTE_EXPIRY + state->timer_expiry += sock->nak_rpt_ivl; + while (pgm_time_after_eq (now, state->timer_expiry)) { + state->timer_expiry += sock->nak_rpt_ivl; + state->ncf_retry_count++; + } +#else + state->timer_expiry = now + sock->nak_rpt_ivl; +#endif + pgm_timer_lock (sock); + if (pgm_time_after (sock->next_poll, state->timer_expiry)) + sock->next_poll = state->timer_expiry; + pgm_timer_unlock (sock); + } + else + { /* different transmission group */ + break; + } + } + else + { /* packet expires some time later */ + break; + } + + list = next_list_el; + } + + if (nak_pkt_cnt && !send_parity_nak (sock, peer, nak_tg_sqn, nak_pkt_cnt)) + return FALSE; + } + else + { + +/* select NAK generation */ + + while (list) + { + pgm_list_t* next_list_el = list->prev; + struct pgm_sk_buff_t* skb = (struct pgm_sk_buff_t*)list; + pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; + +/* check this packet for state expiration */ + if (pgm_time_after_eq(now, state->timer_expiry)) + { + if (PGM_UNLIKELY(!is_valid_nla)) { + dropped_invalid++; + pgm_rxw_lost (window, skb->sequence); +/* mark receiver window for flushing on next recv() */ + pgm_peer_set_pending (sock, peer); + list = next_list_el; + continue; + } + + pgm_rxw_state (window, skb, PGM_PKT_STATE_WAIT_NCF); + nak_list.sqn[nak_list.len++] = skb->sequence; + state->nak_transmit_count++; + +/* we have two options here, calculate the expiry time in the new state relative to the current + * state execution time, skipping missed expirations due to delay in state processing, or base + * from the actual current time. + */ +#ifdef PGM_ABSOLUTE_EXPIRY + state->timer_expiry += sock->nak_rpt_ivl; + while (pgm_time_after_eq(now, state->timer_expiry)){ + state->timer_expiry += sock->nak_rpt_ivl; + state->ncf_retry_count++; + } +#else + state->timer_expiry = now + sock->nak_rpt_ivl; +pgm_trace(PGM_LOG_ROLE_NETWORK,_("nak_rpt_expiry in %f seconds."), + pgm_to_secsf( state->timer_expiry - now ) ); +#endif + pgm_timer_lock (sock); + if (pgm_time_after (sock->next_poll, state->timer_expiry)) + sock->next_poll = state->timer_expiry; + pgm_timer_unlock (sock); + + if (nak_list.len == PGM_N_ELEMENTS(nak_list.sqn)) { + if (sock->can_send_nak && !send_nak_list (sock, peer, &nak_list)) + return FALSE; + nak_list.len = 0; + } + } + else + { /* packet expires some time later */ + break; + } + + list = next_list_el; + } + + if (sock->can_send_nak && nak_list.len) + { + if (nak_list.len > 1 && !send_nak_list (sock, peer, &nak_list)) + return FALSE; + else if (!send_nak (sock, peer, nak_list.sqn[0])) + return FALSE; + } + + } + + if (PGM_UNLIKELY(dropped_invalid)) + { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Dropped %u messages due to invalid NLA."), dropped_invalid); + +/* mark receiver window for flushing on next recv() */ + if (window->cumulative_losses != peer->last_cumulative_losses && + !peer->pending_link.data) + { + sock->is_reset = TRUE; + peer->lost_count = window->cumulative_losses - peer->last_cumulative_losses; + peer->last_cumulative_losses = window->cumulative_losses; + pgm_peer_set_pending (sock, peer); + } + } + + if (window->nak_backoff_queue.length == 0) + { + pgm_assert ((struct rxw_packet*)window->nak_backoff_queue.head == NULL); + pgm_assert ((struct rxw_packet*)window->nak_backoff_queue.tail == NULL); + } + else + { + pgm_assert ((struct rxw_packet*)window->nak_backoff_queue.head != NULL); + pgm_assert ((struct rxw_packet*)window->nak_backoff_queue.tail != NULL); + } + + if (window->nak_backoff_queue.tail) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Next expiry set in %f seconds."), + pgm_to_secsf(next_nak_rb_expiry(window) - now)); + } + else + { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("NAK backoff queue empty.")); + } + return TRUE; +} + +/* check this peer for NAK state timers, uses the tail of each queue for the nearest + * timer execution. + * + * returns TRUE on complete sweep, returns FALSE if operation would block. + */ + +bool +pgm_check_peer_state ( + pgm_sock_t* sock, + const pgm_time_t now + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + + pgm_debug ("pgm_check_peer_state (sock:%p now:%" PGM_TIME_FORMAT ")", + (const void*)sock, now); + + if (!sock->peers_list) + return TRUE; + + pgm_list_t* list = sock->peers_list; + do { + pgm_list_t* next = list->next; + pgm_peer_t* peer = list->data; + pgm_rxw_t* window = peer->window; + + if (peer->spmr_expiry) + { + if (pgm_time_after_eq (now, peer->spmr_expiry)) + { + if (sock->can_send_nak) { + if (!send_spmr (sock, peer)) { + return FALSE; + } + peer->spmr_tstamp = now; + } + peer->spmr_expiry = 0; + } + } + + if (window->ack_backoff_queue.tail) + { + if (pgm_time_after_eq (now, next_ack_rb_expiry (window))) + if (!ack_rb_state (peer, now)) { + return FALSE; + } + } + + if (window->nak_backoff_queue.tail) + { + if (pgm_time_after_eq (now, next_nak_rb_expiry (window))) + if (!nak_rb_state (peer, now)) { + return FALSE; + } + } + + if (window->wait_ncf_queue.tail) + { + if (pgm_time_after_eq (now, next_nak_rpt_expiry (window))) + nak_rpt_state (peer, now); + } + + if (window->wait_data_queue.tail) + { + if (pgm_time_after_eq (now, next_nak_rdata_expiry (window))) + nak_rdata_state (peer, now); + } + +/* expired, remove from hash table and linked list */ + if (pgm_time_after_eq (now, peer->expiry)) + { + if (peer->pending_link.data) + { + pgm_trace (PGM_LOG_ROLE_SESSION,_("Peer expiration postponed due to committing data, tsi %s"), pgm_tsi_print (&peer->tsi)); + peer->expiry += sock->peer_expiry; + } + else if (window->committed_count) + { + pgm_trace (PGM_LOG_ROLE_SESSION,_("Peer expiration postponed due to committed data, tsi %s"), pgm_tsi_print (&peer->tsi)); + peer->expiry += sock->peer_expiry; + } + else + { + pgm_trace (PGM_LOG_ROLE_SESSION,_("Peer expired, tsi %s"), pgm_tsi_print (&peer->tsi)); + pgm_hashtable_remove (sock->peers_hashtable, &peer->tsi); + sock->peers_list = pgm_list_remove_link (sock->peers_list, &peer->peers_link); + if (sock->last_hash_value == peer) + sock->last_hash_value = NULL; + pgm_peer_unref (peer); + } + } + + list = next; + } while (list); + +/* check for waiting contiguous packets */ + if (sock->peers_pending && !sock->is_pending_read) + { + pgm_debug ("prod rx thread"); + pgm_notify_send (&sock->pending_notify); + sock->is_pending_read = TRUE; + } + return TRUE; +} + +/* find the next state expiration time among the socks peers. + * + * on success, returns the earliest of the expiration parameter or next + * peer expiration time. + */ + +pgm_time_t +pgm_min_receiver_expiry ( + pgm_time_t expiration, /* absolute time */ + pgm_sock_t* sock + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + + pgm_debug ("pgm_min_receiver_expiry (expiration:%" PGM_TIME_FORMAT " sock:%p)", + expiration, (const void*)sock); + + if (!sock->peers_list) + return expiration; + + pgm_list_t* list = sock->peers_list; + do { + pgm_list_t* next = list->next; + pgm_peer_t* peer = (pgm_peer_t*)list->data; + pgm_rxw_t* window = peer->window; + + if (peer->spmr_expiry) + { + if (pgm_time_after_eq (expiration, peer->spmr_expiry)) + expiration = peer->spmr_expiry; + } + + if (window->ack_backoff_queue.tail) + { + if (pgm_time_after_eq (expiration, next_ack_rb_expiry (window))) + expiration = next_ack_rb_expiry (window); + } + + if (window->nak_backoff_queue.tail) + { + if (pgm_time_after_eq (expiration, next_nak_rb_expiry (window))) + expiration = next_nak_rb_expiry (window); + } + + if (window->wait_ncf_queue.tail) + { + if (pgm_time_after_eq (expiration, next_nak_rpt_expiry (window))) + expiration = next_nak_rpt_expiry (window); + } + + if (window->wait_data_queue.tail) + { + if (pgm_time_after_eq (expiration, next_nak_rdata_expiry (window))) + expiration = next_nak_rdata_expiry (window); + } + + list = next; + } while (list); + + return expiration; +} + +/* check WAIT_NCF_STATE, on expiration move back to BACK-OFF_STATE, on exceeding NAK_NCF_RETRIES + * cancel the sequence number. + */ +static +void +nak_rpt_state ( + pgm_peer_t* peer, + const pgm_time_t now + ) +{ +/* pre-conditions */ + pgm_assert (NULL != peer); + + pgm_debug ("nak_rpt_state (peer:%p now:%" PGM_TIME_FORMAT ")", + (void*)peer, now); + + pgm_rxw_t* window = peer->window; + pgm_sock_t* sock = peer->sock; + pgm_list_t* list = window->wait_ncf_queue.tail; + + unsigned dropped_invalid = 0; + unsigned dropped = 0; + +/* have not learned this peers NLA */ + const bool is_valid_nla = 0 != peer->nla.ss_family; + + while (list) + { + pgm_list_t* next_list_el = list->prev; + struct pgm_sk_buff_t* skb = (struct pgm_sk_buff_t*)list; + pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; + +/* check this packet for state expiration */ + if (pgm_time_after_eq (now, state->timer_expiry)) + { + if (PGM_UNLIKELY(!is_valid_nla)) { + dropped_invalid++; + pgm_rxw_lost (window, skb->sequence); +/* mark receiver window for flushing on next recv() */ + pgm_peer_set_pending (sock, peer); + list = next_list_el; + continue; + } + + if (++state->ncf_retry_count >= sock->nak_ncf_retries) + { + dropped++; + cancel_skb (sock, peer, skb, now); + peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_NCF_RETRIES_EXCEEDED]++; + } + else + { +/* retry */ +// state->timer_expiry += nak_rb_ivl(sock); + state->timer_expiry = now + nak_rb_ivl (sock); + pgm_rxw_state (window, skb, PGM_PKT_STATE_BACK_OFF); + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("NCF retry #%u attempt %u/%u."), skb->sequence, state->ncf_retry_count, sock->nak_ncf_retries); + } + } + else + { +/* packet expires some time later */ + pgm_trace(PGM_LOG_ROLE_RX_WINDOW,_("NCF retry #%u is delayed %f seconds."), + skb->sequence, pgm_to_secsf (state->timer_expiry - now)); + break; + } + + list = next_list_el; + } + + if (window->wait_ncf_queue.length == 0) + { + pgm_assert ((pgm_rxw_state_t*)window->wait_ncf_queue.head == NULL); + pgm_assert ((pgm_rxw_state_t*)window->wait_ncf_queue.tail == NULL); + } + else + { + pgm_assert ((pgm_rxw_state_t*)window->wait_ncf_queue.head != NULL); + pgm_assert ((pgm_rxw_state_t*)window->wait_ncf_queue.tail != NULL); + } + + if (PGM_UNLIKELY(dropped_invalid)) { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Dropped %u messages due to invalid NLA."), dropped_invalid); + } + + if (PGM_UNLIKELY(dropped)) { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Dropped %u messages due to ncf cancellation, " + "rxw_sqns %" PRIu32 + " bo %" PRIu32 + " ncf %" PRIu32 + " wd %" PRIu32 + " lost %" PRIu32 + " frag %" PRIu32), + dropped, + pgm_rxw_length (window), + window->nak_backoff_queue.length, + window->wait_ncf_queue.length, + window->wait_data_queue.length, + window->lost_count, + window->fragment_count); + } + +/* mark receiver window for flushing on next recv() */ + if (PGM_UNLIKELY(window->cumulative_losses != peer->last_cumulative_losses && + !peer->pending_link.data)) + { + sock->is_reset = TRUE; + peer->lost_count = window->cumulative_losses - peer->last_cumulative_losses; + peer->last_cumulative_losses = window->cumulative_losses; + pgm_peer_set_pending (sock, peer); + } + + if (window->wait_ncf_queue.tail) + { + if (next_nak_rpt_expiry (window) > now) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Next expiry set in %f seconds."), pgm_to_secsf (next_nak_rpt_expiry (window) - now)); + } else { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Next expiry set in -%f seconds."), pgm_to_secsf (now - next_nak_rpt_expiry (window))); + } + } + else + { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Wait ncf queue empty.")); + } +} + +/* check WAIT_DATA_STATE, on expiration move back to BACK-OFF_STATE, on exceeding NAK_DATA_RETRIES + * canel the sequence number. + */ +static +void +nak_rdata_state ( + pgm_peer_t* peer, + const pgm_time_t now + ) +{ +/* pre-conditions */ + pgm_assert (NULL != peer); + + pgm_debug ("nak_rdata_state (peer:%p now:%" PGM_TIME_FORMAT ")", + (const void*)peer, now); + + pgm_rxw_t* window = peer->window; + pgm_sock_t* sock = peer->sock; + pgm_list_t* list = window->wait_data_queue.tail; + + unsigned dropped_invalid = 0; + unsigned dropped = 0; + +/* have not learned this peers NLA */ + const bool is_valid_nla = 0 != peer->nla.ss_family; + + while (list) + { + pgm_list_t* next_list_el = list->prev; + struct pgm_sk_buff_t* rdata_skb = (struct pgm_sk_buff_t*)list; + pgm_assert (NULL != rdata_skb); + pgm_rxw_state_t* rdata_state = (pgm_rxw_state_t*)&rdata_skb->cb; + +/* check this packet for state expiration */ + if (pgm_time_after_eq (now, rdata_state->timer_expiry)) + { + if (PGM_UNLIKELY(!is_valid_nla)) { + dropped_invalid++; + pgm_rxw_lost (window, rdata_skb->sequence); +/* mark receiver window for flushing on next recv() */ + pgm_peer_set_pending (sock, peer); + list = next_list_el; + continue; + } + + if (++rdata_state->data_retry_count >= sock->nak_data_retries) + { + dropped++; + cancel_skb (sock, peer, rdata_skb, now); + peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_DATA_RETRIES_EXCEEDED]++; + list = next_list_el; + continue; + } + +// rdata_state->timer_expiry += nak_rb_ivl(sock); + rdata_state->timer_expiry = now + nak_rb_ivl (sock); + pgm_rxw_state (window, rdata_skb, PGM_PKT_STATE_BACK_OFF); + +/* retry back to back-off state */ + pgm_trace(PGM_LOG_ROLE_RX_WINDOW,_("Data retry #%u attempt %u/%u."), rdata_skb->sequence, rdata_state->data_retry_count, sock->nak_data_retries); + } + else + { /* packet expires some time later */ + break; + } + + + list = next_list_el; + } + + if (window->wait_data_queue.length == 0) + { + pgm_assert (NULL == (pgm_rxw_state_t*)window->wait_data_queue.head); + pgm_assert (NULL == (pgm_rxw_state_t*)window->wait_data_queue.tail); + } + else + { + pgm_assert (NULL != (pgm_rxw_state_t*)window->wait_data_queue.head); + pgm_assert (NULL != (pgm_rxw_state_t*)window->wait_data_queue.tail); + } + + if (PGM_UNLIKELY(dropped_invalid)) { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Dropped %u messages due to invalid NLA."), dropped_invalid); + } + + if (PGM_UNLIKELY(dropped)) { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Dropped %u messages due to data cancellation."), dropped); + } + +/* mark receiver window for flushing on next recv() */ + if (PGM_UNLIKELY(window->cumulative_losses != peer->last_cumulative_losses && + !peer->pending_link.data)) + { + sock->is_reset = TRUE; + peer->lost_count = window->cumulative_losses - peer->last_cumulative_losses; + peer->last_cumulative_losses = window->cumulative_losses; + pgm_peer_set_pending (sock, peer); + } + + if (window->wait_data_queue.tail) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Next expiry set in %f seconds."), pgm_to_secsf (next_nak_rdata_expiry (window) - now)); + } else { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Wait data queue empty.")); + } +} + +/* ODATA or RDATA packet with any of the following options: + * + * OPT_FRAGMENT - this TPDU part of a larger APDU. + * + * Ownership of skb is taken and must be passed to the receive window or destroyed. + * + * returns TRUE is skb has been replaced, FALSE is remains unchanged and can be recycled. + */ + +bool +pgm_on_data ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict source, + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != source); + pgm_assert (NULL != skb); + + pgm_debug ("pgm_on_data (sock:%p source:%p skb:%p)", + (void*)sock, (void*)source, (void*)skb); + + unsigned msg_count = 0; + const pgm_time_t nak_rb_expiry = skb->tstamp + nak_rb_ivl (sock); + pgm_time_t ack_rb_expiry = 0; + const unsigned tsdu_length = ntohs (skb->pgm_header->pgm_tsdu_length); + + skb->pgm_data = skb->data; + + const unsigned opt_total_length = (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) ? ntohs(*(uint16_t*)( (char*)( skb->pgm_data + 1 ) + sizeof(uint16_t))) : 0; + +/* advance data pointer to payload */ + pgm_skb_pull (skb, sizeof(struct pgm_data) + opt_total_length); + + if (opt_total_length > 0 && /* there are options */ + get_pgm_options (skb) && /* valid options */ + sock->use_pgmcc && /* PGMCC is enabled */ + NULL != skb->pgm_opt_pgmcc_data && /* PGMCC options */ + 0 == source->ack_rb_expiry) /* not partaking in a current election */ + { + ack_rb_expiry = skb->tstamp + ack_rb_ivl (sock); + } + + const int add_status = pgm_rxw_add (source->window, skb, skb->tstamp, nak_rb_expiry); + +/* skb reference is now invalid */ + bool flush_naks = FALSE; + + switch (add_status) { + case PGM_RXW_MISSING: + flush_naks = TRUE; +/* fall through */ + case PGM_RXW_INSERTED: + case PGM_RXW_APPENDED: + msg_count++; + break; + + case PGM_RXW_DUPLICATE: + source->cumulative_stats[PGM_PC_RECEIVER_DUP_DATAS]++; + goto discarded; + + case PGM_RXW_MALFORMED: + source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_ODATA]++; +/* fall through */ + case PGM_RXW_BOUNDS: +discarded: + return FALSE; + + default: pgm_assert_not_reached(); break; + } + +/* valid data */ + PGM_HISTOGRAM_COUNTS("Rx.DataBytesReceived", tsdu_length); + source->cumulative_stats[PGM_PC_RECEIVER_DATA_BYTES_RECEIVED] += tsdu_length; + source->cumulative_stats[PGM_PC_RECEIVER_DATA_MSGS_RECEIVED] += msg_count; + +/* congestion control */ + if (0 != ack_rb_expiry) + { +/* save source timestamp and local timestamp for RTT calculation */ + source->ack_last_tstamp = ntohl (skb->pgm_opt_pgmcc_data->opt_tstamp); + source->last_data_tstamp = skb->tstamp; + if (_pgm_is_acker (source, skb)) + { + if (PGM_UNLIKELY(pgm_sockaddr_is_addr_unspecified ((struct sockaddr*)&source->nla))) + { + pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Unable to send ACK due to unknown NLA.")); + } + else if (PGM_UNLIKELY(!send_ack (sock, source, skb->tstamp))) + { + pgm_debug ("send_ack failed"); + } + ack_rb_expiry = 0; + } + else if (_pgm_is_acker_election (skb)) + { + pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("ACKer election.")); + _pgm_add_ack (source, ack_rb_expiry); + } + else if (0 != source->window->ack_backoff_queue.length) + { +/* purge ACK backoff queue as host is not elected ACKer */ + _pgm_remove_ack (source); + ack_rb_expiry = 0; + } + else + { +/* no election, not the elected ACKer, no outstanding ACKs */ + ack_rb_expiry = 0; + } + } + + if (flush_naks || 0 != ack_rb_expiry) { +/* flush out 1st time nak packets */ + pgm_timer_lock (sock); + if (flush_naks && pgm_time_after (sock->next_poll, nak_rb_expiry)) + sock->next_poll = nak_rb_expiry; + if (0 != ack_rb_expiry && pgm_time_after (sock->next_poll, ack_rb_expiry)) + sock->next_poll = ack_rb_expiry; + pgm_timer_unlock (sock); + } + return TRUE; +} + +/* POLLs are generated by PGM Parents (Sources or Network Elements). + * + * returns TRUE on valid packet, FALSE on invalid packet. + */ + +bool +pgm_on_poll ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict source, + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != source); + pgm_assert (NULL != skb); + + pgm_debug ("pgm_on_poll (sock:%p source:%p skb:%p)", + (void*)sock, (void*)source, (void*)skb); + + if (PGM_UNLIKELY(!pgm_verify_poll (skb))) { + pgm_trace(PGM_LOG_ROLE_NETWORK,_("Discarded invalid POLL.")); + return FALSE; + } + + struct pgm_poll* poll4 = (struct pgm_poll*) skb->data; + struct pgm_poll6* poll6 = (struct pgm_poll6*)skb->data; + uint32_t poll_rand; + memcpy (&poll_rand, (AFI_IP6 == ntohs (poll4->poll_nla_afi)) ? poll6->poll6_rand : poll4->poll_rand, sizeof(poll_rand)); + const uint32_t poll_mask = (AFI_IP6 == ntohs (poll4->poll_nla_afi)) ? ntohl (poll6->poll6_mask) : ntohl (poll4->poll_mask); + +/* Check for probability match */ + if (poll_mask && + (sock->rand_node_id & poll_mask) != poll_rand) + { +/* discard early */ + return FALSE; + } + +/* scoped per path nla + * TODO: manage list of pollers per peer + */ + const uint32_t poll_sqn = ntohl (poll4->poll_sqn); + const uint16_t poll_round = ntohs (poll4->poll_round); + +/* Check for new poll round */ + if (poll_round && + poll_sqn != source->last_poll_sqn) + { + return FALSE; + } + +/* save sequence and round of valid poll */ + source->last_poll_sqn = poll_sqn; + source->last_poll_round = poll_round; + + const uint16_t poll_s_type = ntohs (poll4->poll_s_type); + +/* Check poll type */ + switch (poll_s_type) { + case PGM_POLL_GENERAL: + return on_general_poll (sock, source, skb); + + case PGM_POLL_DLR: + return on_dlr_poll (sock, source, skb); + + default: +/* unknown sub-type, discard */ + break; + } + + return FALSE; +} + +/* Used to count PGM children */ + +static +bool +on_general_poll ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict source, + struct pgm_sk_buff_t* const restrict skb + ) +{ + struct pgm_poll* poll4 = (struct pgm_poll*) skb->data; + struct pgm_poll6* poll6 = (struct pgm_poll6*)skb->data; + +/* TODO: cancel any pending poll-response */ + +/* defer response based on provided back-off interval */ + const uint32_t poll_bo_ivl = (AFI_IP6 == ntohs (poll4->poll_nla_afi)) ? ntohl (poll6->poll6_bo_ivl) : ntohl (poll4->poll_bo_ivl); + source->polr_expiry = skb->tstamp + pgm_rand_int_range (&sock->rand_, 0, poll_bo_ivl); + pgm_nla_to_sockaddr (&poll4->poll_nla_afi, (struct sockaddr*)&source->poll_nla); +/* TODO: schedule poll-response */ + + return TRUE; +} + +/* Used to count off-tree DLRs */ + +static +bool +on_dlr_poll ( + PGM_GNUC_UNUSED pgm_sock_t* const restrict sock, + PGM_GNUC_UNUSED pgm_peer_t* const restrict source, + PGM_GNUC_UNUSED struct pgm_sk_buff_t* const restrict skb + ) +{ +/* we are not a DLR */ + return FALSE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/receiver_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/receiver_unittest.c new file mode 100644 index 0000000..5221a0b --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/receiver_unittest.c @@ -0,0 +1,857 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for PGM receiver transport. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include + + +/* mock state */ + +#define TEST_NETWORK "" +#define TEST_PORT 7500 +#define TEST_MAX_TPDU 1500 +#define TEST_TXW_SQNS 32 +#define TEST_RXW_SQNS 32 +#define TEST_HOPS 16 +#define TEST_SPM_AMBIENT ( pgm_secs(30) ) +#define TEST_SPM_HEARTBEAT_INIT { pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(1300), pgm_secs(7), pgm_secs(16), pgm_secs(25), pgm_secs(30) } +#define TEST_PEER_EXPIRY ( pgm_secs(300) ) +#define TEST_SPMR_EXPIRY ( pgm_msecs(250) ) +#define TEST_NAK_BO_IVL ( pgm_msecs(50) ) +#define TEST_NAK_RPT_IVL ( pgm_secs(2) ) +#define TEST_NAK_RDATA_IVL ( pgm_secs(2) ) +#define TEST_NAK_DATA_RETRIES 5 +#define TEST_NAK_NCF_RETRIES 2 + + +#define pgm_histogram_add mock_pgm_histogram_add +#define pgm_verify_spm mock_pgm_verify_spm +#define pgm_verify_nak mock_pgm_verify_nak +#define pgm_verify_ncf mock_pgm_verify_ncf +#define pgm_verify_poll mock_pgm_verify_poll +#define pgm_sendto mock_pgm_sendto +#define pgm_time_now mock_pgm_time_now +#define pgm_time_update_now mock_pgm_time_update_now +#define pgm_rxw_destroy mock_pgm_rxw_destroy +#define pgm_rxw_create mock_pgm_rxw_create +#define pgm_rxw_update mock_pgm_rxw_update +#define pgm_rxw_update_fec mock_pgm_rxw_update_fec +#define pgm_rxw_confirm mock_pgm_rxw_confirm +#define pgm_rxw_lost mock_pgm_rxw_lost +#define pgm_rxw_state mock_pgm_rxw_state +#define pgm_rxw_add mock_pgm_rxw_add +#define pgm_rxw_remove_commit mock_pgm_rxw_remove_commit +#define pgm_rxw_readv mock_pgm_rxw_readv +#define pgm_csum_fold mock_pgm_csum_fold +#define pgm_compat_csum_partial mock_pgm_compat_csum_partial +#define pgm_histogram_init mock_pgm_histogram_init + + +#define RECEIVER_DEBUG +#include "receiver.c" + + +static +void +mock_setup (void) +{ + if (!g_thread_supported ()) g_thread_init (NULL); +} + +static +struct pgm_sock_t* +generate_sock (void) +{ + struct pgm_sock_t* sock = g_malloc0 (sizeof(struct pgm_sock_t)); + return sock; +} + +static +pgm_peer_t* +generate_peer (void) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_peer_t* peer = g_malloc0 (sizeof(pgm_peer_t)); + peer->window = g_malloc0 (sizeof(pgm_rxw_t)); + pgm_atomic_inc32 (&peer->ref_count); + return peer; +} + +/** socket module */ +static +int +mock_pgm_poll_info ( + pgm_sock_t* const sock, + struct pollfd* fds, + int* n_fds, + int events + ) +{ +} + +static +gboolean +mock_pgm_on_nak ( + pgm_sock_t* const sock, + struct pgm_sk_buff_t* const skb + ) +{ + return TRUE; +} + +static +gboolean +mock_pgm_on_nnak ( + pgm_sock_t* const sock, + struct pgm_sk_buff_t* const skb + ) +{ + return TRUE; +} + +static +gboolean +mock_pgm_on_spmr ( + pgm_sock_t* const sock, + pgm_peer_t* const peer, + struct pgm_sk_buff_t* const skb + ) +{ + return TRUE; +} + +/** net module */ +PGM_GNUC_INTERNAL +ssize_t +mock_pgm_sendto ( + pgm_sock_t* sock, + bool use_rate_limit, + bool use_router_alert, + const void* buf, + size_t len, + const struct sockaddr* to, + socklen_t tolen + ) +{ + return len; +} + +/** time module */ +static pgm_time_t mock_pgm_time_now = 0x1; +static pgm_time_t _mock_pgm_time_update_now (void); +pgm_time_update_func mock_pgm_time_update_now = _mock_pgm_time_update_now; + +pgm_time_t +_mock_pgm_time_update_now (void) +{ + return mock_pgm_time_now; +} + +/* packet module */ +bool +mock_pgm_verify_spm ( + const struct pgm_sk_buff_t* const skb + ) +{ + return TRUE; +} + +bool +mock_pgm_verify_nak ( + const struct pgm_sk_buff_t* const skb + ) +{ + return TRUE; +} + +bool +mock_pgm_verify_ncf ( + const struct pgm_sk_buff_t* const skb + ) +{ + return TRUE; +} + +bool +mock_pgm_verify_poll ( + const struct pgm_sk_buff_t* const skb + ) +{ + return TRUE; +} + +/* receive window module */ +pgm_rxw_t* +mock_pgm_rxw_create ( + const pgm_tsi_t* tsi, + const uint16_t tpdu_size, + const unsigned sqns, + const unsigned secs, + const ssize_t max_rte, + const uint32_t ack_c_p + ) +{ + return g_malloc0 (sizeof(pgm_rxw_t)); +} + +void +mock_pgm_rxw_destroy ( + pgm_rxw_t* const window + ) +{ + g_assert (NULL != window); + g_free (window); +} + +int +mock_pgm_rxw_confirm ( + pgm_rxw_t* const window, + const uint32_t sequence, + const pgm_time_t now, + const pgm_time_t nak_rdata_expiry, + const pgm_time_t nak_rb_expiry + ) +{ + return PGM_RXW_DUPLICATE; +} + +void +mock_pgm_rxw_lost ( + pgm_rxw_t* const window, + const uint32_t sequence + ) +{ +} + +void +mock_pgm_rxw_state ( + pgm_rxw_t* const window, + struct pgm_sk_buff_t* const skb, + const int new_state + ) +{ +} + +unsigned +mock_pgm_rxw_update ( + pgm_rxw_t* const window, + const uint32_t txw_lead, + const uint32_t txw_trail, + const pgm_time_t now, + const pgm_time_t nak_rb_expiry + ) +{ + return 0; +} + +void +mock_pgm_rxw_update_fec ( + pgm_rxw_t* const window, + const uint8_t rs_k + ) +{ +} + +int +mock_pgm_rxw_add ( + pgm_rxw_t* const window, + struct pgm_sk_buff_t* const skb, + const pgm_time_t now, + const pgm_time_t nak_rb_expiry + ) +{ + return PGM_RXW_APPENDED; +} + +void +mock_pgm_rxw_remove_commit ( + pgm_rxw_t* const window + ) +{ +} + +ssize_t +mock_pgm_rxw_readv ( + pgm_rxw_t* const window, + struct pgm_msgv_t** pmsg, + const unsigned pmsglen + ) +{ + return 0; +} + +/* checksum module */ +uint16_t +mock_pgm_csum_fold ( + uint32_t csum + ) +{ + return 0x0; +} + +uint32_t +mock_pgm_compat_csum_partial ( + const void* addr, + uint16_t len, + uint32_t csum + ) +{ + return 0x0; +} + +void +mock_pgm_histogram_init ( + pgm_histogram_t* histogram + ) +{ +} + +void +mock_pgm_histogram_add ( + pgm_histogram_t* histogram, + int value + ) +{ +} + +/* mock functions for external references */ + +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return 0; +} + + +/* target: + * void + * pgm_peer_unref ( + * pgm_peer_t* peer + * ) + */ + +/* last ref */ +START_TEST (test_peer_unref_pass_001) +{ + pgm_peer_t* peer = generate_peer(); + pgm_peer_unref (peer); +} +END_TEST + +/* non-last ref */ +START_TEST (test_peer_unref_pass_002) +{ + pgm_peer_t* peer = _pgm_peer_ref (generate_peer()); + pgm_peer_unref (peer); +} +END_TEST + + +START_TEST (test_peer_unref_fail_001) +{ + pgm_peer_unref (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_check_peer_state ( + * pgm_sock_t* sock, + * const pgm_time_t now + * ) + */ + +START_TEST (test_check_peer_state_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + sock->is_bound = TRUE; + pgm_check_peer_state (sock, mock_pgm_time_now); +} +END_TEST + +START_TEST (test_check_peer_state_fail_001) +{ + pgm_check_peer_state (NULL, mock_pgm_time_now); + fail ("reached"); +} +END_TEST + +/* target: + * pgm_time_t + * pgm_min_receiver_expiry ( + * pgm_time_t expiration, + * pgm_sock_t* sock + * ) + */ + +START_TEST (test_min_receiver_expiry_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + sock->is_bound = TRUE; + const pgm_time_t expiration = pgm_secs(1); + pgm_time_t next_expiration = pgm_min_receiver_expiry (expiration, sock); +} +END_TEST + +START_TEST (test_min_receiver_expiry_fail_001) +{ + const pgm_time_t expiration = pgm_secs(1); + pgm_min_receiver_expiry (expiration, NULL); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_RXW_SQNS + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_rxw_sqns_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + const int optname = PGM_RXW_SQNS; + const int rxw_sqns = 100; + const void* optval = &rxw_sqns; + const socklen_t optlen = sizeof(int); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_rxw_sqns failed"); +} +END_TEST + +START_TEST (test_set_rxw_sqns_fail_001) +{ + const int optname = PGM_RXW_SQNS; + const int rxw_sqns = 100; + const void* optval = &rxw_sqns; + const socklen_t optlen = sizeof(int); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_rxw_sqns failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_RXW_SECS, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_rxw_secs_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + const int optname = PGM_RXW_SECS; + const int rxw_secs = 10; + const void* optval = &rxw_secs; + const socklen_t optlen = sizeof(int); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_rxw_secs failed"); +} +END_TEST + +START_TEST (test_set_rxw_secs_fail_001) +{ + const int optname = PGM_RXW_SECS; + const int rxw_secs = 10; + const void* optval = &rxw_secs; + const socklen_t optlen = sizeof(int); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_rxw_secs failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_RXW_MAX_RTE, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_rxw_max_rte_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + const int optname = PGM_RXW_MAX_RTE; + const int rxw_max_rte = 100*1000; + const void* optval = &rxw_max_rte; + const socklen_t optlen = sizeof(int); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_rxw_max_rte failed"); +} +END_TEST + +START_TEST (test_set_rxw_max_rte_fail_001) +{ + const int optname = PGM_RXW_MAX_RTE; + const int rxw_max_rte = 100*1000; + const void* optval = &rxw_max_rte; + const socklen_t optlen = sizeof(int); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_rxw_max_rte failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_PEER_EXPIRY, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_peer_expiry_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + const int optname = PGM_PEER_EXPIRY; + const int peer_expiry = pgm_secs(100); + const void* optval = &peer_expiry; + const socklen_t optlen = sizeof(int); +/* pre-checking should verify value to spm ambient interval + sock->spm_ambient_interval = pgm_secs(30); + */ + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_peer_expiry failed"); +} +END_TEST + +START_TEST (test_set_peer_expiry_fail_001) +{ + const int optname = PGM_PEER_EXPIRY; + const int peer_expiry = pgm_secs(100); + const void* optval = &peer_expiry; + const socklen_t optlen = sizeof(int); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_peer_expiry failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_SPMR_EXPIRY, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_spmr_expiry_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + const int optname = PGM_SPMR_EXPIRY; + const int spmr_expiry = pgm_secs(10); + const void* optval = &spmr_expiry; + const socklen_t optlen = sizeof(int); +/* pre-checking should verify value to spm ambient interval + sock->spm_ambient_interval = pgm_secs(30); + */ + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_spmr_expiry failed"); +} +END_TEST + +START_TEST (test_set_spmr_expiry_fail_001) +{ + const int optname = PGM_SPMR_EXPIRY; + const int spmr_expiry = pgm_secs(10); + const void* optval = &spmr_expiry; + const socklen_t optlen = sizeof(int); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_spmr_expiry failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_NAK_BO_IVL, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_nak_bo_ivl_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + const int optname = PGM_NAK_BO_IVL; + const int nak_bo_ivl = pgm_msecs(1000); + const void* optval = &nak_bo_ivl; + const socklen_t optlen = sizeof(int); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_nak_bo_ivl failed"); +} +END_TEST + +START_TEST (test_set_nak_bo_ivl_fail_001) +{ + const int optname = PGM_NAK_BO_IVL; + const int nak_bo_ivl = pgm_msecs(1000); + const void* optval = &nak_bo_ivl; + const socklen_t optlen = sizeof(int); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_nak_bo_ivl failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_NAK_RPT_IVL, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_nak_rpt_ivl_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + const int optname = PGM_NAK_RPT_IVL; + const int nak_rpt_ivl = pgm_msecs(1000); + const void* optval = &nak_rpt_ivl; + const socklen_t optlen = sizeof(int); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_nak_rpt_ivl failed"); +} +END_TEST + +START_TEST (test_set_nak_rpt_ivl_fail_001) +{ + const int optname = PGM_NAK_RPT_IVL; + const int nak_rpt_ivl = pgm_msecs(1000); + const void* optval = &nak_rpt_ivl; + const socklen_t optlen = sizeof(int); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_nak_rpt_ivl failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_NAK_RDATA_IVL, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_nak_rdata_ivl_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + const int optname = PGM_NAK_RDATA_IVL; + const int nak_rdata_ivl = pgm_msecs(1000); + const void* optval = &nak_rdata_ivl; + const socklen_t optlen = sizeof(int); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_nak_rdata_ivl failed"); +} +END_TEST + +START_TEST (test_set_nak_rdata_ivl_fail_001) +{ + const int optname = PGM_NAK_RDATA_IVL; + const int nak_rdata_ivl = pgm_msecs(1000); + const void* optval = &nak_rdata_ivl; + const socklen_t optlen = sizeof(int); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_nak_rdata_ivl failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_NAK_DATA_RETRIES, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_nak_data_retries_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + const int optname = PGM_NAK_DATA_RETRIES; + const int retries = 1000; + const void* optval = &retries; + const socklen_t optlen = sizeof(int); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_nak_data_retries failed"); +} +END_TEST + +START_TEST (test_set_nak_data_retries_fail_001) +{ + const int optname = PGM_NAK_DATA_RETRIES; + const int retries = 1000; + const void* optval = &retries; + const socklen_t optlen = sizeof(int); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_nak_data_retries failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_NAK_NCF_RETRIES, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_nak_ncf_retries_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + const int optname = PGM_NAK_NCF_RETRIES; + const int retries = 1000; + const void* optval = &retries; + const socklen_t optlen = sizeof(int); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_ncf_data_retries failed"); +} +END_TEST + +START_TEST (test_set_nak_ncf_retries_fail_001) +{ + const int optname = PGM_NAK_NCF_RETRIES; + const int retries = 1000; + const void* optval = &retries; + const socklen_t optlen = sizeof(int); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_ncf_data_retries failed"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_peer_unref = tcase_create ("peer_unref"); + suite_add_tcase (s, tc_peer_unref); + tcase_add_checked_fixture (tc_peer_unref, mock_setup, NULL); + tcase_add_test (tc_peer_unref, test_peer_unref_pass_001); + tcase_add_test_raise_signal (tc_peer_unref, test_peer_unref_fail_001, SIGABRT); + +/* formally check-peer-nak-state */ + TCase* tc_check_peer_state = tcase_create ("check-peer-state"); + suite_add_tcase (s, tc_check_peer_state); + tcase_add_checked_fixture (tc_check_peer_state, mock_setup, NULL); + tcase_add_test (tc_check_peer_state, test_check_peer_state_pass_001); + tcase_add_test_raise_signal (tc_check_peer_state, test_check_peer_state_fail_001, SIGABRT); + +/* formally min-nak-expiry */ + TCase* tc_min_receiver_expiry = tcase_create ("min-receiver-expiry"); + suite_add_tcase (s, tc_min_receiver_expiry); + tcase_add_checked_fixture (tc_min_receiver_expiry, mock_setup, NULL); + tcase_add_test (tc_min_receiver_expiry, test_min_receiver_expiry_pass_001); + tcase_add_test_raise_signal (tc_min_receiver_expiry, test_min_receiver_expiry_fail_001, SIGABRT); + + TCase* tc_set_rxw_sqns = tcase_create ("set-rxw_sqns"); + suite_add_tcase (s, tc_set_rxw_sqns); + tcase_add_checked_fixture (tc_set_rxw_sqns, mock_setup, NULL); + tcase_add_test (tc_set_rxw_sqns, test_set_rxw_sqns_pass_001); + tcase_add_test (tc_set_rxw_sqns, test_set_rxw_sqns_fail_001); + + TCase* tc_set_rxw_secs = tcase_create ("set-rxw-secs"); + suite_add_tcase (s, tc_set_rxw_secs); + tcase_add_checked_fixture (tc_set_rxw_secs, mock_setup, NULL); + tcase_add_test (tc_set_rxw_secs, test_set_rxw_secs_pass_001); + tcase_add_test (tc_set_rxw_secs, test_set_rxw_secs_fail_001); + + TCase* tc_set_rxw_max_rte = tcase_create ("set-rxw-max-rte"); + suite_add_tcase (s, tc_set_rxw_max_rte); + tcase_add_checked_fixture (tc_set_rxw_max_rte, mock_setup, NULL); + tcase_add_test (tc_set_rxw_max_rte, test_set_rxw_max_rte_pass_001); + tcase_add_test (tc_set_rxw_max_rte, test_set_rxw_max_rte_fail_001); + + TCase* tc_set_peer_expiry = tcase_create ("set-peer-expiry"); + suite_add_tcase (s, tc_set_peer_expiry); + tcase_add_checked_fixture (tc_set_peer_expiry, mock_setup, NULL); + tcase_add_test (tc_set_peer_expiry, test_set_peer_expiry_pass_001); + tcase_add_test (tc_set_peer_expiry, test_set_peer_expiry_fail_001); + + TCase* tc_set_spmr_expiry = tcase_create ("set-spmr-expiry"); + suite_add_tcase (s, tc_set_spmr_expiry); + tcase_add_checked_fixture (tc_set_spmr_expiry, mock_setup, NULL); + tcase_add_test (tc_set_spmr_expiry, test_set_spmr_expiry_pass_001); + tcase_add_test (tc_set_spmr_expiry, test_set_spmr_expiry_fail_001); + + TCase* tc_set_nak_bo_ivl = tcase_create ("set-nak-bo-ivl"); + suite_add_tcase (s, tc_set_nak_bo_ivl); + tcase_add_checked_fixture (tc_set_nak_bo_ivl, mock_setup, NULL); + tcase_add_test (tc_set_nak_bo_ivl, test_set_nak_bo_ivl_pass_001); + tcase_add_test (tc_set_nak_bo_ivl, test_set_nak_bo_ivl_fail_001); + + TCase* tc_set_nak_rpt_ivl = tcase_create ("set-nak-rpt-ivl"); + suite_add_tcase (s, tc_set_nak_rpt_ivl); + tcase_add_checked_fixture (tc_set_nak_rpt_ivl, mock_setup, NULL); + tcase_add_test (tc_set_nak_rpt_ivl, test_set_nak_rpt_ivl_pass_001); + tcase_add_test (tc_set_nak_rpt_ivl, test_set_nak_rpt_ivl_fail_001); + + TCase* tc_set_nak_rdata_ivl = tcase_create ("set-nak-rdata-ivl"); + suite_add_tcase (s, tc_set_nak_rdata_ivl); + tcase_add_checked_fixture (tc_set_nak_rdata_ivl, mock_setup, NULL); + tcase_add_test (tc_set_nak_rdata_ivl, test_set_nak_rdata_ivl_pass_001); + tcase_add_test (tc_set_nak_rdata_ivl, test_set_nak_rdata_ivl_fail_001); + + TCase* tc_set_nak_data_retries = tcase_create ("set-nak-data-retries"); + suite_add_tcase (s, tc_set_nak_data_retries); + tcase_add_checked_fixture (tc_set_nak_data_retries, mock_setup, NULL); + tcase_add_test (tc_set_nak_data_retries, test_set_nak_data_retries_pass_001); + tcase_add_test (tc_set_nak_data_retries, test_set_nak_data_retries_fail_001); + + TCase* tc_set_nak_ncf_retries = tcase_create ("set-nak-ncf-retries"); + suite_add_tcase (s, tc_set_nak_ncf_retries); + tcase_add_checked_fixture (tc_set_nak_ncf_retries, mock_setup, NULL); + tcase_add_test (tc_set_nak_ncf_retries, test_set_nak_ncf_retries_pass_001); + tcase_add_test (tc_set_nak_ncf_retries, test_set_nak_ncf_retries_fail_001); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/recv.c b/3rdparty/openpgm-svn-r1085/pgm/recv.c new file mode 100644 index 0000000..63cbce5 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/recv.c @@ -0,0 +1,1059 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Transport recv API. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE +#include +#ifndef _WIN32 +# include +# include +# include /* _GNU_SOURCE for in6_pktinfo */ +#else +# include +# include +#endif +#include +#include +#include +#include +#include +#include + + +//#define RECV_DEBUG + +#ifndef RECV_DEBUG +# define PGM_DISABLE_ASSERT +#endif + +#ifdef _WIN32 +# define cmsghdr wsacmsghdr +# define CMSG_FIRSTHDR(msg) WSA_CMSG_FIRSTHDR(msg) +# define CMSG_NXTHDR(msg, cmsg) WSA_CMSG_NXTHDR(msg, cmsg) +# define CMSG_DATA(cmsg) WSA_CMSG_DATA(cmsg) +# define CMSG_SPACE(len) WSA_CMSG_SPACE(len) +# define CMSG_LEN(len) WSA_CMSG_LEN(len) +#endif + + +/* read a packet into a PGM skbuff + * on success returns packet length, on closed socket returns 0, + * on error returns -1. + */ + +static +ssize_t +recvskb ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t* const restrict skb, + const int flags, + struct sockaddr* const restrict src_addr, + const socklen_t src_addrlen, + struct sockaddr* const restrict dst_addr, + const socklen_t dst_addrlen + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + pgm_assert (NULL != src_addr); + pgm_assert (src_addrlen > 0); + pgm_assert (NULL != dst_addr); + pgm_assert (dst_addrlen > 0); + + pgm_debug ("recvskb (sock:%p skb:%p flags:%d src-addr:%p src-addrlen:%d dst-addr:%p dst-addrlen:%d)", + (void*)sock, (void*)skb, flags, (void*)src_addr, (int)src_addrlen, (void*)dst_addr, (int)dst_addrlen); + + if (PGM_UNLIKELY(sock->is_destroyed)) + return 0; + +#ifdef CONFIG_TARGET_WINE + socklen_t fromlen = src_addrlen; + const ssize_t len = recvfrom (sock->recv_sock, skb->head, sock->max_tpdu, 0, src_addr, &fromlen); + if (len <= 0) + return len; +#else + struct pgm_iovec iov = { + .iov_base = skb->head, + .iov_len = sock->max_tpdu + }; + char aux[ 1024 ]; +# ifndef _WIN32 + struct msghdr msg = { + .msg_name = src_addr, + .msg_namelen = src_addrlen, + .msg_iov = (void*)&iov, + .msg_iovlen = 1, + .msg_control = aux, + .msg_controllen = sizeof(aux), + .msg_flags = 0 + }; + + ssize_t len = recvmsg (sock->recv_sock, &msg, flags); + if (len <= 0) + return len; +# else /* !_WIN32 */ + WSAMSG msg = { + .name = (LPSOCKADDR)src_addr, + .namelen = src_addrlen, + .lpBuffers = (LPWSABUF)&iov, + .dwBufferCount = 1, + .dwFlags = 0 + }; + msg.Control.buf = aux; + msg.Control.len = sizeof(aux); + DWORD len; + if (SOCKET_ERROR == pgm_WSARecvMsg (sock->recv_sock, &msg, &len, NULL, NULL)) { + return -1; + } +# endif /* !_WIN32 */ +#endif /* !CONFIG_TARGET_WINE */ + +#ifdef PGM_DEBUG + if (PGM_UNLIKELY(pgm_loss_rate > 0)) { + const unsigned percent = pgm_rand_int_range (&sock->rand_, 0, 100); + if (percent <= pgm_loss_rate) { + pgm_debug ("Simulated packet loss"); +# ifndef _WIN32 + errno = EAGAIN; +# else + WSASetLastError (WSAEWOULDBLOCK); +# endif + return -1; + } + } +#endif + + skb->sock = sock; + skb->tstamp = pgm_time_update_now(); + skb->data = skb->head; + skb->len = len; + skb->zero_padded = 0; + skb->tail = (char*)skb->data + len; + +#ifdef CONFIG_TARGET_WINE + pgm_assert (pgm_sockaddr_len (&sock->recv_gsr[0].gsr_group) <= dst_addrlen); + memcpy (dst_addr, &sock->recv_gsr[0].gsr_group, pgm_sockaddr_len (&sock->recv_gsr[0].gsr_group)); +#else + if (sock->udp_encap_ucast_port || + AF_INET6 == pgm_sockaddr_family (src_addr)) + { +#ifdef CONFIG_HAVE_WSACMSGHDR + WSACMSGHDR* cmsg; +#else + struct cmsghdr* cmsg; +#endif + for (cmsg = CMSG_FIRSTHDR(&msg); + cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) + { +/* both IP_PKTINFO and IP_RECVDSTADDR exist on OpenSolaris, so capture + * each type if defined. + */ +#ifdef IP_PKTINFO + if (IPPROTO_IP == cmsg->cmsg_level && + IP_PKTINFO == cmsg->cmsg_type) + { + const void* pktinfo = CMSG_DATA(cmsg); +/* discard on invalid address */ + if (PGM_UNLIKELY(NULL == pktinfo)) { + pgm_debug ("in_pktinfo is NULL"); + return -1; + } + const struct in_pktinfo* in = pktinfo; + struct sockaddr_in s4; + memset (&s4, 0, sizeof(s4)); + s4.sin_family = AF_INET; + s4.sin_addr.s_addr = in->ipi_addr.s_addr; + memcpy (dst_addr, &s4, sizeof(s4)); + break; + } +#endif +#ifdef IP_RECVDSTADDR + if (IPPROTO_IP == cmsg->cmsg_level && + IP_RECVDSTADDR == cmsg->cmsg_type) + { + const void* recvdstaddr = CMSG_DATA(cmsg); +/* discard on invalid address */ + if (PGM_UNLIKELY(NULL == recvdstaddr)) { + pgm_debug ("in_recvdstaddr is NULL"); + return -1; + } + const struct in_addr* in = recvdstaddr; + struct sockaddr_in s4; + memset (&s4, 0, sizeof(s4)); + s4.sin_family = AF_INET; + s4.sin_addr.s_addr = in->s_addr; + memcpy (dst_addr, &s4, sizeof(s4)); + break; + } +#endif +#if !defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR) +# error "No defined CMSG type for IPv4 destination address." +#endif + + if (IPPROTO_IPV6 == cmsg->cmsg_level && + IPV6_PKTINFO == cmsg->cmsg_type) + { + const void* pktinfo = CMSG_DATA(cmsg); +/* discard on invalid address */ + if (PGM_UNLIKELY(NULL == pktinfo)) { + pgm_debug ("in6_pktinfo is NULL"); + return -1; + } + const struct in6_pktinfo* in6 = pktinfo; + struct sockaddr_in6 s6; + memset (&s6, 0, sizeof(s6)); + s6.sin6_family = AF_INET6; + s6.sin6_addr = in6->ipi6_addr; + s6.sin6_scope_id = in6->ipi6_ifindex; + memcpy (dst_addr, &s6, sizeof(s6)); +/* does not set flow id */ + break; + } + } + } +#endif + return len; +} + +/* upstream = receiver to source, peer-to-peer = receive to receiver + * + * NB: SPMRs can be upstream or peer-to-peer, if the packet is multicast then its + * a peer-to-peer message, if its unicast its an upstream message. + * + * returns TRUE on valid processed packet, returns FALSE on discarded packet. + */ + +static +bool +on_upstream ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + pgm_assert_cmpuint (skb->pgm_header->pgm_dport, ==, sock->tsi.sport); + + pgm_debug ("on_upstream (sock:%p skb:%p)", + (const void*)sock, (const void*)skb); + + if (PGM_UNLIKELY(!sock->can_send_data)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded packet for muted source.")); + goto out_discarded; + } + +/* unicast upstream message, note that dport & sport are reversed */ + if (PGM_UNLIKELY(skb->pgm_header->pgm_sport != sock->dport)) { +/* its upstream/peer-to-peer for another session */ + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded packet on data-destination port mismatch.")); + goto out_discarded; + } + + if (PGM_UNLIKELY(!pgm_gsi_equal (&skb->tsi.gsi, &sock->tsi.gsi))) { +/* its upstream/peer-to-peer for another session */ + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded packet on data-destination port mismatch.")); + goto out_discarded; + } + +/* advance SKB pointer to PGM type header */ + skb->data = (char*)skb->data + sizeof(struct pgm_header); + skb->len -= sizeof(struct pgm_header); + + switch (skb->pgm_header->pgm_type) { + case PGM_NAK: + if (PGM_UNLIKELY(!pgm_on_nak (sock, skb))) + goto out_discarded; + break; + + case PGM_NNAK: + if (PGM_UNLIKELY(!pgm_on_nnak (sock, skb))) + goto out_discarded; + break; + + case PGM_SPMR: + if (PGM_UNLIKELY(!pgm_on_spmr (sock, NULL, skb))) + goto out_discarded; + break; + + case PGM_ACK: + if (PGM_UNLIKELY(!pgm_on_ack (sock, skb))) + goto out_discarded; + break; + + case PGM_POLR: + default: + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded unsupported PGM type packet.")); + goto out_discarded; + } + + return TRUE; +out_discarded: + sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED]++; + return FALSE; +} + +/* peer to peer message, either multicast NAK or multicast SPMR. + * + * returns TRUE on valid processed packet, returns FALSE on discarded packet. + */ + +static +bool +on_peer ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t* const restrict skb, + pgm_peer_t** restrict source + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + pgm_assert_cmpuint (skb->pgm_header->pgm_dport, !=, sock->tsi.sport); + pgm_assert (NULL != source); + + pgm_debug ("on_peer (sock:%p skb:%p source:%p)", + (const void*)sock, (const void*)skb, (const void*)source); + +/* we are not the source */ + if (PGM_UNLIKELY(!sock->can_recv_data)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded packet for muted receiver.")); + goto out_discarded; + } + +/* unicast upstream message, note that dport & sport are reversed */ + if (PGM_UNLIKELY(skb->pgm_header->pgm_sport != sock->dport)) { +/* its upstream/peer-to-peer for another session */ + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded packet on data-destination port mismatch.")); + goto out_discarded; + } + +/* check to see the source this peer-to-peer message is about is in our peer list */ + pgm_tsi_t upstream_tsi; + memcpy (&upstream_tsi.gsi, &skb->tsi.gsi, sizeof(pgm_gsi_t)); + upstream_tsi.sport = skb->pgm_header->pgm_dport; + + pgm_rwlock_reader_lock (&sock->peers_lock); + *source = pgm_hashtable_lookup (sock->peers_hashtable, &upstream_tsi); + pgm_rwlock_reader_unlock (&sock->peers_lock); + if (PGM_UNLIKELY(NULL == *source)) { +/* this source is unknown, we don't care about messages about it */ + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded peer packet about new source.")); + goto out_discarded; + } + +/* advance SKB pointer to PGM type header */ + skb->data = (char*)skb->data + sizeof(struct pgm_header); + skb->len -= sizeof(struct pgm_header); + + switch (skb->pgm_header->pgm_type) { + case PGM_NAK: + if (PGM_UNLIKELY(!pgm_on_peer_nak (sock, *source, skb))) + goto out_discarded; + break; + + case PGM_SPMR: + if (PGM_UNLIKELY(!pgm_on_spmr (sock, *source, skb))) + goto out_discarded; + break; + + case PGM_NNAK: + case PGM_POLR: + default: + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded unsupported PGM type packet.")); + goto out_discarded; + } + + return TRUE; +out_discarded: + if (*source) + (*source)->cumulative_stats[PGM_PC_RECEIVER_PACKETS_DISCARDED]++; + else if (sock->can_send_data) + sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED]++; + return FALSE; +} + +/* source to receiver message + * + * returns TRUE on valid processed packet, returns FALSE on discarded packet. + */ + +static +bool +on_downstream ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t* const restrict skb, + struct sockaddr* const restrict src_addr, + struct sockaddr* const restrict dst_addr, + pgm_peer_t** restrict source + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + pgm_assert (NULL != src_addr); + pgm_assert (NULL != dst_addr); + pgm_assert (NULL != source); + +#ifdef RECV_DEBUG + char saddr[INET6_ADDRSTRLEN], daddr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop (src_addr, saddr, sizeof(saddr)); + pgm_sockaddr_ntop (dst_addr, daddr, sizeof(daddr)); + pgm_debug ("on_downstream (sock:%p skb:%p src-addr:%s dst-addr:%s source:%p)", + (const void*)sock, (const void*)skb, saddr, daddr, (const void*)source); +#endif + + if (PGM_UNLIKELY(!sock->can_recv_data)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded packet for muted receiver.")); + goto out_discarded; + } + +/* pgm packet DPORT contains our sock DPORT */ + if (PGM_UNLIKELY(skb->pgm_header->pgm_dport != sock->dport)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded packet on data-destination port mismatch.")); + goto out_discarded; + } + +/* search for TSI peer context or create a new one */ + if (PGM_LIKELY(pgm_tsi_hash (&skb->tsi) == sock->last_hash_key && + NULL != sock->last_hash_value)) + { + *source = sock->last_hash_value; + } + else + { + pgm_rwlock_reader_lock (&sock->peers_lock); + *source = pgm_hashtable_lookup_extended (sock->peers_hashtable, &skb->tsi, &sock->last_hash_key); + pgm_rwlock_reader_unlock (&sock->peers_lock); + if (PGM_UNLIKELY(NULL == *source)) { + *source = pgm_new_peer (sock, + &skb->tsi, + (struct sockaddr*)src_addr, pgm_sockaddr_len(src_addr), + (struct sockaddr*)dst_addr, pgm_sockaddr_len(dst_addr), + skb->tstamp); + } + sock->last_hash_value = *source; + } + + (*source)->cumulative_stats[PGM_PC_RECEIVER_BYTES_RECEIVED] += skb->len; + (*source)->last_packet = skb->tstamp; + + skb->data = (void*)( skb->pgm_header + 1 ); + skb->len -= sizeof(struct pgm_header); + +/* handle PGM packet type */ + switch (skb->pgm_header->pgm_type) { + case PGM_ODATA: + case PGM_RDATA: + if (PGM_UNLIKELY(!pgm_on_data (sock, *source, skb))) + goto out_discarded; + sock->rx_buffer = pgm_alloc_skb (sock->max_tpdu); + break; + + case PGM_NCF: + if (PGM_UNLIKELY(!pgm_on_ncf (sock, *source, skb))) + goto out_discarded; + break; + + case PGM_SPM: + if (PGM_UNLIKELY(!pgm_on_spm (sock, *source, skb))) + goto out_discarded; + +/* update group NLA if appropriate */ + if (PGM_LIKELY(pgm_sockaddr_is_addr_multicast ((struct sockaddr*)dst_addr))) + memcpy (&(*source)->group_nla, dst_addr, pgm_sockaddr_len(dst_addr)); + break; + +#ifdef CONFIG_PGM_POLLING + case PGM_POLL: + if (PGM_UNLIKELY(!pgm_on_poll (sock, *source, skb))) + goto out_discarded; + break; +#endif + + default: + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded unsupported PGM type packet.")); + goto out_discarded; + } + + return TRUE; +out_discarded: + if (*source) + (*source)->cumulative_stats[PGM_PC_RECEIVER_PACKETS_DISCARDED]++; + else if (sock->can_send_data) + sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED]++; + return FALSE; +} + +/* process a pgm packet + * + * returns TRUE on valid processed packet, returns FALSE on discarded packet. + */ +static +bool +on_pgm ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t* const restrict skb, + struct sockaddr* const restrict src_addr, + struct sockaddr* const restrict dst_addr, + pgm_peer_t** restrict source + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + pgm_assert (NULL != src_addr); + pgm_assert (NULL != dst_addr); + pgm_assert (NULL != source); + +#ifdef RECV_DEBUG + char saddr[INET6_ADDRSTRLEN], daddr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop (src_addr, saddr, sizeof(saddr)); + pgm_sockaddr_ntop (dst_addr, daddr, sizeof(daddr)); + pgm_debug ("on_pgm (sock:%p skb:%p src-addr:%s dst-addr:%s source:%p)", + (const void*)sock, (const void*)skb, saddr, daddr, (const void*)source); +#endif + + if (PGM_IS_DOWNSTREAM (skb->pgm_header->pgm_type)) + return on_downstream (sock, skb, src_addr, dst_addr, source); + if (skb->pgm_header->pgm_dport == sock->tsi.sport) + { + if (PGM_IS_UPSTREAM (skb->pgm_header->pgm_type) || + PGM_IS_PEER (skb->pgm_header->pgm_type)) + { + return on_upstream (sock, skb); + } + } + else if (PGM_IS_PEER (skb->pgm_header->pgm_type)) + return on_peer (sock, skb, source); + + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded unknown PGM packet.")); + if (sock->can_send_data) + sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED]++; + return FALSE; +} + +/* block on receiving socket whilst holding sock::waiting-mutex + * returns EAGAIN for waiting data, returns EINTR for waiting timer event, + * returns ENOENT on closed sock, and returns EFAULT for libc error. + */ + +static +int +wait_for_event ( + pgm_sock_t* const sock + ) +{ + int n_fds = 3; + +/* pre-conditions */ + pgm_assert (NULL != sock); + + pgm_debug ("wait_for_event (sock:%p)", (const void*)sock); + + do { + if (PGM_UNLIKELY(sock->is_destroyed)) + return ENOENT; + + if (sock->can_send_data && !pgm_txw_retransmit_is_empty (sock->window)) +/* tight loop on blocked send */ + pgm_on_deferred_nak (sock); + +#ifdef CONFIG_HAVE_POLL + struct pollfd fds[ n_fds ]; + memset (fds, 0, sizeof(fds)); + const int status = pgm_poll_info (sock, fds, &n_fds, POLLIN); + pgm_assert (-1 != status); +#else + fd_set readfds; + FD_ZERO(&readfds); + const int status = pgm_select_info (sock, &readfds, NULL, &n_fds); + pgm_assert (-1 != status); +#endif /* CONFIG_HAVE_POLL */ + +/* flush any waiting notifications */ + if (sock->is_pending_read) { + pgm_notify_clear (&sock->pending_notify); + sock->is_pending_read = FALSE; + } + + int timeout; + if (sock->can_send_data && !pgm_txw_retransmit_is_empty (sock->window)) + timeout = 0; + else + timeout = pgm_timer_expiration (sock); + +#ifdef CONFIG_HAVE_POLL + const int ready = poll (fds, n_fds, timeout /* μs */ / 1000 /* to ms */); +#else + struct timeval tv_timeout = { + .tv_sec = timeout > 1000000L ? timeout / 1000000UL : 0, + .tv_usec = timeout > 1000000L ? timeout % 1000000UL : timeout + }; + const int ready = select (n_fds, &readfds, NULL, NULL, &tv_timeout); +#endif + if (PGM_UNLIKELY(-1 == ready)) { + pgm_debug ("block returned errno=%i",errno); + return EFAULT; + } else if (ready > 0) { + pgm_debug ("recv again on empty"); + return EAGAIN; + } + } while (pgm_timer_check (sock)); + pgm_debug ("state generated event"); + return EINTR; +} + +/* data incoming on receive sockets, can be from a sender or receiver, or simply bogus. + * for IPv4 we receive the IP header to handle fragmentation, for IPv6 we cannot, but the + * underlying stack handles this for us. + * + * recvmsgv reads a vector of apdus each contained in a IO scatter/gather array. + * + * can be called due to event from incoming socket(s) or timer induced data loss. + * + * On success, returns PGM_IO_STATUS_NORMAL and saves the count of bytes read + * into _bytes_read. With non-blocking sockets a block returns + * PGM_IO_STATUS_WOULD_BLOCK. When rate limited sending repair data, returns + * PGM_IO_STATUS_RATE_LIMITED and caller should wait. During recovery state, + * returns PGM_IO_STATUS_TIMER_PENDING and caller should also wait. On + * unrecoverable dataloss, returns PGM_IO_STATUS_CONN_RESET. If connection is + * closed, returns PGM_IO_STATUS_EOF. On error, returns PGM_IO_STATUS_ERROR. + */ + +int +pgm_recvmsgv ( + pgm_sock_t* const restrict sock, + struct pgm_msgv_t* const restrict msg_start, + const size_t msg_len, + const int flags, /* MSG_DONTWAIT for non-blocking */ + size_t* restrict _bytes_read, /* may be NULL */ + pgm_error_t** restrict error + ) +{ + int status = PGM_IO_STATUS_WOULD_BLOCK; + + pgm_debug ("pgm_recvmsgv (sock:%p msg-start:%p msg-len:%zu flags:%d bytes-read:%p error:%p)", + (void*)sock, (void*)msg_start, msg_len, flags, (void*)_bytes_read, (void*)error); + +/* parameters */ + pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); + if (PGM_LIKELY(msg_len)) pgm_return_val_if_fail (NULL != msg_start, PGM_IO_STATUS_ERROR); + +/* shutdown */ + if (PGM_UNLIKELY(!pgm_rwlock_reader_trylock (&sock->lock))) + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + +/* state */ + if (PGM_UNLIKELY(!sock->is_bound || sock->is_destroyed)) + { + pgm_rwlock_reader_unlock (&sock->lock); + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + } + +/* pre-conditions */ + pgm_assert (NULL != sock->rx_buffer); + pgm_assert (sock->max_tpdu > 0); + if (sock->can_recv_data) { + pgm_assert (NULL != sock->peers_hashtable); + pgm_assert_cmpuint (sock->nak_bo_ivl, >, 1); + pgm_assert (pgm_notify_is_valid (&sock->pending_notify)); + } + +/* receiver */ + pgm_mutex_lock (&sock->receiver_mutex); + + if (PGM_UNLIKELY(sock->is_reset)) { + pgm_assert (NULL != sock->peers_pending); + pgm_assert (NULL != sock->peers_pending->data); + pgm_peer_t* peer = sock->peers_pending->data; + if (flags & MSG_ERRQUEUE) + pgm_set_reset_error (sock, peer, msg_start); + else if (error) { + char tsi[PGM_TSISTRLEN]; + pgm_tsi_print_r (&peer->tsi, tsi, sizeof(tsi)); + pgm_set_error (error, + PGM_ERROR_DOMAIN_RECV, + PGM_ERROR_CONNRESET, + _("Transport has been reset on unrecoverable loss from %s."), + tsi); + } + if (!sock->is_abort_on_reset) + sock->is_reset = !sock->is_reset; + pgm_mutex_unlock (&sock->receiver_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_RESET; + } + +/* timer status */ + if (pgm_timer_check (sock) && + !pgm_timer_dispatch (sock)) + { +/* block on send-in-recv */ + status = PGM_IO_STATUS_RATE_LIMITED; + } +/* NAK status */ + else if (sock->can_send_data) + { + if (!pgm_txw_retransmit_is_empty (sock->window)) + { + if (!pgm_on_deferred_nak (sock)) + status = PGM_IO_STATUS_RATE_LIMITED; + } + else + pgm_notify_clear (&sock->rdata_notify); + } + + size_t bytes_read = 0; + unsigned data_read = 0; + struct pgm_msgv_t* pmsg = msg_start; + const struct pgm_msgv_t* msg_end = msg_start + msg_len - 1; + + if (PGM_UNLIKELY(0 == ++(sock->last_commit))) + ++(sock->last_commit); + + /* second, flush any remaining contiguous messages from previous call(s) */ + if (sock->peers_pending) { + if (0 != pgm_flush_peers_pending (sock, &pmsg, msg_end, &bytes_read, &data_read)) + goto out; +/* returns on: reset or full buffer */ + } + +/* read the data: + * + * We cannot actually block here as packets pushed by the timers need to be addressed too. + */ + struct sockaddr_storage src, dst; + ssize_t len; + size_t bytes_received = 0; + +recv_again: + + len = recvskb (sock, + sock->rx_buffer, /* PGM skbuff */ + 0, + (struct sockaddr*)&src, + sizeof(src), + (struct sockaddr*)&dst, + sizeof(dst)); + if (len < 0) + { +#ifndef _WIN32 + const int save_errno = errno; + if (PGM_LIKELY(EAGAIN == save_errno)) { + goto check_for_repeat; + } + status = PGM_IO_STATUS_ERROR; + pgm_set_error (error, + PGM_ERROR_DOMAIN_RECV, + pgm_error_from_errno (save_errno), + _("Transport socket error: %s"), + strerror (save_errno)); +#else + const int save_wsa_errno = WSAGetLastError (); + if (PGM_LIKELY(WSAEWOULDBLOCK == save_wsa_errno)) { + goto check_for_repeat; + } + status = PGM_IO_STATUS_ERROR; + pgm_set_error (error, + PGM_ERROR_DOMAIN_RECV, + pgm_error_from_wsa_errno (save_wsa_errno), + _("Transport socket error: %s"), + pgm_wsastrerror (save_wsa_errno)); +#endif /* !_WIN32 */ + goto out; + } + else if (0 == len) + { +/* cannot return NORMAL/0 as that is valid payload with SKB */ + status = PGM_IO_STATUS_EOF; + goto out; + } + else + { + bytes_received += len; + } + + pgm_error_t* err = NULL; + const bool is_valid = (sock->udp_encap_ucast_port || AF_INET6 == src.ss_family) ? + pgm_parse_udp_encap (sock->rx_buffer, &err) : + pgm_parse_raw (sock->rx_buffer, (struct sockaddr*)&dst, &err); + if (PGM_UNLIKELY(!is_valid)) + { +/* inherently cannot determine PGM_PC_RECEIVER_CKSUM_ERRORS unless only one receiver */ + pgm_trace (PGM_LOG_ROLE_NETWORK, + _("Discarded invalid packet: %s"), + (err && err->message) ? err->message : "(null)"); + pgm_error_free (err); + if (sock->can_send_data) { + if (err && PGM_ERROR_CKSUM == err->code) + sock->cumulative_stats[PGM_PC_SOURCE_CKSUM_ERRORS]++; + sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED]++; + } + goto recv_again; + } + + pgm_peer_t* source = NULL; + if (PGM_UNLIKELY(!on_pgm (sock, sock->rx_buffer, (struct sockaddr*)&src, (struct sockaddr*)&dst, &source))) + goto recv_again; + +/* check whether this source has waiting data */ + if (source && pgm_peer_has_pending (source)) { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("New pending data.")); + pgm_peer_set_pending (sock, source); + } + +flush_pending: +/* flush any congtiguous packets generated by the receipt of this packet */ + if (sock->peers_pending) + { + if (0 != pgm_flush_peers_pending (sock, &pmsg, msg_end, &bytes_read, &data_read)) + { +/* recv vector is now full */ + goto out; + } + } + +check_for_repeat: +/* repeat if non-blocking and not full */ + if (sock->is_nonblocking || + flags & MSG_DONTWAIT) + { + if (len > 0 && pmsg <= msg_end) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Recv again on not-full")); + goto recv_again; /* \:D/ */ + } + } + else + { +/* repeat if blocking and empty, i.e. received non data packet. + */ + if (0 == data_read) { + const int wait_status = wait_for_event (sock); + switch (wait_status) { + case EAGAIN: + goto recv_again; + case EINTR: + if (!pgm_timer_dispatch (sock)) + goto check_for_repeat; + goto flush_pending; + case ENOENT: + pgm_mutex_unlock (&sock->receiver_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_EOF; + case EFAULT: + pgm_set_error (error, + PGM_ERROR_DOMAIN_RECV, + pgm_error_from_errno (errno), + _("Waiting for event: %s"), +#ifndef _WIN32 + strerror (errno) +#else + pgm_wsastrerror (WSAGetLastError()) /* from select() */ +#endif + ); + pgm_mutex_unlock (&sock->receiver_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_ERROR; + default: + pgm_assert_not_reached(); + } + } + } + +out: + if (0 == data_read) + { +/* clear event notification */ + if (sock->is_pending_read) { + pgm_notify_clear (&sock->pending_notify); + sock->is_pending_read = FALSE; + } +/* report data loss */ + if (PGM_UNLIKELY(sock->is_reset)) { + pgm_assert (NULL != sock->peers_pending); + pgm_assert (NULL != sock->peers_pending->data); + pgm_peer_t* peer = sock->peers_pending->data; + if (flags & MSG_ERRQUEUE) + pgm_set_reset_error (sock, peer, msg_start); + else if (error) { + char tsi[PGM_TSISTRLEN]; + pgm_tsi_print_r (&peer->tsi, tsi, sizeof(tsi)); + pgm_set_error (error, + PGM_ERROR_DOMAIN_RECV, + PGM_ERROR_CONNRESET, + _("Transport has been reset on unrecoverable loss from %s."), + tsi); + } + if (!sock->is_abort_on_reset) + sock->is_reset = !sock->is_reset; + pgm_mutex_unlock (&sock->receiver_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_RESET; + } + pgm_mutex_unlock (&sock->receiver_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + if (PGM_IO_STATUS_WOULD_BLOCK == status && + ( sock->can_send_data || + ( sock->can_recv_data && NULL != sock->peers_list ))) + { + status = PGM_IO_STATUS_TIMER_PENDING; + } + return status; + } + + if (sock->peers_pending) + { +/* set event notification for additional available data */ + if (sock->is_pending_read && sock->is_edge_triggered_recv) + { +/* empty pending-pipe */ + pgm_notify_clear (&sock->pending_notify); + sock->is_pending_read = FALSE; + } + else if (!sock->is_pending_read && !sock->is_edge_triggered_recv) + { +/* fill pending-pipe */ + pgm_notify_send (&sock->pending_notify); + sock->is_pending_read = TRUE; + } + } + + if (NULL != _bytes_read) + *_bytes_read = bytes_read; + pgm_mutex_unlock (&sock->receiver_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_NORMAL; +} + +/* read one contiguous apdu and return as a IO scatter/gather array. msgv is owned by + * the caller, tpdu contents are owned by the receive window. + * + * on success, returns PGM_IO_STATUS_NORMAL. + */ + +int +pgm_recvmsg ( + pgm_sock_t* const restrict sock, + struct pgm_msgv_t* const restrict msgv, + const int flags, /* MSG_DONTWAIT for non-blocking */ + size_t* restrict bytes_read, /* may be NULL */ + pgm_error_t** restrict error + ) +{ + pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); + pgm_return_val_if_fail (NULL != msgv, PGM_IO_STATUS_ERROR); + + pgm_debug ("pgm_recvmsg (sock:%p msgv:%p flags:%d bytes_read:%p error:%p)", + (const void*)sock, (const void*)msgv, flags, (const void*)bytes_read, (const void*)error); + + return pgm_recvmsgv (sock, msgv, 1, flags, bytes_read, error); +} + +/* vanilla read function. copies from the receive window to the provided buffer + * location. the caller must provide an adequately sized buffer to store the largest + * expected apdu or else it will be truncated. + * + * on success, returns PGM_IO_STATUS_NORMAL. + */ + +int +pgm_recvfrom ( + pgm_sock_t* const restrict sock, + void* restrict buf, + const size_t buflen, + const int flags, /* MSG_DONTWAIT for non-blocking */ + size_t* restrict _bytes_read, /* may be NULL */ + struct pgm_sockaddr_t* restrict from, /* may be NULL */ + socklen_t* restrict fromlen, + pgm_error_t** restrict error + ) +{ + struct pgm_msgv_t msgv; + size_t bytes_read = 0; + + pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); + if (PGM_LIKELY(buflen)) pgm_return_val_if_fail (NULL != buf, PGM_IO_STATUS_ERROR); + if (fromlen) { + pgm_return_val_if_fail (NULL != from, PGM_IO_STATUS_ERROR); + pgm_return_val_if_fail (sizeof (struct pgm_sockaddr_t) == *fromlen, PGM_IO_STATUS_ERROR); + } + + pgm_debug ("pgm_recvfrom (sock:%p buf:%p buflen:%zu flags:%d bytes-read:%p from:%p from:%p error:%p)", + (const void*)sock, buf, buflen, flags, (const void*)_bytes_read, (const void*)from, (const void*)fromlen, (const void*)error); + + const int status = pgm_recvmsg (sock, &msgv, flags & ~(MSG_ERRQUEUE), &bytes_read, error); + if (PGM_IO_STATUS_NORMAL != status) + return status; + + size_t bytes_copied = 0; + struct pgm_sk_buff_t** skb = msgv.msgv_skb; + struct pgm_sk_buff_t* pskb = *skb; + + if (from) { + from->sa_port = ntohs (sock->dport); + from->sa_addr.sport = ntohs (pskb->tsi.sport); + memcpy (&from->sa_addr.gsi, &pskb->tsi.gsi, sizeof(pgm_gsi_t)); + } + + while (bytes_copied < bytes_read) { + size_t copy_len = pskb->len; + if (bytes_copied + copy_len > buflen) { + pgm_warn (_("APDU truncated, original length %zu bytes."), + bytes_read); + copy_len = buflen - bytes_copied; + bytes_read = buflen; + } + memcpy ((char*)buf + bytes_copied, pskb->data, copy_len); + bytes_copied += copy_len; + pskb = *(++skb); + } + if (_bytes_read) + *_bytes_read = bytes_copied; + return PGM_IO_STATUS_NORMAL; +} + +/* Basic recv operation, copying data from window to application. + * + * on success, returns PGM_IO_STATUS_NORMAL. + */ + +int +pgm_recv ( + pgm_sock_t* const restrict sock, + void* restrict buf, + const size_t buflen, + const int flags, /* MSG_DONTWAIT for non-blocking */ + size_t* const restrict bytes_read, /* may be NULL */ + pgm_error_t** restrict error + ) +{ + pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); + if (PGM_LIKELY(buflen)) pgm_return_val_if_fail (NULL != buf, PGM_IO_STATUS_ERROR); + + pgm_debug ("pgm_recv (sock:%p buf:%p buflen:%zu flags:%d bytes-read:%p error:%p)", + (const void*)sock, buf, buflen, flags, (const void*)bytes_read, (const void*)error); + + return pgm_recvfrom (sock, buf, buflen, flags, bytes_read, NULL, NULL, error); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/recv_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/recv_unittest.c new file mode 100644 index 0000000..2719897 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/recv_unittest.c @@ -0,0 +1,1600 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for transport recv api + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include /* _GNU_SOURCE for in6_pktinfo */ +#include +#include +#include + + +/* mock state */ + +#define TEST_NETWORK "" +#define TEST_DPORT 7500 +#define TEST_SPORT 1000 +#define TEST_SRC_ADDR "127.0.0.1" +#define TEST_END_ADDR "127.0.0.2" +#define TEST_GROUP_ADDR "239.192.0.1" +#define TEST_PEER_ADDR "127.0.0.6" +#define TEST_DLR_ADDR "127.0.0.9" +#define TEST_MAX_TPDU 1500 +#define TEST_TXW_SQNS 32 +#define TEST_RXW_SQNS 32 +#define TEST_HOPS 16 +#define TEST_SPM_AMBIENT ( pgm_secs(30) ) +#define TEST_SPM_HEARTBEAT_INIT { pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(1300), pgm_secs(7), pgm_secs(16), pgm_secs(25), pgm_secs(30) } +#define TEST_PEER_EXPIRY ( pgm_secs(300) ) +#define TEST_SPMR_EXPIRY ( pgm_msecs(250) ) +#define TEST_NAK_BO_IVL ( pgm_msecs(50) ) +#define TEST_NAK_RPT_IVL ( pgm_secs(2) ) +#define TEST_NAK_RDATA_IVL ( pgm_secs(2) ) +#define TEST_NAK_DATA_RETRIES 5 +#define TEST_NAK_NCF_RETRIES 2 + +struct mock_recvmsg_t { + struct msghdr* mr_msg; + ssize_t mr_retval; + int mr_errno; +}; + +struct pgm_peer_t; + +GList* mock_recvmsg_list = NULL; +static int mock_pgm_type = -1; +static gboolean mock_reset_on_spmr = FALSE; +static gboolean mock_data_on_spmr = FALSE; +static struct pgm_peer_t* mock_peer = NULL; +GList* mock_data_list = NULL; +unsigned mock_pgm_loss_rate = 0; + + +static ssize_t mock_recvmsg (int, struct msghdr*, int); + +#define pgm_parse_raw mock_pgm_parse_raw +#define pgm_parse_udp_encap mock_pgm_parse_udp_encap +#define pgm_verify_spm mock_pgm_verify_spm +#define pgm_verify_nak mock_pgm_verify_nak +#define pgm_verify_ncf mock_pgm_verify_ncf +#define pgm_poll_info mock_pgm_poll_info +#define pgm_set_reset_error mock_pgm_set_reset_error +#define pgm_flush_peers_pending mock_pgm_flush_peers_pending +#define pgm_peer_has_pending mock_pgm_peer_has_pending +#define pgm_peer_set_pending mock_pgm_peer_set_pending +#define pgm_txw_retransmit_is_empty mock_pgm_txw_retransmit_is_empty +#define pgm_rxw_create mock_pgm_rxw_create +#define pgm_rxw_readv mock_pgm_rxw_readv +#define pgm_new_peer mock_pgm_new_peer +#define pgm_on_data mock_pgm_on_data +#define pgm_on_spm mock_pgm_on_spm +#define pgm_on_ack mock_pgm_on_ack +#define pgm_on_nak mock_pgm_on_nak +#define pgm_on_deferred_nak mock_pgm_on_deferred_nak +#define pgm_on_peer_nak mock_pgm_on_peer_nak +#define pgm_on_nnak mock_pgm_on_nnak +#define pgm_on_ncf mock_pgm_on_ncf +#define pgm_on_spmr mock_pgm_on_spmr +#define pgm_sendto mock_pgm_sendto +#define pgm_timer_prepare mock_pgm_timer_prepare +#define pgm_timer_check mock_pgm_timer_check +#define pgm_timer_expiration mock_pgm_timer_expiration +#define pgm_timer_dispatch mock_pgm_timer_dispatch +#define pgm_time_now mock_pgm_time_now +#define pgm_time_update_now mock_pgm_time_update_now +#define recvmsg mock_recvmsg +#define pgm_loss_rate mock_pgm_loss_rate + +#define RECV_DEBUG +#include "recv.c" + + +pgm_rxw_t* mock_pgm_rxw_create (const pgm_tsi_t*, const uint16_t, const unsigned, const unsigned, const ssize_t, const uint32_t); +static pgm_time_t _mock_pgm_time_update_now (void); +pgm_time_update_func mock_pgm_time_update_now = _mock_pgm_time_update_now; + + +static +void +mock_setup (void) +{ + if (!g_thread_supported ()) g_thread_init (NULL); +} + +static +struct pgm_sock_t* +generate_sock (void) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, g_htons(TEST_SPORT) }; + struct pgm_sock_t* sock = g_new0 (struct pgm_sock_t, 1); + sock->window = g_new0 (pgm_txw_t, 1); + memcpy (&sock->tsi, &tsi, sizeof(pgm_tsi_t)); + sock->is_bound = TRUE; + sock->rx_buffer = pgm_alloc_skb (TEST_MAX_TPDU); + sock->max_tpdu = TEST_MAX_TPDU; + sock->rxw_sqns = TEST_RXW_SQNS; + sock->dport = g_htons(TEST_DPORT); + sock->can_send_data = TRUE; + sock->can_send_nak = TRUE; + sock->can_recv_data = TRUE; + sock->peers_hashtable = pgm_hashtable_new (pgm_tsi_hash, pgm_tsi_equal); + pgm_rand_create (&sock->rand_); + sock->nak_bo_ivl = 100*1000; + pgm_notify_init (&sock->pending_notify); + pgm_notify_init (&sock->rdata_notify); + return sock; +} + +static +struct pgm_sk_buff_t* +generate_packet (void) +{ + struct pgm_sk_buff_t* skb; + + skb = pgm_alloc_skb (TEST_MAX_TPDU); + skb->data = skb->head; + skb->len = sizeof(struct pgm_ip) + sizeof(struct pgm_header); + skb->tail = (guint8*)skb->data + skb->len; + +/* add IP header */ + struct pgm_ip* iphdr = skb->data; + iphdr->ip_hl = sizeof(struct pgm_ip) / 4; + iphdr->ip_v = 4; + iphdr->ip_tos = 0; + iphdr->ip_id = 0; + iphdr->ip_off = 0; + iphdr->ip_ttl = 16; + iphdr->ip_p = IPPROTO_PGM; + iphdr->ip_sum = 0; + iphdr->ip_src.s_addr = inet_addr (TEST_SRC_ADDR); + iphdr->ip_dst.s_addr = inet_addr (TEST_GROUP_ADDR); + +/* add PGM header */ + struct pgm_header* pgmhdr = (gpointer)(iphdr + 1); + pgmhdr->pgm_sport = g_htons ((guint16)TEST_SPORT); + pgmhdr->pgm_dport = g_htons ((guint16)TEST_DPORT); + pgmhdr->pgm_options = 0; + pgmhdr->pgm_gsi[0] = 1; + pgmhdr->pgm_gsi[1] = 2; + pgmhdr->pgm_gsi[2] = 3; + pgmhdr->pgm_gsi[3] = 4; + pgmhdr->pgm_gsi[4] = 5; + pgmhdr->pgm_gsi[5] = 6; + pgmhdr->pgm_tsdu_length = 0; + pgmhdr->pgm_checksum = 0; + + skb->pgm_header = pgmhdr; + return skb; +} + +static +void +generate_odata ( + const char* source, + const guint source_len, + const guint32 data_sqn, + const guint32 data_trail, + gpointer* packet, + gsize* len + ) +{ + struct pgm_sk_buff_t* skb = generate_packet (); + pgm_skb_put (skb, sizeof(struct pgm_data) + source_len); + +/* add ODATA header */ + struct pgm_data* datahdr = (gpointer)(skb->pgm_header + 1); + datahdr->data_sqn = g_htonl (data_sqn); + datahdr->data_trail = g_htonl (data_trail); + +/* add payload */ + gpointer data = (gpointer)(datahdr + 1); + memcpy (data, source, source_len); + +/* finalize PGM header */ + struct pgm_header* pgmhdr = skb->pgm_header; + pgmhdr->pgm_type = PGM_ODATA; + pgmhdr->pgm_tsdu_length = g_htons (source_len); + +/* finalize IP header */ + struct pgm_ip* iphdr = skb->data; + iphdr->ip_len = g_htons (skb->len); + + *packet = skb->head; + *len = skb->len; +} + +static +void +generate_spm ( + const guint32 spm_sqn, + const guint32 spm_trail, + const guint32 spm_lead, + gpointer* packet, + gsize* len + ) +{ + struct pgm_sk_buff_t* skb = generate_packet (); + pgm_skb_put (skb, sizeof(struct pgm_spm)); + +/* add SPM header */ + struct pgm_spm* spm = (gpointer)(skb->pgm_header + 1); + spm->spm_sqn = g_htonl (spm_sqn); + spm->spm_trail = g_htonl (spm_trail); + spm->spm_lead = g_htonl (spm_lead); + +/* finalize PGM header */ + struct pgm_header* pgmhdr = skb->pgm_header; + pgmhdr->pgm_type = PGM_SPM; + +/* finalize IP header */ + struct pgm_ip* iphdr = skb->data; + iphdr->ip_len = g_htons (skb->len); + + *packet = skb->head; + *len = skb->len; +} + +static +void +generate_nak ( + const guint32 nak_sqn, + gpointer* packet, + gsize* len + ) +{ + struct pgm_sk_buff_t* skb = generate_packet (); + pgm_skb_put (skb, sizeof(struct pgm_spm)); + +/* update IP header */ + struct pgm_ip* iphdr = skb->data; + iphdr->ip_src.s_addr = inet_addr (TEST_END_ADDR); + iphdr->ip_dst.s_addr = inet_addr (TEST_SRC_ADDR); + +/* update PGM header */ + struct pgm_header* pgmhdr = skb->pgm_header; + pgmhdr->pgm_sport = g_htons ((guint16)TEST_DPORT); + pgmhdr->pgm_dport = g_htons ((guint16)TEST_SPORT); + +/* add NAK header */ + struct pgm_nak* nak = (gpointer)(skb->pgm_header + 1); + nak->nak_sqn = g_htonl (nak_sqn); + +/* finalize PGM header */ + pgmhdr->pgm_type = PGM_NAK; + +/* finalize IP header */ + iphdr->ip_len = g_htons (skb->len); + + *packet = skb->head; + *len = skb->len; +} + +static +void +generate_peer_nak ( + const guint32 nak_sqn, + gpointer* packet, + gsize* len + ) +{ + struct pgm_sk_buff_t* skb = generate_packet (); + pgm_skb_put (skb, sizeof(struct pgm_spm)); + +/* update IP header */ + struct pgm_ip* iphdr = skb->data; + iphdr->ip_src.s_addr = inet_addr (TEST_PEER_ADDR); + iphdr->ip_dst.s_addr = inet_addr (TEST_GROUP_ADDR); + +/* update PGM header */ + struct pgm_header* pgmhdr = skb->pgm_header; + pgmhdr->pgm_sport = g_htons ((guint16)TEST_DPORT); + pgmhdr->pgm_dport = g_htons ((guint16)TEST_SPORT); + +/* add NAK header */ + struct pgm_nak* nak = (gpointer)(skb->pgm_header + 1); + nak->nak_sqn = g_htonl (nak_sqn); + +/* finalize PGM header */ + pgmhdr->pgm_type = PGM_NAK; + +/* finalize IP header */ + iphdr->ip_len = g_htons (skb->len); + + *packet = skb->head; + *len = skb->len; +} + +static +void +generate_nnak ( + const guint32 nak_sqn, + gpointer* packet, + gsize* len + ) +{ + struct pgm_sk_buff_t* skb = generate_packet (); + pgm_skb_put (skb, sizeof(struct pgm_nak)); + +/* update IP header */ + struct pgm_ip* iphdr = skb->data; + iphdr->ip_src.s_addr = inet_addr (TEST_DLR_ADDR); + iphdr->ip_dst.s_addr = inet_addr (TEST_SRC_ADDR); + +/* update PGM header */ + struct pgm_header* pgmhdr = skb->pgm_header; + pgmhdr->pgm_sport = g_htons ((guint16)TEST_DPORT); + pgmhdr->pgm_dport = g_htons ((guint16)TEST_SPORT); + +/* add NNAK header */ + struct pgm_nak* nak = (gpointer)(skb->pgm_header + 1); + nak->nak_sqn = g_htonl (nak_sqn); + +/* finalize PGM header */ + pgmhdr->pgm_type = PGM_NNAK; + +/* finalize IP header */ + iphdr->ip_len = g_htons (skb->len); + + *packet = skb->head; + *len = skb->len; +} + +static +void +generate_ncf ( + const guint32 nak_sqn, + gpointer* packet, + gsize* len + ) +{ + struct pgm_sk_buff_t* skb = generate_packet (); + pgm_skb_put (skb, sizeof(struct pgm_nak)); + +/* add NAK header */ + struct pgm_nak* nak = (gpointer)(skb->pgm_header + 1); + nak->nak_sqn = g_htonl (nak_sqn); + +/* finalize PGM header */ + struct pgm_header* pgmhdr = skb->pgm_header; + pgmhdr->pgm_type = PGM_NCF; + +/* finalize IP header */ + struct pgm_ip* iphdr = skb->data; + iphdr->ip_len = g_htons (skb->len); + + *packet = skb->head; + *len = skb->len; +} + +static +void +generate_spmr ( + gpointer* packet, + gsize* len + ) +{ + struct pgm_sk_buff_t* skb = generate_packet (); + +/* update IP header */ + struct pgm_ip* iphdr = skb->data; + iphdr->ip_src.s_addr = inet_addr (TEST_END_ADDR); + iphdr->ip_dst.s_addr = inet_addr (TEST_SRC_ADDR); + +/* update PGM header */ + struct pgm_header* pgmhdr = skb->pgm_header; + pgmhdr->pgm_sport = g_htons ((guint16)TEST_DPORT); + pgmhdr->pgm_dport = g_htons ((guint16)TEST_SPORT); + +/* finalize PGM header */ + pgmhdr->pgm_type = PGM_SPMR; + +/* finalize IP header */ + iphdr->ip_len = g_htons (skb->len); + + *packet = skb->head; + *len = skb->len; +} + +static +void +generate_peer_spmr ( + gpointer* packet, + gsize* len + ) +{ + struct pgm_sk_buff_t* skb = generate_packet (); + +/* update IP header */ + struct pgm_ip* iphdr = skb->data; + iphdr->ip_src.s_addr = inet_addr (TEST_PEER_ADDR); + iphdr->ip_dst.s_addr = inet_addr (TEST_GROUP_ADDR); + +/* update PGM header */ + struct pgm_header* pgmhdr = skb->pgm_header; + pgmhdr->pgm_sport = g_htons ((guint16)TEST_DPORT); + pgmhdr->pgm_dport = g_htons ((guint16)TEST_SPORT); + +/* finalize PGM header */ + pgmhdr->pgm_type = PGM_SPMR; + +/* finalize IP header */ + iphdr->ip_len = g_htons (skb->len); + + *packet = skb->head; + *len = skb->len; +} + +static +void +generate_msghdr ( + const gpointer packet, + const gsize packet_len + ) +{ + struct pgm_ip* iphdr = packet; + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = iphdr->ip_src.s_addr + }; + struct iovec iov = { + .iov_base = packet, + .iov_len = packet_len + }; + struct cmsghdr* packet_cmsg = g_malloc0 (sizeof(struct cmsghdr) + sizeof(struct in_pktinfo)); + packet_cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(struct in_pktinfo); + packet_cmsg->cmsg_level = IPPROTO_IP; + packet_cmsg->cmsg_type = IP_PKTINFO; + struct in_pktinfo packet_info = { + .ipi_ifindex = 2, + .ipi_spec_dst = iphdr->ip_src.s_addr, /* local address */ + .ipi_addr = iphdr->ip_dst.s_addr /* destination address */ + }; + memcpy ((char*)(packet_cmsg + 1), &packet_info, sizeof(struct in_pktinfo)); + struct msghdr packet_msg = { + .msg_name = g_memdup (&addr, sizeof(addr)), /* source address */ + .msg_namelen = sizeof(addr), + .msg_iov = g_memdup (&iov, sizeof(iov)), + .msg_iovlen = 1, + .msg_control = &packet_cmsg, + .msg_controllen = sizeof(struct cmsghdr) + sizeof(struct in_pktinfo), + .msg_flags = 0 + }; + + struct mock_recvmsg_t* mr = g_malloc (sizeof(struct mock_recvmsg_t)); + mr->mr_msg = g_memdup (&packet_msg, sizeof(packet_msg)); + mr->mr_errno = 0; + mr->mr_retval = packet_len; + mock_recvmsg_list = g_list_append (mock_recvmsg_list, mr); +} + +static +void +push_block_event (void) +{ +/* block */ + struct mock_recvmsg_t* mr = g_malloc (sizeof(struct mock_recvmsg_t)); + mr->mr_msg = NULL; + mr->mr_errno = EAGAIN; + mr->mr_retval = -1; + mock_recvmsg_list = g_list_append (mock_recvmsg_list, mr); +} + +/** packet module */ +bool +mock_pgm_parse_raw ( + struct pgm_sk_buff_t* const skb, + struct sockaddr* const dst, + pgm_error_t** error + ) +{ + const struct pgm_ip* ip = (struct pgm_ip*)skb->data; + struct sockaddr_in* sin = (struct sockaddr_in*)dst; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = ip->ip_dst.s_addr; + const gsize ip_header_length = ip->ip_hl * 4; + skb->pgm_header = (gpointer)( (guint8*)skb->data + ip_header_length ); + skb->data = skb->pgm_header; + skb->len -= ip_header_length; + memcpy (&skb->tsi.gsi, skb->pgm_header->pgm_gsi, sizeof(pgm_gsi_t)); + skb->tsi.sport = skb->pgm_header->pgm_sport; + return TRUE; +} + +bool +mock_pgm_parse_udp_encap ( + struct pgm_sk_buff_t* const skb, + pgm_error_t** error + ) +{ + skb->pgm_header = skb->data; + memcpy (&skb->tsi.gsi, skb->pgm_header->pgm_gsi, sizeof(pgm_gsi_t)); + skb->tsi.sport = skb->pgm_header->pgm_sport; + return TRUE; +} + +bool +mock_pgm_verify_spm ( + const struct pgm_sk_buff_t* const skb + ) +{ + return TRUE; +} + +bool +mock_pgm_verify_nak ( + const struct pgm_sk_buff_t* const skb + ) +{ + return TRUE; +} + +bool +mock_pgm_verify_ncf ( + const struct pgm_sk_buff_t* const skb + ) +{ + return TRUE; +} + +/** socket module */ +int +mock_pgm_poll_info ( + pgm_sock_t* const sock, + struct pollfd* fds, + int* n_fds, + int events + ) +{ +} + +pgm_peer_t* +mock__pgm_peer_ref ( + pgm_peer_t* peer + ) +{ + pgm_atomic_inc32 (&peer->ref_count); + return peer; +} + +PGM_GNUC_INTERNAL +pgm_peer_t* +mock_pgm_new_peer ( + pgm_sock_t* const sock, + const pgm_tsi_t* const tsi, + const struct sockaddr* const src_addr, + const socklen_t src_addr_len, + const struct sockaddr* const dst_addr, + const socklen_t dst_addr_len, + const pgm_time_t now + ) +{ + pgm_peer_t* peer = g_malloc0 (sizeof(pgm_peer_t)); + peer->expiry = now + sock->peer_expiry; + peer->sock = sock; + memcpy (&peer->tsi, tsi, sizeof(pgm_tsi_t)); + memcpy (&peer->group_nla, dst_addr, dst_addr_len); + memcpy (&peer->local_nla, src_addr, src_addr_len); + ((struct sockaddr_in*)&peer->local_nla)->sin_port = g_htons (sock->udp_encap_ucast_port); + ((struct sockaddr_in*)&peer->nla)->sin_port = g_htons (sock->udp_encap_ucast_port); + peer->window = mock_pgm_rxw_create (&peer->tsi, + sock->max_tpdu, + sock->rxw_sqns, + sock->rxw_secs, + sock->rxw_max_rte, + sock->ack_c_p); + peer->spmr_expiry = now + sock->spmr_expiry; + gpointer entry = mock__pgm_peer_ref(peer); + pgm_hashtable_insert (sock->peers_hashtable, &peer->tsi, entry); + peer->peers_link.next = sock->peers_list; + peer->peers_link.data = peer; + if (sock->peers_list) + sock->peers_list->prev = &peer->peers_link; + sock->peers_list = &peer->peers_link; + return peer; +} + +PGM_GNUC_INTERNAL +void +mock_pgm_set_reset_error ( + pgm_sock_t* const sock, + pgm_peer_t* const source, + struct pgm_msgv_t* const msgv + ) +{ +} + +PGM_GNUC_INTERNAL +int +mock_pgm_flush_peers_pending ( + pgm_sock_t* const sock, + struct pgm_msgv_t** pmsg, + const struct pgm_msgv_t* const msg_end, + size_t* const bytes_read, + unsigned* const data_read + ) +{ + if (mock_data_list) { + size_t len = 0; + unsigned count = 0; + while (mock_data_list && *pmsg <= msg_end) { + struct pgm_msgv_t* mock_msgv = mock_data_list->data; + (*pmsg)->msgv_len = mock_msgv->msgv_len; + for (unsigned i = 0; i < mock_msgv->msgv_len; i++) { + (*pmsg)->msgv_skb[i] = mock_msgv->msgv_skb[i]; + len += mock_msgv->msgv_skb[i]->len; + } + count++; + (*pmsg)++; + mock_data_list = g_list_delete_link (mock_data_list, mock_data_list); + } + *bytes_read = len; + *data_read = count; + if (*pmsg > msg_end) + return -ENOBUFS; + } + return 0; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_peer_has_pending ( + pgm_peer_t* const peer + ) +{ + return FALSE; +} + +PGM_GNUC_INTERNAL +void +mock_pgm_peer_set_pending ( + pgm_sock_t* const sock, + pgm_peer_t* const peer + ) +{ + g_assert (NULL != sock); + g_assert (NULL != peer); + if (peer->pending_link.data) return; + peer->pending_link.data = peer; + peer->pending_link.next = sock->peers_pending; + sock->peers_pending = &peer->pending_link; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_on_data ( + pgm_sock_t* const sock, + pgm_peer_t* const sender, + struct pgm_sk_buff_t* const skb + ) +{ + g_debug ("mock_pgm_on_data (sock:%p sender:%p skb:%p)", + (gpointer)sock, (gpointer)sender, (gpointer)skb); + mock_pgm_type = PGM_ODATA; + ((pgm_rxw_t*)sender->window)->has_event = 1; + return TRUE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_on_ack ( + pgm_sock_t* const sock, + struct pgm_sk_buff_t* const skb + ) +{ + g_debug ("mock_pgm_on_ack (sock:%p skb:%p)", + (gpointer)sock, (gpointer)skb); + mock_pgm_type = PGM_ACK; + return TRUE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_on_deferred_nak ( + pgm_sock_t* const sock + ) +{ + return TRUE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_on_nak ( + pgm_sock_t* const sock, + struct pgm_sk_buff_t* const skb + ) +{ + g_debug ("mock_pgm_on_nak (sock:%p skb:%p)", + (gpointer)sock, (gpointer)skb); + mock_pgm_type = PGM_NAK; + return TRUE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_on_peer_nak ( + pgm_sock_t* const sock, + pgm_peer_t* const sender, + struct pgm_sk_buff_t* const skb + ) +{ + g_debug ("mock_pgm_on_peer_nak (sock:%p sender:%p skb:%p)", + (gpointer)sock, (gpointer)sender, (gpointer)skb); + mock_pgm_type = PGM_NAK; + return TRUE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_on_ncf ( + pgm_sock_t* const sock, + pgm_peer_t* const sender, + struct pgm_sk_buff_t* const skb + ) +{ + g_debug ("mock_pgm_on_ncf (sock:%p sender:%p skb:%p)", + (gpointer)sock, (gpointer)sender, (gpointer)skb); + mock_pgm_type = PGM_NCF; + return TRUE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_on_nnak ( + pgm_sock_t* const sock, + struct pgm_sk_buff_t* const skb + ) +{ + g_debug ("mock_pgm_on_nnak (sock:%p skb:%p)", + (gpointer)sock, (gpointer)skb); + mock_pgm_type = PGM_NNAK; + return TRUE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_on_spm ( + pgm_sock_t* const sock, + pgm_peer_t* const sender, + struct pgm_sk_buff_t* const skb + ) +{ + g_debug ("mock_pgm_on_spm (sock:%p sender:%p skb:%p)", + (gpointer)sock, (gpointer)sender, (gpointer)skb); + mock_pgm_type = PGM_SPM; + return TRUE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_on_spmr ( + pgm_sock_t* const sock, + pgm_peer_t* const peer, + struct pgm_sk_buff_t* const skb + ) +{ + g_debug ("mock_pgm_on_spmr (sock:%p peer:%p skb:%p)", + (gpointer)sock, (gpointer)peer, (gpointer)skb); + mock_pgm_type = PGM_SPMR; + if (mock_reset_on_spmr) { + sock->is_reset = TRUE; + mock_pgm_peer_set_pending (sock, mock_peer); + } + if (mock_data_on_spmr) { + mock_pgm_peer_set_pending (sock, mock_peer); + } + return TRUE; +} + +/** transmit window */ +PGM_GNUC_INTERNAL +bool +mock_pgm_txw_retransmit_is_empty ( + const pgm_txw_t*const window + ) +{ + return TRUE; +} + +/** receive window */ +pgm_rxw_t* +mock_pgm_rxw_create ( + const pgm_tsi_t* tsi, + const uint16_t tpdu_size, + const unsigned sqns, + const unsigned secs, + const ssize_t max_rte, + const uint32_t ack_c_p + ) +{ + return g_new0 (pgm_rxw_t, 1); +} + +ssize_t +mock_pgm_rxw_readv ( + pgm_rxw_t* const window, + struct pgm_msgv_t** pmsg, + const unsigned pmsglen + ) +{ + return -1; +} + +/** net module */ +PGM_GNUC_INTERNAL +ssize_t +mock_pgm_sendto ( + pgm_sock_t* sock, + bool use_rate_limit, + bool use_router_alert, + const void* buf, + size_t len, + const struct sockaddr* to, + socklen_t tolen + ) +{ + return len; +} + +/** timer module */ +PGM_GNUC_INTERNAL +bool +mock_pgm_timer_prepare ( + pgm_sock_t* const sock + ) +{ + return FALSE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_timer_check ( + pgm_sock_t* const sock + ) +{ + return FALSE; +} + +PGM_GNUC_INTERNAL +pgm_time_t +mock_pgm_timer_expiration ( + pgm_sock_t* const sock + ) +{ + return 100L; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_timer_dispatch ( + pgm_sock_t* const sock + ) +{ + return TRUE; +} + +/** time module */ +static pgm_time_t mock_pgm_time_now = 0x1; + +static +pgm_time_t +_mock_pgm_time_update_now (void) +{ + return mock_pgm_time_now; +} + +/** libc */ +static +ssize_t +mock_recvmsg ( + int s, + struct msghdr* msg, + int flags + ) +{ + g_assert (NULL != msg); + g_assert (NULL != mock_recvmsg_list); + + g_debug ("mock_recvmsg (s:%d msg:%p flags:%d)", + s, (gpointer)msg, flags); + + struct mock_recvmsg_t* mr = mock_recvmsg_list->data; + struct msghdr* mock_msg = mr->mr_msg; + ssize_t mock_retval = mr->mr_retval; + int mock_errno = mr->mr_errno; + mock_recvmsg_list = g_list_delete_link (mock_recvmsg_list, mock_recvmsg_list); + if (mock_msg) { + g_assert_cmpuint (mock_msg->msg_namelen, <=, msg->msg_namelen); + g_assert_cmpuint (mock_msg->msg_iovlen, <=, msg->msg_iovlen); + g_assert_cmpuint (mock_msg->msg_controllen, <=, msg->msg_controllen); + if (mock_msg->msg_namelen) + memcpy (msg->msg_name, mock_msg->msg_name, mock_msg->msg_namelen); + if (mock_msg->msg_iovlen) { + for (unsigned i = 0; i < mock_msg->msg_iovlen; i++) { + g_assert (mock_msg->msg_iov[i].iov_len <= msg->msg_iov[i].iov_len); + memcpy (msg->msg_iov[i].iov_base, mock_msg->msg_iov[i].iov_base, mock_msg->msg_iov[i].iov_len); + } + } + if (mock_msg->msg_controllen) + memcpy (msg->msg_control, mock_msg->msg_control, mock_msg->msg_controllen); + msg->msg_flags = mock_msg->msg_flags; + } + errno = mock_errno; + return mock_retval; +} + + +/* mock functions for external references */ + +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return 0; +} + + +/* target: + * int + * pgm_recv ( + * pgm_sock_t* sock, + * void* data, + * size_t len, + * int flags, + * size_t* bytes_read, + * pgm_error_t** error + * ) + * + * Most tests default to PGM_IO_STATUS_TIMER_PENDING, PGM_IO_STATUS_WOULD_BLOCK is not expected due + * to peer state engine and SPM broadcasts. + */ + +START_TEST (test_block_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + push_block_event (); + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); +} +END_TEST + +/* recv -> on_data */ +START_TEST (test_data_pass_001) +{ + const char source[] = "i am not a string"; + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gpointer packet; gsize packet_len; + generate_odata (source, sizeof(source), 0 /* sqn */, -1 /* trail */, &packet, &packet_len); + generate_msghdr (packet, packet_len); + push_block_event (); + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (PGM_ODATA == mock_pgm_type, "unexpected PGM packet"); +} +END_TEST + +/* recv -> on_spm */ +START_TEST (test_spm_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gpointer packet; gsize packet_len; + generate_spm (200 /* spm-sqn */, -1 /* trail */, 0 /* lead */, &packet, &packet_len); + generate_msghdr (packet, packet_len); + push_block_event (); + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (PGM_SPM == mock_pgm_type, "unexpected PGM packet"); +} +END_TEST + +/* recv -> on_nak */ +START_TEST (test_nak_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gpointer packet; gsize packet_len; + generate_nak (0 /* sqn */, &packet, &packet_len); + generate_msghdr (packet, packet_len); + push_block_event (); + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (PGM_NAK == mock_pgm_type, "unexpected PGM packet"); +} +END_TEST + +/* recv -> on_peer_nak */ +START_TEST (test_peer_nak_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gpointer packet; gsize packet_len; + generate_peer_nak (0 /* sqn */, &packet, &packet_len); + generate_msghdr (packet, packet_len); + push_block_event (); + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (PGM_NAK == mock_pgm_type, "unexpected PGM packet"); +} +END_TEST + +/* recv -> on_nnak */ +START_TEST (test_nnak_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gpointer packet; gsize packet_len; + generate_nnak (0 /* sqn */, &packet, &packet_len); + generate_msghdr (packet, packet_len); + push_block_event (); + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (PGM_NNAK == mock_pgm_type, "unexpected PGM packet"); +} +END_TEST + +/* recv -> on_ncf */ +START_TEST (test_ncf_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gpointer packet; gsize packet_len; + generate_ncf (0 /* sqn */, &packet, &packet_len); + generate_msghdr (packet, packet_len); + push_block_event (); + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (PGM_NCF == mock_pgm_type, "unexpected PGM packet"); +} +END_TEST + +/* recv -> on_spmr */ +START_TEST (test_spmr_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gpointer packet; gsize packet_len; + generate_spmr (&packet, &packet_len); + generate_msghdr (packet, packet_len); + push_block_event (); + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (PGM_SPMR == mock_pgm_type, "unexpected PGM packet"); +} +END_TEST + +/* recv -> on (peer) spmr */ +START_TEST (test_peer_spmr_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gpointer packet; gsize packet_len; + generate_peer_spmr (&packet, &packet_len); + generate_msghdr (packet, packet_len); + push_block_event (); + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (PGM_SPMR == mock_pgm_type, "unexpected PGM packet"); +} +END_TEST + +/* recv -> lost data */ +START_TEST (test_lost_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + sock->is_reset = TRUE; + const pgm_tsi_t peer_tsi = { { 9, 8, 7, 6, 5, 4 }, g_htons(9000) }; + struct sockaddr_in grp_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_GROUP_ADDR) + }, peer_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_END_ADDR) + }; + pgm_peer_t* peer = mock_pgm_new_peer (sock, &peer_tsi, (struct sockaddr*)&grp_addr, sizeof(grp_addr), (struct sockaddr*)&peer_addr, sizeof(peer_addr), mock_pgm_time_now); + fail_if (NULL == peer, "new_peer failed"); + mock_pgm_peer_set_pending (sock, peer); + push_block_event (); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_RESET == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + if (err) { + g_message ("%s", err->message); + pgm_error_free (err); + err = NULL; + } + push_block_event (); + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); +} +END_TEST + +/* recv -> lost data and abort transport */ +START_TEST (test_abort_on_lost_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + sock->is_reset = TRUE; + sock->is_abort_on_reset = TRUE; + const pgm_tsi_t peer_tsi = { { 9, 8, 7, 6, 5, 4 }, g_htons(9000) }; + struct sockaddr_in grp_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_GROUP_ADDR) + }, peer_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_END_ADDR) + }; + pgm_peer_t* peer = mock_pgm_new_peer (sock, &peer_tsi, (struct sockaddr*)&grp_addr, sizeof(grp_addr), (struct sockaddr*)&peer_addr, sizeof(peer_addr), mock_pgm_time_now); + fail_if (NULL == peer, "new_peer failed"); + mock_pgm_peer_set_pending (sock, peer); + push_block_event (); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_RESET == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + if (err) { + g_message ("%s", err->message); + pgm_error_free (err); + err = NULL; + } + push_block_event (); + fail_unless (PGM_IO_STATUS_RESET == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); +} +END_TEST + +/* recv -> (spmr) & loss */ +START_TEST (test_then_lost_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + mock_reset_on_spmr = TRUE; + gpointer packet; gsize packet_len; + generate_spmr (&packet, &packet_len); + generate_msghdr (packet, packet_len); + const pgm_tsi_t peer_tsi = { { 9, 8, 7, 6, 5, 4 }, g_htons(9000) }; + struct sockaddr_in grp_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_GROUP_ADDR) + }, peer_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_END_ADDR) + }; + mock_peer = mock_pgm_new_peer (sock, &peer_tsi, (struct sockaddr*)&grp_addr, sizeof(grp_addr), (struct sockaddr*)&peer_addr, sizeof(peer_addr), mock_pgm_time_now); + fail_if (NULL == mock_peer, "new_peer failed"); + push_block_event (); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_RESET == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + if (err) { + g_message ("%s", err->message); + pgm_error_free (err); + err = NULL; + } + push_block_event (); + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); +} +END_TEST + +/* recv -> data & loss & abort transport */ +START_TEST (test_then_abort_on_lost_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + mock_reset_on_spmr = TRUE; + sock->is_abort_on_reset = TRUE; + gpointer packet; gsize packet_len; + generate_spmr (&packet, &packet_len); + generate_msghdr (packet, packet_len); + const pgm_tsi_t peer_tsi = { { 9, 8, 7, 6, 5, 4 }, g_htons(9000) }; + struct sockaddr_in grp_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_GROUP_ADDR) + }, peer_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_END_ADDR) + }; + mock_peer = mock_pgm_new_peer (sock, &peer_tsi, (struct sockaddr*)&grp_addr, sizeof(grp_addr), (struct sockaddr*)&peer_addr, sizeof(peer_addr), mock_pgm_time_now); + fail_if (NULL == mock_peer, "new_peer failed"); + push_block_event (); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_RESET == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + if (err) { + g_message ("%s", err->message); + pgm_error_free (err); + err = NULL; + } + push_block_event (); + fail_unless (PGM_IO_STATUS_RESET == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); +} +END_TEST + +/* new waiting peer -> return data */ +START_TEST (test_on_data_pass_001) +{ + const char source[] = "i am not a string"; + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + mock_data_on_spmr = TRUE; + gpointer packet; gsize packet_len; + generate_spmr (&packet, &packet_len); + generate_msghdr (packet, packet_len); + const pgm_tsi_t peer_tsi = { { 9, 8, 7, 6, 5, 4 }, g_htons(9000) }; + struct sockaddr_in grp_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_GROUP_ADDR) + }, peer_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_END_ADDR) + }; + mock_peer = mock_pgm_new_peer (sock, &peer_tsi, (struct sockaddr*)&grp_addr, sizeof(grp_addr), (struct sockaddr*)&peer_addr, sizeof(peer_addr), mock_pgm_time_now); + fail_if (NULL == mock_peer, "new_peer failed"); + struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); + pgm_skb_put (skb, sizeof(source)); + memcpy (skb->data, source, sizeof(source)); + struct pgm_msgv_t* msgv = g_new0 (struct pgm_msgv_t, 1); + msgv->msgv_len = 1; + msgv->msgv_skb[0] = skb; + mock_data_list = g_list_append (mock_data_list, msgv); + push_block_event (); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (NULL == err, "error raised"); + fail_unless ((gsize)sizeof(source) == bytes_read, "unexpected data length"); + push_block_event (); + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); +} +END_TEST + +/* zero length data waiting */ +START_TEST (test_on_zero_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + mock_data_on_spmr = TRUE; + gpointer packet; gsize packet_len; + generate_spmr (&packet, &packet_len); + generate_msghdr (packet, packet_len); + const pgm_tsi_t peer_tsi = { { 9, 8, 7, 6, 5, 4 }, g_htons(9000) }; + struct sockaddr_in grp_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_GROUP_ADDR) + }, peer_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_END_ADDR) + }; + mock_peer = mock_pgm_new_peer (sock, &peer_tsi, (struct sockaddr*)&grp_addr, sizeof(grp_addr), (struct sockaddr*)&peer_addr, sizeof(peer_addr), mock_pgm_time_now); + fail_if (NULL == mock_peer, "new_peer failed"); + struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); + struct pgm_msgv_t* msgv = g_new0 (struct pgm_msgv_t, 1); + msgv->msgv_len = 1; + msgv->msgv_skb[0] = skb; + mock_data_list = g_list_append (mock_data_list, msgv); + push_block_event (); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (NULL == err, "error raised"); + fail_unless ((gsize)0 == bytes_read, "unexpected data length"); + push_block_event (); + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); +} +END_TEST + +/* more than vector length data waiting */ +START_TEST (test_on_many_data_pass_001) +{ + const char* source[] = { + "i am not a string", + "i am not an iguana", + "i am not a peach" + }; + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + mock_data_on_spmr = TRUE; + gpointer packet; gsize packet_len; + generate_spmr (&packet, &packet_len); + generate_msghdr (packet, packet_len); + const pgm_tsi_t peer_tsi = { { 9, 8, 7, 6, 5, 4 }, g_htons(9000) }; + struct sockaddr_in grp_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_GROUP_ADDR) + }, peer_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_END_ADDR) + }; + mock_peer = mock_pgm_new_peer (sock, &peer_tsi, (struct sockaddr*)&grp_addr, sizeof(grp_addr), (struct sockaddr*)&peer_addr, sizeof(peer_addr), mock_pgm_time_now); + fail_if (NULL == mock_peer, "new_peer failed"); + struct pgm_sk_buff_t* skb; + struct pgm_msgv_t* msgv; +/* #1 */ + msgv = g_new0 (struct pgm_msgv_t, 1); + skb = pgm_alloc_skb (TEST_MAX_TPDU); + pgm_skb_put (skb, strlen(source[0]) + 1); + memcpy (skb->data, source[0], strlen(source[0])); + msgv->msgv_len = 1; + msgv->msgv_skb[0] = skb; + mock_data_list = g_list_append (mock_data_list, msgv); +/* #2 */ + msgv = g_new0 (struct pgm_msgv_t, 1); + skb = pgm_alloc_skb (TEST_MAX_TPDU); + pgm_skb_put (skb, strlen(source[1]) + 1); + memcpy (skb->data, source[1], strlen(source[1])); + msgv->msgv_len = 1; + msgv->msgv_skb[0] = skb; + mock_data_list = g_list_append (mock_data_list, msgv); +/* #3 */ + msgv = g_new0 (struct pgm_msgv_t, 1); + skb = pgm_alloc_skb (TEST_MAX_TPDU); + pgm_skb_put (skb, strlen(source[2]) + 1); + memcpy (skb->data, source[2], strlen(source[2])); + msgv->msgv_len = 1; + msgv->msgv_skb[0] = skb; + mock_data_list = g_list_append (mock_data_list, msgv); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (NULL == err, "error raised"); + fail_unless ((gsize)(strlen(source[0]) + 1) == bytes_read, "unexpected data length"); + g_message ("#1 = \"%s\"", buffer); + fail_unless (PGM_IO_STATUS_NORMAL == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (NULL == err, "error raised"); + fail_unless ((gsize)(strlen(source[1]) + 1) == bytes_read, "unexpected data length"); + g_message ("#2 = \"%s\"", buffer); + fail_unless (PGM_IO_STATUS_NORMAL == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (NULL == err, "error raised"); + fail_unless ((gsize)(strlen(source[2]) + 1) == bytes_read, "unexpected data length"); + g_message ("#3 = \"%s\"", buffer); + push_block_event (); + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); +} +END_TEST + +START_TEST (test_recv_fail_001) +{ + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + fail_unless (PGM_IO_STATUS_ERROR == pgm_recv (NULL, buffer, sizeof(buffer), 0, NULL, NULL), "recv faied"); +} +END_TEST + +/* target: + * int + * pgm_recvfrom ( + * pgm_sock_t* sock, + * void* data, + * size_t len, + * int flags, + * size_t* bytes_read, + * struct pgm_sockaddr_t* from, + * socklen_t* fromlen, + * pgm_error_t** error + * ) + */ + +START_TEST (test_recvfrom_fail_001) +{ + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + struct pgm_sockaddr_t from; + socklen_t fromlen = sizeof(from); + fail_unless (PGM_IO_STATUS_ERROR == pgm_recvfrom (NULL, buffer, sizeof(buffer), 0, NULL, &from, &fromlen, NULL), "recvfrom failed"); +} +END_TEST + +/* target: + * int + * pgm_recvmsg ( + * pgm_sock_t* sock, + * pgm_msgv_t* msgv, + * int flags, + * size_t* bytes_read, + * pgm_error_t** error + * ) + */ + +START_TEST (test_recvmsg_fail_001) +{ + struct pgm_msgv_t msgv; + fail_unless (PGM_IO_STATUS_ERROR == pgm_recvmsg (NULL, &msgv, 0, NULL, NULL), "recvmsg failed"); +} +END_TEST + +/* target: + * int + * pgm_recvmsgv ( + * pgm_sock_t* sock, + * pgm_msgv_t* msgv, + * unsigned msgv_length, + * int flags, + * size_t* bytes_read, + * pgm_error_t** error + * ) + */ + +START_TEST (test_recvmsgv_fail_001) +{ + struct pgm_msgv_t msgv[1]; + fail_unless (PGM_IO_STATUS_ERROR == pgm_recvmsgv (NULL, msgv, G_N_ELEMENTS(msgv), 0, NULL, NULL), "recvmsgv failed"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_block = tcase_create ("block"); + suite_add_tcase (s, tc_block); + tcase_add_checked_fixture (tc_block, mock_setup, NULL); + tcase_add_test (tc_block, test_block_pass_001); + + TCase* tc_data = tcase_create ("data"); + suite_add_tcase (s, tc_data); + tcase_add_checked_fixture (tc_data, mock_setup, NULL); + tcase_add_test (tc_data, test_data_pass_001); + + TCase* tc_spm = tcase_create ("spm"); + suite_add_tcase (s, tc_spm); + tcase_add_checked_fixture (tc_spm, mock_setup, NULL); + tcase_add_test (tc_spm, test_spm_pass_001); + + TCase* tc_nak = tcase_create ("nak"); + suite_add_tcase (s, tc_nak); + tcase_add_checked_fixture (tc_nak, mock_setup, NULL); + tcase_add_test (tc_nak, test_nak_pass_001); + + TCase* tc_peer_nak = tcase_create ("peer-nak"); + suite_add_tcase (s, tc_peer_nak); + tcase_add_checked_fixture (tc_peer_nak, mock_setup, NULL); + tcase_add_test (tc_peer_nak, test_peer_nak_pass_001); + + TCase* tc_nnak = tcase_create ("nnak"); + suite_add_tcase (s, tc_nnak); + tcase_add_checked_fixture (tc_nnak, mock_setup, NULL); + tcase_add_test (tc_nnak, test_nnak_pass_001); + + TCase* tc_ncf = tcase_create ("ncf"); + suite_add_tcase (s, tc_ncf); + tcase_add_checked_fixture (tc_ncf, mock_setup, NULL); + tcase_add_test (tc_ncf, test_ncf_pass_001); + + TCase* tc_spmr = tcase_create ("spmr"); + suite_add_tcase (s, tc_spmr); + tcase_add_checked_fixture (tc_spmr, mock_setup, NULL); + tcase_add_test (tc_spmr, test_spmr_pass_001); + + TCase* tc_peer_spmr = tcase_create ("peer-spmr"); + suite_add_tcase (s, tc_peer_spmr); + tcase_add_checked_fixture (tc_peer_spmr, mock_setup, NULL); + tcase_add_test (tc_peer_spmr, test_peer_spmr_pass_001); + + TCase* tc_lost = tcase_create ("lost"); + suite_add_tcase (s, tc_lost); + tcase_add_checked_fixture (tc_lost, mock_setup, NULL); + tcase_add_test (tc_lost, test_lost_pass_001); + + TCase* tc_abort_on_lost = tcase_create ("abort-on-lost"); + suite_add_tcase (s, tc_abort_on_lost); + tcase_add_checked_fixture (tc_abort_on_lost, mock_setup, NULL); + tcase_add_test (tc_abort_on_lost, test_abort_on_lost_pass_001); + + TCase* tc_then_lost = tcase_create ("then-lost"); + suite_add_tcase (s, tc_then_lost); + tcase_add_checked_fixture (tc_then_lost, mock_setup, NULL); + tcase_add_test (tc_then_lost, test_then_lost_pass_001); + + TCase* tc_then_abort_on_lost = tcase_create ("then-abort-on-lost"); + suite_add_tcase (s, tc_then_abort_on_lost); + tcase_add_checked_fixture (tc_then_abort_on_lost, mock_setup, NULL); + tcase_add_test (tc_then_abort_on_lost, test_then_abort_on_lost_pass_001); + + TCase* tc_on_data = tcase_create ("on-data"); + suite_add_tcase (s, tc_on_data); + tcase_add_checked_fixture (tc_on_data, mock_setup, NULL); + tcase_add_test (tc_on_data, test_on_data_pass_001); + + TCase* tc_on_zero = tcase_create ("on-zero"); + suite_add_tcase (s, tc_on_zero); + tcase_add_checked_fixture (tc_on_zero, mock_setup, NULL); + tcase_add_test (tc_on_zero, test_on_zero_pass_001); + + TCase* tc_on_many_data = tcase_create ("on-many-data"); + suite_add_tcase (s, tc_on_many_data); + tcase_add_checked_fixture (tc_on_many_data, mock_setup, NULL); + tcase_add_test (tc_on_many_data, test_on_many_data_pass_001); + + TCase* tc_recv = tcase_create ("recv"); + suite_add_tcase (s, tc_recv); + tcase_add_checked_fixture (tc_recv, mock_setup, NULL); + tcase_add_test (tc_recv, test_recv_fail_001); + + TCase* tc_recvfrom = tcase_create ("recvfrom"); + suite_add_tcase (s, tc_recvfrom); + tcase_add_checked_fixture (tc_recvfrom, mock_setup, NULL); + tcase_add_test (tc_recvfrom, test_recvfrom_fail_001); + + TCase* tc_recvmsg = tcase_create ("recvmsg"); + suite_add_tcase (s, tc_recvmsg); + tcase_add_checked_fixture (tc_recvmsg, mock_setup, NULL); + tcase_add_test (tc_recvmsg, test_recvmsg_fail_001); + + TCase* tc_recvmsgv = tcase_create ("recvmsgv"); + suite_add_tcase (s, tc_recvmsgv); + tcase_add_checked_fixture (tc_recvmsgv, mock_setup, NULL); + tcase_add_test (tc_recvmsgv, test_recvmsgv_fail_001); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/reed_solomon.c b/3rdparty/openpgm-svn-r1085/pgm/reed_solomon.c new file mode 100644 index 0000000..a9a292c --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/reed_solomon.c @@ -0,0 +1,576 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Reed-Solomon forward error correction based on Vandermonde matrices. + * + * Output is incompatible with BCH style Reed-Solomon encoding. + * + * draft-ietf-rmt-bb-fec-rs-05.txt + * + rfc5052 + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + + +/* Vector GF(2⁸) plus-equals multiplication. + * + * d[] += b • s[] + */ + +static +void +_pgm_gf_vec_addmul ( + pgm_gf8_t* restrict d, + const pgm_gf8_t b, + const pgm_gf8_t* restrict s, + uint16_t len /* length of vectors */ + ) +{ + uint_fast16_t i; + uint_fast16_t count8; + + if (PGM_UNLIKELY(b == 0)) + return; + +#ifdef CONFIG_GALOIS_MUL_LUT + const pgm_gf8_t* gfmul_b = &pgm_gftable[ (uint16_t)b << 8 ]; +#endif + + i = 0; + count8 = len >> 3; /* 8-way unrolls */ + if (count8) + { + while (count8--) { +#ifdef CONFIG_GALOIS_MUL_LUT + d[i ] ^= gfmul_b[ s[i ] ]; + d[i+1] ^= gfmul_b[ s[i+1] ]; + d[i+2] ^= gfmul_b[ s[i+2] ]; + d[i+3] ^= gfmul_b[ s[i+3] ]; + d[i+4] ^= gfmul_b[ s[i+4] ]; + d[i+5] ^= gfmul_b[ s[i+5] ]; + d[i+6] ^= gfmul_b[ s[i+6] ]; + d[i+7] ^= gfmul_b[ s[i+7] ]; +#else + d[i ] ^= gfmul( b, s[i ] ); + d[i+1] ^= gfmul( b, s[i+1] ); + d[i+2] ^= gfmul( b, s[i+2] ); + d[i+3] ^= gfmul( b, s[i+3] ); + d[i+4] ^= gfmul( b, s[i+4] ); + d[i+5] ^= gfmul( b, s[i+5] ); + d[i+6] ^= gfmul( b, s[i+6] ); + d[i+7] ^= gfmul( b, s[i+7] ); +#endif + i += 8; + } + +/* remaining */ + len %= 8; + } + + while (len--) { +#ifdef CONFIG_GALOIS_MUL_LUT + d[i] ^= gfmul_b[ s[i] ]; +#else + d[i] ^= gfmul( b, s[i] ); +#endif + i++; + } +} + +/* Basic matrix multiplication. + * + * C = AB + * n + * c_i,j = ∑ a_i,j × b_r,j = a_i,1 × b_1,j + a_i,2 × b_2,j + ⋯ + a_i,n × b_n,j + * r=1 + */ + +static +void +_pgm_matmul ( + const pgm_gf8_t* restrict a, /* m-by-n */ + const pgm_gf8_t* restrict b, /* n-by-p */ + pgm_gf8_t* restrict c, /* ∴ m-by-p */ + const uint16_t m, + const uint16_t n, + const uint16_t p + ) +{ + for (uint_fast16_t j = 0; j < m; j++) + { + for (uint_fast16_t i = 0; i < p; i++) + { + pgm_gf8_t sum = 0; + + for (uint_fast16_t k = 0; k < n; k++) + { + sum ^= pgm_gfmul ( a[ (j * n) + k ], b[ (k * p) + i ] ); + } + + c[ (j * p) + i ] = sum; + } + } +} + +/* Generic square matrix inversion + */ + +#ifdef CONFIG_XOR_SWAP +/* whilst cute the xor swap is quite slow */ +#define SWAP(a, b) (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b))) +#else +#define SWAP(a, b) do { const pgm_gf8_t _t = (b); (b) = (a); (a) = _t; } while (0) +#endif + +static +void +_pgm_matinv ( + pgm_gf8_t* M, /* is n-by-n */ + const uint8_t n + ) +{ + uint8_t pivot_rows[ n ]; + uint8_t pivot_cols[ n ]; + bool pivots[ n ]; + memset (pivots, 0, sizeof(pivots)); + + pgm_gf8_t identity[ n ]; + memset (identity, 0, sizeof(identity)); + + for (uint_fast8_t i = 0; i < n; i++) + { + uint_fast8_t row = 0, col = 0; + +/* check diagonal for new pivot */ + if (!pivots[ i ] && M[ (i * n) + i ]) + { + row = col = i; + } + else + { + for (uint_fast8_t j = 0; j < n; j++) + { + if (pivots[ j ]) continue; + + for (uint_fast8_t x = 0; x < n; x++) + { + if (!pivots[ x ] && M[ (j * n) + x ]) + { + row = j; + col = x; + goto found; + } + } + } + } + +found: + pivots[ col ] = TRUE; + +/* pivot */ + if (row != col) + { + for (uint_fast8_t x = 0; x < n; x++) + { + pgm_gf8_t *pivot_row = &M[ (row * n) + x ], + *pivot_col = &M[ (col * n) + x ]; + SWAP( *pivot_row, *pivot_col ); + } + } + +/* save location */ + pivot_rows[ i ] = row; + pivot_cols[ i ] = col; + +/* divide row by pivot element */ + if (M[ (col * n) + col ] != 1) + { + const pgm_gf8_t c = M[ (col * n) + col ]; + M[ (col * n) + col ] = 1; + + for (uint_fast8_t x = 0; x < n; x++) + { + M[ (col * n) + x ] = pgm_gfdiv( M[ (col * n) + x ], c ); + } + } + +/* reduce if not an identity row */ + identity[ col ] = 1; + if (memcmp (&M[ (col * n) ], identity, n * sizeof(pgm_gf8_t))) + { + for ( uint_fast8_t x = 0; + x < n; + x++ ) + { + if (x == col) continue; + + const pgm_gf8_t c = M[ (x * n) + col ]; + M[ (x * n) + col ] = 0; + + _pgm_gf_vec_addmul (&M[ x * n ], c, &M[ col * n ], n); + } + } + identity[ col ] = 0; + } + +/* revert all pivots */ + for (int_fast16_t i = n - 1; i >= 0; i--) + { + if (pivot_rows[ i ] != pivot_cols[ i ]) + { + for (uint_fast8_t j = 0; j < n; j++) + { + pgm_gf8_t *pivot_row = &M[ (j * n) + pivot_rows[ i ] ], + *pivot_col = &M[ (j * n) + pivot_cols[ i ] ]; + SWAP( *pivot_row, *pivot_col ); + } + } + } +} + +/* Gauss–Jordan elimination optimised for Vandermonde matrices + * + * matrix = matrix⁻¹ + * + * A Vandermonde matrix exhibits geometric progression in each row: + * + * ⎡ 1 α₁ α₁² ⋯ α₁^^(n-1) ⎤ + * V = ⎢ 1 α₂ α₂² ⋯ α₂^^(n-1) ⎥ + * ⎣ 1 α₃ α₃² ⋯ α₃^^(n-1) ⎦ + * + * First column is actually α_m⁰, second column is α_m¹. + * + * As only the second column is actually unique so optimise from that. + */ + +static +void +_pgm_matinv_vandermonde ( + pgm_gf8_t* V, /* is n-by-n */ + const uint8_t n + ) +{ +/* trivial cases */ + if (n == 1) return; + +/* P_j(α) is polynomial of degree n - 1 defined by + * + * n + * P_j(α) = ∏ (α - α_m) + * m=1 + * + * 1: Work out coefficients. + */ + + pgm_gf8_t P[ n ]; + memset (P, 0, sizeof(P)); + +/* copy across second row, i.e. j = 2 */ + for (uint_fast8_t i = 0; i < n; i++) + { + P[ i ] = V[ (i * n) + 1 ]; + } + + pgm_gf8_t alpha[ n ]; + memset (alpha, 0, sizeof(alpha)); + + alpha[ n - 1 ] = P[ 0 ]; + for (uint_fast8_t i = 1; i < n; i++) + { + for (uint_fast8_t j = (n - i); j < (n - 1); j++) + { + alpha[ j ] ^= pgm_gfmul( P[ i ], alpha[ j + 1 ] ); + } + alpha[ n - 1 ] ^= P[ i ]; + } + +/* 2: Obtain numberators and denominators by synthetic division. + */ + + pgm_gf8_t b[ n ]; + b[ n - 1 ] = 1; + for (uint_fast8_t j = 0; j < n; j++) + { + const pgm_gf8_t xx = P[ j ]; + pgm_gf8_t t = 1; + +/* skip first iteration */ + for (int_fast16_t i = n - 2; i >= 0; i--) + { + b[ i ] = alpha[ i + 1 ] ^ pgm_gfmul( xx, b[ i + 1 ] ); + t = pgm_gfmul( xx, t ) ^ b[ i ]; + } + + for (uint_fast8_t i = 0; i < n; i++) + { + V[ (i * n) + j ] = pgm_gfdiv ( b[ i ], t ); + } + } +} + +/* create the generator matrix of a reed-solomon code. + * + * s GM e + * ⎧ ⎡ s₀ ⎤ ⎡ 1 0 0 ⎤ ⎡ e₀ ⎤ ⎫ + * ⎪ ⎢ ⋮ ⎥ ⎢ 0 1 ⎥ = ⎢ ⋮ ⎥ ⎬ n + * k ⎨ ⎢ ⋮ ⎥ × ⎢ ⋱ ⎥ ⎣e_{n-1}⎦ ⎭ + * ⎪ ⎢ ⋮ ⎥ ⎢ ⋱ ⎥ + * ⎩ ⎣s_{k-1}⎦ ⎣ 0 0 1 ⎦ + * + * e = s × GM + */ + +void +pgm_rs_create ( + pgm_rs_t* rs, + const uint8_t n, + const uint8_t k + ) +{ + pgm_assert (NULL != rs); + pgm_assert (n > 0); + pgm_assert (k > 0); + + rs->n = n; + rs->k = k; + rs->GM = pgm_new0 (pgm_gf8_t, n * k); + rs->RM = pgm_new0 (pgm_gf8_t, k * k); + +/* alpha = root of primitive polynomial of degree m + * ( 1 + x² + x³ + x⁴ + x⁸ ) + * + * V = Vandermonde matrix of k rows and n columns. + * + * Be careful, Harry! + */ +#ifdef CONFIG_PREFER_MALLOC + pgm_gf8_t* V = pgm_new0 (pgm_gf8_t, n * k); +#else + pgm_gf8_t* V = pgm_newa (pgm_gf8_t, n * k); + memset (V, 0, n * k); +#endif + pgm_gf8_t* p = V + k; + V[0] = 1; + for (uint_fast8_t j = 0; j < (n - 1); j++) + { + for (uint_fast8_t i = 0; i < k; i++) + { +/* the {i, j} entry of V_{k,n} is v_{i,j} = α^^(i×j), + * where 0 <= i <= k - 1 and 0 <= j <= n - 1. + */ + *p++ = pgm_gfantilog[ ( i * j ) % PGM_GF_MAX ]; + } + } + +/* This generator matrix would create a Maximum Distance Separable (MDS) + * matrix, a systematic result is required, i.e. original data is left + * unchanged. + * + * GM = V_{k,k}⁻¹ × V_{k,n} + * + * 1: matrix V_{k,k} formed by the first k columns of V_{k,n} + */ + pgm_gf8_t* V_kk = V; + pgm_gf8_t* V_kn = V + (k * k); + +/* 2: invert it + */ + _pgm_matinv_vandermonde (V_kk, k); + +/* 3: multiply by V_{k,n} + */ + _pgm_matmul (V_kn, V_kk, rs->GM + (k * k), n - k, k, k); + +#ifdef CONFIG_PREFER_MALLOC + pgm_free (V); +#endif + +/* 4: set identity matrix for original data + */ + for (uint_fast8_t i = 0; i < k; i++) + { + rs->GM[ (i * k) + i ] = 1; + } +} + +void +pgm_rs_destroy ( + pgm_rs_t* rs + ) +{ + pgm_assert (NULL != rs); + + if (rs->RM) { + pgm_free (rs->RM); + rs->RM = NULL; + } + + if (rs->GM) { + pgm_free (rs->GM); + rs->GM = NULL; + } +} + +/* create a parity packet from a vector of original data packets and + * FEC block packet offset. + */ + +void +pgm_rs_encode ( + pgm_rs_t* restrict rs, + const pgm_gf8_t** restrict src, /* length rs_t::k */ + const uint8_t offset, + pgm_gf8_t* restrict dst, + const uint16_t len + ) +{ + pgm_assert (NULL != rs); + pgm_assert (NULL != src); + pgm_assert (offset >= rs->k && offset < rs->n); /* parity packet */ + pgm_assert (NULL != dst); + pgm_assert (len > 0); + + memset (dst, 0, len); + for (uint_fast8_t i = 0; i < rs->k; i++) + { + const pgm_gf8_t c = rs->GM[ (offset * rs->k) + i ]; + _pgm_gf_vec_addmul (dst, c, src[i], len); + } +} + +/* original data block of packets with missing packet entries replaced + * with on-demand parity packets. + */ + +void +pgm_rs_decode_parity_inline ( + pgm_rs_t* restrict rs, + pgm_gf8_t** restrict block, /* length rs_t::k */ + const uint8_t* restrict offsets, /* offsets within FEC block, 0 < offset < n */ + const uint16_t len /* packet length */ + ) +{ + pgm_assert (NULL != rs); + pgm_assert (NULL != block); + pgm_assert (NULL != offsets); + pgm_assert (len > 0); + +/* create new recovery matrix from generator + */ + for (uint_fast8_t i = 0; i < rs->k; i++) + { + if (offsets[i] < rs->k) { + memset (&rs->RM[ i * rs->k ], 0, rs->k * sizeof(pgm_gf8_t)); + rs->RM[ (i * rs->k) + i ] = 1; + continue; + } + memcpy (&rs->RM[ i * rs->k ], &rs->GM[ offsets[ i ] * rs->k ], rs->k * sizeof(pgm_gf8_t)); + } + +/* invert */ + _pgm_matinv (rs->RM, rs->k); + + pgm_gf8_t* repairs[ rs->k ]; + +/* multiply out, through the length of erasures[] */ + for (uint_fast8_t j = 0; j < rs->k; j++) + { + if (offsets[ j ] < rs->k) + continue; + +#ifdef CONFIG_PREFER_MALLOC + pgm_gf8_t* erasure = repairs[ j ] = pgm_malloc0 (len); +#else + pgm_gf8_t* erasure = repairs[ j ] = pgm_alloca (len); + memset (erasure, 0, len); +#endif + for (uint_fast8_t i = 0; i < rs->k; i++) + { + pgm_gf8_t* src = block[ i ]; + pgm_gf8_t c = rs->RM[ (j * rs->k) + i ]; + _pgm_gf_vec_addmul (erasure, c, src, len); + } + } + +/* move repaired over parity packets */ + for (uint_fast8_t j = 0; j < rs->k; j++) + { + if (offsets[ j ] < rs->k) + continue; + + memcpy (block[ j ], repairs[ j ], len * sizeof(pgm_gf8_t)); +#ifdef CONFIG_PREFER_MALLOC + pgm_free (repairs[ j ]); +#endif + } +} + +/* entire FEC block of original data and parity packets. + * + * erased packet buffers must be zeroed. + */ +void +pgm_rs_decode_parity_appended ( + pgm_rs_t* restrict rs, + pgm_gf8_t** restrict block, /* length rs_t::n, the FEC block */ + const uint8_t* restrict offsets, /* ordered index of packets */ + const uint16_t len /* packet length */ + ) +{ + pgm_assert (NULL != rs); + pgm_assert (NULL != block); + pgm_assert (NULL != offsets); + pgm_assert (len > 0); + +/* create new recovery matrix from generator + */ + for (uint_fast8_t i = 0; i < rs->k; i++) + { + if (offsets[i] < rs->k) { + memset (&rs->RM[ i * rs->k ], 0, rs->k * sizeof(pgm_gf8_t)); + rs->RM[ (i * rs->k) + i ] = 1; + continue; + } + memcpy (&rs->RM[ i * rs->k ], &rs->GM[ offsets[ i ] * rs->k ], rs->k * sizeof(pgm_gf8_t)); + } + +/* invert */ + _pgm_matinv (rs->RM, rs->k); + +/* multiply out, through the length of erasures[] */ + for (uint_fast8_t j = 0; j < rs->k; j++) + { + if (offsets[ j ] < rs->k) + continue; + + uint_fast8_t p = rs->k; + pgm_gf8_t* erasure = block[ j ]; + for (uint_fast8_t i = 0; i < rs->k; i++) + { + pgm_gf8_t* src; + if (offsets[ i ] < rs->k) + src = block[ i ]; + else + src = block[ p++ ]; + const pgm_gf8_t c = rs->RM[ (j * rs->k) + i ]; + _pgm_gf_vec_addmul (erasure, c, src, len); + } + } +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/reed_solomon_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/reed_solomon_unittest.c new file mode 100644 index 0000000..5e0e75e --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/reed_solomon_unittest.c @@ -0,0 +1,305 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for Reed-Solomon forward error correction based on Vandermonde matrices. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include + + +/* mock state */ + + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + + +#define REED_SOLOMON_DEBUG +#include "reed_solomon.c" + + +/* target: + * void + * pgm_rs_create ( + * pgm_rs_t* rs, + * const uint8_t n, + * const uint8_t k + * ) + */ + +START_TEST (test_create_pass_001) +{ + pgm_rs_t rs; + pgm_rs_create (&rs, 255, 16); +} +END_TEST + +START_TEST (test_create_fail_001) +{ + pgm_rs_create (NULL, 255, 16); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_rs_destroy ( + * pgm_rs_t* rs, + * ) + */ + +START_TEST (test_destroy_pass_001) +{ + pgm_rs_t rs; + pgm_rs_create (&rs, 255, 16); + pgm_rs_destroy (&rs); +} +END_TEST + +START_TEST (test_destroy_fail_001) +{ + pgm_rs_destroy (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_rs_encode ( + * pgm_rs_t* rs, + * const pgm_gf8_t** src, + * const uint8_t offset, + * pgm_gf8_t* dst, + * const uint16_t len + * ) + */ + +START_TEST (test_encode_pass_001) +{ + const gchar source[] = "i am not a string"; + const guint16 source_len = strlen (source); + pgm_rs_t rs; + const guint8 k = source_len; + const guint8 parity_index = k; + const guint16 packet_len = 100; + pgm_gf8_t* source_packets[k]; + pgm_gf8_t* parity_packet = g_malloc0 (packet_len); + pgm_rs_create (&rs, 255, k); + for (unsigned i = 0; i < k; i++) { + source_packets[i] = g_malloc0 (packet_len); + source_packets[i][0] = source[i]; + g_message ("packet#%2.2d: 0x%02.2x '%c'", i, source[i], source[i]); + } + pgm_rs_encode (&rs, (const pgm_gf8_t**)source_packets, parity_index, parity_packet, packet_len); + g_message ("parity-packet: %02.2x", parity_packet[0]); + pgm_rs_destroy (&rs); +} +END_TEST + +START_TEST (test_encode_fail_001) +{ + pgm_rs_encode (NULL, NULL, 0, NULL, 0); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_rs_decode_parity_inline ( + * pgm_rs_t* rs, + * pgm_gf8_t** block, + * const uint8_t* offsets, + * const uint16_t len + * ) + */ + +START_TEST (test_decode_parity_inline_pass_001) +{ + const gchar source[] = "i am not a string"; + const guint16 source_len = strlen (source); + pgm_rs_t rs; + const guint8 k = source_len; + const guint8 parity_index = k; + const guint16 packet_len = 100; + pgm_gf8_t* source_packets[k]; + pgm_gf8_t* parity_packet = g_malloc0 (packet_len); + pgm_rs_create (&rs, 255, k); + for (unsigned i = 0; i < k; i++) { + source_packets[i] = g_malloc0 (packet_len); + source_packets[i][0] = source[i]; + } + pgm_rs_encode (&rs, (const pgm_gf8_t**)source_packets, parity_index, parity_packet, packet_len); +/* simulate error occuring at index #3 */ + const guint erased_index = 3; + source_packets[erased_index][0] = 'X'; + for (unsigned i = 0; i < k; i++) { + g_message ("damaged-packet#%2.2d: 0x%02.2x '%c'", + i, source_packets[i][0], source_packets[i][0]); + } + guint8 offsets[k]; + for (unsigned i = 0; i < k; i++) + offsets[i] = i; +/* erased offset now points to parity packet at k */ + offsets[erased_index] = parity_index; +/* move parity inline */ + g_free (source_packets[erased_index]); + source_packets[erased_index] = parity_packet; + pgm_rs_decode_parity_inline (&rs, source_packets, offsets, packet_len); + pgm_rs_destroy (&rs); + for (unsigned i = 0; i < k; i++) { + g_message ("repaired-packet#%2.2d: 0x%02.2x '%c'", + i, source_packets[i][0], source_packets[i][0]); + } +} +END_TEST + +START_TEST (test_decode_parity_inline_fail_001) +{ + pgm_rs_decode_parity_inline (NULL, NULL, NULL, 0); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_rs_decode_parity_appended ( + * pgm_rs_t* rs, + * pgm_gf8_t* block, + * const uint8_t* offsets, + * const uint16_t len + * ) + */ + +START_TEST (test_decode_parity_appended_pass_001) +{ + const gchar source[] = "i am not a string"; + const guint16 source_len = strlen (source); + pgm_rs_t rs; + const guint8 k = source_len; + const guint8 parity_index = k; + const guint16 packet_len = 100; + pgm_gf8_t* source_packets[k+1]; /* include 1 appended parity packet */ + pgm_gf8_t* parity_packet = g_malloc0 (packet_len); + pgm_rs_create (&rs, 255, k); + for (unsigned i = 0; i < k; i++) { + source_packets[i] = g_malloc0 (packet_len); + source_packets[i][0] = source[i]; + } + pgm_rs_encode (&rs, (const pgm_gf8_t**)source_packets, parity_index, parity_packet, packet_len); +/* simulate error occuring at index #3 */ + const guint erased_index = 3; + source_packets[erased_index][0] = 'X'; + for (unsigned i = 0; i < k; i++) { + g_message ("damaged-packet#%2.2d: 0x%02.2x '%c'", + i, source_packets[i][0], source_packets[i][0]); + } + guint8 offsets[k]; + for (unsigned i = 0; i < k; i++) + offsets[i] = i; +/* erased offset now points to parity packet at k */ + offsets[erased_index] = parity_index; +/* erase damaged packet */ + memset (source_packets[erased_index], 0, packet_len); +/* append parity to source packet block */ + source_packets[parity_index] = parity_packet; + pgm_rs_decode_parity_appended (&rs, source_packets, offsets, packet_len); + pgm_rs_destroy (&rs); + for (unsigned i = 0; i < k; i++) { + g_message ("repaired-packet#%2.2d: 0x%02.2x '%c'", + i, source_packets[i][0], source_packets[i][0]); + } +} +END_TEST + +START_TEST (test_decode_parity_appended_fail_001) +{ + pgm_rs_decode_parity_appended (NULL, NULL, NULL, 0); + fail ("reached"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_create = tcase_create ("create"); + suite_add_tcase (s, tc_create); + tcase_add_test (tc_create, test_create_pass_001); + tcase_add_test_raise_signal (tc_create, test_create_fail_001, SIGABRT); + + TCase* tc_destroy = tcase_create ("destroy"); + suite_add_tcase (s, tc_destroy); + tcase_add_test (tc_destroy, test_destroy_pass_001); + tcase_add_test_raise_signal (tc_destroy, test_destroy_fail_001, SIGABRT); + + TCase* tc_encode = tcase_create ("encode"); + suite_add_tcase (s, tc_encode); + tcase_add_test (tc_encode, test_encode_pass_001); + tcase_add_test_raise_signal (tc_encode, test_encode_fail_001, SIGABRT); + + TCase* tc_decode_parity_inline = tcase_create ("decode-parity-inline"); + suite_add_tcase (s, tc_decode_parity_inline); + tcase_add_test (tc_decode_parity_inline, test_decode_parity_inline_pass_001); + tcase_add_test_raise_signal (tc_decode_parity_inline, test_decode_parity_inline_fail_001, SIGABRT); + + TCase* tc_decode_parity_appended = tcase_create ("decode-parity-appended"); + suite_add_tcase (s, tc_decode_parity_appended); + tcase_add_test (tc_decode_parity_appended, test_decode_parity_appended_pass_001); + tcase_add_test_raise_signal (tc_decode_parity_appended, test_decode_parity_appended_fail_001, SIGABRT); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/rxw.c b/3rdparty/openpgm-svn-r1085/pgm/rxw.c new file mode 100644 index 0000000..ce04ef5 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/rxw.c @@ -0,0 +1,2229 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * A basic receive window: pointer array implementation. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include + + +//#define RXW_DEBUG + +#ifndef RXW_DEBUG +# define PGM_DISABLE_ASSERT +#endif + + +/* testing function: is TSI null + * + * returns TRUE if null, returns FALSE if not null. + */ + +static inline +bool +_pgm_tsi_is_null ( + const void*const tsi + ) +{ + const union { + pgm_tsi_t tsi; + uint32_t l[2]; + } *u = tsi; + +/* pre-conditions */ + pgm_assert (NULL != tsi); + + return (0 == u->l[0] && 0 == u->l[1]); +} + +/* sequence state must be smaller than PGM skbuff control buffer */ +PGM_STATIC_ASSERT(sizeof(struct pgm_rxw_state_t) <= sizeof(((struct pgm_sk_buff_t*)0)->cb)); + +static void _pgm_rxw_define (pgm_rxw_t*const, const uint32_t); +static void _pgm_rxw_update_trail (pgm_rxw_t*const, const uint32_t); +static inline uint32_t _pgm_rxw_update_lead (pgm_rxw_t*const, const uint32_t, const pgm_time_t, const pgm_time_t); +static inline uint32_t _pgm_rxw_tg_sqn (pgm_rxw_t*const, const uint32_t); +static inline uint32_t _pgm_rxw_pkt_sqn (pgm_rxw_t*const, const uint32_t); +static inline bool _pgm_rxw_is_first_of_tg_sqn (pgm_rxw_t*const, const uint32_t); +static inline bool _pgm_rxw_is_last_of_tg_sqn (pgm_rxw_t*const, const uint32_t); +static int _pgm_rxw_insert (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*const restrict); +static int _pgm_rxw_append (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*const restrict, const pgm_time_t); +static int _pgm_rxw_add_placeholder_range (pgm_rxw_t*const, const uint32_t, const pgm_time_t, const pgm_time_t); +static void _pgm_rxw_unlink (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*restrict); +static uint32_t _pgm_rxw_remove_trail (pgm_rxw_t*const); +static void _pgm_rxw_state (pgm_rxw_t*restrict, struct pgm_sk_buff_t*restrict, const int); +static inline void _pgm_rxw_shuffle_parity (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*const restrict); +static inline ssize_t _pgm_rxw_incoming_read (pgm_rxw_t*const restrict, struct pgm_msgv_t**restrict, uint32_t); +static bool _pgm_rxw_is_apdu_complete (pgm_rxw_t*const restrict, const uint32_t); +static inline ssize_t _pgm_rxw_incoming_read_apdu (pgm_rxw_t*const restrict, struct pgm_msgv_t**restrict); +static inline int _pgm_rxw_recovery_update (pgm_rxw_t*const, const uint32_t, const pgm_time_t); +static inline int _pgm_rxw_recovery_append (pgm_rxw_t*const, const pgm_time_t, const pgm_time_t); + + +/* returns the pointer at the given index of the window. + */ + +static +struct pgm_sk_buff_t* +_pgm_rxw_peek ( + const pgm_rxw_t* const window, + const uint32_t sequence + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + + if (pgm_rxw_is_empty (window)) + return NULL; + + if (pgm_uint32_gte (sequence, window->trail) && pgm_uint32_lte (sequence, window->lead)) + { + const uint_fast32_t index_ = sequence % pgm_rxw_max_length (window); + struct pgm_sk_buff_t* skb = window->pdata[index_]; +/* availability only guaranteed inside commit window */ + if (pgm_uint32_lt (sequence, window->commit_lead)) { + pgm_assert (NULL != skb); + pgm_assert (pgm_skb_is_valid (skb)); + pgm_assert (!_pgm_tsi_is_null (&skb->tsi)); + } + return skb; + } + + return NULL; +} + +/* sections of the receive window: + * + * | Commit | Incoming | + * |<---------------->|<------------>| + * | | | + * trail commit-lead lead + * + * commit buffers are currently held by the application, the window trail + * cannot be advanced if packets remain in the commit buffer. + * + * incoming buffers are waiting to be passed to the application. + */ + +static inline +uint32_t +_pgm_rxw_commit_length ( + const pgm_rxw_t* const window + ) +{ + pgm_assert (NULL != window); + return window->commit_lead - window->trail; +} + +static inline +bool +_pgm_rxw_commit_is_empty ( + const pgm_rxw_t* const window + ) +{ + pgm_assert (NULL != window); + return (_pgm_rxw_commit_length (window) == 0); +} + +static inline +uint32_t +_pgm_rxw_incoming_length ( + const pgm_rxw_t* const window + ) +{ + pgm_assert (NULL != window); + return ( 1 + window->lead ) - window->commit_lead; +} + +static inline +bool +_pgm_rxw_incoming_is_empty ( + const pgm_rxw_t* const window + ) +{ + pgm_assert (NULL != window); + return (_pgm_rxw_incoming_length (window) == 0); +} + +/* constructor for receive window. zero-length windows are not permitted. + * + * returns pointer to window. + */ + +pgm_rxw_t* +pgm_rxw_create ( + const pgm_tsi_t* tsi, + const uint16_t tpdu_size, + const unsigned sqns, /* transmit window size in sequence numbers */ + const unsigned secs, /* size in seconds */ + const ssize_t max_rte, /* max bandwidth */ + const uint32_t ack_c_p + ) +{ + pgm_rxw_t* window; + +/* pre-conditions */ + pgm_assert (NULL != tsi); + pgm_assert_cmpuint (tpdu_size, >, 0); + if (sqns) { + pgm_assert_cmpuint (sqns, >, 0); + pgm_assert_cmpuint (sqns & PGM_UINT32_SIGN_BIT, ==, 0); + pgm_assert_cmpuint (secs, ==, 0); + pgm_assert_cmpuint (max_rte, ==, 0); + } else { + pgm_assert_cmpuint (secs, >, 0); + pgm_assert_cmpuint (max_rte, >, 0); + } + + pgm_debug ("create (tsi:%s max-tpdu:%" PRIu16 " sqns:%" PRIu32 " secs %u max-rte %zd ack-c_p %" PRIu32 ")", + pgm_tsi_print (tsi), tpdu_size, sqns, secs, max_rte, ack_c_p); + +/* calculate receive window parameters */ + pgm_assert (sqns || (secs && max_rte)); + const unsigned alloc_sqns = sqns ? sqns : ( (secs * max_rte) / tpdu_size ); + window = pgm_malloc0 (sizeof(pgm_rxw_t) + ( alloc_sqns * sizeof(struct pgm_sk_buff_t*) )); + + window->tsi = tsi; + window->max_tpdu = tpdu_size; + +/* empty state: + * + * trail = 0, lead = -1 + * commit_trail = commit_lead = rxw_trail = rxw_trail_init = 0 + */ + window->lead = -1; + window->trail = window->lead + 1; + +/* limit retransmit requests on late session joining */ + window->is_constrained = TRUE; + +/* minimum value of RS::k = 1 */ + window->tg_size = 1; + +/* PGMCC filter weight */ + window->ack_c_p = pgm_fp16 (ack_c_p); + window->bitmap = 0xffffffff; + +/* pointer array */ + window->alloc = alloc_sqns; + +/* post-conditions */ + pgm_assert_cmpuint (pgm_rxw_max_length (window), ==, alloc_sqns); + pgm_assert_cmpuint (pgm_rxw_length (window), ==, 0); + pgm_assert_cmpuint (pgm_rxw_size (window), ==, 0); + pgm_assert (pgm_rxw_is_empty (window)); + pgm_assert (!pgm_rxw_is_full (window)); + + return window; +} + +/* destructor for receive window. must not be called more than once for same window. + */ + +void +pgm_rxw_destroy ( + pgm_rxw_t* const window + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert_cmpuint (window->alloc, >, 0); + + pgm_debug ("destroy (window:%p)", (const void*)window); + +/* contents of window */ + while (!pgm_rxw_is_empty (window)) { + _pgm_rxw_remove_trail (window); + } + +/* window must now be empty */ + pgm_assert_cmpuint (pgm_rxw_length (window), ==, 0); + pgm_assert_cmpuint (pgm_rxw_size (window), ==, 0); + pgm_assert (pgm_rxw_is_empty (window)); + pgm_assert (!pgm_rxw_is_full (window)); + +/* window */ + pgm_free (window); +} + +/* add skb to receive window. window has fixed size and will not grow. + * PGM skbuff data/tail pointers must point to the PGM payload, and hence skb->len + * is allowed to be zero. + * + * if the skb sequence number indicates lost packets placeholders will be defined + * for each missing entry in the window. + * + * side effects: + * + * 1) sequence number is set in skb from PGM header value. + * 2) window may be updated with new skb. + * 3) placeholders may be created for detected lost packets. + * 4) parity skbs may be shuffled to accomodate original data. + * + * returns: + * PGM_RXW_INSERTED - packet filled a waiting placeholder, skb consumed. + * PGM_RXW_APPENDED - packet advanced window lead, skb consumed. + * PGM_RXW_MISSING - missing packets detected whilst window lead was adanced, skb consumed. + * PGM_RXW_DUPLICATE - re-transmission of previously seen packet. + * PGM_RXW_MALFORMED - corrupted or invalid packet. + * PGM_RXW_BOUNDS - packet out of window. + * + * it is an error to try to free the skb after adding to the window. + */ + +int +pgm_rxw_add ( + pgm_rxw_t* const restrict window, + struct pgm_sk_buff_t* const restrict skb, + const pgm_time_t now, + const pgm_time_t nak_rb_expiry /* calculated expiry time for this skb */ + ) +{ + pgm_rxw_state_t* const state = (pgm_rxw_state_t*)&skb->cb; + int status; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != skb); + pgm_assert_cmpuint (nak_rb_expiry, >, 0); + pgm_assert_cmpuint (pgm_rxw_max_length (window), >, 0); + pgm_assert (pgm_skb_is_valid (skb)); + pgm_assert (((const pgm_list_t*)skb)->next == NULL); + pgm_assert (((const pgm_list_t*)skb)->prev == NULL); + pgm_assert (!_pgm_tsi_is_null (&skb->tsi)); + pgm_assert ((char*)skb->data > (char*)skb->head); + pgm_assert (sizeof(struct pgm_header) + sizeof(struct pgm_data) <= (size_t)((char*)skb->data - (char*)skb->head)); + pgm_assert (skb->len == ((char*)skb->tail - (char*)skb->data)); + + pgm_debug ("add (window:%p skb:%p nak_rb_expiry:%" PGM_TIME_FORMAT ")", + (const void*)window, (const void*)skb, nak_rb_expiry); + + skb->sequence = ntohl (skb->pgm_data->data_sqn); + +/* protocol sanity check: tsdu size */ + if (PGM_UNLIKELY(skb->len != ntohs (skb->pgm_header->pgm_tsdu_length))) + return PGM_RXW_MALFORMED; + +/* protocol sanity check: valid trail pointer wrt. sequence */ + if (PGM_UNLIKELY(skb->sequence - ntohl (skb->pgm_data->data_trail) >= ((UINT32_MAX/2)-1))) + return PGM_RXW_MALFORMED; + +/* verify fragment header for original data, parity packets include a + * parity fragment header + */ + if (!(skb->pgm_header->pgm_options & PGM_OPT_PARITY) && + skb->pgm_opt_fragment) + { +/* protocol sanity check: single fragment APDU */ + if (PGM_UNLIKELY(ntohl (skb->of_apdu_len) == skb->len)) + skb->pgm_opt_fragment = NULL; + +/* protocol sanity check: minimum APDU length */ + if (PGM_UNLIKELY(ntohl (skb->of_apdu_len) < skb->len)) + return PGM_RXW_MALFORMED; + +/* protocol sanity check: sequential ordering */ + if (PGM_UNLIKELY(pgm_uint32_gt (ntohl (skb->of_apdu_first_sqn), skb->sequence))) + return PGM_RXW_MALFORMED; + +/* protocol sanity check: maximum APDU length */ + if (PGM_UNLIKELY(ntohl (skb->of_apdu_len) > PGM_MAX_APDU)) + return PGM_RXW_MALFORMED; + } + +/* first packet of a session defines the window */ + if (PGM_UNLIKELY(!window->is_defined)) + _pgm_rxw_define (window, skb->sequence - 1); /* previous_lead needed for append to occur */ + else + _pgm_rxw_update_trail (window, ntohl (skb->pgm_data->data_trail)); + +/* bounds checking for parity data occurs at the transmission group sequence number */ + if (skb->pgm_header->pgm_options & PGM_OPT_PARITY) + { + if (pgm_uint32_lt (_pgm_rxw_tg_sqn (window, skb->sequence), _pgm_rxw_tg_sqn (window, window->commit_lead))) + return PGM_RXW_DUPLICATE; + + if (pgm_uint32_lt (_pgm_rxw_tg_sqn (window, skb->sequence), _pgm_rxw_tg_sqn (window, window->lead))) { + window->has_event = 1; + return _pgm_rxw_insert (window, skb); + } + + const struct pgm_sk_buff_t* const first_skb = _pgm_rxw_peek (window, _pgm_rxw_tg_sqn (window, skb->sequence)); + const pgm_rxw_state_t* const first_state = (pgm_rxw_state_t*)&first_skb->cb; + + if (_pgm_rxw_tg_sqn (window, skb->sequence) == _pgm_rxw_tg_sqn (window, window->lead)) { + window->has_event = 1; + if (NULL == first_state || first_state->is_contiguous) { + state->is_contiguous = 1; + return _pgm_rxw_append (window, skb, now); + } else + return _pgm_rxw_insert (window, skb); + } + + pgm_assert (NULL != first_state); + status = _pgm_rxw_add_placeholder_range (window, _pgm_rxw_tg_sqn (window, skb->sequence), now, nak_rb_expiry); + } + else + { + if (pgm_uint32_lt (skb->sequence, window->commit_lead)) { + if (pgm_uint32_gte (skb->sequence, window->trail)) + return PGM_RXW_DUPLICATE; + else + return PGM_RXW_BOUNDS; + } + + if (pgm_uint32_lte (skb->sequence, window->lead)) { + window->has_event = 1; + return _pgm_rxw_insert (window, skb); + } + + if (skb->sequence == pgm_rxw_next_lead (window)) { + window->has_event = 1; + if (_pgm_rxw_is_first_of_tg_sqn (window, skb->sequence)) + state->is_contiguous = 1; + return _pgm_rxw_append (window, skb, now); + } + + status = _pgm_rxw_add_placeholder_range (window, skb->sequence, now, nak_rb_expiry); + } + + if (PGM_RXW_APPENDED == status) { + status = _pgm_rxw_append (window, skb, now); + if (PGM_RXW_APPENDED == status) + status = PGM_RXW_MISSING; + } + return status; +} + +/* trail is the next packet to commit upstream, lead is the leading edge + * of the receive window with possible gaps inside, rxw_trail is the transmit + * window trail for retransmit requests. + */ + +/* define window by parameters of first data packet. + */ + +static +void +_pgm_rxw_define ( + pgm_rxw_t* const window, + const uint32_t lead + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (pgm_rxw_is_empty (window)); + pgm_assert (_pgm_rxw_commit_is_empty (window)); + pgm_assert (_pgm_rxw_incoming_is_empty (window)); + pgm_assert (!window->is_defined); + + window->lead = lead; + window->commit_lead = window->rxw_trail = window->rxw_trail_init = window->trail = window->lead + 1; + window->is_constrained = window->is_defined = TRUE; + +/* post-conditions */ + pgm_assert (pgm_rxw_is_empty (window)); + pgm_assert (_pgm_rxw_commit_is_empty (window)); + pgm_assert (_pgm_rxw_incoming_is_empty (window)); + pgm_assert (window->is_defined); + pgm_assert (window->is_constrained); +} + +/* update window with latest transmitted parameters. + * + * returns count of placeholders added into window, used to start sending naks. + */ + +unsigned +pgm_rxw_update ( + pgm_rxw_t* const window, + const uint32_t txw_lead, + const uint32_t txw_trail, + const pgm_time_t now, + const pgm_time_t nak_rb_expiry /* packet expiration time */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert_cmpuint (nak_rb_expiry, >, 0); + + pgm_debug ("pgm_rxw_update (window:%p txw-lead:%" PRIu32 " txw-trail:%" PRIu32 " nak-rb-expiry:%" PGM_TIME_FORMAT ")", + (void*)window, txw_lead, txw_trail, nak_rb_expiry); + + if (PGM_UNLIKELY(!window->is_defined)) { + _pgm_rxw_define (window, txw_lead); + return 0; + } + + _pgm_rxw_update_trail (window, txw_trail); + return _pgm_rxw_update_lead (window, txw_lead, now, nak_rb_expiry); +} + +/* update trailing edge of receive window + */ + +static +void +_pgm_rxw_update_trail ( + pgm_rxw_t* const window, + const uint32_t txw_trail + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + +/* advertised trail is less than the current value */ + if (PGM_UNLIKELY(pgm_uint32_lte (txw_trail, window->rxw_trail))) + return; + +/* protocol sanity check: advertised trail jumps too far ahead */ + if (PGM_UNLIKELY(txw_trail - window->rxw_trail > ((UINT32_MAX/2)-1))) + return; + +/* retransmissions requests are constrained on startup until the advertised trail advances + * beyond the first data sequence number. + */ + if (PGM_UNLIKELY(window->is_constrained)) + { + if (pgm_uint32_gt (txw_trail, window->rxw_trail_init)) + window->is_constrained = FALSE; + else + return; + } + + window->rxw_trail = txw_trail; + +/* new value doesn't affect window */ + if (PGM_UNLIKELY(pgm_uint32_lte (window->rxw_trail, window->trail))) + return; + +/* jump remaining sequence numbers if window is empty */ + if (pgm_rxw_is_empty (window)) + { + const uint32_t distance = (int32_t)(window->rxw_trail) - (int32_t)(window->trail); + window->commit_lead = window->trail += distance; + window->lead += distance; + +/* add loss to bitmap */ + if (distance > 32) window->bitmap = 0; + else window->bitmap <<= distance; + +/* update the Exponential Moving Average (EMA) data loss with long jump: + * s_t = α × (p₁ + (1 - α) × p₂ + (1 - α)² × p₃ + ⋯) + * omitting the weight by stopping after k terms, + * = α × ((1 - α)^^k + (1 - α)^^{k+1} +(1 - α)^^{k+1} + ⋯) + * = α × (1 - α)^^k × (1 + (1 - α) + (1 - α)² + ⋯) + * = (1 - α)^^k + */ + window->data_loss = pgm_fp16mul (window->data_loss, pgm_fp16pow (pgm_fp16 (1) - window->ack_c_p, distance)); + + window->cumulative_losses += distance; + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Data loss due to trailing edge update, fragment count %" PRIu32 "."),window->fragment_count); + pgm_assert (pgm_rxw_is_empty (window)); + pgm_assert (_pgm_rxw_commit_is_empty (window)); + pgm_assert (_pgm_rxw_incoming_is_empty (window)); + return; + } + +/* remove all buffers between commit lead and advertised rxw_trail */ + for (uint32_t sequence = window->commit_lead; + pgm_uint32_gt (window->rxw_trail, sequence) && pgm_uint32_gte (window->lead, sequence); + sequence++) + { + struct pgm_sk_buff_t* skb; + pgm_rxw_state_t* state; + + skb = _pgm_rxw_peek (window, sequence); + pgm_assert (NULL != skb); + state = (pgm_rxw_state_t*)&skb->cb; + + switch (state->pkt_state) { + case PGM_PKT_STATE_HAVE_DATA: + case PGM_PKT_STATE_HAVE_PARITY: + case PGM_PKT_STATE_LOST_DATA: + break; + + case PGM_PKT_STATE_ERROR: + pgm_assert_not_reached(); + + default: + pgm_rxw_lost (window, sequence); + break; + } + } + +/* post-conditions: only after flush */ +// pgm_assert (!pgm_rxw_is_full (window)); +} + +/* update FEC parameters + */ + +void +pgm_rxw_update_fec ( + pgm_rxw_t* const window, + const uint8_t rs_k + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert_cmpuint (rs_k, >, 1); + + pgm_debug ("pgm_rxw_update_fec (window:%p rs(k):%u)", + (void*)window, rs_k); + + if (window->is_fec_available) { + if (rs_k == window->rs.k) return; + pgm_rs_destroy (&window->rs); + } else + window->is_fec_available = 1; + pgm_rs_create (&window->rs, PGM_RS_DEFAULT_N, rs_k); + window->tg_sqn_shift = pgm_power2_log2 (rs_k); + window->tg_size = window->rs.k; +} + +/* add one placeholder to leading edge due to detected lost packet. + */ + +static +void +_pgm_rxw_add_placeholder ( + pgm_rxw_t* const window, + const pgm_time_t now, + const pgm_time_t nak_rb_expiry + ) +{ + struct pgm_sk_buff_t* skb; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (!pgm_rxw_is_full (window)); + +/* advance lead */ + window->lead++; + +/* add loss to bitmap */ + window->bitmap <<= 1; + +/* update the Exponential Moving Average (EMA) data loss with loss: + * s_t = α × x_{t-1} + (1 - α) × s_{t-1} + * x_{t-1} = 1 + * ∴ s_t = α + (1 - α) × s_{t-1} + */ + window->data_loss = window->ack_c_p + pgm_fp16mul ((pgm_fp16 (1) - window->ack_c_p), window->data_loss); + + skb = pgm_alloc_skb (window->max_tpdu); + pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; + skb->tstamp = now; + skb->sequence = window->lead; + state->timer_expiry = nak_rb_expiry; + + if (!_pgm_rxw_is_first_of_tg_sqn (window, skb->sequence)) + { + struct pgm_sk_buff_t* first_skb = _pgm_rxw_peek (window, _pgm_rxw_tg_sqn (window, skb->sequence)); + if (first_skb) { + pgm_rxw_state_t* first_state = (pgm_rxw_state_t*)&first_skb->cb; + first_state->is_contiguous = 0; + } + } + +/* add skb to window */ + const uint_fast32_t index_ = skb->sequence % pgm_rxw_max_length (window); + window->pdata[index_] = skb; + + pgm_rxw_state (window, skb, PGM_PKT_STATE_BACK_OFF); + +/* post-conditions */ + pgm_assert_cmpuint (pgm_rxw_length (window), >, 0); + pgm_assert_cmpuint (pgm_rxw_length (window), <=, pgm_rxw_max_length (window)); + pgm_assert_cmpuint (_pgm_rxw_incoming_length (window), >, 0); +} + +/* add a range of placeholders to the window. + */ + +static +int +_pgm_rxw_add_placeholder_range ( + pgm_rxw_t* const window, + const uint32_t sequence, + const pgm_time_t now, + const pgm_time_t nak_rb_expiry + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (pgm_uint32_gt (sequence, pgm_rxw_lead (window))); + +/* check bounds of commit window */ + const uint32_t new_commit_sqns = ( 1 + sequence ) - window->trail; + if ( !_pgm_rxw_commit_is_empty (window) && + (new_commit_sqns >= pgm_rxw_max_length (window)) ) + { + _pgm_rxw_update_lead (window, sequence, now, nak_rb_expiry); + return PGM_RXW_BOUNDS; /* effectively a slow consumer */ + } + + if (pgm_rxw_is_full (window)) { + pgm_assert (_pgm_rxw_commit_is_empty (window)); + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Receive window full on placeholder sequence.")); + _pgm_rxw_remove_trail (window); + } + +/* if packet is non-contiguous to current leading edge add place holders + * TODO: can be rather inefficient on packet loss looping through dropped sequence numbers + */ + while (pgm_rxw_next_lead (window) != sequence) + { + _pgm_rxw_add_placeholder (window, now, nak_rb_expiry); + if (pgm_rxw_is_full (window)) { + pgm_assert (_pgm_rxw_commit_is_empty (window)); + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Receive window full on placeholder sequence.")); + _pgm_rxw_remove_trail (window); + } + } + +/* post-conditions */ + pgm_assert (!pgm_rxw_is_full (window)); + + return PGM_RXW_APPENDED; +} + +/* update leading edge of receive window. + * + * returns number of place holders added. + */ + +static +unsigned +_pgm_rxw_update_lead ( + pgm_rxw_t* const window, + const uint32_t txw_lead, + const pgm_time_t now, + const pgm_time_t nak_rb_expiry + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + +/* advertised lead is less than the current value */ + if (PGM_UNLIKELY(pgm_uint32_lte (txw_lead, window->lead))) + return 0; + + uint32_t lead; + +/* committed packets limit constrain the lead until they are released */ + if (!_pgm_rxw_commit_is_empty (window) && + (txw_lead - window->trail) >= pgm_rxw_max_length (window)) + { + lead = window->trail + pgm_rxw_max_length (window) - 1; + if (lead == window->lead) + return 0; + } + else + lead = txw_lead; + + unsigned lost = 0; + + while (window->lead != lead) + { +/* slow consumer or fast producer */ + if (pgm_rxw_is_full (window)) { + pgm_assert (_pgm_rxw_commit_is_empty (window)); + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Receive window full on window lead advancement.")); + _pgm_rxw_remove_trail (window); + } + _pgm_rxw_add_placeholder (window, now, nak_rb_expiry); + lost++; + } + + return lost; +} + +/* checks whether an APDU is unrecoverable due to lost TPDUs. + */ +static inline +bool +_pgm_rxw_is_apdu_lost ( + pgm_rxw_t* const restrict window, + struct pgm_sk_buff_t* const restrict skb + ) +{ + const pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != skb); + +/* lost is lost */ + if (PGM_PKT_STATE_LOST_DATA == state->pkt_state) + return TRUE; + +/* by definition, a single-TPDU APDU is complete */ + if (!skb->pgm_opt_fragment) + return FALSE; + + const uint32_t apdu_first_sqn = ntohl (skb->of_apdu_first_sqn); + +/* by definition, first fragment indicates APDU is available */ + if (apdu_first_sqn == skb->sequence) + return FALSE; + + const struct pgm_sk_buff_t* const first_skb = _pgm_rxw_peek (window, apdu_first_sqn); +/* first fragment out-of-bounds */ + if (NULL == first_skb) + return TRUE; + + const pgm_rxw_state_t* first_state = (pgm_rxw_state_t*)&first_skb->cb; + if (PGM_PKT_STATE_LOST_DATA == first_state->pkt_state) + return TRUE; + + return FALSE; +} + +/* return the first missing packet sequence in the specified transmission + * group or NULL if not required. + */ + +static inline +struct pgm_sk_buff_t* +_pgm_rxw_find_missing ( + pgm_rxw_t* const window, + const uint32_t tg_sqn /* tg_sqn | pkt_sqn */ + ) +{ + struct pgm_sk_buff_t* skb; + pgm_rxw_state_t* state; + +/* pre-conditions */ + pgm_assert (NULL != window); + + for (uint32_t i = tg_sqn, j = 0; j < window->tg_size; i++, j++) + { + skb = _pgm_rxw_peek (window, i); + pgm_assert (NULL != skb); + state = (pgm_rxw_state_t*)&skb->cb; + switch (state->pkt_state) { + case PGM_PKT_STATE_BACK_OFF: + case PGM_PKT_STATE_WAIT_NCF: + case PGM_PKT_STATE_WAIT_DATA: + case PGM_PKT_STATE_LOST_DATA: + return skb; + + case PGM_PKT_STATE_HAVE_DATA: + case PGM_PKT_STATE_HAVE_PARITY: + break; + + default: pgm_assert_not_reached(); break; + } + } + + return NULL; +} + +/* returns TRUE if skb is a parity packet with packet length not + * matching the transmission group length without the variable-packet-length + * flag set. + */ + +static inline +bool +_pgm_rxw_is_invalid_var_pktlen ( + pgm_rxw_t* const restrict window, + const struct pgm_sk_buff_t* const restrict skb + ) +{ + const struct pgm_sk_buff_t* first_skb; + +/* pre-conditions */ + pgm_assert (NULL != window); + + if (!window->is_fec_available) + return FALSE; + + if (skb->pgm_header->pgm_options & PGM_OPT_VAR_PKTLEN) + return FALSE; + + const uint32_t tg_sqn = _pgm_rxw_tg_sqn (window, skb->sequence); + if (tg_sqn == skb->sequence) + return FALSE; + + first_skb = _pgm_rxw_peek (window, tg_sqn); + if (NULL == first_skb) + return TRUE; /* transmission group unrecoverable */ + + if (first_skb->len == skb->len) + return FALSE; + + return TRUE; +} + +static inline +bool +_pgm_rxw_has_payload_op ( + const struct pgm_sk_buff_t* const skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + pgm_assert (NULL != skb->pgm_header); + + return skb->pgm_opt_fragment || skb->pgm_header->pgm_options & PGM_OP_ENCODED; +} + +/* returns TRUE is skb options are invalid when compared to the transmission group + */ + +static inline +bool +_pgm_rxw_is_invalid_payload_op ( + pgm_rxw_t* const restrict window, + const struct pgm_sk_buff_t* const restrict skb + ) +{ + const struct pgm_sk_buff_t* first_skb; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != skb); + + if (!window->is_fec_available) + return FALSE; + + const uint32_t tg_sqn = _pgm_rxw_tg_sqn (window, skb->sequence); + if (tg_sqn == skb->sequence) + return FALSE; + + first_skb = _pgm_rxw_peek (window, tg_sqn); + if (NULL == first_skb) + return TRUE; /* transmission group unrecoverable */ + + if (_pgm_rxw_has_payload_op (first_skb) == _pgm_rxw_has_payload_op (skb)) + return FALSE; + + return TRUE; +} + +/* insert skb into window range, discard if duplicate. window will have placeholder, + * parity, or data packet already matching sequence. + * + * returns: + * PGM_RXW_INSERTED - packet filled a waiting placeholder, skb consumed. + * PGM_RXW_DUPLICATE - re-transmission of previously seen packet. + * PGM_RXW_MALFORMED - corrupted or invalid packet. + * PGM_RXW_BOUNDS - packet out of window. + */ + +static +int +_pgm_rxw_insert ( + pgm_rxw_t* const restrict window, + struct pgm_sk_buff_t* const restrict new_skb + ) +{ + struct pgm_sk_buff_t* skb; + pgm_rxw_state_t* state; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != new_skb); + pgm_assert (!_pgm_rxw_incoming_is_empty (window)); + + if (PGM_UNLIKELY(_pgm_rxw_is_invalid_var_pktlen (window, new_skb) || + _pgm_rxw_is_invalid_payload_op (window, new_skb))) + return PGM_RXW_MALFORMED; + + if (new_skb->pgm_header->pgm_options & PGM_OPT_PARITY) + { + skb = _pgm_rxw_find_missing (window, new_skb->sequence); + if (NULL == skb) + return PGM_RXW_DUPLICATE; + state = (pgm_rxw_state_t*)&skb->cb; + } + else + { + skb = _pgm_rxw_peek (window, new_skb->sequence); + pgm_assert (NULL != skb); + state = (pgm_rxw_state_t*)&skb->cb; + + if (state->pkt_state == PGM_PKT_STATE_HAVE_DATA) + return PGM_RXW_DUPLICATE; + } + +/* APDU fragments are already declared lost */ + if (new_skb->pgm_opt_fragment && + _pgm_rxw_is_apdu_lost (window, new_skb)) + { + pgm_rxw_lost (window, skb->sequence); + return PGM_RXW_BOUNDS; + } + +/* verify placeholder state */ + switch (state->pkt_state) { + case PGM_PKT_STATE_BACK_OFF: + case PGM_PKT_STATE_WAIT_NCF: + case PGM_PKT_STATE_WAIT_DATA: + case PGM_PKT_STATE_LOST_DATA: + break; + + case PGM_PKT_STATE_HAVE_PARITY: + _pgm_rxw_shuffle_parity (window, skb); + break; + + default: pgm_assert_not_reached(); break; + } + +/* statistics */ + const pgm_time_t fill_time = new_skb->tstamp - skb->tstamp; + PGM_HISTOGRAM_TIMES("Rx.RepairTime", fill_time); + PGM_HISTOGRAM_COUNTS("Rx.NakTransmits", state->nak_transmit_count); + PGM_HISTOGRAM_COUNTS("Rx.NcfRetries", state->ncf_retry_count); + PGM_HISTOGRAM_COUNTS("Rx.DataRetries", state->data_retry_count); + if (!window->max_fill_time) { + window->max_fill_time = window->min_fill_time = fill_time; + } + else + { + if (fill_time > window->max_fill_time) + window->max_fill_time = fill_time; + else if (fill_time < window->min_fill_time) + window->min_fill_time = fill_time; + + if (!window->max_nak_transmit_count) { + window->max_nak_transmit_count = window->min_nak_transmit_count = state->nak_transmit_count; + } else { + if (state->nak_transmit_count > window->max_nak_transmit_count) + window->max_nak_transmit_count = state->nak_transmit_count; + else if (state->nak_transmit_count < window->min_nak_transmit_count) + window->min_nak_transmit_count = state->nak_transmit_count; + } + } + +/* add packet to bitmap */ + const uint_fast32_t pos = window->lead - new_skb->sequence; + if (pos < 32) { + window->bitmap |= 1 << pos; + } + +/* update the Exponential Moving Average (EMA) data loss with repair data. + * s_t = α × x_{t-1} + (1 - α) × s_{t-1} + * x_{t-1} = 0 + * ∴ s_t = (1 - α) × s_{t-1} + */ + const uint_fast32_t s = pgm_fp16pow (pgm_fp16 (1) - window->ack_c_p, pos); + if (s > window->data_loss) window->data_loss = 0; + else window->data_loss -= s; + +/* replace place holder skb with incoming skb */ + memcpy (new_skb->cb, skb->cb, sizeof(skb->cb)); + pgm_rxw_state_t* rxw_state = (void*)new_skb->cb; + rxw_state->pkt_state = PGM_PKT_STATE_ERROR; + _pgm_rxw_unlink (window, skb); + pgm_free_skb (skb); + const uint_fast32_t index_ = new_skb->sequence % pgm_rxw_max_length (window); + window->pdata[index_] = new_skb; + if (new_skb->pgm_header->pgm_options & PGM_OPT_PARITY) + _pgm_rxw_state (window, new_skb, PGM_PKT_STATE_HAVE_PARITY); + else + _pgm_rxw_state (window, new_skb, PGM_PKT_STATE_HAVE_DATA); + window->size += new_skb->len; + + return PGM_RXW_INSERTED; +} + +/* shuffle parity packet at skb->sequence to any other needed spot. + */ + +static inline +void +_pgm_rxw_shuffle_parity ( + pgm_rxw_t* const restrict window, + struct pgm_sk_buff_t* const restrict skb + ) +{ + uint_fast32_t index_; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != skb); + + struct pgm_sk_buff_t* restrict missing = _pgm_rxw_find_missing (window, skb->sequence); + if (NULL == missing) + return; + +/* replace place holder skb with parity skb */ + char cb[48]; + _pgm_rxw_unlink (window, missing); + memcpy (cb, skb->cb, sizeof(skb->cb)); + memcpy (skb->cb, missing->cb, sizeof(skb->cb)); + memcpy (missing->cb, cb, sizeof(skb->cb)); + index_ = skb->sequence % pgm_rxw_max_length (window); + window->pdata[index_] = skb; + index_ = missing->sequence % pgm_rxw_max_length (window); + window->pdata[index_] = missing; +} + +/* skb advances the window lead. + * + * returns: + * PGM_RXW_APPENDED - packet advanced window lead, skb consumed. + * PGM_RXW_MALFORMED - corrupted or invalid packet. + * PGM_RXW_BOUNDS - packet out of window. + */ + +static +int +_pgm_rxw_append ( + pgm_rxw_t* const restrict window, + struct pgm_sk_buff_t* const restrict skb, + const pgm_time_t now + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != skb); + if (skb->pgm_header->pgm_options & PGM_OPT_PARITY) { + pgm_assert (_pgm_rxw_tg_sqn (window, skb->sequence) == _pgm_rxw_tg_sqn (window, pgm_rxw_lead (window))); + } else { + pgm_assert (skb->sequence == pgm_rxw_next_lead (window)); + } + + if (PGM_UNLIKELY(_pgm_rxw_is_invalid_var_pktlen (window, skb) || + _pgm_rxw_is_invalid_payload_op (window, skb))) + return PGM_RXW_MALFORMED; + + if (pgm_rxw_is_full (window)) { + if (_pgm_rxw_commit_is_empty (window)) { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Receive window full on new data.")); + _pgm_rxw_remove_trail (window); + } else { + return PGM_RXW_BOUNDS; /* constrained by commit window */ + } + } + +/* advance leading edge */ + window->lead++; + +/* add packet to bitmap */ + window->bitmap = (window->bitmap << 1) | 1; + +/* update the Exponential Moving Average (EMA) data loss with data: + * s_t = α × x_{t-1} + (1 - α) × s_{t-1} + * x_{t-1} = 0 + * ∴ s_t = (1 - α) × s_{t-1} + */ + window->data_loss = pgm_fp16mul (window->data_loss, pgm_fp16 (1) - window->ack_c_p); + +/* APDU fragments are already declared lost */ + if (PGM_UNLIKELY(skb->pgm_opt_fragment && + _pgm_rxw_is_apdu_lost (window, skb))) + { + struct pgm_sk_buff_t* lost_skb = pgm_alloc_skb (window->max_tpdu); + lost_skb->tstamp = now; + lost_skb->sequence = skb->sequence; + +/* add lost-placeholder skb to window */ + const uint_fast32_t index_ = lost_skb->sequence % pgm_rxw_max_length (window); + window->pdata[index_] = lost_skb; + + _pgm_rxw_state (window, lost_skb, PGM_PKT_STATE_LOST_DATA); + return PGM_RXW_BOUNDS; + } + +/* add skb to window */ + if (skb->pgm_header->pgm_options & PGM_OPT_PARITY) + { + const uint_fast32_t index_ = skb->sequence % pgm_rxw_max_length (window); + window->pdata[index_] = skb; + _pgm_rxw_state (window, skb, PGM_PKT_STATE_HAVE_PARITY); + } + else + { + const uint_fast32_t index_ = skb->sequence % pgm_rxw_max_length (window); + window->pdata[index_] = skb; + _pgm_rxw_state (window, skb, PGM_PKT_STATE_HAVE_DATA); + } + +/* statistics */ + window->size += skb->len; + + return PGM_RXW_APPENDED; +} + +/* remove references to all commit packets not in the same transmission group + * as the commit-lead + */ + +void +pgm_rxw_remove_commit ( + pgm_rxw_t* const window + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + + const uint32_t tg_sqn_of_commit_lead = _pgm_rxw_tg_sqn (window, window->commit_lead); + + while (!_pgm_rxw_commit_is_empty (window) && + tg_sqn_of_commit_lead != _pgm_rxw_tg_sqn (window, window->trail)) + { + _pgm_rxw_remove_trail (window); + } +} + +/* flush packets but instead of calling on_data append the contiguous data packets + * to the provided scatter/gather vector. + * + * when transmission groups are enabled, packets remain in the windows tagged committed + * until the transmission group has been completely committed. this allows the packet + * data to be used in parity calculations to recover the missing packets. + * + * returns -1 on nothing read, returns length of bytes read, 0 is a valid read length. + * + * PGM skbuffs will have an increased reference count and must be unreferenced by the + * calling application. + */ + +ssize_t +pgm_rxw_readv ( + pgm_rxw_t* const restrict window, + struct pgm_msgv_t** restrict pmsg, /* message array, updated as messages appended */ + const unsigned pmsglen /* number of items in pmsg */ + ) +{ + const struct pgm_msgv_t* msg_end; + struct pgm_sk_buff_t* skb; + pgm_rxw_state_t* state; + ssize_t bytes_read; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != pmsg); + pgm_assert_cmpuint (pmsglen, >, 0); + + pgm_debug ("readv (window:%p pmsg:%p pmsglen:%u)", + (void*)window, (void*)pmsg, pmsglen); + + msg_end = *pmsg + pmsglen - 1; + + if (_pgm_rxw_incoming_is_empty (window)) + return -1; + + skb = _pgm_rxw_peek (window, window->commit_lead); + pgm_assert (NULL != skb); + + state = (pgm_rxw_state_t*)&skb->cb; + switch (state->pkt_state) { + case PGM_PKT_STATE_HAVE_DATA: + bytes_read = _pgm_rxw_incoming_read (window, pmsg, msg_end - *pmsg + 1); + break; + + case PGM_PKT_STATE_LOST_DATA: +/* do not purge in situ sequence */ + if (_pgm_rxw_commit_is_empty (window)) { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Removing lost trail from window")); + _pgm_rxw_remove_trail (window); + } else { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Locking trail at commit window")); + } +/* fall through */ + case PGM_PKT_STATE_BACK_OFF: + case PGM_PKT_STATE_WAIT_NCF: + case PGM_PKT_STATE_WAIT_DATA: + case PGM_PKT_STATE_HAVE_PARITY: + bytes_read = -1; + break; + + case PGM_PKT_STATE_COMMIT_DATA: + case PGM_PKT_STATE_ERROR: + default: + pgm_assert_not_reached(); + break; + } + + return bytes_read; +} + +/* remove lost sequences from the trailing edge of the window. lost sequence + * at lead of commit window invalidates all parity-data packets as any + * transmission group is now unrecoverable. + * + * returns number of sequences purged. + */ + +static +unsigned +_pgm_rxw_remove_trail ( + pgm_rxw_t* const window + ) +{ + struct pgm_sk_buff_t* skb; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (!pgm_rxw_is_empty (window)); + + skb = _pgm_rxw_peek (window, window->trail); + pgm_assert (NULL != skb); + _pgm_rxw_unlink (window, skb); + window->size -= skb->len; +/* remove reference to skb */ + if (PGM_UNLIKELY(pgm_mem_gc_friendly)) { + const uint_fast32_t index_ = skb->sequence % pgm_rxw_max_length (window); + window->pdata[index_] = NULL; + } + pgm_free_skb (skb); + if (window->trail++ == window->commit_lead) { +/* data-loss */ + window->commit_lead++; + window->cumulative_losses++; + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Data loss due to pulled trailing edge, fragment count %" PRIu32 "."),window->fragment_count); + return 1; + } + return 0; +} + +unsigned +pgm_rxw_remove_trail ( + pgm_rxw_t* const window + ) +{ + pgm_debug ("remove_trail (window:%p)", (const void*)window); + return _pgm_rxw_remove_trail (window); +} + +/* read contiguous APDU-grouped sequences from the incoming window. + * + * side effects: + * + * 1) increments statics for window messages and bytes read. + * + * returns count of bytes read. + */ + +static inline +ssize_t +_pgm_rxw_incoming_read ( + pgm_rxw_t* const restrict window, + struct pgm_msgv_t** restrict pmsg, /* message array, updated as messages appended */ + unsigned pmsglen /* number of items in pmsg */ + ) +{ + const struct pgm_msgv_t* msg_end; + struct pgm_sk_buff_t* skb; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != pmsg); + pgm_assert_cmpuint (pmsglen, >, 0); + pgm_assert (!_pgm_rxw_incoming_is_empty (window)); + + pgm_debug ("_pgm_rxw_incoming_read (window:%p pmsg:%p pmsglen:%u)", + (void*)window, (void*)pmsg, pmsglen); + + msg_end = *pmsg + pmsglen - 1; + ssize_t bytes_read = 0; + size_t data_read = 0; + + do { + skb = _pgm_rxw_peek (window, window->commit_lead); + pgm_assert (NULL != skb); + if (_pgm_rxw_is_apdu_complete (window, + skb->pgm_opt_fragment ? ntohl (skb->of_apdu_first_sqn) : skb->sequence)) + { + bytes_read += _pgm_rxw_incoming_read_apdu (window, pmsg); + data_read ++; + } + else break; + } while (*pmsg <= msg_end && !_pgm_rxw_incoming_is_empty (window)); + + window->bytes_delivered += bytes_read; + window->msgs_delivered += data_read; + return data_read > 0 ? bytes_read : -1; +} + +/* returns TRUE if transmission group is lost. + * + * checking is lightly limited to bounds. + */ + +static inline +bool +_pgm_rxw_is_tg_sqn_lost ( + pgm_rxw_t* const window, + const uint32_t tg_sqn /* transmission group sequence */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert_cmpuint (_pgm_rxw_pkt_sqn (window, tg_sqn), ==, 0); + + if (pgm_rxw_is_empty (window)) + return TRUE; + + if (pgm_uint32_lt (tg_sqn, window->trail)) + return TRUE; + + return FALSE; +} + +/* reconstruct missing sequences in a transmission group using embedded parity data. + */ + +static +void +_pgm_rxw_reconstruct ( + pgm_rxw_t* const window, + const uint32_t tg_sqn /* transmission group sequence */ + ) +{ + struct pgm_sk_buff_t* skb; + pgm_rxw_state_t* state; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (1 == window->is_fec_available); + pgm_assert_cmpuint (_pgm_rxw_pkt_sqn (window, tg_sqn), ==, 0); + + skb = _pgm_rxw_peek (window, tg_sqn); + pgm_assert (NULL != skb); + + const bool is_var_pktlen = skb->pgm_header->pgm_options & PGM_OPT_VAR_PKTLEN; + const bool is_op_encoded = skb->pgm_header->pgm_options & PGM_OPT_PRESENT; + const uint16_t parity_length = ntohs (skb->pgm_header->pgm_tsdu_length); + struct pgm_sk_buff_t* tg_skbs[ window->rs.n ]; + pgm_gf8_t* tg_data[ window->rs.n ]; + pgm_gf8_t* tg_opts[ window->rs.n ]; + uint8_t offsets[ window->rs.k ]; + uint8_t rs_h = 0; + + for (uint32_t i = tg_sqn, j = 0; i != (tg_sqn + window->rs.k); i++, j++) + { + skb = _pgm_rxw_peek (window, i); + pgm_assert (NULL != skb); + state = (pgm_rxw_state_t*)&skb->cb; + switch (state->pkt_state) { + case PGM_PKT_STATE_HAVE_DATA: + tg_skbs[ j ] = skb; + tg_data[ j ] = skb->data; + tg_opts[ j ] = (pgm_gf8_t*)skb->pgm_opt_fragment; + offsets[ j ] = j; + break; + + case PGM_PKT_STATE_HAVE_PARITY: + tg_skbs[ window->rs.k + rs_h ] = skb; + tg_data[ window->rs.k + rs_h ] = skb->data; + tg_opts[ window->rs.k + rs_h ] = (pgm_gf8_t*)skb->pgm_opt_fragment; + offsets[ j ] = window->rs.k + rs_h; + ++rs_h; +/* fall through and alloc new skb for reconstructed data */ + case PGM_PKT_STATE_BACK_OFF: + case PGM_PKT_STATE_WAIT_NCF: + case PGM_PKT_STATE_WAIT_DATA: + case PGM_PKT_STATE_LOST_DATA: + skb = pgm_alloc_skb (window->max_tpdu); + pgm_skb_reserve (skb, sizeof(struct pgm_header) + sizeof(struct pgm_data)); + skb->pgm_header = skb->head; + skb->pgm_data = (void*)( skb->pgm_header + 1 ); + if (is_op_encoded) { + const uint16_t opt_total_length = sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment); + pgm_skb_reserve (skb, opt_total_length); + skb->pgm_opt_fragment = (void*)( skb->pgm_data + 1 ); + pgm_skb_put (skb, parity_length); + memset (skb->pgm_opt_fragment, 0, opt_total_length + parity_length); + } else { + pgm_skb_put (skb, parity_length); + memset (skb->data, 0, parity_length); + } + tg_skbs[ j ] = skb; + tg_data[ j ] = skb->data; + tg_opts[ j ] = (void*)skb->pgm_opt_fragment; + break; + + default: pgm_assert_not_reached(); break; + } + + if (!skb->zero_padded) { + memset (skb->tail, 0, parity_length - skb->len); + skb->zero_padded = 1; + } + + } + +/* reconstruct payload */ + pgm_rs_decode_parity_appended (&window->rs, + tg_data, + offsets, + parity_length); + +/* reconstruct opt_fragment option */ + if (is_op_encoded) + pgm_rs_decode_parity_appended (&window->rs, + tg_opts, + offsets, + sizeof(struct pgm_opt_fragment)); + +/* swap parity skbs with reconstructed skbs */ + for (uint_fast8_t i = 0; i < window->rs.k; i++) + { + if (offsets[i] < window->rs.k) + continue; + + struct pgm_sk_buff_t* repair_skb = tg_skbs[i]; + + if (is_var_pktlen) + { + const uint16_t pktlen = *(uint16_t*)( (char*)repair_skb->tail - sizeof(uint16_t)); + if (pktlen > parity_length) { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Invalid encoded variable packet length in reconstructed packet, dropping entire transmission group.")); + pgm_free_skb (repair_skb); + for (uint_fast8_t j = i; j < window->rs.k; j++) + { + if (offsets[j] < window->rs.k) + continue; + pgm_rxw_lost (window, tg_skbs[offsets[j]]->sequence); + } + break; + } + const uint16_t padding = parity_length - pktlen; + repair_skb->len -= padding; + repair_skb->tail = (char*)repair_skb->tail - padding; + } + +#ifdef PGM_DISABLE_ASSERT + _pgm_rxw_insert (window, repair_skb); +#else + pgm_assert_cmpint (_pgm_rxw_insert (window, repair_skb), ==, PGM_RXW_INSERTED); +#endif + } +} + +/* check every TPDU in an APDU and verify that the data has arrived + * and is available to commit to the application. + * + * if APDU sits in a transmission group that can be reconstructed use parity + * data then the entire group will be decoded and any missing data packets + * replaced by the recovery calculation. + * + * packets with single fragment fragment headers must be normalised as regular + * packets before calling. + * + * APDUs exceeding PGM_MAX_FRAGMENTS or PGM_MAX_APDU length will be discarded. + * + * returns FALSE if APDU is incomplete or longer than max_len sequences. + */ + +static +bool +_pgm_rxw_is_apdu_complete ( + pgm_rxw_t* const window, + const uint32_t first_sequence + ) +{ + struct pgm_sk_buff_t* skb; + pgm_rxw_state_t* state; + +/* pre-conditions */ + pgm_assert (NULL != window); + + pgm_debug ("_pgm_rxw_is_apdu_complete (window:%p first-sequence:%" PRIu32 ")", + (const void*)window, first_sequence); + + skb = _pgm_rxw_peek (window, first_sequence); + if (PGM_UNLIKELY(NULL == skb)) { + return FALSE; + } + + const size_t apdu_size = skb->pgm_opt_fragment ? ntohl (skb->of_apdu_len) : skb->len; + const uint32_t tg_sqn = _pgm_rxw_tg_sqn (window, first_sequence); + uint32_t sequence = first_sequence; + unsigned contiguous_tpdus = 0; + size_t contiguous_size = 0; + bool check_parity = FALSE; + + pgm_assert_cmpuint (apdu_size, >=, skb->len); + +/* protocol sanity check: maximum length */ + if (PGM_UNLIKELY(apdu_size > PGM_MAX_APDU)) { + pgm_rxw_lost (window, first_sequence); + return FALSE; + } + + do { + state = (pgm_rxw_state_t*)&skb->cb; + + if (!check_parity && + PGM_PKT_STATE_HAVE_DATA != state->pkt_state) + { + if (window->is_fec_available && + !_pgm_rxw_is_tg_sqn_lost (window, tg_sqn) ) + { + check_parity = TRUE; +/* pre-seed committed sequence count */ + if (pgm_uint32_lte (tg_sqn, window->commit_lead)) + contiguous_tpdus += window->commit_lead - tg_sqn; + } + else + return FALSE; + } + + if (check_parity) + { + if (PGM_PKT_STATE_HAVE_DATA == state->pkt_state || + PGM_PKT_STATE_HAVE_PARITY == state->pkt_state) + ++contiguous_tpdus; + +/* have sufficient been received for reconstruction */ + if (contiguous_tpdus >= window->tg_size) { + _pgm_rxw_reconstruct (window, tg_sqn); + return _pgm_rxw_is_apdu_complete (window, first_sequence); + } + } + else + { +/* single packet APDU, already complete */ + if (PGM_PKT_STATE_HAVE_DATA == state->pkt_state && + !skb->pgm_opt_fragment) + return TRUE; + +/* protocol sanity check: matching first sequence reference */ + if (PGM_UNLIKELY(ntohl (skb->of_apdu_first_sqn) != first_sequence)) { + pgm_rxw_lost (window, first_sequence); + return FALSE; + } + +/* protocol sanity check: matching apdu length */ + if (PGM_UNLIKELY(ntohl (skb->of_apdu_len) != apdu_size)) { + pgm_rxw_lost (window, first_sequence); + return FALSE; + } + +/* protocol sanity check: maximum number of fragments per apdu */ + if (PGM_UNLIKELY(++contiguous_tpdus > PGM_MAX_FRAGMENTS)) { + pgm_rxw_lost (window, first_sequence); + return FALSE; + } + + contiguous_size += skb->len; + if (apdu_size == contiguous_size) + return TRUE; + else if (PGM_UNLIKELY(apdu_size < contiguous_size)) { + pgm_rxw_lost (window, first_sequence); + return FALSE; + } + } + + skb = _pgm_rxw_peek (window, ++sequence); + } while (skb); + +/* pending */ + return FALSE; +} + +/* read one APDU consisting of one or more TPDUs. target array is guaranteed + * to be big enough to store complete APDU. + */ + +static inline +ssize_t +_pgm_rxw_incoming_read_apdu ( + pgm_rxw_t* const restrict window, + struct pgm_msgv_t** restrict pmsg /* message array, updated as messages appended */ + ) +{ + struct pgm_sk_buff_t *skb; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != pmsg); + + pgm_debug ("_pgm_rxw_incoming_read_apdu (window:%p pmsg:%p)", + (const void*)window, (const void*)pmsg); + + skb = _pgm_rxw_peek (window, window->commit_lead); + size_t contiguous_len = 0; + const size_t apdu_len = skb->pgm_opt_fragment ? ntohl (skb->of_apdu_len) : skb->len; + unsigned i = 0; + pgm_assert_cmpuint (apdu_len, >=, skb->len); + (*pmsg)->msgv_len = 0; + do { + _pgm_rxw_state (window, skb, PGM_PKT_STATE_COMMIT_DATA); + (*pmsg)->msgv_skb[i++] = skb; + (*pmsg)->msgv_len++; + contiguous_len += skb->len; + window->commit_lead++; + if (apdu_len == contiguous_len) + break; + skb = _pgm_rxw_peek (window, window->commit_lead); + } while (apdu_len > contiguous_len); + + (*pmsg)++; + +/* post-conditions */ + pgm_assert (!_pgm_rxw_commit_is_empty (window)); + +return contiguous_len; +} + +/* returns transmission group sequence (TG_SQN) from sequence (SQN). + */ + +static inline +uint32_t +_pgm_rxw_tg_sqn ( + pgm_rxw_t* const window, + const uint32_t sequence + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + + const uint32_t tg_sqn_mask = 0xffffffff << window->tg_sqn_shift; + return sequence & tg_sqn_mask; +} + +/* returns packet number (PKT_SQN) from sequence (SQN). + */ + +static inline +uint32_t +_pgm_rxw_pkt_sqn ( + pgm_rxw_t* const window, + const uint32_t sequence + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + + const uint32_t tg_sqn_mask = 0xffffffff << window->tg_sqn_shift; + return sequence & ~tg_sqn_mask; +} + +/* returns TRUE when the sequence is the first of a transmission group. + */ + +static inline +bool +_pgm_rxw_is_first_of_tg_sqn ( + pgm_rxw_t* const window, + const uint32_t sequence + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + + return _pgm_rxw_pkt_sqn (window, sequence) == 0; +} + +/* returns TRUE when the sequence is the last of a transmission group + */ + +static inline +bool +_pgm_rxw_is_last_of_tg_sqn ( + pgm_rxw_t* const window, + const uint32_t sequence + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + + return _pgm_rxw_pkt_sqn (window, sequence) == window->tg_size - 1; +} + +/* set PGM skbuff to new FSM state. + */ + +static +void +_pgm_rxw_state ( + pgm_rxw_t* const restrict window, + struct pgm_sk_buff_t* const restrict skb, + const int new_pkt_state + ) +{ + pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != skb); + +/* remove current state */ + if (PGM_PKT_STATE_ERROR != state->pkt_state) + _pgm_rxw_unlink (window, skb); + + switch (new_pkt_state) { + case PGM_PKT_STATE_BACK_OFF: + pgm_queue_push_head_link (&window->nak_backoff_queue, (pgm_list_t*)skb); + break; + + case PGM_PKT_STATE_WAIT_NCF: + pgm_queue_push_head_link (&window->wait_ncf_queue, (pgm_list_t*)skb); + break; + + case PGM_PKT_STATE_WAIT_DATA: + pgm_queue_push_head_link (&window->wait_data_queue, (pgm_list_t*)skb); + break; + + case PGM_PKT_STATE_HAVE_DATA: + window->fragment_count++; + pgm_assert_cmpuint (window->fragment_count, <=, pgm_rxw_length (window)); + break; + + case PGM_PKT_STATE_HAVE_PARITY: + window->parity_count++; + pgm_assert_cmpuint (window->parity_count, <=, pgm_rxw_length (window)); + break; + + case PGM_PKT_STATE_COMMIT_DATA: + window->committed_count++; + pgm_assert_cmpuint (window->committed_count, <=, pgm_rxw_length (window)); + break; + + case PGM_PKT_STATE_LOST_DATA: + window->lost_count++; + window->cumulative_losses++; + window->has_event = 1; + pgm_assert_cmpuint (window->lost_count, <=, pgm_rxw_length (window)); + break; + + case PGM_PKT_STATE_ERROR: + break; + + default: pgm_assert_not_reached(); break; + } + + state->pkt_state = new_pkt_state; +} + +void +pgm_rxw_state ( + pgm_rxw_t* const restrict window, + struct pgm_sk_buff_t* const restrict skb, + const int new_pkt_state + ) +{ + pgm_debug ("state (window:%p skb:%p new_pkt_state:%s)", + (const void*)window, (const void*)skb, pgm_pkt_state_string (new_pkt_state)); + _pgm_rxw_state (window, skb, new_pkt_state); +} + +/* remove current state from sequence. + */ + +static +void +_pgm_rxw_unlink ( + pgm_rxw_t* const restrict window, + struct pgm_sk_buff_t* const restrict skb + ) +{ + pgm_queue_t* queue; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != skb); + + pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; + + switch (state->pkt_state) { + case PGM_PKT_STATE_BACK_OFF: + pgm_assert (!pgm_queue_is_empty (&window->nak_backoff_queue)); + queue = &window->nak_backoff_queue; + goto unlink_queue; + + case PGM_PKT_STATE_WAIT_NCF: + pgm_assert (!pgm_queue_is_empty (&window->wait_ncf_queue)); + queue = &window->wait_ncf_queue; + goto unlink_queue; + + case PGM_PKT_STATE_WAIT_DATA: + pgm_assert (!pgm_queue_is_empty (&window->wait_data_queue)); + queue = &window->wait_data_queue; +unlink_queue: + pgm_queue_unlink (queue, (pgm_list_t*)skb); + break; + + case PGM_PKT_STATE_HAVE_DATA: + pgm_assert_cmpuint (window->fragment_count, >, 0); + window->fragment_count--; + break; + + case PGM_PKT_STATE_HAVE_PARITY: + pgm_assert_cmpuint (window->parity_count, >, 0); + window->parity_count--; + break; + + case PGM_PKT_STATE_COMMIT_DATA: + pgm_assert_cmpuint (window->committed_count, >, 0); + window->committed_count--; + break; + + case PGM_PKT_STATE_LOST_DATA: + pgm_assert_cmpuint (window->lost_count, >, 0); + window->lost_count--; + break; + + case PGM_PKT_STATE_ERROR: + break; + + default: pgm_assert_not_reached(); break; + } + + state->pkt_state = PGM_PKT_STATE_ERROR; + pgm_assert (((pgm_list_t*)skb)->next == NULL); + pgm_assert (((pgm_list_t*)skb)->prev == NULL); +} + +/* returns the pointer at the given index of the window. + */ + +struct pgm_sk_buff_t* +pgm_rxw_peek ( + pgm_rxw_t* const window, + const uint32_t sequence + ) +{ + pgm_debug ("peek (window:%p sequence:%" PRIu32 ")", (void*)window, sequence); + return _pgm_rxw_peek (window, sequence); +} + +/* mark an existing sequence lost due to failed recovery. + */ + +void +pgm_rxw_lost ( + pgm_rxw_t* const window, + const uint32_t sequence + ) +{ + struct pgm_sk_buff_t* skb; + pgm_rxw_state_t* state; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (!pgm_rxw_is_empty (window)); + + pgm_debug ("lost (window:%p sequence:%" PRIu32 ")", + (const void*)window, sequence); + + skb = _pgm_rxw_peek (window, sequence); + pgm_assert (NULL != skb); + + state = (pgm_rxw_state_t*)&skb->cb; + + if (PGM_UNLIKELY(!(state->pkt_state == PGM_PKT_STATE_BACK_OFF || + state->pkt_state == PGM_PKT_STATE_WAIT_NCF || + state->pkt_state == PGM_PKT_STATE_WAIT_DATA || + state->pkt_state == PGM_PKT_STATE_HAVE_DATA || /* fragments */ + state->pkt_state == PGM_PKT_STATE_HAVE_PARITY))) + { + pgm_fatal (_("Unexpected state %s(%u)"), pgm_pkt_state_string (state->pkt_state), state->pkt_state); + pgm_assert_not_reached(); + } + + _pgm_rxw_state (window, skb, PGM_PKT_STATE_LOST_DATA); +} + +/* received a uni/multicast ncf, search for a matching nak & tag or extend window if + * beyond lead + * + * returns: + * PGM_RXW_BOUNDS - sequence is outside of window, or window is undefined. + * PGM_RXW_UPDATED - receiver state updated, waiting for data. + * PGM_RXW_DUPLICATE - data already exists at sequence. + * PGM_RXW_APPENDED - lead is extended with state set waiting for data. + */ + +int +pgm_rxw_confirm ( + pgm_rxw_t* const window, + const uint32_t sequence, + const pgm_time_t now, + const pgm_time_t nak_rdata_expiry, /* pre-calculated expiry times */ + const pgm_time_t nak_rb_expiry + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + + pgm_debug ("confirm (window:%p sequence:%" PRIu32 " nak_rdata_expiry:%" PGM_TIME_FORMAT " nak_rb_expiry:%" PGM_TIME_FORMAT ")", + (void*)window, sequence, nak_rdata_expiry, nak_rb_expiry); + +/* NCFs do not define the transmit window */ + if (PGM_UNLIKELY(!window->is_defined)) + return PGM_RXW_BOUNDS; + +/* sequence already committed */ + if (pgm_uint32_lt (sequence, window->commit_lead)) { + if (pgm_uint32_gte (sequence, window->trail)) + return PGM_RXW_DUPLICATE; + else + return PGM_RXW_BOUNDS; + } + + if (pgm_uint32_lte (sequence, window->lead)) + return _pgm_rxw_recovery_update (window, sequence, nak_rdata_expiry); + + if (sequence == window->lead) + return _pgm_rxw_recovery_append (window, now, nak_rdata_expiry); + else { + _pgm_rxw_add_placeholder_range (window, sequence, now, nak_rb_expiry); + return _pgm_rxw_recovery_append (window, now, nak_rdata_expiry); + } +} + +/* update an incoming sequence with state transition to WAIT-DATA. + * + * returns: + * PGM_RXW_UPDATED - receiver state updated, waiting for data. + * PGM_RXW_DUPLICATE - data already exists at sequence. + */ + +static inline +int +_pgm_rxw_recovery_update ( + pgm_rxw_t* const window, + const uint32_t sequence, + const pgm_time_t nak_rdata_expiry /* pre-calculated expiry times */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + +/* fetch skb from window and bump expiration times */ + struct pgm_sk_buff_t* skb = _pgm_rxw_peek (window, sequence); + pgm_assert (NULL != skb); + pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; + switch (state->pkt_state) { + case PGM_PKT_STATE_BACK_OFF: + case PGM_PKT_STATE_WAIT_NCF: + pgm_rxw_state (window, skb, PGM_PKT_STATE_WAIT_DATA); + +/* fall through */ + case PGM_PKT_STATE_WAIT_DATA: + state->timer_expiry = nak_rdata_expiry; + return PGM_RXW_UPDATED; + + case PGM_PKT_STATE_HAVE_DATA: + case PGM_PKT_STATE_HAVE_PARITY: + case PGM_PKT_STATE_COMMIT_DATA: + case PGM_PKT_STATE_LOST_DATA: + break; + + default: pgm_assert_not_reached(); break; + } + + return PGM_RXW_DUPLICATE; +} + +/* append an skb to the incoming window with WAIT-DATA state. + * + * returns: + * PGM_RXW_APPENDED - lead is extended with state set waiting for data. + * PGM_RXW_BOUNDS - constrained by commit window + */ + +static inline +int +_pgm_rxw_recovery_append ( + pgm_rxw_t* const window, + const pgm_time_t now, + const pgm_time_t nak_rdata_expiry /* pre-calculated expiry times */ + ) +{ + struct pgm_sk_buff_t* skb; + +/* pre-conditions */ + pgm_assert (NULL != window); + + if (pgm_rxw_is_full (window)) { + if (_pgm_rxw_commit_is_empty (window)) { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Receive window full on confirmed sequence.")); + _pgm_rxw_remove_trail (window); + } else { + return PGM_RXW_BOUNDS; /* constrained by commit window */ + } + } + +/* advance leading edge */ + window->lead++; + +/* add loss to bitmap */ + window->bitmap <<= 1; + +/* update the Exponential Moving Average (EMA) data loss with loss: + * s_t = α × x_{t-1} + (1 - α) × s_{t-1} + * x_{t-1} = 1 + * ∴ s_t = α + (1 - α) × s_{t-1} + */ + window->data_loss = window->ack_c_p + pgm_fp16mul (pgm_fp16 (1) - window->ack_c_p, window->data_loss); + + skb = pgm_alloc_skb (window->max_tpdu); + pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; + skb->tstamp = now; + skb->sequence = window->lead; + state->timer_expiry = nak_rdata_expiry; + + const uint_fast32_t index_ = pgm_rxw_lead (window) % pgm_rxw_max_length (window); + window->pdata[index_] = skb; + _pgm_rxw_state (window, skb, PGM_PKT_STATE_WAIT_DATA); + + return PGM_RXW_APPENDED; +} + +/* dumps window state to stdout + */ + +void +pgm_rxw_dump ( + const pgm_rxw_t* const window + ) +{ + pgm_info ("window = {" + "tsi = {gsi = {identifier = %i.%i.%i.%i.%i.%i}, sport = %" PRIu16 "}, " + "nak_backoff_queue = {head = %p, tail = %p, length = %u}, " + "wait_ncf_queue = {head = %p, tail = %p, length = %u}, " + "wait_data_queue = {head = %p, tail = %p, length = %u}, " + "lost_count = %" PRIu32 ", " + "fragment_count = %" PRIu32 ", " + "parity_count = %" PRIu32 ", " + "committed_count = %" PRIu32 ", " + "max_tpdu = %" PRIu16 ", " + "tg_size = %" PRIu32 ", " + "tg_sqn_shift = %u, " + "lead = %" PRIu32 ", " + "trail = %" PRIu32 ", " + "rxw_trail = %" PRIu32 ", " + "rxw_trail_init = %" PRIu32 ", " + "commit_lead = %" PRIu32 ", " + "is_constrained = %u, " + "is_defined = %u, " + "has_event = %u, " + "is_fec_available = %u, " + "min_fill_time = %" PRIu32 ", " + "max_fill_time = %" PRIu32 ", " + "min_nak_transmit_count = %" PRIu32 ", " + "max_nak_transmit_count = %" PRIu32 ", " + "cumulative_losses = %" PRIu32 ", " + "bytes_delivered = %" PRIu32 ", " + "msgs_delivered = %" PRIu32 ", " + "size = %zu, " + "alloc = %" PRIu32 ", " + "pdata = []" + "}", + window->tsi->gsi.identifier[0], + window->tsi->gsi.identifier[1], + window->tsi->gsi.identifier[2], + window->tsi->gsi.identifier[3], + window->tsi->gsi.identifier[4], + window->tsi->gsi.identifier[5], + ntohs (window->tsi->sport), + (void*)window->nak_backoff_queue.head, + (void*)window->nak_backoff_queue.tail, + window->nak_backoff_queue.length, + (void*)window->wait_ncf_queue.head, + (void*)window->wait_ncf_queue.tail, + window->wait_ncf_queue.length, + (void*)window->wait_data_queue.head, + (void*)window->wait_data_queue.tail, + window->wait_data_queue.length, + window->lost_count, + window->fragment_count, + window->parity_count, + window->committed_count, + window->max_tpdu, + window->tg_size, + window->tg_sqn_shift, + window->lead, + window->trail, + window->rxw_trail, + window->rxw_trail_init, + window->commit_lead, + window->is_constrained, + window->is_defined, + window->has_event, + window->is_fec_available, + window->min_fill_time, + window->max_fill_time, + window->min_nak_transmit_count, + window->max_nak_transmit_count, + window->cumulative_losses, + window->bytes_delivered, + window->msgs_delivered, + window->size, + window->alloc + ); +} + +/* state string helper + */ + +const char* +pgm_pkt_state_string ( + const int pkt_state + ) +{ + const char* c; + + switch (pkt_state) { + case PGM_PKT_STATE_BACK_OFF: c = "PGM_PKT_STATE_BACK_OFF"; break; + case PGM_PKT_STATE_WAIT_NCF: c = "PGM_PKT_STATE_WAIT_NCF"; break; + case PGM_PKT_STATE_WAIT_DATA: c = "PGM_PKT_STATE_WAIT_DATA"; break; + case PGM_PKT_STATE_HAVE_DATA: c = "PGM_PKT_STATE_HAVE_DATA"; break; + case PGM_PKT_STATE_HAVE_PARITY: c = "PGM_PKT_STATE_HAVE_PARITY"; break; + case PGM_PKT_STATE_COMMIT_DATA: c = "PGM_PKT_STATE_COMMIT_DATA"; break; + case PGM_PKT_STATE_LOST_DATA: c = "PGM_PKT_STATE_LOST_DATA"; break; + case PGM_PKT_STATE_ERROR: c = "PGM_PKT_STATE_ERROR"; break; + default: c = "(unknown)"; break; + } + + return c; +} + +const char* +pgm_rxw_returns_string ( + const int rxw_returns + ) +{ + const char* c; + + switch (rxw_returns) { + case PGM_RXW_OK: c = "PGM_RXW_OK"; break; + case PGM_RXW_INSERTED: c = "PGM_RXW_INSERTED"; break; + case PGM_RXW_APPENDED: c = "PGM_RXW_APPENDED"; break; + case PGM_RXW_UPDATED: c = "PGM_RXW_UPDATED"; break; + case PGM_RXW_MISSING: c = "PGM_RXW_MISSING"; break; + case PGM_RXW_DUPLICATE: c = "PGM_RXW_DUPLICATE"; break; + case PGM_RXW_MALFORMED: c = "PGM_RXW_MALFORMED"; break; + case PGM_RXW_BOUNDS: c = "PGM_RXW_BOUNDS"; break; + case PGM_RXW_SLOW_CONSUMER: c = "PGM_RXW_SLOW_CONSUMER"; break; + case PGM_RXW_UNKNOWN: c = "PGM_RXW_UNKNOWN"; break; + default: c = "(unknown)"; break; + } + + return c; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/rxw_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/rxw_unittest.c new file mode 100644 index 0000000..635c854 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/rxw_unittest.c @@ -0,0 +1,1844 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for receive window. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + + +/* mock global */ + + +#define pgm_histogram_add mock_pgm_histogram_add +#define pgm_time_now mock_pgm_time_now +#define pgm_rs_create mock_pgm_rs_create +#define pgm_rs_destroy mock_pgm_rs_destroy +#define pgm_rs_decode_parity_appended mock_pgm_rs_decode_parity_appended +#define pgm_histogram_init mock_pgm_histogram_init + +#define RXW_DEBUG +#include "rxw.c" + +#ifdef PGM_DISABLE_ASSERT +# error "PGM_DISABLE_ASSERT set" +#endif + +static pgm_time_t mock_pgm_time_now = 0x1; + + +/* mock functions for external references */ + +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return 0; +} + +/** reed-solomon module */ +void +mock_pgm_rs_create ( + pgm_rs_t* rs, + uint8_t n, + uint8_t k + ) +{ +} + +void +mock_pgm_rs_destroy ( + pgm_rs_t* rs + ) +{ +} + +void +mock_pgm_rs_decode_parity_appended ( + pgm_rs_t* rs, + pgm_gf8_t** block, + const uint8_t* offsets, + uint16_t len + ) +{ +// null +} + +void +mock_pgm_histogram_init ( + pgm_histogram_t* histogram + ) +{ +} + +void +mock_pgm_histogram_add ( + pgm_histogram_t* histogram, + int value + ) +{ +} + + +/* generate valid skb, data pointer pointing to PGM payload + */ +static +struct pgm_sk_buff_t* +generate_valid_skb (void) +{ + const pgm_tsi_t tsi = { { 200, 202, 203, 204, 205, 206 }, 2000 }; + const guint16 tsdu_length = 1000; + const guint16 header_length = sizeof(struct pgm_header) + sizeof(struct pgm_data); + struct pgm_sk_buff_t* skb = pgm_alloc_skb (1500); + memcpy (&skb->tsi, &tsi, sizeof(tsi)); +/* fake but valid socket and timestamp */ + skb->sock = (pgm_sock_t*)0x1; + skb->tstamp = pgm_time_now; +/* header */ + pgm_skb_reserve (skb, header_length); + memset (skb->head, 0, header_length); + skb->pgm_header = (struct pgm_header*)skb->head; + skb->pgm_data = (struct pgm_data*)(skb->pgm_header + 1); + skb->pgm_header->pgm_type = PGM_ODATA; + skb->pgm_header->pgm_tsdu_length = g_htons (tsdu_length); +/* DATA */ + pgm_skb_put (skb, tsdu_length); + return skb; +} + +/* target: + * pgm_rxw_t* + * pgm_rxw_create ( + * const pgm_tsi_t* tsi, + * const uint16_t tpdu_size, + * const unsigned sqns, + * const unsigned secs, + * const ssize_t max_rte, + * const uint32_t ack_c_p + * ) + */ + +/* vanilla sequence count window */ +START_TEST (test_create_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + fail_if (NULL == pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p), "create failed"); +} +END_TEST + +/* vanilla time based window */ +START_TEST (test_create_pass_002) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + fail_if (NULL == pgm_rxw_create (&tsi, 1500, 0, 60, 800000, ack_c_p), "create failed"); +} +END_TEST + +/* jumbo frame */ +START_TEST (test_create_pass_003) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + fail_if (NULL == pgm_rxw_create (&tsi, 9000, 0, 60, 800000, ack_c_p), "create failed"); +} +END_TEST + +/* max frame */ +START_TEST (test_create_pass_004) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + fail_if (NULL == pgm_rxw_create (&tsi, UINT16_MAX, 0, 60, 800000, ack_c_p), "create failed"); +} +END_TEST + +/* invalid tsi pointer */ +START_TEST (test_create_fail_001) +{ + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (NULL, 1500, 100, 0, 0, ack_c_p); + fail ("reached"); +} +END_TEST + +/* invalid tpdu size */ +START_TEST (test_create_fail_002) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + fail_if (NULL == pgm_rxw_create (&tsi, 0, 100, 0, 0, ack_c_p), "create failed"); +} +END_TEST + +START_TEST (test_create_fail_003) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 0, 0, 60, 800000, ack_c_p); + fail ("reached"); +} +END_TEST + +/* no specified sequence count or time value */ +START_TEST (test_create_fail_004) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 0, 0, 0, 800000, ack_c_p); + fail ("reached"); +} +END_TEST + +/* no specified rate */ +START_TEST (test_create_fail_005) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 0, 0, 60, 0, ack_c_p); + fail ("reached"); +} +END_TEST + +/* all invalid */ +START_TEST (test_create_fail_006) +{ + pgm_rxw_t* window = pgm_rxw_create (NULL, 0, 0, 0, 0, 0); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_rxw_destroy ( + * pgm_rxw_t* const window + * ) + */ + +START_TEST (test_destroy_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_destroy_fail_001) +{ + pgm_rxw_destroy (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * int + * pgm_rxw_add ( + * pgm_rxw_t* const window, + * struct pgm_sk_buff_t* const skb, + * const pgm_time_t now, + * const pgm_time_t nak_rb_expiry + * ) + * failures raise assert errors and stop process execution. + */ + +START_TEST (test_add_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + pgm_rxw_destroy (window); +} +END_TEST + +/* missing + inserted */ +START_TEST (test_add_pass_002) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); +/* #1 */ + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); +/* #2 with jump */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (2); + fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not missing"); +/* #3 to fill in gap */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (1); + fail_unless (PGM_RXW_INSERTED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not inserted"); + pgm_rxw_destroy (window); +} +END_TEST + +/* duplicate + append */ +START_TEST (test_add_pass_003) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); +/* #1 */ + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); +/* #2 repeat sequence */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + fail_unless (PGM_RXW_DUPLICATE == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not duplicate"); +/* #3 append */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (1); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + pgm_rxw_destroy (window); +} +END_TEST + +/* malformed: tpdu too long */ +START_TEST (test_add_pass_004) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_header->pgm_tsdu_length = g_htons (65535); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_MALFORMED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not malformed"); +} +END_TEST + +/* bounds + append */ +START_TEST (test_add_pass_005) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); +/* #1 */ + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + skb->pgm_data->data_trail = g_htonl (-10); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); +/* #2 jump backwards */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (-1); + skb->pgm_data->data_trail = g_htonl (-10); + fail_unless (PGM_RXW_BOUNDS == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not bounds"); +/* #3 append */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (1); + skb->pgm_data->data_trail = g_htonl (-10); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); +/* #4 jump forward */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (100 + (UINT32_MAX / 2)); + skb->pgm_data->data_trail = g_htonl (UINT32_MAX / 2); + fail_unless (PGM_RXW_BOUNDS == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not bounds"); +/* #5 append */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (2); + skb->pgm_data->data_trail = g_htonl (-10); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + pgm_rxw_destroy (window); +} +END_TEST + +/* null skb */ +START_TEST (test_add_fail_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + int retval = pgm_rxw_add (window, NULL, now, nak_rb_expiry); + fail ("reached"); +} +END_TEST + +/* null window */ +START_TEST (test_add_fail_002) +{ + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + int retval = pgm_rxw_add (NULL, skb, now, nak_rb_expiry); + fail ("reached"); +} +END_TEST + +/* null skb content */ +START_TEST (test_add_fail_003) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + char buffer[1500]; + memset (buffer, 0, sizeof(buffer)); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + int retval = pgm_rxw_add (window, (struct pgm_sk_buff_t*)buffer, now, nak_rb_expiry); + fail ("reached"); +} +END_TEST + +/* 0 nak_rb_expiry */ +START_TEST (test_add_fail_004) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + int retval = pgm_rxw_add (window, skb, now, 0); + fail ("reached"); +} +END_TEST + +/* target: + * struct pgm_sk_buff_t* + * pgm_rxw_peek ( + * pgm_rxw_t* const window, + * const uint32_t sequence + * ) + */ + +START_TEST (test_peek_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + fail_unless (NULL == pgm_rxw_peek (window, 0)); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (skb == pgm_rxw_peek (window, 0), "peek failed"); + fail_unless (NULL == pgm_rxw_peek (window, 1), "peek failed"); + fail_unless (NULL == pgm_rxw_peek (window, -1), "peek failed"); + pgm_rxw_destroy (window); +} +END_TEST + +/* null window */ +START_TEST (test_peek_fail_001) +{ + struct pgm_sk_buff_t* skb = pgm_rxw_peek (NULL, 0); + fail ("reached"); +} +END_TEST + +/** inline function tests **/ +/* pgm_rxw_max_length () + */ +START_TEST (test_max_length_pass_001) +{ + const guint window_length = 100; + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, window_length, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + fail_unless (window_length == pgm_rxw_max_length (window), "max_length failed"); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_max_length_fail_001) +{ + const unsigned len = pgm_rxw_max_length (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_rxw_length () + */ +START_TEST (test_length_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + fail_unless (0 == pgm_rxw_length (window), "length failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (1 == pgm_rxw_length (window), "length failed"); +/* #2 */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (1); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (2 == pgm_rxw_length (window), "length failed"); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_length_fail_001) +{ + const uint32_t answer = pgm_rxw_length (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_rxw_size () + */ +START_TEST (test_size_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + fail_unless (0 == pgm_rxw_size (window), "size failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (1000 == pgm_rxw_size (window), "size failed"); +/* #2 */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (1); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (2000 == pgm_rxw_size (window), "size failed"); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_size_fail_001) +{ + const size_t answer = pgm_rxw_size (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_rxw_is_empty + */ +START_TEST (test_is_empty_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + fail_unless (pgm_rxw_is_empty (window), "is_empty failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_if (pgm_rxw_is_empty (window), "is_empty failed"); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_is_empty_fail_001) +{ + const bool answer = pgm_rxw_is_empty (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_rxw_is_full + */ +START_TEST (test_is_full_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 1, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + fail_if (pgm_rxw_is_full (window), "is_full failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (pgm_rxw_is_full (window), "is_full failed"); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_is_full_fail_001) +{ + const bool answer = pgm_rxw_is_full (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_rxw_lead + */ +START_TEST (test_lead_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + guint32 lead = pgm_rxw_lead (window); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (lead + 1 == pgm_rxw_lead (window), "lead failed"); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_lead_fail_001) +{ + const uint32_t answer = pgm_rxw_lead (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_rxw_next_lead + */ +START_TEST (test_next_lead_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + guint32 next_lead = pgm_rxw_next_lead (window); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (next_lead == pgm_rxw_lead (window), "lead failed"); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_next_lead_fail_001) +{ + const uint32_t answer = pgm_rxw_next_lead (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * ssize_t + * pgm_rxw_readv ( + * pgm_rxw_t* const window, + * struct pgm_msgv_t** pmsg, + * const unsigned msg_len + * ) + */ + +START_TEST (test_readv_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_msgv_t msgv[2], *pmsg; +/* #1 empty */ + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); +/* #2 single TPDU-APDU */ + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + pmsg = msgv; + fail_unless (1000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); +/* #3,4 two APDUs */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (1); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (2); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + pmsg = msgv; + fail_unless (2000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); +/* #5,6 skip and repair APDU */ + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (4); + fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not missing"); + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (3); + fail_unless (PGM_RXW_INSERTED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not inserted"); + pmsg = msgv; + fail_unless (2000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + pgm_rxw_destroy (window); +} +END_TEST + +/* zero-length */ +START_TEST (test_readv_pass_002) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_msgv_t msgv[2], *pmsg; + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + pgm_rxw_destroy (window); +} +END_TEST + +/* full window */ +START_TEST (test_readv_pass_003) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_msgv_t msgv[1], *pmsg; + struct pgm_sk_buff_t* skb; + for (unsigned i = 0; i < 100; i++) + { + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless ((1 + i) == pgm_rxw_length (window), "length failed"); + } + fail_unless (pgm_rxw_is_full (window), "is_full failed"); + fail_unless (_pgm_rxw_commit_is_empty (window), "commit_is_empty failed"); + for (unsigned i = 0; i < 100; i++) + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + fail_unless ((1 + i) == _pgm_rxw_commit_length (window), "commit_length failed"); + } + fail_unless (pgm_rxw_length (window) == _pgm_rxw_commit_length (window), "commit_length failed"); + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + pgm_rxw_destroy (window); +} +END_TEST + +/* full + 1 window */ +START_TEST (test_readv_pass_004) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_msgv_t msgv[1], *pmsg; + struct pgm_sk_buff_t* skb; + for (unsigned i = 0; i < 101; i++) + { + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (MIN(100, 1 + i) == pgm_rxw_length (window), "length failed"); + } + fail_unless (pgm_rxw_is_full (window), "is_full failed"); + fail_unless (_pgm_rxw_commit_is_empty (window), "commit_is_empty failed"); + for (unsigned i = 0; i < 100; i++) + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + fail_unless ((1 + i) == _pgm_rxw_commit_length (window), "commit_length failed"); + } + fail_unless (pgm_rxw_length (window) == _pgm_rxw_commit_length (window), "commit_length failed"); + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + pgm_rxw_destroy (window); +} +END_TEST + +/* full - 2 lost last in window */ +START_TEST (test_readv_pass_005) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_msgv_t msgv[1], *pmsg; + struct pgm_sk_buff_t* skb; + for (unsigned i = 0; i < 98; i++) + { + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless ((1 + i) == pgm_rxw_length (window), "length failed"); + } + fail_if (pgm_rxw_is_full (window), "is_full failed"); + fail_unless (_pgm_rxw_commit_is_empty (window), "commit_is_empty failed"); + { + unsigned i = 99; + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not missing"); + fail_unless ((1 + i) == pgm_rxw_length (window), "length failed"); + } + fail_unless (pgm_rxw_is_full (window)); + fail_unless (_pgm_rxw_commit_is_empty (window)); + for (unsigned i = 0; i < 98; i++) + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + fail_unless ((1 + i) == _pgm_rxw_commit_length (window), "commit_length failed"); + } + fail_unless (pgm_rxw_length (window) == (2 + _pgm_rxw_commit_length (window)), "commit_length failed"); +/* read end-of-window */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + } + pgm_rxw_destroy (window); +} +END_TEST + +/* add full window, readv 1 skb, add 1 more */ +START_TEST (test_readv_pass_006) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_msgv_t msgv[1], *pmsg; + struct pgm_sk_buff_t* skb; + for (unsigned i = 0; i < 100; i++) + { + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (MIN(100, 1 + i) == pgm_rxw_length (window), "length failed"); + } + fail_unless (pgm_rxw_is_full (window)); + fail_unless (_pgm_rxw_commit_is_empty (window)); +/* read one skb */ + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + fail_unless (1 == _pgm_rxw_commit_length (window), "commit_length failed"); + } +/* add one more new skb */ + { + unsigned i = 100; + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_BOUNDS == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not bounds"); + fail_unless (MIN(100, 1 + i) == pgm_rxw_length (window), "length failed"); + } +/* read off 99 more skbs */ + for (unsigned i = 0; i < 99; i++) + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + fail_unless ((2 + i) == _pgm_rxw_commit_length (window), "commit_length failed"); + } +/* read end-of-window */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + } + pgm_rxw_destroy (window); +} +END_TEST + +/* NULL window */ +START_TEST (test_readv_fail_001) +{ + struct pgm_msgv_t msgv[1], *pmsg = msgv; + gssize len = pgm_rxw_readv (NULL, &pmsg, G_N_ELEMENTS(msgv)); + fail ("reached"); +} +END_TEST + +/* NULL pmsg */ +START_TEST (test_readv_fail_002) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + struct pgm_msgv_t msgv[1], *pmsg = msgv; + gssize len = pgm_rxw_readv (window, NULL, G_N_ELEMENTS(msgv)); + fail ("reached"); +} +END_TEST + +/* 0 msg-len */ +START_TEST (test_readv_fail_003) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + struct pgm_msgv_t msgv[1], *pmsg = msgv; + gssize len = pgm_rxw_readv (window, &pmsg, 0); + fail ("reached"); +} +END_TEST + +/* target: + * + * void + * pgm_rxw_remove_commit ( + * pgm_rxw_t* const window + * ) + */ + +/* full - 2 lost last in window */ +START_TEST (test_remove_commit_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_msgv_t msgv[1], *pmsg; + struct pgm_sk_buff_t* skb; + for (unsigned i = 0; i < 98; i++) + { + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless ((1 + i) == pgm_rxw_length (window), "length failed"); + } + fail_if (pgm_rxw_is_full (window)); + fail_unless (_pgm_rxw_commit_is_empty (window)); +/* #98 is missing */ + { + unsigned i = 99; + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless ((1 + i) == pgm_rxw_length (window), "length failed"); + } + fail_unless (pgm_rxw_is_full (window), "is_full failed"); + fail_unless (_pgm_rxw_commit_is_empty (window), "commit_is_empty"); +/* now mark #98 lost */ + pgm_rxw_lost (window, 98); + for (unsigned i = 0; i < 98; i++) + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + fail_unless ((1 + i) == _pgm_rxw_commit_length (window), "commit_length failed"); + } + fail_unless (100 == pgm_rxw_length (window), "length failed"); + fail_unless ( 98 == _pgm_rxw_commit_length (window), "commit_length failed"); +/* read end-of-window */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + } + fail_unless (100 == pgm_rxw_length (window), "length failed"); + fail_unless ( 98 == _pgm_rxw_commit_length (window), "commit_length failed"); + pgm_rxw_remove_commit (window); +/* read lost skb #98 */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + } + pgm_rxw_remove_commit (window); +/* read valid skb #99 */ + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + } +/* read end-of-window */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + } + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_remove_commit_fail_001) +{ + pgm_rxw_remove_commit (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * unsigned + * pgm_rxw_remove_trail ( + * pgm_rxw_t* const window + * ) + */ + +START_TEST (test_remove_trail_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_msgv_t msgv[2], *pmsg; + fail_unless (0 == pgm_rxw_remove_trail (window), "remove_trail failed"); +/* #1,2 two APDUs */ + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (1); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry)); + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (2); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry)); + fail_unless (1 == pgm_rxw_remove_trail (window), "remove_trail failed"); + fail_unless (1 == pgm_rxw_length (window), "length failed"); + fail_unless (1000 == pgm_rxw_size (window), "size failed"); + pmsg = msgv; + fail_unless (1000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + fail_unless (0 == pgm_rxw_remove_trail (window), "remove_trail failed"); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_remove_trail_fail_001) +{ + guint count = pgm_rxw_remove_trail (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * unsigned + * pgm_rxw_update ( + * pgm_rxw_t* const window, + * const uint32_t txw_trail, + * const uint32_t txw_lead, + * const pgm_time_t now, + * const pgm_time_t nak_rb_expiry + * ) + */ + +START_TEST (test_update_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (0 == pgm_rxw_update (window, 100, 99, now, nak_rb_expiry), "update failed"); +/* dupe */ + fail_unless (0 == pgm_rxw_update (window, 100, 99, now, nak_rb_expiry), "update failed"); +/* #1 at 100 */ + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (100); + fail_unless (PGM_RXW_BOUNDS == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not bounds"); +/* #2 at 101 */ + skb->pgm_data->data_sqn = g_htonl (101); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + struct pgm_msgv_t msgv[1], *pmsg = msgv; + fail_unless (1000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); +/* #3 at 102 */ + fail_unless (1 == pgm_rxw_update (window, 102, 99, now, nak_rb_expiry), "update failed"); + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (102); + fail_unless (PGM_RXW_INSERTED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not inserted"); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_update_fail_001) +{ + guint count = pgm_rxw_update (NULL, 0, 0, 0, 0); + fail ("reached"); +} +END_TEST + +/* target: + * int + * pgm_rxw_confirm ( + * pgm_rxw_t* const window, + * const uint32_t sequence, + * const pgm_time_t now, + * const pgm_time_t nak_rdata_expiry, + * const pgm_time_t nak_rb_expiry + * ) + */ + +START_TEST (test_confirm_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + const pgm_time_t now = 1; + const pgm_time_t nak_rdata_expiry = 2; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_BOUNDS == pgm_rxw_confirm (window, 0, now, nak_rdata_expiry, nak_rb_expiry), "confirm not bounds"); +/* #1 at 100 */ + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (100); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (1 == pgm_rxw_length (window), "length failed"); + fail_unless (PGM_RXW_BOUNDS == pgm_rxw_confirm (window, 99, now, nak_rdata_expiry, nak_rb_expiry), "confirm not bounds"); + fail_unless (PGM_RXW_DUPLICATE == pgm_rxw_confirm (window, 100, now, nak_rdata_expiry, nak_rb_expiry), "confirm not duplicate"); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_confirm (window, 101, now, nak_rdata_expiry, nak_rb_expiry), "confirm not appended"); + fail_unless (2 == pgm_rxw_length (window)); + fail_unless (PGM_RXW_UPDATED == pgm_rxw_confirm (window, 101, now, nak_rdata_expiry, nak_rb_expiry), "confirm not updated"); +/* #2 at 101 */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (101); + fail_unless (PGM_RXW_INSERTED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not inserted"); + struct pgm_msgv_t msgv[2], *pmsg = msgv; + fail_unless (2000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + pgm_rxw_destroy (window); +} +END_TEST + +/* constrained confirm */ +START_TEST (test_confirm_pass_002) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_msgv_t msgv[1], *pmsg; + struct pgm_sk_buff_t* skb; + for (unsigned i = 0; i < 100; i++) + { + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (MIN(100, 1 + i) == pgm_rxw_length (window), "length failed"); + } + fail_unless (pgm_rxw_is_full (window), "is_full failed"); + fail_unless (_pgm_rxw_commit_is_empty (window), "is_empty failed"); +/* read one skb */ + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + fail_unless (1 == _pgm_rxw_commit_length (window), "commit_length failed"); + } +/* confirm next sequence */ + const pgm_time_t now = 1; + const pgm_time_t nak_rdata_expiry = 2; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_BOUNDS == pgm_rxw_confirm (window, 100, now, nak_rdata_expiry, nak_rb_expiry), "confirm not bounds"); +/* read off 99 more skbs */ + for (unsigned i = 0; i < 99; i++) + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + fail_unless ((2 + i) == _pgm_rxw_commit_length (window), "commit_length failed"); + } +/* read end-of-window */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + } + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_confirm_fail_001) +{ + int retval = pgm_rxw_confirm (NULL, 0, 0, 0, 0); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_rxw_lost ( + * pgm_rxw_t* const window, + * const uint32_t sequence + * ) + */ + +START_TEST (test_lost_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + const pgm_time_t now = 1; + const pgm_time_t nak_rdata_expiry = 2; + const pgm_time_t nak_rb_expiry = 2; +/* #1 at 100 */ + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (100); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (1 == pgm_rxw_length (window), "length failed"); + fail_unless (1000 == pgm_rxw_size (window), "size failed"); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_confirm (window, 101, now, nak_rdata_expiry, nak_rb_expiry), "confirm not appended"); + fail_unless (2 == pgm_rxw_length (window), "length failed"); + fail_unless (1000 == pgm_rxw_size (window), "size failed"); + pgm_rxw_lost (window, 101); + fail_unless (2 == pgm_rxw_length (window), "length failed"); + fail_unless (1000 == pgm_rxw_size (window), "size failed"); +/* #2 at 101 */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (101); + fail_unless (PGM_RXW_INSERTED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not inserted"); + fail_unless (2 == pgm_rxw_length (window), "length failed"); + fail_unless (2000 == pgm_rxw_size (window), "size failed"); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_lost_fail_001) +{ + pgm_rxw_lost (NULL, 0); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_rxw_state ( + * pgm_rxw_t* const window, + * struct pgm_sk_buff_t* skb, + * int new_state + * ) + */ + +START_TEST (test_state_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + const pgm_time_t now = 1; + const pgm_time_t nak_rdata_expiry = 2; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (0 == pgm_rxw_update (window, 100, 99, now, nak_rb_expiry), "update failed"); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_confirm (window, 101, now, nak_rdata_expiry, nak_rb_expiry), "confirm not appended"); + struct pgm_sk_buff_t* skb = pgm_rxw_peek (window, 101); + pgm_rxw_state (window, skb, PGM_PKT_STATE_WAIT_NCF); + pgm_rxw_state (window, skb, PGM_PKT_STATE_WAIT_DATA); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_state_fail_001) +{ + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + pgm_rxw_state (NULL, skb, PGM_PKT_STATE_BACK_OFF); + fail ("reached"); +} +END_TEST + +START_TEST (test_state_fail_002) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + pgm_rxw_state (window, NULL, PGM_PKT_STATE_BACK_OFF); + fail ("reached"); +} +END_TEST + +START_TEST (test_state_fail_003) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + pgm_rxw_state (window, skb, -1); + fail ("reached"); +} +END_TEST + +/* pgm_peer_has_pending + */ + +START_TEST (test_has_pending_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); +/* empty */ + fail_unless (0 == window->has_event, "unexpected event"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rdata_expiry = 2; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); +/* 1 sequence */ + fail_unless (1 == window->has_event, "no event"); + window->has_event = 0; +/* jump */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (2); + fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not missing"); + fail_unless (0 == window->has_event, "unexpected event"); +/* loss */ + pgm_rxw_lost (window, 1); + fail_unless (1 == window->has_event, "no event"); + window->has_event = 0; +/* insert */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (1); + fail_unless (PGM_RXW_INSERTED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not inserted"); + fail_unless (1 == window->has_event, "no event"); + window->has_event = 0; +/* confirm */ + fail_unless (PGM_RXW_APPENDED == pgm_rxw_confirm (window, 3, now, nak_rdata_expiry, nak_rb_expiry), "confirm not appended"); + fail_unless (0 == window->has_event, "unexpected event"); +/* partial read */ + struct pgm_msgv_t msgv[2], *pmsg = msgv; + fail_unless (2000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + fail_unless (0 == window->has_event, "unexpected event"); +/* finish read */ + pmsg = msgv; + fail_unless (1000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + fail_unless (0 == window->has_event, "unexpected event"); + pgm_rxw_destroy (window); +} +END_TEST + +static +Suite* +make_basic_test_suite (void) +{ + Suite* s; + + s = suite_create ("basic receive window API"); + + TCase* tc_create = tcase_create ("create"); + suite_add_tcase (s, tc_create); + tcase_add_test (tc_create, test_create_pass_001); + tcase_add_test (tc_create, test_create_pass_002); + tcase_add_test (tc_create, test_create_pass_003); + tcase_add_test (tc_create, test_create_pass_004); + tcase_add_test_raise_signal (tc_create, test_create_fail_001, SIGABRT); + tcase_add_test_raise_signal (tc_create, test_create_fail_002, SIGABRT); + tcase_add_test_raise_signal (tc_create, test_create_fail_003, SIGABRT); + tcase_add_test_raise_signal (tc_create, test_create_fail_004, SIGABRT); + + TCase* tc_destroy = tcase_create ("destroy"); + suite_add_tcase (s, tc_destroy); + tcase_add_test (tc_destroy, test_destroy_pass_001); + tcase_add_test_raise_signal (tc_destroy, test_destroy_fail_001, SIGABRT); + + TCase* tc_add = tcase_create ("add"); + suite_add_tcase (s, tc_add); + tcase_add_test (tc_add, test_add_pass_001); + tcase_add_test (tc_add, test_add_pass_002); + tcase_add_test (tc_add, test_add_pass_003); + tcase_add_test (tc_add, test_add_pass_004); + tcase_add_test (tc_add, test_add_pass_005); + tcase_add_test_raise_signal (tc_add, test_add_fail_001, SIGABRT); + tcase_add_test_raise_signal (tc_add, test_add_fail_002, SIGABRT); + tcase_add_test_raise_signal (tc_add, test_add_fail_003, SIGABRT); + + TCase* tc_peek = tcase_create ("peek"); + suite_add_tcase (s, tc_peek); + tcase_add_test (tc_peek, test_peek_pass_001); + tcase_add_test_raise_signal (tc_peek, test_peek_fail_001, SIGABRT); + + TCase* tc_max_length = tcase_create ("max-length"); + suite_add_tcase (s, tc_max_length); + tcase_add_test (tc_max_length, test_max_length_pass_001); + tcase_add_test_raise_signal (tc_max_length, test_max_length_fail_001, SIGABRT); + + TCase* tc_length = tcase_create ("length"); + suite_add_tcase (s, tc_length); + tcase_add_test (tc_length, test_length_pass_001); + tcase_add_test_raise_signal (tc_length, test_length_fail_001, SIGABRT); + + TCase* tc_size = tcase_create ("size"); + suite_add_tcase (s, tc_size); + tcase_add_test (tc_size, test_size_pass_001); + tcase_add_test_raise_signal (tc_size, test_size_fail_001, SIGABRT); + + TCase* tc_is_empty = tcase_create ("is-empty"); + suite_add_tcase (s, tc_is_empty); + tcase_add_test (tc_is_empty, test_is_empty_pass_001); + tcase_add_test_raise_signal (tc_is_empty, test_is_empty_fail_001, SIGABRT); + + TCase* tc_is_full = tcase_create ("is-full"); + suite_add_tcase (s, tc_is_full); + tcase_add_test (tc_is_full, test_is_full_pass_001); + tcase_add_test_raise_signal (tc_is_full, test_is_full_fail_001, SIGABRT); + + TCase* tc_lead = tcase_create ("lead"); + suite_add_tcase (s, tc_lead); + tcase_add_test (tc_lead, test_lead_pass_001); + tcase_add_test_raise_signal (tc_lead, test_lead_fail_001, SIGABRT); + + TCase* tc_next_lead = tcase_create ("next-lead"); + suite_add_tcase (s, tc_next_lead); + tcase_add_test (tc_next_lead, test_next_lead_pass_001); + tcase_add_test_raise_signal (tc_next_lead, test_next_lead_fail_001, SIGABRT); + + TCase* tc_readv = tcase_create ("readv"); + suite_add_tcase (s, tc_readv); + tcase_add_test (tc_readv, test_readv_pass_001); + tcase_add_test (tc_readv, test_readv_pass_002); + tcase_add_test (tc_readv, test_readv_pass_003); + tcase_add_test (tc_readv, test_readv_pass_004); + tcase_add_test (tc_readv, test_readv_pass_005); + tcase_add_test (tc_readv, test_readv_pass_006); + tcase_add_test_raise_signal (tc_readv, test_readv_fail_001, SIGABRT); + tcase_add_test_raise_signal (tc_readv, test_readv_fail_002, SIGABRT); + tcase_add_test_raise_signal (tc_readv, test_readv_fail_003, SIGABRT); + + TCase* tc_remove_commit = tcase_create ("remove-commit"); + suite_add_tcase (s, tc_remove_commit); + tcase_add_test (tc_remove_commit, test_remove_commit_pass_001); + tcase_add_test_raise_signal (tc_remove_commit, test_remove_commit_fail_001, SIGABRT); + + TCase* tc_remove_trail = tcase_create ("remove-trail"); + TCase* tc_update = tcase_create ("update"); + suite_add_tcase (s, tc_update); + tcase_add_test (tc_update, test_update_pass_001); + tcase_add_test_raise_signal (tc_update, test_update_fail_001, SIGABRT); + + TCase* tc_confirm = tcase_create ("confirm"); + suite_add_tcase (s, tc_confirm); + tcase_add_test (tc_confirm, test_confirm_pass_001); + tcase_add_test (tc_confirm, test_confirm_pass_002); + tcase_add_test_raise_signal (tc_confirm, test_confirm_fail_001, SIGABRT); + + TCase* tc_lost = tcase_create ("lost"); + suite_add_tcase (s, tc_lost); + tcase_add_test (tc_lost, test_lost_pass_001); + tcase_add_test_raise_signal (tc_lost, test_lost_fail_001, SIGABRT); + + TCase* tc_state = tcase_create ("state"); + suite_add_tcase (s, tc_state); + tcase_add_test (tc_state, test_state_pass_001); + tcase_add_test_raise_signal (tc_state, test_state_fail_001, SIGABRT); + + return s; +} + +/* read through lost packet */ +START_TEST (test_readv_pass_007) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window); + struct pgm_msgv_t msgv[1], *pmsg; + struct pgm_sk_buff_t* skb; +/* add #0 */ + { + unsigned i = 0; + skb = generate_valid_skb (); + fail_if (NULL == skb); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry)); + fail_unless ((1 + i) == pgm_rxw_length (window)); + } +/* add # 2 */ + { + unsigned i = 2; + skb = generate_valid_skb (); + fail_if (NULL == skb); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry)); + fail_unless ((1 + i) == pgm_rxw_length (window)); + } +/* lose #1 */ + { + pgm_rxw_lost (window, 1); + } + fail_unless (_pgm_rxw_commit_is_empty (window)); +/* read #0 */ + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + } +/* end-of-window */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + } + pgm_rxw_remove_commit (window); +/* read lost skb #1 */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + } + pgm_rxw_remove_commit (window); +/* read #2 */ + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + } +/* end-of-window */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + } + pgm_rxw_destroy (window); +} +END_TEST + +/* read through loss extended window */ +START_TEST (test_readv_pass_008) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window); + struct pgm_msgv_t msgv[1], *pmsg; + struct pgm_sk_buff_t* skb; +/* add #0 */ + { + unsigned i = 0; + skb = generate_valid_skb (); + fail_if (NULL == skb); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry)); + fail_unless ((1 + i) == pgm_rxw_length (window)); + } + fail_unless (_pgm_rxw_commit_is_empty (window)); +/* read #0 */ + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + } + pgm_rxw_remove_commit (window); +/* end-of-window */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + } +/* add #100 */ + { + unsigned i = 100; + skb = generate_valid_skb (); + fail_if (NULL == skb); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry)); + } +/* lose #1-99 */ + { + for (unsigned i = 1; i < 100; i++) + pgm_rxw_lost (window, i); + } +/* read #100 */ + { + int i = 0; + int bytes_read; + pmsg = msgv; + do { + bytes_read = pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)); + pgm_rxw_remove_commit (window); + i++; + if (i > 100) break; + } while (-1 == bytes_read); + fail_unless (100 == i); + } +/* end-of-window */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + } + pgm_rxw_destroy (window); +} +END_TEST + +/* read through long data-loss */ +START_TEST (test_readv_pass_009) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window); + struct pgm_msgv_t msgv[1], *pmsg; + struct pgm_sk_buff_t* skb; +/* add #0 */ + { + unsigned i = 0; + skb = generate_valid_skb (); + fail_if (NULL == skb); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry)); + fail_unless ((1 + i) == pgm_rxw_length (window)); + } + fail_unless (_pgm_rxw_commit_is_empty (window)); +/* read #0 */ + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + } + pgm_rxw_remove_commit (window); +/* end-of-window */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + } +/* add #2000 */ + { + unsigned i = 2000; + skb = generate_valid_skb (); + fail_if (NULL == skb); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry)); + } +/* lose #1-1999 */ + { + for (unsigned i = 1901; i < 2000; i++) + pgm_rxw_lost (window, i); + } +/* read #2000 */ + { + int i = 0; + int bytes_read; + pmsg = msgv; + do { + bytes_read = pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)); + pgm_rxw_remove_commit (window); + i++; + if (i > 100) break; + } while (-1 == bytes_read); + fail_unless (100 == i); + } +/* end-of-window */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + } + pgm_rxw_destroy (window); +} +END_TEST + +/* a.k.a. unreliable delivery + */ + +static +Suite* +make_best_effort_test_suite (void) +{ + Suite* s; + + s = suite_create ("Best effort delivery"); + + TCase* tc_readv = tcase_create ("readv"); + suite_add_tcase (s, tc_readv); + tcase_add_test (tc_readv, test_readv_pass_007); + tcase_add_test (tc_readv, test_readv_pass_008); + tcase_add_test (tc_readv, test_readv_pass_009); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_basic_test_suite ()); + srunner_add_suite (sr, make_best_effort_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/signal.c b/3rdparty/openpgm-svn-r1085/pgm/signal.c new file mode 100644 index 0000000..1279a8f --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/signal.c @@ -0,0 +1,176 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Re-entrant safe signal handling. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE +#include +#include /* _GNU_SOURCE for strsignal() */ +#include +#ifndef G_OS_WIN32 +# include +#else +# include +#endif +#include +#include "pgm/signal.h" + + +//#define SIGNAL_DEBUG + + +/* globals */ + +static pgm_sighandler_t signal_list[NSIG]; +static int signal_pipe[2]; +static GIOChannel* signal_io = NULL; + +static void on_signal (int); +static gboolean on_io_signal (GIOChannel*, GIOCondition, gpointer); +static const char* cond_string (GIOCondition); + + +static +void +set_nonblock ( + const int s, + const gboolean v + ) +{ +#ifndef G_OS_WIN32 + int flags = fcntl (s, F_GETFL); + if (!v) flags &= ~O_NONBLOCK; + else flags |= O_NONBLOCK; + fcntl (s, F_SETFL, flags); +#else + u_long mode = v; + ioctlsocket (s, FIONBIO, &mode); +#endif +} + +/* install signal handler and return unix fd to add to event loop + */ + +gboolean +pgm_signal_install ( + int signum, + pgm_sighandler_t handler, + gpointer user_data + ) +{ + g_debug ("pgm_signal_install (signum:%d handler:%p user_data:%p)", + signum, (const void*)handler, user_data); + + if (NULL == signal_io) + { +#ifdef G_OS_UNIX + if (pipe (signal_pipe)) +#else + if (_pipe (signal_pipe, 4096, _O_BINARY | _O_NOINHERIT)) +#endif + return FALSE; + + set_nonblock (signal_pipe[0], TRUE); + set_nonblock (signal_pipe[1], TRUE); +/* add to evm */ + signal_io = g_io_channel_unix_new (signal_pipe[0]); + g_io_add_watch (signal_io, G_IO_IN, on_io_signal, user_data); + } + + signal_list[signum] = handler; + return (SIG_ERR != signal (signum, on_signal)); +} + +/* process signal from operating system + */ + +static +void +on_signal ( + int signum + ) +{ + g_debug ("on_signal (signum:%d)", signum); + if (write (signal_pipe[1], &signum, sizeof(signum)) != sizeof(signum)) + { +#ifndef G_OS_WIN32 + g_warning ("Unix signal %s (%d) lost", strsignal (signum), signum); +#else + g_warning ("Unix signal (%d) lost", signum); +#endif + } +} + +/* process signal from pipe + */ + +static +gboolean +on_io_signal ( + GIOChannel* source, + GIOCondition cond, + gpointer user_data + ) +{ +/* pre-conditions */ + g_assert (NULL != source); + g_assert (G_IO_IN == cond); + + g_debug ("on_io_signal (source:%p cond:%s user_data:%p)", + (gpointer)source, cond_string (cond), user_data); + + int signum; + const gsize bytes_read = read (g_io_channel_unix_get_fd (source), &signum, sizeof(signum)); + + if (sizeof(signum) == bytes_read) + { + signal_list[signum] (signum, user_data); + } + else + { + g_warning ("Lost data in signal pipe, read %" G_GSIZE_FORMAT " byte%s expected %" G_GSIZE_FORMAT ".", + bytes_read, bytes_read > 1 ? "s" : "", sizeof(signum)); + } + + return TRUE; +} + +static +const char* +cond_string ( + GIOCondition cond + ) +{ + const char* c; + + switch (cond) { + case G_IO_IN: c = "G_IO_IN"; break; + case G_IO_OUT: c = "G_IO_OUT"; break; + case G_IO_PRI: c = "G_IO_PRI"; break; + case G_IO_ERR: c = "G_IO_ERR"; break; + case G_IO_HUP: c = "G_IO_HUP"; break; + case G_IO_NVAL: c = "G_IO_NVAL"; break; + default: c = "(unknown)"; break; + } + + return c; +} + + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/signal_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/signal_unittest.c new file mode 100644 index 0000000..4784053 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/signal_unittest.c @@ -0,0 +1,115 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for re-entrant safe signal handling. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include + + +/* mock state */ + +static +void +on_sigusr1 ( + int signum, + gpointer user_data + ) +{ + g_assert (SIGUSR1 == signum); + g_assert (NULL != user_data); + GMainLoop* loop = (GMainLoop*)user_data; + g_debug ("on_sigusr1 (signum:%d)", signum); + g_main_loop_quit (loop); +} + +/* mock functions for external references */ + +#define SIGNAL_DEBUG +#include "signal.c" + + +/* target: + * pgm_sighandler_t + * pgm_signal_install ( + * int signum, + pgm_sighandler_t handler + * ) + */ + +static +gboolean +on_startup ( + gpointer data + ) +{ + g_assert (NULL != data); + const int signum = *(const int*)data; + fail_unless (0 == raise (signum)); + return FALSE; +} + +START_TEST (test_install_pass_001) +{ + const int signum = SIGUSR1; + GMainLoop* loop = g_main_loop_new (NULL, FALSE); + fail_unless (TRUE == pgm_signal_install (signum, on_sigusr1, loop)); + g_timeout_add (0, (GSourceFunc)on_startup, &signum); + g_main_loop_run (loop); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_install = tcase_create ("install"); + suite_add_tcase (s, tc_install); + tcase_add_test (tc_install, test_install_pass_001); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/skbuff.c b/3rdparty/openpgm-svn-r1085/pgm/skbuff.c new file mode 100644 index 0000000..5db6ffc --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/skbuff.c @@ -0,0 +1,115 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM socket buffers + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "pgm/skbuff.h" + + +void +pgm_skb_over_panic ( + const struct pgm_sk_buff_t*const skb, + const uint16_t len + ) +{ + pgm_fatal ("skput:over: %u put:%u", + skb->len, len); + pgm_assert_not_reached(); +} + +void +pgm_skb_under_panic ( + const struct pgm_sk_buff_t*const skb, + const uint16_t len + ) +{ + pgm_fatal ("skput:under: %u put:%u", + skb->len, len); + pgm_assert_not_reached(); +} + +#ifndef SKB_DEBUG +bool +pgm_skb_is_valid ( + PGM_GNUC_UNUSED const struct pgm_sk_buff_t*const skb + ) +{ + return TRUE; +} +#else +bool +pgm_skb_is_valid ( + const struct pgm_sk_buff_t*const skb + ) +{ + pgm_return_val_if_fail (skb, FALSE); +/* link_ */ +/* socket */ + pgm_return_val_if_fail (skb->sock, FALSE); +/* tstamp */ + pgm_return_val_if_fail (skb->tstamp > 0, FALSE); +/* tsi */ +/* sequence can be any value */ +/* cb can be any value */ +/* len can be any value */ +/* zero_padded can be any value */ +/* gpointers */ + pgm_return_val_if_fail (skb->head, FALSE); + pgm_return_val_if_fail ((const char*)skb->head > (const char*)&skb->users, FALSE); + pgm_return_val_if_fail (skb->data, FALSE); + pgm_return_val_if_fail ((const char*)skb->data >= (const char*)skb->head, FALSE); + pgm_return_val_if_fail (skb->tail, FALSE); + pgm_return_val_if_fail ((const char*)skb->tail >= (const char*)skb->data, FALSE); + pgm_return_val_if_fail (skb->len == (char*)skb->tail - (const char*)skb->data, FALSE); + pgm_return_val_if_fail (skb->end, FALSE); + pgm_return_val_if_fail ((const char*)skb->end >= (const char*)skb->tail, FALSE); +/* pgm_header */ + if (skb->pgm_header) { + pgm_return_val_if_fail ((const char*)skb->pgm_header >= (const char*)skb->head, FALSE); + pgm_return_val_if_fail ((const char*)skb->pgm_header + sizeof(struct pgm_header) <= (const char*)skb->tail, FALSE); + pgm_return_val_if_fail (skb->pgm_data, FALSE); + pgm_return_val_if_fail ((const char*)skb->pgm_data >= (const char*)skb->pgm_header + sizeof(struct pgm_header), FALSE); + pgm_return_val_if_fail ((const char*)skb->pgm_data <= (const char*)skb->tail, FALSE); + if (skb->pgm_opt_fragment) { + pgm_return_val_if_fail ((const char*)skb->pgm_opt_fragment > (const char*)skb->pgm_data, FALSE); + pgm_return_val_if_fail ((const char*)skb->pgm_opt_fragment + sizeof(struct pgm_opt_fragment) < (const char*)skb->tail, FALSE); +/* of_apdu_first_sqn can be any value */ +/* of_frag_offset */ + pgm_return_val_if_fail (ntohl (skb->of_frag_offset) < ntohl (skb->of_apdu_len), FALSE); +/* of_apdu_len can be any value */ + } + pgm_return_val_if_fail (PGM_ODATA == skb->pgm_header->pgm_type || PGM_RDATA == skb->pgm_header->pgm_type, FALSE); +/* FEC broken */ + pgm_return_val_if_fail (0 == (skb->pgm_header->pgm_options & PGM_OPT_PARITY), FALSE); + pgm_return_val_if_fail (0 == (skb->pgm_header->pgm_options & PGM_OPT_VAR_PKTLEN), FALSE); + } else { + pgm_return_val_if_fail (NULL == skb->pgm_data, FALSE); + pgm_return_val_if_fail (NULL == skb->pgm_opt_fragment, FALSE); + } +/* truesize */ + pgm_return_val_if_fail (skb->truesize >= sizeof(struct pgm_sk_buff_t*) + skb->len, FALSE); + pgm_return_val_if_fail (skb->truesize == ((const char*)skb->end - (const char*)skb), FALSE); +/* users */ + pgm_return_val_if_fail (pgm_atomic_read32 (&skb->users) > 0, FALSE); + return TRUE; +} +#endif /* SKB_DEBUG */ + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/slist.c b/3rdparty/openpgm-svn-r1085/pgm/slist.c new file mode 100644 index 0000000..9ba68ea --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/slist.c @@ -0,0 +1,166 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable singly-linked list. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + + +//#define SLIST_DEBUG + +pgm_slist_t* +pgm_slist_append ( + pgm_slist_t* restrict list, + void* restrict data + ) +{ + pgm_slist_t* new_list; + pgm_slist_t* last; + + new_list = pgm_new (pgm_slist_t, 1); + new_list->data = data; + new_list->next = NULL; + + if (list) + { + last = pgm_slist_last (list); + last->next = new_list; + return list; + } + else + return new_list; +} + +pgm_slist_t* +pgm_slist_prepend ( + pgm_slist_t* restrict list, + void* restrict data + ) +{ + pgm_slist_t *new_list; + + new_list = pgm_new (pgm_slist_t, 1); + new_list->data = data; + new_list->next = list; + + return new_list; +} + +pgm_slist_t* +pgm_slist_prepend_link ( + pgm_slist_t* restrict list, + pgm_slist_t* restrict link_ + ) +{ + pgm_slist_t *new_list; + + new_list = link_; + new_list->next = list; + + return new_list; +} + +pgm_slist_t* +pgm_slist_remove ( + pgm_slist_t* restrict list, + const void* restrict data + ) +{ + pgm_slist_t *tmp = list, *prev = NULL; + + while (tmp) + { + if (tmp->data == data) + { + if (prev) + prev->next = tmp->next; + else + list = tmp->next; + pgm_free (tmp); + break; + } + prev = tmp; + tmp = prev->next; + } + + return list; +} + +pgm_slist_t* +pgm_slist_remove_first ( + pgm_slist_t* list + ) +{ + pgm_slist_t *tmp; + + if (PGM_LIKELY (NULL != list)) + { + tmp = list->next; + list->data = NULL; + list->next = NULL; + return tmp; + } + else + return NULL; +} + +void +pgm_slist_free ( + pgm_slist_t* list + ) +{ + while (list) + { + pgm_slist_t* current = list; + list = list->next; + pgm_free (current); + } +} + +pgm_slist_t* +pgm_slist_last ( + pgm_slist_t* list + ) +{ + if (PGM_LIKELY (NULL != list)) + { + while (list->next) + list = list->next; + } + + return list; +} + +unsigned +pgm_slist_length ( + pgm_slist_t* list + ) +{ + unsigned length = 0; + + while (list) + { + length++; + list = list->next; + } + + return length; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/snmp.c b/3rdparty/openpgm-svn-r1085/pgm/snmp.c new file mode 100644 index 0000000..5673878 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/snmp.c @@ -0,0 +1,222 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * SNMP agent, single session. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include +#include +#include + +#include "pgm/snmp.h" +#include "impl/pgmMIB.h" + + +/* globals */ + +bool pgm_agentx_subagent = TRUE; +char* pgm_agentx_socket = NULL; +char* pgm_snmp_appname = "PGM"; + +/* locals */ + +#ifndef _WIN32 +static pthread_t snmp_thread; +static void* snmp_routine (void*); +#else +static HANDLE snmp_thread; +static unsigned __stdcall snmp_routine (void*); +#endif +static pgm_notify_t snmp_notify = PGM_NOTIFY_INIT; +static volatile uint32_t snmp_ref_count = 0; + + +/* Calling application needs to redirect SNMP logging before prior to this + * function. + */ + +bool +pgm_snmp_init ( + pgm_error_t** error + ) +{ + if (pgm_atomic_exchange_and_add32 (&snmp_ref_count, 1) > 0) + return TRUE; + + if (pgm_agentx_subagent) + { + pgm_minor (_("Configuring as SNMP AgentX sub-agent.")); + if (pgm_agentx_socket) + { + pgm_minor (_("Using AgentX socket %s."), pgm_agentx_socket); + netsnmp_ds_set_string (NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_X_SOCKET, + pgm_agentx_socket); + } + netsnmp_ds_set_boolean (NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_ROLE, + TRUE); + } + + pgm_minor (_("Initialising SNMP agent.")); + if (0 != init_agent (pgm_snmp_appname)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SNMP, + PGM_ERROR_FAILED, + _("Initialise SNMP agent: see SNMP log for further details.")); + goto err_cleanup; + } + + if (!pgm_mib_init (error)) { + goto err_cleanup; + } + +/* read config and parse mib */ + pgm_minor (_("Initialising SNMP.")); + init_snmp (pgm_snmp_appname); + + if (!pgm_agentx_subagent) + { + pgm_minor (_("Connecting to SNMP master agent.")); + if (0 != init_master_agent ()) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SNMP, + PGM_ERROR_FAILED, + _("Initialise SNMP master agent: see SNMP log for further details.")); + snmp_shutdown (pgm_snmp_appname); + goto err_cleanup; + } + } + +/* create notification channel */ + if (0 != pgm_notify_init (&snmp_notify)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SNMP, + pgm_error_from_errno (errno), + _("Creating SNMP notification channel: %s"), + strerror (errno)); + snmp_shutdown (pgm_snmp_appname); + goto err_cleanup; + } + +/* spawn thread to handle SNMP requests */ +#ifndef _WIN32 + const int status = pthread_create (&snmp_thread, NULL, &snmp_routine, NULL); + if (0 != status) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SNMP, + pgm_error_from_errno (errno), + _("Creating SNMP thread: %s"), + strerror (errno)); + snmp_shutdown (pgm_snmp_appname); + goto err_cleanup; + } +#else + snmp_thread = (HANDLE)_beginthreadex (NULL, 0, &snmp_routine, NULL, 0, NULL); + const int save_errno = errno; + if (0 == snmp_thread) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SNMP, + pgm_error_from_errno (save_errno), + _("Creating SNMP thread: %s"), + strerror (save_errno)); + snmp_shutdown (pgm_snmp_appname); + goto err_cleanup; + } +#endif /* _WIN32 */ + return TRUE; +err_cleanup: + if (pgm_notify_is_valid (&snmp_notify)) { + pgm_notify_destroy (&snmp_notify); + } + pgm_atomic_dec32 (&snmp_ref_count); + return FALSE; +} + +/* Terminate SNMP thread and free resources. + */ + +bool +pgm_snmp_shutdown (void) +{ + pgm_return_val_if_fail (pgm_atomic_read32 (&snmp_ref_count) > 0, FALSE); + + if (pgm_atomic_exchange_and_add32 (&snmp_ref_count, (uint32_t)-1) != 1) + return TRUE; + + pgm_notify_send (&snmp_notify); +#ifndef _WIN32 + pthread_join (snmp_thread, NULL); +#else + CloseHandle (snmp_thread); +#endif + pgm_notify_destroy (&snmp_notify); + snmp_shutdown (pgm_snmp_appname); + return TRUE; +} + +/* Thread routine for processing SNMP requests + */ + +static +#ifndef _WIN32 +void* +#else +unsigned +__stdcall +#endif +snmp_routine ( + PGM_GNUC_UNUSED void* arg + ) +{ + const int notify_fd = pgm_notify_get_fd (&snmp_notify); + + for (;;) + { + int fds = 0, block = 1; + fd_set fdset; + struct timeval timeout; + + FD_ZERO(&fdset); + snmp_select_info (&fds, &fdset, &timeout, &block); + FD_SET(notify_fd, &fdset); + if (notify_fd+1 > fds) + fds = notify_fd+1; + fds = select (fds, &fdset, NULL, NULL, block ? NULL : &timeout); + if (FD_ISSET(notify_fd, &fdset)) + break; + if (fds) + snmp_read (&fdset); + else + snmp_timeout(); + } + +/* cleanup */ +#ifndef _WIN32 + return NULL; +#else + _endthread(); + return 0; +#endif /* WIN32 */ +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/snmp_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/snmp_unittest.c new file mode 100644 index 0000000..9005a82 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/snmp_unittest.c @@ -0,0 +1,184 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for SNMP. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include + +#include "pgm/transport.h" + + +/* mock state */ +static const guint mock_pgm_major_version = 0; +static const guint mock_pgm_minor_version = 0; +static const guint mock_pgm_micro_version = 0; +static GStaticRWLock mock_pgm_transport_list_lock = G_STATIC_RW_LOCK_INIT; +static GSList* mock_pgm_transport_list = NULL; + +static +gboolean +mock_pgm_tsi_equal ( + gconstpointer v1, + gconstpointer v2 + ) +{ + return memcmp (v1, v2, sizeof(struct pgm_tsi_t)) == 0; +} + +static +void +mock_pgm_time_since_epoch ( + pgm_time_t* pgm_time_t_time, + time_t* time_t_time + ) +{ + *time_t_time = pgm_to_secs (*pgm_time_t_time + 0); +} + +static +gboolean +mock_pgm_mib_init ( + GError** error + ) +{ + return TRUE; +} + +/* mock functions for external references */ + +#define pgm_major_version mock_pgm_major_version +#define pgm_minor_version mock_pgm_minor_version +#define pgm_micro_version mock_pgm_micro_version +#define pgm_transport_list_lock mock_pgm_transport_list_lock +#define pgm_transport_list mock_pgm_transport_list +#define pgm_tsi_equal mock_pgm_tsi_equal +#define pgm_time_since_epoch mock_pgm_time_since_epoch +#define pgm_mib_init mock_pgm_mib_init + + +#define SNMP_DEBUG +#include "snmp.c" + + +/* target: + * gboolean + * pgm_snmp_init ( + * GError** error + * ) + */ + +START_TEST (test_init_pass_001) +{ + GError* err = NULL; + fail_unless (TRUE == pgm_snmp_init (&err)); + fail_unless (NULL == err); +} +END_TEST + +/* duplicate servers */ +START_TEST (test_init_fail_001) +{ + GError* err = NULL; + fail_unless (TRUE == pgm_snmp_init (&err)); + fail_unless (FALSE == pgm_snmp_init (&err)); +} +END_TEST + +/* target: + * gboolean + * pgm_snmp_shutdown (void) + */ + +START_TEST (test_shutdown_pass_001) +{ + GError* err = NULL; + fail_unless (TRUE == pgm_snmp_init (&err)); + fail_unless (NULL == err); + fail_unless (TRUE == pgm_snmp_shutdown ()); +} +END_TEST + +/* repeatability + */ +START_TEST (test_shutdown_pass_002) +{ + GError* err = NULL; + fail_unless (TRUE == pgm_snmp_init (&err)); + fail_unless (NULL == err); + fail_unless (TRUE == pgm_snmp_shutdown ()); + fail_unless (TRUE == pgm_snmp_init (&err)); + fail_unless (NULL == err); + fail_unless (TRUE == pgm_snmp_shutdown ()); +} +END_TEST + +/* no running server */ +START_TEST (test_shutdown_fail_001) +{ + fail_unless (FALSE == pgm_snmp_shutdown ()); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_init = tcase_create ("init"); + suite_add_tcase (s, tc_init); + tcase_add_test (tc_init, test_init_pass_001); + tcase_add_test (tc_init, test_init_fail_001); + + TCase* tc_shutdown = tcase_create ("shutdown"); + suite_add_tcase (s, tc_shutdown); + tcase_add_test (tc_shutdown, test_shutdown_pass_001); + tcase_add_test (tc_shutdown, test_shutdown_pass_002); + tcase_add_test (tc_shutdown, test_shutdown_fail_001); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/sockaddr.c b/3rdparty/openpgm-svn-r1085/pgm/sockaddr.c new file mode 100644 index 0000000..9b8dcb9 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/sockaddr.c @@ -0,0 +1,1193 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * struct sockaddr functions independent of in or in6. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#ifndef _WIN32 +# include +# include +#endif +#include + + +/* FreeBSD */ +#ifndef IPV6_ADD_MEMBERSHIP +# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +# define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP +#endif +/* OpenSolaris differences */ +#ifndef MCAST_MSFILTER +# include +#endif +#ifndef SOL_IP +# define SOL_IP IPPROTO_IP +#endif +#ifndef SOL_IPV6 +# define SOL_IPV6 IPPROTO_IPV6 +#endif +#ifndef IP_MAX_MEMBERSHIPS +# define IP_MAX_MEMBERSHIPS 20 +#endif + + +sa_family_t +pgm_sockaddr_family ( + const struct sockaddr* sa + ) +{ + return sa->sa_family; +} + +uint16_t +pgm_sockaddr_port ( + const struct sockaddr* sa + ) +{ + uint16_t sa_port; + switch (sa->sa_family) { + case AF_INET: { + struct sockaddr_in s4; + memcpy (&s4, sa, sizeof(s4)); + sa_port = s4.sin_port; + break; + } + + case AF_INET6: { + struct sockaddr_in6 s6; + memcpy (&s6, sa, sizeof(s6)); + sa_port = s6.sin6_port; + break; + } + + default: + sa_port = 0; + break; + } + return sa_port; +} + +socklen_t +pgm_sockaddr_len ( + const struct sockaddr* sa + ) +{ + socklen_t sa_len; + switch (sa->sa_family) { + case AF_INET: sa_len = sizeof(struct sockaddr_in); break; + case AF_INET6: sa_len = sizeof(struct sockaddr_in6); break; + default: sa_len = 0; break; + } + return sa_len; +} + +socklen_t +pgm_sockaddr_storage_len ( + const struct sockaddr_storage* ss + ) +{ + socklen_t ss_len; + switch (ss->ss_family) { + case AF_INET: ss_len = sizeof(struct sockaddr_in); break; + case AF_INET6: ss_len = sizeof(struct sockaddr_in6); break; + default: ss_len = 0; break; + } + return ss_len; +} + +uint32_t +pgm_sockaddr_scope_id ( + const struct sockaddr* sa + ) +{ + uint32_t scope_id; + if (AF_INET6 == sa->sa_family) { + struct sockaddr_in6 s6; + memcpy (&s6, sa, sizeof(s6)); + scope_id = s6.sin6_scope_id; + } else + scope_id = 0; + return scope_id; +} + +int +pgm_sockaddr_ntop ( + const struct sockaddr* restrict sa, + char* restrict host, + size_t hostlen + ) +{ + return getnameinfo (sa, pgm_sockaddr_len (sa), + host, hostlen, + NULL, 0, + NI_NUMERICHOST); +} + +int +pgm_sockaddr_pton ( + const char* restrict src, + struct sockaddr* restrict dst /* will error on wrong size */ + ) +{ + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, /* not really */ + .ai_protocol = IPPROTO_TCP, /* not really */ + .ai_flags = AI_NUMERICHOST + }, *result = NULL; + const int status = getaddrinfo (src, NULL, &hints, &result); + if (PGM_LIKELY(0 == status)) { + memcpy (dst, result->ai_addr, result->ai_addrlen); + freeaddrinfo (result); + return 1; + } + return 0; +} + +/* returns tri-state value: 1 if sa is multicast, 0 if sa is not multicast, -1 on error + */ + +int +pgm_sockaddr_is_addr_multicast ( + const struct sockaddr* sa + ) +{ + int retval; + + switch (sa->sa_family) { + case AF_INET: { + struct sockaddr_in s4; + memcpy (&s4, sa, sizeof(s4)); + retval = IN_MULTICAST(ntohl( s4.sin_addr.s_addr )); + break; + } + + case AF_INET6: { + struct sockaddr_in6 s6; + memcpy (&s6, sa, sizeof(s6)); + retval = IN6_IS_ADDR_MULTICAST( &s6.sin6_addr ); + break; + } + + default: + retval = -1; + break; + } + return retval; +} + +/* returns 1 if sa is unspecified, 0 if specified. + */ + +int +pgm_sockaddr_is_addr_unspecified ( + const struct sockaddr* sa + ) +{ + int retval; + + switch (sa->sa_family) { + case AF_INET: { + struct sockaddr_in s4; + memcpy (&s4, sa, sizeof(s4)); + retval = (INADDR_ANY == s4.sin_addr.s_addr); + break; + } + + case AF_INET6: { + struct sockaddr_in6 s6; + memcpy (&s6, sa, sizeof(s6)); + retval = IN6_IS_ADDR_UNSPECIFIED( &s6.sin6_addr ); + break; + } + + default: + retval = -1; + break; + } + return retval; +} + +int +pgm_sockaddr_cmp ( + const struct sockaddr* restrict sa1, + const struct sockaddr* restrict sa2 + ) +{ + int retval = 0; + + if (sa1->sa_family != sa2->sa_family) + retval = sa1->sa_family < sa2->sa_family ? -1 : 1; + else { + switch (sa1->sa_family) { + case AF_INET: { + struct sockaddr_in sa1_in, sa2_in; + memcpy (&sa1_in, sa1, sizeof(sa1_in)); + memcpy (&sa2_in, sa2, sizeof(sa2_in)); + if (sa1_in.sin_addr.s_addr != sa2_in.sin_addr.s_addr) + retval = sa1_in.sin_addr.s_addr < sa2_in.sin_addr.s_addr ? -1 : 1; + break; + } + +/* IN6_ARE_ADDR_EQUAL(a,b) only returns true or false */ + case AF_INET6: { + struct sockaddr_in6 sa1_in6, sa2_in6; + memcpy (&sa1_in6, sa1, sizeof(sa1_in6)); + memcpy (&sa2_in6, sa2, sizeof(sa2_in6)); + retval = memcmp (&sa1_in6.sin6_addr, &sa2_in6.sin6_addr, sizeof(struct in6_addr)); + if (0 == retval && sa1_in6.sin6_scope_id != sa2_in6.sin6_scope_id) + retval = sa1_in6.sin6_scope_id < sa2_in6.sin6_scope_id ? -1 : 1; + break; + } + + default: + break; + } + } + return retval; +} + +/* IP header included with data. + * + * If no error occurs, pgm_sockaddr_hdrincl returns zero. Otherwise, a value + * of PGM_SOCKET_ERROR is returned, and a specific error code can be retrieved + * by calling pgm_sock_errno(). + */ + +int +pgm_sockaddr_hdrincl ( + const int s, + const sa_family_t sa_family, + const bool v + ) +{ + int retval = PGM_SOCKET_ERROR; + + switch (sa_family) { + case AF_INET: { +#ifndef _WIN32 +/* Solaris:ip(7P) Mentioned but not detailed. + * + * Linux:ip(7) "A boolean integer flag is zero when it is false, otherwise + * true. If enabled, the user supplies an IP header in front of the user + * data." Mentions only send-side, nothing about receive-side. + * Linux:raw(7) "For receiving the IP header is always included in the packet." + * + * FreeBSD,OS X:IP(4) provided by example "int hincl = 1;" + * + * Stevens: "IP_HDRINCL has datatype int." + */ + const int optval = v ? 1 : 0; +#else + const DWORD optval = v ? 1 : 0; +#endif + retval = setsockopt (s, IPPROTO_IP, IP_HDRINCL, (const char*)&optval, sizeof(optval)); + break; + } + + case AF_INET6: /* method only exists on Win32, just ignore */ + retval = 0; + break; + + default: break; + } + return retval; +} + +/* Return destination IP address. + * + * If no error occurs, pgm_sockaddr_pktinfo returns zero. Otherwise, a value + * of PGM_SOCKET_ERROR is returned, and a specific error code can be retrieved + * by calling pgm_sock_errno(). + */ + +int +pgm_sockaddr_pktinfo ( + const int s, + const sa_family_t sa_family, + const bool v + ) +{ + int retval = PGM_SOCKET_ERROR; +#ifndef _WIN32 +/* Solaris:ip(7P) "The following options take in_pktinfo_t as the parameter" + * Completely different, although ip6(7P) is a little better, "The following + * options are boolean switches controlling the reception of ancillary data" + * + * Linux:ip(7) "A boolean integer flag is zero when it is false, otherwise + * true. The argument is a flag that tells the socket whether the IP_PKTINFO + * message should be passed or not." + * Linux:ipv6(7) Not listed, however IPV6_PKTINFO is with "Argument is a pointer + * to a boolean value in an integer." + * + * Absent from FreeBSD & OS X, suggested replacement IP_RECVDSTADDR. + * OS X:IP6(4) "IPV6_PKTINFO int *" + * + * Stevens: "IP_RECVDSTADDR has datatype int." + */ + const int optval = v ? 1 : 0; +#else + const DWORD optval = v ? 1 : 0; +#endif + + switch (sa_family) { + case AF_INET: +#ifdef IP_RECVDSTADDR + retval = setsockopt (s, IPPROTO_IP, IP_RECVDSTADDR, (const char*)&optval, sizeof(optval)); +#else + retval = setsockopt (s, IPPROTO_IP, IP_PKTINFO, (const char*)&optval, sizeof(optval)); +#endif + break; + + case AF_INET6: +#ifdef IPV6_RECVPKTINFO + retval = setsockopt (s, IPPROTO_IPV6, IPV6_RECVPKTINFO, (const char*)&optval, sizeof(optval)); +#else + retval = setsockopt (s, IPPROTO_IPV6, IPV6_PKTINFO, (const char*)&optval, sizeof(optval)); +#endif + break; + + default: break; + } + return retval; +} + +/* Set IP Router Alert option for all outgoing packets. + * + * If no error occurs, pgm_sockaddr_router_alert returns zero. Otherwise, a + * value of PGM_SOCKET_ERROR is returned, and a specific error code can be + * retrieved by calling pgm_sock_errno(). + */ + +int +pgm_sockaddr_router_alert ( + const int s, + const sa_family_t sa_family, + const bool v + ) +{ + int retval = PGM_SOCKET_ERROR; +#ifdef CONFIG_IP_ROUTER_ALERT +/* Linux:ip(7) "A boolean integer flag is zero when it is false, otherwise + * true. Expects an integer flag." + * Linux:ipv6(7) "Argument is a pointer to an integer." + * + * Sent on special queue to rsvpd on Linux and so best avoided. + */ + const int optval = v ? 1 : 0; + + switch (sa_family) { + case AF_INET: + retval = setsockopt (s, IPPROTO_IP, IP_ROUTER_ALERT, (const char*)&optval, sizeof(optval)); + break; + + case AF_INET6: + retval = setsockopt (s, IPPROTO_IPV6, IPV6_ROUTER_ALERT, (const char*)&optval, sizeof(optval)); + break; + + default: break; + } +#else +# if defined(CONFIG_HAVE_IPOPTION) +/* NB: struct ipoption is not very portable and requires a lot of additional headers */ + const struct ipoption router_alert = { + .ipopt_dst = 0, + .ipopt_list = { PGM_IPOPT_RA, 0x04, 0x00, 0x00 } + }; + const int optlen = v ? sizeof(router_alert) : 0; +# else +/* manually set the IP option */ + const int ipopt_ra = (PGM_IPOPT_RA << 24) | (0x04 << 16); + const int router_alert = htonl (ipopt_ra); + const int optlen = v ? sizeof(router_alert) : 0; +# endif + + switch (sa_family) { + case AF_INET: +/* Linux:ip(7) "The maximum option size for IPv4 is 40 bytes." + */ + retval = setsockopt (s, IPPROTO_IP, IP_OPTIONS, (const char*)&router_alert, optlen); +retval = 0; + break; + + default: break; + } +#endif + return retval; +} + +/* Type-of-service and precedence. + * + * If no error occurs, pgm_sockaddr_tos returns zero. Otherwise, a value of + * PGM_SOCKET_ERROR is returned, and a specific error code can be retrieved by + * calling pgm_sock_errno(). + */ + +int +pgm_sockaddr_tos ( + const int s, + const sa_family_t sa_family, + const int tos + ) +{ + int retval = PGM_SOCKET_ERROR; + + switch (sa_family) { + case AF_INET: { +#ifndef _WIN32 +/* Solaris:ip(7P) "This option takes an integer argument as its input value." + * + * Linux:ip(7) "TOS is a byte." + * + * FreeBSD,OS X:IP(4) provided by example "int tos = IPTOS_LOWDELAY;" + * + * Stevens: "IP_TOS has datatype int." + */ + const int optval = tos; +#else +/* IP_TOS only works on Win32 with system override: + * http://support.microsoft.com/kb/248611 + * TODO: Implement GQoS (IPv4 only), qWAVE QOS is Vista+ only + */ + const DWORD optval = tos; +#endif + retval = setsockopt (s, IPPROTO_IP, IP_TOS, (const char*)&optval, sizeof(optval)); + break; + } + + case AF_INET6: /* TRAFFIC_CLASS not implemented */ + break; + + default: break; + } + return retval; +} + +/* Join multicast group. + * NB: IPV6_JOIN_GROUP == IPV6_ADD_MEMBERSHIP + * + * If no error occurs, pgm_sockaddr_join_group returns zero. Otherwise, a + * value of PGM_SOCKET_ERROR is returned, and a specific error code can be + * retrieved by calling pgm_sock_errno(). + */ + +int +pgm_sockaddr_join_group ( + const int s, + const sa_family_t sa_family, + const struct group_req* gr + ) +{ + int retval = PGM_SOCKET_ERROR; +#ifdef CONFIG_HAVE_MCAST_JOIN +/* Solaris:ip(7P) "The following options take a struct ip_mreq_source as the + * parameter." Presumably with source field zeroed out. + * Solaris:ip6(7P) "Takes a struct group_req as the parameter." + * Different type for each family, however group_req is protocol-independent. + * + * Stevens: "MCAST_JOIN_GROUP has datatype group_req{}." + * + * RFC3678: Argument type struct group_req + */ + const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6; + retval = setsockopt (s, recv_level, MCAST_JOIN_GROUP, gr, sizeof(struct group_req)); +#else + switch (sa_family) { + case AF_INET: { +/* Solaris:ip(7P) Just mentions "Join a multicast group." + * No further details provided. + * + * Linux:ip(7) "Argument is an ip_mreqn structure. For compatibility, the old + * ip_mreq structure (present since Linux 1.2) is still supported." + * + * FreeBSD,OS X:IP(4) provided by example "struct ip_mreq mreq;" + * + * Windows can optionally abuse imt_interface to be 0.0.0. + * + * Stevens: "IP_ADD_MEMBERSHIP has datatype ip_mreq{}." + * + * RFC3678: Argument type struct ip_mreq + */ +#ifdef CONFIG_HAVE_IP_MREQN + struct ip_mreqn mreqn; + struct sockaddr_in ifaddr; + memset (&mreqn, 0, sizeof(mreqn)); + mreqn.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gr->gr_group)->sin_addr.s_addr; + if (!pgm_if_indextoaddr (gr->gr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL)) + return -1; + mreqn.imr_address.s_addr = ifaddr.sin_addr.s_addr; + mreqn.imr_ifindex = gr->gr_interface; + retval = setsockopt (s, SOL_IP, IP_ADD_MEMBERSHIP, (const char*)&mreqn, sizeof(mreqn)); +#else + struct ip_mreq mreq; + struct sockaddr_in ifaddr; + memset (&mreq, 0, sizeof(mreq)); + mreq.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gr->gr_group)->sin_addr.s_addr; + if (!pgm_if_indextoaddr (gr->gr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL)) + return -1; + mreq.imr_interface.s_addr = ifaddr.sin_addr.s_addr; + retval = setsockopt (s, SOL_IP, IP_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)); +#endif /* !CONFIG_HAVE_IP_MREQN */ + break; + } + + case AF_INET6: { +/* Solaris:ip6(7P) "Takes a struct ipv6_mreq as the parameter;" + * + * Linux:ipv6(7) "Argument is a pointer to a struct ipv6_mreq structure." + * + * OS X:IP6(4) "IPV6_JOIN_GROUP struct ipv6_mreq *" + * + * Stevens: "IPV6_JOIN_GROUP has datatype ipv6_mreq{}." + */ + struct ipv6_mreq mreq6; + memset (&mreq6, 0, sizeof(mreq6)); + mreq6.ipv6mr_multiaddr = ((const struct sockaddr_in6*)&gr->gr_group)->sin6_addr; + mreq6.ipv6mr_interface = gr->gr_interface; + retval = setsockopt (s, SOL_IPV6, IPV6_ADD_MEMBERSHIP, (const char*)&mreq6, sizeof(mreq6)); + break; + } + + default: break; + } +#endif /* CONFIG_HAVE_MCAST_JOIN */ + return retval; +} + +/* leave a joined group + */ + +int +pgm_sockaddr_leave_group ( + const int s, + const sa_family_t sa_family, + const struct group_req* gr + ) +{ + int retval = PGM_SOCKET_ERROR; +#ifdef CONFIG_HAVE_MCAST_JOIN + const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6; + retval = setsockopt (s, recv_level, MCAST_LEAVE_GROUP, gr, sizeof(struct group_req)); +#else + switch (sa_family) { + case AF_INET: { +#ifdef CONFIG_HAVE_IP_MREQN + struct ip_mreqn mreqn; + struct sockaddr_in ifaddr; + memset (&mreqn, 0, sizeof(mreqn)); + mreqn.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gr->gr_group)->sin_addr.s_addr; + if (!pgm_if_indextoaddr (gr->gr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL)) + return -1; + mreqn.imr_address.s_addr = ifaddr.sin_addr.s_addr; + mreqn.imr_ifindex = gr->gr_interface; + retval = setsockopt (s, SOL_IP, IP_DROP_MEMBERSHIP, (const char*)&mreqn, sizeof(mreqn)); +#else + struct ip_mreq mreq; + struct sockaddr_in ifaddr; + memset (&mreq, 0, sizeof(mreq)); + mreq.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gr->gr_group)->sin_addr.s_addr; + if (!pgm_if_indextoaddr (gr->gr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL)) + return -1; + mreq.imr_interface.s_addr = ifaddr.sin_addr.s_addr; + retval = setsockopt (s, SOL_IP, IP_DROP_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)); +#endif /* !CONFIG_HAVE_IP_MREQN */ + break; + } + + case AF_INET6: { + struct ipv6_mreq mreq6; + memset (&mreq6, 0, sizeof(mreq6)); + mreq6.ipv6mr_multiaddr = ((const struct sockaddr_in6*)&gr->gr_group)->sin6_addr; + mreq6.ipv6mr_interface = gr->gr_interface; + retval = setsockopt (s, SOL_IPV6, IPV6_DROP_MEMBERSHIP, (const char*)&mreq6, sizeof(mreq6)); + break; + } + + default: break; + } +#endif /* CONFIG_HAVE_MCAST_JOIN */ + return retval; +} + +/* block either at the NIC or kernel, packets from a particular source + */ + +int +pgm_sockaddr_block_source ( + const int s, + const sa_family_t sa_family, + const struct group_source_req* gsr + ) +{ + int retval = PGM_SOCKET_ERROR; +#ifdef CONFIG_HAVE_MCAST_JOIN + const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6; + retval = setsockopt (s, recv_level, MCAST_BLOCK_SOURCE, gsr, sizeof(struct group_source_req)); +#elif defined(IP_BLOCK_SOURCE) + switch (sa_family) { + case AF_INET: { + struct ip_mreq_source mreqs; + struct sockaddr_in ifaddr; + memset (&mreqs, 0, sizeof(mreqs)); + mreqs.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_group)->sin_addr.s_addr; + mreqs.imr_sourceaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_source)->sin_addr.s_addr; + pgm_if_indextoaddr (gsr->gsr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL); + mreqs.imr_interface.s_addr = ifaddr.sin_addr.s_addr; + retval = setsockopt (s, SOL_IP, IP_BLOCK_SOURCE, (const char*)&mreqs, sizeof(mreqs)); + break; + } + + case AF_INET6: +/* No IPv6 API implemented, MCAST_BLOCK_SOURCE should be available instead. + */ + break; + + default: break; + } +#endif /* CONFIG_HAVE_MCAST_JOIN */ + return retval; +} + +/* unblock a blocked multicast source. + */ + +int +pgm_sockaddr_unblock_source ( + const int s, + const sa_family_t sa_family, + const struct group_source_req* gsr + ) +{ + int retval = PGM_SOCKET_ERROR; +#ifdef CONFIG_HAVE_MCAST_JOIN + const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6; + retval = setsockopt (s, recv_level, MCAST_UNBLOCK_SOURCE, gsr, sizeof(struct group_source_req)); +#elif defined(IP_UNBLOCK_SOURCE) + switch (sa_family) { + case AF_INET: { + struct ip_mreq_source mreqs; + struct sockaddr_in ifaddr; + memset (&mreqs, 0, sizeof(mreqs)); + mreqs.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_group)->sin_addr.s_addr; + mreqs.imr_sourceaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_source)->sin_addr.s_addr; + pgm_if_indextoaddr (gsr->gsr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL); + mreqs.imr_interface.s_addr = ifaddr.sin_addr.s_addr; + retval = setsockopt (s, SOL_IP, IP_UNBLOCK_SOURCE, (const char*)&mreqs, sizeof(mreqs)); + break; + } + + case AF_INET6: +/* No IPv6 API implemented, MCAST_UNBLOCK_SOURCE should be available instead. + */ + break; + + default: break; + } +#endif /* CONFIG_HAVE_MCAST_JOIN */ + return retval; +} + +/* Join source-specific multicast. + * NB: Silently reverts to ASM if SSM not supported. + * + * If no error occurs, pgm_sockaddr_join_source_group returns zero. + * Otherwise, a value of PGM_SOCKET_ERROR is returned, and a specific error + * code can be retrieved by calling pgm_sock_errno(). + */ + +int +pgm_sockaddr_join_source_group ( + const int s, + const sa_family_t sa_family, + const struct group_source_req* gsr + ) +{ + int retval = PGM_SOCKET_ERROR; +#ifdef CONFIG_HAVE_MCAST_JOIN +/* Solaris:ip(7P) "The following options take a struct ip_mreq_source as the + * parameter." + * Solaris:ip6(7P) "Takes a struct group_source_req as the parameter." + * Different type for each family, however group_source_req is protocol- + * independent. + * + * Stevens: "MCAST_JOIN_SOURCE_GROUP has datatype group_source_req{}." + * + * RFC3678: Argument type struct group_source_req + */ + const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6; + retval = setsockopt (s, recv_level, MCAST_JOIN_SOURCE_GROUP, gsr, sizeof(struct group_source_req)); +#elif defined(IP_ADD_SOURCE_MEMBERSHIP) + switch (sa_family) { + case AF_INET: { +/* Solaris:ip(7P) "The following options take a struct ip_mreq as the + * parameter." Incorrect literature wrt RFC. + * + * Linux:ip(7) absent. + * + * OS X:IP(4) absent. + * + * Stevens: "IP_ADD_SOURCE_MEMBERSHIP has datatype ip_mreq_source{}." + * + * RFC3678: Argument type struct ip_mreq_source + */ + struct ip_mreq_source mreqs; + struct sockaddr_in ifaddr; + memset (&mreqs, 0, sizeof(mreqs)); + mreqs.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_group)->sin_addr.s_addr; + mreqs.imr_sourceaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_source)->sin_addr.s_addr; + pgm_if_indextoaddr (gsr->gsr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL); + mreqs.imr_interface.s_addr = ifaddr.sin_addr.s_addr; + retval = setsockopt (s, SOL_IP, IP_ADD_SOURCE_MEMBERSHIP, (const char*)&mreqs, sizeof(mreqs)); + break; + } + + case AF_INET6: +/* No IPv6 API implemented, MCAST_JOIN_SOURCE_GROUP should be available instead. + */ + retval = pgm_sockaddr_join_group (s, sa_family, (const struct group_req*)gsr); + break; + + default: break; + } +#else + retval = pgm_sockaddr_join_group (s, sa_family, (const struct group_req*)gsr); +#endif /* CONFIG_HAVE_MCAST_JOIN */ + return retval; +} + +/* drop a SSM source + */ + +int +pgm_sockaddr_leave_source_group ( + const int s, + const sa_family_t sa_family, + const struct group_source_req* gsr + ) +{ + int retval = PGM_SOCKET_ERROR; +#ifdef CONFIG_HAVE_MCAST_JOIN + const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6; + retval = setsockopt (s, recv_level, MCAST_LEAVE_SOURCE_GROUP, gsr, sizeof(struct group_source_req)); +#elif defined(IP_ADD_SOURCE_MEMBERSHIP) + switch (sa_family) { + case AF_INET: { + struct ip_mreq_source mreqs; + struct sockaddr_in ifaddr; + memset (&mreqs, 0, sizeof(mreqs)); + mreqs.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_group)->sin_addr.s_addr; + mreqs.imr_sourceaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_source)->sin_addr.s_addr; + pgm_if_indextoaddr (gsr->gsr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL); + mreqs.imr_interface.s_addr = ifaddr.sin_addr.s_addr; + retval = setsockopt (s, SOL_IP, IP_DROP_SOURCE_MEMBERSHIP, (const char*)&mreqs, sizeof(mreqs)); + break; + } + + case AF_INET6: +/* No IPv6 API implemented, MCAST_LEAVE_SOURCE_GROUP should be available instead. + */ + retval = pgm_sockaddr_leave_group (s, sa_family, (const struct group_req*)gsr); + break; + + default: break; + } +#else + retval = pgm_sockaddr_leave_group (s, sa_family, (const struct group_req*)gsr); +#endif /* CONFIG_HAVE_MCAST_JOIN */ + return retval; +} + +#if defined(MCAST_MSFILTER) || defined(SIOCSMSFILTER) +/* Batch block and unblock sources. + */ + +int +pgm_sockaddr_msfilter ( + const int s, + const sa_family_t sa_family, + const struct group_filter* gf_list + ) +{ + int retval = PGM_SOCKET_ERROR; +#ifdef MCAST_MSFILTER + const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6; + const socklen_t len = GROUP_FILTER_SIZE(gf_list->gf_numsrc); + retval = setsockopt (s, recv_level, MCAST_MSFILTER, (const char*)gf_list, len); +#elif defined(SIOCSMSFILTER) + retval = ioctl (s, SIOCSMSFILTER, (const char*)gf_list); +#endif + return retval; +} +#endif /* MCAST_MSFILTER || SIOCSMSFILTER */ + +/* Specify outgoing interface. + * + * If no error occurs, pgm_sockaddr_multicast_if returns zero. Otherwise, a + * value of PGM_SOCKET_ERROR is returned, and a specific error code can be + * retrieved by calling pgm_sock_errno(). + */ + +int +pgm_sockaddr_multicast_if ( + int s, + const struct sockaddr* address, + unsigned ifindex + ) +{ + int retval = PGM_SOCKET_ERROR; + + switch (address->sa_family) { + case AF_INET: { +/* Solaris:ip(7P) "This option takes a struct in_addr as an argument, and it + * selects that interface for outgoing IP multicast packets." + * + * Linux:ip(7) "Argument is an ip_mreqn or ip_mreq structure similar to + * IP_ADD_MEMBERSHIP." + * + * OS X:IP(4) provided by example "struct in_addr addr;" + * + * Stevens: "IP_MULTICAST_IF has datatype struct in_addr{}." + */ + struct sockaddr_in s4; + memcpy (&s4, address, sizeof(s4)); + retval = setsockopt (s, IPPROTO_IP, IP_MULTICAST_IF, (const char*)&s4.sin_addr, sizeof(s4.sin_addr)); + break; + } + + case AF_INET6: { +#ifndef _WIN32 +/* Solaris:ip6(7P) "This option takes an integer as an argument; the integer + * is the interface index of the selected interface." + * + * Linux:ipv6(7) "The argument is a pointer to an interface index (see + * netdevice(7)) in an integer." + * + * OS X:IP6(4) "IPV6_MULTICAST_IF u_int *" + * + * Stevens: "IPV6_MULTICAST_IF has datatype u_int." + */ + const unsigned int optval = ifindex; +#else + const DWORD optval = ifindex; +#endif + retval = setsockopt (s, IPPROTO_IPV6, IPV6_MULTICAST_IF, (const char*)&optval, sizeof(optval)); + break; + } + + default: break; + } + return retval; +} + +/* Specify multicast loop, other applications on the same host may receive + * outgoing packets. This does not affect unicast packets such as NAKs. + * + * If no error occurs, pgm_sockaddr_multicast_loop returns zero. Otherwise, a + * value of PGM_SOCKET_ERROR is returned, and a specific error code can be + * retrieved by calling pgm_sock_errno(). + */ + +int +pgm_sockaddr_multicast_loop ( + const int s, + const sa_family_t sa_family, + const bool v + ) +{ + int retval = PGM_SOCKET_ERROR; + + switch (sa_family) { + case AF_INET: { +#ifndef _WIN32 +/* Solaris:ip(7P) "Setting the unsigned character argument to 0 causes the + * opposite behavior, meaning that when multiple zones are present, the + * datagrams are delivered to all zones except the sending zone." + * + * Linux:ip(7) "Sets or reads a boolean integer argument" + * + * OS X:IP(4) provided by example "u_char loop;" + * + * Stevens: "IP_MULTICAST_LOOP has datatype u_char." + */ + const unsigned char optval = v ? 1 : 0; +#else + const DWORD optval = v ? 1 : 0; +#endif + retval = setsockopt (s, IPPROTO_IP, IP_MULTICAST_LOOP, (const char*)&optval, sizeof(optval)); + break; + } + + case AF_INET6: { +#ifndef _WIN32 +/* Solaris:ip(7P) "Setting the unsigned character argument to 0 will cause the opposite behavior." + * + * Linux:ipv6(7) "Argument is a pointer to boolean." + * + * OS X:IP6(7) "IPV6_MULTICAST_LOOP u_int *" + * + * Stevens: "IPV6_MULTICAST_LOOP has datatype u_int." + */ + const unsigned int optval = v ? 1 : 0; +#else + const DWORD optval = v ? 1 : 0; +#endif + retval = setsockopt (s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const char*)&optval, sizeof(optval)); + break; + } + + default: break; + } + return retval; +} + +/* Specify TTL or outgoing hop limit. + * NB: Only affects multicast hops, unicast hop-limit is not changed. + * + * If no error occurs, pgm_sockaddr_multicast_hops returns zero. Otherwise, a + * value of PGM_SOCKET_ERROR is returned, and a specific error code can be + * retrieved by calling pgm_sock_errno(). + */ + +int +pgm_sockaddr_multicast_hops ( + const int s, + const sa_family_t sa_family, + const unsigned hops + ) +{ + int retval = PGM_SOCKET_ERROR; + + switch (sa_family) { + case AF_INET: { +#ifndef _WIN32 +/* Solaris:ip(7P) "This option takes an unsigned character as an argument." + * + * Linux:ip(7) "Argument is an integer." + * + * OS X:IP(4) provided by example for SOCK_DGRAM with IP_TTL: "int ttl = 60;", + * or for SOCK_RAW & SOCK_DGRAM with IP_MULTICAST_TTL: "u_char ttl;" + * + * Stevens: "IP_MULTICAST_TTL has datatype u_char." + */ + const unsigned char optval = hops; +#else + const DWORD optval = hops; +#endif + retval = setsockopt (s, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&optval, sizeof(optval)); + break; + } + + case AF_INET6: { +#ifndef _WIN32 +/* Solaris:ip6(7P) "This option takes an integer as an argument." + * + * Linux:ipv6(7) "Argument is a pointer to an integer." + * + * OS X:IP6(7) "IPV6_MULTICAST_HOPS int *" + * + * Stevens: "IPV6_MULTICAST_HOPS has datatype int." + */ + const int optval = hops; +#else + const DWORD optval = hops; +#endif + retval = setsockopt (s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char*)&optval, sizeof(optval)); + break; + } + + default: break; + } + return retval; +} + +void +pgm_sockaddr_nonblocking ( + const int s, + const bool v + ) +{ +#ifndef _WIN32 + int flags = fcntl (s, F_GETFL); + if (!v) flags &= ~O_NONBLOCK; + else flags |= O_NONBLOCK; + fcntl (s, F_SETFL, flags); +#else + u_long mode = v; + ioctlsocket (s, FIONBIO, &mode); +#endif +} + +/* Note that are sockaddr structure is not passed these functions inherently + * cannot support IPv6 Zone Indices and hence are rather limited for the + * link-local scope. + */ +const char* +pgm_inet_ntop ( + int af, + const void* restrict src, + char* restrict dst, + socklen_t size + ) +{ + pgm_assert (AF_INET == af || AF_INET6 == af); + pgm_assert (NULL != src); + pgm_assert (NULL != dst); + pgm_assert (size > 0); + + switch (af) { + case AF_INET: + { + struct sockaddr_in sin; + memset (&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr = *(const struct in_addr*)src; + getnameinfo ((struct sockaddr*)&sin, sizeof(sin), + dst, size, + NULL, 0, + NI_NUMERICHOST); + return dst; + } + case AF_INET6: + { + struct sockaddr_in6 sin6; + memset (&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = *(const struct in6_addr*)src; + getnameinfo ((struct sockaddr*)&sin6, sizeof(sin6), + dst, size, + NULL, 0, + NI_NUMERICHOST); + return dst; + } + } + + errno = EAFNOSUPPORT; + return NULL; +} + +int +pgm_inet_pton ( + int af, + const char* restrict src, + void* restrict dst + ) +{ + pgm_assert (AF_INET == af || AF_INET6 == af); + pgm_assert (NULL != src); + pgm_assert (NULL != dst); + + struct addrinfo hints = { + .ai_family = af, + .ai_socktype = SOCK_STREAM, /* not really */ + .ai_protocol = IPPROTO_TCP, /* not really */ + .ai_flags = AI_NUMERICHOST + }, *result = NULL; + + const int e = getaddrinfo (src, NULL, &hints, &result); + if (0 != e) { + return 0; /* error */ + } + + pgm_assert (NULL != result->ai_addr); + pgm_assert (0 != result->ai_addrlen); + + switch (result->ai_addr->sa_family) { + case AF_INET: { + struct sockaddr_in s4; + memcpy (&s4, result->ai_addr, sizeof(s4)); + memcpy (dst, &s4.sin_addr.s_addr, sizeof(struct in_addr)); + break; + } + + case AF_INET6: { + struct sockaddr_in6 s6; + memcpy (&s6, result->ai_addr, sizeof(s6)); + memcpy (dst, &s6.sin6_addr, sizeof(struct in6_addr)); + break; + } + + default: + pgm_assert_not_reached(); + break; + } + + freeaddrinfo (result); + return 1; /* success */ +} + +int +pgm_nla_to_sockaddr ( + const void* restrict nla, + struct sockaddr* restrict sa + ) +{ + uint16_t nla_family; + int retval = 0; + + memcpy (&nla_family, nla, sizeof(nla_family)); + sa->sa_family = ntohs (nla_family); + switch (sa->sa_family) { + case AFI_IP: + sa->sa_family = AF_INET; + ((struct sockaddr_in*)sa)->sin_addr.s_addr = ((const struct in_addr*)((const char*)nla + sizeof(uint32_t)))->s_addr; + break; + + case AFI_IP6: + sa->sa_family = AF_INET6; + memcpy (&((struct sockaddr_in6*)sa)->sin6_addr, (const struct in6_addr*)((const char*)nla + sizeof(uint32_t)), sizeof(struct in6_addr)); + break; + + default: + retval = -EINVAL; + break; + } + + return retval; +} + +int +pgm_sockaddr_to_nla ( + const struct sockaddr* restrict sa, + void* restrict nla + ) +{ + int retval = 0; + + *(uint16_t*)nla = sa->sa_family; + *(uint16_t*)((char*)nla + sizeof(uint16_t)) = 0; /* reserved 16bit space */ + switch (sa->sa_family) { + case AF_INET: + *(uint16_t*)nla = htons (AFI_IP); + ((struct in_addr*)((char*)nla + sizeof(uint32_t)))->s_addr = ((const struct sockaddr_in*)sa)->sin_addr.s_addr; + break; + + case AF_INET6: + *(uint16_t*)nla = htons (AFI_IP6); + memcpy ((struct in6_addr*)((char*)nla + sizeof(uint32_t)), &((const struct sockaddr_in6*)sa)->sin6_addr, sizeof(struct in6_addr)); + break; + + default: + retval = -EINVAL; + break; + } + + return retval; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/socket.c b/3rdparty/openpgm-svn-r1085/pgm/socket.c new file mode 100644 index 0000000..1338085 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/socket.c @@ -0,0 +1,2046 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM socket: manage incoming & outgoing sockets with ambient SPMs, + * transmit & receive windows. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#ifdef CONFIG_HAVE_POLL +# include +#endif +#ifdef CONFIG_HAVE_EPOLL +# include +#endif +#include +#include +#include +#include +#include +#include +#include + + +//#define SOCK_DEBUG +//#define SOCK_SPM_DEBUG + + +/* global locals */ +pgm_rwlock_t pgm_sock_list_lock; /* list of all sockets for admin interfaces */ +pgm_slist_t* pgm_sock_list = NULL; + + +static const char* pgm_family_string (const int) PGM_GNUC_CONST; +static const char* pgm_sock_type_string (const int) PGM_GNUC_CONST; +static const char* pgm_protocol_string (const int) PGM_GNUC_CONST; + + +size_t +pgm_pkt_offset ( + bool can_fragment, + sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + static const size_t data_size = sizeof(struct pgm_header) + sizeof(struct pgm_data); + size_t pkt_size = data_size; + if (can_fragment || 0 != pgmcc_family) + pkt_size += sizeof(struct pgm_opt_length) + sizeof(struct pgm_opt_header); + if (can_fragment) + pkt_size += sizeof(struct pgm_opt_fragment); + if (AF_INET == pgmcc_family) + pkt_size += sizeof(struct pgm_opt_pgmcc_data); + else if (AF_INET6 == pgmcc_family) + pkt_size += sizeof(struct pgm_opt6_pgmcc_data); + return pkt_size; +} + +/* destroy a pgm_sock object and contents, if last sock also destroy + * associated event loop + * + * outstanding locks: + * 1) pgm_sock_t::lock + * 2) pgm_sock_t::receiver_mutex + * 3) pgm_sock_t::source_mutex + * 4) pgm_sock_t::txw_spinlock + * 5) pgm_sock_t::timer_mutex + * + * If application calls a function on the sock after destroy() it is a + * programmer error: segv likely to occur on unlock. + * + * on success, returns TRUE, on failure returns FALSE. + */ + +bool +pgm_close ( + pgm_sock_t* sock, + bool flush + ) +{ + pgm_return_val_if_fail (sock != NULL, FALSE); + if (!pgm_rwlock_reader_trylock (&sock->lock)) + pgm_return_val_if_reached (FALSE); + pgm_return_val_if_fail (!sock->is_destroyed, FALSE); + pgm_debug ("pgm_sock_destroy (sock:%p flush:%s)", + (const void*)sock, + flush ? "TRUE":"FALSE"); +/* flag existing calls */ + sock->is_destroyed = TRUE; +/* cancel running blocking operations */ + if (PGM_INVALID_SOCKET != sock->recv_sock) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Closing receive socket.")); + pgm_closesocket (sock->recv_sock); + sock->recv_sock = PGM_INVALID_SOCKET; + } + if (PGM_INVALID_SOCKET != sock->send_sock) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Closing send socket.")); + pgm_closesocket (sock->send_sock); + sock->send_sock = PGM_INVALID_SOCKET; + } + pgm_rwlock_reader_unlock (&sock->lock); + pgm_debug ("blocking on destroy lock ..."); + pgm_rwlock_writer_lock (&sock->lock); + + pgm_debug ("removing sock from inventory."); + pgm_rwlock_writer_lock (&pgm_sock_list_lock); + pgm_sock_list = pgm_slist_remove (pgm_sock_list, sock); + pgm_rwlock_writer_unlock (&pgm_sock_list_lock); + +/* flush source side by sending heartbeat SPMs */ + if (sock->can_send_data && + sock->is_connected && + flush) + { + pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Flushing PGM source with session finish option broadcast SPMs.")); + if (!pgm_send_spm (sock, PGM_OPT_FIN) || + !pgm_send_spm (sock, PGM_OPT_FIN) || + !pgm_send_spm (sock, PGM_OPT_FIN)) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Failed to send flushing SPMs.")); + } + } + + if (sock->peers_hashtable) { + pgm_debug ("destroying peer lookup table."); + pgm_hashtable_destroy (sock->peers_hashtable); + sock->peers_hashtable = NULL; + } + if (sock->peers_list) { + pgm_debug ("destroying peer list."); + do { + pgm_list_t* next = sock->peers_list->next; + pgm_peer_unref ((pgm_peer_t*)sock->peers_list->data); + + sock->peers_list = next; + } while (sock->peers_list); + } + + if (sock->window) { + pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Destroying transmit window.")); + pgm_txw_shutdown (sock->window); + sock->window = NULL; + } + pgm_trace (PGM_LOG_ROLE_RATE_CONTROL,_("Destroying rate control.")); + pgm_rate_destroy (&sock->rate_control); + if (PGM_INVALID_SOCKET != sock->send_with_router_alert_sock) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Closing send with router alert socket.")); + pgm_closesocket (sock->send_with_router_alert_sock); + sock->send_with_router_alert_sock = PGM_INVALID_SOCKET; + } + if (sock->spm_heartbeat_interval) { + pgm_debug ("freeing SPM heartbeat interval data."); + pgm_free (sock->spm_heartbeat_interval); + sock->spm_heartbeat_interval = NULL; + } + if (sock->rx_buffer) { + pgm_debug ("freeing receive buffer."); + pgm_free_skb (sock->rx_buffer); + sock->rx_buffer = NULL; + } + pgm_debug ("destroying notification channels."); + if (sock->can_send_data) { + if (sock->use_pgmcc) { + pgm_notify_destroy (&sock->ack_notify); + } + pgm_notify_destroy (&sock->rdata_notify); + } + pgm_notify_destroy (&sock->pending_notify); + pgm_debug ("freeing sock locks."); + pgm_rwlock_free (&sock->peers_lock); + pgm_spinlock_free (&sock->txw_spinlock); + pgm_mutex_free (&sock->send_mutex); + pgm_mutex_free (&sock->timer_mutex); + pgm_mutex_free (&sock->source_mutex); + pgm_mutex_free (&sock->receiver_mutex); + pgm_rwlock_writer_unlock (&sock->lock); + pgm_rwlock_free (&sock->lock); + pgm_debug ("freeing sock data."); + pgm_free (sock); + pgm_debug ("finished."); + return TRUE; +} + +/* Create a pgm_sock object. Create sockets that require superuser + * priviledges. If interface ports are specified then UDP encapsulation will + * be used instead of raw protocol. + * + * If send == recv only two sockets need to be created iff ip headers are not + * required (IPv6). + * + * All receiver addresses must be the same family. + * interface and multiaddr must be the same family. + * family cannot be AF_UNSPEC! + * + * returns TRUE on success, or FALSE on error and sets error appropriately. + */ + +#if ( AF_INET != PF_INET ) || ( AF_INET6 != PF_INET6 ) +#error AF_INET and PF_INET are different values, the bananas are jumping in their pyjamas! +#endif + +bool +pgm_socket ( + pgm_sock_t** restrict sock, + const sa_family_t family, /* communications domain */ + const int pgm_sock_type, + const int protocol, + pgm_error_t** restrict error + ) +{ + pgm_sock_t* new_sock; + + pgm_return_val_if_fail (NULL != sock, FALSE); + pgm_return_val_if_fail (AF_INET == family || AF_INET6 == family, FALSE); + pgm_return_val_if_fail (SOCK_SEQPACKET == pgm_sock_type, FALSE); + pgm_return_val_if_fail (IPPROTO_UDP == protocol || IPPROTO_PGM == protocol, FALSE); + + pgm_debug ("socket (sock:%p family:%s sock-type:%s protocol:%s error:%p)", + (const void*)sock, pgm_family_string(family), pgm_sock_type_string(pgm_sock_type), pgm_protocol_string(protocol), (const void*)error); + + new_sock = pgm_new0 (pgm_sock_t, 1); + new_sock->family = family; + new_sock->socket_type = pgm_sock_type; + new_sock->protocol = protocol; + new_sock->can_send_data = TRUE; + new_sock->can_send_nak = TRUE; + new_sock->can_recv_data = TRUE; + new_sock->dport = DEFAULT_DATA_DESTINATION_PORT; + new_sock->tsi.sport = DEFAULT_DATA_SOURCE_PORT; + new_sock->adv_mode = 0; /* advance with time */ + +/* PGMCC */ + new_sock->acker_nla.ss_family = family; + +/* source-side */ + pgm_mutex_init (&new_sock->source_mutex); +/* transmit window */ + pgm_spinlock_init (&new_sock->txw_spinlock); +/* send socket */ + pgm_mutex_init (&new_sock->send_mutex); +/* next timer & spm expiration */ + pgm_mutex_init (&new_sock->timer_mutex); +/* receiver-side */ + pgm_mutex_init (&new_sock->receiver_mutex); +/* peer hash map & list lock */ + pgm_rwlock_init (&new_sock->peers_lock); +/* destroy lock */ + pgm_rwlock_init (&new_sock->lock); + +/* open sockets to implement PGM */ + int socket_type; + if (IPPROTO_UDP == new_sock->protocol) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Opening UDP encapsulated sockets.")); + socket_type = SOCK_DGRAM; + new_sock->udp_encap_ucast_port = DEFAULT_UDP_ENCAP_UCAST_PORT; + new_sock->udp_encap_mcast_port = DEFAULT_UDP_ENCAP_MCAST_PORT; + } else { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Opening raw sockets.")); + socket_type = SOCK_RAW; + } + + if ((new_sock->recv_sock = socket (new_sock->family, + socket_type, + new_sock->protocol)) == PGM_INVALID_SOCKET) + { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + _("Creating receive socket: %s"), + pgm_sock_strerror (save_errno)); +#ifndef _WIN32 + if (EPERM == save_errno) { + pgm_error (_("PGM protocol requires CAP_NET_RAW capability, e.g. sudo execcap 'cap_net_raw=ep'")); + } +#endif + goto err_destroy; + } + + if ((new_sock->send_sock = socket (new_sock->family, + socket_type, + new_sock->protocol)) == PGM_INVALID_SOCKET) + { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + _("Creating send socket: %s"), + pgm_sock_strerror (save_errno)); + goto err_destroy; + } + + if ((new_sock->send_with_router_alert_sock = socket (new_sock->family, + socket_type, + new_sock->protocol)) == PGM_INVALID_SOCKET) + { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + _("Creating IP Router Alert (RFC 2113) send socket: %s"), + pgm_sock_strerror (save_errno)); + goto err_destroy; + } + + *sock = new_sock; + + pgm_rwlock_writer_lock (&pgm_sock_list_lock); + pgm_sock_list = pgm_slist_append (pgm_sock_list, *sock); + pgm_rwlock_writer_unlock (&pgm_sock_list_lock); + pgm_debug ("PGM socket successfully created."); + return TRUE; + +err_destroy: + if (PGM_INVALID_SOCKET != new_sock->recv_sock) { + if (PGM_SOCKET_ERROR == pgm_closesocket (new_sock->recv_sock)) { + const int save_errno = pgm_sock_errno(); + pgm_warn (_("Close on receive socket failed: %s"), + pgm_sock_strerror (save_errno)); + } + new_sock->recv_sock = PGM_INVALID_SOCKET; + } + if (PGM_INVALID_SOCKET != new_sock->send_sock) { + if (PGM_SOCKET_ERROR == pgm_closesocket (new_sock->send_sock)) { + const int save_errno = pgm_sock_errno(); + pgm_warn (_("Close on send socket failed: %s"), + pgm_sock_strerror (save_errno)); + } + new_sock->send_sock = PGM_INVALID_SOCKET; + } + if (PGM_INVALID_SOCKET != new_sock->send_with_router_alert_sock) { + if (PGM_SOCKET_ERROR == pgm_closesocket (new_sock->send_with_router_alert_sock)) { + const int save_errno = pgm_sock_errno(); + pgm_warn (_("Close on IP Router Alert (RFC 2113) send socket failed: %s"), + pgm_sock_strerror (save_errno)); + } + new_sock->send_with_router_alert_sock = PGM_INVALID_SOCKET; + } + pgm_free (new_sock); + return FALSE; +} + +bool +pgm_getsockopt ( + pgm_sock_t* const restrict sock, + const int optname, + void* restrict optval, + socklen_t* restrict optlen /* required */ + ) +{ + bool status = FALSE; + pgm_return_val_if_fail (sock != NULL, status); + pgm_return_val_if_fail (optval != NULL, status); + pgm_return_val_if_fail (optlen != NULL, status); + if (PGM_UNLIKELY(!pgm_rwlock_reader_trylock (&sock->lock))) + pgm_return_val_if_reached (status); + if (PGM_UNLIKELY(sock->is_destroyed)) { + pgm_rwlock_reader_unlock (&sock->lock); + return status; + } + switch (optname) { +/* maximum TPDU size */ + case PGM_MTU: + if (PGM_UNLIKELY(*optlen != sizeof (int))) + break; + *(int*restrict)optval = sock->max_tpdu; + status = TRUE; + break; + +/* receive socket */ + case PGM_RECV_SOCK: + if (PGM_UNLIKELY(!sock->is_connected)) + break; + if (PGM_UNLIKELY(*optlen != sizeof (int))) + break; + *(int*)optval = sock->recv_sock; + status = TRUE; + break; + +/* repair socket */ + case PGM_REPAIR_SOCK: + if (PGM_UNLIKELY(!sock->is_connected)) + break; + if (PGM_UNLIKELY(*optlen != sizeof (int))) + break; + *(int*)optval = pgm_notify_get_fd (&sock->rdata_notify); + status = TRUE; + break; + +/* pending socket */ + case PGM_PENDING_SOCK: + if (PGM_UNLIKELY(!sock->is_connected)) + break; + if (PGM_UNLIKELY(*optlen != sizeof (int))) + break; + *(int*)optval = pgm_notify_get_fd (&sock->pending_notify); + status = TRUE; + break; + +/* ACK or congestion socket */ + case PGM_ACK_SOCK: + if (PGM_UNLIKELY(!sock->is_connected)) + break; + if (PGM_UNLIKELY(*optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(!sock->use_pgmcc)) + break; + *(int*)optval = pgm_notify_get_fd (&sock->ack_notify); + status = TRUE; + break; + + +/* timeout for pending timer */ + case PGM_TIME_REMAIN: + if (PGM_UNLIKELY(!sock->is_connected)) + break; + if (PGM_UNLIKELY(*optlen != sizeof (struct timeval))) + break; + { + struct timeval* tv = optval; + const pgm_time_t usecs = pgm_timer_expiration (sock); + tv->tv_sec = usecs / 1000000UL; + tv->tv_usec = usecs % 1000000UL; + } + status = TRUE; + break; + +/* timeout for blocking sends */ + case PGM_RATE_REMAIN: + if (PGM_UNLIKELY(!sock->is_connected)) + break; + if (PGM_UNLIKELY(*optlen != sizeof (struct timeval))) + break; + { + struct timeval* tv = optval; + const pgm_time_t usecs = pgm_rate_remaining (&sock->rate_control, sock->blocklen); + tv->tv_sec = usecs / 1000000UL; + tv->tv_usec = usecs % 1000000UL; + } + status = TRUE; + break; + + + } + pgm_rwlock_reader_unlock (&sock->lock); + return status; +} + +bool +pgm_setsockopt ( + pgm_sock_t* const sock, + const int optname, + const void* optval, + const socklen_t optlen + ) +{ + bool status = FALSE; + pgm_return_val_if_fail (sock != NULL, status); + if (PGM_UNLIKELY(!pgm_rwlock_reader_trylock (&sock->lock))) + pgm_return_val_if_reached (status); + if (PGM_UNLIKELY(sock->is_connected || sock->is_destroyed)) { + pgm_rwlock_reader_unlock (&sock->lock); + return status; + } + switch (optname) { + +/* RFC2113 IP Router Alert + */ + case PGM_IP_ROUTER_ALERT: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + { + const bool v = (0 != *(const int*)optval); + if (PGM_SOCKET_ERROR == pgm_sockaddr_router_alert (sock->send_with_router_alert_sock, sock->family, v)) + break; + } + status = TRUE; + break; + +/* IPv4: 68 <= tpdu < 65536 (RFC 2765) + * IPv6: 1280 <= tpdu < 65536 (RFC 2460) + */ + case PGM_MTU: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval < (sizeof(struct pgm_ip) + sizeof(struct pgm_header)))) + break; + if (PGM_UNLIKELY(*(const int*)optval > UINT16_MAX)) + break; + sock->max_tpdu = *(const int*)optval; + status = TRUE; + break; + +/* 1 = enable multicast loopback. + * 0 = default, to disable. + */ + case PGM_MULTICAST_LOOP: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + { + const bool v = (0 != *(const int*)optval); +#ifndef _WIN32 /* loop on send */ + if (PGM_SOCKET_ERROR == pgm_sockaddr_multicast_loop (sock->send_sock, sock->family, v) || + PGM_SOCKET_ERROR == pgm_sockaddr_multicast_loop (sock->send_with_router_alert_sock, sock->family, v)) + break; +#else /* loop on receive */ + if (PGM_SOCKET_ERROR == pgm_sockaddr_multicast_loop (sock->recv_sock, sock->family, v)) + break; +#endif + } + status = TRUE; + break; + +/* 0 < hops < 256, hops == -1 use kernel default (ignored). + */ + case PGM_MULTICAST_HOPS: +#ifndef CONFIG_TARGET_WINE + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + if (PGM_UNLIKELY(*(const int*)optval > UINT8_MAX)) + break; + { + const unsigned hops = *(const int*)optval; + if (PGM_SOCKET_ERROR == pgm_sockaddr_multicast_hops (sock->send_sock, sock->family, hops) || + PGM_SOCKET_ERROR == pgm_sockaddr_multicast_hops (sock->send_with_router_alert_sock, sock->family, hops)) + break; + } +#endif + status = TRUE; + break; + +/* IP Type of Service (ToS) or RFC 3246, differentiated services (DSCP) + */ + case PGM_TOS: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_SOCKET_ERROR == pgm_sockaddr_tos (sock->send_sock, sock->family, *(const int*)optval) || + PGM_SOCKET_ERROR == pgm_sockaddr_tos (sock->send_with_router_alert_sock, sock->family, *(const int*)optval)) + { + pgm_warn (_("ToS/DSCP setting requires CAP_NET_ADMIN or ADMIN capability.")); + break; + } + status = TRUE; + break; + +/* 0 < wmem < wmem_max (user) + * + * operating system and sysctl dependent maximum, minimum on Linux 256 (doubled). + */ + case PGM_SNDBUF: + if (PGM_SOCKET_ERROR == setsockopt (sock->send_sock, SOL_SOCKET, SO_SNDBUF, (const char*)optval, optlen) || + PGM_SOCKET_ERROR == setsockopt (sock->send_with_router_alert_sock, SOL_SOCKET, SO_SNDBUF, (const char*)optval, optlen)) + break; + status = TRUE; + break; + +/* 0 < rmem < rmem_max (user) + * + * minimum on Linux is 2048 (doubled). + */ + case PGM_RCVBUF: + if (PGM_SOCKET_ERROR == setsockopt (sock->recv_sock, SOL_SOCKET, SO_RCVBUF, (const char*)optval, optlen)) + break; + status = TRUE; + break; + +/* periodic ambient broadcast SPM interval in milliseconds. + */ + case PGM_AMBIENT_SPM: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + sock->spm_ambient_interval = *(const int*)optval; + status = TRUE; + break; + +/* sequence of heartbeat broadcast SPMS to flush out original + */ + case PGM_HEARTBEAT_SPM: + if (PGM_UNLIKELY(0 != optlen % sizeof (int))) + break; + { + sock->spm_heartbeat_len = optlen / sizeof (int); + sock->spm_heartbeat_interval = pgm_new (unsigned, sock->spm_heartbeat_len + 1); + sock->spm_heartbeat_interval[0] = 0; + for (unsigned i = 0; i < sock->spm_heartbeat_len; i++) + sock->spm_heartbeat_interval[i + 1] = ((const int*)optval)[i]; + } + status = TRUE; + break; + +/* size of transmit window in sequence numbers. + * 0 < txw_sqns < one less than half sequence space + */ + case PGM_TXW_SQNS: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + if (PGM_UNLIKELY(*(const int*)optval >= ((UINT32_MAX/2)-1))) + break; + sock->txw_sqns = *(const int*)optval; + status = TRUE; + break; + +/* size of transmit window in seconds. + * 0 < secs < ( txw_sqns / txw_max_rte ) + */ + case PGM_TXW_SECS: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + sock->txw_secs = *(const int*)optval; + status = TRUE; + break; + +/* maximum transmit rate. + * 0 < txw_max_rte < interface capacity + * 10mb : 1250000 + * 100mb : 12500000 + * 1gb : 125000000 + */ + case PGM_TXW_MAX_RTE: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + sock->txw_max_rte = *(const int*)optval; + status = TRUE; + break; + +/* timeout for peers. + * 0 < 2 * spm_ambient_interval <= peer_expiry + */ + case PGM_PEER_EXPIRY: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + sock->peer_expiry = *(const int*)optval; + status = TRUE; + break; + +/* maximum back off range for listening for multicast SPMR. + * 0 < spmr_expiry < spm_ambient_interval + */ + case PGM_SPMR_EXPIRY: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + sock->spmr_expiry = *(const int*)optval; + status = TRUE; + break; + +/* size of receive window in sequence numbers. + * 0 < rxw_sqns < one less than half sequence space + */ + case PGM_RXW_SQNS: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + if (PGM_UNLIKELY(*(const int*)optval >= ((UINT32_MAX/2)-1))) + break; + sock->rxw_sqns = *(const int*)optval; + status = TRUE; + break; + +/* size of receive window in seconds. + * 0 < secs < ( rxw_sqns / rxw_max_rte ) + */ + case PGM_RXW_SECS: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + sock->rxw_secs = *(const int*)optval; + status = TRUE; + break; + +/* maximum receive rate, for determining window size with txw_secs. + * 0 < rxw_max_rte < interface capacity + */ + case PGM_RXW_MAX_RTE: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + sock->rxw_max_rte = *(const int*)optval; + status = TRUE; + break; + +/* maximum NAK back-off value nak_rb_ivl in milliseconds. + * 0 < nak_rb_ivl <= nak_bo_ivl + */ + case PGM_NAK_BO_IVL: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + sock->nak_bo_ivl = *(const int*)optval; + status = TRUE; + break; + +/* repeat interval prior to re-sending a NAK, in milliseconds. + */ + case PGM_NAK_RPT_IVL: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + sock->nak_rpt_ivl = *(const int*)optval; + status = TRUE; + break; + +/* interval waiting for repair data, in milliseconds. + */ + case PGM_NAK_RDATA_IVL: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + sock->nak_rdata_ivl = *(const int*)optval; + status = TRUE; + break; + +/* limit for data. + * 0 < nak_data_retries < 256 + */ + case PGM_NAK_DATA_RETRIES: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + if (PGM_UNLIKELY(*(const int*)optval > UINT8_MAX)) + break; + sock->nak_data_retries = *(const int*)optval; + status = TRUE; + break; + +/* limit for NAK confirms. + * 0 < nak_ncf_retries < 256 + */ + case PGM_NAK_NCF_RETRIES: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + if (PGM_UNLIKELY(*(const int*)optval > UINT8_MAX)) + break; + sock->nak_ncf_retries = *(const int*)optval; + status = TRUE; + break; + +/* Enable FEC for this sock, specifically Reed Solmon encoding RS(n,k), common + * setting is RS(255, 223). + * + * inputs: + * + * n = FEC Block size = [k+1, 255] + * k = original data packets == transmission group size = [2, 4, 8, 16, 32, 64, 128] + * m = symbol size = 8 bits + * + * outputs: + * + * h = 2t = n - k = parity packets + * + * when h > k parity packets can be lost. + */ + case PGM_USE_FEC: + if (PGM_UNLIKELY(optlen != sizeof (struct pgm_fecinfo_t))) + break; + { + const struct pgm_fecinfo_t* fecinfo = optval; + if (PGM_UNLIKELY(0 != (fecinfo->group_size & (fecinfo->group_size - 1)))) + break; + if (PGM_UNLIKELY(fecinfo->group_size < 2 || fecinfo->group_size > 128)) + break; + if (PGM_UNLIKELY(fecinfo->group_size > fecinfo->block_size)) + break; + const uint8_t parity_packets = fecinfo->block_size - fecinfo->group_size; +/* technically could re-send previous packets */ + if (PGM_UNLIKELY(fecinfo->proactive_packets > parity_packets)) + break; +/* check validity of parameters */ + if (PGM_UNLIKELY(fecinfo->group_size > 223 && ((parity_packets * 223.0) / fecinfo->group_size) < 1.0)) + { + pgm_error (_("k/h ratio too low to generate parity data.")); + break; + } + sock->use_proactive_parity = (fecinfo->proactive_packets > 0); + sock->use_ondemand_parity = fecinfo->ondemand_parity_enabled; + sock->use_var_pktlen = fecinfo->var_pktlen_enabled; + sock->rs_n = fecinfo->block_size; + sock->rs_k = fecinfo->group_size; + sock->rs_proactive_h = fecinfo->proactive_packets; + } + status = TRUE; + break; + +/* congestion reporting */ + case PGM_USE_CR: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + sock->crqst_ivl = *(const int*)optval; + sock->use_cr = (sock->crqst_ivl > 0); + status = TRUE; + break; + +/* congestion control */ + case PGM_USE_PGMCC: + if (PGM_UNLIKELY(optlen != sizeof (struct pgm_pgmccinfo_t))) + break; + { + const struct pgm_pgmccinfo_t* pgmccinfo = optval; + sock->ack_bo_ivl = pgmccinfo->ack_bo_ivl; + sock->ack_c = pgmccinfo->ack_c; + sock->ack_c_p = pgmccinfo->ack_c_p; + sock->use_pgmcc = (sock->ack_c > 0); + } + status = TRUE; + break; + +/* declare socket only for sending, discard any incoming SPM, ODATA, + * RDATA, etc, packets. + */ + case PGM_SEND_ONLY: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + sock->can_recv_data = (0 == *(const int*)optval); + status = TRUE; + break; + +/* declare socket only for receiving, no transmit window will be created + * and no SPM broadcasts sent. + */ + case PGM_RECV_ONLY: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + sock->can_send_data = (0 == *(const int*)optval); + status = TRUE; + break; + +/* passive receiving socket, i.e. no back channel to source + */ + case PGM_PASSIVE: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + sock->can_send_nak = (0 == *(const int*)optval); + status = TRUE; + break; + +/* on unrecoverable data loss stop socket from further transmission and + * receiving. + */ + case PGM_ABORT_ON_RESET: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + sock->is_abort_on_reset = (0 != *(const int*)optval); + status = TRUE; + break; + +/* default non-blocking operation on send and receive sockets. + */ + case PGM_NOBLOCK: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + sock->is_nonblocking = (0 != *(const int*)optval); + pgm_sockaddr_nonblocking (sock->recv_sock, sock->is_nonblocking); + pgm_sockaddr_nonblocking (sock->send_sock, sock->is_nonblocking); + pgm_sockaddr_nonblocking (sock->send_with_router_alert_sock, sock->is_nonblocking); + status = TRUE; + break; + +/* sending group, singular. + */ + case PGM_SEND_GROUP: + if (PGM_UNLIKELY(optlen != sizeof(struct group_req))) + break; + memcpy (&sock->send_gsr, optval, optlen); + ((struct sockaddr_in*)&sock->send_gsr.gsr_group)->sin_port = htons (sock->udp_encap_mcast_port); + if (PGM_UNLIKELY(sock->family != sock->send_gsr.gsr_group.ss_family)) + break; + if (PGM_SOCKET_ERROR == pgm_sockaddr_multicast_if (sock->send_sock, (const struct sockaddr*)&sock->send_gsr.gsr_group, sock->send_gsr.gsr_interface) || + PGM_SOCKET_ERROR == pgm_sockaddr_multicast_if (sock->send_with_router_alert_sock, (const struct sockaddr*)&sock->send_gsr.gsr_group, sock->send_gsr.gsr_interface)) + break; + status = TRUE; + break; + +/* for any-source applications (ASM), join a new group + */ + case PGM_JOIN_GROUP: + if (PGM_UNLIKELY(optlen != sizeof(struct group_req))) + break; + if (PGM_UNLIKELY(sock->recv_gsr_len >= IP_MAX_MEMBERSHIPS)) + break; + { + const struct group_req* gr = optval; +/* verify not duplicate group/interface pairing */ + for (unsigned i = 0; i < sock->recv_gsr_len; i++) + { + if (pgm_sockaddr_cmp ((const struct sockaddr*)&gr->gr_group, (struct sockaddr*)&sock->recv_gsr[i].gsr_group) == 0 && + pgm_sockaddr_cmp ((const struct sockaddr*)&gr->gr_group, (struct sockaddr*)&sock->recv_gsr[i].gsr_source) == 0 && + (gr->gr_interface == sock->recv_gsr[i].gsr_interface || + 0 == sock->recv_gsr[i].gsr_interface )) + { +#ifdef SOCKET_DEBUG + char s[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((const struct sockaddr*)&gr->gr_group, s, sizeof(s)); + if (sock->recv_gsr[i].gsr_interface) { + pgm_warn(_("Socket has already joined group %s on interface %u"), s, gr->gr_interface); + } else { + pgm_warn(_("Socket has already joined group %s on all interfaces."), s); + } +#endif + break; + } + } + if (PGM_UNLIKELY(sock->family != gr->gr_group.ss_family)) + break; + if (PGM_SOCKET_ERROR == pgm_sockaddr_join_group (sock->recv_sock, sock->family, gr)) + break; + sock->recv_gsr[sock->recv_gsr_len].gsr_interface = gr->gr_interface; + memcpy (&sock->recv_gsr[sock->recv_gsr_len].gsr_group, &gr->gr_group, pgm_sockaddr_len ((const struct sockaddr*)&gr->gr_group)); + memcpy (&sock->recv_gsr[sock->recv_gsr_len].gsr_source, &gr->gr_group, pgm_sockaddr_len ((const struct sockaddr*)&gr->gr_group)); + sock->recv_gsr_len++; + } + status = TRUE; + break; + +/* for any-source applications (ASM), leave a joined group. + */ + case PGM_LEAVE_GROUP: + if (PGM_UNLIKELY(optlen != sizeof(struct group_req))) + break; + if (PGM_UNLIKELY(0 == sock->recv_gsr_len)) + break; + { + const struct group_req* gr = optval; + for (unsigned i = 0; i < sock->recv_gsr_len;) + { + if ((pgm_sockaddr_cmp ((const struct sockaddr*)&gr->gr_group, (struct sockaddr*)&sock->recv_gsr[i].gsr_group) == 0) && +/* drop all matching receiver entries */ + (gr->gr_interface == 0 || +/* drop all sources with matching interface */ + gr->gr_interface == sock->recv_gsr[i].gsr_interface) ) + { + sock->recv_gsr_len--; + if (i < (IP_MAX_MEMBERSHIPS - 1)) + { + memmove (&sock->recv_gsr[i], &sock->recv_gsr[i+1], (sock->recv_gsr_len - i) * sizeof(struct group_source_req)); + continue; + } + } + i++; + } + if (PGM_UNLIKELY(sock->family != gr->gr_group.ss_family)) + break; + if (PGM_SOCKET_ERROR == pgm_sockaddr_leave_group (sock->recv_sock, sock->family, gr)) + break; + } + status = TRUE; + break; + +/* for any-source applications (ASM), turn off a given source + */ + case PGM_BLOCK_SOURCE: + if (PGM_UNLIKELY(optlen != sizeof(struct group_source_req))) + break; + { + const struct group_source_req* gsr = optval; + if (PGM_UNLIKELY(sock->family != gsr->gsr_group.ss_family)) + break; + if (PGM_SOCKET_ERROR == pgm_sockaddr_block_source (sock->recv_sock, sock->family, gsr)) + break; + } + status = TRUE; + break; + +/* for any-source applications (ASM), re-allow a blocked source + */ + case PGM_UNBLOCK_SOURCE: + if (PGM_UNLIKELY(optlen != sizeof(struct group_source_req))) + break; + { + const struct group_source_req* gsr = optval; + if (PGM_UNLIKELY(sock->family != gsr->gsr_group.ss_family)) + break; + if (PGM_SOCKET_ERROR == pgm_sockaddr_unblock_source (sock->recv_sock, sock->family, gsr)) + break; + } + status = TRUE; + break; + +/* for controlled-source applications (SSM), join each group/source pair. + * + * SSM joins are allowed on top of ASM in order to merge a remote source onto the local segment. + */ + case PGM_JOIN_SOURCE_GROUP: + if (PGM_UNLIKELY(optlen != sizeof(struct group_source_req))) + break; + if (PGM_UNLIKELY(sock->recv_gsr_len >= IP_MAX_MEMBERSHIPS)) + break; + { + const struct group_source_req* gsr = optval; +/* verify if existing group/interface pairing */ + for (unsigned i = 0; i < sock->recv_gsr_len; i++) + { + if (pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_group, (struct sockaddr*)&sock->recv_gsr[i].gsr_group) == 0 && + (gsr->gsr_interface == sock->recv_gsr[i].gsr_interface || + 0 == sock->recv_gsr[i].gsr_interface )) + { + if (pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_source, (struct sockaddr*)&sock->recv_gsr[i].gsr_source) == 0) + { +#ifdef SOCKET_DEBUG + char s1[INET6_ADDRSTRLEN], s2[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((const struct sockaddr*)&gsr->gsr_group, s1, sizeof(s1)); + pgm_sockaddr_ntop ((const struct sockaddr*)&gsr->gsr_source, s2, sizeof(s2)); + if (sock->recv_gsr[i].gsr_interface) { + pgm_warn(_("Socket has already joined group %s from source %s on interface %d"), + s1, s2, (unsigned)gsr->gsr_interface); + } else { + pgm_warn(_("Socket has already joined group %s from source %s on all interfaces"), + s1, s2); + } +#endif + break; + } + break; + } + } + if (PGM_UNLIKELY(sock->family != gsr->gsr_group.ss_family)) + break; + if (PGM_UNLIKELY(sock->family != gsr->gsr_source.ss_family)) + break; + if (PGM_SOCKET_ERROR == pgm_sockaddr_join_source_group (sock->recv_sock, sock->family, gsr)) + break; + memcpy (&sock->recv_gsr[sock->recv_gsr_len], gsr, sizeof(struct group_source_req)); + sock->recv_gsr_len++; + } + status = TRUE; + break; + +/* for controlled-source applications (SSM), leave each group/source pair + */ + case PGM_LEAVE_SOURCE_GROUP: + if (PGM_UNLIKELY(optlen != sizeof(struct group_source_req))) + break; + if (PGM_UNLIKELY(0 == sock->recv_gsr_len)) + break; + { + const struct group_source_req* gsr = optval; +/* verify if existing group/interface pairing */ + for (unsigned i = 0; i < sock->recv_gsr_len; i++) + { + if (pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_group, (struct sockaddr*)&sock->recv_gsr[i].gsr_group) == 0 && + pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_source, (struct sockaddr*)&sock->recv_gsr[i].gsr_source) == 0 && + gsr->gsr_interface == sock->recv_gsr[i].gsr_interface) + { + sock->recv_gsr_len--; + if (i < (IP_MAX_MEMBERSHIPS - 1)) + { + memmove (&sock->recv_gsr[i], &sock->recv_gsr[i+1], (sock->recv_gsr_len - i) * sizeof(struct group_source_req)); + break; + } + } + } + if (PGM_UNLIKELY(sock->family != gsr->gsr_group.ss_family)) + break; + if (PGM_UNLIKELY(sock->family != gsr->gsr_source.ss_family)) + break; + if (PGM_SOCKET_ERROR == pgm_sockaddr_leave_source_group (sock->recv_sock, sock->family, gsr)) + break; + } + status = TRUE; + break; + +/* batch block and unblock sources */ + case PGM_MSFILTER: +#if defined(MCAST_MSFILTER) || defined(SIOCSMSFILTER) + if (PGM_UNLIKELY(optlen < sizeof(struct group_filter))) + break; + { + const struct group_filter* gf_list = optval; + if (GROUP_FILTER_SIZE( gf_list->gf_numsrc ) != optlen) + break; + if (PGM_UNLIKELY(sock->family != gf_list->gf_group.ss_family)) + break; +/* check only first */ + if (PGM_UNLIKELY(sock->family != gf_list->gf_slist[0].ss_family)) + break; + if (PGM_SOCKET_ERROR == pgm_sockaddr_msfilter (sock->recv_sock, sock->family, gf_list)) + break; + } + status = TRUE; +#endif + break; + +/* UDP encapsulation ports */ + case PGM_UDP_ENCAP_UCAST_PORT: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + sock->udp_encap_ucast_port = *(const int*)optval; + status = TRUE; + break; + + case PGM_UDP_ENCAP_MCAST_PORT: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + sock->udp_encap_mcast_port = *(const int*)optval; + status = TRUE; + break; + + } + + pgm_rwlock_reader_unlock (&sock->lock); + return status; +} + +bool +pgm_bind ( + pgm_sock_t* restrict sock, + const struct pgm_sockaddr_t*const restrict sockaddr, + const socklen_t sockaddrlen, + pgm_error_t** restrict error + ) +{ + struct pgm_interface_req_t null_req; + memset (&null_req, 0, sizeof(null_req)); + return pgm_bind3 (sock, sockaddr, sockaddrlen, &null_req, sizeof(null_req), &null_req, sizeof(null_req), error); +} + +/* bind the sockets to the link layer to start receiving data. + * + * returns TRUE on success, or FALSE on error and sets error appropriately, + */ + +bool +pgm_bind3 ( + pgm_sock_t* restrict sock, + const struct pgm_sockaddr_t* restrict sockaddr, + const socklen_t sockaddrlen, + const struct pgm_interface_req_t* send_req, /* only use gr_interface and gr_group::sin6_scope */ + const socklen_t send_req_len, + const struct pgm_interface_req_t* recv_req, + const socklen_t recv_req_len, + pgm_error_t** restrict error /* maybe NULL */ + ) +{ + pgm_return_val_if_fail (NULL != sock, FALSE); + pgm_return_val_if_fail (NULL != sockaddr, FALSE); + pgm_return_val_if_fail (0 != sockaddrlen, FALSE); + if (sockaddr->sa_addr.sport) pgm_return_val_if_fail (sockaddr->sa_addr.sport != sockaddr->sa_port, FALSE); + pgm_return_val_if_fail (NULL != send_req, FALSE); + pgm_return_val_if_fail (sizeof(struct pgm_interface_req_t) == send_req_len, FALSE); + pgm_return_val_if_fail (NULL != recv_req, FALSE); + pgm_return_val_if_fail (sizeof(struct pgm_interface_req_t) == recv_req_len, FALSE); + + if (!pgm_rwlock_writer_trylock (&sock->lock)) + pgm_return_val_if_reached (FALSE); + if (sock->is_bound || + sock->is_destroyed) + { + pgm_rwlock_writer_unlock (&sock->lock); + pgm_return_val_if_reached (FALSE); + } + +/* sanity checks on state */ + if (sock->max_tpdu < (sizeof(struct pgm_ip) + sizeof(struct pgm_header))) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("Invalid maximum TPDU size.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (sock->can_send_data) { + if (PGM_UNLIKELY(0 == sock->spm_ambient_interval)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("SPM ambient interval not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (PGM_UNLIKELY(0 == sock->spm_heartbeat_len)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("SPM heartbeat interval not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (PGM_UNLIKELY(0 == sock->txw_sqns && 0 == sock->txw_secs)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("TXW_SQNS not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (PGM_UNLIKELY(0 == sock->txw_sqns && 0 == sock->txw_max_rte)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("TXW_MAX_RTE not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + } + if (sock->can_recv_data) { + if (PGM_UNLIKELY(0 == sock->rxw_sqns && 0 == sock->rxw_secs)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("RXW_SQNS not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (PGM_UNLIKELY(0 == sock->rxw_sqns && 0 == sock->rxw_max_rte)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("RXW_MAX_RTE not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (PGM_UNLIKELY(0 == sock->peer_expiry)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("Peer timeout not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (PGM_UNLIKELY(0 == sock->spmr_expiry)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("SPM-Request timeout not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (PGM_UNLIKELY(0 == sock->nak_bo_ivl)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("NAK_BO_IVL not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (PGM_UNLIKELY(0 == sock->nak_rpt_ivl)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("NAK_RPT_IVL not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (PGM_UNLIKELY(0 == sock->nak_rdata_ivl)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("NAK_RDATA_IVL not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (PGM_UNLIKELY(0 == sock->nak_data_retries)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("NAK_DATA_RETRIES not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (PGM_UNLIKELY(0 == sock->nak_ncf_retries)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("NAK_NCF_RETRIES not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + } + + pgm_debug ("bind3 (sock:%p sockaddr:%p sockaddrlen:%u send-req:%p send-req-len:%u recv-req:%p recv-req-len:%u error:%p)", + (const void*)sock, (const void*)sockaddr, (unsigned)sockaddrlen, (const void*)send_req, (unsigned)send_req_len, (const void*)recv_req, (unsigned)recv_req_len, (const void*)error); + + memcpy (&sock->tsi, &sockaddr->sa_addr, sizeof(pgm_tsi_t)); + sock->dport = htons (sockaddr->sa_port); + if (sock->tsi.sport) { + sock->tsi.sport = htons (sock->tsi.sport); + } else { + do { + sock->tsi.sport = htons (pgm_random_int_range (0, UINT16_MAX)); + } while (sock->tsi.sport == sock->dport); + } + +/* UDP encapsulation port */ + if (sock->udp_encap_mcast_port) { + ((struct sockaddr_in*)&sock->send_gsr.gsr_group)->sin_port = htons (sock->udp_encap_mcast_port); + } + +/* pseudo-random number generator for back-off intervals */ + pgm_rand_create (&sock->rand_); + +/* PGM Children support of POLLs requires 32-bit random node identifier RAND_NODE_ID */ + if (sock->can_recv_data) { + sock->rand_node_id = pgm_rand_int (&sock->rand_); + } + + if (sock->can_send_data) + { +/* Windows notify call will raise an assertion on error, only Unix versions will return + * a valid error. + */ + if (sock->use_pgmcc && + 0 != pgm_notify_init (&sock->ack_notify)) + { + const int save_errno = errno; + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_errno (save_errno), + _("Creating ACK notification channel: %s"), + strerror (save_errno)); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (0 != pgm_notify_init (&sock->rdata_notify)) + { + const int save_errno = errno; + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_errno (save_errno), + _("Creating RDATA notification channel: %s"), + strerror (save_errno)); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + } + if (0 != pgm_notify_init (&sock->pending_notify)) + { + const int save_errno = errno; + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_errno (save_errno), + _("Creating waiting peer notification channel: %s"), + strerror (save_errno)); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + +/* determine IP header size for rate regulation engine & stats */ + sock->iphdr_len = (AF_INET == sock->family) ? sizeof(struct pgm_ip) : sizeof(struct pgm_ip6_hdr); + pgm_trace (PGM_LOG_ROLE_NETWORK,"Assuming IP header size of %zu bytes", sock->iphdr_len); + + if (sock->udp_encap_ucast_port) { + const size_t udphdr_len = sizeof(struct pgm_udphdr); + pgm_trace (PGM_LOG_ROLE_NETWORK,"Assuming UDP header size of %zu bytes", udphdr_len); + sock->iphdr_len += udphdr_len; + } + + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + sock->max_tsdu = sock->max_tpdu - sock->iphdr_len - pgm_pkt_offset (FALSE, pgmcc_family); + sock->max_tsdu_fragment = sock->max_tpdu - sock->iphdr_len - pgm_pkt_offset (TRUE, pgmcc_family); + const unsigned max_fragments = sock->txw_sqns ? MIN( PGM_MAX_FRAGMENTS, sock->txw_sqns ) : PGM_MAX_FRAGMENTS; + sock->max_apdu = MIN( PGM_MAX_APDU, max_fragments * sock->max_tsdu_fragment ); + + if (sock->can_send_data) + { + pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Create transmit window.")); + sock->window = sock->txw_sqns ? + pgm_txw_create (&sock->tsi, + 0, /* MAX_TPDU */ + sock->txw_sqns, /* TXW_SQNS */ + 0, /* TXW_SECS */ + 0, /* TXW_MAX_RTE */ + sock->use_ondemand_parity || sock->use_proactive_parity, + sock->rs_n, + sock->rs_k) : + pgm_txw_create (&sock->tsi, + sock->max_tpdu, /* MAX_TPDU */ + 0, /* TXW_SQNS */ + sock->txw_secs, /* TXW_SECS */ + sock->txw_max_rte, /* TXW_MAX_RTE */ + sock->use_ondemand_parity || sock->use_proactive_parity, + sock->rs_n, + sock->rs_k); + pgm_assert (NULL != sock->window); + } + +/* create peer list */ + if (sock->can_recv_data) { + sock->peers_hashtable = pgm_hashtable_new (pgm_tsi_hash, pgm_tsi_equal); + pgm_assert (NULL != sock->peers_hashtable); + } + + if (IPPROTO_UDP == sock->protocol) + { +/* Stevens: "SO_REUSEADDR has datatype int." + */ + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Set socket sharing.")); + const int v = 1; + if (PGM_SOCKET_ERROR == setsockopt (sock->recv_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&v, sizeof(v)) || + PGM_SOCKET_ERROR == setsockopt (sock->send_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&v, sizeof(v)) || + PGM_SOCKET_ERROR == setsockopt (sock->send_with_router_alert_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&v, sizeof(v))) + { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + _("Enabling reuse of socket local address: %s"), + pgm_sock_strerror (save_errno)); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + +/* request extra packet information to determine destination address on each packet */ +#ifndef CONFIG_TARGET_WINE + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Request socket packet-info.")); + const sa_family_t recv_family = sock->family; + if (PGM_SOCKET_ERROR == pgm_sockaddr_pktinfo (sock->recv_sock, recv_family, TRUE)) + { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + _("Enabling receipt of ancillary information per incoming packet: %s"), + pgm_sock_strerror (save_errno)); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } +#endif + } + else + { + const sa_family_t recv_family = sock->family; + if (AF_INET == recv_family) + { +/* include IP header only for incoming data, only works for IPv4 */ + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Request IP headers.")); + if (PGM_SOCKET_ERROR == pgm_sockaddr_hdrincl (sock->recv_sock, recv_family, TRUE)) + { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + _("Enabling IP header in front of user data: %s"), + pgm_sock_strerror (save_errno)); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + } + else + { + pgm_assert (AF_INET6 == recv_family); + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Request socket packet-info.")); + if (PGM_SOCKET_ERROR == pgm_sockaddr_pktinfo (sock->recv_sock, recv_family, TRUE)) + { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + _("Enabling receipt of control message per incoming datagram: %s"), + pgm_sock_strerror (save_errno)); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + } + } + +/* Bind UDP sockets to interfaces, note multicast on a bound interface is + * fruity on some platforms. Roughly, binding to INADDR_ANY provides all + * data, binding to the multicast group provides only multicast traffic, + * and binding to the interface address provides only unicast traffic. + * + * Multicast routing, IGMP & MLD require a link local address, for IPv4 + * this is provided through MULTICAST_IF and IPv6 through bind, and these + * may be overridden by per packet scopes. + * + * After binding, default interfaces (0.0.0.0) are resolved. + */ +/* TODO: different ports requires a new bound socket */ + + union { + struct sockaddr sa; + struct sockaddr_in s4; + struct sockaddr_in6 s6; + struct sockaddr_storage ss; + } recv_addr, recv_addr2, send_addr, send_with_router_alert_addr; + +#ifdef CONFIG_BIND_INADDR_ANY +/* force default interface for bind-only, source address is still valid for multicast membership. + * effectively same as running getaddrinfo(hints = {ai_flags = AI_PASSIVE}) + */ + if (AF_INET == sock->family) { + memset (&recv_addr.s4, 0, sizeof(struct sockaddr_in)); + recv_addr.s4.sin_family = AF_INET; + recv_addr.s4.sin_addr.s_addr = INADDR_ANY; + } else { + memset (&recv_addr.s6, 0, sizeof(struct sockaddr_in6)); + recv_addr.s6.sin6_family = AF_INET6; + recv_addr.s6.sin6_addr = in6addr_any; + } + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Binding receive socket to INADDR_ANY.")); +#else + if (!pgm_if_indextoaddr (recv_req->ir_interface, + sock->family, + recv_req->ir_scope_id, + &recv_addr.sa, + error)) + { + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Binding receive socket to interface index %u scope %u"), + recv_req->ir_interface, + recv_req->ir_scope_id); + +#endif /* CONFIG_BIND_INADDR_ANY */ + + memcpy (&recv_addr2.sa, &recv_addr.sa, pgm_sockaddr_len (&recv_addr.sa)); + ((struct sockaddr_in*)&recv_addr)->sin_port = htons (sock->udp_encap_mcast_port); + if (PGM_SOCKET_ERROR == bind (sock->recv_sock, + &recv_addr.sa, + pgm_sockaddr_len (&recv_addr.sa))) + { + char addr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&recv_addr, addr, sizeof(addr)); + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + _("Binding receive socket to address %s: %s"), + addr, + pgm_sock_strerror (save_errno)); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + + if (PGM_UNLIKELY(pgm_log_mask & PGM_LOG_ROLE_NETWORK)) + { + char s[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&recv_addr, s, sizeof(s)); + pgm_debug ("bind succeeded on recv_gsr[0] interface %s", s); + } + +/* keep a copy of the original address source to re-use for router alert bind */ + memset (&send_addr, 0, sizeof(send_addr)); + + if (!pgm_if_indextoaddr (send_req->ir_interface, + sock->family, + send_req->ir_scope_id, + (struct sockaddr*)&send_addr, + error)) + { + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + else if (PGM_UNLIKELY(pgm_log_mask & PGM_LOG_ROLE_NETWORK)) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Binding send socket to interface index %u scope %u"), + send_req->ir_interface, + send_req->ir_scope_id); + } + + memcpy (&send_with_router_alert_addr, &send_addr, pgm_sockaddr_len ((struct sockaddr*)&send_addr)); + if (PGM_SOCKET_ERROR == bind (sock->send_sock, + (struct sockaddr*)&send_addr, + pgm_sockaddr_len ((struct sockaddr*)&send_addr))) + { + char addr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&send_addr, addr, sizeof(addr)); + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + _("Binding send socket to address %s: %s"), + addr, + pgm_sock_strerror (save_errno)); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + +/* resolve bound address if 0.0.0.0 */ + if (AF_INET == send_addr.ss.ss_family) + { + if ((INADDR_ANY == ((struct sockaddr_in*)&send_addr)->sin_addr.s_addr) && + !pgm_if_getnodeaddr (AF_INET, (struct sockaddr*)&send_addr, sizeof(send_addr), error)) + { + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + } + else if ((memcmp (&in6addr_any, &((struct sockaddr_in6*)&send_addr)->sin6_addr, sizeof(in6addr_any)) == 0) && + !pgm_if_getnodeaddr (AF_INET6, (struct sockaddr*)&send_addr, sizeof(send_addr), error)) + { + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + + if (PGM_UNLIKELY(pgm_log_mask & PGM_LOG_ROLE_NETWORK)) + { + char s[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&send_addr, s, sizeof(s)); + pgm_debug ("bind succeeded on send_gsr interface %s", s); + } + + if (PGM_SOCKET_ERROR == bind (sock->send_with_router_alert_sock, + (struct sockaddr*)&send_with_router_alert_addr, + pgm_sockaddr_len((struct sockaddr*)&send_with_router_alert_addr))) + { + char addr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&send_with_router_alert_addr, addr, sizeof(addr)); + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + _("Binding IP Router Alert (RFC 2113) send socket to address %s: %s"), + addr, + pgm_sock_strerror (save_errno)); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + + if (PGM_UNLIKELY(pgm_log_mask & PGM_LOG_ROLE_NETWORK)) + { + char s[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&send_with_router_alert_addr, s, sizeof(s)); + pgm_debug ("bind (router alert) succeeded on send_gsr interface %s", s); + } + +/* save send side address for broadcasting as source nla */ + memcpy (&sock->send_addr, &send_addr, pgm_sockaddr_len ((struct sockaddr*)&send_addr)); + +/* rx to nak processor notify channel */ + if (sock->can_send_data) + { +/* setup rate control */ + if (sock->txw_max_rte) + { + pgm_trace (PGM_LOG_ROLE_RATE_CONTROL,_("Setting rate regulation to %zd bytes per second."), + sock->txw_max_rte); + + pgm_rate_create (&sock->rate_control, sock->txw_max_rte, sock->iphdr_len, sock->max_tpdu); + sock->is_controlled_spm = TRUE; /* must always be set */ + sock->is_controlled_odata = TRUE; + sock->is_controlled_rdata = TRUE; + } + else + { + sock->is_controlled_spm = FALSE; + sock->is_controlled_odata = FALSE; + sock->is_controlled_rdata = FALSE; + } + } + +/* allocate first incoming packet buffer */ + sock->rx_buffer = pgm_alloc_skb (sock->max_tpdu); + +/* bind complete */ + sock->is_bound = TRUE; + +/* cleanup */ + pgm_rwlock_writer_unlock (&sock->lock); + pgm_debug ("PGM socket successfully bound."); + return TRUE; +} + +bool +pgm_connect ( + pgm_sock_t* restrict sock, + pgm_error_t** restrict error /* maybe NULL */ + ) +{ + pgm_return_val_if_fail (sock != NULL, FALSE); + pgm_return_val_if_fail (sock->recv_gsr_len > 0, FALSE); +#ifdef CONFIG_TARGET_WINE + pgm_return_val_if_fail (sock->recv_gsr_len == 1, FALSE); +#endif + for (unsigned i = 0; i < sock->recv_gsr_len; i++) + { + pgm_return_val_if_fail (sock->recv_gsr[i].gsr_group.ss_family == sock->recv_gsr[0].gsr_group.ss_family, FALSE); + pgm_return_val_if_fail (sock->recv_gsr[i].gsr_group.ss_family == sock->recv_gsr[i].gsr_source.ss_family, FALSE); + } + pgm_return_val_if_fail (sock->send_gsr.gsr_group.ss_family == sock->recv_gsr[0].gsr_group.ss_family, FALSE); +/* shutdown */ + if (PGM_UNLIKELY(!pgm_rwlock_writer_trylock (&sock->lock))) + pgm_return_val_if_reached (FALSE); +/* state */ + if (PGM_UNLIKELY(sock->is_connected || !sock->is_bound || sock->is_destroyed)) { + pgm_rwlock_writer_unlock (&sock->lock); + pgm_return_val_if_reached (FALSE); + } + + pgm_debug ("connect (sock:%p error:%p)", + (const void*)sock, (const void*)error); + +/* rx to nak processor notify channel */ + if (sock->can_send_data) + { +/* announce new sock by sending out SPMs */ + if (!pgm_send_spm (sock, PGM_OPT_SYN) || + !pgm_send_spm (sock, PGM_OPT_SYN) || + !pgm_send_spm (sock, PGM_OPT_SYN)) + { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + _("Sending SPM broadcast: %s"), + pgm_sock_strerror (save_errno)); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + + sock->next_poll = sock->next_ambient_spm = pgm_time_update_now() + sock->spm_ambient_interval; + +/* start PGMCC with one token */ + sock->tokens = sock->cwnd_size = pgm_fp8 (1); + +/* slow start threshold */ + sock->ssthresh = pgm_fp8 (4); + +/* ACK timeout, should be greater than first SPM heartbeat interval in order to be scheduled correctly */ + sock->ack_expiry_ivl = pgm_secs (3); + +/* start full history */ + sock->ack_bitmap = 0xffffffff; + } + else + { + pgm_assert (sock->can_recv_data); + sock->next_poll = pgm_time_update_now() + pgm_secs( 30 ); + } + + sock->is_connected = TRUE; + +/* cleanup */ + pgm_rwlock_writer_unlock (&sock->lock); + pgm_debug ("PGM socket successfully connected."); + return TRUE; +} + +/* return local endpoint address + */ + +bool +pgm_getsockname ( + pgm_sock_t* const restrict sock, + struct pgm_sockaddr_t* restrict addr, + socklen_t* restrict addrlen + ) +{ + pgm_assert (NULL != sock); + pgm_assert (NULL != addr); + pgm_assert (NULL != addrlen); + pgm_assert (sizeof(struct pgm_sockaddr_t) == *addrlen); + + if (!sock->is_bound) { + errno = EBADF; + return FALSE; + } + + addr->sa_port = sock->dport; + memcpy (&addr->sa_addr, &sock->tsi, sizeof(pgm_tsi_t)); + return TRUE; +} + +/* add select parameters for the receive socket(s) + * + * returns highest file descriptor used plus one. + */ + +int +pgm_select_info ( + pgm_sock_t* const restrict sock, + fd_set* const restrict readfds, /* blocking recv fds */ + fd_set* const restrict writefds, /* blocking send fds */ + int* const restrict n_fds /* in: max fds, out: max (in:fds, sock:fds) */ + ) +{ + int fds = 0; + + pgm_assert (NULL != sock); + pgm_assert (NULL != n_fds); + + if (!sock->is_bound || sock->is_destroyed) + { + errno = EBADF; + return -1; + } + + const bool is_congested = (sock->use_pgmcc && sock->tokens < pgm_fp8 (1)) ? TRUE : FALSE; + + if (readfds) + { + FD_SET(sock->recv_sock, readfds); + fds = sock->recv_sock + 1; + if (sock->can_send_data) { + const int rdata_fd = pgm_notify_get_fd (&sock->rdata_notify); + FD_SET(rdata_fd, readfds); + fds = MAX(fds, rdata_fd + 1); + if (is_congested) { + const int ack_fd = pgm_notify_get_fd (&sock->ack_notify); + FD_SET(ack_fd, readfds); + fds = MAX(fds, ack_fd + 1); + } + } + const int pending_fd = pgm_notify_get_fd (&sock->pending_notify); + FD_SET(pending_fd, readfds); + fds = MAX(fds, pending_fd + 1); + } + + if (sock->can_send_data && writefds && !is_congested) + { + FD_SET(sock->send_sock, writefds); + fds = MAX(sock->send_sock + 1, fds); + } + + return *n_fds = MAX(fds, *n_fds); +} + +#ifdef CONFIG_HAVE_POLL +/* add poll parameters for the receive socket(s) + * + * returns number of pollfd structures filled. + */ + +int +pgm_poll_info ( + pgm_sock_t* const restrict sock, + struct pollfd* const restrict fds, + int* const restrict n_fds, /* in: #fds, out: used #fds */ + const int events /* POLLIN, POLLOUT */ + ) +{ + pgm_assert (NULL != sock); + pgm_assert (NULL != fds); + pgm_assert (NULL != n_fds); + + if (!sock->is_bound || sock->is_destroyed) + { + errno = EBADF; + return -1; + } + + int moo = 0; + +/* we currently only support one incoming socket */ + if (events & POLLIN) + { + pgm_assert ( (1 + moo) <= *n_fds ); + fds[moo].fd = sock->recv_sock; + fds[moo].events = POLLIN; + moo++; + if (sock->can_send_data) { + pgm_assert ( (1 + moo) <= *n_fds ); + fds[moo].fd = pgm_notify_get_fd (&sock->rdata_notify); + fds[moo].events = POLLIN; + moo++; + } + pgm_assert ( (1 + moo) <= *n_fds ); + fds[moo].fd = pgm_notify_get_fd (&sock->pending_notify); + fds[moo].events = POLLIN; + moo++; + } + +/* ODATA only published on regular socket, no need to poll router-alert sock */ + if (sock->can_send_data && events & POLLOUT) + { + pgm_assert ( (1 + moo) <= *n_fds ); + if (sock->use_pgmcc && sock->tokens < pgm_fp8 (1)) { +/* rx thread poll for ACK */ + fds[moo].fd = pgm_notify_get_fd (&sock->ack_notify); + fds[moo].events = POLLIN; + } else { +/* kernel resource poll */ + fds[moo].fd = sock->send_sock; + fds[moo].events = POLLOUT; + } + moo++; + } + + return *n_fds = moo; +} +#endif /* CONFIG_HAVE_POLL */ + +/* add epoll parameters for the recieve socket(s), events should + * be set to EPOLLIN to wait for incoming events (data), and EPOLLOUT to wait + * for non-blocking write. + * + * returns 0 on success, -1 on failure and sets errno appropriately. + */ +#ifdef CONFIG_HAVE_EPOLL +int +pgm_epoll_ctl ( + pgm_sock_t* const sock, + const int epfd, + const int op, /* EPOLL_CTL_ADD, ... */ + const int events /* EPOLLIN, EPOLLOUT */ + ) +{ + if (!(op == EPOLL_CTL_ADD || op == EPOLL_CTL_MOD)) + { + errno = EINVAL; + return -1; + } + else if (!sock->is_bound || sock->is_destroyed) + { + errno = EBADF; + return -1; + } + + struct epoll_event event; + int retval = 0; + + if (events & EPOLLIN) + { + event.events = events & (EPOLLIN | EPOLLET | EPOLLONESHOT); + event.data.ptr = sock; + retval = epoll_ctl (epfd, op, sock->recv_sock, &event); + if (retval) + goto out; + if (sock->can_send_data) { + retval = epoll_ctl (epfd, op, pgm_notify_get_fd (&sock->rdata_notify), &event); + if (retval) + goto out; + } + retval = epoll_ctl (epfd, op, pgm_notify_get_fd (&sock->pending_notify), &event); + if (retval) + goto out; + + if (events & EPOLLET) + sock->is_edge_triggered_recv = TRUE; + } + + if (sock->can_send_data && events & EPOLLOUT) + { + bool enable_ack_socket = FALSE; + bool enable_send_socket = FALSE; + +/* both sockets need to be added when PGMCC is enabled */ + if (sock->use_pgmcc && EPOLL_CTL_ADD == op) { + enable_ack_socket = enable_send_socket = TRUE; + } else { +/* automagically switch socket when congestion stall occurs */ + if (sock->use_pgmcc && sock->tokens < pgm_fp8 (1)) + enable_ack_socket = TRUE; + else + enable_send_socket = TRUE; + } + + if (enable_ack_socket) + { +/* rx thread poll for ACK */ + event.events = EPOLLIN | (events & (EPOLLONESHOT)); + event.data.ptr = sock; + retval = epoll_ctl (epfd, op, pgm_notify_get_fd (&sock->ack_notify), &event); + } + + if (enable_send_socket) + { +/* kernel resource poll */ + event.events = events & (EPOLLOUT | EPOLLET | EPOLLONESHOT); + event.data.ptr = sock; + retval = epoll_ctl (epfd, op, sock->send_sock, &event); + } + } +out: + return retval; +} +#endif + +static +const char* +pgm_family_string ( + const int family + ) +{ + const char* c; + + switch (family) { + case AF_UNSPEC: c = "AF_UNSPEC"; break; + case AF_INET: c = "AF_INET"; break; + case AF_INET6: c = "AF_INET6"; break; + default: c = "(unknown)"; break; + } + + return c; +} + +static +const char* +pgm_sock_type_string ( + const int sock_type + ) +{ + const char* c; + + switch (sock_type) { + case SOCK_SEQPACKET: c = "SOCK_SEQPACKET"; break; + default: c = "(unknown)"; break; + } + + return c; +} + +static +const char* +pgm_protocol_string ( + const int protocol + ) +{ + const char* c; + + switch (protocol) { + case IPPROTO_UDP: c = "IPPROTO_UDP"; break; + case IPPROTO_PGM: c = "IPPROTO_PGM"; break; + default: c = "(unknown)"; break; + } + + return c; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/socket_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/socket_unittest.c new file mode 100644 index 0000000..7f79f06 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/socket_unittest.c @@ -0,0 +1,1186 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for PGM socket. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* mock state */ + +#define TEST_NETWORK "" +#define TEST_PORT 7500 +#define TEST_MAX_TPDU 1500 +#define TEST_TXW_SQNS 32 +#define TEST_RXW_SQNS 32 +#define TEST_HOPS 16 +#define TEST_SPM_AMBIENT ( pgm_secs(30) ) +#define TEST_SPM_HEARTBEAT_INIT { pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(1300), pgm_secs(7), pgm_secs(16), pgm_secs(25), pgm_secs(30) } +#define TEST_PEER_EXPIRY ( pgm_secs(300) ) +#define TEST_SPMR_EXPIRY ( pgm_msecs(250) ) +#define TEST_NAK_BO_IVL ( pgm_msecs(50) ) +#define TEST_NAK_RPT_IVL ( pgm_secs(2) ) +#define TEST_NAK_RDATA_IVL ( pgm_secs(2) ) +#define TEST_NAK_DATA_RETRIES 5 +#define TEST_NAK_NCF_RETRIES 2 + +#define pgm_ipproto_pgm mock_pgm_ipproto_pgm +#define pgm_peer_unref mock_pgm_peer_unref +#define pgm_on_nak_notify mock_pgm_on_nak_notify +#define pgm_send_spm mock_pgm_send_spm +#define pgm_timer_prepare mock_pgm_timer_prepare +#define pgm_timer_check mock_pgm_timer_check +#define pgm_timer_expiration mock_pgm_timer_expiration +#define pgm_timer_dispatch mock_pgm_timer_dispatch +#define pgm_txw_create mock_pgm_txw_create +#define pgm_txw_shutdown mock_pgm_txw_shutdown +#define pgm_rate_create mock_pgm_rate_create +#define pgm_rate_destroy mock_pgm_rate_destroy +#define pgm_rate_remaining mock_pgm_rate_remaining +#define pgm_rs_create mock_pgm_rs_create +#define pgm_rs_destroy mock_pgm_rs_destroy +#define pgm_time_update_now mock_pgm_time_update_now + +#define SOCK_DEBUG +#include "socket.c" + +int mock_pgm_ipproto_pgm = IPPROTO_PGM; + + +static +void +mock_setup (void) +{ + if (!g_thread_supported ()) g_thread_init (NULL); +} + +static +void +mock_teardown (void) +{ +} + +/* stock create pgm sockaddr structure for calls to pgm_bind() + */ + +static +struct pgm_sockaddr_t* +generate_asm_sockaddr (void) +{ + const pgm_gsi_t gsi = { 200, 202, 203, 204, 205, 206 }; + struct pgm_sockaddr_t* pgmsa = g_new0 (struct pgm_sockaddr_t, 1); + pgmsa->sa_port = 123; + memcpy (&pgmsa->sa_addr.gsi, &gsi, sizeof(gsi)); + return pgmsa; +} + +/* stock create unconnected socket for pgm_setsockopt(), etc. + */ + +static +struct pgm_sock_t* +generate_sock (void) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, g_htons(1000) }; + struct pgm_sock_t* sock = g_new0 (struct pgm_sock_t, 1); + memcpy (&sock->tsi, &tsi, sizeof(pgm_tsi_t)); + ((struct sockaddr*)&sock->send_addr)->sa_family = AF_INET; + ((struct sockaddr_in*)&sock->send_addr)->sin_addr.s_addr = inet_addr ("127.0.0.2"); + ((struct sockaddr*)&sock->send_gsr.gsr_group)->sa_family = AF_INET; + ((struct sockaddr_in*)&sock->send_gsr.gsr_group)->sin_addr.s_addr = inet_addr ("239.192.0.1"); + sock->dport = g_htons(TEST_PORT); + sock->window = g_malloc0 (sizeof(pgm_txw_t)); + sock->txw_sqns = TEST_TXW_SQNS; + sock->max_tpdu = TEST_MAX_TPDU; + sock->max_tsdu = TEST_MAX_TPDU - sizeof(struct pgm_ip) - pgm_pkt_offset (FALSE, FALSE); + sock->max_tsdu_fragment = TEST_MAX_TPDU - sizeof(struct pgm_ip) - pgm_pkt_offset (TRUE, FALSE); + sock->max_apdu = MIN(TEST_TXW_SQNS, PGM_MAX_FRAGMENTS) * sock->max_tsdu_fragment; + sock->iphdr_len = sizeof(struct pgm_ip); + sock->spm_heartbeat_interval = g_malloc0 (sizeof(guint) * (2+2)); + sock->spm_heartbeat_interval[0] = pgm_secs(1); + pgm_spinlock_init (&sock->txw_spinlock); + sock->is_bound = FALSE; + sock->is_connected = FALSE; + sock->is_destroyed = FALSE; + sock->is_reset = FALSE; + return sock; +} + +/** receiver module */ +PGM_GNUC_INTERNAL +void +mock_pgm_peer_unref ( + pgm_peer_t* peer + ) +{ +} + +/** source module */ +static +bool +mock_pgm_on_nak_notify ( + GIOChannel* source, + GIOCondition condition, + gpointer data + ) +{ + return TRUE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_send_spm ( + pgm_sock_t* sock, + int flags + ) +{ + return TRUE; +} + +/** timer module */ +PGM_GNUC_INTERNAL +bool +mock_pgm_timer_prepare ( + pgm_sock_t* const sock + ) +{ + return FALSE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_timer_check ( + pgm_sock_t* const sock + ) +{ + return FALSE; +} + +PGM_GNUC_INTERNAL +pgm_time_t +mock_pgm_timer_expiration ( + pgm_sock_t* const sock + ) +{ + return 100L; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_timer_dispatch ( + pgm_sock_t* const sock + ) +{ + return TRUE; +} + +/** transmit window module */ +pgm_txw_t* +mock_pgm_txw_create ( + const pgm_tsi_t* const tsi, + const uint16_t tpdu_size, + const uint32_t sqns, + const unsigned secs, + const ssize_t max_rte, + const bool use_fec, + const uint8_t rs_n, + const uint8_t rs_k + ) +{ + pgm_txw_t* window = g_new0 (pgm_txw_t, 1); + return window; +} + +void +mock_pgm_txw_shutdown ( + pgm_txw_t* const window + ) +{ + g_free (window); +} + +/** rate control module */ +PGM_GNUC_INTERNAL +void +mock_pgm_rate_create ( + pgm_rate_t* bucket, + ssize_t rate_per_sec, + size_t iphdr_len, + uint16_t max_tpdu + ) +{ +} + +PGM_GNUC_INTERNAL +void +mock_pgm_rate_destroy ( + pgm_rate_t* bucket + ) +{ +} + +PGM_GNUC_INTERNAL +pgm_time_t +mock_pgm_rate_remaining ( + pgm_rate_t* bucket, + gsize packetlen + ) +{ + return 0; +} + +/** reed solomon module */ +void +mock_pgm_rs_create ( + pgm_rs_t* rs, + const uint8_t n, + const uint8_t k + ) +{ +} + +void +mock_pgm_rs_destroy ( + pgm_rs_t* rs + ) +{ +} + +/** time module */ +static pgm_time_t _mock_pgm_time_update_now (void); +pgm_time_update_func mock_pgm_time_update_now = _mock_pgm_time_update_now; + +static +pgm_time_t +_mock_pgm_time_update_now (void) +{ + return 0x1; +} + + +/* mock functions for external references */ + + +/* target: + * bool + * pgm_socket ( + * pgm_sock_t** sock, + * const sa_family_t family, + * const int pgm_sock_type, + * const int protocol, + * pgm_error_t** error + * ) + */ + +START_TEST (test_create_pass_001) +{ + pgm_error_t* err = NULL; + pgm_sock_t* sock; +/* only one type currently implemented */ + const int pgm_sock_type = SOCK_SEQPACKET; +/* PGM/IPv4 */ + sock = NULL; + fail_unless (TRUE == pgm_socket (&sock, AF_INET, pgm_sock_type, IPPROTO_PGM, &err), "create failed"); +/* PGM/UDP over IPv4 */ + sock = NULL; + fail_unless (TRUE == pgm_socket (&sock, AF_INET, pgm_sock_type, IPPROTO_UDP, &err), "create failed"); +/* PGM/IPv6 */ + sock = NULL; + fail_unless (TRUE == pgm_socket (&sock, AF_INET6, pgm_sock_type, IPPROTO_PGM, &err), "create failed"); +/* PGM/UDP over IPv6 */ + sock = NULL; + fail_unless (TRUE == pgm_socket (&sock, AF_INET6, pgm_sock_type, IPPROTO_UDP, &err), "create failed"); + fail_unless (NULL == err, "error raised"); +} +END_TEST + +/* NULL socket */ +START_TEST (test_create_fail_002) +{ + pgm_error_t* err = NULL; + fail_unless (FALSE == pgm_socket (NULL, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); +} +END_TEST + +/* invalid protocol family */ +START_TEST (test_create_fail_003) +{ + pgm_error_t* err = NULL; + pgm_sock_t* sock = NULL; + fail_unless (FALSE == pgm_socket (&sock, AF_UNSPEC, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); +} +END_TEST + +/* invalid socket type */ +START_TEST (test_create_fail_004) +{ + pgm_error_t* err = NULL; + pgm_sock_t* sock = NULL; + fail_unless (FALSE == pgm_socket (&sock, AF_INET, SOCK_STREAM, IPPROTO_PGM, &err), "create failed"); + fail_unless (FALSE == pgm_socket (&sock, AF_INET, SOCK_DGRAM, IPPROTO_PGM, &err), "create failed"); +} +END_TEST + +/* invalid protocol */ +START_TEST (test_create_fail_005) +{ + pgm_error_t* err = NULL; + pgm_sock_t* sock = NULL; + fail_unless (FALSE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_TCP, &err), "create failed"); +} +END_TEST + + +/* target: + * bool + * pgm_bind ( + * pgm_sock_t* sock, + * const struct pgm_sockaddr_t* sockaddr, + * const socklen_t sockaddrlen, + * pgm_error_t** error + * ) + */ + +START_TEST (test_bind_pass_001) +{ + pgm_error_t* err = NULL; + pgm_sock_t* sock = NULL; + struct pgm_sockaddr_t* pgmsa = generate_asm_sockaddr (); + fail_if (NULL == pgmsa, "generate_asm_sockaddr failed"); + fail_unless (TRUE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); + fail_unless (NULL == err, "error raised"); + fail_unless (TRUE == pgm_bind (sock, pgmsa, sizeof(*pgmsa), &err), "bind failed"); +} +END_TEST + +/* fail on unset options */ +START_TEST (test_bind_fail_001) +{ + pgm_error_t* err = NULL; + pgm_sock_t* sock = NULL; + struct pgm_sockaddr_t* pgmsa = generate_asm_sockaddr (); + fail_if (NULL == pgmsa, "generate_asm_sockaddr failed"); + fail_unless (TRUE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); + fail_unless (NULL == err, "error raised"); + fail_unless (FALSE == pgm_bind (sock, pgmsa, sizeof(*pgmsa), &err), "bind failed"); +} +END_TEST + +/* invalid parameters */ +START_TEST (test_bind_fail_002) +{ + pgm_error_t* err = NULL; + fail_unless (FALSE == pgm_bind (NULL, NULL, 0, &err), "bind failed"); +} +END_TEST + +/* target: + * bool + * pgm_bind3 ( + * pgm_sock_t* sock, + * const struct pgm_sockaddr_t* sockaddr, + * const socklen_t sockaddrlen, + * const struct pgm_interface_req_t* send_req, + * const socklen_t send_req_len, + * const struct pgm_interface_req_t* recv_req, + * const socklen_t recv_req_len, + * pgm_error_t** error + * ) + */ + +/* fail on unset options */ +START_TEST (test_bind3_fail_001) +{ + pgm_error_t* err = NULL; + pgm_sock_t* sock = NULL; + struct pgm_sockaddr_t* pgmsa = generate_asm_sockaddr (); + fail_if (NULL == pgmsa, "generate_asm_sockaddr failed"); + fail_unless (TRUE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); + fail_unless (NULL == err, "error raised"); + struct pgm_interface_req_t send_req = { .ir_interface = 0, .ir_scope_id = 0 }, + recv_req = { .ir_interface = 0, .ir_scope_id = 0 }; + fail_unless (FALSE == pgm_bind3 (sock, + pgmsa, sizeof(*pgmsa), + &send_req, sizeof(send_req), + &recv_req, sizeof(recv_req), + &err), "bind failed"); +} +END_TEST + +/* invalid parameters */ +START_TEST (test_bind3_fail_002) +{ + pgm_error_t* err = NULL; + fail_unless (FALSE == pgm_bind3 (NULL, NULL, 0, NULL, 0, NULL, 0, &err), "bind failed"); +} +END_TEST + +/* target: + * bool + * pgm_connect ( + * pgm_sock_t* sock, + * pgm_error_t** error + * ) + */ + +START_TEST (test_connect_pass_001) +{ + pgm_error_t* err = NULL; + pgm_sock_t* sock = NULL; + struct pgm_sockaddr_t* pgmsa = generate_asm_sockaddr (); + fail_if (NULL == pgmsa, "generate_asm_sockaddr failed"); + fail_unless (TRUE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); + fail_unless (NULL == err, "error raised"); + fail_unless (TRUE == pgm_bind (sock, pgmsa, sizeof(*pgmsa), &err), "bind failed"); + fail_unless (TRUE == pgm_connect (sock, &err), "connect failed"); +} +END_TEST + +/* invalid parameters */ +START_TEST (test_connect_fail_001) +{ + pgm_error_t* err = NULL; + fail_unless (FALSE == pgm_connect (NULL, &err), "connect failed"); +} +END_TEST + +/* target: + * bool + * pgm_close ( + * pgm_sock_t* sock, + * bool flush + * ) + */ + +/* socket > close */ +START_TEST (test_destroy_pass_001) +{ + pgm_error_t* err = NULL; + pgm_sock_t* sock = NULL; + fail_unless (TRUE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); + fail_unless (TRUE == pgm_close (sock, FALSE), "destroy failed"); +} +END_TEST + +/* socket > bind > close */ +START_TEST (test_destroy_pass_002) +{ + pgm_error_t* err = NULL; + pgm_sock_t* sock = NULL; + struct pgm_sockaddr_t* pgmsa = generate_asm_sockaddr (); + fail_if (NULL == pgmsa, "generate_asm_sockaddr failed"); + fail_unless (TRUE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); + fail_unless (NULL == err, "error raised"); + fail_unless (TRUE == pgm_bind (sock, pgmsa, sizeof(*pgmsa), &err), "bind failed"); + fail_unless (TRUE == pgm_close (sock, FALSE), "destroy failed"); +} +END_TEST + +/* socket > bind > connect > close */ +START_TEST (test_destroy_pass_003) +{ + pgm_error_t* err = NULL; + pgm_sock_t* sock = NULL; + struct pgm_sockaddr_t* pgmsa = generate_asm_sockaddr (); + fail_if (NULL == pgmsa, "generate_asm_sockaddr failed"); + fail_unless (TRUE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); + fail_unless (NULL == err, "error raised"); + fail_unless (TRUE == pgm_bind (sock, pgmsa, sizeof(*pgmsa), &err), "bind failed"); + fail_unless (TRUE == pgm_connect (sock, &err), "connect failed"); + fail_unless (TRUE == pgm_close (sock, FALSE), "destroy failed"); +} +END_TEST + +/* invalid parameters */ +START_TEST (test_destroy_fail_001) +{ + fail_unless (FALSE == pgm_close (NULL, FALSE), "destroy failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_MAX_TPDU, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_max_tpdu_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int optname = PGM_MTU; + const int max_tpdu = 1500; + const void* optval = &max_tpdu; + const socklen_t optlen = sizeof(max_tpdu); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_max_tpdu failed"); +} +END_TEST + +/* invalid parameters */ +START_TEST (test_set_max_tpdu_fail_001) +{ + const int optname = PGM_MTU; + const int max_tpdu = 1500; + const void* optval = &max_tpdu; + const socklen_t optlen = sizeof(max_tpdu); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_max_tpdu failed"); +} +END_TEST + +/* too small */ +START_TEST (test_set_max_tpdu_fail_002) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int optname = PGM_MTU; + const int max_tpdu = 1; + const void* optval = &max_tpdu; + const socklen_t optlen = sizeof(max_tpdu); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_max_tpdu failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_MULTICAST_LOOP, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_multicast_loop_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int optname = PGM_MULTICAST_LOOP; + const int loop_enabled = 1; + const void* optval = &loop_enabled; + const socklen_t optlen = sizeof(loop_enabled); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_multicast_loop failed"); +} +END_TEST + +START_TEST (test_set_multicast_loop_fail_001) +{ + const int optname = PGM_MULTICAST_LOOP; + const int loop_enabled = 1; + const void* optval = &loop_enabled; + const socklen_t optlen = sizeof(loop_enabled); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_multicast_loop failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_MULTICAST_HOPS, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_hops_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int optname = PGM_MULTICAST_HOPS; + const int hops = 16; + const void* optval = &hops; + const socklen_t optlen = sizeof(hops); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_hops failed"); +} +END_TEST + +START_TEST (test_set_hops_fail_001) +{ + const int optname = PGM_MULTICAST_HOPS; + const int hops = 16; + const void* optval = &hops; + const socklen_t optlen = sizeof(hops); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_hops failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_SNDBUF, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_sndbuf_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int optname = PGM_SNDBUF; + const int bufsize = 131071; /* 128kB */ + const void* optval = &bufsize; + const socklen_t optlen = sizeof(bufsize); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_sndbuf failed"); +} +END_TEST + +START_TEST (test_set_sndbuf_fail_001) +{ + const int optname = PGM_SNDBUF; + const int bufsize = 131071; /* 128kB */ + const void* optval = &bufsize; + const socklen_t optlen = sizeof(bufsize); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_sndbuf failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_RCVBUF, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_rcvbuf_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int optname = PGM_RCVBUF; + const int bufsize = 131071; /* 128kB */ + const void* optval = &bufsize; + const socklen_t optlen = sizeof(bufsize); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_rcvbuf failed"); +} +END_TEST + +START_TEST (test_set_rcvbuf_fail_001) +{ + const int optname = PGM_RCVBUF; + const int bufsize = 131071; /* 128kB */ + const void* optval = &bufsize; + const socklen_t optlen = sizeof(bufsize); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_rcvbuf failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_USE_FEC, + * const void* optval, + * const socklen_t optlen = sizeof(struct pgm_fecinfo_t) + * ) + */ + +START_TEST (test_set_fec_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int optname = PGM_USE_FEC; + const struct pgm_fecinfo_t fecinfo = { + .ondemand_parity_enabled = TRUE, + .proactive_packets = 16, + .var_pktlen_enabled = TRUE, + .block_size = 255, + .group_size = 239 + }; + const void* optval = &fecinfo; + const socklen_t optlen = sizeof(fecinfo); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_fec failed"); +} +END_TEST + +START_TEST (test_set_fec_fail_001) +{ + const int optname = PGM_USE_FEC; + const struct pgm_fecinfo_t fecinfo = { + .ondemand_parity_enabled = TRUE, + .proactive_packets = 16, + .var_pktlen_enabled = TRUE, + .block_size = 255, + .group_size = 239 + }; + const void* optval = &fecinfo; + const socklen_t optlen = sizeof(fecinfo); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_fec failed"); +} +END_TEST + +/* TODO: invalid Reed-Solomon parameters + */ + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_USE_PGMCC, + * const void* optval, + * const socklen_t optlen = sizeof(struct pgm_pgmccinfo_t) + * ) + */ + +START_TEST (test_set_pgmcc_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int optname = PGM_USE_PGMCC; + const struct pgm_pgmccinfo_t pgmccinfo = { + .ack_bo_ivl = pgm_msecs(100), + .ack_c = 123, + .ack_c_p = 456 + }; + const void* optval = &pgmccinfo; + const socklen_t optlen = sizeof(pgmccinfo); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_pgmcc failed"); +} +END_TEST + +START_TEST (test_set_pgmcc_fail_001) +{ + const int optname = PGM_USE_PGMCC; + const struct pgm_pgmccinfo_t pgmccinfo = { + .ack_bo_ivl = pgm_msecs(100), + .ack_c = 123, + .ack_c_p = 456 + }; + const void* optval = &pgmccinfo; + const socklen_t optlen = sizeof(pgmccinfo); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_pgmcc failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_USE_CR, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_cr_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int optname = PGM_USE_CR; + const int magic_bunny = 1; + const void* optval = &magic_bunny; + const socklen_t optlen = sizeof(magic_bunny); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_cr failed"); +} +END_TEST + +START_TEST (test_set_cr_fail_001) +{ + const int optname = PGM_USE_CR; + const int magic_bunny = 1; + const void* optval = &magic_bunny; + const socklen_t optlen = sizeof(magic_bunny); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_cr failed"); +} +END_TEST + + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_SEND_ONLY, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_send_only_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int optname = PGM_SEND_ONLY; + const int send_only = 1; + const void* optval = &send_only; + const socklen_t optlen = sizeof(send_only); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_send_only failed"); +} +END_TEST + +START_TEST (test_set_send_only_fail_001) +{ + const int optname = PGM_SEND_ONLY; + const int send_only = 1; + const void* optval = &send_only; + const socklen_t optlen = sizeof(send_only); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_send_only failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_RECV_ONLY, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_recv_only_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int optname = PGM_RECV_ONLY; + const int recv_only = 1; + const void* optval = &recv_only; + const socklen_t optlen = sizeof(recv_only); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_recv_only failed"); +} +END_TEST + +START_TEST (test_set_recv_only_fail_001) +{ + const int optname = PGM_RECV_ONLY; + const int recv_only = 1; + const void* optval = &recv_only; + const socklen_t optlen = sizeof(recv_only); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_recv_only failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_PASSIVE, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_recv_only_pass_002) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int optname = PGM_PASSIVE; + const int passive = 1; + const void* optval = &passive; + const socklen_t optlen = sizeof(passive); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_passive failed"); +} +END_TEST + +START_TEST (test_set_recv_only_fail_002) +{ + const int optname = PGM_PASSIVE; + const int passive = 1; + const void* optval = &passive; + const socklen_t optlen = sizeof(passive); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_passive failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_ABORT_ON_RESET, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_abort_on_reset_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int optname = PGM_ABORT_ON_RESET; + const int abort_on_reset= 1; + const void* optval = &abort_on_reset; + const socklen_t optlen = sizeof(abort_on_reset); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_abort_on_reset failed"); +} +END_TEST + +START_TEST (test_set_abort_on_reset_fail_001) +{ + const int optname = PGM_ABORT_ON_RESET; + const int abort_on_reset= 1; + const void* optval = &abort_on_reset; + const socklen_t optlen = sizeof(abort_on_reset); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_abort_on_reset failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_NOBLOCK, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_noblock_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int optname = PGM_NOBLOCK; + const int noblock = 1; + const void* optval = &noblock; + const socklen_t optlen = sizeof(noblock); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_noblock failed"); +} +END_TEST + +START_TEST (test_set_noblock_fail_001) +{ + const int optname = PGM_NOBLOCK; + const int noblock = 1; + const void* optval = &noblock; + const socklen_t optlen = sizeof(noblock); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_noblock failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_UDP_ENCAP_UCAST_PORT, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_udp_unicast_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int optname = PGM_NOBLOCK; + const int unicast_port = 10001; + const void* optval = &unicast_port; + const socklen_t optlen = sizeof(unicast_port); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_udp_unicast failed"); +} +END_TEST + +START_TEST (test_set_udp_unicast_fail_001) +{ + const int optname = PGM_NOBLOCK; + const int unicast_port = 10001; + const void* optval = &unicast_port; + const socklen_t optlen = sizeof(unicast_port); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_udp_unicast failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_UDP_ENCAP_MCAST_PORT, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_udp_multicast_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int optname = PGM_NOBLOCK; + const int multicast_port= 10001; + const void* optval = &multicast_port; + const socklen_t optlen = sizeof(multicast_port); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_udp_multicast failed"); +} +END_TEST + +START_TEST (test_set_udp_multicast_fail_001) +{ + const int optname = PGM_NOBLOCK; + const int multicast_port= 10002; + const void* optval = &multicast_port; + const socklen_t optlen = sizeof(multicast_port); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_udp_multicast failed"); +} +END_TEST + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_create = tcase_create ("create"); + suite_add_tcase (s, tc_create); + tcase_add_checked_fixture (tc_create, mock_setup, mock_teardown); + tcase_add_test (tc_create, test_create_pass_001); + tcase_add_test (tc_create, test_create_fail_002); + tcase_add_test (tc_create, test_create_fail_003); + tcase_add_test (tc_create, test_create_fail_004); + tcase_add_test (tc_create, test_create_fail_005); + + TCase* tc_bind = tcase_create ("bind"); + suite_add_tcase (s, tc_bind); + tcase_add_checked_fixture (tc_bind, mock_setup, mock_teardown); + tcase_add_test (tc_bind, test_bind_fail_001); + tcase_add_test (tc_bind, test_bind_fail_002); + + TCase* tc_connect = tcase_create ("connect"); + suite_add_tcase (s, tc_connect); + tcase_add_checked_fixture (tc_connect, mock_setup, mock_teardown); + tcase_add_test (tc_connect, test_connect_pass_001); + tcase_add_test (tc_connect, test_connect_fail_001); + + TCase* tc_destroy = tcase_create ("destroy"); + suite_add_tcase (s, tc_destroy); + tcase_add_checked_fixture (tc_destroy, mock_setup, mock_teardown); + tcase_add_test (tc_destroy, test_destroy_pass_001); + tcase_add_test (tc_destroy, test_destroy_fail_001); + + TCase* tc_set_max_tpdu = tcase_create ("set-max-tpdu"); + suite_add_tcase (s, tc_set_max_tpdu); + tcase_add_checked_fixture (tc_set_max_tpdu, mock_setup, mock_teardown); + tcase_add_test (tc_set_max_tpdu, test_set_max_tpdu_pass_001); + tcase_add_test (tc_set_max_tpdu, test_set_max_tpdu_fail_001); + + TCase* tc_set_multicast_loop = tcase_create ("set-multicast-loop"); + suite_add_tcase (s, tc_set_multicast_loop); + tcase_add_checked_fixture (tc_set_multicast_loop, mock_setup, mock_teardown); + tcase_add_test (tc_set_multicast_loop, test_set_multicast_loop_pass_001); + tcase_add_test (tc_set_multicast_loop, test_set_multicast_loop_fail_001); + + TCase* tc_set_hops = tcase_create ("set-hops"); + suite_add_tcase (s, tc_set_hops); + tcase_add_checked_fixture (tc_set_hops, mock_setup, mock_teardown); + tcase_add_test (tc_set_hops, test_set_hops_pass_001); + tcase_add_test (tc_set_hops, test_set_hops_fail_001); + + TCase* tc_set_sndbuf = tcase_create ("set-sndbuf"); + suite_add_tcase (s, tc_set_sndbuf); + tcase_add_checked_fixture (tc_set_sndbuf, mock_setup, mock_teardown); + tcase_add_test (tc_set_sndbuf, test_set_sndbuf_pass_001); + tcase_add_test (tc_set_sndbuf, test_set_sndbuf_fail_001); + + TCase* tc_set_rcvbuf = tcase_create ("set-rcvbuf"); + suite_add_tcase (s, tc_set_rcvbuf); + tcase_add_checked_fixture (tc_set_rcvbuf, mock_setup, mock_teardown); + tcase_add_test (tc_set_rcvbuf, test_set_rcvbuf_pass_001); + tcase_add_test (tc_set_rcvbuf, test_set_rcvbuf_fail_001); + + TCase* tc_set_fec = tcase_create ("set-fec"); + suite_add_tcase (s, tc_set_fec); + tcase_add_checked_fixture (tc_set_fec, mock_setup, mock_teardown); + tcase_add_test (tc_set_fec, test_set_fec_pass_001); + tcase_add_test (tc_set_fec, test_set_fec_fail_001); + + TCase* tc_set_pgmcc = tcase_create ("set-pgmcc"); + suite_add_tcase (s, tc_set_pgmcc); + tcase_add_checked_fixture (tc_set_pgmcc, mock_setup, mock_teardown); + tcase_add_test (tc_set_pgmcc, test_set_pgmcc_pass_001); + tcase_add_test (tc_set_pgmcc, test_set_pgmcc_fail_001); + + TCase* tc_set_cr = tcase_create ("set-cr"); + suite_add_tcase (s, tc_set_cr); + tcase_add_checked_fixture (tc_set_cr, mock_setup, mock_teardown); + tcase_add_test (tc_set_cr, test_set_cr_pass_001); + tcase_add_test (tc_set_cr, test_set_cr_fail_001); + + TCase* tc_set_send_only = tcase_create ("set-send-only"); + suite_add_tcase (s, tc_set_send_only); + tcase_add_checked_fixture (tc_set_send_only, mock_setup, mock_teardown); + tcase_add_test (tc_set_send_only, test_set_send_only_pass_001); + tcase_add_test (tc_set_send_only, test_set_send_only_fail_001); + + TCase* tc_set_recv_only = tcase_create ("set-recv-only"); + suite_add_tcase (s, tc_set_recv_only); + tcase_add_checked_fixture (tc_set_recv_only, mock_setup, mock_teardown); + tcase_add_test (tc_set_recv_only, test_set_recv_only_pass_001); + tcase_add_test (tc_set_recv_only, test_set_recv_only_pass_002); + tcase_add_test (tc_set_recv_only, test_set_recv_only_fail_001); + tcase_add_test (tc_set_recv_only, test_set_recv_only_fail_002); + + TCase* tc_set_abort_on_reset = tcase_create ("set-abort-on-reset"); + suite_add_tcase (s, tc_set_abort_on_reset); + tcase_add_checked_fixture (tc_set_abort_on_reset, mock_setup, mock_teardown); + tcase_add_test (tc_set_abort_on_reset, test_set_abort_on_reset_pass_001); + tcase_add_test (tc_set_abort_on_reset, test_set_abort_on_reset_fail_001); + + TCase* tc_set_noblock = tcase_create ("set-non-blocking"); + suite_add_tcase (s, tc_set_noblock); + tcase_add_checked_fixture (tc_set_noblock, mock_setup, mock_teardown); + tcase_add_test (tc_set_noblock, test_set_noblock_pass_001); + tcase_add_test (tc_set_noblock, test_set_noblock_fail_001); + + TCase* tc_set_udp_unicast = tcase_create ("set-udp-encap-ucast-port"); + suite_add_tcase (s, tc_set_udp_unicast); + tcase_add_checked_fixture (tc_set_udp_unicast, mock_setup, mock_teardown); + tcase_add_test (tc_set_udp_unicast, test_set_udp_unicast_pass_001); + tcase_add_test (tc_set_udp_unicast, test_set_udp_unicast_fail_001); + + TCase* tc_set_udp_multicast = tcase_create ("set-udp-encap-mcast-port"); + suite_add_tcase (s, tc_set_udp_multicast); + tcase_add_checked_fixture (tc_set_udp_multicast, mock_setup, mock_teardown); + tcase_add_test (tc_set_udp_multicast, test_set_udp_multicast_pass_001); + tcase_add_test (tc_set_udp_multicast, test_set_udp_multicast_fail_001); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/source.c b/3rdparty/openpgm-svn-r1085/pgm/source.c new file mode 100644 index 0000000..12a61b6 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/source.c @@ -0,0 +1,2339 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM source socket. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +//#define SOURCE_DEBUG + +#ifndef SOURCE_DEBUG +# define PGM_DISABLE_ASSERT +#endif + +#if !defined(ENOBUFS) && defined(WSAENOBUFS) +# define ENOBUFS WSAENOBUFS +#endif + + +/* locals */ +static inline bool peer_is_source (const pgm_peer_t*) PGM_GNUC_CONST; +static inline bool peer_is_peer (const pgm_peer_t*) PGM_GNUC_CONST; +static void reset_heartbeat_spm (pgm_sock_t*const, const pgm_time_t); +static bool send_ncf (pgm_sock_t*const restrict, const struct sockaddr*const restrict, const struct sockaddr*const restrict, const uint32_t, const bool); +static bool send_ncf_list (pgm_sock_t*const restrict, const struct sockaddr*const restrict, const struct sockaddr*restrict, struct pgm_sqn_list_t*const restrict, const bool); +static int send_odata (pgm_sock_t*const restrict, struct pgm_sk_buff_t*const restrict, size_t*restrict); +static int send_odata_copy (pgm_sock_t*const restrict, const void*restrict, const uint16_t, size_t*restrict); +static int send_odatav (pgm_sock_t*const restrict, const struct pgm_iovec*const restrict, const unsigned, size_t*restrict); +static bool send_rdata (pgm_sock_t*const restrict, struct pgm_sk_buff_t*const restrict); + + +static inline +unsigned +_pgm_popcount ( + uint32_t n + ) +{ +#if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) + return __builtin_popcount (n); +#else +/* MIT HAKMEM 169 */ + const uint32_t t = n - ((n >> 1) & 033333333333) + - ((n >> 2) & 011111111111); + return ((t + (t >> 3) & 030707070707)) % 63; +#endif +} + +static inline +bool +peer_is_source ( + const pgm_peer_t* peer + ) +{ + return (NULL == peer); +} + +static inline +bool +peer_is_peer ( + const pgm_peer_t* peer + ) +{ + return (NULL != peer); +} + +static inline +void +reset_spmr_timer ( + pgm_peer_t* const peer + ) +{ + peer->spmr_expiry = 0; +} + +static inline +size_t +source_max_tsdu ( + const pgm_sock_t* sock, + const bool can_fragment + ) +{ + size_t max_tsdu = can_fragment ? sock->max_tsdu_fragment : sock->max_tsdu; + if (sock->use_var_pktlen /* OPT_VAR_PKT_LEN */) + max_tsdu -= sizeof (uint16_t); + return max_tsdu; +} + +/* prototype of function to send pro-active parity NAKs. + */ +static +bool +pgm_schedule_proactive_nak ( + pgm_sock_t* sock, + uint32_t nak_tg_sqn /* transmission group (shifted) */ + ) +{ + pgm_return_val_if_fail (NULL != sock, FALSE); + const bool status = pgm_txw_retransmit_push (sock->window, + nak_tg_sqn | sock->rs_proactive_h, + TRUE /* is_parity */, + sock->tg_sqn_shift); + return status; +} + +/* a deferred request for RDATA, now processing in the timer thread, we check the transmit + * window to see if the packet exists and forward on, maintaining a lock until the queue is + * empty. + * + * returns TRUE on success, returns FALSE if operation would block. + */ + +bool +pgm_on_deferred_nak ( + pgm_sock_t* const sock + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + +/* We can flush queue and block all odata, or process one set, or process each + * sequence number individually. + */ + +/* parity packets are re-numbered across the transmission group with index h, sharing the space + * with the original packets. beyond the transmission group size (k), the PGM option OPT_PARITY_GRP + * provides the extra offset value. + */ + +/* peek from the retransmit queue so we can eliminate duplicate NAKs up until the repair packet + * has been retransmitted. + */ + pgm_spinlock_lock (&sock->txw_spinlock); + struct pgm_sk_buff_t* skb = pgm_txw_retransmit_try_peek (sock->window); + if (skb) { + skb = pgm_skb_get (skb); + pgm_spinlock_unlock (&sock->txw_spinlock); + if (!send_rdata (sock, skb)) { + pgm_free_skb (skb); + pgm_notify_send (&sock->rdata_notify); + return FALSE; + } + pgm_free_skb (skb); +/* now remove sequence number from retransmit queue, re-enabling NAK processing for this sequence number */ + pgm_txw_retransmit_remove_head (sock->window); + } else + pgm_spinlock_unlock (&sock->txw_spinlock); + return TRUE; +} + +/* SPMR indicates if multicast to cancel own SPMR, or unicast to send SPM. + * + * rate limited to 1/IHB_MIN per TSI (13.4). + * + * if SPMR was valid, returns TRUE, if invalid returns FALSE. + */ + +bool +pgm_on_spmr ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict peer, /* maybe NULL if socket is source */ + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + + pgm_debug ("pgm_on_spmr (sock:%p peer:%p skb:%p)", + (void*)sock, (void*)peer, (void*)skb); + + if (PGM_UNLIKELY(!pgm_verify_spmr (skb))) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed SPMR rejected.")); + return FALSE; + } + + if (peer_is_source (peer)) { + const bool send_status = pgm_send_spm (sock, 0); + if (PGM_UNLIKELY(!send_status)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Failed to send SPM on SPM-Request.")); + } + } else { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Suppressing SPMR due to peer multicast SPMR.")); + reset_spmr_timer (peer); + } + return TRUE; +} + +/* Process opt_pgmcc_feedback PGM option that ships attached to ACK or NAK. + * Contents use to elect best ACKer. + * + * returns TRUE if peer is the elected ACKer. + */ + +static +bool +on_opt_pgmcc_feedback ( + pgm_sock_t* const restrict sock, + const struct pgm_sk_buff_t* const restrict skb, + const struct pgm_opt_pgmcc_feedback* restrict opt_pgmcc_feedback + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + pgm_assert (NULL != opt_pgmcc_feedback); + + const uint32_t opt_tstamp = ntohl (opt_pgmcc_feedback->opt_tstamp); + const uint16_t opt_loss_rate = ntohs (opt_pgmcc_feedback->opt_loss_rate); + + const uint32_t rtt = pgm_to_msecs (skb->tstamp) - opt_tstamp; + const uint64_t peer_loss = rtt * rtt * opt_loss_rate; + + struct sockaddr_storage peer_nla; + pgm_nla_to_sockaddr (&opt_pgmcc_feedback->opt_nla_afi, (struct sockaddr*)&peer_nla); + +/* ACKer elections */ + if (PGM_UNLIKELY(pgm_sockaddr_is_addr_unspecified ((const struct sockaddr*)&sock->acker_nla))) + { + pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Elected first ACKer")); + memcpy (&sock->acker_nla, &peer_nla, pgm_sockaddr_storage_len (&peer_nla)); + } + else if (peer_loss > sock->acker_loss && + 0 != pgm_sockaddr_cmp ((const struct sockaddr*)&peer_nla, (const struct sockaddr*)&sock->acker_nla)) + { + pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Elected new ACKer")); + memcpy (&sock->acker_nla, &peer_nla, pgm_sockaddr_storage_len (&peer_nla)); + } + +/* update ACKer state */ + if (0 == pgm_sockaddr_cmp ((const struct sockaddr*)&peer_nla, (const struct sockaddr*)&sock->acker_nla)) + { + sock->acker_loss = peer_loss; + return TRUE; + } + + return FALSE; +} + +/* NAK requesting RDATA transmission for a sending sock, only valid if + * sequence number(s) still in transmission window. + * + * we can potentially have different IP versions for the NAK packet to the send group. + * + * TODO: fix IPv6 AFIs + * + * take in a NAK and pass off to an asynchronous queue for another thread to process + * + * if NAK is valid, returns TRUE. on error, FALSE is returned. + */ + +bool +pgm_on_nak ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + + pgm_debug ("pgm_on_nak (sock:%p skb:%p)", + (const void*)sock, (const void*)skb); + + const bool is_parity = skb->pgm_header->pgm_options & PGM_OPT_PARITY; + if (is_parity) { + sock->cumulative_stats[PGM_PC_SOURCE_PARITY_NAKS_RECEIVED]++; + if (!sock->use_ondemand_parity) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Parity NAK rejected as on-demand parity is not enabled.")); + sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; + return FALSE; + } + } else + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED]++; + + if (PGM_UNLIKELY(!pgm_verify_nak (skb))) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed NAK rejected.")); + sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; + return FALSE; + } + + const struct pgm_nak* nak = (struct pgm_nak*) skb->data; + const struct pgm_nak6* nak6 = (struct pgm_nak6*)skb->data; + +/* NAK_SRC_NLA contains our sock unicast NLA */ + struct sockaddr_storage nak_src_nla; + pgm_nla_to_sockaddr (&nak->nak_src_nla_afi, (struct sockaddr*)&nak_src_nla); + if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&nak_src_nla, (struct sockaddr*)&sock->send_addr) != 0)) + { + char saddr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&nak_src_nla, saddr, sizeof(saddr)); + pgm_trace (PGM_LOG_ROLE_NETWORK,_("NAK rejected for unmatched NLA: %s"), saddr); + sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; + return FALSE; + } + +/* NAK_GRP_NLA containers our sock multicast group */ + struct sockaddr_storage nak_grp_nla; + pgm_nla_to_sockaddr ((AF_INET6 == nak_src_nla.ss_family) ? &nak6->nak6_grp_nla_afi : &nak->nak_grp_nla_afi, (struct sockaddr*)&nak_grp_nla); + if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&nak_grp_nla, (struct sockaddr*)&sock->send_gsr.gsr_group) != 0)) + { + char sgroup[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&nak_src_nla, sgroup, sizeof(sgroup)); + pgm_trace (PGM_LOG_ROLE_NETWORK,_("NAK rejected as targeted for different multicast group: %s"), sgroup); + sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; + return FALSE; + } + +/* create queue object */ + struct pgm_sqn_list_t sqn_list; + sqn_list.sqn[0] = ntohl (nak->nak_sqn); + sqn_list.len = 1; + + pgm_debug ("nak_sqn %" PRIu32, sqn_list.sqn[0]); + +/* check NAK list */ + const uint32_t* nak_list = NULL; + uint_fast8_t nak_list_len = 0; + if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) + { + const struct pgm_opt_length* opt_len = (AF_INET6 == nak_src_nla.ss_family) ? + (const struct pgm_opt_length*)(nak6 + 1) : + (const struct pgm_opt_length*)(nak + 1); + if (PGM_UNLIKELY(opt_len->opt_type != PGM_OPT_LENGTH)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed NAK rejected.")); + sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; + return FALSE; + } + if (PGM_UNLIKELY(opt_len->opt_length != sizeof(struct pgm_opt_length))) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed NAK rejected.")); + sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; + return FALSE; + } +/* TODO: check for > 16 options & past packet end */ + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; + do { + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); + if ((opt_header->opt_type & PGM_OPT_MASK) == PGM_OPT_NAK_LIST) { + nak_list = ((const struct pgm_opt_nak_list*)(opt_header + 1))->opt_sqn; + nak_list_len = ( opt_header->opt_length - sizeof(struct pgm_opt_header) - sizeof(uint8_t) ) / sizeof(uint32_t); + break; + } + } while (!(opt_header->opt_type & PGM_OPT_END)); + } + +/* nak list numbers */ + if (PGM_UNLIKELY(nak_list_len > 63)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed NAK rejected on too long sequence list.")); + return FALSE; + } + + for (uint_fast8_t i = 0; i < nak_list_len; i++) + { + sqn_list.sqn[sqn_list.len++] = ntohl (*nak_list); + nak_list++; + } + +/* send NAK confirm packet immediately, then defer to timer thread for a.s.a.p + * delivery of the actual RDATA packets. blocking send for NCF is ignored as RDATA + * broadcast will be sent later. + */ + if (nak_list_len) + send_ncf_list (sock, (struct sockaddr*)&nak_src_nla, (struct sockaddr*)&nak_grp_nla, &sqn_list, is_parity); + else + send_ncf (sock, (struct sockaddr*)&nak_src_nla, (struct sockaddr*)&nak_grp_nla, sqn_list.sqn[0], is_parity); + +/* queue retransmit requests */ + for (uint_fast8_t i = 0; i < sqn_list.len; i++) { + const bool push_status = pgm_txw_retransmit_push (sock->window, sqn_list.sqn[i], is_parity, sock->tg_sqn_shift); + if (PGM_UNLIKELY(!push_status)) { + pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Failed to push retransmit request for #%" PRIu32), sqn_list.sqn[i]); + } + } + return TRUE; +} + +/* Null-NAK, or N-NAK propogated by a DLR for hand waving excitement + * + * if NNAK is valid, returns TRUE. on error, FALSE is returned. + */ + +bool +pgm_on_nnak ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + + pgm_debug ("pgm_on_nnak (sock:%p skb:%p)", + (void*)sock, (void*)skb); + + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAK_PACKETS_RECEIVED]++; + + if (PGM_UNLIKELY(!pgm_verify_nnak (skb))) { + sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]++; + return FALSE; + } + + const struct pgm_nak* nnak = (struct pgm_nak*) skb->data; + const struct pgm_nak6* nnak6 = (struct pgm_nak6*)skb->data; + +/* NAK_SRC_NLA contains our sock unicast NLA */ + struct sockaddr_storage nnak_src_nla; + pgm_nla_to_sockaddr (&nnak->nak_src_nla_afi, (struct sockaddr*)&nnak_src_nla); + + if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&nnak_src_nla, (struct sockaddr*)&sock->send_addr) != 0)) + { + sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]++; + return FALSE; + } + +/* NAK_GRP_NLA containers our sock multicast group */ + struct sockaddr_storage nnak_grp_nla; + pgm_nla_to_sockaddr ((AF_INET6 == nnak_src_nla.ss_family) ? &nnak6->nak6_grp_nla_afi : &nnak->nak_grp_nla_afi, (struct sockaddr*)&nnak_grp_nla); + if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&nnak_grp_nla, (struct sockaddr*)&sock->send_gsr.gsr_group) != 0)) + { + sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]++; + return FALSE; + } + +/* check NNAK list */ + uint_fast8_t nnak_list_len = 0; + if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) + { + const struct pgm_opt_length* opt_len = (AF_INET6 == nnak_src_nla.ss_family) ? + (const struct pgm_opt_length*)(nnak6 + 1) : + (const struct pgm_opt_length*)(nnak + 1); + if (PGM_UNLIKELY(opt_len->opt_type != PGM_OPT_LENGTH)) { + sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]++; + return FALSE; + } + if (PGM_UNLIKELY(opt_len->opt_length != sizeof(struct pgm_opt_length))) { + sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]++; + return FALSE; + } +/* TODO: check for > 16 options & past packet end */ + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; + do { + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); + if ((opt_header->opt_type & PGM_OPT_MASK) == PGM_OPT_NAK_LIST) { + nnak_list_len = ( opt_header->opt_length - sizeof(struct pgm_opt_header) - sizeof(uint8_t) ) / sizeof(uint32_t); + break; + } + } while (!(opt_header->opt_type & PGM_OPT_END)); + } + + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAKS_RECEIVED] += 1 + nnak_list_len; + return TRUE; +} + +/* ACK, sent upstream by one selected ACKER for congestion control feedback. + * + * if ACK is valid, returns TRUE. on error, FALSE is returned. + */ + +bool +pgm_on_ack ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + + pgm_debug ("pgm_on_ack (sock:%p skb:%p)", + (const void*)sock, (const void*)skb); + + sock->cumulative_stats[PGM_PC_SOURCE_ACK_PACKETS_RECEIVED]++; + + if (PGM_UNLIKELY(!pgm_verify_ack (skb))) { + sock->cumulative_stats[PGM_PC_SOURCE_ACK_ERRORS]++; + return FALSE; + } + + if (!sock->use_pgmcc) + return FALSE; + + const struct pgm_ack* ack = (struct pgm_ack*)skb->data; + bool is_acker = FALSE; + +/* check PGMCC feedback option for new elections */ + if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) + { + const struct pgm_opt_length* opt_len = (const struct pgm_opt_length*)(ack + 1); + if (PGM_UNLIKELY(opt_len->opt_type != PGM_OPT_LENGTH)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed ACK rejected.")); + return FALSE; + } + if (PGM_UNLIKELY(opt_len->opt_length != sizeof(struct pgm_opt_length))) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed ACK rejected.")); + return FALSE; + } + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; + do { + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); + if ((opt_header->opt_type & PGM_OPT_MASK) == PGM_OPT_PGMCC_FEEDBACK) { + const struct pgm_opt_pgmcc_feedback* opt_pgmcc_feedback = (const struct pgm_opt_pgmcc_feedback*)(opt_header + 1); + is_acker = on_opt_pgmcc_feedback (sock, skb, opt_pgmcc_feedback); + break; /* ignore other options */ + } + } while (!(opt_header->opt_type & PGM_OPT_END)); + } + +/* ignore ACKs from other receivers or sessions */ + if (!is_acker) + return TRUE; + +/* reset ACK expiration */ + sock->next_crqst = 0; + +/* count new ACK sequences */ + const uint32_t ack_rx_max = ntohl (ack->ack_rx_max); + const int32_t delta = ack_rx_max - sock->ack_rx_max; +/* ignore older ACKs when multiple active ACKers */ + if (pgm_uint32_gt (ack_rx_max, sock->ack_rx_max)) + sock->ack_rx_max = ack_rx_max; + uint32_t ack_bitmap = ntohl (ack->ack_bitmap); + if (delta > 32) sock->ack_bitmap = 0; /* sequence jump ahead beyond past bitmap */ + else if (delta > 0) sock->ack_bitmap <<= delta; /* immediate sequence */ + else if (delta > -32) ack_bitmap <<= -delta; /* repair sequence scoped by bitmap */ + else ack_bitmap = 0; /* old sequence */ + unsigned new_acks = _pgm_popcount (ack_bitmap & ~sock->ack_bitmap); + sock->ack_bitmap |= ack_bitmap; + + if (0 == new_acks) + return TRUE; + + const bool is_congestion_limited = (sock->tokens < pgm_fp8 (1)); + +/* after loss detection cancel any further manipulation of the window + * until feedback is received for the next transmitted packet. + */ + if (sock->is_congested) + { + if (pgm_uint32_lte (ack_rx_max, sock->suspended_sqn)) + { + pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("PGMCC window token manipulation suspended due to congestion (T:%u W:%u)"), + pgm_fp8tou (sock->tokens), pgm_fp8tou (sock->cwnd_size)); + const uint_fast32_t token_inc = pgm_fp8mul (pgm_fp8 (new_acks), pgm_fp8 (1) + pgm_fp8div (pgm_fp8 (1), sock->cwnd_size)); + sock->tokens = MIN( sock->tokens + token_inc, sock->cwnd_size ); + goto notify_tx; + } + sock->is_congested = FALSE; + } + +/* count outstanding lost sequences */ + const unsigned total_lost = _pgm_popcount (~sock->ack_bitmap); + +/* no detected data loss at ACKer, increase congestion window size */ + if (0 == total_lost) + { + new_acks += sock->acks_after_loss; + sock->acks_after_loss = 0; + uint_fast32_t n = pgm_fp8 (new_acks); + uint_fast32_t token_inc = 0; + +/* slow-start phase, exponential increase to SSTHRESH */ + if (sock->cwnd_size < sock->ssthresh) { + const uint_fast32_t d = MIN( n, sock->ssthresh - sock->cwnd_size ); + n -= d; + token_inc = d + d; + sock->cwnd_size += d; + } + + const uint_fast32_t iw = pgm_fp8div (pgm_fp8 (1), sock->cwnd_size); + +/* linear window increase */ + token_inc += pgm_fp8mul (n, pgm_fp8 (1) + iw); + sock->cwnd_size += pgm_fp8mul (n, iw); + sock->tokens = MIN( sock->tokens + token_inc, sock->cwnd_size ); +// pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("PGMCC++ (T:%u W:%u)"), +// pgm_fp8tou (sock->tokens), pgm_fp8tou (sock->cwnd_size)); + } + else + { +/* Look for an unacknowledged data packet which is followed by at least three + * acknowledged data packets, then the packet is assumed to be lost and PGMCC + * reacts by halving the window. + * + * Common value will be 0xfffffff7. + */ + sock->acks_after_loss += new_acks; + if (sock->acks_after_loss >= 3) + { + sock->acks_after_loss = 0; + sock->suspended_sqn = ack_rx_max; + sock->is_congested = TRUE; + sock->cwnd_size = pgm_fp8div (sock->cwnd_size, pgm_fp8 (2)); + if (sock->cwnd_size > sock->tokens) + sock->tokens = 0; + else + sock->tokens -= sock->cwnd_size; + sock->ack_bitmap = 0xffffffff; + pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("PGMCC congestion, half window size (T:%u W:%u)"), + pgm_fp8tou (sock->tokens), pgm_fp8tou (sock->cwnd_size)); + } + } + +/* token is now available so notify tx thread that transmission time is available */ +notify_tx: + if (is_congestion_limited && + sock->tokens >= pgm_fp8 (1)) + { + pgm_notify_send (&sock->ack_notify); + } + return TRUE; +} + +/* ambient/heartbeat SPM's + * + * heartbeat: ihb_tmr decaying between ihb_min and ihb_max 2x after last packet + * + * on success, TRUE is returned, if operation would block, FALSE is returned. + */ + +bool +pgm_send_spm ( + pgm_sock_t* const sock, + const int flags + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != sock->window); + + pgm_debug ("pgm_send_spm (sock:%p flags:%d)", + (const void*)sock, flags); + + size_t tpdu_length = sizeof(struct pgm_header); + if (AF_INET == sock->send_gsr.gsr_group.ss_family) + tpdu_length += sizeof(struct pgm_spm); + else + tpdu_length += sizeof(struct pgm_spm6); + if (sock->use_proactive_parity || + sock->use_ondemand_parity || + sock->is_pending_crqst || + PGM_OPT_FIN == flags) + { + tpdu_length += sizeof(struct pgm_opt_length); +/* forward error correction */ + if (sock->use_proactive_parity || + sock->use_ondemand_parity) + tpdu_length += sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_parity_prm); +/* congestion report request */ + if (sock->is_pending_crqst) + tpdu_length += sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_crqst); +/* end of session */ + if (PGM_OPT_FIN == flags) + tpdu_length += sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fin); + } + char buf[ tpdu_length ]; + if (PGM_UNLIKELY(pgm_mem_gc_friendly)) + memset (buf, 0, tpdu_length); + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_spm* spm = (struct pgm_spm *)(header + 1); + struct pgm_spm6* spm6 = (struct pgm_spm6*)(header + 1); + memcpy (header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + header->pgm_sport = sock->tsi.sport; + header->pgm_dport = sock->dport; + header->pgm_type = PGM_SPM; + header->pgm_options = 0; + header->pgm_tsdu_length = 0; + +/* SPM */ + spm->spm_sqn = htonl (sock->spm_sqn); + spm->spm_trail = htonl (pgm_txw_trail_atomic (sock->window)); + spm->spm_lead = htonl (pgm_txw_lead_atomic (sock->window)); + spm->spm_reserved = 0; +/* our nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&sock->send_addr, (char*)&spm->spm_nla_afi); + +/* PGM options */ + if (sock->use_proactive_parity || + sock->use_ondemand_parity || + sock->is_pending_crqst || + PGM_OPT_FIN == flags) + { + struct pgm_opt_length* opt_len; + struct pgm_opt_header *opt_header, *last_opt_header; + uint16_t opt_total_length; + + if (AF_INET == sock->send_gsr.gsr_group.ss_family) + opt_header = (struct pgm_opt_header*)(spm + 1); + else + opt_header = (struct pgm_opt_header*)(spm6 + 1); + header->pgm_options |= PGM_OPT_PRESENT; + opt_len = (struct pgm_opt_length*)opt_header; + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_total_length = sizeof(struct pgm_opt_length); + last_opt_header = opt_header = (struct pgm_opt_header*)(opt_len + 1); + +/* OPT_PARITY_PRM */ + if (sock->use_proactive_parity || + sock->use_ondemand_parity) + { + header->pgm_options |= PGM_OPT_NETWORK; + opt_total_length += sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_parity_prm); + opt_header->opt_type = PGM_OPT_PARITY_PRM; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_parity_prm); + struct pgm_opt_parity_prm* opt_parity_prm = (struct pgm_opt_parity_prm*)(opt_header + 1); + opt_parity_prm->opt_reserved = (sock->use_proactive_parity ? PGM_PARITY_PRM_PRO : 0) | + (sock->use_ondemand_parity ? PGM_PARITY_PRM_OND : 0); + opt_parity_prm->parity_prm_tgs = htonl (sock->rs_k); + last_opt_header = opt_header; + opt_header = (struct pgm_opt_header*)(opt_parity_prm + 1); + } + +/* OPT_CRQST */ + if (sock->is_pending_crqst) + { + header->pgm_options |= PGM_OPT_NETWORK; + opt_total_length += sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_crqst); + opt_header->opt_type = PGM_OPT_CRQST; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_crqst); + struct pgm_opt_crqst* opt_crqst = (struct pgm_opt_crqst*)(opt_header + 1); +/* request receiver worst path report, OPT_CR_RX_WP */ + opt_crqst->opt_reserved = PGM_OPT_CRQST_RXP; + sock->is_pending_crqst = FALSE; + last_opt_header = opt_header; + opt_header = (struct pgm_opt_header*)(opt_crqst + 1); + } + +/* OPT_FIN */ + if (PGM_OPT_FIN == flags) + { + opt_total_length += sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fin); + opt_header->opt_type = PGM_OPT_FIN; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_fin); + struct pgm_opt_fin* opt_fin = (struct pgm_opt_fin*)(opt_header + 1); + opt_fin->opt_reserved = 0; + last_opt_header = opt_header; + opt_header = (struct pgm_opt_header*)(opt_fin + 1); + } + + last_opt_header->opt_type |= PGM_OPT_END; + opt_len->opt_total_length = htons (opt_total_length); + } + +/* checksum optional for SPMs */ + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + + const ssize_t sent = pgm_sendto (sock, + flags != PGM_OPT_SYN && sock->is_controlled_spm, /* rate limited */ + TRUE, /* with router alert */ + buf, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { + sock->blocklen = tpdu_length; + return FALSE; + } +/* advance SPM sequence only on successful transmission */ + sock->spm_sqn++; + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length); + return TRUE; +} + +/* send a NAK confirm (NCF) message with provided sequence number list. + * + * on success, TRUE is returned, returns FALSE if operation would block. + */ + +static +bool +send_ncf ( + pgm_sock_t* const restrict sock, + const struct sockaddr* const restrict nak_src_nla, + const struct sockaddr* const restrict nak_grp_nla, + const uint32_t sequence, + const bool is_parity /* send parity NCF */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != nak_src_nla); + pgm_assert (NULL != nak_grp_nla); + pgm_assert (nak_src_nla->sa_family == nak_grp_nla->sa_family); + +#ifdef SOURCE_DEBUG + char saddr[INET6_ADDRSTRLEN], gaddr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop (nak_src_nla, saddr, sizeof(saddr)); + pgm_sockaddr_ntop (nak_grp_nla, gaddr, sizeof(gaddr)); + pgm_debug ("send_ncf (sock:%p nak-src-nla:%s nak-grp-nla:%s sequence:%" PRIu32" is-parity:%s)", + (void*)sock, + saddr, + gaddr, + sequence, + is_parity ? "TRUE": "FALSE" + ); +#endif + + size_t tpdu_length = sizeof(struct pgm_header); + tpdu_length += (AF_INET == nak_src_nla->sa_family) ? sizeof(struct pgm_nak) : sizeof(struct pgm_nak6); + char buf[ tpdu_length ]; + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_nak* ncf = (struct pgm_nak *)(header + 1); + struct pgm_nak6* ncf6 = (struct pgm_nak6*)(header + 1); + memcpy (header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + header->pgm_sport = sock->tsi.sport; + header->pgm_dport = sock->dport; + header->pgm_type = PGM_NCF; + header->pgm_options = is_parity ? PGM_OPT_PARITY : 0; + header->pgm_tsdu_length = 0; + +/* NCF */ + ncf->nak_sqn = htonl (sequence); + +/* source nla */ + pgm_sockaddr_to_nla (nak_src_nla, (char*)&ncf->nak_src_nla_afi); + +/* group nla */ + pgm_sockaddr_to_nla (nak_grp_nla, (AF_INET6 == nak_src_nla->sa_family) ? (char*)&ncf6->nak6_grp_nla_afi : (char*)&ncf->nak_grp_nla_afi ); + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + + const ssize_t sent = pgm_sendto (sock, + FALSE, /* not rate limited */ + TRUE, /* with router alert */ + buf, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) + return FALSE; + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length); + return TRUE; +} + +/* A NCF packet with a OPT_NAK_LIST option extension + * + * on success, TRUE is returned. on error, FALSE is returned. + */ + +static +bool +send_ncf_list ( + pgm_sock_t* const restrict sock, + const struct sockaddr* const restrict nak_src_nla, + const struct sockaddr* const restrict nak_grp_nla, + struct pgm_sqn_list_t* const restrict sqn_list, /* will change to network-order */ + const bool is_parity /* send parity NCF */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != nak_src_nla); + pgm_assert (NULL != nak_grp_nla); + pgm_assert (sqn_list->len > 1); + pgm_assert (sqn_list->len <= 63); + pgm_assert (nak_src_nla->sa_family == nak_grp_nla->sa_family); + +#ifdef SOURCE_DEBUG + char saddr[INET6_ADDRSTRLEN], gaddr[INET6_ADDRSTRLEN]; + char list[1024]; + pgm_sockaddr_ntop (nak_src_nla, saddr, sizeof(saddr)); + pgm_sockaddr_ntop (nak_grp_nla, gaddr, sizeof(gaddr)); + sprintf (list, "%" PRIu32, sqn_list->sqn[0]); + for (uint_fast8_t i = 1; i < sqn_list->len; i++) { + char sequence[ 2 + strlen("4294967295") ]; + sprintf (sequence, " %" PRIu32, sqn_list->sqn[i]); + strcat (list, sequence); + } + pgm_debug ("send_ncf_list (sock:%p nak-src-nla:%s nak-grp-nla:%s sqn-list:[%s] is-parity:%s)", + (void*)sock, + saddr, + gaddr, + list, + is_parity ? "TRUE": "FALSE" + ); +#endif + + size_t tpdu_length = sizeof(struct pgm_header) + + sizeof(struct pgm_opt_length) + /* includes header */ + sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(uint32_t) ); + tpdu_length += (AF_INET == nak_src_nla->sa_family) ? sizeof(struct pgm_nak) : sizeof(struct pgm_nak6); + char buf[ tpdu_length ]; + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_nak* ncf = (struct pgm_nak *)(header + 1); + struct pgm_nak6* ncf6 = (struct pgm_nak6*)(header + 1); + memcpy (header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + header->pgm_sport = sock->tsi.sport; + header->pgm_dport = sock->dport; + header->pgm_type = PGM_NCF; + header->pgm_options = is_parity ? (PGM_OPT_PRESENT | PGM_OPT_NETWORK | PGM_OPT_PARITY) : (PGM_OPT_PRESENT | PGM_OPT_NETWORK); + header->pgm_tsdu_length = 0; +/* NCF */ + ncf->nak_sqn = htonl (sqn_list->sqn[0]); + +/* source nla */ + pgm_sockaddr_to_nla (nak_src_nla, (char*)&ncf->nak_src_nla_afi); + +/* group nla */ + pgm_sockaddr_to_nla (nak_grp_nla, (AF_INET6 == nak_src_nla->sa_family) ? (char*)&ncf6->nak6_grp_nla_afi : (char*)&ncf->nak_grp_nla_afi ); + +/* OPT_NAK_LIST */ + struct pgm_opt_length* opt_len = (AF_INET6 == nak_src_nla->sa_family) ? (struct pgm_opt_length*)(ncf6 + 1) : (struct pgm_opt_length*)(ncf + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(uint32_t) ) ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_NAK_LIST | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(uint32_t) ); + struct pgm_opt_nak_list* opt_nak_list = (struct pgm_opt_nak_list*)(opt_header + 1); + opt_nak_list->opt_reserved = 0; +/* to network-order */ + for (uint_fast8_t i = 1; i < sqn_list->len; i++) + opt_nak_list->opt_sqn[i-1] = htonl (sqn_list->sqn[i]); + + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + + const ssize_t sent = pgm_sendto (sock, + FALSE, /* not rate limited */ + TRUE, /* with router alert */ + buf, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) + return FALSE; + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length); + return TRUE; +} + +/* cancel any pending heartbeat SPM and schedule a new one + */ + +static +void +reset_heartbeat_spm ( + pgm_sock_t* sock, + const pgm_time_t now + ) +{ + pgm_mutex_lock (&sock->timer_mutex); + const pgm_time_t next_poll = sock->next_poll; + const pgm_time_t spm_heartbeat_interval = sock->spm_heartbeat_interval[ sock->spm_heartbeat_state = 1 ]; + sock->next_heartbeat_spm = now + spm_heartbeat_interval; + if (pgm_time_after( next_poll, sock->next_heartbeat_spm )) + { + sock->next_poll = sock->next_heartbeat_spm; + if (!sock->is_pending_read) { + pgm_notify_send (&sock->pending_notify); + sock->is_pending_read = TRUE; + } + } + pgm_mutex_unlock (&sock->timer_mutex); +} + +/* state helper for resuming sends + */ +#define STATE(x) (sock->pkt_dontwait_state.x) + +/* send one PGM data packet, transmit window owned memory. + * + * On success, returns PGM_IO_STATUS_NORMAL and the number of data bytes pushed + * into the transmit window and attempted to send to the socket layer is saved + * into bytes_written. On non-blocking sockets, PGM_IO_STATUS_WOULD_BLOCK is + * returned if the send would block. PGM_IO_STATUS_RATE_LIMITED is returned if + * the packet sizes would exceed the current rate limit. + * + * ! always returns successful if data is pushed into the transmit window, even if + * sendto() double fails ¡ we don't want the application to try again as that is the + * reliable socks role. + */ + +static +int +send_odata ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t* const restrict skb, + size_t* restrict bytes_written + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + pgm_assert (skb->len <= sock->max_tsdu); + + pgm_debug ("send_odata (sock:%p skb:%p bytes-written:%p)", + (void*)sock, (void*)skb, (void*)bytes_written); + + const uint16_t tsdu_length = skb->len; + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + const size_t tpdu_length = tsdu_length + pgm_pkt_offset (FALSE, pgmcc_family); + +/* continue if send would block */ + if (sock->is_apdu_eagain) { + STATE(skb)->tstamp = pgm_time_update_now(); + goto retry_send; + } + +/* add PGM header to skbuff */ + STATE(skb) = pgm_skb_get(skb); + STATE(skb)->sock = sock; + STATE(skb)->tstamp = pgm_time_update_now(); + + STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->head; + STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); + memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; + STATE(skb)->pgm_header->pgm_dport = sock->dport; + STATE(skb)->pgm_header->pgm_type = PGM_ODATA; + STATE(skb)->pgm_header->pgm_options = sock->use_pgmcc ? PGM_OPT_PRESENT : 0; + STATE(skb)->pgm_header->pgm_tsdu_length = htons (tsdu_length); + +/* ODATA */ + STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + + STATE(skb)->pgm_header->pgm_checksum = 0; + void* data = STATE(skb)->pgm_data + 1; + if (sock->use_pgmcc) { + struct pgm_opt_length* opt_len = data; + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + ((AF_INET6 == sock->acker_nla.ss_family) ? + sizeof(struct pgm_opt6_pgmcc_data) : + sizeof(struct pgm_opt_pgmcc_data)) ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_PGMCC_DATA | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + ((AF_INET6 == sock->acker_nla.ss_family) ? + sizeof(struct pgm_opt6_pgmcc_data) : + sizeof(struct pgm_opt_pgmcc_data)); + struct pgm_opt_pgmcc_data* pgmcc_data = (struct pgm_opt_pgmcc_data*)(opt_header + 1); + struct pgm_opt6_pgmcc_data* pgmcc_data6 = (struct pgm_opt6_pgmcc_data*)(opt_header + 1); + + pgmcc_data->opt_tstamp = htonl (pgm_to_msecs (STATE(skb)->tstamp)); +/* acker nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&sock->acker_nla, (char*)&pgmcc_data->opt_nla_afi); + if (AF_INET6 == sock->acker_nla.ss_family) + data = (char*)pgmcc_data6 + sizeof(struct pgm_opt6_pgmcc_data); + else + data = (char*)pgmcc_data + sizeof(struct pgm_opt_pgmcc_data); + } + const size_t pgm_header_len = (char*)data - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + STATE(unfolded_odata) = pgm_csum_partial (data, tsdu_length, 0); + STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); + +/* add to transmit window, skb::data set to payload */ + pgm_spinlock_lock (&sock->txw_spinlock); + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); + +/* the transmit window MUST check the user count to ensure it does not + * attempt to send a repair-data packet based on in transit original data. + */ + + ssize_t sent; +retry_send: + +/* congestion control */ + if (sock->use_pgmcc && + sock->tokens < pgm_fp8 (1)) + { +// pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Token limit reached.")); + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + return PGM_IO_STATUS_CONGESTION; /* peer expiration to re-elect ACKer */ + } + + sent = pgm_sendto (sock, + sock->is_controlled_odata, /* rate limited */ + FALSE, /* regular socket */ + STATE(skb)->head, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + if (EAGAIN == errno) { + if (sock->use_pgmcc) + pgm_notify_clear (&sock->ack_notify); + return PGM_IO_STATUS_WOULD_BLOCK; + } + return PGM_IO_STATUS_RATE_LIMITED; + } + +/* save unfolded odata for retransmissions */ + pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); + + sock->is_apdu_eagain = FALSE; + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + if (sock->use_pgmcc) { + sock->tokens -= pgm_fp8 (1); + sock->ack_expiry = STATE(skb)->tstamp + sock->ack_expiry_ivl; + } + + if (PGM_LIKELY((size_t)sent == tpdu_length)) { + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += tsdu_length; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] ++; + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length + sock->iphdr_len); + } + +/* check for end of transmission group */ + if (sock->use_proactive_parity) { + const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); + const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; + if (!((odata_sqn + 1) & ~tg_sqn_mask)) + pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); + } + +/* remove applications reference to skbuff */ + pgm_free_skb (STATE(skb)); + if (bytes_written) + *bytes_written = tsdu_length; + return PGM_IO_STATUS_NORMAL; +} + +/* send one PGM original data packet, callee owned memory. + * + * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets + * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if + * packet size exceeds the current rate limit. + */ + +static +int +send_odata_copy ( + pgm_sock_t* const restrict sock, + const void* restrict tsdu, + const uint16_t tsdu_length, + size_t* restrict bytes_written + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (tsdu_length <= sock->max_tsdu); + if (PGM_LIKELY(tsdu_length)) pgm_assert (NULL != tsdu); + + pgm_debug ("send_odata_copy (sock:%p tsdu:%p tsdu_length:%u bytes-written:%p)", + (void*)sock, tsdu, tsdu_length, (void*)bytes_written); + + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + const size_t tpdu_length = tsdu_length + pgm_pkt_offset (FALSE, pgmcc_family); + +/* continue if blocked mid-apdu, updating timestamp */ + if (sock->is_apdu_eagain) { + STATE(skb)->tstamp = pgm_time_update_now(); + goto retry_send; + } + + STATE(skb) = pgm_alloc_skb (sock->max_tpdu); + STATE(skb)->sock = sock; + STATE(skb)->tstamp = pgm_time_update_now(); + pgm_skb_reserve (STATE(skb), pgm_pkt_offset (FALSE, pgmcc_family)); + pgm_skb_put (STATE(skb), tsdu_length); + + STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->head; + STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); + memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; + STATE(skb)->pgm_header->pgm_dport = sock->dport; + STATE(skb)->pgm_header->pgm_type = PGM_ODATA; + STATE(skb)->pgm_header->pgm_options = sock->use_pgmcc ? PGM_OPT_PRESENT : 0; + STATE(skb)->pgm_header->pgm_tsdu_length = htons (tsdu_length); + +/* ODATA */ + STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + + STATE(skb)->pgm_header->pgm_checksum = 0; + void* data = STATE(skb)->pgm_data + 1; + if (sock->use_pgmcc) { + struct pgm_opt_length* opt_len = data; + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + ((AF_INET6 == sock->acker_nla.ss_family) ? + sizeof(struct pgm_opt6_pgmcc_data) : + sizeof(struct pgm_opt_pgmcc_data)) ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_PGMCC_DATA | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + ((AF_INET6 == sock->acker_nla.ss_family) ? + sizeof(struct pgm_opt6_pgmcc_data) : + sizeof(struct pgm_opt_pgmcc_data)); + struct pgm_opt_pgmcc_data* pgmcc_data = (struct pgm_opt_pgmcc_data*)(opt_header + 1); + struct pgm_opt6_pgmcc_data* pgmcc_data6 = (struct pgm_opt6_pgmcc_data*)(opt_header + 1); + + pgmcc_data->opt_reserved = 0; + pgmcc_data->opt_tstamp = htonl (pgm_to_msecs (STATE(skb)->tstamp)); +/* acker nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&sock->acker_nla, (char*)&pgmcc_data->opt_nla_afi); + data = (char*)opt_header + opt_header->opt_length; + } + const size_t pgm_header_len = (char*)data - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + STATE(unfolded_odata) = pgm_csum_partial_copy (tsdu, data, tsdu_length, 0); + STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); + +/* add to transmit window, skb::data set to payload */ + pgm_spinlock_lock (&sock->txw_spinlock); + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); + + ssize_t sent; +retry_send: + +/* congestion control */ + if (sock->use_pgmcc && + sock->tokens < pgm_fp8 (1)) + { +// pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Token limit reached.")); + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + return PGM_IO_STATUS_CONGESTION; + } + + sent = pgm_sendto (sock, + sock->is_controlled_odata, /* rate limited */ + FALSE, /* regular socket */ + STATE(skb)->head, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + if (EAGAIN == errno) { + if (sock->use_pgmcc) + pgm_notify_clear (&sock->ack_notify); + return PGM_IO_STATUS_WOULD_BLOCK; + } + return PGM_IO_STATUS_RATE_LIMITED; + } + + if (sock->use_pgmcc) { + sock->tokens -= pgm_fp8 (1); + pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("PGMCC tokens-- (T:%u W:%u)"), + pgm_fp8tou (sock->tokens), pgm_fp8tou (sock->cwnd_size)); + sock->ack_expiry = STATE(skb)->tstamp + sock->ack_expiry_ivl; + } + +/* save unfolded odata for retransmissions */ + pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); + + sock->is_apdu_eagain = FALSE; + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + + if (PGM_LIKELY((size_t)sent == tpdu_length)) { + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += tsdu_length; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] ++; + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length + sock->iphdr_len); + } + +/* check for end of transmission group */ + if (sock->use_proactive_parity) { + const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); + const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; + if (!((odata_sqn + 1) & ~tg_sqn_mask)) + pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); + } + +/* return data payload length sent */ + if (bytes_written) + *bytes_written = tsdu_length; + return PGM_IO_STATUS_NORMAL; +} + +/* send one PGM original data packet, callee owned scatter/gather io vector + * + * ⎢ DATA₀ ⎢ + * ⎢ DATA₁ ⎢ → send_odatav() → ⎢ TSDU₀ ⎢ → libc + * ⎢ ⋮ ⎢ + * + * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets + * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if + * packet size exceeds the current rate limit. + */ + +static +int +send_odatav ( + pgm_sock_t* const restrict sock, + const struct pgm_iovec* const restrict vector, + const unsigned count, /* number of items in vector */ + size_t* restrict bytes_written + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (count <= PGM_MAX_FRAGMENTS); + if (PGM_LIKELY(count)) pgm_assert (NULL != vector); + + pgm_debug ("send_odatav (sock:%p vector:%p count:%u bytes-written:%p)", + (const void*)sock, (const void*)vector, count, (const void*)bytes_written); + + if (PGM_UNLIKELY(0 == count)) + return send_odata_copy (sock, NULL, 0, bytes_written); + +/* continue if blocked on send */ + if (sock->is_apdu_eagain) + goto retry_send; + + STATE(tsdu_length) = 0; + for (unsigned i = 0; i < count; i++) + { +#ifdef TRANSPORT_DEBUG + if (PGM_LIKELY(vector[i].iov_len)) { + pgm_assert( vector[i].iov_base ); + } +#endif + STATE(tsdu_length) += vector[i].iov_len; + } + pgm_return_val_if_fail (STATE(tsdu_length) <= sock->max_tsdu, PGM_IO_STATUS_ERROR); + + STATE(skb) = pgm_alloc_skb (sock->max_tpdu); + STATE(skb)->sock = sock; + STATE(skb)->tstamp = pgm_time_update_now(); + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + pgm_skb_reserve (STATE(skb), pgm_pkt_offset (FALSE, pgmcc_family)); + pgm_skb_put (STATE(skb), STATE(tsdu_length)); + + STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->data; + STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); + memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; + STATE(skb)->pgm_header->pgm_dport = sock->dport; + STATE(skb)->pgm_header->pgm_type = PGM_ODATA; + STATE(skb)->pgm_header->pgm_options = 0; + STATE(skb)->pgm_header->pgm_tsdu_length = htons (STATE(tsdu_length)); + +/* ODATA */ + STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + + STATE(skb)->pgm_header->pgm_checksum = 0; + const size_t pgm_header_len = (char*)(STATE(skb)->pgm_data + 1) - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + +/* unroll first iteration to make friendly branch prediction */ + char* dst = (char*)(STATE(skb)->pgm_data + 1); + STATE(unfolded_odata) = pgm_csum_partial_copy ((const char*)vector[0].iov_base, dst, vector[0].iov_len, 0); + +/* iterate over one or more vector elements to perform scatter/gather checksum & copy */ + for (unsigned i = 1; i < count; i++) { + dst += vector[i-1].iov_len; + const uint32_t unfolded_element = pgm_csum_partial_copy ((const char*)vector[i].iov_base, dst, vector[i].iov_len, 0); + STATE(unfolded_odata) = pgm_csum_block_add (STATE(unfolded_odata), unfolded_element, vector[i-1].iov_len); + } + + STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); + +/* add to transmit window, skb::data set to payload */ + pgm_spinlock_lock (&sock->txw_spinlock); + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); + + ssize_t sent; + size_t tpdu_length; +retry_send: + pgm_assert ((char*)STATE(skb)->tail > (char*)STATE(skb)->head); + tpdu_length = (char*)STATE(skb)->tail - (char*)STATE(skb)->head; + sent = pgm_sendto (sock, + sock->is_controlled_odata, /* rate limited */ + FALSE, /* regular socket */ + STATE(skb)->head, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + if (EAGAIN == errno) { + if (sock->use_pgmcc) + pgm_notify_clear (&sock->ack_notify); + return PGM_IO_STATUS_WOULD_BLOCK; + } + return PGM_IO_STATUS_RATE_LIMITED; + } + +/* save unfolded odata for retransmissions */ + pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); + + sock->is_apdu_eagain = FALSE; + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + + if (PGM_LIKELY((size_t)sent == STATE(skb)->len)) { + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += STATE(tsdu_length); + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] ++; + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length + sock->iphdr_len); + } + +/* check for end of transmission group */ + if (sock->use_proactive_parity) { + const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); + const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; + if (!((odata_sqn + 1) & ~tg_sqn_mask)) + pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); + } + +/* return data payload length sent */ + if (bytes_written) + *bytes_written = STATE(tsdu_length); + return PGM_IO_STATUS_NORMAL; +} + +/* send PGM original data, callee owned memory. if larger than maximum TPDU + * size will be fragmented. + * + * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets + * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if + * packet size exceeds the current rate limit. + */ + +static +int +send_apdu ( + pgm_sock_t* const restrict sock, + const void* restrict apdu, + const size_t apdu_length, + size_t* restrict bytes_written + ) +{ + size_t bytes_sent = 0; /* counted at IP layer */ + unsigned packets_sent = 0; /* IP packets */ + size_t data_bytes_sent = 0; + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + + pgm_assert (NULL != sock); + pgm_assert (NULL != apdu); + +/* continue if blocked mid-apdu */ + if (sock->is_apdu_eagain) + goto retry_send; + +/* if non-blocking calculate total wire size and check rate limit */ + STATE(is_rate_limited) = FALSE; + if (sock->is_nonblocking && sock->is_controlled_odata) + { + const size_t header_length = pgm_pkt_offset (TRUE, pgmcc_family); + size_t tpdu_length = 0; + size_t offset_ = 0; + do { + const uint16_t tsdu_length = MIN( source_max_tsdu (sock, TRUE), apdu_length - offset_ ); + tpdu_length += sock->iphdr_len + header_length + tsdu_length; + offset_ += tsdu_length; + } while (offset_ < apdu_length); + +/* calculation includes one iphdr length already */ + if (!pgm_rate_check (&sock->rate_control, + tpdu_length - sock->iphdr_len, + sock->is_nonblocking)) + { + sock->blocklen = tpdu_length; + return PGM_IO_STATUS_RATE_LIMITED; + } + STATE(is_rate_limited) = TRUE; + } + + STATE(data_bytes_offset) = 0; + STATE(first_sqn) = pgm_txw_next_lead(sock->window); + + do { +/* retrieve packet storage from transmit window */ + size_t header_length = pgm_pkt_offset (TRUE, pgmcc_family); + STATE(tsdu_length) = MIN( source_max_tsdu (sock, TRUE), apdu_length - STATE(data_bytes_offset) ); + + STATE(skb) = pgm_alloc_skb (sock->max_tpdu); + STATE(skb)->sock = sock; + STATE(skb)->tstamp = pgm_time_update_now(); + pgm_skb_reserve (STATE(skb), header_length); + pgm_skb_put (STATE(skb), STATE(tsdu_length)); + + STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->head; + STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); + memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; + STATE(skb)->pgm_header->pgm_dport = sock->dport; + STATE(skb)->pgm_header->pgm_type = PGM_ODATA; + STATE(skb)->pgm_header->pgm_options = PGM_OPT_PRESENT; + STATE(skb)->pgm_header->pgm_tsdu_length = htons (STATE(tsdu_length)); + +/* ODATA */ + STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + +/* OPT_LENGTH */ + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(STATE(skb)->pgm_data + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment) ); +/* OPT_FRAGMENT */ + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_FRAGMENT | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment); + STATE(skb)->pgm_opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); + STATE(skb)->pgm_opt_fragment->opt_reserved = 0; + STATE(skb)->pgm_opt_fragment->opt_sqn = htonl (STATE(first_sqn)); + STATE(skb)->pgm_opt_fragment->opt_frag_off = htonl (STATE(data_bytes_offset)); + STATE(skb)->pgm_opt_fragment->opt_frag_len = htonl (apdu_length); + +/* TODO: the assembly checksum & copy routine is faster than memcpy & pgm_cksum on >= opteron hardware */ + STATE(skb)->pgm_header->pgm_checksum = 0; + const size_t pgm_header_len = (char*)(STATE(skb)->pgm_opt_fragment + 1) - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + STATE(unfolded_odata) = pgm_csum_partial_copy ((const char*)apdu + STATE(data_bytes_offset), STATE(skb)->pgm_opt_fragment + 1, STATE(tsdu_length), 0); + STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); + +/* add to transmit window, skb::data set to payload */ + pgm_spinlock_lock (&sock->txw_spinlock); + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); + + ssize_t sent; + size_t tpdu_length; +retry_send: + pgm_assert ((char*)STATE(skb)->tail > (char*)STATE(skb)->head); + tpdu_length = (char*)STATE(skb)->tail - (char*)STATE(skb)->head; + sent = pgm_sendto (sock, + !STATE(is_rate_limited), /* rate limit on blocking */ + FALSE, /* regular socket */ + STATE(skb)->head, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + goto blocked; + } + +/* save unfolded odata for retransmissions */ + pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); + + if (PGM_LIKELY((size_t)sent == tpdu_length)) { + bytes_sent += tpdu_length + sock->iphdr_len; /* as counted at IP layer */ + packets_sent++; /* IP packets */ + data_bytes_sent += STATE(tsdu_length); + } + + STATE(data_bytes_offset) += STATE(tsdu_length); + +/* check for end of transmission group */ + if (sock->use_proactive_parity) { + const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); + const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; + if (!((odata_sqn + 1) & ~tg_sqn_mask)) + pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); + } + + } while ( STATE(data_bytes_offset) < apdu_length); + pgm_assert( STATE(data_bytes_offset) == apdu_length ); + + sock->is_apdu_eagain = FALSE; + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; + if (bytes_written) + *bytes_written = apdu_length; + return PGM_IO_STATUS_NORMAL; + +blocked: + if (bytes_sent) { + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; + } + if (EAGAIN == errno) { + if (sock->use_pgmcc) + pgm_notify_clear (&sock->ack_notify); + return PGM_IO_STATUS_WOULD_BLOCK; + } + return PGM_IO_STATUS_RATE_LIMITED; +} + +/* Send one APDU, whether it fits within one TPDU or more. + * + * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets + * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if + * packet size exceeds the current rate limit. + */ +int +pgm_send ( + pgm_sock_t* const restrict sock, + const void* restrict apdu, + const size_t apdu_length, + size_t* restrict bytes_written + ) +{ + pgm_debug ("pgm_send (sock:%p apdu:%p apdu-length:%zu bytes-written:%p)", + (void*)sock, apdu, apdu_length, (void*)bytes_written); + +/* parameters */ + pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); + if (PGM_LIKELY(apdu_length)) pgm_return_val_if_fail (NULL != apdu, PGM_IO_STATUS_ERROR); + +/* shutdown */ + if (PGM_UNLIKELY(!pgm_rwlock_reader_trylock (&sock->lock))) + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + +/* state */ + if (PGM_UNLIKELY(!sock->is_bound || + sock->is_destroyed || + apdu_length > sock->max_apdu)) + { + pgm_rwlock_reader_unlock (&sock->lock); + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + } + +/* source */ + pgm_mutex_lock (&sock->source_mutex); + +/* pass on non-fragment calls */ + if (apdu_length <= sock->max_tsdu) + { + const int status = send_odata_copy (sock, apdu, apdu_length, bytes_written); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + } + + const int status = send_apdu (sock, apdu, apdu_length, bytes_written); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; +} + +/* send PGM original data, callee owned scatter/gather IO vector. if larger than maximum TPDU + * size will be fragmented. + * + * is_one_apdu = true: + * + * ⎢ DATA₀ ⎢ + * ⎢ DATA₁ ⎢ → pgm_sendv() → ⎢ ⋯ TSDU₁ TSDU₀ ⎢ → libc + * ⎢ ⋮ ⎢ + * + * is_one_apdu = false: + * + * ⎢ APDU₀ ⎢ ⎢ ⋯ TSDU₁,₀ TSDU₀,₀ ⎢ + * ⎢ APDU₁ ⎢ → pgm_sendv() → ⎢ ⋯ TSDU₁,₁ TSDU₀,₁ ⎢ → libc + * ⎢ ⋮ ⎢ ⎢ ⋮ ⋮ ⎢ + * + * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets + * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if + * packet size exceeds the current rate limit. + */ + +int +pgm_sendv ( + pgm_sock_t* const restrict sock, + const struct pgm_iovec* const restrict vector, + const unsigned count, /* number of items in vector */ + const bool is_one_apdu, /* true = vector = apdu, false = vector::iov_base = apdu */ + size_t* restrict bytes_written + ) +{ + pgm_debug ("pgm_sendv (sock:%p vector:%p count:%u is-one-apdu:%s bytes-written:%p)", + (const void*)sock, + (const void*)vector, + count, + is_one_apdu ? "TRUE" : "FALSE", + (const void*)bytes_written); + + pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); + pgm_return_val_if_fail (count <= PGM_MAX_FRAGMENTS, PGM_IO_STATUS_ERROR); + if (PGM_LIKELY(count)) pgm_return_val_if_fail (NULL != vector, PGM_IO_STATUS_ERROR); + if (PGM_UNLIKELY(!pgm_rwlock_reader_trylock (&sock->lock))) + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + if (PGM_UNLIKELY(!sock->is_bound || + sock->is_destroyed)) + { + pgm_rwlock_reader_unlock (&sock->lock); + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + } + + pgm_mutex_lock (&sock->source_mutex); + +/* pass on zero length as cannot count vector lengths */ + if (PGM_UNLIKELY(0 == count)) + { + const int status = send_odata_copy (sock, NULL, count, bytes_written); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + } + + size_t bytes_sent = 0; + unsigned packets_sent = 0; + size_t data_bytes_sent = 0; + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + +/* continue if blocked mid-apdu */ + if (sock->is_apdu_eagain) { + if (is_one_apdu) { + if (STATE(apdu_length) <= sock->max_tsdu) + { + const int status = send_odatav (sock, vector, count, bytes_written); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + } + else + goto retry_one_apdu_send; + } else { + goto retry_send; + } + } + +/* calculate (total) APDU length */ + STATE(apdu_length) = 0; + for (unsigned i = 0; i < count; i++) + { +#ifdef TRANSPORT_DEBUG + if (PGM_LIKELY(vector[i].iov_len)) { + pgm_assert( vector[i].iov_base ); + } +#endif + if (!is_one_apdu && + vector[i].iov_len > sock->max_apdu) + { + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + } + STATE(apdu_length) += vector[i].iov_len; + } + +/* pass on non-fragment calls */ + if (is_one_apdu) { + if (STATE(apdu_length) <= sock->max_tsdu) { + const int status = send_odatav (sock, vector, count, bytes_written); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + } else if (STATE(apdu_length) > sock->max_apdu) { + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + } + } + +/* if non-blocking calculate total wire size and check rate limit */ + STATE(is_rate_limited) = FALSE; + if (sock->is_nonblocking && sock->is_controlled_odata) + { + const size_t header_length = pgm_pkt_offset (TRUE, pgmcc_family); + size_t tpdu_length = 0; + size_t offset_ = 0; + do { + const uint16_t tsdu_length = MIN( source_max_tsdu (sock, TRUE), STATE(apdu_length) - offset_ ); + tpdu_length += sock->iphdr_len + header_length + tsdu_length; + offset_ += tsdu_length; + } while (offset_ < STATE(apdu_length)); + +/* calculation includes one iphdr length already */ + if (!pgm_rate_check (&sock->rate_control, + tpdu_length - sock->iphdr_len, + sock->is_nonblocking)) + { + sock->blocklen = tpdu_length; + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_RATE_LIMITED; + } + STATE(is_rate_limited) = TRUE; + } + +/* non-fragmented packets can be forwarded onto basic send() */ + if (!is_one_apdu) + { + for (STATE(data_pkt_offset) = 0; STATE(data_pkt_offset) < count; STATE(data_pkt_offset)++) + { + size_t wrote_bytes; + int status; +retry_send: + status = send_apdu (sock, + vector[STATE(data_pkt_offset)].iov_base, + vector[STATE(data_pkt_offset)].iov_len, + &wrote_bytes); + switch (status) { + case PGM_IO_STATUS_NORMAL: + break; + case PGM_IO_STATUS_WOULD_BLOCK: + case PGM_IO_STATUS_RATE_LIMITED: + sock->is_apdu_eagain = TRUE; + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + case PGM_IO_STATUS_ERROR: + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + default: + pgm_assert_not_reached(); + } + data_bytes_sent += wrote_bytes; + } + + sock->is_apdu_eagain = FALSE; + if (bytes_written) + *bytes_written = data_bytes_sent; + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_NORMAL; + } + + STATE(data_bytes_offset) = 0; + STATE(vector_index) = 0; + STATE(vector_offset) = 0; + + STATE(first_sqn) = pgm_txw_next_lead(sock->window); + + do { +/* retrieve packet storage from transmit window */ + size_t header_length = pgm_pkt_offset (TRUE, pgmcc_family); + STATE(tsdu_length) = MIN( source_max_tsdu (sock, TRUE), STATE(apdu_length) - STATE(data_bytes_offset) ); + STATE(skb) = pgm_alloc_skb (sock->max_tpdu); + STATE(skb)->sock = sock; + STATE(skb)->tstamp = pgm_time_update_now(); + pgm_skb_reserve (STATE(skb), header_length); + pgm_skb_put (STATE(skb), STATE(tsdu_length)); + + STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->head; + STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); + memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; + STATE(skb)->pgm_header->pgm_dport = sock->dport; + STATE(skb)->pgm_header->pgm_type = PGM_ODATA; + STATE(skb)->pgm_header->pgm_options = PGM_OPT_PRESENT; + STATE(skb)->pgm_header->pgm_tsdu_length = htons (STATE(tsdu_length)); + +/* ODATA */ + STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + +/* OPT_LENGTH */ + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(STATE(skb)->pgm_data + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment) ); +/* OPT_FRAGMENT */ + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_FRAGMENT | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment); + STATE(skb)->pgm_opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); + STATE(skb)->pgm_opt_fragment->opt_reserved = 0; + STATE(skb)->pgm_opt_fragment->opt_sqn = htonl (STATE(first_sqn)); + STATE(skb)->pgm_opt_fragment->opt_frag_off = htonl (STATE(data_bytes_offset)); + STATE(skb)->pgm_opt_fragment->opt_frag_len = htonl (STATE(apdu_length)); + +/* checksum & copy */ + STATE(skb)->pgm_header->pgm_checksum = 0; + const size_t pgm_header_len = (char*)(STATE(skb)->pgm_opt_fragment + 1) - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + +/* iterate over one or more vector elements to perform scatter/gather checksum & copy + * + * STATE(vector_index) - index into application scatter/gather vector + * STATE(vector_offset) - current offset into current vector element + * STATE(unfolded_odata)- checksum accumulator + */ + const char* src = (const char*)vector[STATE(vector_index)].iov_base + STATE(vector_offset); + char* dst = (char*)(STATE(skb)->pgm_opt_fragment + 1); + size_t src_length = vector[STATE(vector_index)].iov_len - STATE(vector_offset); + size_t dst_length = 0; + size_t copy_length = MIN( STATE(tsdu_length), src_length ); + STATE(unfolded_odata) = pgm_csum_partial_copy (src, dst, copy_length, 0); + + for(;;) + { + if (copy_length == src_length) { +/* application packet complete */ + STATE(vector_index)++; + STATE(vector_offset) = 0; + } else { +/* data still remaining */ + STATE(vector_offset) += copy_length; + } + + dst_length += copy_length; + +/* sock packet complete */ + if (dst_length == STATE(tsdu_length)) + break; + + src = (const char*)vector[STATE(vector_index)].iov_base + STATE(vector_offset); + dst += copy_length; + src_length = vector[STATE(vector_index)].iov_len - STATE(vector_offset); + copy_length = MIN( STATE(tsdu_length) - dst_length, src_length ); + const uint32_t unfolded_element = pgm_csum_partial_copy (src, dst, copy_length, 0); + STATE(unfolded_odata) = pgm_csum_block_add (STATE(unfolded_odata), unfolded_element, dst_length); + } + + STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); + +/* add to transmit window, skb::data set to payload */ + pgm_spinlock_lock (&sock->txw_spinlock); + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); + + ssize_t sent; + size_t tpdu_length; +retry_one_apdu_send: + tpdu_length = (char*)STATE(skb)->tail - (char*)STATE(skb)->head; + sent = pgm_sendto (sock, + !STATE(is_rate_limited), /* rate limited on blocking */ + FALSE, /* regular socket */ + STATE(skb)->head, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + goto blocked; + } + +/* save unfolded odata for retransmissions */ + pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); + + if (PGM_LIKELY((size_t)sent == tpdu_length)) { + bytes_sent += tpdu_length + sock->iphdr_len; /* as counted at IP layer */ + packets_sent++; /* IP packets */ + data_bytes_sent += STATE(tsdu_length); + } + + STATE(data_bytes_offset) += STATE(tsdu_length); + +/* check for end of transmission group */ + if (sock->use_proactive_parity) { + const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); + const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; + if (!((odata_sqn + 1) & ~tg_sqn_mask)) + pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); + } + + } while ( STATE(data_bytes_offset) < STATE(apdu_length) ); + pgm_assert( STATE(data_bytes_offset) == STATE(apdu_length) ); + + sock->is_apdu_eagain = FALSE; + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; + if (bytes_written) + *bytes_written = STATE(apdu_length); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_NORMAL; + +blocked: + if (bytes_sent) { + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; + } + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + if (EAGAIN == errno) { + if (sock->use_pgmcc) + pgm_notify_clear (&sock->ack_notify); + return PGM_IO_STATUS_WOULD_BLOCK; + } + return PGM_IO_STATUS_RATE_LIMITED; +} + +/* send PGM original data, transmit window owned scatter/gather IO vector. + * + * ⎢ TSDU₀ ⎢ + * ⎢ TSDU₁ ⎢ → pgm_send_skbv() → ⎢ ⋯ TSDU₁ TSDU₀ ⎢ → libc + * ⎢ ⋮ ⎢ + * + * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets + * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if + * packet size exceeds the current rate limit. + */ + +int +pgm_send_skbv ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t** const restrict vector, /* array of skb pointers vs. array of skbs */ + const unsigned count, + const bool is_one_apdu, /* true: vector = apdu, false: vector::iov_base = apdu */ + size_t* restrict bytes_written + ) +{ + pgm_debug ("pgm_send_skbv (sock:%p vector:%p count:%u is-one-apdu:%s bytes-written:%p)", + (const void*)sock, + (const void*)vector, + count, + is_one_apdu ? "TRUE" : "FALSE", + (const void*)bytes_written); + + pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); + pgm_return_val_if_fail (count <= PGM_MAX_FRAGMENTS, PGM_IO_STATUS_ERROR); + if (PGM_LIKELY(count)) pgm_return_val_if_fail (NULL != vector, PGM_IO_STATUS_ERROR); + if (PGM_UNLIKELY(!pgm_rwlock_reader_trylock (&sock->lock))) + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + if (PGM_UNLIKELY(!sock->is_bound || + sock->is_destroyed)) + { + pgm_rwlock_reader_unlock (&sock->lock); + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + } + + pgm_mutex_lock (&sock->source_mutex); + +/* pass on zero length as cannot count vector lengths */ + if (PGM_UNLIKELY(0 == count)) + { + const int status = send_odata_copy (sock, NULL, count, bytes_written); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + } + else if (1 == count) + { + const int status = send_odata (sock, vector[0], bytes_written); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + } + + size_t bytes_sent = 0; + unsigned packets_sent = 0; + size_t data_bytes_sent = 0; + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + +/* continue if blocked mid-apdu */ + if (sock->is_apdu_eagain) + goto retry_send; + + STATE(is_rate_limited) = FALSE; + if (sock->is_nonblocking && sock->is_controlled_odata) + { + size_t total_tpdu_length = 0; + for (unsigned i = 0; i < count; i++) + total_tpdu_length += sock->iphdr_len + pgm_pkt_offset (is_one_apdu, pgmcc_family) + vector[i]->len; + +/* calculation includes one iphdr length already */ + if (!pgm_rate_check (&sock->rate_control, + total_tpdu_length - sock->iphdr_len, + sock->is_nonblocking)) + { + sock->blocklen = total_tpdu_length; + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_RATE_LIMITED; + } + STATE(is_rate_limited) = TRUE; + } + + if (is_one_apdu) + { + STATE(apdu_length) = 0; + STATE(first_sqn) = pgm_txw_next_lead(sock->window); + for (unsigned i = 0; i < count; i++) + { + if (PGM_UNLIKELY(vector[i]->len > sock->max_tsdu_fragment)) { + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_ERROR; + } + STATE(apdu_length) += vector[i]->len; + } + if (PGM_UNLIKELY(STATE(apdu_length) > sock->max_apdu)) { + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_ERROR; + } + } + + for (STATE(vector_index) = 0; STATE(vector_index) < count; STATE(vector_index)++) + { + STATE(tsdu_length) = vector[STATE(vector_index)]->len; + + STATE(skb) = pgm_skb_get(vector[STATE(vector_index)]); + STATE(skb)->sock = sock; + STATE(skb)->tstamp = pgm_time_update_now(); + + STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->head; + STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); + memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; + STATE(skb)->pgm_header->pgm_dport = sock->dport; + STATE(skb)->pgm_header->pgm_type = PGM_ODATA; + STATE(skb)->pgm_header->pgm_options = is_one_apdu ? PGM_OPT_PRESENT : 0; + STATE(skb)->pgm_header->pgm_tsdu_length = htons (STATE(tsdu_length)); + +/* ODATA */ + STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + + if (is_one_apdu) + { +/* OPT_LENGTH */ + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(STATE(skb)->pgm_data + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment) ); +/* OPT_FRAGMENT */ + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_FRAGMENT | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment); + STATE(skb)->pgm_opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); + STATE(skb)->pgm_opt_fragment->opt_reserved = 0; + STATE(skb)->pgm_opt_fragment->opt_sqn = htonl (STATE(first_sqn)); + STATE(skb)->pgm_opt_fragment->opt_frag_off = htonl (STATE(data_bytes_offset)); + STATE(skb)->pgm_opt_fragment->opt_frag_len = htonl (STATE(apdu_length)); + + pgm_assert (STATE(skb)->data == (STATE(skb)->pgm_opt_fragment + 1)); + } + else + { + pgm_assert (STATE(skb)->data == (STATE(skb)->pgm_data + 1)); + } + +/* TODO: the assembly checksum & copy routine is faster than memcpy & pgm_cksum on >= opteron hardware */ + STATE(skb)->pgm_header->pgm_checksum = 0; + pgm_assert ((char*)STATE(skb)->data > (char*)STATE(skb)->pgm_header); + const size_t pgm_header_len = (char*)STATE(skb)->data - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + STATE(unfolded_odata) = pgm_csum_partial ((char*)STATE(skb)->data, STATE(tsdu_length), 0); + STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); + +/* add to transmit window, skb::data set to payload */ + pgm_spinlock_lock (&sock->txw_spinlock); + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); + ssize_t sent; + size_t tpdu_length; +retry_send: + pgm_assert ((char*)STATE(skb)->tail > (char*)STATE(skb)->head); + tpdu_length = (char*)STATE(skb)->tail - (char*)STATE(skb)->head; + sent = pgm_sendto (sock, + !STATE(is_rate_limited), /* rate limited on blocking */ + FALSE, /* regular socket */ + STATE(skb)->head, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + goto blocked; + } + +/* save unfolded odata for retransmissions */ + pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); + + if (PGM_LIKELY((size_t)sent == tpdu_length)) { + bytes_sent += tpdu_length + sock->iphdr_len; /* as counted at IP layer */ + packets_sent++; /* IP packets */ + data_bytes_sent += STATE(tsdu_length); + } + + pgm_free_skb (STATE(skb)); + STATE(data_bytes_offset) += STATE(tsdu_length); + +/* check for end of transmission group */ + if (sock->use_proactive_parity) { + const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); + const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; + if (!((odata_sqn + 1) & ~tg_sqn_mask)) + pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); + } + + } +#ifdef TRANSPORT_DEBUG + if (is_one_apdu) + { + pgm_assert( STATE(data_bytes_offset) == STATE(apdu_length) ); + } +#endif + + sock->is_apdu_eagain = FALSE; + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; + if (bytes_written) + *bytes_written = data_bytes_sent; + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_NORMAL; + +blocked: + if (bytes_sent) { + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; + } + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + if (EAGAIN == errno) { + if (sock->use_pgmcc) + pgm_notify_clear (&sock->ack_notify); + return PGM_IO_STATUS_WOULD_BLOCK; + } + return PGM_IO_STATUS_RATE_LIMITED; +} + +/* cleanup resuming send state helper + */ +#undef STATE + +/* send repair packet. + * + * on success, TRUE is returned. on error, FALSE is returned. + */ + +static +bool +send_rdata ( + pgm_sock_t* restrict sock, + struct pgm_sk_buff_t* restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + pgm_assert ((char*)skb->tail > (char*)skb->head); + + const size_t tpdu_length = (char*)skb->tail - (char*)skb->head; + +/* update previous odata/rdata contents */ + struct pgm_header* header = skb->pgm_header; + struct pgm_data* rdata = skb->pgm_data; + header->pgm_type = PGM_RDATA; +/* RDATA */ + rdata->data_trail = htonl (pgm_txw_trail(sock->window)); + + header->pgm_checksum = 0; + const size_t pgm_header_len = tpdu_length - ntohs(header->pgm_tsdu_length); + uint32_t unfolded_header = pgm_csum_partial (header, pgm_header_len, 0); + uint32_t unfolded_odata = pgm_txw_get_unfolded_checksum (skb); + header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, unfolded_odata, pgm_header_len)); + +/* congestion control */ + if (sock->use_pgmcc && + sock->tokens < pgm_fp8 (1)) + { +// pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Token limit reached.")); + sock->blocklen = tpdu_length; + return FALSE; + } + + const ssize_t sent = pgm_sendto (sock, + sock->is_controlled_rdata, /* rate limited */ + TRUE, /* with router alert */ + header, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + + if (sent < 0 && EAGAIN == errno) { + sock->blocklen = tpdu_length; + return FALSE; + } + + const pgm_time_t now = pgm_time_update_now(); + + if (sock->use_pgmcc) { + sock->tokens -= pgm_fp8 (1); + sock->ack_expiry = now + sock->ack_expiry_ivl; + } + +/* re-set spm timer: we are already in the timer thread, no need to prod timers + */ + pgm_mutex_lock (&sock->timer_mutex); + sock->spm_heartbeat_state = 1; + sock->next_heartbeat_spm = now + sock->spm_heartbeat_interval[sock->spm_heartbeat_state++]; + pgm_mutex_unlock (&sock->timer_mutex); + + pgm_txw_inc_retransmit_count (skb); + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_BYTES_RETRANSMITTED] += ntohs(header->pgm_tsdu_length); + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED]++; /* impossible to determine APDU count */ + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length + sock->iphdr_len); + return TRUE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/source_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/source_unittest.c new file mode 100644 index 0000000..27ffebe --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/source_unittest.c @@ -0,0 +1,1216 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for PGM source transport. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* mock state */ + +#define TEST_NETWORK "" +#define TEST_PORT 7500 +#define TEST_MAX_TPDU 1500 +#define TEST_TXW_SQNS 32 +#define TEST_RXW_SQNS 32 +#define TEST_HOPS 16 +#define TEST_SPM_AMBIENT ( pgm_secs(30) ) +#define TEST_SPM_HEARTBEAT_INIT { pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(1300), pgm_secs(7), pgm_secs(16), pgm_secs(25), pgm_secs(30) } +#define TEST_PEER_EXPIRY ( pgm_secs(300) ) +#define TEST_SPMR_EXPIRY ( pgm_msecs(250) ) +#define TEST_NAK_BO_IVL ( pgm_msecs(50) ) +#define TEST_NAK_RPT_IVL ( pgm_secs(2) ) +#define TEST_NAK_RDATA_IVL ( pgm_secs(2) ) +#define TEST_NAK_DATA_RETRIES 5 +#define TEST_NAK_NCF_RETRIES 2 + +static gboolean mock_is_valid_spmr = TRUE; +static gboolean mock_is_valid_ack = TRUE; +static gboolean mock_is_valid_nak = TRUE; +static gboolean mock_is_valid_nnak = TRUE; + + +#define pgm_txw_get_unfolded_checksum mock_pgm_txw_get_unfolded_checksum +#define pgm_txw_set_unfolded_checksum mock_pgm_txw_set_unfolded_checksum +#define pgm_txw_inc_retransmit_count mock_pgm_txw_inc_retransmit_count +#define pgm_txw_add mock_pgm_txw_add +#define pgm_txw_peek mock_pgm_txw_peek +#define pgm_txw_retransmit_push mock_pgm_txw_retransmit_push +#define pgm_txw_retransmit_try_peek mock_pgm_txw_retransmit_try_peek +#define pgm_txw_retransmit_remove_head mock_pgm_txw_retransmit_remove_head +#define pgm_rs_encode mock_pgm_rs_encode +#define pgm_rate_check mock_pgm_rate_check +#define pgm_verify_spmr mock_pgm_verify_spmr +#define pgm_verify_ack mock_pgm_verify_ack +#define pgm_verify_nak mock_pgm_verify_nak +#define pgm_verify_nnak mock_pgm_verify_nnak +#define pgm_compat_csum_partial mock_pgm_compat_csum_partial +#define pgm_compat_csum_partial_copy mock_pgm_compat_csum_partial_copy +#define pgm_csum_block_add mock_pgm_csum_block_add +#define pgm_csum_fold mock_pgm_csum_fold +#define pgm_sendto mock_pgm_sendto +#define pgm_time_update_now mock_pgm_time_update_now + + +#define SOURCE_DEBUG +#include "source.c" + + +static +void +mock_setup (void) +{ + if (!g_thread_supported ()) g_thread_init (NULL); +} + +static +struct pgm_sock_t* +generate_sock (void) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, g_htons(1000) }; + struct pgm_sock_t* sock = g_new0 (struct pgm_sock_t, 1); + memcpy (&sock->tsi, &tsi, sizeof(pgm_tsi_t)); + ((struct sockaddr*)&sock->send_addr)->sa_family = AF_INET; + ((struct sockaddr_in*)&sock->send_addr)->sin_addr.s_addr = inet_addr ("127.0.0.2"); + ((struct sockaddr*)&sock->send_gsr.gsr_group)->sa_family = AF_INET; + ((struct sockaddr_in*)&sock->send_gsr.gsr_group)->sin_addr.s_addr = inet_addr ("239.192.0.1"); + sock->dport = g_htons(TEST_PORT); + sock->window = g_malloc0 (sizeof(pgm_txw_t)); + sock->txw_sqns = TEST_TXW_SQNS; + sock->max_tpdu = TEST_MAX_TPDU; + sock->max_tsdu = TEST_MAX_TPDU - sizeof(struct pgm_ip) - pgm_pkt_offset (FALSE, FALSE); + sock->max_tsdu_fragment = TEST_MAX_TPDU - sizeof(struct pgm_ip) - pgm_pkt_offset (TRUE, FALSE); + sock->max_apdu = MIN(TEST_TXW_SQNS, PGM_MAX_FRAGMENTS) * sock->max_tsdu_fragment; + sock->iphdr_len = sizeof(struct pgm_ip); + sock->spm_heartbeat_interval = g_malloc0 (sizeof(guint) * (2+2)); + sock->spm_heartbeat_interval[0] = pgm_secs(1); + pgm_spinlock_init (&sock->txw_spinlock); + sock->is_bound = FALSE; + sock->is_destroyed = FALSE; + return sock; +} + +static +struct pgm_sk_buff_t* +generate_skb (void) +{ + const char source[] = "i am not a string"; + struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); + pgm_skb_reserve (skb, pgm_pkt_offset (FALSE, FALSE)); + pgm_skb_put (skb, sizeof(source)); + memcpy (skb->data, source, sizeof(source)); + return skb; +} + +static +struct pgm_sk_buff_t* +generate_fragment_skb (void) +{ + const char source[] = "i am not a string"; + struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); + pgm_skb_reserve (skb, pgm_pkt_offset (TRUE, FALSE)); + pgm_skb_put (skb, sizeof(source)); + memcpy (skb->data, source, sizeof(source)); + return skb; +} + +static +struct pgm_sk_buff_t* +generate_odata (void) +{ + const char source[] = "i am not a string"; + const guint16 header_length = sizeof(struct pgm_header) + sizeof(struct pgm_data); + struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); + pgm_skb_reserve (skb, header_length); + memset (skb->head, 0, header_length); + skb->pgm_header = (struct pgm_header*)skb->head; + skb->pgm_data = (struct pgm_data*)(skb->pgm_header + 1); + skb->pgm_header->pgm_type = PGM_ODATA; + skb->pgm_header->pgm_tsdu_length = g_htons (sizeof(source)); + memcpy (skb->data, source, sizeof(source)); + pgm_skb_put (skb, sizeof(source)); +/* reverse pull */ + skb->len += (guint8*)skb->data - (guint8*)skb->head; + skb->data = skb->head; + return skb; +} + +static +pgm_peer_t* +generate_peer (void) +{ + pgm_peer_t* peer = g_malloc0 (sizeof(pgm_peer_t)); + return peer; +} + +static +struct pgm_sk_buff_t* +generate_spmr (void) +{ + struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); + const guint16 header_length = sizeof(struct pgm_header); + pgm_skb_reserve (skb, header_length); + memset (skb->head, 0, header_length); + skb->pgm_header = (struct pgm_header*)skb->head; + skb->pgm_header->pgm_type = PGM_SPMR; + pgm_skb_put (skb, header_length); + return skb; +} + +static +struct pgm_sk_buff_t* +generate_single_nak (void) +{ + struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); + const guint16 header_length = sizeof(struct pgm_header) + sizeof(struct pgm_nak); + pgm_skb_reserve (skb, sizeof(struct pgm_header)); + memset (skb->head, 0, header_length); + skb->pgm_header = (struct pgm_header*)skb->head; + skb->pgm_header->pgm_type = PGM_NAK; + struct pgm_nak* nak = (struct pgm_nak*)(skb->pgm_header + 1); + struct sockaddr_in nla = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr("127.0.0.2") + }; + pgm_sockaddr_to_nla ((struct sockaddr*)&nla, (char*)&nak->nak_src_nla_afi); + struct sockaddr_in group = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr("239.192.0.1") + }; + pgm_sockaddr_to_nla ((struct sockaddr*)&group, (char*)&nak->nak_grp_nla_afi); + pgm_skb_put (skb, header_length); + return skb; +} + +static +struct pgm_sk_buff_t* +generate_single_nnak (void) +{ + struct pgm_sk_buff_t* skb = generate_single_nak (); + skb->pgm_header->pgm_type = PGM_NNAK; + return skb; +} + +static +struct pgm_sk_buff_t* +generate_parity_nak (void) +{ + struct pgm_sk_buff_t* skb = generate_single_nak (); + skb->pgm_header->pgm_options = PGM_OPT_PARITY; + return skb; +} + +static +struct pgm_sk_buff_t* +generate_nak_list (void) +{ + struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); + const guint16 header_length = sizeof(struct pgm_header) + sizeof(struct pgm_nak) + + sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + + ( 62 * sizeof(guint32) ); + pgm_skb_reserve (skb, sizeof(struct pgm_header)); + memset (skb->head, 0, header_length); + skb->pgm_header = (struct pgm_header*)skb->head; + skb->pgm_header->pgm_type = PGM_NAK; + skb->pgm_header->pgm_options = PGM_OPT_PRESENT | PGM_OPT_NETWORK; + struct pgm_nak *nak = (struct pgm_nak*)(skb->pgm_header + 1); + struct sockaddr_in nla = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr("127.0.0.2") + }; + pgm_sockaddr_to_nla ((struct sockaddr*)&nla, (char*)&nak->nak_src_nla_afi); + struct sockaddr_in group = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr("239.192.0.1") + }; + pgm_sockaddr_to_nla ((struct sockaddr*)&group, (char*)&nak->nak_grp_nla_afi); + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(nak + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = g_htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_nak_list) + + ( 62 * sizeof(guint32) ) ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_NAK_LIST | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + + ( 62 * sizeof(guint32) ); + struct pgm_opt_nak_list* opt_nak_list = (struct pgm_opt_nak_list*)(opt_header + 1); + for (unsigned i = 1; i < 63; i++) { + opt_nak_list->opt_sqn[i-1] = g_htonl (i); + } + pgm_skb_put (skb, header_length); + return skb; +} + +static +struct pgm_sk_buff_t* +generate_parity_nak_list (void) +{ + struct pgm_sk_buff_t* skb = generate_nak_list (); + skb->pgm_header->pgm_options = PGM_OPT_PARITY | PGM_OPT_PRESENT | PGM_OPT_NETWORK; + return skb; +} + +void +mock_pgm_txw_add ( + pgm_txw_t* const window, + struct pgm_sk_buff_t* const skb + ) +{ + g_debug ("mock_pgm_txw_add (window:%p skb:%p)", + (gpointer)window, (gpointer)skb); +} + +struct pgm_sk_buff_t* +mock_pgm_txw_peek ( + const pgm_txw_t* const window, + const uint32_t sequence + ) +{ + g_debug ("mock_pgm_txw_peek (window:%p sequence:%" G_GUINT32_FORMAT ")", + (gpointer)window, sequence); + return NULL; +} + +bool +mock_pgm_txw_retransmit_push ( + pgm_txw_t* const window, + const uint32_t sequence, + const bool is_parity, + const uint8_t tg_sqn_shift + ) +{ + g_debug ("mock_pgm_txw_retransmit_push (window:%p sequence:%" G_GUINT32_FORMAT " is-parity:%s tg-sqn-shift:%d)", + (gpointer)window, + sequence, + is_parity ? "YES" : "NO", + tg_sqn_shift); + return TRUE; +} + +void +mock_pgm_txw_set_unfolded_checksum ( + struct pgm_sk_buff_t*const skb, + const uint32_t csum + ) +{ +} + +uint32_t +pgm_txw_get_unfolded_checksum ( + const struct pgm_sk_buff_t*const skb + ) +{ + return 0; +} + +void +mock_pgm_txw_inc_retransmit_count ( + struct pgm_sk_buff_t*const skb + ) +{ +} + +struct pgm_sk_buff_t* +mock_pgm_txw_retransmit_try_peek ( + pgm_txw_t* const window + ) +{ + g_debug ("mock_pgm_txw_retransmit_try_peek (window:%p)", + (gpointer)window); + return generate_odata (); +} + +void +mock_pgm_txw_retransmit_remove_head ( + pgm_txw_t* const window + ) +{ + g_debug ("mock_pgm_txw_retransmit_remove_head (window:%p)", + (gpointer)window); +} + +void +mock_pgm_rs_encode ( + pgm_rs_t* rs, + const pgm_gf8_t** src, + uint8_t offset, + pgm_gf8_t* dst, + uint16_t len + ) +{ + g_debug ("mock_pgm_rs_encode (rs:%p src:%p offset:%u dst:%p len:%" G_GSIZE_FORMAT ")", + rs, src, offset, dst, len); +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_rate_check ( + pgm_rate_t* bucket, + const size_t data_size, + const bool is_nonblocking + ) +{ + g_debug ("mock_pgm_rate_check (bucket:%p data-size:%u is-nonblocking:%s)", + bucket, data_size, is_nonblocking ? "TRUE" : "FALSE"); + return TRUE; +} + +bool +mock_pgm_verify_spmr ( + const struct pgm_sk_buff_t* const skb + ) +{ + return mock_is_valid_spmr; +} + +bool +mock_pgm_verify_ack ( + const struct pgm_sk_buff_t* const skb + ) +{ + return mock_is_valid_ack; +} + +bool +mock_pgm_verify_nak ( + const struct pgm_sk_buff_t* const skb + ) +{ + return mock_is_valid_nak; +} + +bool +mock_pgm_verify_nnak ( + const struct pgm_sk_buff_t* const skb + ) +{ + return mock_is_valid_nnak; +} + +uint32_t +mock_pgm_compat_csum_partial ( + const void* addr, + uint16_t len, + uint32_t csum + ) +{ + return 0x0; +} + +uint32_t +mock_pgm_compat_csum_partial_copy ( + const void* src, + void* dst, + uint16_t len, + uint32_t csum + ) +{ + return 0x0; +} + +uint32_t +mock_pgm_csum_block_add ( + uint32_t csum, + uint32_t csum2, + uint16_t offset + ) +{ + return 0x0; +} + +uint16_t +mock_pgm_csum_fold ( + uint32_t csum + ) +{ + return 0x0; +} + +PGM_GNUC_INTERNAL +ssize_t +mock_pgm_sendto ( + pgm_sock_t* sock, + bool use_rate_limit, + bool use_router_alert, + const void* buf, + size_t len, + const struct sockaddr* to, + socklen_t tolen + ) +{ + char saddr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop (to, saddr, sizeof(saddr)); + g_debug ("mock_pgm_sendto (sock:%p use-rate-limit:%s use-router-alert:%s buf:%p len:%d to:%s tolen:%d)", + (gpointer)sock, + use_rate_limit ? "YES" : "NO", + use_router_alert ? "YES" : "NO", + buf, + len, + saddr, + tolen); + return len; +} + +/** time module */ +static pgm_time_t _mock_pgm_time_update_now (void); +pgm_time_update_func mock_pgm_time_update_now = _mock_pgm_time_update_now; + +static +pgm_time_t +_mock_pgm_time_update_now (void) +{ + return 0x1; +} + +/** socket module */ +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return can_fragment ? ( sizeof(struct pgm_header) + + sizeof(struct pgm_data) + + sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment) ) + : ( sizeof(struct pgm_header) + sizeof(struct pgm_data) ); +} + + +/* mock functions for external references */ + + +/* target: + * PGMIOStatus + * pgm_send ( + * pgm_sock_t* sock, + * gconstpointer apdu, + * gsize apdu_length, + * gsize* bytes_written + * ) + */ + +START_TEST (test_send_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->is_bound = TRUE; + const gsize apdu_length = 100; + guint8 buffer[ apdu_length ]; + gsize bytes_written; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_send (sock, buffer, apdu_length, &bytes_written), "send not normal"); + fail_unless ((gssize)apdu_length == bytes_written, "send underrun"); +} +END_TEST + +/* large apdu */ +START_TEST (test_send_pass_002) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->is_bound = TRUE; + const gsize apdu_length = 16000; + guint8 buffer[ apdu_length ]; + gsize bytes_written; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_send (sock, buffer, apdu_length, &bytes_written), "send not normal"); + fail_unless ((gssize)apdu_length == bytes_written, "send underrun"); +} +END_TEST + +START_TEST (test_send_fail_001) +{ + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + const gsize apdu_length = 100; + gsize bytes_written; + fail_unless (PGM_IO_STATUS_ERROR == pgm_send (NULL, buffer, apdu_length, &bytes_written), "send not error"); +} +END_TEST + +/* target: + * PGMIOStatus + * pgm_sendv ( + * pgm_sock_t* sock, + * const struct pgmiovec* vector, + * guint count, + * gboolean is_one_apdu, + * gsize* bytes_written + * ) + */ + +START_TEST (test_sendv_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->is_bound = TRUE; + const gsize apdu_length = 100; + guint8 buffer[ apdu_length ]; + struct pgm_iovec vector[] = { { .iov_base = buffer, .iov_len = apdu_length } }; + gsize bytes_written; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_sendv (sock, vector, 1, TRUE, &bytes_written), "send not normal"); + fail_unless ((gssize)apdu_length == bytes_written, "send underrun"); +} +END_TEST + +/* large apdu */ +START_TEST (test_sendv_pass_002) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->is_bound = TRUE; + const gsize apdu_length = 16000; + guint8 buffer[ apdu_length ]; + struct pgm_iovec vector[] = { { .iov_base = buffer, .iov_len = apdu_length } }; + gsize bytes_written; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_sendv (sock, vector, 1, TRUE, &bytes_written), "send not normal"); + fail_unless ((gssize)apdu_length == bytes_written, "send underrun"); +} +END_TEST + +/* multipart apdu */ +START_TEST (test_sendv_pass_003) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->is_bound = TRUE; + const gsize apdu_length = 16000; + guint8 buffer[ apdu_length ]; + struct pgm_iovec vector[ 16 ]; + for (unsigned i = 0; i < G_N_ELEMENTS(vector); i++) { + vector[i].iov_base = &buffer[ (i * apdu_length) / G_N_ELEMENTS(vector) ]; + vector[i].iov_len = apdu_length / G_N_ELEMENTS(vector); + } + gsize bytes_written; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_sendv (sock, vector, G_N_ELEMENTS(vector), TRUE, &bytes_written), "send not normal"); + fail_unless ((gssize)apdu_length == bytes_written, "send underrun"); +} +END_TEST + +/* multiple apdus */ +START_TEST (test_sendv_pass_004) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->is_bound = TRUE; + const gsize apdu_length = 16000; + struct pgm_iovec vector[ 16 ]; + for (unsigned i = 0; i < G_N_ELEMENTS(vector); i++) { + vector[i].iov_base = g_malloc0 (apdu_length); + vector[i].iov_len = apdu_length; + } + gsize bytes_written; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_sendv (sock, vector, G_N_ELEMENTS(vector), FALSE, &bytes_written), "send not normal"); + fail_unless ((gssize)(apdu_length * G_N_ELEMENTS(vector)) == bytes_written, "send underrun"); +} +END_TEST + +START_TEST (test_sendv_fail_001) +{ + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + const gsize tsdu_length = 100; + struct pgm_iovec vector[] = { { .iov_base = buffer, .iov_len = tsdu_length } }; + gsize bytes_written; + fail_unless (PGM_IO_STATUS_ERROR == pgm_sendv (NULL, vector, 1, TRUE, &bytes_written), "send not error"); +} +END_TEST + +/* target: + * PGMIOStatus + * pgm_send_skbv ( + * pgm_sock_t* sock, + * struct pgm_sk_buff_t* vector[], + * guint count, + * gboolean is_one_apdu, + * gsize* bytes_written + * ) + */ + +START_TEST (test_send_skbv_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->is_bound = TRUE; + struct pgm_sk_buff_t* skb = NULL; + skb = generate_skb (); + fail_if (NULL == skb, "generate_skb failed"); + gsize apdu_length = (gsize)skb->len; + gsize bytes_written; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_send_skbv (sock, &skb, 1, TRUE, &bytes_written), "send not normal"); + fail_unless (apdu_length == bytes_written, "send underrun"); +} +END_TEST + +/* multipart apdu */ +START_TEST (test_send_skbv_pass_002) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->is_bound = TRUE; + struct pgm_sk_buff_t* skb[16]; + for (unsigned i = 0; i < G_N_ELEMENTS(skb); i++) { + skb[i] = generate_fragment_skb (); + fail_if (NULL == skb[i], "generate_fragment_skb failed"); + } + gsize apdu_length = (gsize)skb[0]->len * G_N_ELEMENTS(skb); + gsize bytes_written; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_send_skbv (sock, skb, G_N_ELEMENTS(skb), TRUE, &bytes_written), "send not normal"); + fail_unless (apdu_length == bytes_written, "send underrun"); +} +END_TEST + +/* multiple apdus */ +START_TEST (test_send_skbv_pass_003) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->is_bound = TRUE; + struct pgm_sk_buff_t* skb[16]; + for (unsigned i = 0; i < G_N_ELEMENTS(skb); i++) { + skb[i] = generate_skb (); + fail_if (NULL == skb[i], "generate_skb failed"); + } + gsize bytes_written; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_send_skbv (sock, skb, G_N_ELEMENTS(skb), FALSE, &bytes_written), "send not normal"); + fail_unless ((gssize)(skb[0]->len * G_N_ELEMENTS(skb)) == bytes_written, "send underrun"); +} +END_TEST + +START_TEST (test_send_skbv_fail_001) +{ + struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU), *skbv[] = { skb }; + fail_if (NULL == skb, "alloc_skb failed"); +/* reserve PGM header */ + pgm_skb_put (skb, pgm_pkt_offset (TRUE, FALSE)); + const gsize tsdu_length = 100; + gsize bytes_written; + fail_unless (PGM_IO_STATUS_ERROR == pgm_send_skbv (NULL, skbv, 1, TRUE, &bytes_written), "send not error"); +} +END_TEST + +/* target: + * gboolean + * pgm_send_spm ( + * pgm_sock_t* sock, + * int flags + * ) + */ + +START_TEST (test_send_spm_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + fail_unless (TRUE == pgm_send_spm (sock, 0), "send_spm failed"); +} +END_TEST + +START_TEST (test_send_spm_fail_001) +{ + pgm_send_spm (NULL, 0); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_on_deferred_nak ( + * pgm_sock_t* sock + * ) + */ + +START_TEST (test_on_deferred_nak_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + pgm_on_deferred_nak (sock); +} +END_TEST + +START_TEST (test_on_deferred_nak_fail_001) +{ + pgm_on_deferred_nak (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * gboolean + * pgm_on_spmr ( + * pgm_sock_t* sock, + * pgm_peer_t* peer, + * struct pgm_sk_buff_t* skb + * ) + */ + +/* peer spmr */ +START_TEST (test_on_spmr_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + pgm_peer_t* peer = generate_peer (); + fail_if (NULL == peer, "generate_peer failed"); + struct pgm_sk_buff_t* skb = generate_spmr (); + fail_if (NULL == skb, "generate_spmr failed"); + skb->sock = sock; + fail_unless (TRUE == pgm_on_spmr (sock, peer, skb), "on_spmr failed"); +} +END_TEST + +/* source spmr */ +START_TEST (test_on_spmr_pass_002) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + struct pgm_sk_buff_t* skb = generate_spmr (); + fail_if (NULL == skb, "generate_spmr failed"); + skb->sock = sock; + fail_unless (TRUE == pgm_on_spmr (sock, NULL, skb), "on_spmr failed"); +} +END_TEST + +/* invalid spmr */ +START_TEST (test_on_spmr_fail_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + pgm_peer_t* peer = generate_peer (); + fail_if (NULL == peer, "generate_peer failed"); + struct pgm_sk_buff_t* skb = generate_spmr (); + fail_if (NULL == skb, "generate_spmr failed"); + skb->sock = sock; + mock_is_valid_spmr = FALSE; + fail_unless (FALSE == pgm_on_spmr (sock, peer, skb), "on_spmr failed"); +} +END_TEST + +START_TEST (test_on_spmr_fail_002) +{ + pgm_on_spmr (NULL, NULL, NULL); + fail ("reached"); +} +END_TEST + +/* target: + * gboolean + * pgm_on_nak ( + * pgm_sock_t* sock, + * struct pgm_sk_buff_t* skb + * ) + */ + +/* single nak */ +START_TEST (test_on_nak_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + struct pgm_sk_buff_t* skb = generate_single_nak (); + fail_if (NULL == skb, "generate_single_nak failed"); + skb->sock = sock; + fail_unless (TRUE == pgm_on_nak (sock, skb), "on_nak failed"); +} +END_TEST + +/* nak list */ +START_TEST (test_on_nak_pass_002) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + struct pgm_sk_buff_t* skb = generate_nak_list (); + fail_if (NULL == skb, "generate_nak_list failed"); + skb->sock = sock; + fail_unless (TRUE == pgm_on_nak (sock, skb), "on_nak failed"); +} +END_TEST + +/* single parity nak */ +START_TEST (test_on_nak_pass_003) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->use_ondemand_parity = TRUE; + struct pgm_sk_buff_t* skb = generate_parity_nak (); + fail_if (NULL == skb, "generate_parity_nak failed"); + skb->sock = sock; + fail_unless (TRUE == pgm_on_nak (sock, skb), "on_nak failed"); +} +END_TEST + +/* parity nak list */ +START_TEST (test_on_nak_pass_004) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->use_ondemand_parity = TRUE; + struct pgm_sk_buff_t* skb = generate_parity_nak_list (); + fail_if (NULL == skb, "generate_parity_nak_list failed"); + skb->sock = sock; + fail_unless (TRUE == pgm_on_nak (sock, skb), "on_nak failed"); +} +END_TEST + +START_TEST (test_on_nak_fail_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + struct pgm_sk_buff_t* skb = generate_single_nak (); + fail_if (NULL == skb, "generate_single_nak failed"); + skb->sock = sock; + mock_is_valid_nak = FALSE; + fail_unless (FALSE == pgm_on_nak (sock, skb), "on_nak failed"); +} +END_TEST + +START_TEST (test_on_nak_fail_002) +{ + pgm_on_nak (NULL, NULL); + fail ("reached"); +} +END_TEST + +/* target: + * gboolean + * pgm_on_nnak ( + * pgm_sock_t* sock, + * struct pgm_sk_buff_t* skb + * ) + */ + +START_TEST (test_on_nnak_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + struct pgm_sk_buff_t* skb = generate_single_nnak (); + fail_if (NULL == skb, "generate_single_nnak failed"); + skb->sock = sock; + fail_unless (TRUE == pgm_on_nnak (sock, skb), "on_nnak failed"); +} +END_TEST + +START_TEST (test_on_nnak_fail_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + struct pgm_sk_buff_t* skb = generate_single_nnak (); + fail_if (NULL == skb, "generate_single_nnak failed"); + skb->sock = sock; + mock_is_valid_nnak = FALSE; + fail_unless (FALSE == pgm_on_nnak (sock, skb), "on_nnak failed"); +} +END_TEST + +START_TEST (test_on_nnak_fail_002) +{ + pgm_on_nnak (NULL, NULL); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_AMBIENT_SPM, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_ambient_spm_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int optname = PGM_AMBIENT_SPM; + const int ambient_spm = pgm_msecs(1000); + const void* optval = &ambient_spm; + const socklen_t optlen = sizeof(ambient_spm); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_ambient_spm failed"); +} +END_TEST + +START_TEST (test_set_ambient_spm_fail_001) +{ + const int optname = PGM_AMBIENT_SPM; + const int ambient_spm = pgm_msecs(1000); + const void* optval = &ambient_spm; + const socklen_t optlen = sizeof(ambient_spm); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_ambient_spm failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_HEARTBEAT_SPM, + * const void* optval, + * const socklen_t optlen = sizeof(int) * n + * ) + */ + +START_TEST (test_set_heartbeat_spm_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int optname = PGM_HEARTBEAT_SPM; + const int intervals[] = { 1, 2, 3, 4, 5 }; + const void* optval = &intervals; + const socklen_t optlen = sizeof(intervals); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_heartbeat_spm failed"); +} +END_TEST + +START_TEST (test_set_heartbeat_spm_fail_001) +{ + const int optname = PGM_HEARTBEAT_SPM; + const int intervals[] = { 1, 2, 3, 4, 5 }; + const void* optval = &intervals; + const socklen_t optlen = sizeof(intervals); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_heartbeat_spm failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_TXW_SQNS, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_txw_sqns_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int optname = PGM_TXW_SQNS; + const int txw_sqns = 100; + const void* optval = &txw_sqns; + const socklen_t optlen = sizeof(txw_sqns); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_txw_sqns failed"); +} +END_TEST + +START_TEST (test_set_txw_sqns_fail_001) +{ + const int optname = PGM_TXW_SQNS; + const int txw_sqns = 100; + const void* optval = &txw_sqns; + const socklen_t optlen = sizeof(txw_sqns); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_txw_sqns failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_TXW_SECS, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_txw_secs_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int optname = PGM_TXW_SECS; + const int txw_secs = pgm_secs(10); + const void* optval = &txw_secs; + const socklen_t optlen = sizeof(txw_secs); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_txw_secs failed"); +} +END_TEST + +START_TEST (test_set_txw_secs_fail_001) +{ + const int optname = PGM_TXW_SECS; + const int txw_secs = pgm_secs(10); + const void* optval = &txw_secs; + const socklen_t optlen = sizeof(txw_secs); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_txw_secs failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int optname = PGM_TXW_MAX_RTE, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_txw_max_rte_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int optname = PGM_TXW_MAX_RTE; + const int txw_max_rte = 100*1000; + const void* optval = &txw_max_rte; + const socklen_t optlen = sizeof(txw_max_rte); + fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_txw_max_rte failed"); +} +END_TEST + +START_TEST (test_set_txw_max_rte_fail_001) +{ + const int optname = PGM_TXW_MAX_RTE; + const int txw_max_rte = 100*1000; + const void* optval = &txw_max_rte; + const socklen_t optlen = sizeof(txw_max_rte); + fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_txw_max_rte failed"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_send = tcase_create ("send"); + suite_add_tcase (s, tc_send); + tcase_add_checked_fixture (tc_send, mock_setup, NULL); + tcase_add_test (tc_send, test_send_pass_001); + tcase_add_test (tc_send, test_send_pass_002); + tcase_add_test (tc_send, test_send_fail_001); + + TCase* tc_sendv = tcase_create ("sendv"); + suite_add_tcase (s, tc_sendv); + tcase_add_checked_fixture (tc_sendv, mock_setup, NULL); + tcase_add_test (tc_sendv, test_sendv_pass_001); + tcase_add_test (tc_sendv, test_sendv_pass_002); + tcase_add_test (tc_sendv, test_sendv_pass_003); + tcase_add_test (tc_sendv, test_sendv_pass_004); + tcase_add_test (tc_sendv, test_sendv_fail_001); + + TCase* tc_send_skbv = tcase_create ("send-skbv"); + suite_add_tcase (s, tc_send_skbv); + tcase_add_checked_fixture (tc_send_skbv, mock_setup, NULL); + tcase_add_test (tc_send_skbv, test_send_skbv_pass_001); + tcase_add_test (tc_send_skbv, test_send_skbv_pass_002); + tcase_add_test (tc_send_skbv, test_send_skbv_fail_001); + + TCase* tc_send_spm = tcase_create ("send-spm"); + suite_add_tcase (s, tc_send_spm); + tcase_add_checked_fixture (tc_send_spm, mock_setup, NULL); + tcase_add_test (tc_send_spm, test_send_spm_pass_001); + tcase_add_test_raise_signal (tc_send_spm, test_send_spm_fail_001, SIGABRT); + + TCase* tc_on_deferred_nak = tcase_create ("on-deferred-nak"); + suite_add_tcase (s, tc_on_deferred_nak); + tcase_add_checked_fixture (tc_on_deferred_nak, mock_setup, NULL); + tcase_add_test (tc_on_deferred_nak, test_on_deferred_nak_pass_001); + tcase_add_test_raise_signal (tc_on_deferred_nak, test_on_deferred_nak_fail_001, SIGABRT); + + TCase* tc_on_spmr = tcase_create ("on-spmr"); + suite_add_tcase (s, tc_on_spmr); + tcase_add_checked_fixture (tc_on_spmr, mock_setup, NULL); + tcase_add_test (tc_on_spmr, test_on_spmr_pass_001); + tcase_add_test (tc_on_spmr, test_on_spmr_pass_002); + tcase_add_test (tc_on_spmr, test_on_spmr_fail_001); + tcase_add_test_raise_signal (tc_on_spmr, test_on_spmr_fail_002, SIGABRT); + + TCase* tc_on_nak = tcase_create ("on-nak"); + suite_add_tcase (s, tc_on_nak); + tcase_add_checked_fixture (tc_on_nak, mock_setup, NULL); + tcase_add_test (tc_on_nak, test_on_nak_pass_001); + tcase_add_test (tc_on_nak, test_on_nak_pass_002); + tcase_add_test (tc_on_nak, test_on_nak_pass_003); + tcase_add_test (tc_on_nak, test_on_nak_pass_004); + tcase_add_test (tc_on_nak, test_on_nak_fail_001); + tcase_add_test_raise_signal (tc_on_nak, test_on_nak_fail_002, SIGABRT); + + TCase* tc_on_nnak = tcase_create ("on-nnak"); + suite_add_tcase (s, tc_on_nnak); + tcase_add_checked_fixture (tc_on_nnak, mock_setup, NULL); + tcase_add_test (tc_on_nnak, test_on_nnak_pass_001); + tcase_add_test (tc_on_nnak, test_on_nnak_fail_001); + tcase_add_test_raise_signal (tc_on_nnak, test_on_nnak_fail_002, SIGABRT); + + TCase* tc_set_ambient_spm = tcase_create ("set-ambient-spm"); + suite_add_tcase (s, tc_set_ambient_spm); + tcase_add_checked_fixture (tc_set_ambient_spm, mock_setup, NULL); + tcase_add_test (tc_set_ambient_spm, test_set_ambient_spm_pass_001); + tcase_add_test (tc_set_ambient_spm, test_set_ambient_spm_fail_001); + + TCase* tc_set_heartbeat_spm = tcase_create ("set-heartbeat-spm"); + suite_add_tcase (s, tc_set_heartbeat_spm); + tcase_add_checked_fixture (tc_set_heartbeat_spm, mock_setup, NULL); + tcase_add_test (tc_set_heartbeat_spm, test_set_heartbeat_spm_pass_001); + tcase_add_test (tc_set_heartbeat_spm, test_set_heartbeat_spm_fail_001); + + TCase* tc_set_txw_sqns = tcase_create ("set-txw-sqns"); + suite_add_tcase (s, tc_set_txw_sqns); + tcase_add_checked_fixture (tc_set_txw_sqns, mock_setup, NULL); + tcase_add_test (tc_set_txw_sqns, test_set_txw_sqns_pass_001); + tcase_add_test (tc_set_txw_sqns, test_set_txw_sqns_fail_001); + + TCase* tc_set_txw_secs = tcase_create ("set-txw-secs"); + suite_add_tcase (s, tc_set_txw_secs); + tcase_add_checked_fixture (tc_set_txw_secs, mock_setup, NULL); + tcase_add_test (tc_set_txw_secs, test_set_txw_secs_pass_001); + tcase_add_test (tc_set_txw_secs, test_set_txw_secs_fail_001); + + TCase* tc_set_txw_max_rte = tcase_create ("set-txw-max-rte"); + suite_add_tcase (s, tc_set_txw_max_rte); + tcase_add_checked_fixture (tc_set_txw_max_rte, mock_setup, NULL); + tcase_add_test (tc_set_txw_max_rte, test_set_txw_max_rte_pass_001); + tcase_add_test (tc_set_txw_max_rte, test_set_txw_max_rte_fail_001); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/string.c b/3rdparty/openpgm-svn-r1085/pgm/string.c new file mode 100644 index 0000000..93458fd --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/string.c @@ -0,0 +1,486 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable string manipulation functions. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef CONFIG_HAVE_VASPRINTF +# define _GNU_SOURCE +#endif +#include +#include +#include /* _GNU_SOURCE for vasprintf */ +#include +#include + + +//#define STRING_DEBUG + +/* Return copy of string, must be freed with pgm_free(). + */ + +char* +pgm_strdup ( + const char* str + ) +{ + char* new_str; + size_t length; + + if (PGM_LIKELY (NULL != str)) + { + length = strlen (str) + 1; + new_str = malloc (length); + memcpy (new_str, str, length); + } + else + new_str = NULL; + + return new_str; +} + +/* Calculates the maximum space needed to store the output of the sprintf() function. + */ + +int +pgm_printf_string_upper_bound ( + const char* format, + va_list args + ) +{ + char c; + return vsnprintf (&c, 1, format, args) + 1; +} + +/* memory must be freed with free() + */ + +int +pgm_vasprintf ( + char** restrict string, + const char* restrict format, + va_list args + ) +{ + pgm_return_val_if_fail (string != NULL, -1); +#ifdef CONFIG_HAVE_VASPRINTF + const int len = vasprintf (string, format, args); + if (len < 0) + *string = NULL; +#else + va_list args2; + va_copy (args2, args); + *string = malloc (pgm_printf_string_upper_bound (format, args)); +/* NB: must be able to handle NULL args, fails on GCC */ + const int len = vsprintf (*string, format, args); + va_end (args2); +#endif + return len; +} + +char* +pgm_strdup_vprintf ( + const char* format, + va_list args + ) +{ + char *string = NULL; + pgm_vasprintf (&string, format, args); + return string; +} + +static +char* +pgm_stpcpy ( + char* restrict dest, + const char* restrict src + ) +{ + pgm_return_val_if_fail (dest != NULL, NULL); + pgm_return_val_if_fail (src != NULL, NULL); +#ifdef CONFIG_HAVE_STPCPY + return stpcpy (dest, src); +#else + char *d = dest; + const char *s = src; + do { + *d++ = *s; + } while (*s++ != '\0'); + return d - 1; +#endif +} + +char* +pgm_strconcat ( + const char* string1, + ... + ) +{ + size_t l; + va_list args; + char* s; + char* concat; + char* ptr; + + if (!string1) + return NULL; + + l = 1 + strlen (string1); + va_start (args, string1); + s = va_arg (args, char*); + while (s) { + l += strlen (s); + s = va_arg (args, char*); + } + va_end (args); + + concat = malloc (l); + ptr = concat; + + ptr = pgm_stpcpy (ptr, string1); + va_start (args, string1); + s = va_arg (args, char*); + while (s) { + ptr = pgm_stpcpy (ptr, s); + s = va_arg (args, char*); + } + va_end (args); + + return concat; +} + +/* Split a string with delimiter, result must be freed with pgm_strfreev(). + */ + +char** +pgm_strsplit ( + const char* restrict string, + const char* restrict delimiter, + int max_tokens + ) +{ + pgm_slist_t *string_list = NULL, *slist; + char **str_array, *s; + unsigned n = 0; + const char *remainder; + + pgm_return_val_if_fail (string != NULL, NULL); + pgm_return_val_if_fail (delimiter != NULL, NULL); + pgm_return_val_if_fail (delimiter[0] != '\0', NULL); + + if (max_tokens < 1) + max_tokens = INT_MAX; + + remainder = string; + s = strstr (remainder, delimiter); + if (s) + { + const size_t delimiter_len = strlen (delimiter); + + while (--max_tokens && s) + { + const size_t len = s - remainder; + char *new_string = malloc (len + 1); + strncpy (new_string, remainder, len); + new_string[len] = 0; + string_list = pgm_slist_prepend (string_list, new_string); + n++; + remainder = s + delimiter_len; + s = strstr (remainder, delimiter); + } + } + if (*string) + { + n++; + string_list = pgm_slist_prepend (string_list, pgm_strdup (remainder)); + } + + str_array = pgm_new (char*, n + 1); + str_array[n--] = NULL; + for (slist = string_list; slist; slist = slist->next) + str_array[n--] = slist->data; + + pgm_slist_free (string_list); + + return str_array; +} + +/* Free a NULL-terminated array of strings, such as created by pgm_strsplit + */ + +void +pgm_strfreev ( + char** str_array + ) +{ + if (PGM_LIKELY (NULL != str_array)) + { + for (unsigned i = 0; str_array[i] != NULL; i++) + free (str_array[i]); + + pgm_free (str_array); + } +} + +/* resize dynamic string + */ + +static +void +pgm_string_maybe_expand ( + pgm_string_t* string, + size_t len + ) +{ + if ((string->len + len) >= string->allocated_len) + { + string->allocated_len = pgm_nearest_power (1, string->len + len + 1); + string->str = pgm_realloc (string->str, string->allocated_len); + } +} + +/* val may not be a part of string + */ + +static +pgm_string_t* +pgm_string_insert_len ( + pgm_string_t* restrict string, + ssize_t pos, + const char* restrict val, + ssize_t len + ) +{ + pgm_return_val_if_fail (NULL != string, NULL); + pgm_return_val_if_fail (NULL != val, string); + + if (len < 0) + len = strlen (val); + + if (pos < 0) + pos = string->len; + else + pgm_return_val_if_fail ((size_t)pos <= string->len, string); + + pgm_string_maybe_expand (string, len); + + if ((size_t)pos < string->len) + memmove (string->str + pos + len, string->str + pos, string->len - pos); + + if (len == 1) + string->str[pos] = *val; + else + memcpy (string->str + pos, val, len); + string->len += len; + string->str[string->len] = 0; + return string; +} + +static +pgm_string_t* +pgm_string_insert_c ( + pgm_string_t* string, + ssize_t pos, + char c + ) +{ + pgm_return_val_if_fail (NULL != string, NULL); + + if (pos < 0) + pos = string->len; + else + pgm_return_val_if_fail ((size_t)pos <= string->len, string); + + pgm_string_maybe_expand (string, 1); + + if ((size_t)pos < string->len) + memmove (string->str + pos + 1, string->str + pos, string->len - pos); + + string->str[pos] = c; + string->len ++; + string->str[string->len] = '\0'; + return string; +} + +static +pgm_string_t* +pgm_string_append_len ( + pgm_string_t* restrict string, + const char* restrict val, + size_t len + ) +{ + pgm_return_val_if_fail (NULL != string, NULL); + pgm_return_val_if_fail (NULL != val, string); + + return pgm_string_insert_len (string, -1, val, len); +} + +/* create new dynamic string + */ + +static +pgm_string_t* +pgm_string_sized_new ( + size_t init_size + ) +{ + pgm_string_t* string = pgm_new (pgm_string_t, 1); + string->allocated_len = 0; + string->len = 0; + string->str = NULL; + pgm_string_maybe_expand (string, MAX(init_size, 2)); + string->str[0] = '\0'; + return string; +} + +pgm_string_t* +pgm_string_new ( + const char* init + ) +{ + pgm_string_t* string; + + if (NULL == init || '\0' == *init) + string = pgm_string_sized_new (2); + else + { + const size_t len = strlen (init); + string = pgm_string_sized_new (len + 2); + pgm_string_append_len (string, init, len); + } + return string; +} + +/* free dynamic string, optionally just the wrapper object + */ + +char* +pgm_string_free ( + pgm_string_t* string, + bool free_segment + ) +{ + char* segment; + + pgm_return_val_if_fail (NULL != string, NULL); + + if (free_segment) { + pgm_free (string->str); + segment = NULL; + } else + segment = string->str; + + pgm_free (string); + return segment; +} + +static +pgm_string_t* +pgm_string_truncate ( + pgm_string_t* restrict string, + size_t len + ) +{ + pgm_return_val_if_fail (NULL != string, NULL); + + string->len = MIN (len, string->len); + string->str[ string->len ] = '\0'; + + return string; +} + +pgm_string_t* +pgm_string_append ( + pgm_string_t* restrict string, + const char* restrict val + ) +{ + pgm_return_val_if_fail (NULL != string, NULL); + pgm_return_val_if_fail (NULL != val, string); + + return pgm_string_insert_len (string, -1, val, -1); +} + +pgm_string_t* +pgm_string_append_c ( + pgm_string_t* string, + char c + ) +{ + pgm_return_val_if_fail (NULL != string, NULL); + + return pgm_string_insert_c (string, -1, c); +} + +static void pgm_string_append_vprintf (pgm_string_t*restrict, const char*restrict, va_list) PGM_GNUC_PRINTF(2, 0); + +static +void +pgm_string_append_vprintf ( + pgm_string_t* restrict string, + const char* restrict format, + va_list args + ) +{ + char *buf; + int len; + + pgm_return_if_fail (NULL != string); + pgm_return_if_fail (NULL != format); + + len = pgm_vasprintf (&buf, format, args); + if (len >= 0) { + pgm_string_maybe_expand (string, len); + memcpy (string->str + string->len, buf, len + 1); + string->len += len; + free (buf); + } +} + +void +pgm_string_printf ( + pgm_string_t* restrict string, + const char* restrict format, + ... + ) +{ + va_list args; + + pgm_string_truncate (string, 0); + + va_start (args, format); + pgm_string_append_vprintf (string, format, args); + va_end (args); +} + +void +pgm_string_append_printf ( + pgm_string_t* restrict string, + const char* restrict format, + ... + ) +{ + va_list args; + + va_start (args, format); + pgm_string_append_vprintf (string, format, args); + va_end (args); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/PGM/Test.pm b/3rdparty/openpgm-svn-r1085/pgm/test/PGM/Test.pm new file mode 100644 index 0000000..cb2ee6d --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/PGM/Test.pm @@ -0,0 +1,394 @@ +package PGM::Test; + +use strict; +our($VERSION); +use Carp; +use IO::File; +use IPC::Open2; +use Net::SSH qw(sshopen2); +use Sys::Hostname; +use POSIX ":sys_wait_h"; +use JSON; + +$VERSION = '1.00'; + +=head1 NAME + +PGM::Test - PGM test module + +=head1 SYNOPSIS + + $test = PGM::Test->new(); + +=cut + +my $json = new JSON; + +sub new { + my $class = shift; + my $self = {}; + my %params = @_; + + $self->{tag} = exists $params{tag} ? $params{tag} : confess "tag parameter is required"; + $self->{host} = exists $params{host} ? $params{host} : confess "host parameter is required"; + $self->{cmd} = exists $params{cmd} ? $params{cmd} : confess "cmd parameter is required"; + + $self->{in} = IO::File->new(); + $self->{out} = IO::File->new(); + $self->{pid} = undef; + + bless $self, $class; + return $self; +} + +sub connect { + my $self = shift; + my $host = hostname; + + if ($self->{host} =~ /^(localhost|127\.1|127\.0\.0\.1|$host)$/) + { + print "$self->{tag}: opening local connection\n"; + $self->{pid} = open2 ($self->{in}, + $self->{out}, + "uname -a && sudo $self->{cmd}") + or croak "open2 failed $!"; + } + else + { + print "$self->{tag}: opening SSH connection to $self->{host} ...\n"; + $self->{pid} = sshopen2 ($self->{host}, + $self->{in}, + $self->{out}, + "uname -a && sudo $self->{cmd}") + or croak "SSH failed: $!"; + } + + print "$self->{tag}: connected.\n"; + $self->wait_for_ready; +} + +sub disconnect { + my($self,$quiet) = @_; + my $out = $self->{out}; + + print "$self->{tag}: sending quit command ...\n"; + eval { + local($SIG{ALRM}) = sub { die "alarm\n"; }; + alarm 10; + print $out "quit\n"; + while (readline($self->{in})) { + chomp; + print "$self->{tag} [$_]\n" if (!$quiet); + } + alarm 0; + }; + if ($@) { + print "$self->{tag}: alarm raised on quit command.\n"; + } else { + print "$self->{tag}: eof.\n"; + } + + print "$self->{tag}: closing SSH connection ...\n"; + close ($self->{in}); + close ($self->{out}); + print "$self->{tag}: closed.\n"; +} + +sub DESTROY { + my $self = shift; + + if ($self->{pid}) { + print "$self->{tag}: waiting child to terminate ...\n"; + eval { + local($SIG{ALRM}) = sub { die "alarm\n"; }; + alarm 10; + waitpid $self->{pid}, 0; + alarm 0; + }; + if ($@) { + die unless $@ eq "alarm\n"; + local($SIG{CHLD}) = 'IGNORE'; + print "$self->{tag}: killing child ...\n"; + kill 'INT' => $self->{pid}; + print "$self->{tag}: killed.\n"; + } else { + print "$self->{tag}: terminated.\n"; + } + } +} + +sub wait_for_ready { + my $self = shift; + + while (readline($self->{in})) { + chomp; + print "$self->{tag} [$_]\n"; + last if /^READY/; + } +} + +sub wait_for_block { + my $self = shift; + my $fh = $self->{in}; + my $b = ''; + my $state = 0; + + while (<$fh>) { + chomp(); + my $l = $_; + if ($state == 0) { + if ($l =~ /^{$/) { + $state = 1; + } else { + print "$self->{tag} [$l]\n"; + } + } + + if ($state == 1) { + $b .= $l; + + if ($l =~ /^}$/) { + $state = 0; + return $b; + } + } + } +} + +sub wait_for_spm { + my $self = shift; + my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; + my $obj = undef; + + eval { + local $SIG{ALRM} = sub { die "alarm\n"; }; + alarm $timeout; + for (;;) { + my $block = $self->wait_for_block; + $obj = $json->jsonToObj($block); + last if ($obj->{PGM}->{type} =~ /SPM$/); + } + alarm 0; + }; + if ($@) { + die unless $@ eq "alarm\n"; + confess "$self->{tag}: alarm raised waiting for spm.\n"; + } + + return $obj; +} + +sub wait_for_spmr { + my $self = shift; + my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; + my $obj = undef; + + eval { + local $SIG{ALRM} = sub { die "alarm\n"; }; + alarm $timeout; + for (;;) { + my $block = $self->wait_for_block; + $obj = $json->jsonToObj($block); + last if ($obj->{PGM}->{type} =~ /SPMR/); + } + alarm 0; + }; + if ($@) { + die unless $@ eq "alarm\n"; + confess "$self->{tag}: alarm raised waiting for spmr.\n"; + } + + return $obj; +} + +sub die_on_spmr { + my $self = shift; + my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; + my $obj = undef; + + eval { + local $SIG{ALRM} = sub { die "alarm\n"; }; + alarm $timeout; + for (;;) { + my $block = $self->wait_for_block; + $obj = $json->jsonToObj($block); + last if ($obj->{PGM}->{type} =~ /SPMR/); + } + alarm 0; + }; + if ($@) { + die unless $@ eq "alarm\n"; + return $obj; + } + + confess "$self->{tag}: spmr received during blackout.\n"; +} + +# data to {app} +sub wait_for_data { + my $self = shift; + my $fh = $self->{in}; + my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; + my $data = undef; + + eval { + local $SIG{ALRM} = sub { die "alarm\n"; }; + alarm $timeout; + while (<$fh>) { + chomp; + if (/^DATA: (.+)$/) { + $data = $1; + last; + } + print "$self->{tag} [$_]\n"; + } + alarm 0; + }; + if ($@) { + die unless $@ eq "alarm\n"; + confess "$self->{tag}: alarm raised waiting for data.\n"; + } + + return $data; +} + +sub wait_for_odata { + my $self = shift; + my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; + my $obj = undef; + + eval { + local $SIG{ALRM} = sub { die "alarm\n"; }; + alarm $timeout; + for (;;) { + my $block = $self->wait_for_block; + $obj = $json->jsonToObj($block); + last if ($obj->{PGM}->{type} =~ /ODATA/); + } + alarm 0; + }; + if ($@) { + die unless $@ eq "alarm\n"; + confess "$self->{tag}: alarm raised waiting for odata.\n"; + } + + return $obj; +} + +sub wait_for_rdata { + my $self = shift; + my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; + my $obj = undef; + + eval { + local $SIG{ALRM} = sub { die "alarm\n"; }; + alarm $timeout; + for (;;) { + my $block = $self->wait_for_block; + $obj = $json->jsonToObj($block); + last if ($obj->{PGM}->{type} =~ /RDATA/); + } + alarm 0; + }; + if ($@) { + die unless $@ eq "alarm\n"; + confess "$self->{tag}: alarm raised waiting for rdata.\n"; + } + + return $obj; +} + +sub die_on_nak { + my $self = shift; + my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; + my $obj = undef; + + eval { + local $SIG{ALRM} = sub { die "alarm\n"; }; + alarm $timeout; + for (;;) { + my $block = $self->wait_for_block; + $obj = $json->jsonToObj($block); + last if ($obj->{PGM}->{type} =~ /NAK/); + } + alarm 0; + }; + if ($@) { + die unless $@ eq "alarm\n"; + return $obj; + } + + confess "$self->{tag}: nak received during blackout.\n"; +} + +sub wait_for_nak { + my $self = shift; + my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; + my $obj = undef; + + eval { + local $SIG{ALRM} = sub { die "alarm\n"; }; + alarm $timeout; + for (;;) { + my $block = $self->wait_for_block; + $obj = $json->jsonToObj($block); + last if ($obj->{PGM}->{type} =~ /NAK/); + } + alarm 0; + }; + if ($@) { + die unless $@ eq "alarm\n"; + confess "$self->{tag}: alarm raised waiting for nak.\n"; + } + + return $obj; +} + +sub wait_for_ncf { + my $self = shift; + my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; + my $obj = undef; + + eval { + local $SIG{ALRM} = sub { die "alarm\n"; }; + alarm $timeout; + for (;;) { + my $block = $self->wait_for_block; + $obj = $json->jsonToObj($block); + last if ($obj->{PGM}->{type} =~ /NCF/); + } + alarm 0; + }; + if ($@) { + die unless $@ eq "alarm\n"; + confess "$self->{tag}: alarm raised waiting for ncf.\n"; + } + + return $obj; +} + +sub print { + my $self = shift; + my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; + my $out = $self->{out}; + + print "$self->{tag}> @_"; + eval { + local($SIG{ALRM}) = sub { die "alarm\n"; }; + alarm $timeout; + print $out "@_"; + $self->wait_for_ready; + alarm 0; + }; + if ($@) { + die unless $@ eq "alarm\n"; + confess "$self->{tag}: alarm raised.\n"; + } +} + +sub say { + my $self = shift; + $self->print ("@_\n"); +} + +1; diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/SConscript b/3rdparty/openpgm-svn-r1085/pgm/test/SConscript new file mode 100644 index 0000000..7ca9926 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/SConscript @@ -0,0 +1,15 @@ +# -*- mode: python -*- +# OpenPGM build script +# $Id$ + +Import('env') +e = env.Clone(); +e.MergeFlags(env['GLIB_FLAGS']); +e.Append(LIBS = ['libpgm', 'libpgmex']); +e.Append(CCFLAGS = '-DGETTEXT_PACKAGE=\'"pgm"\''); + +e.Program(['monitor.c', 'dump-json.c']) +e.Program(['app.c', 'async.c']) +e.Program(['sim.c', 'dump-json.c', 'async.c']) + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/ambient_spm.pl b/3rdparty/openpgm-svn-r1085/pgm/test/ambient_spm.pl new file mode 100755 index 0000000..dca52a5 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/ambient_spm.pl @@ -0,0 +1,86 @@ +#!/usr/bin/perl +# ambient_spm.pl +# 5.1.4. Ambient SPMs + +use strict; +use PGM::Test; +use IO::Handle; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +pipe(FROM_PARENT, TO_CHILD) or die "pipe: $!"; +FROM_PARENT->autoflush(1); + +$mon->connect; +$app->connect; + +sub close_ssh { + close FROM_PARENT; close TO_CHILD; + $mon = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +if (my $pid = fork) { +# parent + close FROM_PARENT; + + print "mon: wait for odata ...\n"; + $mon->wait_for_odata; + print "mon: odata received.\n"; + print "mon: wait for spm ...\n"; + $mon->wait_for_spm ({ 'timeout' => 45 }); + print "mon: received spm.\n"; + + print TO_CHILD "die\n"; + + close TO_CHILD; + waitpid($pid,0); +} else { +# child + die "cannot fork: $!" unless defined $pid; + close TO_CHILD; + print "app: loop sending data.\n"; + vec(my $rin, fileno(FROM_PARENT), 1) = 1; + my $rout = undef; + +# hide stdout + open(OLDOUT, ">&STDOUT"); + open(STDOUT, ">/dev/null") or die "Can't redirect stdout: $!"; + +# send every ~50ms + while (! select($rout = $rin, undef, undef, 0.05)) + { + $app->say ("send ao ringo"); + } + +# restore stdout + close(STDOUT) or die "Can't close STDOUT: $!"; + open(STDOUT, ">&OLDOUT") or die "Can't restore stdout: $!"; + close(OLDOUT) or die "Can't close OLDOUT: $!"; + + print "app: loop finished.\n"; + close FROM_PARENT; + exit; +} + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/apdu.pl b/3rdparty/openpgm-svn-r1085/pgm/test/apdu.pl new file mode 100755 index 0000000..f4669c0 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/apdu.pl @@ -0,0 +1,56 @@ +#!/usr/bin/perl +# apdu.pl +# 6.1. Data Reception + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("set network $config{app}{network}"); +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +$sim->say ("set network $config{sim}{network}"); +$sim->say ("create ao"); +$sim->say ("bind ao"); + +print "sim: publish APDU.\n"; +$sim->say ("send ao ringo x 1000"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: received data [$data].\n"; + +my $ref_data = "ringo" x 1000; +die "incoming data corrupt\n" unless ($data == $ref_data); + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/apdu_parity.pl b/3rdparty/openpgm-svn-r1085/pgm/test/apdu_parity.pl new file mode 100755 index 0000000..eb4cf27 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/apdu_parity.pl @@ -0,0 +1,59 @@ +#!/usr/bin/perl +# apdu_parity.pl +# 6.1. Data Reception + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$sim->connect; +$app->connect; + +sub close_ssh { + $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$app->say ("create ao"); +##$app->say ("set ao FEC RS(255,4)"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +$sim->say ("create ao"); +$sim->say ("set ao FEC RS(255,4)"); +$sim->say ("bind ao"); + +print "sim: publish APDU.\n"; +$sim->say ("send brokn ao ringo x 1200"); + +print "sim: insert parity NAK from app.\n"; + +#print "sim: wait for NAK.\n"; +#my $nak = $sim->wait_for_nak; +#die "Selective NAK received, parityPacket=false\n" unless $nak->{PGM}->{options}->{parityPacket}; +#print "Parity NAK received.\n"; + +print "sim: insert parity RDATA from sim.\n"; + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: received data [$data].\n"; + +my $ref_data = "ringo" x 1200; +die "incoming data corrupt\n" unless ($data == $ref_data); + +print "test completed successfully.\n"; + +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/app.c b/3rdparty/openpgm-svn-r1085/pgm/test/app.c new file mode 100644 index 0000000..1d8f585 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/app.c @@ -0,0 +1,904 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM conformance test application. + * + * Copyright (c) 2006-2007 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "async.h" + + +/* typedefs */ + +struct idle_source { + GSource source; + guint64 expiration; +}; + +struct app_session { + char* name; + pgm_transport_t* transport; + pgm_async_t* async; +}; + +/* globals */ +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "app" + +static int g_port = 7500; +static const char* g_network = ";239.192.0.1"; + +static guint g_max_tpdu = 1500; +static guint g_sqns = 100 * 1000; + +static GHashTable* g_sessions = NULL; +static GMainLoop* g_loop = NULL; +static GIOChannel* g_stdin_channel = NULL; + + +static void on_signal (int, gpointer); +static gboolean on_startup (gpointer); +static gboolean on_mark (gpointer); +static void destroy_session (gpointer, gpointer, gpointer); +static int on_data (gpointer, guint, gpointer); +static gboolean on_stdin_data (GIOChannel*, GIOCondition, gpointer); + + +G_GNUC_NORETURN static +void +usage (const char* bin) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + exit (1); +} + +int +main ( + int argc, + char *argv[] + ) +{ + pgm_error_t* err = NULL; + +/* pre-initialise PGM messages module to add hook for GLib logging */ + pgm_messages_init(); + log_init (); + g_message ("app"); + + if (!pgm_init (&err)) { + g_error ("Unable to start PGM engine: %s", (err && err->message) ? err->message : "(null)"); + pgm_error_free (err); + pgm_messages_shutdown(); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:h")) != -1) + { + switch (c) { + case 'n': g_network = optarg; break; + case 's': g_port = atoi (optarg); break; + + case 'h': + case '?': + pgm_messages_shutdown(); + usage (binary_name); + } + } + + g_loop = g_main_loop_new (NULL, FALSE); + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); + signal (SIGHUP, SIG_IGN); + pgm_signal_install (SIGINT, on_signal, g_loop); + pgm_signal_install (SIGTERM, on_signal, g_loop); + +/* delayed startup */ + g_message ("scheduling startup."); + g_timeout_add (0, (GSourceFunc)on_startup, NULL); + +/* dispatch loop */ + g_message ("entering main event loop ... "); + g_main_loop_run (g_loop); + + g_message ("event loop terminated, cleaning up."); + +/* cleanup */ + g_main_loop_unref(g_loop); + g_loop = NULL; + + if (g_sessions) { + g_message ("destroying sessions."); + g_hash_table_foreach_remove (g_sessions, (GHRFunc)destroy_session, NULL); + g_hash_table_unref (g_sessions); + g_sessions = NULL; + } + + if (g_stdin_channel) { + puts ("unbinding stdin."); + g_io_channel_unref (g_stdin_channel); + g_stdin_channel = NULL; + } + + g_message ("PGM engine shutdown."); + pgm_shutdown(); + g_message ("finished."); + pgm_messages_shutdown(); + return EXIT_SUCCESS; +} + +static +void +destroy_session ( + gpointer key, /* session name */ + gpointer value, /* transport_session object */ + G_GNUC_UNUSED gpointer user_data + ) +{ + struct app_session* sess = (struct app_session*)value; + + g_message ("destroying transport \"%s\"", (char*)key); + pgm_transport_destroy (sess->transport, TRUE); + sess->transport = NULL; + + if (sess->async) { + g_message ("destroying asynchronous session on \"%s\"", (char*)key); + pgm_async_destroy (sess->async); + sess->async = NULL; + } + + g_free (sess->name); + sess->name = NULL; + g_free (sess); +} + +static +void +on_signal ( + int signum, + gpointer user_data + ) +{ + GMainLoop* loop = (GMainLoop*)user_data; + g_message ("on_signal (signum:%d user-data:%p)", signum, user_data); + g_main_loop_quit (loop); +} + +static +gboolean +on_startup ( + G_GNUC_UNUSED gpointer data + ) +{ + g_message ("startup."); + + g_sessions = g_hash_table_new (g_str_hash, g_str_equal); + +/* add stdin to event manager */ + g_stdin_channel = g_io_channel_unix_new (fileno(stdin)); + printf ("binding stdin with encoding %s.\n", g_io_channel_get_encoding(g_stdin_channel)); + + g_io_add_watch (g_stdin_channel, G_IO_IN | G_IO_PRI, on_stdin_data, NULL); + +/* period timer to indicate some form of life */ +// TODO: Gnome 2.14: replace with g_timeout_add_seconds() + g_timeout_add(10 * 1000, (GSourceFunc)on_mark, NULL); + + puts ("READY"); + fflush (stdout); + return FALSE; +} + +static +int +on_data ( + gpointer data, + G_GNUC_UNUSED guint len, + G_GNUC_UNUSED gpointer user_data + ) +{ + printf ("DATA: %s\n", (char*)data); + fflush (stdout); + return 0; +} + +static +void +session_create ( + char* name + ) +{ + struct pgm_transport_info_t hints = { + .ti_family = AF_INET + }, *res = NULL; + pgm_error_t* err = NULL; + +/* check for duplicate */ + struct app_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess != NULL) { + puts ("FAILED: duplicate session"); + return; + } + +/* create new and fill in bits */ + sess = g_new0(struct app_session, 1); + sess->name = g_memdup (name, strlen(name)+1); + + if (!pgm_if_get_transport_info (g_network, &hints, &res, &err)) { + printf ("FAILED: pgm_if_get_transport_info(): %s\n", (err && err->message) ? err->message : "(null)"); + pgm_error_free (err); + goto err_free; + } + + if (!pgm_gsi_create_from_hostname (&res->ti_gsi, &err)) { + printf ("FAILED: pgm_gsi_create_from_hostname(): %s\n", (err && err->message) ? err->message : "(null)"); + pgm_error_free (err); + pgm_if_free_transport_info (res); + goto err_free; + } + + res->ti_dport = g_port; + res->ti_sport = 0; +printf ("pgm_transport_create (transport:%p res:%p err:%p)\n", (gpointer)sess->transport, (gpointer)res, (gpointer)&err); + if (!pgm_transport_create (&sess->transport, res, &err)) { + printf ("FAILED: pgm_transport_create(): %s\n", (err && err->message) ? err->message : "(null)"); + pgm_error_free (err); + pgm_if_free_transport_info (res); + goto err_free; + } + + pgm_if_free_transport_info (res); + +/* success */ + g_hash_table_insert (g_sessions, sess->name, sess); + printf ("created new session \"%s\"\n", sess->name); + puts ("READY"); + + return; + +err_free: + g_free(sess->name); + g_free(sess); +} + +static +void +session_set_nak_bo_ivl ( + char* name, + guint nak_bo_ivl /* milliseconds */ + ) +{ +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess == NULL) { + puts ("FAILED: session not found"); + return; + } + + if (!pgm_transport_set_nak_bo_ivl (sess->transport, pgm_msecs(nak_bo_ivl))) + puts ("FAILED: pgm_transport_set_nak_bo_ivl"); + else + puts ("READY"); +} + +static +void +session_set_nak_rpt_ivl ( + char* name, + guint nak_rpt_ivl /* milliseconds */ + ) +{ +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess == NULL) { + puts ("FAILED: session not found"); + return; + } + + if (!pgm_transport_set_nak_rpt_ivl (sess->transport, pgm_msecs(nak_rpt_ivl))) + puts ("FAILED: pgm_transport_set_nak_rpt_ivl"); + else + puts ("READY"); +} + +static +void +session_set_nak_rdata_ivl ( + char* name, + guint nak_rdata_ivl /* milliseconds */ + ) +{ +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess == NULL) { + puts ("FAILED: session not found"); + return; + } + + if (!pgm_transport_set_nak_rdata_ivl (sess->transport, pgm_msecs(nak_rdata_ivl))) + puts ("FAILED: pgm_transport_set_nak_rdata_ivl"); + else + puts ("READY"); +} + +static +void +session_set_nak_ncf_retries ( + char* name, + guint nak_ncf_retries + ) +{ +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess == NULL) { + puts ("FAILED: session not found"); + return; + } + + if (!pgm_transport_set_nak_ncf_retries (sess->transport, nak_ncf_retries)) + puts ("FAILED pgm_transport_set_nak_ncf_retries"); + else + puts ("READY"); +} + +static +void +session_set_nak_data_retries ( + char* name, + guint nak_data_retries + ) +{ +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess == NULL) { + puts ("FAILED: session not found"); + return; + } + + if (!pgm_transport_set_nak_data_retries (sess->transport, nak_data_retries)) + puts ("FAILED: pgm_transport_set_nak_data_retries"); + else + puts ("READY"); +} + +static +void +session_set_txw_max_rte ( + char* name, + guint txw_max_rte + ) +{ +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess == NULL) { + puts ("FAILED: session not found"); + return; + } + + if (!pgm_transport_set_txw_max_rte (sess->transport, txw_max_rte)) + puts ("FAILED:pgm_transport_set_txw_max_rte"); + else + puts ("READY"); +} + +static +void +session_set_fec ( + char* name, + guint default_n, + guint default_k + ) +{ +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess == NULL) { + puts ("FAILED: session not found"); + return; + } + + if (!pgm_transport_set_fec (sess->transport, + FALSE /* pro-active */, + TRUE /* on-demand */, + TRUE /* varpkt-len */, + default_n, + default_k)) + { + puts ("FAILED: pgm_transport_set_fec"); + } + else + { + puts ("READY"); + } +} + +static +void +session_bind ( + char* name + ) +{ + const guint spm_heartbeat[] = { pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(1300), pgm_secs(7), pgm_secs(16), pgm_secs(25), pgm_secs(30) }; + pgm_error_t* err = NULL; + +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess == NULL) { + puts ("FAILED: session not found"); + return; + } + + if (!pgm_transport_set_nonblocking (sess->transport, TRUE)) + puts ("FAILED: pgm_transport_set_nonblocking"); + if (!pgm_transport_set_max_tpdu (sess->transport, g_max_tpdu)) + puts ("FAILED: pgm_transport_set_max_tpdu"); + if (!pgm_transport_set_txw_sqns (sess->transport, g_sqns)) + puts ("FAILED: pgm_transport_set_txw_sqns"); + if (!pgm_transport_set_rxw_sqns (sess->transport, g_sqns)) + puts ("FAILED: pgm_transport_set_rxw_sqns"); + if (!pgm_transport_set_hops (sess->transport, 16)) + puts ("FAILED: pgm_transport_set_hops"); + if (!pgm_transport_set_ambient_spm (sess->transport, pgm_secs(30))) + puts ("FAILED: pgm_transport_set_ambient_spm"); + if (!pgm_transport_set_heartbeat_spm (sess->transport, spm_heartbeat, G_N_ELEMENTS(spm_heartbeat))) + puts ("FAILED: pgm_transport_set_heartbeat_spm"); + if (!pgm_transport_set_peer_expiry (sess->transport, pgm_secs(300))) + puts ("FAILED: pgm_transport_set_peer_expiry"); + if (!pgm_transport_set_spmr_expiry (sess->transport, pgm_msecs(250))) + puts ("FAILED: pgm_transport_set_spmr_expiry"); + if (!sess->transport->nak_bo_ivl && !pgm_transport_set_nak_bo_ivl (sess->transport, pgm_msecs(50))) + puts ("FAILED: pgm_transport_set_nak_bo_ivl"); + if (!sess->transport->nak_rpt_ivl && !pgm_transport_set_nak_rpt_ivl (sess->transport, pgm_secs(2))) + puts ("FAILED: pgm_transport_set_nak_rpt_ivl"); + if (!sess->transport->nak_rdata_ivl && !pgm_transport_set_nak_rdata_ivl (sess->transport, pgm_secs(2))) + puts ("FAILED: pgm_transport_set_nak_rdata_ivl"); + if (!sess->transport->nak_data_retries && !pgm_transport_set_nak_data_retries (sess->transport, 50)) + puts ("FAILED: pgm_transport_set_nak_data_retries"); + if (!sess->transport->nak_ncf_retries && !pgm_transport_set_nak_ncf_retries (sess->transport, 50)) + puts ("FAILED: pgm_transport_set_nak_ncf_retries"); + +printf ("pgm_transport_bind (transport:%p err:%p)\n", (gpointer)sess->transport, (gpointer)&err); + if (!pgm_transport_bind (sess->transport, &err)) { + printf ("FAILED: pgm_transport_bind(): %s\n", (err && err->message) ? err->message : "(null)"); + pgm_error_free (err); + } else + puts ("READY"); +} + +static +void +session_send ( + char* name, + char* string + ) +{ +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess == NULL) { + puts ("FAILED: session not found"); + return; + } + +/* send message */ + int status; + gsize stringlen = strlen(string) + 1; + int n_fds = 1; + struct pollfd fds[ n_fds ]; + struct timeval tv; + int timeout; +again: +printf ("pgm_send (transport:%p string:\"%s\" stringlen:%" G_GSIZE_FORMAT " NULL)\n", (gpointer)sess->transport, string, stringlen); + status = pgm_send (sess->transport, string, stringlen, NULL); + switch (status) { + case PGM_IO_STATUS_NORMAL: + puts ("READY"); + break; + case PGM_IO_STATUS_TIMER_PENDING: + pgm_transport_get_timer_pending (sess->transport, &tv); + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + pgm_transport_get_rate_remaining (sess->transport, &tv); +/* fall through */ + case PGM_IO_STATUS_WOULD_BLOCK: +block: + timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + memset (fds, 0, sizeof(fds)); + pgm_transport_poll_info (sess->transport, fds, &n_fds, POLLOUT); + poll (fds, n_fds, timeout /* ms */); + goto again; + default: + puts ("FAILED: pgm_send()"); + break; + } +} + +static +void +session_listen ( + char* name + ) +{ + GError* err = NULL; + +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess == NULL) { + puts ("FAILED: session not found"); + return; + } + +/* listen */ +printf ("pgm_async_create (async:%p transport:%p err:%p)\n", (gpointer)&sess->async, (gpointer)sess->transport, (gpointer)&err); + if (!pgm_async_create (&sess->async, sess->transport, &err)) { + printf ("FAILED: pgm_async_create(): %s", err->message); + g_error_free (err); + return; + } + pgm_async_add_watch (sess->async, on_data, sess); + puts ("READY"); +} + +static +void +session_destroy ( + char* name + ) +{ +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess == NULL) { + puts ("FAILED: session not found"); + return; + } + +/* remove from hash table */ + g_hash_table_remove (g_sessions, name); + +/* stop any async thread */ + if (sess->async) { + pgm_async_destroy (sess->async); + sess->async = NULL; + } + + pgm_transport_destroy (sess->transport, TRUE); + sess->transport = NULL; + g_free (sess->name); + sess->name = NULL; + g_free (sess); + + puts ("READY"); +} + +/* process input commands from stdin/fd + */ + +static +gboolean +on_stdin_data ( + GIOChannel* source, + G_GNUC_UNUSED GIOCondition condition, + G_GNUC_UNUSED gpointer data + ) +{ + gchar* str = NULL; + gsize len = 0; + gsize term = 0; + GError* err = NULL; + + g_io_channel_read_line (source, &str, &len, &term, &err); + if (len > 0) { + if (term) str[term] = 0; + +/* quit */ + if (strcmp(str, "quit") == 0) + { + g_main_loop_quit(g_loop); + goto out; + } + + regex_t preg; + regmatch_t pmatch[10]; + +/* create transport */ + const char *re = "^create[[:space:]]+([[:alnum:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + session_create (name); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* set NAK_BO_IVL */ + re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+NAK_BO_IVL[[:space:]]+([0-9]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + char *p = str + pmatch[2].rm_so; + guint nak_bo_ivl = strtol (p, &p, 10); + + session_set_nak_bo_ivl (name, nak_bo_ivl); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* set NAK_RPT_IVL */ + re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+NAK_RPT_IVL[[:space:]]+([0-9]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + char *p = str + pmatch[2].rm_so; + guint nak_rpt_ivl = strtol (p, &p, 10); + + session_set_nak_rpt_ivl (name, nak_rpt_ivl); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* set NAK_RDATA_IVL */ + re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+NAK_RDATA_IVL[[:space:]]+([0-9]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + char *p = str + pmatch[2].rm_so; + guint nak_rdata_ivl = strtol (p, &p, 10); + + session_set_nak_rdata_ivl (name, nak_rdata_ivl); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* set NAK_NCF_RETRIES */ + re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+NAK_NCF_RETRIES[[:space:]]+([0-9]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + char *p = str + pmatch[2].rm_so; + guint nak_ncf_retries = strtol (p, &p, 10); + + session_set_nak_ncf_retries (name, nak_ncf_retries); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* set NAK_DATA_RETRIES */ + re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+NAK_DATA_RETRIES[[:space:]]+([0-9]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + char *p = str + pmatch[2].rm_so; + guint nak_data_retries = strtol (p, &p, 10); + + session_set_nak_data_retries (name, nak_data_retries); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* set TXW_MAX_RTE */ + re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+TXW_MAX_RTE[[:space:]]+([0-9]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + char *p = str + pmatch[2].rm_so; + guint txw_max_rte = strtol (p, &p, 10); + + session_set_txw_max_rte (name, txw_max_rte); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* enable Reed-Solomon Forward Error Correction */ + re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+FEC[[:space:]]+RS[[:space:]]*\\([[:space:]]*([0-9]+)[[:space:]]*,[[:space:]]*([0-9]+)[[:space:]]*\\)$"; + regcomp (&preg, re, REG_EXTENDED); + + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + char *p = str + pmatch[2].rm_so; + *(str + pmatch[2].rm_eo) = 0; + guint n = strtol (p, &p, 10); + p = str + pmatch[3].rm_so; + *(str + pmatch[3].rm_eo) = 0; + guint k = strtol (p, &p, 10); + session_set_fec (name, n, k); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* bind transport */ + re = "^bind[[:space:]]+([[:alnum:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + session_bind (name); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* send packet */ + re = "^send[[:space:]]+([[:alnum:]]+)[[:space:]]+([[:alnum:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + char *string = g_memdup (str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so + 1 ); + string[ pmatch[2].rm_eo - pmatch[2].rm_so ] = 0; + + session_send (name, string); + + g_free (name); + g_free (string); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* listen */ + re = "^listen[[:space:]]+([[:alnum:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + session_listen (name); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* destroy transport */ + re = "^destroy[[:space:]]+([[:alnum:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + session_destroy (name); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* set PGM network */ + re = "^set[[:space:]]+network[[:space:]]+([[:print:]]*;[[:print:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char* pgm_network = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + pgm_network[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + g_network = pgm_network; + puts ("READY"); + + regfree (&preg); + goto out; + } + regfree (&preg); + + printf ("unknown command: %s\n", str); + } + +out: + fflush (stdout); + g_free (str); + return TRUE; +} + +/* idle log notification + */ + +static +gboolean +on_mark ( + G_GNUC_UNUSED gpointer data + ) +{ + g_message ("-- MARK --"); + return TRUE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/async.c b/3rdparty/openpgm-svn-r1085/pgm/test/async.c new file mode 100644 index 0000000..cc2c1ba --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/async.c @@ -0,0 +1,572 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Asynchronous queue for receiving packets in a separate managed thread. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include "async.h" + + +//#define ASYNC_DEBUG + +#ifndef ASYNC_DEBUG +# define g_trace(...) while (0) +#else +#include +# define g_trace(...) g_debug(__VA_ARGS__) +#endif + + +/* globals */ + + +/* global locals */ + +typedef struct pgm_event_t pgm_event_t; + +struct pgm_event_t { + gpointer data; + guint len; +}; + + +/* external: Glib event loop GSource of pgm contiguous data */ +struct pgm_watch_t { + GSource source; + GPollFD pollfd; + pgm_async_t* async; +}; + +typedef struct pgm_watch_t pgm_watch_t; + + +static gboolean pgm_src_prepare (GSource*, gint*); +static gboolean pgm_src_check (GSource*); +static gboolean pgm_src_dispatch (GSource*, GSourceFunc, gpointer); + +static GSourceFuncs g_pgm_watch_funcs = { + .prepare = pgm_src_prepare, + .check = pgm_src_check, + .dispatch = pgm_src_dispatch, + .finalize = NULL, + .closure_callback = NULL +}; + + +static inline gpointer pgm_event_alloc (pgm_async_t* const) G_GNUC_MALLOC; +static PGMAsyncError pgm_async_error_from_errno (const gint); + + +static inline +gpointer +pgm_event_alloc ( + pgm_async_t* const async + ) +{ + g_return_val_if_fail (async != NULL, NULL); + return g_slice_alloc (sizeof(pgm_event_t)); +} + +/* release event memory for custom async queue dispatch handlers + */ + +static inline +void +pgm_event_unref ( + pgm_async_t* const async, + pgm_event_t* const event + ) +{ + g_return_if_fail (async != NULL); + g_return_if_fail (event != NULL); + g_slice_free1 (sizeof(pgm_event_t), event); +} + +/* internal receiver thread, sits in a loop processing incoming packets + */ + +static +gpointer +pgm_receiver_thread ( + gpointer data + ) +{ + g_assert (NULL != data); + + pgm_async_t* async = (pgm_async_t*)data; + g_async_queue_ref (async->commit_queue); + +/* incoming message buffer */ + struct pgm_msgv_t msgv; + gsize bytes_read = 0; + struct timeval tv; + + do { +/* blocking read */ + const int status = pgm_recvmsg (async->transport, &msgv, 0, &bytes_read, NULL); + switch (status) { + case PGM_IO_STATUS_NORMAL: + { +/* queue a copy to receiver */ + pgm_event_t* event = pgm_event_alloc (async); + event->data = bytes_read > 0 ? g_malloc (bytes_read) : NULL; + event->len = bytes_read; + gpointer dst = event->data; + guint i = 0; + while (bytes_read) { + const struct pgm_sk_buff_t* skb = msgv.msgv_skb[i++]; + g_assert (NULL != skb); + g_assert (skb->len > 0); + g_assert (skb->len <= bytes_read); + memcpy (dst, skb->data, skb->len); + dst = (char*)dst + skb->len; + bytes_read -= skb->len; + } +/* prod pipe on edge */ + g_async_queue_lock (async->commit_queue); + g_async_queue_push_unlocked (async->commit_queue, event); + if (g_async_queue_length_unlocked (async->commit_queue) == 1) + pgm_notify_send (&async->commit_notify); + g_async_queue_unlock (async->commit_queue); + break; + } + + case PGM_IO_STATUS_TIMER_PENDING: + { + pgm_transport_get_timer_pending (async->transport, &tv); + goto block; + } + + case PGM_IO_STATUS_RATE_LIMITED: + { + pgm_transport_get_rate_remaining (async->transport, &tv); + } +/* fall through */ + case PGM_IO_STATUS_WOULD_BLOCK: +block: + { +#ifdef CONFIG_HAVE_POLL + const int timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + int n_fds = 3; + struct pollfd fds[1+n_fds]; + memset (fds, 0, sizeof(fds)); + fds[0].fd = pgm_notify_get_fd (&async->destroy_notify); + fds[0].events = POLLIN; + if (-1 == pgm_transport_poll_info (async->transport, &fds[1], &n_fds, POLLIN)) { + g_trace ("poll_info returned errno=%i",errno); + goto cleanup; + } + const int ready = poll (fds, 1 + n_fds, timeout); +#else /* HAVE_SELECT */ + fd_set readfds; + int fd = pgm_notify_get_fd (&async->destroy_notify), n_fds = 1 + fd; + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + if (-1 == pgm_transport_select_info (async->transport, &readfds, NULL, &n_fds)) { + g_trace ("select_info returned errno=%i",errno); + goto cleanup; + } + const int ready = select (n_fds, &readfds, NULL, NULL, PGM_IO_STATUS_RATE_LIMITED == status ? &tv : NULL); +#endif + if (-1 == ready) { + g_trace ("block returned errno=%i",errno); + goto cleanup; + } +#ifdef CONFIG_HAVE_POLL + if (ready > 0 && fds[0].revents) +#else + if (ready > 0 && FD_ISSET(fd, &readfds)) +#endif + goto cleanup; + break; + } + + case PGM_IO_STATUS_ERROR: + case PGM_IO_STATUS_EOF: + goto cleanup; + + case PGM_IO_STATUS_RESET: + if (async->transport->is_abort_on_reset) + goto cleanup; + break; + +/* TODO: report to user */ + case PGM_IO_STATUS_FIN: + break; + + default: + g_assert_not_reached(); + } + } while (!async->is_destroyed); + +cleanup: + g_async_queue_unref (async->commit_queue); + return NULL; +} + +/* create asynchronous thread handler + * + * on success, 0 is returned. on error, -1 is returned, and errno set appropriately. + * on invalid parameters, -EINVAL is returned. + */ + +gboolean +pgm_async_create ( + pgm_async_t** async, + pgm_transport_t* const transport, + GError** error + ) +{ + pgm_async_t* new_async; + + g_return_val_if_fail (NULL != async, FALSE); + g_return_val_if_fail (NULL != transport, FALSE); + + g_trace ("create (async:%p transport:%p error:%p)", + (gpointer)async, (gpointer)transport, (gpointer)error); + + if (!g_thread_supported()) + g_thread_init (NULL); + + new_async = g_new0 (pgm_async_t, 1); + new_async->transport = transport; + if (0 != pgm_notify_init (&new_async->commit_notify) || + 0 != pgm_notify_init (&new_async->destroy_notify)) + { + g_set_error (error, + PGM_ASYNC_ERROR, + pgm_async_error_from_errno (errno), + _("Creating async notification channels: %s"), + g_strerror (errno)); + g_free (new_async); + return FALSE; + } + new_async->commit_queue = g_async_queue_new(); +/* setup new thread */ + new_async->thread = g_thread_create_full (pgm_receiver_thread, + new_async, + 0, + TRUE, + TRUE, + G_THREAD_PRIORITY_HIGH, + error); + if (NULL == new_async->thread) { + g_async_queue_unref (new_async->commit_queue); + pgm_notify_destroy (&new_async->commit_notify); + g_free (new_async); + return FALSE; + } + +/* return new object */ + *async = new_async; + return TRUE; +} + +/* tell async thread to stop, wait for it to stop, then cleanup. + * + * on success, 0 is returned. if async is invalid, -EINVAL is returned. + */ + +gboolean +pgm_async_destroy ( + pgm_async_t* const async + ) +{ + g_return_val_if_fail (NULL != async, FALSE); + g_return_val_if_fail (!async->is_destroyed, FALSE); + + async->is_destroyed = TRUE; + pgm_notify_send (&async->destroy_notify); + if (async->thread) + g_thread_join (async->thread); + if (async->commit_queue) { + g_async_queue_unref (async->commit_queue); + async->commit_queue = NULL; + } + pgm_notify_destroy (&async->destroy_notify); + pgm_notify_destroy (&async->commit_notify); + g_free (async); + return TRUE; +} + +/* queue to GSource and GMainLoop */ + +GSource* +pgm_async_create_watch ( + pgm_async_t* async + ) +{ + g_return_val_if_fail (async != NULL, NULL); + + GSource *source = g_source_new (&g_pgm_watch_funcs, sizeof(pgm_watch_t)); + pgm_watch_t *watch = (pgm_watch_t*)source; + + watch->async = async; + watch->pollfd.fd = pgm_async_get_fd (async); + watch->pollfd.events = G_IO_IN; + + g_source_add_poll (source, &watch->pollfd); + + return source; +} + +/* pgm transport attaches to the callees context: the default context instead of + * any internal contexts. + */ + +int +pgm_async_add_watch_full ( + pgm_async_t* async, + gint priority, + pgm_eventfn_t function, + gpointer user_data, + GDestroyNotify notify + ) +{ + g_return_val_if_fail (async != NULL, -EINVAL); + g_return_val_if_fail (function != NULL, -EINVAL); + + GSource* source = pgm_async_create_watch (async); + + if (priority != G_PRIORITY_DEFAULT) + g_source_set_priority (source, priority); + + g_source_set_callback (source, (GSourceFunc)function, user_data, notify); + + guint id = g_source_attach (source, NULL); + g_source_unref (source); + + return id; +} + +int +pgm_async_add_watch ( + pgm_async_t* async, + pgm_eventfn_t function, + gpointer user_data + ) +{ + return pgm_async_add_watch_full (async, G_PRIORITY_HIGH, function, user_data, NULL); +} + +/* returns TRUE if source has data ready, i.e. async queue is not empty + * + * called before event loop poll() + */ + +static +gboolean +pgm_src_prepare ( + GSource* source, + gint* timeout + ) +{ + pgm_watch_t* watch = (pgm_watch_t*)source; + +/* infinite timeout */ + *timeout = -1; + + return ( g_async_queue_length(watch->async->commit_queue) > 0 ); +} + +/* called after event loop poll() + * + * return TRUE if ready to dispatch. + */ + +static +gboolean +pgm_src_check ( + GSource* source + ) +{ + pgm_watch_t* watch = (pgm_watch_t*)source; + + return ( g_async_queue_length(watch->async->commit_queue) > 0 ); +} + +/* called when TRUE returned from prepare or check + */ + +static gboolean +pgm_src_dispatch ( + GSource* source, + GSourceFunc callback, + gpointer user_data + ) +{ + g_trace ("pgm_src_dispatch (source:%p callback:() user-data:%p)", + (gpointer)source, user_data); + + const pgm_eventfn_t function = (pgm_eventfn_t)callback; + pgm_watch_t* watch = (pgm_watch_t*)source; + pgm_async_t* async = watch->async; + +/* empty pipe */ + pgm_notify_read (&async->commit_notify); + +/* purge only one message from the asynchronous queue */ + pgm_event_t* event = g_async_queue_try_pop (async->commit_queue); + if (event) + { +/* important that callback occurs out of lock to allow PGM layer to add more messages */ + (*function) (event->data, event->len, user_data); + +/* return memory to receive window */ + if (event->len) g_free (event->data); + pgm_event_unref (async, event); + } + + return TRUE; +} + +/* synchronous reading from the queue. + * + * returns GIOStatus with success, error, again, or eof. + */ + +GIOStatus +pgm_async_recv ( + pgm_async_t* const async, + gpointer data, + const gsize len, + gsize* const bytes_read, + const int flags, /* MSG_DONTWAIT for non-blocking */ + GError** error + ) +{ + g_return_val_if_fail (NULL != async, G_IO_STATUS_ERROR); + if (len) g_return_val_if_fail (NULL != data, G_IO_STATUS_ERROR); + + g_trace ("pgm_async_recv (async:%p data:%p len:%" G_GSIZE_FORMAT" bytes-read:%p flags:%d error:%p)", + (gpointer)async, data, len, (gpointer)bytes_read, flags, (gpointer)error); + + pgm_event_t* event = NULL; + g_async_queue_lock (async->commit_queue); + if (g_async_queue_length_unlocked (async->commit_queue) == 0) + { + g_async_queue_unlock (async->commit_queue); + if (flags & MSG_DONTWAIT || async->is_nonblocking) + return G_IO_STATUS_AGAIN; +#ifdef CONFIG_HAVE_POLL + struct pollfd fds[1]; + int ready; + do { + memset (fds, 0, sizeof(fds)); + fds[0].fd = pgm_notify_get_fd (&async->commit_notify); + fds[0].events = POLLIN; + ready = poll (fds, G_N_ELEMENTS(fds), -1); + if (-1 == ready || async->is_destroyed) /* errno = EINTR */ + return G_IO_STATUS_ERROR; + } while (ready <= 0); +#else + fd_set readfds; + int n_fds, ready, fd = pgm_notify_get_fd (&async->commit_notify); + do { + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + n_fds = fd + 1; + ready = select (n_fds, &readfds, NULL, NULL, NULL); + if (-1 == ready || async->is_destroyed) /* errno = EINTR */ + return G_IO_STATUS_ERROR; + } while (ready <= 0); +#endif + pgm_notify_read (&async->commit_notify); + g_async_queue_lock (async->commit_queue); + } + event = g_async_queue_pop_unlocked (async->commit_queue); + g_async_queue_unlock (async->commit_queue); + +/* pass data back to callee */ + if (event->len > len) { + *bytes_read = len; + memcpy (data, event->data, *bytes_read); + g_set_error (error, + PGM_ASYNC_ERROR, + PGM_ASYNC_ERROR_OVERFLOW, + _("Message too large to be stored in buffer.")); + pgm_event_unref (async, event); + return G_IO_STATUS_ERROR; + } + + if (bytes_read) + *bytes_read = event->len; + memcpy (data, event->data, event->len); + +/* cleanup */ + if (event->len) g_free (event->data); + pgm_event_unref (async, event); + return G_IO_STATUS_NORMAL; +} + +gboolean +pgm_async_set_nonblocking ( + pgm_async_t* const async, + const gboolean nonblocking + ) +{ + g_return_val_if_fail (NULL != async, FALSE); + async->is_nonblocking = nonblocking; + return TRUE; +} + +GQuark +pgm_async_error_quark (void) +{ + return g_quark_from_static_string ("pgm-async-error-quark"); +} + +static +PGMAsyncError +pgm_async_error_from_errno ( + const gint err_no + ) +{ + switch (err_no) { +#ifdef EFAULT + case EFAULT: + return PGM_ASYNC_ERROR_FAULT; + break; +#endif + +#ifdef EMFILE + case EMFILE: + return PGM_ASYNC_ERROR_MFILE; + break; +#endif + +#ifdef ENFILE + case ENFILE: + return PGM_ASYNC_ERROR_NFILE; + break; +#endif + + default : + return PGM_ASYNC_ERROR_FAILED; + break; + } +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/async.h b/3rdparty/openpgm-svn-r1085/pgm/test/async.h new file mode 100644 index 0000000..c2b91a7 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/async.h @@ -0,0 +1,76 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Asynchronous receive thread helper + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_ASYNC_H__ +#define __PGM_ASYNC_H__ + +#include +#include +#include + + +#define PGM_ASYNC_ERROR pgm_async_error_quark () + +typedef enum +{ + /* Derived from errno */ + PGM_ASYNC_ERROR_FAULT, + PGM_ASYNC_ERROR_MFILE, + PGM_ASYNC_ERROR_NFILE, + PGM_ASYNC_ERROR_OVERFLOW, + PGM_ASYNC_ERROR_FAILED +} PGMAsyncError; + +typedef struct pgm_async_t pgm_async_t; + +struct pgm_async_t { + pgm_transport_t* transport; + GThread* thread; + GAsyncQueue* commit_queue; + pgm_notify_t commit_notify; + pgm_notify_t destroy_notify; + gboolean is_destroyed; + gboolean is_nonblocking; +}; + +typedef int (*pgm_eventfn_t)(gpointer, guint, gpointer); + + +G_BEGIN_DECLS + +int pgm_async_create (pgm_async_t**, pgm_transport_t* const, GError**); +int pgm_async_destroy (pgm_async_t* const); +GIOStatus pgm_async_recv (pgm_async_t* const, gpointer, const gsize, gsize* const, const int, GError**); +gboolean pgm_async_set_nonblocking (pgm_async_t* const, const gboolean); +GSource* pgm_async_create_watch (pgm_async_t* const) G_GNUC_WARN_UNUSED_RESULT; +int pgm_async_add_watch_full (pgm_async_t*, gint, pgm_eventfn_t, gpointer, GDestroyNotify); +int pgm_async_add_watch (pgm_async_t*, pgm_eventfn_t, gpointer); +GQuark pgm_async_error_quark (void); + +static inline int pgm_async_get_fd (pgm_async_t* async) +{ + g_return_val_if_fail (async != NULL, -EINVAL); + return pgm_notify_get_fd (&async->commit_notify); +} + +G_END_DECLS + +#endif /* __PGM_ASYNC_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/dump-json.c b/3rdparty/openpgm-svn-r1085/pgm/test/dump-json.c new file mode 100644 index 0000000..70a7442 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/dump-json.c @@ -0,0 +1,1292 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * JSON packet dump. + * + * Copyright (c) 2006-2008 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "dump-json.h" + + +/* globals */ + +#define OPTIONS_TOTAL_LEN(x) *(guint16*)( ((char*)(x)) + sizeof(guint16) ) + + +int verify_ip_header (struct pgm_ip*, guint); +void print_ip_header (struct pgm_ip*); +int verify_pgm_header (struct pgm_header*, guint); +void print_pgm_header (struct pgm_header*); +int verify_spm (struct pgm_header*, char*, guint); +void print_spm (struct pgm_header*, char*); +int verify_poll (struct pgm_header*, char*, guint); +void print_poll (struct pgm_header*, char*); +int verify_polr (struct pgm_header*, char*, guint); +void print_polr (struct pgm_header*, char*); +int verify_odata (struct pgm_header*, char*, guint); +void print_odata (struct pgm_header*, char*); +int verify_rdata (struct pgm_header*, char*, guint); +void print_rdata (struct pgm_header*, char*); +static int generic_verify_nak (const char*, struct pgm_header*, char*, guint); +static void generic_print_nak (const char*, struct pgm_header*, char*); +int verify_nak (struct pgm_header*, char*, guint); +void print_nak (struct pgm_header*, char*); +int verify_nnak (struct pgm_header*, char*, guint); +void print_nnak (struct pgm_header*, char*); +int verify_ncf (struct pgm_header*, char*, guint); +void print_ncf (struct pgm_header*, char*); +int verify_spmr (struct pgm_header*, char*, guint); +void print_spmr (struct pgm_header*, char*); +int verify_options (char*, guint); +void print_options (char*); + + +int +monitor_packet ( + char* data, + guint len + ) +{ + static int count = 0; + + puts ("{"); + printf ("\t\"id\": %i,\n", ++count); + + int retval = 0; + + struct pgm_ip* ip = (struct pgm_ip*)data; + if (verify_ip_header (ip, len) < 0) { + puts ("\t\"valid\": false"); + retval = -1; + goto out; + } + + struct pgm_header* pgm = (struct pgm_header*)(data + (ip->ip_hl * 4)); + guint pgm_len = len - (ip->ip_hl * 4); + if (verify_pgm_header (pgm, pgm_len) < 0) { + puts ("\t\"valid\": false"); + retval = -1; + goto out; + } + + char* pgm_data = (char*)(pgm + 1); + guint pgm_data_len = pgm_len - sizeof(struct pgm_header); + switch (pgm->pgm_type) { + case PGM_SPM: retval = verify_spm (pgm, pgm_data, pgm_data_len); break; + case PGM_POLL: retval = verify_poll (pgm, pgm_data, pgm_data_len); break; + case PGM_POLR: retval = verify_polr (pgm, pgm_data, pgm_data_len); break; + case PGM_ODATA: retval = verify_odata (pgm, pgm_data, pgm_data_len); break; + case PGM_RDATA: retval = verify_rdata (pgm, pgm_data, pgm_data_len); break; + case PGM_NAK: retval = verify_nak (pgm, pgm_data, pgm_data_len); break; + case PGM_NNAK: retval = verify_nnak (pgm, pgm_data, pgm_data_len); break; + case PGM_NCF: retval = verify_ncf (pgm, pgm_data, pgm_data_len); break; + case PGM_SPMR: retval = verify_spmr (pgm, pgm_data, pgm_data_len); break; + } + + if (retval < 0) { + puts ("\t\"valid\": false"); + goto out; + } + +/* packet verified correct */ + puts ("\t\"valid\": true,"); + + print_ip_header (ip); + print_pgm_header (pgm); + + switch (pgm->pgm_type) { + case PGM_SPM: print_spm (pgm, pgm_data); break; + case PGM_POLL: print_poll (pgm, pgm_data); break; + case PGM_POLR: print_polr (pgm, pgm_data); break; + case PGM_ODATA: print_odata (pgm, pgm_data); break; + case PGM_RDATA: print_rdata (pgm, pgm_data); break; + case PGM_NAK: print_nak (pgm, pgm_data); break; + case PGM_NNAK: print_nnak (pgm, pgm_data); break; + case PGM_NCF: print_ncf (pgm, pgm_data); break; + case PGM_SPMR: print_spmr (pgm, pgm_data); break; + } + +out: + puts ("}"); + return retval; +} + + +int +verify_ip_header ( + struct pgm_ip* ip, + guint len + ) +{ +/* minimum size should be IP header plus PGM header */ + if (len < (sizeof(struct pgm_ip) + sizeof(struct pgm_header))) + { + printf ("\t\"message\": \"IP: packet size too small: %i bytes, expecting at least %" G_GSIZE_FORMAT " bytes.\",\n", len, sizeof(struct pgm_header)); + return -1; + } + +/* IP packet header: IPv4 + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |Version| HL | ToS | Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Fragment ID |R|D|M| Fragment Offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | Protocol | Checksum | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Source IP Address | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Destination IP Address | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | IP Options when present ... + * +-+-+-+-+-+-+-+-+-+-+-+-+ ... + * | Data ... + * +-+-+- ... + * + * IPv6: n/a + */ + +/* decode IP header */ + if (ip->ip_v != 4 && ip->ip_v != 6) { /* IP version, 4 or 6 */ + printf ("\t\"message\": \"IP: unknown IP version %i.\",\n", ip->ip_v); + return -1; + } + + guint ip_header_length = ip->ip_hl * 4; /* IP header length in 32bit octets */ + if (ip_header_length < sizeof(struct ip)) { + printf ("\t\"message\": \"IP: bad IP header length %i, should be at least %" G_GSIZE_FORMAT "lu bytes.\",\n", ip_header_length, sizeof(struct ip)); + return -1; + } + +/* ip_len can equal packet_length - ip_header_length in FreeBSD/NetBSD + * Stevens/Fenner/Rudolph, Unix Network Programming Vol.1, p.739 + * + * RFC3828 allows partial packets such that len < packet_length with UDP lite + */ + guint packet_length = g_ntohs(ip->ip_len); /* total packet length */ + if (len < packet_length) { /* redundant: often handled in kernel */ + printf ("\t\"message\": \"IP: truncated IP packet: header reports %i actual length %i bytes.\",\n", (int)len, (int)packet_length); + return -1; + } + +/* TCP Segmentation Offload (TSO) might have zero length here */ + if (packet_length < ip_header_length) { + printf ("\t\"message\": \"IP: header reports %i less than IP header length %i.\",\n", (int)packet_length, (int)ip_header_length); + return -1; + } + +/* packets that fail checksum will generally not be passed upstream except with rfc3828 + */ + int sum = pgm_inet_checksum((char*)ip, ip_header_length, 0); + if (sum != 0) { + int ip_sum = g_ntohs(ip->ip_sum); + printf ("\t\"message\": \"IP: IP header checksum incorrect: 0x%x.\",\n", ip_sum); + return -2; + } + + if (ip->ip_p != IPPROTO_PGM) { + printf ("\t\"message\": \"IP: packet IP protocol not PGM: %i.\",\n", ip->ip_p); + return -1; + } + +/* fragmentation offset, bit 0: 0, bit 1: do-not-fragment, bit 2: more-fragments */ + int offset = g_ntohs(ip->ip_off); + if ((offset & 0x1fff) != 0) { + printf ("\t\"message\": \"IP: fragmented IP packet, ignoring.\",\n"); + return -1; + } + + return 0; +} + +void +print_ip_header ( + struct pgm_ip* ip + ) +{ + puts ("\t\"IP\": {"); + printf ("\t\t\"version\": %i,\n", + ip->ip_v + ); + printf ("\t\t\"headerLength\": %i,\n", + ip->ip_hl + ); + printf ("\t\t\"ToS\": %i,\n", + ip->ip_tos & 0x3 + ); + printf ("\t\t\"length\": %i,\n", + g_ntohs(ip->ip_len) + ); + printf ("\t\t\"fragmentId\": %i,\n", + g_ntohs(ip->ip_id) + ); + printf ("\t\t\"DF\": %s,\n", + (g_ntohs(ip->ip_off) & 0x4000) ? "true" : "false" + ); + printf ("\t\t\"MF\": %s,\n", + (g_ntohs(ip->ip_off) & 0x2000) ? "true" : "false" + ); + printf ("\t\t\"fragmentOffset\": %i,\n", + g_ntohs(ip->ip_off) & 0x1fff + ); + printf ("\t\t\"TTL\": %i,\n", + ip->ip_ttl + ); + printf ("\t\t\"protocol\": %i,\n", + ip->ip_p + ); + printf ("\t\t\"sourceIp\": \"%s\",\n", + inet_ntoa(*(struct in_addr*)&ip->ip_src) + ); + printf ("\t\t\"destinationIp\": \"%s\",\n", + inet_ntoa(*(struct in_addr*)&ip->ip_dst) + ); + puts ("\t\t\"IpOptions\": {"); + puts ("\t\t}"); + puts ("\t},"); +} + +int +verify_pgm_header ( + struct pgm_header* pgm, + guint pgm_len + ) +{ + +/* PGM payload, header looks as follows: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Source Port | Destination Port | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Options | Checksum | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Global Source ID ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ... Global Source ID | TSDU Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type specific data ... + * +-+-+-+-+-+-+-+-+-+- ... + */ + if (pgm_len < sizeof(pgm)) { + printf ("\t\"message\": \"PGM: packet size less than PGM header: %i bytes.\",\n", pgm_len); + return -1; + } + + if (pgm->pgm_checksum) + { + int sum = pgm->pgm_checksum; + pgm->pgm_checksum = 0; + int pgm_sum = pgm_csum_fold (pgm_csum_partial ((const char*)pgm, pgm_len, 0)); + pgm->pgm_checksum = sum; + if (pgm_sum != sum) { + printf ("\t\"message\": \"PGM: PGM packet checksum incorrect, packet 0x%x calculated 0x%x.\",\n", sum, pgm_sum); + return -2; + } + } else { + if (pgm->pgm_type != PGM_ODATA && pgm->pgm_type != PGM_RDATA) { + printf ("\t\"message\": \"PGM: No PGM checksum value, mandatory for ODATA/RDATA.\",\n"); + return -1; + } + } + + if ( pgm->pgm_type != PGM_SPM && + pgm->pgm_type != PGM_POLL && + pgm->pgm_type != PGM_POLR && + pgm->pgm_type != PGM_ODATA && + pgm->pgm_type != PGM_RDATA && + pgm->pgm_type != PGM_NAK && + pgm->pgm_type != PGM_NNAK && + pgm->pgm_type != PGM_NCF && + pgm->pgm_type != PGM_SPMR ) + { + printf ("\t\"message\": \"PGM: Not a valid PGM packet type: %i.\",\n", pgm->pgm_type); + return -1; + } + + return 0; +} + +/* note: output trails tsdu length line to allow for comma + */ + +void +print_pgm_header ( + struct pgm_header* pgm + ) +{ + puts ("\t\"PGM\": {"); + printf ("\t\t\"sourcePort\": %i,\n", g_ntohs(pgm->pgm_sport)); + printf ("\t\t\"destinationPort\": %i,\n", g_ntohs(pgm->pgm_dport)); + printf ("\t\t\"type\": \"%s\",\n", pgm_type_string(pgm->pgm_type & 0xf)); + printf ("\t\t\"version\": %i,\n", (pgm->pgm_type & 0xc0) >> 6); + puts ("\t\t\"options\": {"); + printf ("\t\t\t\"networkSignificant\": %s,\n", (pgm->pgm_options & PGM_OPT_NETWORK) ? "true" : "false"); + printf ("\t\t\t\"parityPacket\": %s,\n", (pgm->pgm_options & PGM_OPT_PARITY) ? "true" : "false"); + printf ("\t\t\t\"variableLength\": %s\n", (pgm->pgm_options & PGM_OPT_VAR_PKTLEN) ? "true" : "false"); + puts ("\t\t},"); + printf ("\t\t\"checksum\": %i,\n", pgm->pgm_checksum); + printf ("\t\t\"gsi\": \"%i.%i.%i.%i.%i.%i\",\n", + pgm->pgm_gsi[0], + pgm->pgm_gsi[1], + pgm->pgm_gsi[2], + pgm->pgm_gsi[3], + pgm->pgm_gsi[4], + pgm->pgm_gsi[5]); + printf ("\t\t\"tsduLength\": %i", g_ntohs(pgm->pgm_tsdu_length)); +} + +/* 8.1. Source Path Messages (SPM) + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SPM's Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Trailing Edge Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Leading Edge Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Path NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * NLA = Network Layer Address + * NLA AFI = NLA Address Family Indicator: rfc 1700 (ADDRESS FAMILY NUMBERS) + * => Path NLA = IP address of last network element + */ + +int +verify_spm ( + struct pgm_header* header, + char* data, + guint len + ) +{ + int retval = 0; + +/* truncated packet */ + if (len < sizeof(struct pgm_spm)) { + printf ("\t\"message\": \"SPM: packet length: %i less than minimum SPM length: %" G_GSIZE_FORMAT "lu bytes.\",\n", len, sizeof(struct pgm_spm)); + retval = -1; + goto out; + } + + struct pgm_spm* spm = (struct pgm_spm*)data; + char* opt_offset = (char*)(spm + 1); + guint opt_len = len - sizeof(spm); + + switch (g_ntohs(spm->spm_nla_afi)) { + case AFI_IP6: + if (len < sizeof(struct pgm_spm6)) { + printf ("\t\"message\": \"SPM: packet length: %i less than minimum IPv6 SPM length: %" G_GSIZE_FORMAT "lu bytes.\",\n", len, sizeof(struct pgm_spm6)); + retval = -1; + goto out; + } + opt_offset += sizeof(struct pgm_spm6) - sizeof(struct pgm_spm); + opt_len -= sizeof(struct pgm_spm6) - sizeof(struct pgm_spm); + + case AFI_IP: + break; + + default: + printf ("\t\"message\": \"SPM: invalid AFI of source NLA: %i.\",\n", g_ntohs(spm->spm_nla_afi)); + retval = -1; + goto out; + } + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT) + { + retval = verify_options (opt_offset, opt_len); + } + +out: + return retval; +} + +void +print_spm ( + struct pgm_header* header, + char* data + ) +{ + struct pgm_spm* spm = (struct pgm_spm*)data; + struct pgm_spm6* spm6 = (struct pgm_spm6*)data; + char* opt_offset = (char*)(spm + 1); + + puts (","); + printf ("\t\t\"spmSqn\": %i,\n", g_ntohl(spm->spm_sqn)); + printf ("\t\t\"spmTrail\": %i,\n", g_ntohl(spm->spm_trail)); + printf ("\t\t\"spmLead\": %i,\n", g_ntohl(spm->spm_lead)); + printf ("\t\t\"spmNlaAfi\": %i,\n", g_ntohs (spm->spm_nla_afi)); + + char s[INET6_ADDRSTRLEN]; + switch (g_ntohs(spm->spm_nla_afi)) { + case AFI_IP: + inet_ntop ( AF_INET, &spm->spm_nla, s, sizeof (s) ); + break; + + case AFI_IP6: + inet_ntop ( AF_INET6, &spm6->spm6_nla, s, sizeof (s) ); + opt_offset += sizeof(struct pgm_spm6) - sizeof(struct pgm_spm); + break; + } + + printf ("\t\t\"spmNla\": \"%s\"", s); + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT) + { + puts (","); + print_options (opt_offset); + } + + puts ("\n\t}"); +} + +/* 14.7.1. Poll Request + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLL's Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLL's Round | POLL's Sub-type | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Path NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | POLL's Back-off Interval | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Random String | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Matching Bit-Mask | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Sent to ODATA multicast group with IP Router Alert option. + */ + +#define PGM_MIN_POLL_SIZE ( sizeof(struct pgm_poll) ) + +int +verify_poll ( + G_GNUC_UNUSED struct pgm_header* header, + G_GNUC_UNUSED char* data, + G_GNUC_UNUSED guint len + ) +{ + return -1; +} + +void +print_poll ( + G_GNUC_UNUSED struct pgm_header* header, + G_GNUC_UNUSED char* data + ) +{ +} + +/* 14.7.2. Poll Response + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLR's Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLR's Round | reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +int +verify_polr ( + G_GNUC_UNUSED struct pgm_header* header, + G_GNUC_UNUSED char* data, + G_GNUC_UNUSED guint len + ) +{ + return -1; +} + +void +print_polr ( + G_GNUC_UNUSED struct pgm_header* header, + G_GNUC_UNUSED char* data + ) +{ +} + +/* 8.2. Data Packet + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Data Packet Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Trailing Edge Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Data ... + * +-+-+- ... + */ + +int +verify_odata ( + struct pgm_header* header, + char* data, + guint len + ) +{ + int retval = 0; + + if (len < sizeof(struct pgm_data)) { + printf ("\t\"message\": \"ODATA: packet length: %i less than minimum ODATA length: %" G_GSIZE_FORMAT " bytes.\",\n", len, sizeof(struct pgm_data)); + retval = -1; + goto out; + } + + char* tsdu = data + sizeof(struct pgm_data); + guint tsdu_len = len - sizeof(struct pgm_data); + if (header->pgm_options & PGM_OPT_PRESENT) + { + retval = verify_options (tsdu, tsdu_len); + + guint opt_total_len = g_ntohs( OPTIONS_TOTAL_LEN(tsdu) ); + tsdu += opt_total_len; + tsdu_len -= opt_total_len; + } + + if (!retval && g_ntohs(header->pgm_tsdu_length) != tsdu_len) { + printf ("\t\"message\": \"ODATA: TSDU truncated expected %i, found %i bytes.\",\n", g_ntohs(header->pgm_tsdu_length), tsdu_len); + retval = -1; + } +out: + return retval; +} + +void +print_odata ( + struct pgm_header* header, + char* data + ) +{ + struct pgm_data* odata = (struct pgm_data*)data; + char* tsdu = data + sizeof(struct pgm_data); + + puts (","); + printf ("\t\t\"odSqn\": %lu,\n", (gulong)g_ntohl(odata->data_sqn)); + printf ("\t\t\"odTrail\": %lu,\n", (gulong)g_ntohl(odata->data_trail)); + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT) + { + print_options (tsdu); + tsdu += g_ntohs( OPTIONS_TOTAL_LEN(tsdu) ); + puts (","); + } + +/* data */ + printf ("\t\t\"data\": \""); + char* end = tsdu + g_ntohs (header->pgm_tsdu_length); + while (tsdu < end) { + if (isprint(*tsdu)) + putchar(*tsdu); + else + putchar('.'); + tsdu++; + } + + puts ("\""); + puts ("\t}"); +} + +/* 8.2. Repair Data + */ + +int +verify_rdata ( + struct pgm_header* header, + char* data, + guint len + ) +{ + int retval = 0; + + if (len < sizeof(struct pgm_data)) { + printf ("\t\"message\": \"RDATA: packet length: %i less than minimum RDATA length: %" G_GSIZE_FORMAT " bytes.\",\n", len, sizeof(struct pgm_data)); + retval = -1; + goto out; + } + + char* tsdu = data + sizeof(struct pgm_data); + guint tsdu_len = len - sizeof(struct pgm_data); + if (header->pgm_options & PGM_OPT_PRESENT) + { + retval = verify_options (tsdu, tsdu_len); + + guint opt_total_len = g_ntohs( OPTIONS_TOTAL_LEN(tsdu) ); + tsdu += opt_total_len; + tsdu_len -= opt_total_len; + } + + if (!retval && g_ntohs(header->pgm_tsdu_length) != tsdu_len) { + printf ("\t\"message\": \"RDATA: tsdu truncated expected %i, found %i bytes.\",\n", g_ntohs(header->pgm_tsdu_length), tsdu_len); + retval = -1; + } +out: + return retval; +} + +void +print_rdata ( + struct pgm_header* header, + char* data + ) +{ + struct pgm_data* rdata = (struct pgm_data*)data; + char* tsdu = data + sizeof(struct pgm_data); + + puts (","); + printf ("\t\t\"rdSqn\": %lu,\n", (gulong)g_ntohl(rdata->data_sqn)); + printf ("\t\t\"rdTrail\": %lu,\n", (gulong)g_ntohl(rdata->data_trail)); + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT) + { + print_options (tsdu); + tsdu += g_ntohs( OPTIONS_TOTAL_LEN(tsdu) ); + puts (","); + } + +/* data */ + printf ("\t\t\"data\": \""); + char* end = tsdu + g_ntohs (header->pgm_tsdu_length); + while (tsdu < end) { + if (isprint(*tsdu)) + putchar(*tsdu); + else + putchar('.'); + tsdu++; + } + + puts ("\""); + puts ("\t}"); +} + +/* 8.3. NAK + * + * Technically the AFI of the source and multicast group can be different + * but that would be very wibbly wobbly. One example is using a local DLR + * with a IPv4 address to reduce NAK cost for recovery on wide IPv6 + * distribution. + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Requested Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Source NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Multicast Group NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | Option Extensions when present ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... + */ + +int +verify_nak ( + struct pgm_header* header, + char* data, + guint len + ) +{ + return generic_verify_nak ("NAK", header, data, len); +} + +int +verify_ncf ( + struct pgm_header* header, + char* data, + guint len + ) +{ + return generic_verify_nak ("NCF", header, data, len); +} + +int +verify_nnak ( + struct pgm_header* header, + char* data, + guint len + ) +{ + return generic_verify_nak ("NNAK", header, data, len); +} + +static int +generic_verify_nak ( + const char* name, /* upper case */ + G_GNUC_UNUSED struct pgm_header* header, + char* data, + guint len + ) +{ + int retval = 0; + +/* truncated packet */ + if (len < sizeof(struct pgm_nak)) { + printf ("\t\"message\": \"%s: packet length: %i less than minimum %s length: %" G_GSIZE_FORMAT " bytes.\",\n", + name, len, name, sizeof(struct pgm_nak)); + retval = -1; + goto out; + } + + struct pgm_nak* nak = (struct pgm_nak*)data; + int nak_src_nla_afi = g_ntohs (nak->nak_src_nla_afi); + int nak_grp_nla_afi = -1; + +/* check source NLA: unicast address of the ODATA sender */ + switch (nak_src_nla_afi) { + case AFI_IP: + nak_grp_nla_afi = g_ntohs (nak->nak_grp_nla_afi); + break; + + case AFI_IP6: + nak_grp_nla_afi = g_ntohs (((struct pgm_nak6*)nak)->nak6_grp_nla_afi); + break; + + default: + printf ("\t\"message\": \"%s: invalid AFI of source NLA: %i.\",\n", + name, nak_src_nla_afi); + retval = -1; + goto out; + } + +/* check multicast group NLA */ + switch (nak_grp_nla_afi) { + case AFI_IP6: + switch (nak_src_nla_afi) { +/* IPv4 + IPv6 NLA */ + case AFI_IP: + if (len < ( sizeof(struct pgm_nak) + sizeof(struct in6_addr) - sizeof(struct in_addr) )) { + printf ("\t\"message\": \"%s: packet length: %i less than joint IPv4/6 %s length: %" G_GSIZE_FORMAT " bytes.\",\n", + name, len, name, ( sizeof(struct pgm_nak) + sizeof(struct in6_addr) - sizeof(struct in_addr) )); + retval = -1; + } + break; + +/* IPv6 + IPv6 NLA */ + case AFI_IP6: + if (len < sizeof(struct pgm_nak6)) { + printf ("\t\"message\": \"%s: packet length: %i less than IPv6 %s length: %" G_GSIZE_FORMAT " bytes.\",\n", + name, len, name, sizeof(struct pgm_nak6)); + retval = -1; + } + break; + } + break; + + case AFI_IP: + if (nak_src_nla_afi == AFI_IP6) { + if (len < ( sizeof(struct pgm_nak) + sizeof(struct in6_addr) - sizeof(struct in_addr) )) { + printf ("\t\"message\": \"%s: packet length: %i less than joint IPv6/4 %s length: %" G_GSIZE_FORMAT " bytes.\",\n", + name, len, name, ( sizeof(struct pgm_nak) + sizeof(struct in6_addr) - sizeof(struct in_addr) )); + retval = -1; + } + } + break; + + default: + printf ("\t\"message\": \"%s: invalid AFI of group NLA: %i.\",\n", + name, nak_grp_nla_afi); + retval = -1; + break; + } + +out: + return retval; +} + +void +print_nak ( + struct pgm_header* header, + char* data + ) +{ + generic_print_nak ("nak", header, data); +} + +void +print_ncf ( + struct pgm_header* header, + char* data + ) +{ + generic_print_nak ("ncf", header, data); +} + +void +print_nnak ( + struct pgm_header* header, + char* data + ) +{ + generic_print_nak ("nnak", header, data); +} + +static void +generic_print_nak ( + const char* name, /* lower case */ + struct pgm_header* header, + char* data + ) +{ + struct pgm_nak* nak = (struct pgm_nak*)data; + struct pgm_nak6* nak6 = (struct pgm_nak6*)data; + char* opt_offset = (char*)(nak + 1); + + puts (","); + printf ("\t\t\"%sSqn\": %lu,\n", name, (gulong)g_ntohl(nak->nak_sqn)); + + guint16 nak_src_nla_afi = g_ntohs (nak->nak_src_nla_afi); + guint16 nak_grp_nla_afi = 0; + char s[INET6_ADDRSTRLEN]; + + printf ("\t\t\"%sSourceNlaAfi\": %i,\n", name, nak_src_nla_afi); + +/* source nla */ + switch (nak_src_nla_afi) { + case AFI_IP: + inet_ntop ( AF_INET, &nak->nak_src_nla, s, sizeof(s) ); + nak_grp_nla_afi = g_ntohs (nak->nak_grp_nla_afi); + break; + + case AFI_IP6: + inet_ntop ( AF_INET6, &nak6->nak6_src_nla, s, sizeof(s) ); + nak_grp_nla_afi = g_ntohs (nak6->nak6_grp_nla_afi); + opt_offset += sizeof(struct in6_addr) - sizeof(struct in_addr); + break; + } + + printf ("\t\t\"%sSourceNla\": \"%s\",\n", name, s); + printf ("\t\t\"%sGroupNlaAfi\": %i,\n", name, nak_grp_nla_afi); + + switch (nak_grp_nla_afi) { + case AFI_IP6: + switch (nak_src_nla_afi) { +/* IPv4 + IPv6 NLA */ + case AFI_IP: + inet_ntop ( AF_INET6, &nak->nak_grp_nla, s, sizeof(s) ); + break; + +/* IPv6 + IPv6 NLA */ + case AFI_IP6: + inet_ntop ( AF_INET6, &nak6->nak6_grp_nla, s, sizeof(s) ); + break; + } + opt_offset += sizeof(struct in6_addr) - sizeof(struct in_addr); + break; + + case AFI_IP: + switch (nak_src_nla_afi) { +/* IPv4 + IPv4 NLA */ + case AFI_IP: + inet_ntop ( AF_INET, &nak->nak_grp_nla, s, sizeof(s) ); + break; + +/* IPv6 + IPv4 NLA */ + case AFI_IP6: + inet_ntop ( AF_INET, &nak6->nak6_grp_nla, s, sizeof(s) ); + break; + } + break; + } + + printf ("\t\t\"%sGroupNla\": \"%s\"", name, s); + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT) + { + puts (","); + print_options (opt_offset); + } + + puts ("\n\t}"); +} + + +/* 13.6. SPM Request + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... + */ + +int +verify_spmr ( + struct pgm_header* header, + char* data, + guint len + ) +{ + int retval = 0; + + char* opt_offset = data; + guint opt_len = len; + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT) + { + retval = verify_options (opt_offset, opt_len); + } + + return retval; +} + +void +print_spmr ( + struct pgm_header* header, + char* data + ) +{ + char* opt_offset = data; + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT) + { + print_options (opt_offset); + puts (""); + } + + puts ("\t}"); +} + +/* Parse PGM options fields: + * + * assume at least two options, one the mandatory OPT_LENGTH + */ + +#define PGM_MIN_OPT_SIZE ( sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_length) ) + +int +verify_options ( + char* data, + guint len + ) +{ + int retval = 0; + + if (len < PGM_MIN_OPT_SIZE) { + printf ("\t\"message\": \"PGM options: packet size too small for options.\",\n"); + retval = -1; + goto out; + } + +/* OPT_LENGTH first */ + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)data; + if ((opt_len->opt_type & PGM_OPT_MASK) != PGM_OPT_LENGTH) { + printf ("\t\"message\": \"PGM options: first option not OPT_LENGTH.\",\n"); + retval = -1; + goto out; + } + + if (opt_len->opt_length != sizeof(struct pgm_opt_length)) { + printf ("\t\"message\": \"PGM options: OPT_LENGTH incorrect option length: %i, expecting %" G_GSIZE_FORMAT " bytes.\",\n", opt_len->opt_length, sizeof(struct pgm_opt_length)); + retval = -1; + goto out; + } + + if (g_ntohs(opt_len->opt_total_length) < PGM_MIN_OPT_SIZE) { + printf ("\t\"message\": \"PGM options: OPT_LENGTH total length too short: %i bytes.\",\n", g_ntohs(opt_len->opt_total_length)); + retval = -1; + goto out; + } + + if (g_ntohs(opt_len->opt_total_length) > len) { + printf ("\t\"message\": \"PGM options: OPT_LENGTH total length longer than packet allows: %i bytes.\",\n", g_ntohs(opt_len->opt_total_length)); + retval = -1; + goto out; + } + +/* iterate through options (max 16) */ + guint count = 0; + guint total_length = g_ntohs(opt_len->opt_total_length); + + guint opt_counters[256]; + memset (&opt_counters, 0, sizeof(opt_counters)); + + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)data; + for (;;) { + total_length -= opt_header->opt_length; + if (total_length < sizeof(struct pgm_opt_header)) { + printf ("\t\"message\": \"PGM options: option #%i shorter than minimum option size.\",\n", count + 1); + retval = -1; + goto out; + } + opt_header = (struct pgm_opt_header*)( ((char*)opt_header) + opt_header->opt_length ); + if (((int)total_length - (int)opt_header->opt_length) < 0) { + printf ("\t\"message\": \"PGM options: option #%i shorter than embedded size.\",\n", count + 1); + retval = -1; + goto out; + } + + if (opt_counters[opt_header->opt_type]++) { + printf ("\t\"message\": \"PGM options: duplicate option %i.\",\n", opt_header->opt_type); + retval = -1; + goto out; + } + +/* check option types */ + switch (opt_header->opt_type & PGM_OPT_MASK) { + case PGM_OPT_FRAGMENT: + { + if (opt_header->opt_length != sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_fragment)) { + printf ("\t\"message\": \"PGM options: OPT_FRAGMENT incorrect size: %i bytes.\",\n", opt_header->opt_length); + retval = -1; + goto out; + } + + struct pgm_opt_fragment* opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); + if (g_ntohl(opt_fragment->opt_frag_off) > g_ntohl(opt_fragment->opt_frag_len)) { + printf ("\t\"message\": \"PGM options: fragment offset longer than original packet.\",\n"); + retval = -1; + goto out; + } + break; + } + + case PGM_OPT_NAK_LIST: + { + guint list_len = opt_header->opt_length - sizeof(struct pgm_opt_header) - sizeof(guint8); + if (list_len & 1) { + printf ("\t\"message\": \"PGM options: OPT_NAK_LIST invalid odd length: %i bytes.\",\n", opt_header->opt_length); + retval = -1; + goto out; + } + + list_len /= 2; + if (list_len == 0) { + printf ("\t\"message\": \"PGM options: OPT_NAK_LIST empty.\",\n"); + retval = -1; + goto out; + } + + if (list_len > 62) { + printf ("\t\"message\": \"PGM options: OPT_NAK_LIST too long: %i sqns.\",\n", list_len); + retval = -1; + goto out; + } + break; + } + + case PGM_OPT_PARITY_PRM: + { + struct pgm_opt_parity_prm* opt_parity_prm = (struct pgm_opt_parity_prm*)(opt_header + 1); + if ((opt_parity_prm->opt_reserved & PGM_PARITY_PRM_MASK) == 0) { + printf ("\t\"message\": \"PGM options: neither pro-active or on-demand parity set in OPT_PARITY_PRM.\",\n"); + retval = -1; + goto out; + } + guint32 parity_prm_tgs = g_ntohl (opt_parity_prm->parity_prm_tgs); + if (parity_prm_tgs < 2 || parity_prm_tgs > 128) { + printf ("\t\"message\": \"PGM options: transmission group size out of bounds: %i.\",\n", parity_prm_tgs); + retval = -1; + goto out; + } + break; + } + + default: +/* unknown option, skip */ + break; + } +/* end option types */ + + if (opt_header->opt_type & PGM_OPT_END) { + break; + } + + if (count++ == 16) { + printf ("\t\"message\": \"PGM options: more than 16 options found.\",\n"); + retval = -1; + goto out; + } + } + +out: + return retval; +} + +static const char *opx_text[4] = { + "OPX_IGNORE", + "OPX_INVALIDATE", + "OPX_DISCARD", + "OPX_UNKNOWN" +}; + +void +print_options ( + char* data + ) +{ + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)data; + + puts ("\t\t\"pgmOptions\": ["); + puts ("\t\t\t{"); + printf ("\t\t\t\t\"length\": 0x%x,\n", opt_len->opt_length); + puts ("\t\t\t\t\"type\": \"OPT_LENGTH\","); + printf ("\t\t\t\t\"totalLength\": %i\n", g_ntohs (opt_len->opt_total_length)); + printf ("\t\t\t}"); + +/* iterate through options */ + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)data; + do { + opt_header = (struct pgm_opt_header*)( ((char*)opt_header) + opt_header->opt_length ); + + puts (","); + puts ("\t\t\t{"); + printf ("\t\t\t\t\"length\": 0x%x,\n", opt_header->opt_length); + + switch (opt_header->opt_type & PGM_OPT_MASK) { + case PGM_OPT_FRAGMENT: + { + struct pgm_opt_fragment* opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); + printf ("\t\t\t\t\"type\": \"OPT_FRAGMENT%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : ""); + printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false"); + printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]); + printf ("\t\t\t\t\"U-bit\": %s,\n", (opt_fragment->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false"); + printf ("\t\t\t\t\"firstSqn\": %i,\n", g_ntohl(opt_fragment->opt_sqn)); + printf ("\t\t\t\t\"fragmentOffset\": %i,\n", g_ntohl(opt_fragment->opt_frag_off)); + printf ("\t\t\t\t\"originalLength\": %i\n", g_ntohl(opt_fragment->opt_frag_len)); + break; + } + + case PGM_OPT_NAK_LIST: + { + struct pgm_opt_nak_list* opt_nak_list = (struct pgm_opt_nak_list*)(opt_header + 1); + char* end = (char*)opt_header + opt_header->opt_length; + printf ("\t\t\t\t\"type\": \"OPT_NAK_LIST%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : ""); + printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false"); + printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]); + printf ("\t\t\t\t\"U-bit\": %s,\n", (opt_nak_list->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false"); + char sqns[1024] = ""; + guint i = 0; + do { + char sqn[1024]; + sprintf (sqn, "%s%i", (i>0)?", ":"", g_ntohl(opt_nak_list->opt_sqn[i])); + strcat (sqns, sqn); + i++; + } while ((char*)&opt_nak_list->opt_sqn[i] < end); + printf ("\t\t\t\t\"sqn\": [%s]\n", sqns); + break; + } + + case PGM_OPT_PARITY_PRM: + { + struct pgm_opt_parity_prm* opt_parity_prm = (struct pgm_opt_parity_prm*)(opt_header + 1); + printf ("\t\t\t\t\"type\": \"OPT_PARITY_PRM%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : ""); + printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false"); + printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]); + printf ("\t\t\t\t\"U-bit\": %s,\n", (opt_parity_prm->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false"); + printf ("\t\t\t\t\"P-bit\": %s,\n", (opt_parity_prm->opt_reserved & PGM_PARITY_PRM_PRO) ? "true" : "false"); + printf ("\t\t\t\t\"O-bit\": %s,\n", (opt_parity_prm->opt_reserved & PGM_PARITY_PRM_OND) ? "true" : "false"); + printf ("\t\t\t\t\"transmissionGroupSize\": %i\n", g_ntohl(opt_parity_prm->parity_prm_tgs)); + break; + } + + case PGM_OPT_CURR_TGSIZE: + { + struct pgm_opt_curr_tgsize* opt_curr_tgsize = (struct pgm_opt_curr_tgsize*)(opt_header + 1); + printf ("\t\t\t\t\"type\": \"OPT_CURR_TGSIZE%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : ""); + printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false"); + printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]); + printf ("\t\t\t\t\"U-bit\": %s,\n", (opt_curr_tgsize->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false"); + printf ("\t\t\t\t\"actualTransmissionGroupSize\": %i\n", g_ntohl(opt_curr_tgsize->prm_atgsize)); + break; + } + + case PGM_OPT_SYN: + { + struct pgm_opt_syn* opt_syn = (struct pgm_opt_syn*)(opt_header + 1); + printf ("\t\t\t\t\"type\": \"OPT_SYN%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : ""); + printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false"); + printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]); + printf ("\t\t\t\t\"U-bit\": %s\n", (opt_syn->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false"); + break; + } + + case PGM_OPT_FIN: + { + struct pgm_opt_fin* opt_fin = (struct pgm_opt_fin*)(opt_header + 1); + printf ("\t\t\t\t\"type\": \"OPT_FIN%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : ""); + printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false"); + printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]); + printf ("\t\t\t\t\"U-bit\": %s\n", (opt_fin->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false"); + break; + } + + default: + { + guint8 opt_reserved = *(guint8*)(opt_header + 1); + printf ("\t\t\t\t\"type\": \"0x%x%s\",\n", opt_header->opt_type & PGM_OPT_MASK, (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : ""); + printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false"); + printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]); + printf ("\t\t\t\t\"U-bit\": %s\n", (opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false"); + break; + } + } + printf ("\t\t\t}"); + + } while (!(opt_header->opt_type & PGM_OPT_END)); + + printf ("\n\t\t]"); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/dump-json.h b/3rdparty/openpgm-svn-r1085/pgm/test/dump-json.h new file mode 100644 index 0000000..6fa0f9d --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/dump-json.h @@ -0,0 +1,33 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * JSON packet dump. + * + * Copyright (c) 2006-2007 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_DUMP_JSON_H__ +#define __PGM_DUMP_JSON_H__ + + +G_BEGIN_DECLS + +int monitor_packet (char*, guint); + + +G_END_DECLS + +#endif /* __PGM_DUMP_JSON_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/heartbeat_spm.pl b/3rdparty/openpgm-svn-r1085/pgm/test/heartbeat_spm.pl new file mode 100755 index 0000000..60f6266 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/heartbeat_spm.pl @@ -0,0 +1,57 @@ +#!/usr/bin/perl +# heartbeat_spm.pl +# 5.1.5. Heartbeat SPMs + +use strict; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$app->connect; + +sub close_ssh { + $mon = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +print "app: publish test data.\n"; +$app->say ("send ao ringo"); + +print "mon: wait for odata ...\n"; +$mon->wait_for_odata; +my $t0 = [gettimeofday]; + +for (1..4) # look for four consecutive heartbeat SPMs less than 5000ms apart +{ + print "mon: wait for spm ...\n"; + $mon->wait_for_spm ({ 'timeout' => 5 }); + my $tn = [gettimeofday]; + my $elapsed = tv_interval ( $t0, $tn ); + $t0 = $tn; + + print "mon: spm received after $elapsed seconds.\n"; +} + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/monitor.c b/3rdparty/openpgm-svn-r1085/pgm/test/monitor.c new file mode 100644 index 0000000..6569a55 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/monitor.c @@ -0,0 +1,349 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM link monitor. + * + * Copyright (c) 2006-2007 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "dump-json.h" + + +/* globals */ + +static const char* g_network = "239.192.0.1"; +static struct in_addr g_filter /* = { 0 } */; + +static GIOChannel* g_io_channel = NULL; +static GIOChannel* g_stdin_channel = NULL; +static GMainLoop* g_loop = NULL; + + +static void on_signal (int, gpointer); +static gboolean on_startup (gpointer); +static gboolean on_mark (gpointer); + +static gboolean on_io_data (GIOChannel*, GIOCondition, gpointer); +static gboolean on_io_error (GIOChannel*, GIOCondition, gpointer); + +static gboolean on_stdin_data (GIOChannel*, GIOCondition, gpointer); + +int +main ( + G_GNUC_UNUSED int argc, + G_GNUC_UNUSED char *argv[] + ) +{ +/* pre-initialise PGM messages module to add hook for GLib logging */ + pgm_messages_init(); + log_init (); + puts ("monitor"); + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); + signal (SIGHUP, SIG_IGN); + pgm_signal_install (SIGINT, on_signal, g_loop); + pgm_signal_install (SIGTERM, on_signal, g_loop); + + g_filter.s_addr = 0; + +/* delayed startup */ + puts ("scheduling startup."); + g_timeout_add(0, (GSourceFunc)on_startup, NULL); + +/* dispatch loop */ + g_loop = g_main_loop_new(NULL, FALSE); + + puts ("entering main event loop ... "); + g_main_loop_run(g_loop); + + puts ("event loop terminated, cleaning up."); + +/* cleanup */ + g_main_loop_unref(g_loop); + g_loop = NULL; + + if (g_io_channel) { + puts ("closing socket."); + + GError *err = NULL; + g_io_channel_shutdown (g_io_channel, FALSE, &err); + g_io_channel = NULL; + } + + if (g_stdin_channel) { + puts ("unbinding stdin."); + g_io_channel_unref (g_stdin_channel); + g_stdin_channel = NULL; + } + + puts ("finished."); + pgm_messages_shutdown(); + return 0; +} + +static void +on_signal ( + int signum, + gpointer user_data + ) +{ + GMainLoop* loop = (GMainLoop*)user_data; + g_message ("on_signal (signum:%d user-data:%p)", signum, user_data); + g_main_loop_quit (loop); +} + +static gboolean +on_startup ( + G_GNUC_UNUSED gpointer data + ) +{ + int e; + + puts ("startup."); + +/* find PGM protocol id */ +// TODO: fix valgrind errors + int ipproto_pgm = IPPROTO_PGM; +#if HAVE_GETPROTOBYNAME_R + char b[1024]; + struct protoent protobuf, *proto; + e = getprotobyname_r("pgm", &protobuf, b, sizeof(b), &proto); + if (e != -1 && proto != NULL) { + if (proto->p_proto != ipproto_pgm) { + printf("Setting PGM protocol number to %i from /etc/protocols.\n"); + ipproto_pgm = proto->p_proto; + } + } +#else + struct protoent *proto = getprotobyname("pgm"); + if (proto != NULL) { + if (proto->p_proto != ipproto_pgm) { + printf("Setting PGM protocol number to %i from /etc/protocols.\n", proto +->p_proto); + ipproto_pgm = proto->p_proto; + } + } +#endif + +/* open socket for snooping */ + puts ("opening raw socket."); + int sock = socket(PF_INET, SOCK_RAW, ipproto_pgm); + if (sock < 0) { + int _e = errno; + perror("on_startup() failed"); + + if (_e == EPERM && 0 != getuid()) { + puts ("PGM protocol requires this program to run as superuser."); + } + g_main_loop_quit(g_loop); + return FALSE; + } + +/* drop out of setuid 0 */ + if (0 == getuid()) { + puts ("dropping superuser privileges."); + setuid((gid_t)65534); + setgid((uid_t)65534); + } + + char _t = 1; + e = setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &_t, sizeof(_t)); + if (e < 0) { + perror("on_startup() failed"); + close(sock); + g_main_loop_quit(g_loop); + return FALSE; + } + +/* buffers */ + int buffer_size = 0; + socklen_t len = 0; + e = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &buffer_size, &len); + if (e == 0) { + printf ("receive buffer set at %i bytes.\n", buffer_size); + } + e = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &buffer_size, &len); + if (e == 0) { + printf ("send buffer set at %i bytes.\n", buffer_size); + } + +/* bind */ + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + e = bind(sock, (struct sockaddr*)&addr, sizeof(addr)); + if (e < 0) { + perror("on_startup() failed"); + close(sock); + g_main_loop_quit(g_loop); + return FALSE; + } + +/* multicast */ + struct ip_mreq mreq; + memset(&mreq, 0, sizeof(mreq)); + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + printf ("listening on interface %s.\n", inet_ntoa(mreq.imr_interface)); + mreq.imr_multiaddr.s_addr = inet_addr(g_network); + printf ("subscription on multicast address %s.\n", inet_ntoa(mreq.imr_multiaddr)); + e = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + if (e < 0) { + perror("on_startup() failed"); + close(sock); + g_main_loop_quit(g_loop); + return FALSE; + } + +/* multicast loopback */ +/* multicast ttl */ + +/* add socket to event manager */ + g_io_channel = g_io_channel_unix_new (sock); + printf ("socket opened with encoding %s.\n", g_io_channel_get_encoding(g_io_channel)); + + /* guint event = */ g_io_add_watch (g_io_channel, G_IO_IN | G_IO_PRI, on_io_data, NULL); + /* guint event = */ g_io_add_watch (g_io_channel, G_IO_ERR | G_IO_HUP | G_IO_NVAL, on_io_error, NULL); + +/* add stdin to event manager */ + g_stdin_channel = g_io_channel_unix_new (fileno(stdin)); + printf ("binding stdin with encoding %s.\n", g_io_channel_get_encoding(g_stdin_channel)); + + g_io_add_watch (g_stdin_channel, G_IO_IN | G_IO_PRI, on_stdin_data, NULL); + +/* period timer to indicate some form of life */ +// TODO: Gnome 2.14: replace with g_timeout_add_seconds() + g_timeout_add(10 * 1000, (GSourceFunc)on_mark, NULL); + + puts ("READY"); + fflush (stdout); + return FALSE; +} + +static gboolean +on_mark ( + G_GNUC_UNUSED gpointer data + ) +{ + g_message ("-- MARK --"); + return TRUE; +} + +static gboolean +on_io_data ( + GIOChannel* source, + G_GNUC_UNUSED GIOCondition condition, + G_GNUC_UNUSED gpointer data + ) +{ + char buffer[4096]; + int fd = g_io_channel_unix_get_fd(source); + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + int len = recvfrom(fd, buffer, sizeof(buffer), MSG_DONTWAIT, (struct sockaddr*)&addr, &addr_len); + + if (g_filter.s_addr && g_filter.s_addr != addr.sin_addr.s_addr) { + return TRUE; + } + + printf ("%i bytes received from %s.\n", len, inet_ntoa(addr.sin_addr)); + + monitor_packet (buffer, len); + fflush (stdout); + + return TRUE; +} + +static gboolean +on_io_error ( + GIOChannel* source, + G_GNUC_UNUSED GIOCondition condition, + G_GNUC_UNUSED gpointer data + ) +{ + puts ("on_error."); + + GError *err; + g_io_channel_shutdown (source, FALSE, &err); + +/* remove event */ + return FALSE; +} + +/* process input commands from stdin/fd + */ + +static gboolean +on_stdin_data ( + GIOChannel* source, + G_GNUC_UNUSED GIOCondition condition, + G_GNUC_UNUSED gpointer data + ) +{ + gchar* str = NULL; + gsize len = 0; + gsize term = 0; + GError* err = NULL; + + g_io_channel_read_line (source, &str, &len, &term, &err); + if (len > 0) { + if (term) str[term] = 0; + + if (strcmp(str, "quit") == 0) { + g_main_loop_quit(g_loop); + } else if (strncmp(str, "filter ", strlen("filter ")) == 0) { + unsigned a, b, c, d; + int retval = sscanf(str, "filter %u.%u.%u.%u", &a, &b, &c, &d); + if (retval == 4) { + g_filter.s_addr = (d << 24) | (c << 16) | (b << 8) | a; + puts ("READY"); + } else { + printf ("invalid syntax for filter command."); + } + } else { + printf ("unknown command: %s\n", str); + } + } + + fflush (stdout); + g_free (str); + return TRUE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/nak.pl b/3rdparty/openpgm-svn-r1085/pgm/test/nak.pl new file mode 100755 index 0000000..cb75609 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/nak.pl @@ -0,0 +1,66 @@ +#!/usr/bin/perl +# nak.pl +# 5.3. Repairs + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$sim->say ("create ao"); +$sim->say ("bind ao"); +print "sim: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); # to process NAK requests + +print "app: publish test data.\n"; +$app->say ("send ao ringo"); +$app->say ("send ao ichigo"); +$app->say ("send ao momo"); + +my $odata = undef; +my $ocnt = 0; +for (1..3) { + print "mon: wait for odata ...\n"; + $odata = $mon->wait_for_odata; + $ocnt++; + print "mon: received $ocnt x odata.\n"; +} + +print "sim: send nak to app.\n"; +$sim->say ("net send nak ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 2"); + +print "mon: wait for rdata ...\n"; +$mon->wait_for_rdata; +print "mon: rdata received.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/nak_cancellation.pl b/3rdparty/openpgm-svn-r1085/pgm/test/nak_cancellation.pl new file mode 100755 index 0000000..6ad36fe --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/nak_cancellation.pl @@ -0,0 +1,161 @@ +#!/usr/bin/perl +# nak_cancellation.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use IO::Handle; +use JSON; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +pipe(FROM_PARENT, TO_CHILD) or die "pipe: $!"; +FROM_PARENT->autoflush(1); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + close FROM_PARENT; close TO_CHILD; + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("set ao NAK_BO_IVL 5000"); # increase to count for test system latency +$app->say ("set ao NAK_RPT_IVL 10000"); +$app->say ("set ao NAK_RDATA_IVL 10000"); +$app->say ("set ao NAK_NCF_RETRIES 15"); +$app->say ("set ao NAK_DATA_RETRIES 10"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +## capture GSI of test spp +$app->say ("send ao nashi"); +print "mon: wait for odata ...\n"; +my $odata = $mon->wait_for_odata; +print "mon: odata received.\n"; + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; +$sim->say ("net send spm ao 3200 90001 90000"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90001 ringo"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,003.\n"; +$sim->say ("net send odata ao 90003 90001 ichigo"); +my $t0 = [gettimeofday]; + +if (my $pid = fork) { +# parent + close FROM_PARENT; + + sleep 10; + + print "app: wait for data ...\n"; + my $data = $app->wait_for_data({ 'timeout' => 0 }); + print "app: data received [$data].\n"; + + print TO_CHILD "die\n"; + + close TO_CHILD; + waitpid($pid,0); +} else { +# child + die "cannot fork: $!" unless defined $pid; + close TO_CHILD; + + print "sim: loop waiting for NAKs ...\n"; + + my $fh = $sim->{in}; + vec(my $rin, fileno(FROM_PARENT), 1) = 1; + vec($rin, fileno($fh), 1) = 1; + my $rout = undef; + + my $b = ''; + my $state = 0; + my $json = new JSON; + my $io = IO::Handle->new_from_fd( fileno($fh), "r" ); + my $cnt = 0; + while (select($rout = $rin, undef, undef, undef)) + { + last if( vec($rout, fileno(FROM_PARENT), 1) ); + while (defined($_ = $io->getline)) + { + chomp; + my $l = $_; + if ($state == 0) { + if ($l =~ /{$/) { + $state = 1; + } else { + print "sim [$l]\n"; + last; + } + } + + if ($state == 1) { + $b .= $l; + + if ($l =~ /^}$/) { + $state = 0; + + my $obj = $json->jsonToObj($b); + if ($obj->{PGM}->{type} =~ /NAK/) { + $cnt++; + my $elapsed = tv_interval ( $t0, [gettimeofday] ); + print "sim: $cnt x NAK received in $elapsed seconds.\n"; + $sim->say ("net send ncf ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 90002"); + } + +# reset + $b = ''; + last; + } + } + } + last if ($io->eof); + } + + print "sim: loop finished.\n"; + close FROM_PARENT; + exit; +} + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/nak_list.pl b/3rdparty/openpgm-svn-r1085/pgm/test/nak_list.pl new file mode 100755 index 0000000..838dfd5 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/nak_list.pl @@ -0,0 +1,70 @@ +#!/usr/bin/perl +# nak_list.pl +# 9.3. NAK List Option - OPT_NAK_LIST + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$sim->say ("create ao"); +$sim->say ("bind ao"); +print "sim: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +print "app: publish test data.\n"; +$app->say ("send ao ringo"); +$app->say ("send ao ichigo"); +$app->say ("send ao momo"); + +my $odata = undef; +my $ocnt = 0; +for (1..3) { + print "mon: wait for odata ...\n"; + $odata = $mon->wait_for_odata; + $ocnt++; + print "mon: received $ocnt x odata.\n"; +} + +print "sim: send nak to app.\n"; +$sim->say ("net send nak ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 0,1,2"); + +my $rcnt = 0; +for (1..3) { + print "mon: wait for rdata ...\n"; + $mon->wait_for_rdata; + $rcnt++; + print "mon: received $rcnt x rdata.\n"; +} + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/nak_parity.pl b/3rdparty/openpgm-svn-r1085/pgm/test/nak_parity.pl new file mode 100755 index 0000000..50f961b --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/nak_parity.pl @@ -0,0 +1,75 @@ +#!/usr/bin/perl +# nak_parity.pl +# 5.3. Repairs + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$sim->say ("create ao"); +$sim->say ("set ao FEC RS(255,4)"); +$sim->say ("bind ao"); +print "sim: ready.\n"; + +$app->say ("create ao"); +$app->say ("set ao FEC RS(255,4)"); +$app->say ("bind ao"); +$app->say ("listen ao"); # to process NAK requests + +print "app: publish test data.\n"; +$app->say ("send ao ringo"); +$app->say ("send ao ichigo"); +$app->say ("send ao momo"); +$app->say ("send ao budo"); +$app->say ("send ao nashi"); +$app->say ("send ao anzu"); +$app->say ("send ao kaki"); + +my $odata = undef; +my $ocnt = 0; +for (1..7) { + print "mon: wait for odata ...\n"; + $odata = $mon->wait_for_odata; + $ocnt++; + print "mon: received $ocnt x odata.\n"; +} + +print "sim: send nak to app (transmission group = 0, packet count = 1).\n"; +$sim->say ("net send parity nak ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 1"); + +print "mon: wait for rdata ...\n"; +my $rdata = $mon->wait_for_rdata; +print "mon: rdata received.\n"; + +die "Selective RDATA received, parityPacket=false\n" unless $rdata->{PGM}->{options}->{parityPacket}; +print "Parity RDATA received.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/nak_repeat.pl b/3rdparty/openpgm-svn-r1085/pgm/test/nak_repeat.pl new file mode 100755 index 0000000..b811fa8 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/nak_repeat.pl @@ -0,0 +1,69 @@ +#!/usr/bin/perl +# nak_repeat.pl +# 5.3. Repairs + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$sim->say ("create ao"); +$sim->say ("bind ao"); +print "sim: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); # to process NAK requests + +print "app: publish test data.\n"; +$app->say ("send ao " . ("ringo" x 200)); +$app->say ("send ao " . ("ichigo" x 200)); +$app->say ("send ao " . ("momo" x 200)); + +my $odata = undef; +my $ocnt = 0; +for (1..3) { + print "mon: wait for odata ...\n"; + $odata = $mon->wait_for_odata; + $ocnt++; + print "mon: received $ocnt x odata.\n"; +} + +for (1..1000) { + my $i = $_; + print "sim: $i# send nak to app.\n"; + $sim->say ("net send nak ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 2"); + + print "mon: $i# wait for rdata ...\n"; + $mon->wait_for_rdata; + print "mon: $i# rdata received.\n"; +} + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/ncf.pl b/3rdparty/openpgm-svn-r1085/pgm/test/ncf.pl new file mode 100755 index 0000000..e26145f --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/ncf.pl @@ -0,0 +1,66 @@ +#!/usr/bin/perl +# ncf.pl +# 5.2. Negative Acknowledgment Confirmation + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$sim->say ("create ao"); +$sim->say ("bind ao"); +print "sim: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +print "app: publish test data.\n"; +$app->say ("send ao ringo"); +$app->say ("send ao ichigo"); +$app->say ("send ao momo"); + +my $odata = undef; +my $ocnt = 0; +for (1..3) { + print "mon: wait for odata ...\n"; + $odata = $mon->wait_for_odata; + $ocnt++; + print "mon: received $ocnt x odata.\n"; +} + +print "sim: send nak to app.\n"; +$sim->say ("net send nak ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 2"); + +print "mon: wait for ncf ...\n"; +$mon->wait_for_ncf; +print "mon: ncf received.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/ncf_cancellation.pl b/3rdparty/openpgm-svn-r1085/pgm/test/ncf_cancellation.pl new file mode 100755 index 0000000..ca23b22 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/ncf_cancellation.pl @@ -0,0 +1,147 @@ +#!/usr/bin/perl +# ncf_cancellation.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use IO::Handle; +use JSON; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +pipe(FROM_PARENT, TO_CHILD) or die "pipe: $!"; +FROM_PARENT->autoflush(1); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + close FROM_PARENT; close TO_CHILD; + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +## capture GSI of test spp +$app->say ("send ao nashi"); +print "mon: wait for odata ...\n"; +my $odata = $mon->wait_for_odata; +print "mon: odata received.\n"; + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; +$sim->say ("net send spm ao 3200 90001 90000"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90001 ringo"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,003.\n"; +$sim->say ("net send odata ao 90003 90001 ichigo"); +my $t0 = [gettimeofday]; + +if (my $pid = fork) { +# parent + close FROM_PARENT; + + print "app: wait for data ...\n"; + my $data = $app->wait_for_data({ 'timeout' => 0 }); + print "app: data received [$data].\n"; + + print TO_CHILD "die\n"; + + close TO_CHILD; + waitpid($pid,0); +} else { +# child + die "cannot fork: $!" unless defined $pid; + close TO_CHILD; + print "sim: loop waiting for NAKs ...\n"; + + my $fh = $sim->{in}; + vec(my $rin, fileno(FROM_PARENT), 1) = 1; + vec($rin, fileno($fh), 1) = 1; + my $rout = undef; + + my $b = ''; + my $state = 0; + my $json = new JSON; + my $io = IO::Handle->new_from_fd( fileno($fh), "r" ); + my $cnt = 0; + while (select($rout = $rin, undef, undef, undef)) + { + last if( vec($rout, fileno(FROM_PARENT), 1) ); + last unless (defined($_ = $io->getline)); + chomp; + my $l = $_; + if ($state == 0) { + if ($l =~ /{$/) { + $state = 1; + } else { + print "sim [$l]\n"; + } + } + + if ($state == 1) { + $b .= $l; + + if ($l =~ /^}$/) { + $state = 0; + + my $obj = $json->jsonToObj($b); + if ($obj->{PGM}->{type} =~ /NAK/) { + $cnt++; + my $elapsed = tv_interval ( $t0, [gettimeofday] ); + print "sim: $cnt x NAK received in $elapsed seconds.\n"; + } + +# reset + $b = ''; + } + } + } + + print "sim: loop finished.\n"; + close FROM_PARENT; + exit; +} + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/ncf_list.pl b/3rdparty/openpgm-svn-r1085/pgm/test/ncf_list.pl new file mode 100755 index 0000000..55b1d81 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/ncf_list.pl @@ -0,0 +1,72 @@ +#!/usr/bin/perl +# ncf_list.pl +# 9.3. NAK List Option - OPT_NAK_LIST + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$sim->say ("create ao"); +$sim->say ("bind ao"); +print "sim: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +print "app: publish test data.\n"; +$app->say ("send ao ringo"); +$app->say ("send ao ichigo"); +$app->say ("send ao momo"); + +my $odata = undef; +my $ocnt = 0; +for (1..3) { + print "mon: wait for odata ...\n"; + $odata = $mon->wait_for_odata; + $ocnt++; + print "mon: received $ocnt x odata.\n"; +} + +print "sim: send nak to app.\n"; +$sim->say ("net send nak ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 1,2,3"); + +print "mon: wait for ncf ...\n"; +my $ncf = $mon->wait_for_ncf; +print "mon: ncf received.\n"; +die "ncfSqn != 1\n" unless $ncf->{PGM}->{ncfSqn} == 1; +die "NCF list incorrect\n" unless ( + $ncf->{PGM}->{pgmOptions}[1]->{sqn}[0] == 2 + && $ncf->{PGM}->{pgmOptions}[1]->{sqn}[1] == 3 + ); +print "mon: ncf list correct: $ncf->{PGM}->{ncfSqn} + [$ncf->{PGM}->{pgmOptions}[1]->{sqn}[0], $ncf->{PGM}->{pgmOptions}[1]->{sqn}[1]]\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/ncf_suppression.pl b/3rdparty/openpgm-svn-r1085/pgm/test/ncf_suppression.pl new file mode 100755 index 0000000..c1d42a8 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/ncf_suppression.pl @@ -0,0 +1,101 @@ +#!/usr/bin/perl +# ncf_suppression.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +## capture GSI of test spp +$app->say ("send ao nashi"); +print "mon: wait for odata ...\n"; +my $odata = $mon->wait_for_odata; +print "mon: odata received.\n"; + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; +$sim->say ("net send spm ao 3200 90001 90000"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90001 ringo"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +## first run through with regular NAK generation to get regular backoff interval +print "sim: publish ODATA sqn 90,003.\n"; +$sim->say ("net send odata ao 90003 90001 ichigo"); +my $t0 = [gettimeofday]; +print "sim: waiting for valid NAK.\n"; +$sim->wait_for_nak; +my $normal_backoff = tv_interval ( $t0, [gettimeofday] ); +print "sim: NAK received in $normal_backoff seconds.\n"; + +## cleanup by publishing repair data +print "sim: publish RDATA sqn 90,002.\n"; +$sim->say ("net send odata ao 90002 90001 momo"); +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +## second run with NAK suppression +$t0 = [gettimeofday]; +print "sim: publish ODATA sqn 90,005.\n"; +$sim->say ("net send odata ao 90005 90001 anzu"); +print "sim: publish NCF sqn 90,004.\n"; +$sim->say ("net send ncf ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 90004"); + +print "sim: waiting for valid NAK.\n"; +$sim->wait_for_nak; +my $suppressed_backoff = tv_interval ( $t0, [gettimeofday] ); +print "sim: NAK received in $suppressed_backoff seconds.\n"; + +die "NAK suppression failed.\n" unless ($suppressed_backoff > $normal_backoff); + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/odata.pl b/3rdparty/openpgm-svn-r1085/pgm/test/odata.pl new file mode 100755 index 0000000..50bcec0 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/odata.pl @@ -0,0 +1,44 @@ +#!/usr/bin/perl +# odata.pl +# 3.6.2.1. ODATA - Original Data + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$app->connect; + +sub close_ssh { + $mon = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); + +print "app: publish test data.\n"; +$app->say ("send ao ringo"); + +print "mon: wait for odata ...\n"; +$mon->wait_for_odata; +print "mon: received odata.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/odata_completion.pl b/3rdparty/openpgm-svn-r1085/pgm/test/odata_completion.pl new file mode 100755 index 0000000..6b45a47 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/odata_completion.pl @@ -0,0 +1,87 @@ +#!/usr/bin/perl +# odata_completion.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +## capture GSI of test spp +$app->say ("send ao nashi"); +print "mon: wait for odata ...\n"; +my $odata = $mon->wait_for_odata; +print "mon: odata received.\n"; + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; +$sim->say ("net send spm ao 3200 90001 90000"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90001 ringo"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,003.\n"; +$sim->say ("net send odata ao 90003 90001 ichigo"); +print "sim: waiting for valid NAK.\n"; +$sim->wait_for_nak; +print "sim: NAK received.\n"; + +print "sim: publish ODATA sqn 90,002.\n"; +$sim->say ("net send odata ao 90002 90001 momo"); + +for (1..2) +{ + print "app: wait for data ...\n"; + my $data = $app->wait_for_data; + print "app: data received [$data].\n"; +} + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/odata_jump.pl b/3rdparty/openpgm-svn-r1085/pgm/test/odata_jump.pl new file mode 100755 index 0000000..aec4d82 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/odata_jump.pl @@ -0,0 +1,71 @@ +#!/usr/bin/perl +# odata_jump.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; +$sim->say ("net send spm ao 3200 90001 90000"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90001 ringo"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,003.\n"; +$sim->say ("net send odata ao 90003 90001 ichigo"); + +print "sim: waiting for valid NAK.\n"; +$sim->wait_for_nak; +print "sim: NAK received.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/odata_jump_parity.pl b/3rdparty/openpgm-svn-r1085/pgm/test/odata_jump_parity.pl new file mode 100755 index 0000000..99179cc --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/odata_jump_parity.pl @@ -0,0 +1,83 @@ +#!/usr/bin/perl +# odata_jump_parity.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); + +print "sim: publish SPM txw_trail 32,769 txw_lead 32,768 at spm_sqn 3200, advertise on-demand parity, k=4.\n"; +$sim->say ("net send spm ao 3200 32769 32768 on-demand 4"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 32,769.\n"; +$sim->say ("net send odata ao 32769 32769 ringo"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +# force window into next transmission group +print "sim: publish ODATA sqn 32,771.\n"; +$sim->say ("net send odata ao 32771 32769 ichigo"); +print "sim: publish ODATA sqn 32,772.\n"; +$sim->say ("net send odata ao 32772 32769 momo"); +print "sim: publish ODATA sqn 32,773.\n"; +$sim->say ("net send odata ao 32773 32769 yakitori"); +print "sim: publish ODATA sqn 32,774.\n"; +$sim->say ("net send odata ao 32774 32769 sasami"); +print "sim: publish ODATA sqn 32,775.\n"; +$sim->say ("net send odata ao 32775 32769 tebasaki"); + +print "sim: waiting for valid NAK.\n"; +my $nak = $sim->wait_for_nak; +print "sim: NAK received.\n"; + +die "Selective NAK received, parityPacket=false\n" unless $nak->{PGM}->{options}->{parityPacket}; +print "Parity NAK received.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/odata_number.pl b/3rdparty/openpgm-svn-r1085/pgm/test/odata_number.pl new file mode 100755 index 0000000..2d442dc --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/odata_number.pl @@ -0,0 +1,59 @@ +#!/usr/bin/perl +# odata_number.pl +# 5.1.1. Maximum Cumulative Transmit Rate + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$app->connect; + +sub close_ssh { + $mon = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +print "app: send 1000 data packets ...\n"; +# hide stdout +open(OLDOUT, ">&STDOUT"); +open(STDOUT, ">/dev/null") or die "Can't redirect stdout: $!"; + +for (0..999) +{ + my $i = $_; + $app->say ("send ao $i"); + my $odata = $mon->wait_for_odata; + + die "out of sequence ODATA, received $odata->{PGM}->{odSqn} expected $i\n" unless $odata->{PGM}->{odSqn} == $i; +} + +# restore stdout +close(STDOUT) or die "Can't close STDOUT: $!"; +open(STDOUT, ">&OLDOUT") or die "Can't restore stdout: $!"; +close(OLDOUT) or die "Can't close OLDOUT: $!"; + +print "mon: received 1000 x odata.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/odata_rate.pl b/3rdparty/openpgm-svn-r1085/pgm/test/odata_rate.pl new file mode 100755 index 0000000..fb8c7e0 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/odata_rate.pl @@ -0,0 +1,68 @@ +#!/usr/bin/perl +# odata_number.pl +# 5.1.1. Maximum Cumulative Transmit Rate + +use strict; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$app->connect; + +sub close_ssh { + $mon = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("set ao TXW_MAX_RTE 1500"); +$app->say ("bind ao"); + +print "app: send 50 data packets ...\n"; +my $t0 = [gettimeofday]; + +# hide stdout +open(OLDOUT, ">&STDOUT"); +open(STDOUT, ">/dev/null") or die "Can't redirect stdout: $!"; + +my $payload = "ringo" x 100; +my $bytes = 0; +for (1..50) +{ + $app->say ("send ao $payload"); + my $odata = $mon->wait_for_odata; + $bytes += $odata->{IP}->{length}; +} + +close(STDOUT) or die "Can't close STDOUT: $!"; +open(STDOUT, ">&OLDOUT") or die "Can't restore stdout: $!"; +close(OLDOUT) or die "Can't close OLDOUT: $!"; + +my $elapsed = tv_interval ( $t0, [gettimeofday] ); +print "mon: received 50 x odata, $bytes bytes in $elapsed seconds.\n"; + +my $rate = $bytes / $elapsed; +$rate = $bytes if ($rate > $bytes); +print "mon: incoming data rate $rate bps.\n"; + +die "incoming rate exceeds set TXW_MAX_RTE\n" unless $rate < 1650; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/odata_reception.pl b/3rdparty/openpgm-svn-r1085/pgm/test/odata_reception.pl new file mode 100755 index 0000000..ef95974 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/odata_reception.pl @@ -0,0 +1,59 @@ +#!/usr/bin/perl +# odata_reception.pl +# 6.1. Data Reception + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); + +print "sim: publish ODATA sqn 90,000.\n"; +$sim->say ("net send odata ao 90000 90000 ringo"); +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: received data [$data].\n"; + +# no NAKs should be generated. +# TODO: test for silence in {mon} + +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90000 ichigo"); +print "app: wait for data ...\n"; +$data = $app->wait_for_data; +print "app: received data [$data].\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/on-demand_spm.pl b/3rdparty/openpgm-svn-r1085/pgm/test/on-demand_spm.pl new file mode 100755 index 0000000..eb86cf9 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/on-demand_spm.pl @@ -0,0 +1,49 @@ +#!/usr/bin/perl +# on-demand_spm.pl +# 5.1.4. Ambient SPMs with on-demand parity flag + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$app->connect; + +sub close_ssh { + $mon = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("set ao FEC RS(255,64)"); +$app->say ("bind ao"); +print "app: ready.\n"; + +print "mon: wait for spm ...\n"; +my $spm = $mon->wait_for_spm; +print "mon: received spm.\n"; + +die "SPM does not contain any PGM options\n" unless $spm->{PGM}->{pgmOptions}; +die "SPM does not contain a PGM_OPT_PARITY_PRM option\n" unless $spm->{PGM}->{pgmOptions}[1]->{type} =~ /OPT_PARITY_PRM/; +print "pro-active parity " . ($spm->{PGM}->{pgmOptions}[1]->{'P-bit'} ? 'enabled' : 'disabled') . ", P-bit " . ($spm->{PGM}->{pgmOptions}[1]->{'P-bit'} ? 'true' : 'false') . "\n"; +die "on-demand parity disabled, O-bit false\n" unless $spm->{PGM}->{pgmOptions}[1]->{'O-bit'}; +print "on-demand parity enabled, O-bit true\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/outofwindow_ncf.pl b/3rdparty/openpgm-svn-r1085/pgm/test/outofwindow_ncf.pl new file mode 100755 index 0000000..4849e36 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/outofwindow_ncf.pl @@ -0,0 +1,103 @@ +#!/usr/bin/perl +# outofwindow_ncf.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +## capture GSI of test spp +$app->say ("send ao nashi"); +print "mon: wait for odata ...\n"; +my $odata = $mon->wait_for_odata; +print "mon: odata received.\n"; + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; +$sim->say ("net send spm ao 3200 90001 90000"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90001 ringo"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +## first run through with regular NAK generation to get regular backoff interval +print "sim: publish ODATA sqn 90,003.\n"; +$sim->say ("net send odata ao 90003 90001 ichigo"); +my $t0 = [gettimeofday]; +print "sim: waiting for valid NAK.\n"; +$sim->wait_for_nak; +my $normal_backoff = tv_interval ( $t0, [gettimeofday] ); +print "sim: NAK received in $normal_backoff seconds.\n"; + +## cleanup by publishing repair data +print "sim: publish RDATA sqn 90,002.\n"; +$sim->say ("net send odata ao 90002 90001 momo"); +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +## second run with NAK suppression +$t0 = [gettimeofday]; +print "sim: publish ODATA sqn 90,005.\n"; +$sim->say ("net send odata ao 90005 90001 anzu"); +print "sim: publish NCF sqn 90,004.\n"; +$sim->say ("net send ncf ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 90002"); + +print "sim: waiting for valid NAK.\n"; +$sim->wait_for_nak; +my $outofwindow_backoff = tv_interval ( $t0, [gettimeofday] ); +print "sim: NAK received in $outofwindow_backoff seconds.\n"; + +# allow 100ms tolerance +my $fabs = abs( ($outofwindow_backoff - $normal_backoff) * 1000 ); +die "Out-of-window NCF altered back-off interval by $fabs ms.\n" unless ($fabs < 100); + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion.pl b/3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion.pl new file mode 100755 index 0000000..8a0cc60 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion.pl @@ -0,0 +1,87 @@ +#!/usr/bin/perl +# rdata_completion.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +## capture GSI of test spp +$app->say ("send ao nashi"); +print "mon: wait for odata ...\n"; +my $odata = $mon->wait_for_odata; +print "mon: odata received.\n"; + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; +$sim->say ("net send spm ao 3200 90001 90000"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90001 ringo"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,003.\n"; +$sim->say ("net send odata ao 90003 90001 ichigo"); +print "sim: waiting for valid NAK.\n"; +$sim->wait_for_nak; +print "sim: NAK received.\n"; + +print "sim: publish RDATA sqn 90,002.\n"; +$sim->say ("net send rdata ao 90002 90001 momo"); + +for (1..2) +{ + print "app: wait for data ...\n"; + my $data = $app->wait_for_data; + print "app: data received [$data].\n"; +} + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion_parity.pl b/3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion_parity.pl new file mode 100755 index 0000000..95a6f7b --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion_parity.pl @@ -0,0 +1,97 @@ +#!/usr/bin/perl +# rdata_completion_parity.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("set ao FEC RS(255,4)"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +## capture GSI of test spp +$app->say ("send ao nashi"); +print "mon: wait for odata ...\n"; +my $odata = $mon->wait_for_odata; +print "mon: odata received.\n"; + +$sim->say ("create fake ao"); +$sim->say ("set ao FEC RS(255,4)"); +$sim->say ("bind ao"); + +print "sim: publish SPM txw_trail 32,769 txw_lead 32,768 at spm_sqn 3200, advertise on-demand parity, k=4.\n"; +$sim->say ("net send spm ao 3200 32768 32767 on-demand 4"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 32,768.\n"; +$sim->say ("net send odata ao 32768 32768 ringo000"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 32,770.\n"; +$sim->say ("net send odata ao 32770 32768 momo0000"); +print "sim: publish ODATA sqn 32,771.\n"; +$sim->say ("net send odata ao 32771 32768 yakitori"); +print "sim: publish ODATA sqn 32,772.\n"; +$sim->say ("net send odata ao 32772 32768 sasami00"); +print "sim: publish ODATA sqn 32,773.\n"; +$sim->say ("net send odata ao 32773 32768 tebasaki"); + +print "sim: waiting for valid parity NAK.\n"; +my $nak = $sim->wait_for_nak; +die "Selective NAK received, parityPacket=false\n" unless $nak->{PGM}->{options}->{parityPacket}; +print "sim: Parity NAK received.\n"; + +print "sim: publish parity RDATA, tg_sqn 32,768, pkt_cnt 1 (sqn 32,768).\n"; +$sim->say ("net send parity rdata ao 32768 32768 ringo000 ichigo00 momo0000 yakitori"); + +for (1..5) +{ + print "app: wait for data ...\n"; + my $data = $app->wait_for_data; + print "app: data received [$data].\n"; +} + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion_parity_var_pktlen.pl b/3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion_parity_var_pktlen.pl new file mode 100755 index 0000000..9011ea1 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion_parity_var_pktlen.pl @@ -0,0 +1,97 @@ +#!/usr/bin/perl +# rdata_completion_parity_var_pktlen.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("set ao FEC RS(255,4)"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +## capture GSI of test spp +$app->say ("send ao nashi"); +print "mon: wait for odata ...\n"; +my $odata = $mon->wait_for_odata; +print "mon: odata received.\n"; + +$sim->say ("create fake ao"); +$sim->say ("set ao FEC RS(255,4)"); +$sim->say ("bind ao"); + +print "sim: publish SPM txw_trail 32,769 txw_lead 32,768 at spm_sqn 3200, advertise on-demand parity, k=4.\n"; +$sim->say ("net send spm ao 3200 32768 32767 on-demand 4"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 32,768.\n"; +$sim->say ("net send odata ao 32768 32768 ringo"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 32,770.\n"; +$sim->say ("net send odata ao 32770 32768 momo"); +print "sim: publish ODATA sqn 32,771.\n"; +$sim->say ("net send odata ao 32771 32768 yakitori"); +print "sim: publish ODATA sqn 32,772.\n"; +$sim->say ("net send odata ao 32772 32768 sasami"); +print "sim: publish ODATA sqn 32,773.\n"; +$sim->say ("net send odata ao 32773 32768 tebasaki"); + +print "sim: waiting for valid parity NAK.\n"; +my $nak = $sim->wait_for_nak; +die "Selective NAK received, parityPacket=false\n" unless $nak->{PGM}->{options}->{parityPacket}; +print "sim: Parity NAK received.\n"; + +print "sim: publish parity RDATA, tg_sqn 32,768, pkt_cnt 1 (sqn 32,768).\n"; +$sim->say ("net send parity rdata ao 32768 32768 ringo ichigo momo yakitori"); + +for (1..5) +{ + print "app: wait for data ...\n"; + my $data = $app->wait_for_data; + print "app: data received [$data].\n"; +} + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/rdata_jump.pl b/3rdparty/openpgm-svn-r1085/pgm/test/rdata_jump.pl new file mode 100755 index 0000000..f472b33 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/rdata_jump.pl @@ -0,0 +1,71 @@ +#!/usr/bin/perl +# rdata_jump.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; +$sim->say ("net send spm ao 3200 90001 90000"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90001 ringo"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish RDATA sqn 90,003.\n"; +$sim->say ("net send rdata ao 90003 90001 ichigo"); + +print "sim: waiting for valid NAK.\n"; +$sim->wait_for_nak; +print "sim: NAK received.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/rdata_reception.pl b/3rdparty/openpgm-svn-r1085/pgm/test/rdata_reception.pl new file mode 100755 index 0000000..a26bb4e --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/rdata_reception.pl @@ -0,0 +1,59 @@ +#!/usr/bin/perl +# rdata_reception.pl +# 6.1. Data Reception + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); + +print "sim: publish RDATA sqn 90,000.\n"; +$sim->say ("net send rdata ao 90000 90000 ringo"); +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: received data [$data].\n"; + +# no NAKs should be generated. +# TODO: test for silence in {mon} + +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90000 ichigo"); +print "app: wait for data ...\n"; +$data = $app->wait_for_data; +print "app: received data [$data].\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/sim.c b/3rdparty/openpgm-svn-r1085/pgm/test/sim.c new file mode 100644 index 0000000..b0e473b --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/sim.c @@ -0,0 +1,1924 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM conformance endpoint simulator. + * + * Copyright (c) 2006-2008 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "dump-json.h" +#include "async.h" + + +/* typedefs */ + +struct idle_source { + GSource source; + guint64 expiration; +}; + +struct sim_session { + char* name; + pgm_transport_t* transport; + gboolean is_transport_fake; + GIOChannel* recv_channel; + pgm_async_t* async; +}; + +/* globals */ +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "sim" + +#ifndef SOL_IP +# define SOL_IP IPPROTO_IP +#endif +#ifndef SOL_IPV6 +# define SOL_IPV6 IPPROTO_IPV6 +#endif + + +static int g_port = 7500; +static const char* g_network = ";239.192.0.1"; + +static int g_max_tpdu = 1500; +static int g_sqns = 100 * 1000; + +static GList* g_sessions_list = NULL; +static GHashTable* g_sessions = NULL; +static GMainLoop* g_loop = NULL; +static GIOChannel* g_stdin_channel = NULL; + + +static void on_signal (int, gpointer); +static gboolean on_startup (gpointer); +static gboolean on_mark (gpointer); +static void destroy_session (struct sim_session*); +static int on_data (gpointer, guint, gpointer); +static gboolean on_stdin_data (GIOChannel*, GIOCondition, gpointer); +void generic_net_send_nak (guint8, char*, pgm_tsi_t*, struct pgm_sqn_list_t*); + + +G_GNUC_NORETURN static +void +usage (const char* bin) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + exit (1); +} + +int +main ( + int argc, + char *argv[] + ) +{ + pgm_error_t* err = NULL; + +/* pre-initialise PGM messages module to add hook for GLib logging */ + pgm_messages_init(); + log_init (); + g_message ("sim"); + + if (!pgm_init (&err)) { + g_error ("Unable to start PGM engine: %s", (err && err->message) ? err->message : "(null)"); + pgm_error_free (err); + pgm_messages_shutdown(); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:h")) != -1) + { + switch (c) { + case 'n': g_network = optarg; break; + case 's': g_port = atoi (optarg); break; + + case 'h': + case '?': + pgm_messages_shutdown(); + usage (binary_name); + } + } + + g_loop = g_main_loop_new (NULL, FALSE); + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); + signal (SIGHUP, SIG_IGN); + pgm_signal_install (SIGINT, on_signal, g_loop); + pgm_signal_install (SIGTERM, on_signal, g_loop); + +/* delayed startup */ + g_message ("scheduling startup."); + g_timeout_add (0, (GSourceFunc)on_startup, NULL); + +/* dispatch loop */ + g_message ("entering main event loop ... "); + g_main_loop_run (g_loop); + + g_message ("event loop terminated, cleaning up."); + +/* cleanup */ + g_main_loop_unref(g_loop); + g_loop = NULL; + + if (g_sessions) { + g_message ("destroying sessions."); + while (g_sessions_list) { + destroy_session (g_sessions_list->data); + g_sessions_list = g_list_delete_link (g_sessions_list, g_sessions_list); + } + g_hash_table_unref (g_sessions); + g_sessions = NULL; + } + + if (g_stdin_channel) { + puts ("unbinding stdin."); + g_io_channel_unref (g_stdin_channel); + g_stdin_channel = NULL; + } + + g_message ("PGM engine shutdown."); + pgm_shutdown(); + g_message ("finished."); + pgm_messages_shutdown(); + return EXIT_SUCCESS; +} + +static +void +destroy_session ( + struct sim_session* sess + ) +{ + printf ("destroying transport \"%s\"\n", sess->name); + pgm_transport_destroy (sess->transport, TRUE); + sess->transport = NULL; + g_free (sess->name); + sess->name = NULL; + g_free (sess); +} + +static +void +on_signal ( + int signum, + gpointer user_data + ) +{ + GMainLoop* loop = (GMainLoop*)user_data; + g_message ("on_signal (signum:%d user-data:%p)", signum, user_data); + g_main_loop_quit (loop); +} + +static +gboolean +on_startup ( + G_GNUC_UNUSED gpointer data + ) +{ + g_message ("startup."); + + g_sessions = g_hash_table_new (g_str_hash, g_str_equal); + +/* add stdin to event manager */ + g_stdin_channel = g_io_channel_unix_new (fileno(stdin)); + printf ("binding stdin with encoding %s.\n", g_io_channel_get_encoding(g_stdin_channel)); + + g_io_add_watch (g_stdin_channel, G_IO_IN | G_IO_PRI, on_stdin_data, NULL); + +/* period timer to indicate some form of life */ +// TODO: Gnome 2.14: replace with g_timeout_add_seconds() + g_timeout_add(10 * 1000, (GSourceFunc)on_mark, NULL); + + puts ("READY"); + fflush (stdout); + return FALSE; +} + +static +bool +fake_pgm_transport_create ( + pgm_transport_t** transport, + struct pgm_transport_info_t* tinfo, + G_GNUC_UNUSED pgm_error_t** error + ) +{ + pgm_transport_t* new_transport; + + g_return_val_if_fail (NULL != transport, FALSE); + g_return_val_if_fail (NULL != tinfo, FALSE); + if (tinfo->ti_sport) g_return_val_if_fail (tinfo->ti_sport != tinfo->ti_dport, FALSE); + if (tinfo->ti_udp_encap_ucast_port) + g_return_val_if_fail (tinfo->ti_udp_encap_mcast_port, FALSE); + else if (tinfo->ti_udp_encap_mcast_port) + g_return_val_if_fail (tinfo->ti_udp_encap_ucast_port, FALSE); + g_return_val_if_fail (tinfo->ti_recv_addrs_len > 0, FALSE); + g_return_val_if_fail (tinfo->ti_recv_addrs_len <= IP_MAX_MEMBERSHIPS, FALSE); + g_return_val_if_fail (NULL != tinfo->ti_recv_addrs, FALSE); + g_return_val_if_fail (1 == tinfo->ti_send_addrs_len, FALSE); + g_return_val_if_fail (NULL != tinfo->ti_send_addrs, FALSE); + for (unsigned i = 0; i < tinfo->ti_recv_addrs_len; i++) + { + g_return_val_if_fail (tinfo->ti_recv_addrs[i].gsr_group.ss_family == tinfo->ti_recv_addrs[0].gsr_group.ss_family, -FALSE); + g_return_val_if_fail (tinfo->ti_recv_addrs[i].gsr_group.ss_family == tinfo->ti_recv_addrs[i].gsr_source.ss_family, -FALSE); + } + g_return_val_if_fail (tinfo->ti_send_addrs[0].gsr_group.ss_family == tinfo->ti_send_addrs[0].gsr_source.ss_family, -FALSE); + +/* create transport object */ + new_transport = g_new0 (pgm_transport_t, 1); + +/* transport defaults */ + new_transport->can_send_data = TRUE; + new_transport->can_send_nak = FALSE; + new_transport->can_recv_data = TRUE; + + memcpy (&new_transport->tsi.gsi, &tinfo->ti_gsi, sizeof(pgm_gsi_t)); + new_transport->dport = g_htons (tinfo->ti_dport); + if (tinfo->ti_sport) { + new_transport->tsi.sport = tinfo->ti_sport; + } else { + do { + new_transport->tsi.sport = g_htons (g_random_int_range (0, UINT16_MAX)); + } while (new_transport->tsi.sport == new_transport->dport); + } + +/* network data ports */ + new_transport->udp_encap_ucast_port = tinfo->ti_udp_encap_ucast_port; + new_transport->udp_encap_mcast_port = tinfo->ti_udp_encap_mcast_port; + +/* copy network parameters */ + memcpy (&new_transport->send_gsr, &tinfo->ti_send_addrs[0], sizeof(struct group_source_req)); + for (unsigned i = 0; i < tinfo->ti_recv_addrs_len; i++) + { + memcpy (&new_transport->recv_gsr[i], &tinfo->ti_recv_addrs[i], sizeof(struct group_source_req)); + ((struct sockaddr_in*)&new_transport->recv_gsr[i].gsr_group)->sin_port = g_htons (new_transport->udp_encap_mcast_port); + } + new_transport->recv_gsr_len = tinfo->ti_recv_addrs_len; + +/* open sockets to implement PGM */ + int socket_type, protocol; + if (new_transport->udp_encap_ucast_port) { + puts ("opening UDP encapsulated sockets."); + socket_type = SOCK_DGRAM; + protocol = IPPROTO_UDP; + } else { + puts ("opening raw sockets."); + socket_type = SOCK_RAW; + protocol = IPPROTO_PGM; + } + + if ((new_transport->recv_sock = socket (new_transport->recv_gsr[0].gsr_group.ss_family, + socket_type, + protocol)) < 0) + { + if (errno == EPERM && 0 != getuid()) { + g_critical ("PGM protocol requires this program to run as superuser."); + } + goto err_destroy; + } + + if ((new_transport->send_sock = socket (new_transport->send_gsr.gsr_group.ss_family, + socket_type, + protocol)) < 0) + { + goto err_destroy; + } + + if ((new_transport->send_with_router_alert_sock = socket (new_transport->send_gsr.gsr_group.ss_family, + socket_type, + protocol)) < 0) + { + goto err_destroy; + } + + *transport = new_transport; + return TRUE; + +err_destroy: + if (new_transport->recv_sock) { + close(new_transport->recv_sock); + new_transport->recv_sock = 0; + } + if (new_transport->send_sock) { + close(new_transport->send_sock); + new_transport->send_sock = 0; + } + if (new_transport->send_with_router_alert_sock) { + close(new_transport->send_with_router_alert_sock); + new_transport->send_with_router_alert_sock = 0; + } + + g_free (new_transport); + new_transport = NULL; + return FALSE; +} + +static +gboolean +on_io_data ( + GIOChannel* source, + G_GNUC_UNUSED GIOCondition condition, + gpointer data + ) +{ + pgm_transport_t* transport = data; + + struct pgm_sk_buff_t* skb = pgm_alloc_skb (transport->max_tpdu); + int fd = g_io_channel_unix_get_fd(source); + struct sockaddr_storage src_addr; + socklen_t src_addr_len = sizeof(src_addr); + skb->len = recvfrom(fd, skb->head, transport->max_tpdu, MSG_DONTWAIT, (struct sockaddr*)&src_addr, &src_addr_len); + + printf ("%i bytes received from %s.\n", skb->len, inet_ntoa(((struct sockaddr_in*)&src_addr)->sin_addr)); + + monitor_packet (skb->data, skb->len); + fflush (stdout); + +/* parse packet to maintain peer database */ + if (transport->udp_encap_ucast_port) { + if (!pgm_parse_udp_encap (skb, NULL)) + goto out; + } else { + struct sockaddr_storage addr; + if (!pgm_parse_raw (skb, (struct sockaddr*)&addr, NULL)) + goto out; + } + + if (pgm_is_upstream (skb->pgm_header->pgm_type) || + pgm_is_peer (skb->pgm_header->pgm_type)) + goto out; /* ignore */ + +/* downstream = source to receivers */ + if (!pgm_is_downstream (skb->pgm_header->pgm_type)) + goto out; + +/* pgm packet DPORT contains our transport DPORT */ + if (skb->pgm_header->pgm_dport != transport->dport) + goto out; + +/* search for TSI peer context or create a new one */ + pgm_peer_t* sender = pgm_hashtable_lookup (transport->peers_hashtable, &skb->tsi); + if (sender == NULL) + { + printf ("new peer, tsi %s, local nla %s\n", + pgm_tsi_print (&skb->tsi), + inet_ntoa(((struct sockaddr_in*)&src_addr)->sin_addr)); + + pgm_peer_t* peer = g_new0 (pgm_peer_t, 1); + peer->transport = transport; + memcpy (&peer->tsi, &skb->tsi, sizeof(pgm_tsi_t)); + ((struct sockaddr_in*)&peer->nla)->sin_addr.s_addr = INADDR_ANY; + memcpy (&peer->local_nla, &src_addr, src_addr_len); + + pgm_hashtable_insert (transport->peers_hashtable, &peer->tsi, peer); + sender = peer; + } + +/* handle SPMs for advertised NLA */ + if (skb->pgm_header->pgm_type == PGM_SPM) + { + char *pgm_data = (char*)(skb->pgm_header + 1); + struct pgm_spm* spm = (struct pgm_spm*)pgm_data; + guint32 spm_sqn = g_ntohl (spm->spm_sqn); + + if ( pgm_uint32_gte (spm_sqn, sender->spm_sqn) + || ( ((struct sockaddr*)&sender->nla)->sa_family == 0 ) ) + { + pgm_nla_to_sockaddr (&spm->spm_nla_afi, (struct sockaddr*)&sender->nla); + sender->spm_sqn = spm_sqn; + } + } + +out: + return TRUE; +} + +static +bool +fake_pgm_transport_bind ( + pgm_transport_t* transport, + G_GNUC_UNUSED pgm_error_t** error + ) +{ + g_return_val_if_fail (NULL != transport, FALSE); + g_return_val_if_fail (!transport->is_bound, FALSE); + +/* create peer list */ + transport->peers_hashtable = pgm_hashtable_new (pgm_tsi_hash, pgm_tsi_equal); + +/* bind udp unicast sockets to interfaces, note multicast on a bound interface is + * fruity on some platforms so callee should specify any interface. + * + * after binding default interfaces (0.0.0.0) are resolved + */ + struct sockaddr_storage recv_addr; + memset (&recv_addr, 0, sizeof(recv_addr)); + ((struct sockaddr*)&recv_addr)->sa_family = AF_INET; + ((struct sockaddr_in*)&recv_addr)->sin_port = transport->udp_encap_ucast_port; + ((struct sockaddr_in*)&recv_addr)->sin_addr.s_addr = INADDR_ANY; + + int retval = bind (transport->recv_sock, + (struct sockaddr*)&recv_addr, + pgm_sockaddr_len((struct sockaddr*)&recv_addr)); + if (retval < 0) { + goto out; + } + + struct sockaddr_storage send_addr, send_with_router_alert_addr; + memset (&send_addr, 0, sizeof(send_addr)); + if (!pgm_if_indextoaddr (transport->send_gsr.gsr_interface, + transport->send_gsr.gsr_group.ss_family, + pgm_sockaddr_scope_id((struct sockaddr*)&transport->send_gsr.gsr_group), + (struct sockaddr*)&send_addr, + NULL)) + { + goto out; + } + memcpy (&send_with_router_alert_addr, &send_addr, pgm_sockaddr_len((struct sockaddr*)&send_addr)); + retval = bind (transport->send_sock, + (struct sockaddr*)&send_addr, + pgm_sockaddr_len((struct sockaddr*)&send_addr)); + if (retval < 0) + goto out; + +/* resolve bound address if 0.0.0.0 */ + if (((struct sockaddr_in*)&send_addr)->sin_addr.s_addr == INADDR_ANY) + { + if (!pgm_if_getnodeaddr (AF_INET, (struct sockaddr*)&send_addr, sizeof(send_addr), NULL)) + goto out; + } + + retval = bind (transport->send_with_router_alert_sock, + (struct sockaddr*)&send_with_router_alert_addr, + pgm_sockaddr_len((struct sockaddr*)&send_with_router_alert_addr)); + if (retval < 0) + goto out; + + memcpy (&transport->send_addr, &send_addr, pgm_sockaddr_len((struct sockaddr*)&send_addr)); + +/* receiving groups (multiple) */ + for (unsigned i = 0; i < transport->recv_gsr_len; i++) + { + struct group_source_req* p = &transport->recv_gsr[i]; + int optname = (pgm_sockaddr_cmp ((struct sockaddr*)&p->gsr_group, (struct sockaddr*)&p->gsr_source) == 0) + ? MCAST_JOIN_GROUP : MCAST_JOIN_SOURCE_GROUP; + socklen_t plen = MCAST_JOIN_GROUP == optname ? sizeof(struct group_req) : sizeof(struct group_source_req); + retval = setsockopt(transport->recv_sock, SOL_IP, optname, p, plen); + if (retval < 0) + goto out; + } + +/* send group (singular) */ + retval = pgm_sockaddr_multicast_if (transport->send_sock, (struct sockaddr*)&transport->send_addr, transport->send_gsr.gsr_interface); + if (retval < 0) + goto out; + + retval = pgm_sockaddr_multicast_if (transport->send_with_router_alert_sock, (struct sockaddr*)&transport->send_addr, transport->send_gsr.gsr_interface); + if (retval < 0) + goto out; + +/* multicast loopback */ + retval = pgm_sockaddr_multicast_loop (transport->recv_sock, transport->recv_gsr[0].gsr_group.ss_family, FALSE); + if (retval < 0) + goto out; + retval = pgm_sockaddr_multicast_loop (transport->send_sock, transport->send_gsr.gsr_group.ss_family, FALSE); + if (retval < 0) + goto out; + retval = pgm_sockaddr_multicast_loop (transport->send_with_router_alert_sock, transport->send_gsr.gsr_group.ss_family, FALSE); + if (retval < 0) + goto out; + +/* multicast ttl: many crappy network devices go CPU ape with TTL=1, 16 is a popular alternative */ + retval = pgm_sockaddr_multicast_hops (transport->recv_sock, transport->recv_gsr[0].gsr_group.ss_family, transport->hops); + if (retval < 0) + goto out; + retval = pgm_sockaddr_multicast_hops (transport->send_sock, transport->send_gsr.gsr_group.ss_family, transport->hops); + if (retval < 0) + goto out; + retval = pgm_sockaddr_multicast_hops (transport->send_with_router_alert_sock, transport->send_gsr.gsr_group.ss_family, transport->hops); + if (retval < 0) + goto out; + +/* set Expedited Forwarding PHB for network elements, no ECN. + * + * codepoint 101110 (RFC 3246) + */ + int dscp = 0x2e << 2; + retval = pgm_sockaddr_tos (transport->send_sock, transport->send_gsr.gsr_group.ss_family, dscp); + if (retval < 0) + goto out; + retval = pgm_sockaddr_tos (transport->send_with_router_alert_sock, transport->send_gsr.gsr_group.ss_family, dscp); + if (retval < 0) + goto out; + +/* cleanup */ + transport->is_bound = TRUE; + return TRUE; + +out: + return FALSE; +} + +static +bool +fake_pgm_transport_destroy ( + pgm_transport_t* transport, + G_GNUC_UNUSED bool flush + ) +{ + g_return_val_if_fail (transport != NULL, FALSE); + + if (transport->recv_sock) { + puts ("closing receive socket."); + close(transport->recv_sock); + transport->recv_sock = 0; + } + if (transport->send_sock) { + puts ("closing send socket."); + close(transport->send_sock); + transport->send_sock = 0; + } + if (transport->send_with_router_alert_sock) { + puts ("closing send with router alert socket."); + close(transport->send_with_router_alert_sock); + transport->send_with_router_alert_sock = 0; + } + g_free (transport); + return TRUE; +} + +static +void +session_create ( + char* name, + gboolean is_fake + ) +{ + struct pgm_transport_info_t hints = { + .ti_family = AF_INET + }, *res = NULL; + pgm_error_t* err = NULL; + gboolean status; + +/* check for duplicate */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess != NULL) { + puts ("FAILED: duplicate session"); + return; + } + +/* create new and fill in bits */ + sess = g_new0(struct sim_session, 1); + sess->name = g_memdup (name, strlen(name)+1); + + if (!pgm_if_get_transport_info (g_network, &hints, &res, &err)) { + printf ("FAILED: pgm_if_get_transport_info(): %s\n", (err && err->message) ? err->message : "(null)"); + pgm_error_free (err); + goto err_free; + } + + if (!pgm_gsi_create_from_hostname (&res->ti_gsi, &err)) { + printf ("FAILED: pgm_gsi_create_from_hostname(): %s\n", (err && err->message) ? err->message : "(null)"); + pgm_error_free (err); + pgm_if_free_transport_info (res); + goto err_free; + } + + res->ti_dport = g_port; + res->ti_sport = 0; + if (is_fake) { + sess->is_transport_fake = TRUE; + status = fake_pgm_transport_create (&sess->transport, res, &err); + } else + status = pgm_transport_create (&sess->transport, res, &err); + if (!status) { + printf ("FAILED: pgm_transport_create(): %s\n", (err && err->message) ? err->message : "(null)"); + pgm_error_free (err); + pgm_if_free_transport_info (res); + goto err_free; + } + + pgm_if_free_transport_info (res); + +/* success */ + g_hash_table_insert (g_sessions, sess->name, sess); + g_sessions_list = g_list_prepend (g_sessions_list, sess); + printf ("created new session \"%s\"\n", sess->name); + puts ("READY"); + return; + +err_free: + g_free(sess->name); + g_free(sess); +} + +static +void +session_set_fec ( + char* name, + guint default_n, + guint default_k + ) +{ +/* check that session exists */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess == NULL) { + puts ("FAILED: session not found"); + return; + } + + pgm_transport_set_fec (sess->transport, FALSE /* pro-active */, TRUE /* on-demand */, TRUE /* varpkt-len */, default_n, default_k); + puts ("READY"); +} + +static +void +session_bind ( + char* name + ) +{ +/* check that session exists */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess == NULL) { + puts ("FAILED: session not found"); + return; + } + + pgm_transport_set_nonblocking (sess->transport, TRUE); + pgm_sockaddr_nonblocking (sess->transport->send_sock, FALSE); + pgm_transport_set_max_tpdu (sess->transport, g_max_tpdu); + pgm_transport_set_txw_sqns (sess->transport, g_sqns); + pgm_transport_set_rxw_sqns (sess->transport, g_sqns); + pgm_transport_set_hops (sess->transport, 16); + pgm_transport_set_ambient_spm (sess->transport, pgm_secs(30)); + guint spm_heartbeat[] = { pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(1300), pgm_secs(7), pgm_secs(16), pgm_secs(25), pgm_secs(30) }; + pgm_transport_set_heartbeat_spm (sess->transport, spm_heartbeat, G_N_ELEMENTS(spm_heartbeat)); + pgm_transport_set_peer_expiry (sess->transport, pgm_secs(300)); + pgm_transport_set_spmr_expiry (sess->transport, pgm_msecs(250)); + pgm_transport_set_nak_bo_ivl (sess->transport, pgm_msecs(50)); + pgm_transport_set_nak_rpt_ivl (sess->transport, pgm_secs(2)); + pgm_transport_set_nak_rdata_ivl (sess->transport, pgm_secs(2)); + pgm_transport_set_nak_data_retries (sess->transport, 50); + pgm_transport_set_nak_ncf_retries (sess->transport, 50); + + pgm_error_t* err = NULL; + gboolean status; + if (sess->is_transport_fake) + status = fake_pgm_transport_bind (sess->transport, &err); + else + status = pgm_transport_bind (sess->transport, &err); + if (!status) { + printf ("FAILED: pgm_transport_bind(): %s\n", err->message); + pgm_error_free (err); + return; + } + + if (sess->is_transport_fake) + { +/* add receive socket(s) to event manager */ + sess->recv_channel = g_io_channel_unix_new (sess->transport->recv_sock); + + GSource *source; + source = g_io_create_watch (sess->recv_channel, G_IO_IN); + g_source_set_callback (source, (GSourceFunc)on_io_data, sess->transport, NULL); + g_source_attach (source, NULL); + g_source_unref (source); + } + else + { + pgm_async_create (&sess->async, sess->transport, 0); + pgm_async_add_watch (sess->async, on_data, sess); + } + + puts ("READY"); +} + +static inline +gssize +pgm_sendto ( + pgm_transport_t* transport, + gboolean rl, + gboolean ra, + const void* buf, + gsize len, + const struct sockaddr* to, + socklen_t tolen + ) +{ + int sock = ra ? transport->send_with_router_alert_sock : transport->send_sock; + pgm_mutex_lock (&transport->send_mutex); + ssize_t sent = sendto (sock, buf, len, 0, to, tolen); + pgm_mutex_unlock (&transport->send_mutex); + return sent > 0 ? (gssize)len : (gssize)sent; +} + +static +int +pgm_reset_heartbeat_spm (pgm_transport_t* transport) +{ + int retval = 0; + + pgm_mutex_lock (&transport->timer_mutex); + +/* re-set spm timer */ + transport->spm_heartbeat_state = 1; + transport->next_heartbeat_spm = pgm_time_update_now() + transport->spm_heartbeat_interval[transport->spm_heartbeat_state++]; + +/* prod timer thread if sleeping */ + if (pgm_time_after( transport->next_poll, transport->next_heartbeat_spm )) + transport->next_poll = transport->next_heartbeat_spm; + + pgm_mutex_unlock (&transport->timer_mutex); + + return retval; +} + +static inline +int +brokn_send_apdu_unlocked ( + pgm_transport_t* transport, + const gchar* buf, + gsize count, + gsize* bytes_written + ) +{ + guint32 opt_sqn = pgm_txw_next_lead(transport->window); + guint packets = 0; + guint bytes_sent = 0; + guint data_bytes_sent = 0; + + pgm_mutex_lock (&transport->source_mutex); + + do { +/* retrieve packet storage from transmit window */ + int header_length = sizeof(struct pgm_header) + sizeof(struct pgm_data) + + sizeof(struct pgm_opt_length) + /* includes header */ + sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_fragment); + int tsdu_length = MIN(transport->max_tpdu - transport->iphdr_len - header_length, count - data_bytes_sent); + int tpdu_length = header_length + tsdu_length; + + struct pgm_sk_buff_t* skb = pgm_alloc_skb (tsdu_length); + pgm_skb_put (skb, tpdu_length); + + skb->pgm_header = (struct pgm_header*)skb->data; + memcpy (skb->pgm_header->pgm_gsi, &transport->tsi.gsi, sizeof(pgm_gsi_t)); + skb->pgm_header->pgm_sport = transport->tsi.sport; + skb->pgm_header->pgm_dport = transport->dport; + skb->pgm_header->pgm_type = PGM_ODATA; + skb->pgm_header->pgm_options = PGM_OPT_PRESENT; + skb->pgm_header->pgm_tsdu_length = g_htons (tsdu_length); + +/* ODATA */ + skb->pgm_data = (struct pgm_data*)(skb->pgm_header + 1); + skb->pgm_data->data_sqn = g_htonl (pgm_txw_next_lead(transport->window)); + skb->pgm_data->data_trail = g_htonl (pgm_txw_trail(transport->window)); + +/* OPT_LENGTH */ + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(skb->pgm_data + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = g_htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment) ); +/* OPT_FRAGMENT */ + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_FRAGMENT | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment); + skb->pgm_opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); + skb->pgm_opt_fragment->opt_reserved = 0; + skb->pgm_opt_fragment->opt_sqn = g_htonl (opt_sqn); + skb->pgm_opt_fragment->opt_frag_off = g_htonl (data_bytes_sent); + skb->pgm_opt_fragment->opt_frag_len = g_htonl (count); + +/* TODO: the assembly checksum & copy routine is faster than memcpy & pgm_cksum on >= opteron hardware */ + skb->pgm_header->pgm_checksum = 0; + + int pgm_header_len = (char*)(skb->pgm_opt_fragment + 1) - (char*)skb->pgm_header; + guint32 unfolded_header = pgm_csum_partial ((const void*)skb->pgm_header, pgm_header_len, 0); + guint32 unfolded_odata = pgm_csum_partial_copy ((const void*)(buf + data_bytes_sent), (void*)(skb->pgm_opt_fragment + 1), tsdu_length, 0); + skb->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, unfolded_odata, pgm_header_len)); + +/* add to transmit window */ + pgm_spinlock_lock (&transport->txw_spinlock); + pgm_txw_add (transport->window, skb); + pgm_spinlock_unlock (&transport->txw_spinlock); + +/* do not send send packet */ + if (packets != 1) + pgm_sendto (transport, + TRUE, + FALSE, + skb->data, + tpdu_length, + (struct sockaddr*)&transport->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&transport->send_gsr.gsr_group)); + +/* save unfolded odata for retransmissions */ + *(guint32*)&skb->cb = unfolded_odata; + + packets++; + bytes_sent += tpdu_length + transport->iphdr_len; + data_bytes_sent += tsdu_length; + + } while (data_bytes_sent < count); + + if (data_bytes_sent > 0 && bytes_written) + *bytes_written = data_bytes_sent; + +/* release txw lock here in order to allow spms to lock mutex */ + pgm_mutex_unlock (&transport->source_mutex); + pgm_reset_heartbeat_spm (transport); + return PGM_IO_STATUS_NORMAL; +} + +static +int +brokn_send ( + pgm_transport_t* transport, + const gchar* data, + gsize len, + gsize* bytes_written + ) +{ + if ( len <= ( transport->max_tpdu - ( sizeof(struct pgm_header) + + sizeof(struct pgm_data) ) ) ) + { + puts ("FAILED: cannot send brokn single TPDU length APDU"); + return PGM_IO_STATUS_ERROR; + } + + return brokn_send_apdu_unlocked (transport, data, len, bytes_written); +} + +static +void +session_send ( + char* name, + char* string, + gboolean is_brokn /* send broken apdu */ + ) +{ +/* check that session exists */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess == NULL) { + puts ("FAILED: session not found"); + return; + } + +/* send message */ + int status; + gsize stringlen = strlen(string) + 1; + int n_fds = 1; + struct pollfd fds[ n_fds ]; + struct timeval tv; + int timeout; +again: + if (is_brokn) + status = brokn_send (sess->transport, string, stringlen, NULL); + else + status = pgm_send (sess->transport, string, stringlen, NULL); + switch (status) { + case PGM_IO_STATUS_NORMAL: + puts ("READY"); + break; + case PGM_IO_STATUS_TIMER_PENDING: + pgm_transport_get_timer_pending (sess->transport, &tv); + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + pgm_transport_get_rate_remaining (sess->transport, &tv); +/* fall through */ + case PGM_IO_STATUS_WOULD_BLOCK: +block: + timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + memset (fds, 0, sizeof(fds)); + pgm_transport_poll_info (sess->transport, fds, &n_fds, POLLOUT); + poll (fds, n_fds, timeout /* ms */); + goto again; + default: + puts ("FAILED: pgm_send()"); + break; + } +} + +static +void +session_destroy ( + char* name + ) +{ +/* check that session exists */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess == NULL) { + puts ("FAILED: session not found"); + return; + } + +/* remove from hash table */ + g_hash_table_remove (g_sessions, name); + +/* close down receive side first to stop new data incoming */ + if (sess->recv_channel) { + puts ("closing receive channel."); + + GError *err = NULL; + g_io_channel_shutdown (sess->recv_channel, TRUE, &err); + + if (err) { + g_warning ("i/o shutdown error %i %s", err->code, err->message); + } + +/* TODO: flush GLib main loop with context specific to the recv channel */ + + sess->recv_channel = NULL; + } + + if (sess->is_transport_fake) + { + fake_pgm_transport_destroy (sess->transport, TRUE); + } + else + { + pgm_transport_destroy (sess->transport, TRUE); + } + sess->transport = NULL; + g_free (sess->name); + sess->name = NULL; + g_free (sess); + + puts ("READY"); +} + +static +void +net_send_data ( + char* name, + guint8 pgm_type, /* PGM_ODATA or PGM_RDATA */ + guint32 data_sqn, + guint32 txw_trail, + char* string + ) +{ +/* check that session exists */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess == NULL) { + puts ("FAILED: session not found"); + return; + } + + pgm_transport_t* transport = sess->transport; + +/* payload is string including terminating null. */ + int count = strlen(string) + 1; + +/* send */ + int retval = 0; + int tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_data) + count; + + gchar buf[ tpdu_length ]; + + struct pgm_header *header = (struct pgm_header*)buf; + struct pgm_data *data = (struct pgm_data*)(header + 1); + memcpy (header->pgm_gsi, &transport->tsi.gsi, sizeof(pgm_gsi_t)); + header->pgm_sport = transport->tsi.sport; + header->pgm_dport = transport->dport; + header->pgm_type = pgm_type; + header->pgm_options = 0; + header->pgm_tsdu_length = g_htons (count); + +/* O/RDATA */ + data->data_sqn = g_htonl (data_sqn); + data->data_trail = g_htonl (txw_trail); + + memcpy (data + 1, string, count); + + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial ((char*)header, tpdu_length, 0)); + + pgm_mutex_lock (&transport->send_mutex); + retval = sendto (transport->send_sock, + header, + tpdu_length, + 0, /* not expecting a reply */ + (struct sockaddr*)&transport->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&transport->send_gsr.gsr_group)); + pgm_mutex_unlock (&transport->send_mutex); + + puts ("READY"); +} + +/* differs to net_send_data in that the string parameters contains every payload + * for the transmission group. this is required to calculate the correct parity + * as the fake transport does not own a transmission window. + * + * all payloads must be the same length unless variable TSDU support is enabled. + */ +static +void +net_send_parity ( + char* name, + guint8 pgm_type, /* PGM_ODATA or PGM_RDATA */ + guint32 data_sqn, + guint32 txw_trail, + char* string + ) +{ +/* check that session exists */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess == NULL) { + puts ("FAILED: session not found"); + return; + } + + pgm_transport_t* transport = sess->transport; + +/* split string into individual payloads */ + guint16 parity_length = 0; + gchar** src; + src = g_strsplit (string, " ", transport->rs_k); + +/* payload is string including terminating null. */ + parity_length = strlen(*src) + 1; + +/* check length of payload array */ + gboolean is_var_pktlen = FALSE; + guint i; + for (i = 0; src[i]; i++) + { + guint tsdu_length = strlen(src[i]) + 1; + if (tsdu_length != parity_length) { + is_var_pktlen = TRUE; + + if (tsdu_length > parity_length) + parity_length = tsdu_length; + } + } + + if ( i != transport->rs_k ) { + printf ("FAILED: payload array length %u, whilst rs_k is %u.\n", i, transport->rs_k); + return; + } + +/* add padding and append TSDU lengths */ + if (is_var_pktlen) + { + for (i = 0; src[i]; i++) + { + guint tsdu_length = strlen(src[i]) + 1; + gchar* new_string = g_new0 (gchar, parity_length + 2); + strncpy (new_string, src[i], parity_length); + *(guint16*)(new_string + parity_length) = tsdu_length; + g_free (src[i]); + src[i] = new_string; + } + parity_length += 2; + } + +/* calculate FEC block offset */ + guint32 tg_sqn_mask = 0xffffffff << transport->tg_sqn_shift; + guint rs_h = data_sqn & ~tg_sqn_mask; + +/* send */ + int retval = 0; + int tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_data) + parity_length; + + gchar buf[ tpdu_length ]; + + struct pgm_header *header = (struct pgm_header*)buf; + struct pgm_data *data = (struct pgm_data*)(header + 1); + memcpy (header->pgm_gsi, &transport->tsi.gsi, sizeof(pgm_gsi_t)); + header->pgm_sport = transport->tsi.sport; + header->pgm_dport = transport->dport; + header->pgm_type = pgm_type; + header->pgm_options = is_var_pktlen ? (PGM_OPT_PARITY | PGM_OPT_VAR_PKTLEN) : PGM_OPT_PARITY; + header->pgm_tsdu_length = g_htons (parity_length); + +/* O/RDATA */ + data->data_sqn = g_htonl (data_sqn); + data->data_trail = g_htonl (txw_trail); + + memset (data + 1, 0, parity_length); + pgm_rs_t rs; + pgm_rs_create (&rs, transport->rs_n, transport->rs_k); + pgm_rs_encode (&rs, (const pgm_gf8_t**)src, transport->rs_k + rs_h, (pgm_gf8_t*)(data + 1), parity_length); + pgm_rs_destroy (&rs); + + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial ((char*)header, tpdu_length, 0)); + + pgm_mutex_lock (&transport->send_mutex); + retval = sendto (transport->send_sock, + header, + tpdu_length, + 0, /* not expecting a reply */ + (struct sockaddr*)&transport->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&transport->send_gsr.gsr_group)); + pgm_mutex_unlock (&transport->send_mutex); + + g_strfreev (src); + src = NULL; + + puts ("READY"); +} + +static +void +net_send_spm ( + char* name, + guint32 spm_sqn, + guint32 txw_trail, + guint32 txw_lead, + gboolean proactive_parity, + gboolean ondemand_parity, + guint k + ) +{ +/* check that session exists */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess == NULL) { + puts ("FAILED: session not found"); + return; + } + + pgm_transport_t* transport = sess->transport; + +/* send */ + int retval = 0; + int tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_spm); + + if (proactive_parity || ondemand_parity) { + tpdu_length += sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_parity_prm); + } + + gchar buf[ tpdu_length ]; + + struct pgm_header *header = (struct pgm_header*)buf; + struct pgm_spm *spm = (struct pgm_spm*)(header + 1); + memcpy (header->pgm_gsi, &transport->tsi.gsi, sizeof(pgm_gsi_t)); + header->pgm_sport = transport->tsi.sport; + header->pgm_dport = transport->dport; + header->pgm_type = PGM_SPM; + header->pgm_options = (proactive_parity || ondemand_parity) ? (PGM_OPT_PRESENT | PGM_OPT_NETWORK) : 0; + header->pgm_tsdu_length = 0; + +/* SPM */ + spm->spm_sqn = g_htonl (spm_sqn); + spm->spm_trail = g_htonl (txw_trail); + spm->spm_lead = g_htonl (txw_lead); + pgm_sockaddr_to_nla ((struct sockaddr*)&transport->send_addr, (char*)&spm->spm_nla_afi); + + if (proactive_parity || ondemand_parity) { + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(spm + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = g_htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_parity_prm) ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_PARITY_PRM | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_parity_prm); + struct pgm_opt_parity_prm* opt_parity_prm = (struct pgm_opt_parity_prm*)(opt_header + 1); + opt_parity_prm->opt_reserved = (proactive_parity ? PGM_PARITY_PRM_PRO : 0) | + (ondemand_parity ? PGM_PARITY_PRM_OND : 0); + opt_parity_prm->parity_prm_tgs = g_htonl (k); + } + + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial ((char*)header, tpdu_length, 0)); + + retval = sendto (transport->send_sock, + header, + tpdu_length, + 0, /* not expecting a reply */ + (struct sockaddr*)&transport->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&transport->send_gsr.gsr_group)); + puts ("READY"); +} + +static +void +net_send_spmr ( + char* name, + pgm_tsi_t* tsi + ) +{ +/* check that session exists */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess == NULL) { + puts ("FAILED: session not found"); + return; + } + + pgm_transport_t* transport = sess->transport; + +/* check that the peer exists */ + pgm_peer_t* peer = pgm_hashtable_lookup (transport->peers_hashtable, tsi); + struct sockaddr_storage peer_nla; + pgm_gsi_t* peer_gsi; + guint16 peer_sport; + + if (peer == NULL) { +/* ourself */ + if (pgm_tsi_equal (tsi, &transport->tsi)) + { + peer_gsi = &transport->tsi.gsi; + peer_sport = transport->tsi.sport; + } + else + { + printf ("FAILED: peer \"%s\" not found\n", pgm_tsi_print (tsi)); + return; + } + } + else + { + memcpy (&peer_nla, &peer->local_nla, sizeof(struct sockaddr_storage)); + peer_gsi = &peer->tsi.gsi; + peer_sport = peer->tsi.sport; + } + +/* send */ + int retval = 0; + int tpdu_length = sizeof(struct pgm_header); + gchar buf[ tpdu_length ]; + + struct pgm_header *header = (struct pgm_header*)buf; + memcpy (header->pgm_gsi, peer_gsi, sizeof(pgm_gsi_t)); + header->pgm_sport = transport->dport; + header->pgm_dport = peer_sport; + header->pgm_type = PGM_SPMR; + header->pgm_options = 0; + header->pgm_tsdu_length = 0; + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial ((char*)header, tpdu_length, 0)); + + pgm_mutex_lock (&transport->send_mutex); +/* TTL 1 */ + pgm_sockaddr_multicast_hops (transport->send_sock, transport->send_gsr.gsr_group.ss_family, 1); + retval = sendto (transport->send_sock, + header, + tpdu_length, + 0, /* not expecting a reply */ + (struct sockaddr*)&transport->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&transport->send_gsr.gsr_group)); +/* default TTL */ + pgm_sockaddr_multicast_hops (transport->send_sock, transport->send_gsr.gsr_group.ss_family, transport->hops); + + if (!pgm_tsi_equal (tsi, &transport->tsi)) + { + retval = sendto (transport->send_sock, + header, + tpdu_length, + 0, /* not expecting a reply */ + (struct sockaddr*)&peer_nla, + pgm_sockaddr_len((struct sockaddr*)&peer_nla)); + } + + pgm_mutex_unlock (&transport->send_mutex); + + puts ("READY"); +} + +/* Send a NAK on a valid transport. A fake transport would need to specify the senders NLA, + * we use the peer list to bypass extracting it from the monitor output. + */ + +static +void +net_send_ncf ( + char* name, + pgm_tsi_t* tsi, + struct pgm_sqn_list_t* sqn_list /* list of sequence numbers */ + ) +{ +/* check that session exists */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess == NULL) { + puts ("FAILED: session not found"); + return; + } + +/* check that the peer exists */ + pgm_transport_t* transport = sess->transport; + pgm_peer_t* peer = pgm_hashtable_lookup (transport->peers_hashtable, tsi); + if (peer == NULL) { + printf ("FAILED: peer \"%s\" not found\n", pgm_tsi_print (tsi)); + return; + } + +/* check for valid nla */ + if (((struct sockaddr*)&peer->nla)->sa_family == 0 ) { + puts ("FAILED: peer NLA unknown, cannot send NCF."); + return; + } + +/* send */ + int retval = 0; + int tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_nak); + + if (sqn_list->len > 1) { + tpdu_length += sizeof(struct pgm_opt_length) + /* includes header */ + sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(guint32) ); + } + + gchar buf[ tpdu_length ]; + + struct pgm_header *header = (struct pgm_header*)buf; + struct pgm_nak *ncf = (struct pgm_nak*)(header + 1); + memcpy (header->pgm_gsi, &transport->tsi.gsi, sizeof(pgm_gsi_t)); + + struct sockaddr_storage peer_nla; + memcpy (&peer_nla, &peer->nla, sizeof(struct sockaddr_storage)); + +/* dport & sport swap over for a nak */ + header->pgm_sport = transport->tsi.sport; + header->pgm_dport = transport->dport; + header->pgm_type = PGM_NCF; + header->pgm_options = (sqn_list->len > 1) ? (PGM_OPT_PRESENT | PGM_OPT_NETWORK) : 0; + header->pgm_tsdu_length = 0; + +/* NCF */ + ncf->nak_sqn = g_htonl (sqn_list->sqn[0]); + +/* source nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&peer_nla, (char*)&ncf->nak_src_nla_afi); + +/* group nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&transport->recv_gsr[0].gsr_group, (char*)&ncf->nak_grp_nla_afi); + +/* OPT_NAK_LIST */ + if (sqn_list->len > 1) + { + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(ncf + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = g_htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(guint32) ) ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_NAK_LIST | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(guint32) ); + struct pgm_opt_nak_list* opt_nak_list = (struct pgm_opt_nak_list*)(opt_header + 1); + opt_nak_list->opt_reserved = 0; + for (guint i = 1; i < sqn_list->len; i++) { + opt_nak_list->opt_sqn[i-1] = g_htonl (sqn_list->sqn[i]); + } + } + + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial ((char*)header, tpdu_length, 0)); + + retval = sendto (transport->send_with_router_alert_sock, + header, + tpdu_length, + 0, /* not expecting a reply */ + (struct sockaddr*)&transport->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&transport->send_gsr.gsr_group)); + + puts ("READY"); +} + +static +void +net_send_nak ( + char* name, + pgm_tsi_t* tsi, + struct pgm_sqn_list_t* sqn_list, /* list of sequence numbers */ + gboolean is_parity /* TRUE = parity, FALSE = selective */ + ) +{ +/* check that session exists */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, name); + if (sess == NULL) { + puts ("FAILED: session not found"); + return; + } + +/* check that the peer exists */ + pgm_transport_t* transport = sess->transport; + pgm_peer_t* peer = pgm_hashtable_lookup (transport->peers_hashtable, tsi); + if (peer == NULL) { + printf ("FAILED: peer \"%s\" not found\n", pgm_tsi_print(tsi)); + return; + } + +/* send */ + int retval = 0; + int tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_nak); + + if (sqn_list->len > 1) { + tpdu_length += sizeof(struct pgm_opt_length) + /* includes header */ + sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(guint32) ); + } + + gchar buf[ tpdu_length ]; + + struct pgm_header *header = (struct pgm_header*)buf; + struct pgm_nak *nak = (struct pgm_nak*)(header + 1); + memcpy (header->pgm_gsi, &peer->tsi.gsi, sizeof(pgm_gsi_t)); + + guint16 peer_sport = peer->tsi.sport; + struct sockaddr_storage peer_nla; + memcpy (&peer_nla, &peer->nla, sizeof(struct sockaddr_storage)); + +/* dport & sport swap over for a nak */ + header->pgm_sport = transport->dport; + header->pgm_dport = peer_sport; + header->pgm_type = PGM_NAK; + if (is_parity) { + header->pgm_options = (sqn_list->len > 1) ? (PGM_OPT_PRESENT | PGM_OPT_NETWORK | PGM_OPT_PARITY) + : PGM_OPT_PARITY; + } else { + header->pgm_options = (sqn_list->len > 1) ? (PGM_OPT_PRESENT | PGM_OPT_NETWORK) : 0; + } + header->pgm_tsdu_length = 0; + +/* NAK */ + nak->nak_sqn = g_htonl (sqn_list->sqn[0]); + +/* source nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&peer_nla, (char*)&nak->nak_src_nla_afi); + +/* group nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&transport->recv_gsr[0].gsr_group, (char*)&nak->nak_grp_nla_afi); + +/* OPT_NAK_LIST */ + if (sqn_list->len > 1) + { + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(nak + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = g_htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(guint32) ) ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_NAK_LIST | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(guint32) ); + struct pgm_opt_nak_list* opt_nak_list = (struct pgm_opt_nak_list*)(opt_header + 1); + opt_nak_list->opt_reserved = 0; + for (guint i = 1; i < sqn_list->len; i++) { + opt_nak_list->opt_sqn[i-1] = g_htonl (sqn_list->sqn[i]); + } + } + + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial ((char*)header, tpdu_length, 0)); + + retval = sendto (transport->send_with_router_alert_sock, + header, + tpdu_length, + 0, /* not expecting a reply */ + (struct sockaddr*)&peer_nla, + pgm_sockaddr_len((struct sockaddr*)&peer_nla)); + + puts ("READY"); +} + +static +int +on_data ( + gpointer data, + G_GNUC_UNUSED guint len, + G_GNUC_UNUSED gpointer user_data + ) +{ + printf ("DATA: %s\n", (char*)data); + fflush (stdout); + + return 0; +} + +/* process input commands from stdin/fd + */ + +static +gboolean +on_stdin_data ( + GIOChannel* source, + G_GNUC_UNUSED GIOCondition condition, + G_GNUC_UNUSED gpointer data + ) +{ + gchar* str = NULL; + gsize len = 0; + gsize term = 0; + GError* err = NULL; + + g_io_channel_read_line (source, &str, &len, &term, &err); + if (len > 0) { + if (term) str[term] = 0; + +/* quit */ + if (strcmp(str, "quit") == 0) + { + g_main_loop_quit(g_loop); + goto out; + } + + regex_t preg; + regmatch_t pmatch[10]; + const char *re; + +/* endpoint simulator specific: */ + +/* send odata or rdata */ + re = "^net[[:space:]]+send[[:space:]]+([or])data[[:space:]]+" + "([[:alnum:]]+)[[:space:]]+" /* transport */ + "([0-9]+)[[:space:]]+" /* sequence number */ + "([0-9]+)[[:space:]]+" /* txw_trail */ + "([[:alnum:]]+)$"; /* payload */ + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + guint8 pgm_type = *(str + pmatch[1].rm_so) == 'o' ? PGM_ODATA : PGM_RDATA; + + char *name = g_memdup (str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so + 1 ); + name[ pmatch[2].rm_eo - pmatch[2].rm_so ] = 0; + + char* p = str + pmatch[3].rm_so; + guint32 data_sqn = strtoul (p, &p, 10); + + p = str + pmatch[4].rm_so; + guint txw_trail = strtoul (p, &p, 10); + + char *string = g_memdup (str + pmatch[5].rm_so, pmatch[5].rm_eo - pmatch[5].rm_so + 1 ); + string[ pmatch[5].rm_eo - pmatch[5].rm_so ] = 0; + + net_send_data (name, pgm_type, data_sqn, txw_trail, string); + + g_free (name); + g_free (string); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* send parity odata or rdata */ + re = "^net[[:space:]]+send[[:space:]]+parity[[:space:]]+([or])data[[:space:]]+" + "([[:alnum:]]+)[[:space:]]+" /* transport */ + "([0-9]+)[[:space:]]+" /* sequence number */ + "([0-9]+)[[:space:]]+" /* txw_trail */ + "([a-z0-9 ]+)$"; /* payloads */ + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + guint8 pgm_type = *(str + pmatch[1].rm_so) == 'o' ? PGM_ODATA : PGM_RDATA; + + char *name = g_memdup (str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so + 1 ); + name[ pmatch[2].rm_eo - pmatch[2].rm_so ] = 0; + + char* p = str + pmatch[3].rm_so; + guint32 data_sqn = strtoul (p, &p, 10); + + p = str + pmatch[4].rm_so; + guint txw_trail = strtoul (p, &p, 10); + +/* ideally confirm number of payloads matches sess->transport::rs_k ... */ + char *string = g_memdup (str + pmatch[5].rm_so, pmatch[5].rm_eo - pmatch[5].rm_so + 1 ); + string[ pmatch[5].rm_eo - pmatch[5].rm_so ] = 0; + + net_send_parity (name, pgm_type, data_sqn, txw_trail, string); + + g_free (name); + g_free (string); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* send spm */ + re = "^net[[:space:]]+send[[:space:]]+spm[[:space:]]+" + "([[:alnum:]]+)[[:space:]]+" /* transport */ + "([0-9]+)[[:space:]]+" /* spm sequence number */ + "([0-9]+)[[:space:]]+" /* txw_trail */ + "([0-9]+)" /* txw_lead */ + "([[:space:]]+pro-active)?" /* pro-active parity */ + "([[:space:]]+on-demand)?" /* on-demand parity */ + "([[:space:]]+[0-9]+)?$"; /* transmission group size */ + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + char* p = str + pmatch[2].rm_so; + guint32 spm_sqn = strtoul (p, &p, 10); + + p = str + pmatch[3].rm_so; + guint txw_trail = strtoul (p, &p, 10); + + p = str + pmatch[4].rm_so; + guint txw_lead = strtoul (p, &p, 10); + + gboolean proactive_parity = pmatch[5].rm_eo > pmatch[5].rm_so; + gboolean ondemand_parity = pmatch[6].rm_eo > pmatch[6].rm_so; + + p = str + pmatch[7].rm_so; + guint k = (pmatch[7].rm_eo > pmatch[7].rm_so) ? strtoul (p, &p, 10) : 0; + + net_send_spm (name, spm_sqn, txw_trail, txw_lead, proactive_parity, ondemand_parity, k); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* send spmr */ + re = "^net[[:space:]]+send[[:space:]]+spmr[[:space:]]+" + "([[:alnum:]]+)[[:space:]]+" /* transport */ + "([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)$"; /* TSI */ + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + pgm_tsi_t tsi; + char *p = str + pmatch[2].rm_so; + tsi.gsi.identifier[0] = strtol (p, &p, 10); + ++p; + tsi.gsi.identifier[1] = strtol (p, &p, 10); + ++p; + tsi.gsi.identifier[2] = strtol (p, &p, 10); + ++p; + tsi.gsi.identifier[3] = strtol (p, &p, 10); + ++p; + tsi.gsi.identifier[4] = strtol (p, &p, 10); + ++p; + tsi.gsi.identifier[5] = strtol (p, &p, 10); + ++p; + tsi.sport = g_htons ( strtol (p, NULL, 10) ); + + net_send_spmr (name, &tsi); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* send nak/ncf */ + re = "^net[[:space:]]+send[[:space:]](parity[[:space:]])?n(ak|cf)[[:space:]]+" + "([[:alnum:]]+)[[:space:]]+" /* transport */ + "([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)[[:space:]]+" /* TSI */ + "([0-9,]+)$"; /* sequence number or list */ + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[3].rm_so, pmatch[3].rm_eo - pmatch[3].rm_so + 1 ); + name[ pmatch[3].rm_eo - pmatch[3].rm_so ] = 0; + + pgm_tsi_t tsi; + char *p = str + pmatch[4].rm_so; + tsi.gsi.identifier[0] = strtol (p, &p, 10); + ++p; + tsi.gsi.identifier[1] = strtol (p, &p, 10); + ++p; + tsi.gsi.identifier[2] = strtol (p, &p, 10); + ++p; + tsi.gsi.identifier[3] = strtol (p, &p, 10); + ++p; + tsi.gsi.identifier[4] = strtol (p, &p, 10); + ++p; + tsi.gsi.identifier[5] = strtol (p, &p, 10); + ++p; + tsi.sport = g_htons ( strtol (p, NULL, 10) ); + +/* parse list of sequence numbers */ + struct pgm_sqn_list_t sqn_list; + sqn_list.len = 0; + { + char* saveptr = NULL; + for (p = str + pmatch[5].rm_so; ; p = NULL) { + char* token = strtok_r (p, ",", &saveptr); + if (!token) break; + sqn_list.sqn[sqn_list.len++] = strtoul (token, NULL, 10); + } + } + + if ( *(str + pmatch[2].rm_so) == 'a' ) + { + net_send_nak (name, &tsi, &sqn_list, (pmatch[1].rm_eo > pmatch[1].rm_so)); + } + else + { + net_send_ncf (name, &tsi, &sqn_list); + } + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/** same as test application: **/ + +/* create transport */ + re = "^create[[:space:]]+(fake[[:space:]]+)?([[:alnum:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so + 1 ); + name[ pmatch[2].rm_eo - pmatch[2].rm_so ] = 0; + + session_create (name, (pmatch[1].rm_eo > pmatch[1].rm_so)); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* enable Reed-Solomon Forward Error Correction */ + re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+FEC[[:space:]]+RS[[:space:]]*\\([[:space:]]*([0-9]+)[[:space:]]*,[[:space:]]*([0-9]+)[[:space:]]*\\)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + char *p = str + pmatch[2].rm_so; + *(str + pmatch[2].rm_eo) = 0; + guint n = strtol (p, &p, 10); + p = str + pmatch[3].rm_so; + *(str + pmatch[3].rm_eo) = 0; + guint k = strtol (p, &p, 10); + session_set_fec (name, n, k); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* bind transport */ + re = "^bind[[:space:]]+([[:alnum:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + session_bind (name); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* send packet */ + re = "^send[[:space:]]+([[:alnum:]]+)[[:space:]]+([[:alnum:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + char *string = g_memdup (str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so + 1 ); + string[ pmatch[2].rm_eo - pmatch[2].rm_so ] = 0; + + session_send (name, string, FALSE); + + g_free (name); + g_free (string); + regfree (&preg); + goto out; + } + regfree (&preg); + + re = "^send[[:space:]]+(brokn[[:space:]]+)?([[:alnum:]]+)[[:space:]]+([[:alnum:]]+)[[:space:]]+x[[:space:]]([0-9]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so + 1 ); + name[ pmatch[2].rm_eo - pmatch[2].rm_so ] = 0; + + char* p = str + pmatch[4].rm_so; + int factor = strtol (p, &p, 10); + int src_len = pmatch[3].rm_eo - pmatch[3].rm_so; + char *string = g_malloc ( (factor * src_len) + 1 ); + for (int i = 0; i < factor; i++) + { + memcpy (string + (i * src_len), str + pmatch[3].rm_so, src_len); + } + string[ factor * src_len ] = 0; + + session_send (name, string, (pmatch[1].rm_eo > pmatch[1].rm_so)); + + g_free (name); + g_free (string); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* destroy transport */ + re = "^destroy[[:space:]]+([[:alnum:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + session_destroy (name); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* set PGM network */ + re = "^set[[:space:]]+network[[:space:]]+([[:print:]]*;[[:print:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *pgm_network = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + pgm_network[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + g_network = pgm_network; + puts ("READY"); + + regfree (&preg); + goto out; + } + regfree (&preg); + + printf ("unknown command: %s\n", str); + } + +out: + fflush (stdout); + g_free (str); + return TRUE; +} + +/* idle log notification + */ + +static +gboolean +on_mark ( + G_GNUC_UNUSED gpointer data + ) +{ + g_message ("-- MARK --"); + return TRUE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/spm.pl b/3rdparty/openpgm-svn-r1085/pgm/test/spm.pl new file mode 100755 index 0000000..c92b8fc --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/spm.pl @@ -0,0 +1,42 @@ +#!/usr/bin/perl +# spm.pl +# 5.1.4. Ambient SPMs + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$app->connect; + +sub close_ssh { + $mon = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +print "app: ready.\n"; + +print "mon: wait for spm ...\n"; +$mon->wait_for_spm; +print "mon: received spm.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/spm_jump.pl b/3rdparty/openpgm-svn-r1085/pgm/test/spm_jump.pl new file mode 100755 index 0000000..9b89720 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/spm_jump.pl @@ -0,0 +1,60 @@ +#!/usr/bin/perl +# spm_jump.pl +# 6.2. Source Path Messages + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; +$sim->say ("net send spm ao 3200 90001 90000"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,005 at spm_sqn 20.\n"; +$sim->say ("net send spm ao 20 90001 90005"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/spm_jump2.pl b/3rdparty/openpgm-svn-r1085/pgm/test/spm_jump2.pl new file mode 100755 index 0000000..4ad0562 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/spm_jump2.pl @@ -0,0 +1,59 @@ +#!/usr/bin/perl +# spm_jump2.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; +$sim->say ("net send spm ao 3200 90001 90000"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,001 at spm_sqn 3201.\n"; +$sim->say ("net send spm ao 3201 90001 90001"); + +print "sim: waiting for valid NAK.\n"; +$sim->wait_for_nak; +print "sim: NAK received.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/spm_reception.pl b/3rdparty/openpgm-svn-r1085/pgm/test/spm_reception.pl new file mode 100755 index 0000000..63ea43f --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/spm_reception.pl @@ -0,0 +1,58 @@ +#!/usr/bin/perl +# spm_reception.pl +# 6.1. Data Reception + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); + +print "sim: publish SPM txw_trail 90,000.\n"; +$sim->say ("net send spm ao 1 90001 90000"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90000 ichigo"); +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: received data [$data].\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/spmr.pl b/3rdparty/openpgm-svn-r1085/pgm/test/spmr.pl new file mode 100755 index 0000000..a22f14d --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/spmr.pl @@ -0,0 +1,73 @@ +#!/usr/bin/perl +# spmr.pl +# 13.3.1. SPM Requests + +use strict; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); +print "sim: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +## capture GSI of test spp +$app->say ("send ao nashi"); +print "mon: wait for odata ...\n"; +my $odata = $mon->wait_for_odata; +print "mon: odata received.\n"; +my $t0 = [gettimeofday]; +my $elapsed; + +$mon->disconnect (1); + +## spm hearbeats are going to clear out the data, lets wait for some quiet +print "sim: wait for SPM interval > 5 seconds ...\n"; +do { + $sim->wait_for_spm; + $elapsed = tv_interval ( $t0, [gettimeofday] ); + print "sim: received SPM after $elapsed seconds.\n"; +} while ($elapsed < 5); + +print "sim: request SPM via SPMR.\n"; +$sim->say ("net send spmr ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort}"); +$t0 = [gettimeofday]; + +print "sim: wait for SPM ...\n"; +$sim->wait_for_spm; +$elapsed = tv_interval ( $t0, [gettimeofday] ); +print "sim: SPM received after $elapsed seconds.\n"; +die "SPM interval too large, indicates heartbeat not SPMR induced.\n" unless ($elapsed < 5.0); + +print "test completed successfully.\n"; + +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/spmr_after_spm.pl b/3rdparty/openpgm-svn-r1085/pgm/test/spmr_after_spm.pl new file mode 100755 index 0000000..a1ca5ee --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/spmr_after_spm.pl @@ -0,0 +1,78 @@ +#!/usr/bin/perl +# spmr.pl +# 13.3.1. SPM Requests + +use strict; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +## capture GSI of test spp +$app->say ("send ao nashi"); +print "mon: wait for odata ...\n"; +my $odata = $mon->wait_for_odata; +print "mon: odata received.\n"; +my $t0 = [gettimeofday]; +my $elapsed; + +## spm hearbeats are going to clear out the data, lets wait for some quiet +print "mon: wait for SPM interval > 5 seconds ...\n"; +do { + $mon->wait_for_spm; + $elapsed = tv_interval ( $t0, [gettimeofday] ); + print "mon: received SPM after $elapsed seconds.\n"; +} while ($elapsed < 5); + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); +print "sim: ready.\n"; + +## app needs to send packet for sim to learn of local NLA +$app->say ("send ao budo"); +print "sim: wait for odata ...\n"; +$odata = $sim->wait_for_odata; +print "sim: odata received.\n"; + +print "sim: request SPM via SPMR.\n"; +$sim->say ("net send spmr ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort}"); +$t0 = [gettimeofday]; + +print "sim: wait for SPM ...\n"; +$sim->wait_for_spm; +$elapsed = tv_interval ( $t0, [gettimeofday] ); +print "sim: SPM received after $elapsed seconds.\n"; +die "SPM interval too large, indicates heartbeat not SPMR induced.\n" unless ($elapsed < 5.0); + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/spmr_from_odata.pl b/3rdparty/openpgm-svn-r1085/pgm/test/spmr_from_odata.pl new file mode 100755 index 0000000..0563b3f --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/spmr_from_odata.pl @@ -0,0 +1,53 @@ +#!/usr/bin/perl +# spmr_from_odata.pl +# 13.3.1. SPM Requests + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90001 ringo"); + +my $data = $app->wait_for_data; +print "app: received data [$data].\n"; + +print "mon: wait for SPMR ...\n"; +$mon->wait_for_spmr; +print "mon: received SPMR.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/spmr_suppression.pl b/3rdparty/openpgm-svn-r1085/pgm/test/spmr_suppression.pl new file mode 100755 index 0000000..a1df615 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/spmr_suppression.pl @@ -0,0 +1,58 @@ +#!/usr/bin/perl +# spmr_suppression.pl +# 13.3.1. SPM Requests + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); +print "sim: publish ODATA sqn 90,001 to monitor for GSI.\n"; +$sim->say ("net send odata ao 90001 90001 ringo"); + +## capture GSI of test sim (not app!) +my $odata = $mon->wait_for_odata; +$mon->say ("filter $config{app}{ip}"); + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +print "sim: re-publish ODATA sqn 90,001 to app.\n"; +$sim->say ("net send odata ao 90001 90001 ringo"); +$sim->say ("net send spmr ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort}"); + +my $data = $app->wait_for_data; +print "app: received data [$data].\n"; + +print "mon: wait for erroneous SPMR ...\n"; +$mon->die_on_spmr({ 'timeout' => 2 }); +print "mon: no SPMR received.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/sudoers.example b/3rdparty/openpgm-svn-r1085/pgm/test/sudoers.example new file mode 100644 index 0000000..9212139 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/sudoers.example @@ -0,0 +1,26 @@ +# /etc/sudoers +# +# This file MUST be edited with the 'visudo' command as root. +# +# See the man page for details on how to write a sudoers file. +# Host alias specification + +# User alias specification +User_Alias PGM_USER = steve-o + +# Cmnd alias specification +Cmnd_Alias PGM_CONFORMANCE = /miru/projects/openpgm/pgm/ref/debug/test/* + +# Defaults + +Defaults !lecture,tty_tickets,!fqdn + +# User privilege specification +root ALL=(ALL) ALL + +# Members of the admin group may gain root privileges +%admin ALL=(ALL) ALL + + +# PGM testing +PGM_USER ALL = NOPASSWD: PGM_CONFORMANCE diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/test.conf.pl b/3rdparty/openpgm-svn-r1085/pgm/test/test.conf.pl new file mode 100644 index 0000000..428f49e --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/test/test.conf.pl @@ -0,0 +1,27 @@ +# test.conf.pl +use vars qw ( %config ); + +%config = ( + msapp => { + host => 'momo', + ip => '10.6.28.34', + }, + app => { +# host => 'ayaka', ip => '10.6.28.31', +# cmd => '/miru/projects/openpgm/pgm/ref/release-Linux-x86_64/test/app', +# network => 'eth0;239.192.0.1' + host => 'ryoko', ip => '10.6.28.36', + cmd => 'LD_LIBRARY_PATH=/opt/glib-sunstudio/lib:$LD_LIBRARY_PATH /miru/projects/openpgm/pgm/ref/release-SunOS-sun4u-sunstudio/test/app', + network => 'eri0;239.192.0.1' + }, + mon => { + host => 'sora', + cmd => '/miru/projects/openpgm/pgm/ref/release-Linux-x86_64/test/monitor', + network => 'eth0;239.192.0.1' + }, + sim => { + host => 'kiku', + cmd => '/miru/projects/openpgm/pgm/ref/release-Linux-x86_64/test/sim', + network => 'eth0;239.192.0.1' + }, +); diff --git a/3rdparty/openpgm-svn-r1085/pgm/thread.c b/3rdparty/openpgm-svn-r1085/pgm/thread.c new file mode 100644 index 0000000..ad68ca3 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/thread.c @@ -0,0 +1,457 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * mutexes and locks. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +//#define THREAD_DEBUG + + +/* Globals */ + +#if defined(_WIN32) && !defined(CONFIG_HAVE_WIN_COND) +static DWORD cond_event_tls = TLS_OUT_OF_INDEXES; +#endif + +static volatile uint32_t thread_ref_count = 0; + + +#ifndef _WIN32 +# define posix_check_err(err, name) \ + do { \ + const int save_error = (err); \ + if (PGM_UNLIKELY(save_error)) { \ + pgm_error ("file %s: line %d (%s): error '%s' during '%s'", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, \ + strerror (save_error), name); \ + } \ + } while (0) +# define posix_check_cmd(cmd) posix_check_err ((cmd), #cmd) +#else +# define win32_check_err(err, name) \ + do { \ + const bool save_error = (err); \ + if (PGM_UNLIKELY(!save_error)) { \ + pgm_error ("file %s: line %d (%s): error '%s' during '%s'", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, \ + pgm_wsastrerror (GetLastError ()), name); \ + } \ + } while (0) +# define win32_check_cmd(cmd) win32_check_err ((cmd), #cmd) +#endif /* !_WIN32 */ + + +/* only needed for Win32 pre-Vista read-write locks + */ +void +pgm_thread_init (void) +{ + if (pgm_atomic_exchange_and_add32 (&thread_ref_count, 1) > 0) + return; + +#if defined(_WIN32) && !defined(CONFIG_HAVE_WIN_COND) + win32_check_cmd (TLS_OUT_OF_INDEXES != (cond_event_tls = TlsAlloc ())); +#endif +} + +void +pgm_thread_shutdown (void) +{ + pgm_return_if_fail (pgm_atomic_read32 (&thread_ref_count) > 0); + + if (pgm_atomic_exchange_and_add32 (&thread_ref_count, (uint32_t)-1) != 1) + return; + +#if defined(_WIN32) && !defined(CONFIG_HAVE_WIN_COND) + TlsFree (cond_event_tls); +#endif +} + +void +pgm_mutex_init ( + pgm_mutex_t* mutex + ) +{ + pgm_assert (NULL != mutex); +#ifndef _WIN32 + posix_check_cmd (pthread_mutex_init (&mutex->pthread_mutex, NULL)); +#else + HANDLE handle; + win32_check_cmd (handle = CreateMutex (NULL, FALSE, NULL)); + mutex->win32_mutex = handle; +#endif /* !_WIN32 */ +} + +bool +pgm_mutex_trylock ( + pgm_mutex_t* mutex + ) +{ + pgm_assert (NULL != mutex); +#ifndef _WIN32 + const int result = pthread_mutex_trylock (&mutex->pthread_mutex); + if (EBUSY == result) + return FALSE; + posix_check_err (result, "pthread_mutex_trylock"); + return TRUE; +#else + DWORD result; + win32_check_cmd (WAIT_FAILED != (result = WaitForSingleObject (mutex->win32_mutex, 0))); + return WAIT_TIMEOUT != result; +#endif /* !_WIN32 */ +} + +void +pgm_mutex_free ( + pgm_mutex_t* mutex + ) +{ + pgm_assert (NULL != mutex); +#ifndef _WIN32 + posix_check_cmd (pthread_mutex_destroy (&mutex->pthread_mutex)); +#else + win32_check_cmd (CloseHandle (mutex->win32_mutex)); +#endif /* !_WIN32 */ +} + +void +pgm_spinlock_init ( + pgm_spinlock_t* spinlock + ) +{ + pgm_assert (NULL != spinlock); +#ifndef _WIN32 + posix_check_cmd (pthread_spin_init (&spinlock->pthread_spinlock, PTHREAD_PROCESS_PRIVATE)); +#else + InitializeCriticalSection (&spinlock->win32_spinlock); +#endif /* !_WIN32 */ +} + +bool +pgm_spinlock_trylock ( + pgm_spinlock_t* spinlock + ) +{ + pgm_assert (NULL != spinlock); +#ifndef _WIN32 + const int result = pthread_spin_trylock (&spinlock->pthread_spinlock); + if (EBUSY == result) + return FALSE; + posix_check_err (result, "pthread_spinlock_trylock"); + return TRUE; +#else + return TryEnterCriticalSection (&spinlock->win32_spinlock); +#endif /* !_WIN32 */ +} + +void +pgm_spinlock_free ( + pgm_spinlock_t* spinlock + ) +{ + pgm_assert (NULL != spinlock); +#ifndef _WIN32 +/* ignore return value */ + pthread_spin_destroy (&spinlock->pthread_spinlock); +#else + DeleteCriticalSection (&spinlock->win32_spinlock); +#endif /* !_WIN32 */ +} + +void +pgm_cond_init ( + pgm_cond_t* cond + ) +{ + pgm_assert (NULL != cond); +#ifndef _WIN32 + posix_check_cmd (pthread_cond_init (&cond->pthread_cond, NULL)); +#elif defined(CONFIG_HAVE_WIN_COND) + InitializeConditionVariable (&cond->win32_cond); +#else + cond->len = 0; + cond->allocated_len = pgm_nearest_power (1, 2 + 1); + cond->phandle = pgm_new (HANDLE, cond->allocated_len); + InitializeCriticalSection (&cond->win32_spinlock); +#endif /* !_WIN32 */ +} + +void +pgm_cond_signal ( + pgm_cond_t* cond + ) +{ + pgm_assert (NULL != cond); +#ifndef _WIN32 + pthread_cond_signal (&cond->pthread_cond); +#elif defined(CONFIG_HAVE_WIN_COND) + WakeConditionVariable (&cond->win32_cond); +#else + EnterCriticalSection (&cond->win32_spinlock); + if (cond->len > 0) { + SetEvent (cond->phandle[ 0 ]); + memmove (&cond->phandle[ 0 ], &cond->phandle[ 1 ], cond->len - 1); + cond->len--; + } + LeaveCriticalSection (&cond->win32_spinlock); +#endif /* !_WIN32 */ +} + +void +pgm_cond_broadcast ( + pgm_cond_t* cond + ) +{ + pgm_assert (NULL != cond); +#ifndef _WIN32 + pthread_cond_broadcast (&cond->pthread_cond); +#elif defined(CONFIG_HAVE_WIN_COND) + WakeAllConditionVariable (&cond->win32_cond); +#else + EnterCriticalSection (&cond->win32_spinlock); + for (unsigned i = 0; i < cond->len; i++) + SetEvent (cond->phandle[ i ]); + cond->len = 0; + LeaveCriticalSection (&cond->win32_spinlock); +#endif /* !_WIN32 */ +} + +#ifndef _WIN32 +void +pgm_cond_wait ( + pgm_cond_t* cond, + pthread_mutex_t* mutex + ) +{ + pgm_assert (NULL != cond); + pgm_assert (NULL != mutex); + pthread_cond_wait (&cond->pthread_cond, mutex); +} +#else +void +pgm_cond_wait ( + pgm_cond_t* cond, + CRITICAL_SECTION* spinlock + ) +{ + pgm_assert (NULL != cond); + pgm_assert (NULL != spinlock); +# if defined(CONFIG_HAVE_WIN_COND) + SleepConditionVariableCS (&cond->win32_cond, spinlock, INFINITE); +# else + DWORD status; + HANDLE event = TlsGetValue (cond_event_tls); + + if (!event) { + win32_check_cmd (event = CreateEvent (0, FALSE, FALSE, NULL)); + TlsSetValue (cond_event_tls, event); + } + + EnterCriticalSection (&cond->win32_spinlock); + pgm_assert (WAIT_TIMEOUT == WaitForSingleObject (event, 0)); + if ((cond->len + 1) > cond->allocated_len) { + cond->allocated_len = pgm_nearest_power (1, cond->len + 1 + 1); + cond->phandle = pgm_realloc (cond->phandle, cond->allocated_len); + } + cond->phandle[ cond->len++ ] = event; + LeaveCriticalSection (&cond->win32_spinlock); + + EnterCriticalSection (spinlock); + win32_check_cmd (WAIT_FAILED != (status = WaitForSingleObject (event, INFINITE))); + LeaveCriticalSection (spinlock); + + if (WAIT_TIMEOUT == status) { + EnterCriticalSection (&cond->win32_spinlock); + for (unsigned i = 0; i < cond->len; i++) { + if (cond->phandle[ i ] == event) { + if (i != cond->len - 1) + memmove (&cond->phandle[ i ], &cond->phandle[ i + 1 ], sizeof(HANDLE) * (cond->len - i - 1)); + cond->len--; + break; + } + } + win32_check_cmd (WAIT_FAILED != (status = WaitForSingleObject (event, 0))); + LeaveCriticalSection (&cond->win32_spinlock); + } +# endif /* !CONFIG_HAVE_WIN_COND */ +} +#endif /* !_WIN32 */ + +void +pgm_cond_free ( + pgm_cond_t* cond + ) +{ + pgm_assert (NULL != cond); +#ifndef _WIN32 + posix_check_cmd (pthread_cond_destroy (&cond->pthread_cond)); +#elif defined(CONFIG_HAVE_WIN_COND) + /* nop */ +#else + DeleteCriticalSection (&cond->win32_spinlock); + pgm_free (cond->phandle); +#endif /* !_WIN32 */ +} + +void +pgm_rwlock_init ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); +#ifdef CONFIG_HAVE_WIN_SRW_LOCK + InitializeSRWLock (&rwlock->win32_lock); +#elif !defined(_WIN32) + posix_check_cmd (pthread_rwlock_init (&rwlock->pthread_rwlock, NULL)); +#else + InitializeCriticalSection (&rwlock->win32_spinlock); + pgm_cond_init (&rwlock->read_cond); + pgm_cond_init (&rwlock->write_cond); + rwlock->read_counter = 0; + rwlock->have_writer = FALSE; + rwlock->want_to_read = 0; + rwlock->want_to_write = 0; +#endif /* !CONFIG_HAVE_WIN_SRW_LOCK */ +} + +void +pgm_rwlock_free ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); +#ifdef CONFIG_HAVE_WIN_SRW_LOCK + /* nop */ +#elif !defined(_WIN32) + pthread_rwlock_destroy (&rwlock->pthread_rwlock); +#else + pgm_cond_free (&rwlock->read_cond); + pgm_cond_free (&rwlock->write_cond); + DeleteCriticalSection (&rwlock->win32_spinlock); +#endif /* !CONFIG_HAVE_WIN_SRW_LOCK */ +} + +#if !defined(CONFIG_HAVE_WIN_SRW_LOCK) && defined(_WIN32) +static inline +void +_pgm_rwlock_signal ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); + if (rwlock->want_to_write) + pgm_cond_signal (&rwlock->write_cond); + else if (rwlock->want_to_read) + pgm_cond_broadcast (&rwlock->read_cond); +} + +void +pgm_rwlock_reader_lock ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); + EnterCriticalSection (&rwlock->win32_spinlock); + rwlock->want_to_read++; + while (rwlock->have_writer || rwlock->want_to_write) + pgm_cond_wait (&rwlock->read_cond, &rwlock->win32_spinlock); + rwlock->want_to_read--; + rwlock->read_counter++; + LeaveCriticalSection (&rwlock->win32_spinlock); +} + +bool +pgm_rwlock_reader_trylock ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); + bool status; + EnterCriticalSection (&rwlock->win32_spinlock); + if (!rwlock->have_writer && !rwlock->want_to_write) { + rwlock->read_counter++; + status = TRUE; + } else + status = FALSE; + LeaveCriticalSection (&rwlock->win32_spinlock); + return status; +} + +void +pgm_rwlock_reader_unlock( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); + EnterCriticalSection (&rwlock->win32_spinlock); + rwlock->read_counter--; + if (rwlock->read_counter == 0) + _pgm_rwlock_signal (rwlock); + LeaveCriticalSection (&rwlock->win32_spinlock); +} + +void +pgm_rwlock_writer_lock ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); + EnterCriticalSection (&rwlock->win32_spinlock); + rwlock->want_to_write++; + while (rwlock->have_writer || rwlock->read_counter) + pgm_cond_wait (&rwlock->write_cond, &rwlock->win32_spinlock); + rwlock->want_to_write--; + rwlock->have_writer = TRUE; + LeaveCriticalSection (&rwlock->win32_spinlock); +} + +bool +pgm_rwlock_writer_trylock ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); + bool status; + EnterCriticalSection (&rwlock->win32_spinlock); + if (!rwlock->have_writer && !rwlock->read_counter) { + rwlock->have_writer = TRUE; + status = TRUE; + } else + status = FALSE; + LeaveCriticalSection (&rwlock->win32_spinlock); + return status; +} + +void +pgm_rwlock_writer_unlock ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); + EnterCriticalSection (&rwlock->win32_spinlock); + rwlock->have_writer = FALSE; + _pgm_rwlock_signal (rwlock); + LeaveCriticalSection (&rwlock->win32_spinlock); +} +#endif /* !_WIN32 && !CONFIG_HAVE_WIN_SRW_LOCK */ + + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/time.c b/3rdparty/openpgm-svn-r1085/pgm/time.c new file mode 100644 index 0000000..9b8eeaa --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/time.c @@ -0,0 +1,770 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * high resolution timers. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __STDC_FORMAT_MACROS +#include +#include +#include +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include "windows.h" +#endif +#include +#include + +//#define TIME_DEBUG + + +/* globals */ + +pgm_time_update_func pgm_time_update_now PGM_GNUC_READ_MOSTLY; +pgm_time_since_epoch_func pgm_time_since_epoch PGM_GNUC_READ_MOSTLY; + + +/* locals */ + +#define msecs_to_secs(t) ( (t) / 1000 ) +#define usecs_to_secs(t) ( (t) / 1000000UL ) +#define nsecs_to_secs(t) ( (t) / 1000000000UL ) +#define secs_to_msecs(t) ( (pgm_time_t)(t) * 1000 ) +#define secs_to_usecs(t) ( (pgm_time_t)(t) * 1000000UL ) +#define secs_to_nsecs(t) ( (pgm_time_t)(t) * 1000000000UL ) +#define msecs_to_usecs(t) ( (pgm_time_t)(t) * 1000 ) +#define msecs_to_nsecs(t) ( (pgm_time_t)(t) * 1000000UL ) +#define usecs_to_msecs(t) ( (t) / 1000 ) +#define usecs_to_nsecs(t) ( (pgm_time_t)(t) * 1000 ) +#define nsecs_to_msecs(t) ( (t) / 1000000UL ) +#define nsecs_to_usecs(t) ( (t) / 1000 ) +#define fsecs_to_nsecs(t) ( (t) / 1000000UL ) +#define fsecs_to_usecs(t) ( (t) / 1000000000UL ) + +static volatile uint32_t time_ref_count = 0; +static pgm_time_t rel_offset PGM_GNUC_READ_MOSTLY = 0; + +static void pgm_time_conv (const pgm_time_t*const restrict, time_t*restrict); +static void pgm_time_conv_from_reset (const pgm_time_t*const restrict, time_t*restrict); + +#if defined(CONFIG_HAVE_CLOCK_GETTIME) +# include +static pgm_time_t pgm_clock_update (void); +#endif +#ifdef CONFIG_HAVE_FTIME +# include +# ifdef _WIN32 +# define ftime _ftime +# endif +static pgm_time_t pgm_ftime_update (void); +#endif +#ifdef CONFIG_HAVE_GETTIMEOFDAY +# include +static pgm_time_t pgm_gettimeofday_update (void); +#endif +#ifdef CONFIG_HAVE_HPET +# include +# include +# include +# include +# include +# define HPET_MMAP_SIZE 0x400 +# define HPET_GENERAL_CAPS_REGISTER 0x00 +# define HPET_COUNTER_CLK_PERIOD 0x004 +# define HPET_MAIN_COUNTER_REGISTER 0x0f0 +# define HPET_COUNT_SIZE_CAP (1 << 13) +/* HPET counter size maybe 64-bit or 32-bit */ +# if defined(__x86_64__) +typedef uint64_t hpet_counter_t; +# else +typedef uint32_t hpet_counter_t; +# endif +static int hpet_fd PGM_GNUC_READ_MOSTLY = -1; +static char* hpet_ptr PGM_GNUC_READ_MOSTLY; +static uint64_t hpet_offset = 0; +static uint64_t hpet_wrap PGM_GNUC_READ_MOSTLY; +static hpet_counter_t hpet_last = 0; + +# define HPET_NS_SCALE 22 +# define HPET_US_SCALE 34 +static uint_fast32_t hpet_ns_mul PGM_GNUC_READ_MOSTLY = 0; +static uint_fast32_t hpet_us_mul PGM_GNUC_READ_MOSTLY = 0; + +static inline +void +set_hpet_mul ( + const uint32_t hpet_period + ) +{ + hpet_ns_mul = fsecs_to_nsecs((uint64_t)hpet_period << HPET_NS_SCALE); + hpet_us_mul = fsecs_to_usecs((uint64_t)hpet_period << HPET_US_SCALE); +} + +static inline +uint64_t +hpet_to_ns ( + const uint64_t hpet + ) +{ + return (hpet * hpet_ns_mul) >> HPET_NS_SCALE; +} + +static inline +uint64_t +hpet_to_us ( + const uint64_t hpet + ) +{ + return (hpet * hpet_us_mul) >> HPET_US_SCALE; +} + +static bool pgm_hpet_init (pgm_error_t**); +static bool pgm_hpet_shutdown (void); +static pgm_time_t pgm_hpet_update (void); +#endif +#ifdef CONFIG_HAVE_RTC +# include +# include +# include +# include +# include +# include +static int rtc_fd PGM_GNUC_READ_MOSTLY = -1; +static int rtc_frequency PGM_GNUC_READ_MOSTLY = 8192; +static pgm_time_t rtc_count = 0; +static bool pgm_rtc_init (pgm_error_t**); +static bool pgm_rtc_shutdown (void); +static pgm_time_t pgm_rtc_update (void); +#endif +#ifdef CONFIG_HAVE_TSC +# include +# include +# define TSC_NS_SCALE 10 /* 2^10, carefully chosen */ +# define TSC_US_SCALE 20 +static uint_fast32_t tsc_mhz PGM_GNUC_READ_MOSTLY = 0; +static uint_fast32_t tsc_ns_mul PGM_GNUC_READ_MOSTLY = 0; +static uint_fast32_t tsc_us_mul PGM_GNUC_READ_MOSTLY = 0; + +static inline +void +set_tsc_mul ( + const unsigned khz + ) +{ + tsc_ns_mul = (1000000 << TSC_NS_SCALE) / khz; + tsc_us_mul = (1000 << TSC_US_SCALE) / khz; +} + +static inline +uint64_t +tsc_to_ns ( + const uint64_t tsc + ) +{ + return (tsc * tsc_ns_mul) >> TSC_NS_SCALE; +} + +static inline +uint64_t +ns_to_tsc ( + const uint64_t ns + ) +{ + return (ns << TSC_NS_SCALE) / tsc_ns_mul; +} + +static inline +uint64_t +tsc_to_us ( + const uint64_t tsc + ) +{ + return (tsc * tsc_us_mul) >> TSC_US_SCALE; +} + +static inline +uint64_t +us_to_tsc ( + const uint64_t us + ) +{ + return (us << TSC_US_SCALE) / tsc_us_mul; +} + +# ifndef _WIN32 +static bool pgm_tsc_init (pgm_error_t**); +# endif +static pgm_time_t pgm_tsc_update (void); +#endif + + +/* initialize time system. + * + * returns TRUE on success, returns FALSE on error such as being unable to open + * the RTC device, an unstable TSC, or system already initialized. + */ + +bool +pgm_time_init ( + pgm_error_t** error + ) +{ + if (pgm_atomic_exchange_and_add32 (&time_ref_count, 1) > 0) + return TRUE; + +/* current time */ + const char *cfg = getenv ("PGM_TIMER"); + if (cfg == NULL) { +#ifdef CONFIG_HAVE_TSC + cfg = "TSC"; +#else + cfg = "GTOD"; +#endif + } + + pgm_time_since_epoch = pgm_time_conv; + + switch (cfg[0]) { +#ifdef CONFIG_HAVE_FTIME + case 'F': + pgm_minor (_("Using ftime() timer.")); + pgm_time_update_now = pgm_ftime_update; + break; +#endif +#ifdef CONFIG_HAVE_CLOCK_GETTIME + case 'C': + pgm_minor (_("Using clock_gettime() timer.")); + pgm_time_update_now = pgm_clock_update; + break; +#endif +#ifdef CONFIG_HAVE_RTC + case 'R': + pgm_minor (_("Using /dev/rtc timer.")); + pgm_time_update_now = pgm_rtc_update; + pgm_time_since_epoch = pgm_time_conv_from_reset; + break; +#endif +#ifdef CONFIG_HAVE_TSC +# ifdef _WIN32 + default: +# endif + case 'T': + pgm_minor (_("Using TSC timer.")); + pgm_time_update_now = pgm_tsc_update; + pgm_time_since_epoch = pgm_time_conv_from_reset; + break; +#endif +#ifdef CONFIG_HAVE_HPET + case 'H': + pgm_minor (_("Using HPET timer.")); + pgm_time_update_now = pgm_hpet_update; + pgm_time_since_epoch = pgm_time_conv_from_reset; + break; +#endif + +#ifdef CONFIG_HAVE_GETTIMEOFDAY +# ifndef _WIN32 + default: +# endif + case 'G': + pgm_minor (_("Using gettimeofday() timer.")); + pgm_time_update_now = pgm_gettimeofday_update; + break; +#endif + } + +#ifdef CONFIG_HAVE_RTC + if (pgm_time_update_now == pgm_rtc_update) + { + pgm_error_t* sub_error = NULL; + if (!pgm_rtc_init (&sub_error)) { + pgm_propagate_error (error, sub_error); + goto err_cleanup; + } + } +#endif +#ifdef CONFIG_HAVE_TSC + if (pgm_time_update_now == pgm_tsc_update) + { +#ifdef CONFIG_HAVE_PROC +/* attempt to parse clock ticks from kernel + */ + FILE* fp = fopen ("/proc/cpuinfo", "r"); + char buffer[1024]; + if (fp) + { + while (!feof(fp) && fgets (buffer, sizeof(buffer), fp)) + { + if (strstr (buffer, "cpu MHz")) + { + const char *p = strchr (buffer, ':'); + if (p) tsc_mhz = atoi (p + 1); + break; + } + } + fclose (fp); + } +#elif defined(_WIN32) + uint64_t frequency; + if (QueryPerformanceFrequency ((LARGE_INTEGER*)&frequency)) + { + tsc_mhz = frequency / 1000; + } +#endif /* !_WIN32 */ + +/* e.g. export RDTSC_FREQUENCY=3200.000000 + * + * Value can be used to override kernel tick rate as well as internal calibration + */ + const char *env_mhz = getenv ("RDTSC_FREQUENCY"); + if (env_mhz) + tsc_mhz = atoi (env_mhz); + +#ifndef _WIN32 +/* calibrate */ + if (0 >= tsc_mhz) { + pgm_error_t* sub_error = NULL; + if (!pgm_tsc_init (&sub_error)) { + pgm_propagate_error (error, sub_error); + goto err_cleanup; + } + } +#endif + set_tsc_mul (tsc_mhz * 1000); + } +#endif /* CONFIG_HAVE_TSC */ + +#ifdef CONFIG_HAVE_HPET + if (pgm_time_update_now == pgm_hpet_update) + { + pgm_error_t* sub_error = NULL; + if (!pgm_hpet_init (&sub_error)) { + pgm_propagate_error (error, sub_error); + goto err_cleanup; + } + } +#endif + + pgm_time_update_now(); + +/* calculate relative time offset */ +#if defined(CONFIG_HAVE_RTC) || defined(CONFIG_HAVE_TSC) + if ( 0 +# ifdef CONFIG_HAVE_RTC + || pgm_time_update_now == pgm_rtc_update +# endif +# ifdef CONFIG_HAVE_TSC + || pgm_time_update_now == pgm_tsc_update +# endif + ) + { +# if defined( CONFIG_HAVE_GETTIMEOFDAY ) + rel_offset = pgm_gettimeofday_update() - pgm_time_update_now(); +# elif defined( CONFIG_HAVE_FTIME ) + rel_offset = pgm_ftime_update() - pgm_time_update_now(); +# else +# error "gettimeofday() or ftime() required to calculate counter offset" +# endif + } +#else + rel_offset = 0; +#endif + + return TRUE; + +err_cleanup: + pgm_atomic_dec32 (&time_ref_count); + return FALSE; +} + +/* returns TRUE if shutdown succeeded, returns FALSE on error. + */ + +bool +pgm_time_shutdown (void) +{ + pgm_return_val_if_fail (pgm_atomic_read32 (&time_ref_count) > 0, FALSE); + + if (pgm_atomic_exchange_and_add32 (&time_ref_count, (uint32_t)-1) != 1) + return TRUE; + + bool success = TRUE; +#ifdef CONFIG_HAVE_RTC + if (pgm_time_update_now == pgm_rtc_update) + success = pgm_rtc_shutdown (); +#endif +#ifdef CONFIG_HAVE_HPET + if (pgm_time_update_now == pgm_hpet_update) + success = pgm_hpet_shutdown (); +#endif + return success; +} + +#ifdef CONFIG_HAVE_GETTIMEOFDAY +static +pgm_time_t +pgm_gettimeofday_update (void) +{ + struct timeval gettimeofday_now; + static pgm_time_t last = 0; + gettimeofday (&gettimeofday_now, NULL); + const pgm_time_t now = secs_to_usecs (gettimeofday_now.tv_sec) + gettimeofday_now.tv_usec; + if (PGM_UNLIKELY(now < last)) + return last; + else + return last = now; +} +#endif /* CONFIG_HAVE_GETTIMEOFDAY */ + +#ifdef CONFIG_HAVE_CLOCK_GETTIME +static +pgm_time_t +pgm_clock_update (void) +{ + struct timespec clock_now; + static pgm_time_t last = 0; + clock_gettime (CLOCK_MONOTONIC, &clock_now); + const pgm_time_t now = secs_to_usecs (clock_now.tv_sec) + nsecs_to_usecs (clock_now.tv_nsec); + if (PGM_UNLIKELY(now < last)) + return last; + else + return last = now; +} +#endif /* CONFIG_HAVE_CLOCK_GETTIME */ + +#ifdef CONFIG_HAVE_FTIME +static +pgm_time_t +pgm_ftime_update (void) +{ + struct timeb ftime_now; + static pgm_time_t last = 0; + ftime (&ftime_now); + const pgm_time_t now = secs_to_usecs (ftime_now.time) + msecs_to_usecs (ftime_now.millitm); + if (PGM_UNLIKELY(now < last)) + return last; + else + return last = now; +} +#endif /* CONFIG_HAVE_FTIME */ + +#ifdef CONFIG_HAVE_RTC +/* Old PC/AT-Compatible driver: /dev/rtc + * + * Not so speedy 8192 Hz timer, thats 122us resolution. + * + * WARNING: time is relative to start of timer. + * WARNING: only one process is allowed to access the RTC. + */ + +static +bool +pgm_rtc_init ( + pgm_error_t** error + ) +{ + pgm_return_val_if_fail (rtc_fd == -1, FALSE); + + rtc_fd = open ("/dev/rtc", O_RDONLY); + if (-1 == rtc_fd) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_TIME, + PGM_ERROR_FAILED, + _("Cannot open /dev/rtc for reading: %s"), + strerror(errno)); + return FALSE; + } + if (-1 == ioctl (rtc_fd, RTC_IRQP_SET, rtc_frequency)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_TIME, + PGM_ERROR_FAILED, + _("Cannot set RTC frequency to %i Hz: %s"), + rtc_frequency, + strerror(errno)); + return FALSE; + } + if (-1 == ioctl (rtc_fd, RTC_PIE_ON, 0)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_TIME, + PGM_ERROR_FAILED, + _("Cannot enable periodic interrupt (PIE) on RTC: %s"), + strerror(errno)); + return FALSE; + } + return TRUE; +} + +/* returns TRUE on success even if RTC device cannot be closed or had an IO error, + * returns FALSE if the RTC file descriptor is not set. + */ + +static +bool +pgm_rtc_shutdown (void) +{ + pgm_return_val_if_fail (rtc_fd, FALSE); + pgm_warn_if_fail (0 == close (rtc_fd)); + rtc_fd = -1; + return TRUE; +} + +/* RTC only indicates passed ticks therefore is by definition monotonic, we do not + * need to check the difference with respect to the last value. + */ + +static +pgm_time_t +pgm_rtc_update (void) +{ + uint32_t data; + +/* returned value contains interrupt type and count of interrupts since last read */ + pgm_warn_if_fail (sizeof(data) == read (rtc_fd, &data, sizeof(data))); + rtc_count += data >> 8; + return rtc_count * 1000000UL / rtc_frequency; +} +#endif /* CONFIG_HAVE_RTC */ + +#ifdef CONFIG_HAVE_TSC +/* read time stamp counter (TSC), count of ticks from processor reset. + * + * NB: On Windows this will usually be HPET or PIC timer interpolated with TSC. + */ + +static inline +pgm_time_t +rdtsc (void) +{ +# ifndef _WIN32 + uint32_t lo, hi; + +/* We cannot use "=A", since this would use %rax on x86_64 */ + asm volatile ("rdtsc" : "=a" (lo), "=d" (hi)); + + return (pgm_time_t)hi << 32 | lo; +# else + uint64_t counter; + QueryPerformanceCounter ((LARGE_INTEGER*)&counter); + return (pgm_time_t)counter; +# endif +} + +# ifndef _WIN32 +/* determine ratio of ticks to nano-seconds, use /dev/rtc for high accuracy + * millisecond timer and convert. + * + * WARNING: time is relative to start of timer. + */ + +static +bool +pgm_tsc_init ( + PGM_GNUC_UNUSED pgm_error_t** error + ) +{ +# ifdef CONFIG_HAVE_PROC +/* Test for constant TSC from kernel + */ + FILE* fp = fopen ("/proc/cpuinfo", "r"); + char buffer[1024], *flags = NULL; + if (fp) + { + while (!feof(fp) && fgets (buffer, sizeof(buffer), fp)) + { + if (strstr (buffer, "flags")) + { + flags = strchr (buffer, ':'); + break; + } + } + fclose (fp); + } + if (!flags || !strstr (flags, " tsc")) { + pgm_warn (_("Linux kernel reports no Time Stamp Counter (TSC).")); +/* force both to stable clocks even though one might be OK */ + pgm_time_update_now = pgm_gettimeofday_update; + return TRUE; + } + if (!strstr (flags, " constant_tsc")) { + pgm_warn (_("Linux kernel reports non-constant Time Stamp Counter (TSC).")); +/* force both to stable clocks even though one might be OK */ + pgm_time_update_now = pgm_gettimeofday_update; + return TRUE; + } +# endif /* CONFIG_HAVE_PROC */ + pgm_time_t start, stop; + const pgm_time_t calibration_usec = secs_to_usecs (4); + + pgm_info (_("Running a benchmark to measure system clock frequency...")); + + struct timespec req = { + .tv_sec = 4, + .tv_nsec = 0 + }; + start = rdtsc(); + while (-1 == nanosleep (&req, &req) && EINTR == errno); + stop = rdtsc(); + + if (stop < start) + { + pgm_warn (_("Finished RDTSC test. Unstable TSC detected. The benchmark resulted in a " + "non-monotonic time response rendering the TSC unsuitable for high resolution " + "timing. To prevent the start delay from this benchmark and use a stable clock " + "source set the environment variable PGM_TIMER to GTOD.")); +/* force both to stable clocks even though one might be OK */ + pgm_time_update_now = pgm_gettimeofday_update; + return TRUE; + } + +/* TODO: this math needs to be scaled to reduce rounding errors */ + const pgm_time_t tsc_diff = stop - start; + if (tsc_diff > calibration_usec) { +/* cpu > 1 Ghz */ + tsc_mhz = tsc_diff / calibration_usec; + } else { +/* cpu < 1 Ghz */ + tsc_mhz = -( calibration_usec / tsc_diff ); + } + + pgm_info (_("Finished RDTSC test. To prevent the startup delay from this benchmark, " + "set the environment variable RDTSC_FREQUENCY to %" PRIuFAST32 " on this " + "system. This value is dependent upon the CPU clock speed and " + "architecture and should be determined separately for each server."), + tsc_mhz); + return TRUE; +} +# endif + +/* TSC is monotonic on the same core but we do neither force the same core or save the count + * for each core as if the counter is unstable system wide another timing mechanism should be + * used, preferably HPET on x86/AMD64 or gettimeofday() on SPARC. + */ + +static +pgm_time_t +pgm_tsc_update (void) +{ + static pgm_time_t last = 0; + const pgm_time_t now = tsc_to_us (rdtsc()); + if (PGM_UNLIKELY(now < last)) + return last; + else + return last = now; +} +#endif + +#ifdef CONFIG_HAVE_HPET +/* High Precision Event Timer (HPET) created as a system wide stable high resolution timer + * to replace dependency on core specific counters (TSC). + * + * NB: Only available on x86/AMD64 hardware post 2007 + */ + +static +bool +pgm_hpet_init ( + pgm_error_t** error + ) +{ + pgm_return_val_if_fail (hpet_fd == -1, FALSE); + + hpet_fd = open("/dev/hpet", O_RDONLY); + if (hpet_fd < 0) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_TIME, + PGM_ERROR_FAILED, + _("Cannot open /dev/hpet for reading: %s"), + strerror(errno)); + return FALSE; + } + + hpet_ptr = mmap(NULL, HPET_MMAP_SIZE, PROT_READ, MAP_SHARED, hpet_fd, 0); + if (MAP_FAILED == hpet_ptr) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_TIME, + PGM_ERROR_FAILED, + _("Error mapping HPET device: %s"), + strerror(errno)); + close (hpet_fd); + hpet_fd = -1; + return FALSE; + } + +/* HPET counter tick period is in femto-seconds, a value of 0 is not permitted, + * the value must be <= 0x05f5e100 or 100ns. + */ + const uint32_t hpet_period = *((uint32_t*)(hpet_ptr + HPET_COUNTER_CLK_PERIOD)); + set_hpet_mul (hpet_period); +#if defined( __x86_64__ ) || defined( __amd64 ) + const uint32_t hpet_caps = *((uint32_t*)(hpet_ptr + HPET_GENERAL_CAPS_REGISTER)); + hpet_wrap = hpet_caps & HPET_COUNT_SIZE_CAP ? 0 : (1ULL << 32); +#else + hpet_wrap = 1ULL << 32; +#endif + + return TRUE; +} + +static +bool +pgm_hpet_shutdown (void) +{ + pgm_return_val_if_fail (hpet_fd, FALSE); + pgm_warn_if_fail (0 == close (hpet_fd)); + hpet_fd = -1; + return TRUE; +} + +static +pgm_time_t +pgm_hpet_update (void) +{ + const hpet_counter_t hpet_count = *((hpet_counter_t*)(hpet_ptr + HPET_MAIN_COUNTER_REGISTER)); +/* 32-bit HPET counters wrap after ~4 minutes */ + if (PGM_UNLIKELY(hpet_count < hpet_last)) + hpet_offset += hpet_wrap; + hpet_last = hpet_count; + return hpet_to_us (hpet_offset + hpet_count); +} +#endif /* CONFIG_HAVE_HPET */ + +/* convert from pgm_time_t to time_t with pgm_time_t in microseconds since the epoch. + */ +static +void +pgm_time_conv ( + const pgm_time_t* const restrict pgm_time_t_time, + time_t* restrict time_t_time + ) +{ + *time_t_time = pgm_to_secs (*pgm_time_t_time); +} + +/* convert from pgm_time_t to time_t with pgm_time_t in microseconds since the core started. + */ +static +void +pgm_time_conv_from_reset ( + const pgm_time_t* const restrict pgm_time_t_time, + time_t* restrict time_t_time + ) +{ + *time_t_time = pgm_to_secs (*pgm_time_t_time + rel_offset); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/time_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/time_unittest.c new file mode 100644 index 0000000..fd28572 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/time_unittest.c @@ -0,0 +1,188 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for high resolution timers. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include + + +/* mock state */ + + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + + +#include "time.c" + + +/* target: + * boolean + * pgm_time_init (pgm_error_t** error) + */ + +/* time initialisation uses reference counting */ + +START_TEST (test_init_pass_001) +{ + fail_unless (TRUE == pgm_time_init (NULL), "init #1 failed"); + fail_unless (TRUE == pgm_time_init (NULL), "init #2 failed"); +} +END_TEST + +/* target: + * bool + * pgm_time_shutdown (void) + */ + +START_TEST (test_shutdown_pass_001) +{ + fail_unless (TRUE == pgm_time_init (NULL), "init failed"); + fail_unless (TRUE == pgm_time_shutdown (), "shutdown #1 failed"); + fail_unless (FALSE == pgm_time_shutdown (), "shutdown #2 failed"); +} +END_TEST + +START_TEST (test_shutdown_pass_002) +{ + fail_unless (TRUE == pgm_time_init (NULL), "init #1 failed"); + fail_unless (TRUE == pgm_time_init (NULL), "init #2 failed"); + fail_unless (TRUE == pgm_time_shutdown (), "shutdown #1 failed"); + fail_unless (TRUE == pgm_time_shutdown (), "shutdown #2 failed"); + fail_unless (FALSE == pgm_time_shutdown (), "shutdown #3 failed"); +} +END_TEST + +/* target: + * pgm_time_t + * pgm_time_update_now (void) + */ + +START_TEST (test_update_now_pass_001) +{ + pgm_time_t tstamps[11]; + fail_unless (TRUE == pgm_time_init (NULL), "init failed"); + const pgm_time_t start_time = pgm_time_update_now (); + for (unsigned i = 1; i <= 10; i++) + { + tstamps[i] = pgm_time_update_now(); + } + g_message ("start-time: %" PGM_TIME_FORMAT, start_time); + for (unsigned i = 1; i <= 10; i++) + { + const pgm_time_t check_time = tstamps[i]; + const gint64 elapsed_time = check_time - start_time; + +/* must be monotonic */ + fail_unless (G_LIKELY(check_time >= start_time), "non-monotonic"); + + g_message ("check-point-%2.2u: %" PGM_TIME_FORMAT " (%+" G_GINT64_FORMAT "us)", + i, check_time, pgm_to_usecs(elapsed_time)); + } + fail_unless (TRUE == pgm_time_shutdown (), "shutdown failed"); +} +END_TEST + +/* target: + * void + * pgm_time_since_epoch ( + * pgm_time_t* pgm_time, + * time_t* epoch_time + * ) + */ + +START_TEST (test_since_epoch_pass_001) +{ + char stime[1024]; + time_t t; + struct tm* tmp; + fail_unless (TRUE == pgm_time_init (NULL), "init failed"); + pgm_time_t pgm_now = pgm_time_update_now (); + pgm_time_since_epoch (&pgm_now, &t); + tmp = localtime (&t); + fail_unless (NULL != tmp, "localtime failed"); + fail_unless (0 != strftime (stime, sizeof(stime), "%X", tmp), "strftime failed"); + g_message ("pgm-time:%" PGM_TIME_FORMAT " = %s", + pgm_now, stime); + fail_unless (TRUE == pgm_time_shutdown (), "shutdown failed"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_init = tcase_create ("init"); + suite_add_tcase (s, tc_init); + tcase_add_test (tc_init, test_init_pass_001); + + TCase* tc_shutdown = tcase_create ("shutdown"); + suite_add_tcase (s, tc_shutdown); + tcase_add_test (tc_shutdown, test_shutdown_pass_001); + tcase_add_test (tc_shutdown, test_shutdown_pass_002); + + TCase* tc_update_now = tcase_create ("update-now"); + suite_add_tcase (s, tc_update_now); + tcase_add_test (tc_update_now, test_update_now_pass_001); + + TCase* tc_since_epoch = tcase_create ("since-epoch"); + suite_add_tcase (s, tc_since_epoch); + tcase_add_test (tc_since_epoch, test_since_epoch_pass_001); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/timer.c b/3rdparty/openpgm-svn-r1085/pgm/timer.c new file mode 100644 index 0000000..3bee14c --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/timer.c @@ -0,0 +1,223 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM timer thread. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include +#include +#include + + +//#define TIMER_DEBUG + + +/* determine which timer fires next: spm (ihb_tmr), nak_rb_ivl, nak_rpt_ivl, or nak_rdata_ivl + * and check whether its already due. + * + * called in sock creation so locks unrequired. + */ + +bool +pgm_timer_prepare ( + pgm_sock_t* const sock + ) +{ + int32_t msec; + +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (sock->can_send_data || sock->can_recv_data); + + pgm_time_t now = pgm_time_update_now(); + pgm_time_t expiration; + + if (sock->can_send_data) + expiration = sock->next_ambient_spm; + else + expiration = now + sock->peer_expiry; + + sock->next_poll = expiration; + +/* advance time again to adjust for processing time out of the event loop, this + * could cause further timers to expire even before checking for new wire data. + */ + msec = pgm_to_msecs ((int64_t)expiration - (int64_t)now); + if (msec < 0) + msec = 0; + else + msec = MIN (INT32_MAX, msec); + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Next expiration in %" PRIi32 "ms"), msec); + return (msec == 0); +} + +bool +pgm_timer_check ( + pgm_sock_t* const sock + ) +{ + const pgm_time_t now = pgm_time_update_now(); + bool expired; + +/* pre-conditions */ + pgm_assert (NULL != sock); + + pgm_timer_lock (sock); + expired = pgm_time_after_eq (now, sock->next_poll); + pgm_timer_unlock (sock); + return expired; +} + +/* return next timer expiration in microseconds (μs) + */ + +pgm_time_t +pgm_timer_expiration ( + pgm_sock_t* const sock + ) +{ + const pgm_time_t now = pgm_time_update_now(); + pgm_time_t expiration; + +/* pre-conditions */ + pgm_assert (NULL != sock); + + pgm_timer_lock (sock); + expiration = pgm_time_after (sock->next_poll, now) ? pgm_to_usecs (sock->next_poll - now) : 0; + pgm_timer_unlock (sock); + return expiration; +} + +/* call all timers, assume that time_now has been updated by either pgm_timer_prepare + * or pgm_timer_check and no other method calls here. + * + * returns TRUE on success, returns FALSE on blocked send-in-receive operation. + */ + +bool +pgm_timer_dispatch ( + pgm_sock_t* const sock + ) +{ + const pgm_time_t now = pgm_time_update_now(); + pgm_time_t next_expiration = 0; + +/* pre-conditions */ + pgm_assert (NULL != sock); + + pgm_debug ("pgm_timer_dispatch (sock:%p)", (const void*)sock); + +/* find which timers have expired and call each */ + if (sock->can_recv_data) + { + if (!pgm_check_peer_state (sock, now)) + return FALSE; + next_expiration = pgm_min_receiver_expiry (now + sock->peer_expiry, sock); + } + + if (sock->can_send_data) + { +/* reset congestion control on ACK timeout */ + if (sock->use_pgmcc && + sock->tokens < pgm_fp8 (1) && + 0 != sock->ack_expiry) + { + if (pgm_time_after_eq (now, sock->ack_expiry)) + { +#ifdef DEBUG_PGMCC +char nows[1024]; +time_t t = time (NULL); +struct tm* tmp = localtime (&t); +strftime (nows, sizeof(nows), "%Y-%m-%d %H:%M:%S", tmp); +printf ("ACK timeout, T:%u W:%u\n", pgm_fp8tou(sock->tokens), pgm_fp8tou(sock->cwnd_size)); +#endif + sock->tokens = sock->cwnd_size = pgm_fp8 (1); + sock->ack_bitmap = 0xffffffff; + sock->ack_expiry = 0; + +/* notify blocking tx thread that transmission time is now available */ + pgm_notify_send (&sock->ack_notify); + } + next_expiration = next_expiration > 0 ? MIN(next_expiration, sock->ack_expiry) : sock->ack_expiry; + } + +/* SPM broadcast */ + pgm_mutex_lock (&sock->timer_mutex); + const unsigned spm_heartbeat_state = sock->spm_heartbeat_state; + const pgm_time_t next_heartbeat_spm = sock->next_heartbeat_spm; + pgm_mutex_unlock (&sock->timer_mutex); + +/* no lock needed on ambient */ + const pgm_time_t next_ambient_spm = sock->next_ambient_spm; + pgm_time_t next_spm = spm_heartbeat_state ? MIN(next_heartbeat_spm, next_ambient_spm) : next_ambient_spm; + + if (pgm_time_after_eq (now, next_spm) && + !pgm_send_spm (sock, 0)) + return FALSE; + +/* ambient timing not so important so base next event off current time */ + if (pgm_time_after_eq (now, next_ambient_spm)) + { + sock->next_ambient_spm = now + sock->spm_ambient_interval; + next_spm = spm_heartbeat_state ? MIN(next_heartbeat_spm, sock->next_ambient_spm) : sock->next_ambient_spm; + } + +/* heartbeat timing is often high resolution so base times to last event */ + if (spm_heartbeat_state && pgm_time_after_eq (now, next_heartbeat_spm)) + { + unsigned new_heartbeat_state = spm_heartbeat_state; + pgm_time_t new_heartbeat_spm = next_heartbeat_spm; + do { + new_heartbeat_spm += sock->spm_heartbeat_interval[new_heartbeat_state++]; + if (new_heartbeat_state == sock->spm_heartbeat_len) { + new_heartbeat_state = 0; + new_heartbeat_spm = now + sock->spm_ambient_interval; + break; + } + } while (pgm_time_after_eq (now, new_heartbeat_spm)); +/* check for reset heartbeat */ + pgm_mutex_lock (&sock->timer_mutex); + if (next_heartbeat_spm == sock->next_heartbeat_spm) { + sock->spm_heartbeat_state = new_heartbeat_state; + sock->next_heartbeat_spm = new_heartbeat_spm; + next_spm = MIN(sock->next_ambient_spm, new_heartbeat_spm); + } else + next_spm = MIN(sock->next_ambient_spm, sock->next_heartbeat_spm); + sock->next_poll = next_expiration > 0 ? MIN(next_expiration, next_spm) : next_spm; + pgm_mutex_unlock (&sock->timer_mutex); + return TRUE; + } + + next_expiration = next_expiration > 0 ? MIN(next_expiration, next_spm) : next_spm; + +/* check for reset */ + pgm_mutex_lock (&sock->timer_mutex); + sock->next_poll = sock->next_poll > now ? MIN(sock->next_poll, next_expiration) : next_expiration; + pgm_mutex_unlock (&sock->timer_mutex); + } + else + sock->next_poll = next_expiration; + + return TRUE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/timer_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/timer_unittest.c new file mode 100644 index 0000000..2e48802 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/timer_unittest.c @@ -0,0 +1,355 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for PGM timer thread. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include + + +/* mock state */ + + +#define g_main_context_new mock_g_main_context_new +#define g_main_context_unref mock_g_main_context_unref +#define g_main_loop_new mock_g_main_loop_new +#define g_main_loop_run mock_g_main_loop_run +#define g_main_loop_unref mock_g_main_loop_unref +#define g_source_new mock_g_source_new +#define g_source_set_priority mock_g_source_set_priority +#define g_source_attach mock_g_source_attach +#define g_source_unref mock_g_source_unref +#define pgm_time_now mock_pgm_time_now +#define pgm_time_update_now mock_pgm_time_update_now +#define pgm_min_receiver_expiry mock_pgm_min_receiver_expiry +#define pgm_check_peer_state mock_pgm_check_peer_state +#define pgm_send_spm mock_pgm_send_spm + + +#define TIMER_DEBUG +#include "timer.c" + +static pgm_time_t _mock_pgm_time_update_now(void); +pgm_time_update_func mock_pgm_time_update_now = _mock_pgm_time_update_now; +static pgm_time_t mock_pgm_time_now = 0x1; + + +static +pgm_sock_t* +generate_sock (void) +{ + pgm_sock_t* sock = g_new0 (pgm_sock_t, 1); + return sock; +} + + +/* mock functions for external references */ + +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return 0; +} + +/** GLib */ +static +GMainContext* +mock_g_main_context_new (void) +{ + GMainContext* context = g_malloc0 (sizeof(gpointer)); + return context; +} + +static +GMainLoop* +mock_g_main_loop_new ( + GMainContext* context, + gboolean is_running + ) +{ + g_assert (NULL != context); + GMainLoop* loop = g_malloc0 (sizeof(gpointer)); + return loop; +} + +static +void +mock_g_main_loop_run ( + GMainLoop* loop + ) +{ + g_assert (NULL != loop); +} + +static +void +mock_g_main_loop_unref ( + GMainLoop* loop + ) +{ + g_assert (NULL != loop); + g_free (loop); +} + +static +void +mock_g_main_context_unref ( + GMainContext* context + ) +{ + g_assert (NULL != context); + g_free (context); +} + +static +GSource* +mock_g_source_new ( + GSourceFuncs* source_funcs, + guint struct_size + ) +{ + g_assert (struct_size > 0); + GSource* source = g_malloc0 (struct_size); + return source; +} + +static +void +mock_g_source_set_priority ( + GSource* source, + gint priority + ) +{ + g_assert (NULL != source); +} + +static +guint +mock_g_source_attach ( + GSource* source, + GMainContext* context + ) +{ + g_assert (NULL != source); + return 1; +} + +static +void +mock_g_source_unref ( + GSource* source + ) +{ + g_assert (NULL != source); + g_free (source); +} + +/** time module */ +static +pgm_time_t +_mock_pgm_time_update_now (void) +{ + return mock_pgm_time_now; +} + +/** receiver module */ +PGM_GNUC_INTERNAL +pgm_time_t +mock_pgm_min_receiver_expiry ( + pgm_time_t expiration, + pgm_sock_t* sock + ) +{ + g_assert (NULL != sock); + return 0x1; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_check_peer_state ( + pgm_sock_t* sock, + pgm_time_t now + ) +{ + g_assert (NULL != sock); + return TRUE; +} + +/** source module */ +PGM_GNUC_INTERNAL +bool +mock_pgm_send_spm ( + pgm_sock_t* sock, + int flags + ) +{ + g_assert (NULL != sock); + return TRUE; +} + + +/* target: + * bool + * pgm_timer_prepare ( + * pgm_sock_t* sock + * ) + */ + +START_TEST (test_prepare_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->can_send_data = TRUE; + sock->next_ambient_spm = mock_pgm_time_now + pgm_secs(10); + fail_unless (FALSE == pgm_timer_prepare (sock), "prepare failed"); +} +END_TEST + +START_TEST (test_prepare_fail_001) +{ + gboolean expired = pgm_timer_prepare (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_timer_check ( + * pgm_sock_t* sock + * ) + */ + +START_TEST (test_check_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + fail_unless (TRUE == pgm_timer_check (sock), "check failed"); +} +END_TEST + +START_TEST (test_check_fail_001) +{ + gboolean expired = pgm_timer_check (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * pgm_time_t + * pgm_timer_expiration ( + * pgm_sock_t* sock + * ) + */ + +START_TEST (test_expiration_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->next_poll = mock_pgm_time_now + pgm_secs(300); + fail_unless (pgm_secs(300) == pgm_timer_expiration (sock), "expiration failed"); +} +END_TEST + +START_TEST (test_expiration_fail_001) +{ + long expiration = pgm_timer_expiration (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_timer_dispatch ( + * pgm_sock_t* sock + * ) + */ + +START_TEST (test_dispatch_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + pgm_timer_dispatch (sock); +} +END_TEST + +START_TEST (test_dispatch_fail_001) +{ + pgm_timer_dispatch (NULL); + fail ("reached"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_prepare = tcase_create ("prepare"); + suite_add_tcase (s, tc_prepare); + tcase_add_test (tc_prepare, test_prepare_pass_001); + tcase_add_test_raise_signal (tc_prepare, test_prepare_fail_001, SIGABRT); + + TCase* tc_check = tcase_create ("check"); + suite_add_tcase (s, tc_check); + tcase_add_test (tc_check, test_check_pass_001); + tcase_add_test_raise_signal (tc_check, test_check_fail_001, SIGABRT); + + TCase* tc_expiration = tcase_create ("expiration"); + suite_add_tcase (s, tc_expiration); + tcase_add_test (tc_expiration, test_expiration_pass_001); + tcase_add_test_raise_signal (tc_expiration, test_expiration_fail_001, SIGABRT); + + TCase* tc_dispatch = tcase_create ("dispatch"); + suite_add_tcase (s, tc_dispatch); + tcase_add_test (tc_dispatch, test_dispatch_pass_001); + tcase_add_test_raise_signal (tc_dispatch, test_dispatch_fail_001, SIGABRT); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/token and leaky bucket.txt b/3rdparty/openpgm-svn-r1085/pgm/token and leaky bucket.txt new file mode 100644 index 0000000..3efb9c7 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/token and leaky bucket.txt @@ -0,0 +1,12 @@ +leaky bucket: + + if (BUCKET->rate_limit > BUCKET->rate_per_sec) + BUCKET->rate_limit = BUCKET->rate_per_sec; + + +token bucket: + + guint bucket_limit; + + if (BUCKET->rate_limit > BUCKET->bucket_limit) + BUCKET->rate_limit = BUCKET->bucket_limit; diff --git a/3rdparty/openpgm-svn-r1085/pgm/tsi.c b/3rdparty/openpgm-svn-r1085/pgm/tsi.c new file mode 100644 index 0000000..5f9ae85 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/tsi.c @@ -0,0 +1,119 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * transport session ID helper functions. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + + +//#define TSI_DEBUG + + +/* locals */ + + +/* re-entrant form of pgm_tsi_print() + * + * returns number of bytes written to buffer on success, returns -1 on + * invalid parameters. + */ + +int +pgm_tsi_print_r ( + const pgm_tsi_t* restrict tsi, + char* restrict buf, + size_t bufsize + ) +{ + pgm_return_val_if_fail (NULL != tsi, -1); + pgm_return_val_if_fail (NULL != buf, -1); + pgm_return_val_if_fail (bufsize > 0, -1); + + const uint8_t* gsi = (const uint8_t*)tsi; + const uint16_t source_port = tsi->sport; + + return snprintf (buf, bufsize, "%i.%i.%i.%i.%i.%i.%i", + gsi[0], gsi[1], gsi[2], gsi[3], gsi[4], gsi[5], ntohs (source_port)); +} + +/* transform TSI to ASCII string form. + * + * on success, returns pointer to ASCII string. on error, returns NULL. + */ + +char* +pgm_tsi_print ( + const pgm_tsi_t* tsi + ) +{ + pgm_return_val_if_fail (tsi != NULL, NULL); + + static char buf[PGM_TSISTRLEN]; + pgm_tsi_print_r (tsi, buf, sizeof(buf)); + return buf; +} + +/* create hash value of TSI for use with GLib hash tables. + * + * on success, returns a hash value corresponding to the TSI. on error, fails + * on assert. + */ + +pgm_hash_t +pgm_tsi_hash ( + const void* p + ) +{ + const union { + pgm_tsi_t tsi; + uint32_t l[2]; + } *u = p; + +/* pre-conditions */ + pgm_assert (NULL != p); + + return u->l[0] ^ u->l[1]; +} + +/* compare two transport session identifier TSI values. + * + * returns TRUE if they are equal, FALSE if they are not. + */ + +bool +pgm_tsi_equal ( + const void* restrict p1, + const void* restrict p2 + ) +{ + const union { + pgm_tsi_t tsi; + uint32_t l[2]; + uint64_t ll; + } *restrict u1 = p1, *restrict u2 = p2; + +/* pre-conditions */ + pgm_assert (NULL != p1); + pgm_assert (NULL != p2); + + return (u1->l[0] == u2->l[0] && u1->l[1] == u2->l[1]); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/tsi_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/tsi_unittest.c new file mode 100644 index 0000000..fddff25 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/tsi_unittest.c @@ -0,0 +1,185 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for transport session ID helper functions. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include + + +/* mock state */ + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + + +#define TSI_DEBUG +#include "tsi.c" + + +/* target: + * gchar* + * pgm_tsi_print ( + * const pgm_tsi_t* tsi + * ) + */ + +START_TEST (test_print_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + fail_if (NULL == pgm_tsi_print (&tsi), "print failed"); +} +END_TEST + +START_TEST (test_print_pass_002) +{ + fail_unless (NULL == pgm_tsi_print (NULL), "print failed"); +} +END_TEST + +/* target: + * int + * pgm_tsi_print_r ( + * const pgm_tsi_t* tsi, + * char* buf, + * gsize bufsize + * ) + */ + +START_TEST (test_print_r_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + char buf[PGM_TSISTRLEN]; + fail_unless (pgm_tsi_print_r (&tsi, buf, sizeof(buf)) > 0, "print_r failed"); +} +END_TEST + +START_TEST (test_print_r_pass_002) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + char buf[PGM_TSISTRLEN]; + fail_unless (pgm_tsi_print_r (NULL, buf, sizeof(buf)) == -1, "print_r failed"); + fail_unless (pgm_tsi_print_r (&tsi, NULL, sizeof(buf)) == -1, "print_r failed"); + fail_unless (pgm_tsi_print_r (&tsi, buf, 0) == -1, "print_r failed"); +} +END_TEST + +/* target: + * gboolean + * pgm_tsi_equal ( + * gconstpointer tsi1, + * gconstpointer tsi2 + * ) + */ + +START_TEST (test_equal_pass_001) +{ + const pgm_tsi_t tsi1 = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const pgm_tsi_t tsi2 = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + fail_unless (pgm_tsi_equal (&tsi1, &tsi2), "equal failed"); +} +END_TEST + +START_TEST (test_equal_pass_002) +{ + const pgm_tsi_t tsi1 = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const pgm_tsi_t tsi2 = { { 9, 8, 7, 6, 5, 4 }, 2000 }; + fail_if (pgm_tsi_equal (&tsi1, &tsi2), "equal failed"); +} +END_TEST + +START_TEST (test_equal_fail_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + gboolean retval = pgm_tsi_equal (NULL, &tsi); + fail ("reached"); +} +END_TEST + +START_TEST (test_equal_fail_002) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + gboolean retval = pgm_tsi_equal (&tsi, NULL); + fail ("reached"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_print = tcase_create ("print"); + suite_add_tcase (s, tc_print); + tcase_add_test (tc_print, test_print_pass_001); + tcase_add_test (tc_print, test_print_pass_002); + + TCase* tc_print_r = tcase_create ("print-r"); + suite_add_tcase (s, tc_print_r); + tcase_add_test (tc_print_r, test_print_r_pass_001); + tcase_add_test (tc_print_r, test_print_r_pass_002); + + TCase* tc_equal = tcase_create ("equal"); + suite_add_tcase (s, tc_equal); + tcase_add_test (tc_equal, test_equal_pass_001); + tcase_add_test (tc_equal, test_equal_pass_002); + tcase_add_test_raise_signal (tc_equal, test_equal_fail_001, SIGABRT); + tcase_add_test_raise_signal (tc_equal, test_equal_fail_002, SIGABRT); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/txw.c b/3rdparty/openpgm-svn-r1085/pgm/txw.c new file mode 100644 index 0000000..e2487ae --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/txw.c @@ -0,0 +1,763 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * A basic transmit window: pointer array implementation. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include + + +//#define TXW_DEBUG + +#ifndef TXW_DEBUG +# define PGM_DISABLE_ASSERT +#endif + + +/* testing function: is TSI null + * + * returns TRUE if null, returns FALSE if not null. + */ + +static inline +bool +pgm_tsi_is_null ( + const void*const tsi + ) +{ + const union { + pgm_tsi_t tsi; + uint32_t l[2]; + } *u = tsi; + +/* pre-conditions */ + pgm_assert (NULL != tsi); + + return (0 == u->l[0] && 0 == u->l[1]); +} + +/* returns the pointer at the given index of the window. responsibility + * is with the caller to verify a single user ownership. + */ + +static inline +struct pgm_sk_buff_t* +_pgm_txw_peek ( + const pgm_txw_t*const window, + const uint32_t sequence + ) +{ + struct pgm_sk_buff_t* skb; + +/* pre-conditions */ + pgm_assert (NULL != window); + + if (pgm_txw_is_empty (window)) + return NULL; + + if (pgm_uint32_gte (sequence, window->trail) && pgm_uint32_lte (sequence, window->lead)) + { + const uint_fast32_t index_ = sequence % pgm_txw_max_length (window); + skb = window->pdata[index_]; + pgm_assert (NULL != skb); + pgm_assert (pgm_skb_is_valid (skb)); + pgm_assert (pgm_tsi_is_null (&skb->tsi)); + } + else + skb = NULL; + + return skb; +} + +/* testing function: can a request be peeked from the retransmit queue. + * + * returns TRUE if request is available, returns FALSE if not available. + */ + +static inline +bool +pgm_txw_retransmit_can_peek ( + pgm_txw_t*const window + ) +{ + pgm_return_val_if_fail (NULL != window, FALSE); + return (NULL != pgm_txw_retransmit_try_peek (window)); +} + +/* sequence state must be smaller than PGM skbuff control buffer */ +PGM_STATIC_ASSERT(sizeof(struct pgm_txw_state_t) <= sizeof(((struct pgm_sk_buff_t*)0)->cb)); + +uint32_t +pgm_txw_get_unfolded_checksum ( + const struct pgm_sk_buff_t*const skb + ) +{ + const pgm_txw_state_t*const state = (const pgm_txw_state_t*const)&skb->cb; + return state->unfolded_checksum; +} + +void +pgm_txw_set_unfolded_checksum ( + struct pgm_sk_buff_t*const skb, + const uint32_t csum + ) +{ + pgm_txw_state_t* state = (pgm_txw_state_t*)&skb->cb; + state->unfolded_checksum = csum; +} + +void +pgm_txw_inc_retransmit_count ( + struct pgm_sk_buff_t*const skb + ) +{ + pgm_txw_state_t*const state = (pgm_txw_state_t*const)&skb->cb; + state->retransmit_count++; +} + +bool +pgm_txw_retransmit_is_empty ( + const pgm_txw_t*const window + ) +{ + pgm_assert (NULL != window); + return pgm_queue_is_empty (&window->retransmit_queue); +} + + +/* globals */ + +static void pgm_txw_remove_tail (pgm_txw_t*const); +static bool pgm_txw_retransmit_push_parity (pgm_txw_t*const, const uint32_t, const uint8_t); +static bool pgm_txw_retransmit_push_selective (pgm_txw_t*const, const uint32_t); + + +/* constructor for transmit window. zero-length windows are not permitted. + * + * returns pointer to window. + */ + +pgm_txw_t* +pgm_txw_create ( + const pgm_tsi_t*const tsi, + const uint16_t tpdu_size, + const uint32_t sqns, /* transmit window size in sequence numbers */ + const unsigned secs, /* size in seconds */ + const ssize_t max_rte, /* max bandwidth */ + const bool use_fec, + const uint8_t rs_n, + const uint8_t rs_k + ) +{ + pgm_txw_t* window; + +/* pre-conditions */ + pgm_assert (NULL != tsi); + if (sqns) { + pgm_assert_cmpuint (tpdu_size, ==, 0); + pgm_assert_cmpuint (sqns, >, 0); + pgm_assert_cmpuint (sqns & PGM_UINT32_SIGN_BIT, ==, 0); + pgm_assert_cmpuint (secs, ==, 0); + pgm_assert_cmpuint (max_rte, ==, 0); + } else { + pgm_assert_cmpuint (tpdu_size, >, 0); + pgm_assert_cmpuint (secs, >, 0); + pgm_assert_cmpuint (max_rte, >, 0); + } + if (use_fec) { + pgm_assert_cmpuint (rs_n, >, 0); + pgm_assert_cmpuint (rs_k, >, 0); + } + + pgm_debug ("create (tsi:%s max-tpdu:%" PRIu16 " sqns:%" PRIu32 " secs %u max-rte %zd use-fec:%s rs(n):%u rs(k):%u)", + pgm_tsi_print (tsi), + tpdu_size, sqns, secs, max_rte, + use_fec ? "YES" : "NO", + rs_n, rs_k); + +/* calculate transmit window parameters */ + pgm_assert (sqns || (tpdu_size && secs && max_rte)); + const unsigned alloc_sqns = sqns ? sqns : ( (secs * max_rte) / tpdu_size ); + window = pgm_malloc0 (sizeof(pgm_txw_t) + ( alloc_sqns * sizeof(struct pgm_sk_buff_t*) )); + window->tsi = tsi; + +/* empty state for transmission group boundaries to align. + * + * trail = 0, lead = -1 + */ + window->lead = -1; + window->trail = window->lead + 1; + +/* reed-solomon forward error correction */ + if (use_fec) { + window->parity_buffer = pgm_alloc_skb (tpdu_size); + window->tg_sqn_shift = pgm_power2_log2 (rs_k); + pgm_rs_create (&window->rs, rs_n, rs_k); + window->is_fec_enabled = 1; + } + +/* pointer array */ + window->alloc = alloc_sqns; + +/* post-conditions */ + pgm_assert_cmpuint (pgm_txw_max_length (window), ==, alloc_sqns); + pgm_assert_cmpuint (pgm_txw_length (window), ==, 0); + pgm_assert_cmpuint (pgm_txw_size (window), ==, 0); + pgm_assert (pgm_txw_is_empty (window)); + pgm_assert (!pgm_txw_is_full (window)); + pgm_assert (!pgm_txw_retransmit_can_peek (window)); + + return window; +} + +/* destructor for transmit window. must not be called more than once for same window. + */ + +void +pgm_txw_shutdown ( + pgm_txw_t*const window + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert_cmpuint (window->alloc, >, 0); + + pgm_debug ("shutdown (window:%p)", (const void*)window); + +/* contents of window */ + while (!pgm_txw_is_empty (window)) { + pgm_txw_remove_tail (window); + } + +/* window must now be empty */ + pgm_assert_cmpuint (pgm_txw_length (window), ==, 0); + pgm_assert_cmpuint (pgm_txw_size (window), ==, 0); + pgm_assert (pgm_txw_is_empty (window)); + pgm_assert (!pgm_txw_is_full (window)); + +/* retransmit queue must be empty */ + pgm_assert (!pgm_txw_retransmit_can_peek (window)); + +/* free reed-solomon state */ + if (window->is_fec_enabled) { + pgm_free_skb (window->parity_buffer); + pgm_rs_destroy (&window->rs); + } + +/* window */ + pgm_free (window); +} + +/* add skb to transmit window, taking ownership. window does not grow. + * PGM skbuff data/tail pointers must point to the PGM payload, and hence skb->len + * is allowed to be zero. + * + * side effects: + * + * 1) sequence number is set in skb. + * 2) window is updated with new skb. + * + * no return value. fatal error raised on invalid parameters. if window is full then + * an entry is dropped to fulfil the request. + * + * it is an error to try to free the skb after adding to the window. + */ + +void +pgm_txw_add ( + pgm_txw_t* const restrict window, + struct pgm_sk_buff_t* const restrict skb /* cannot be NULL */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != skb); + pgm_assert_cmpuint (pgm_txw_max_length (window), >, 0); + pgm_assert (pgm_skb_is_valid (skb)); + pgm_assert (((const pgm_list_t*)skb)->next == NULL); + pgm_assert (((const pgm_list_t*)skb)->prev == NULL); + pgm_assert (pgm_tsi_is_null (&skb->tsi)); + pgm_assert ((char*)skb->data > (char*)skb->head); + pgm_assert ((sizeof(struct pgm_header) + sizeof(struct pgm_data)) <= (size_t)((char*)skb->data - (char*)skb->head)); + + pgm_debug ("add (window:%p skb:%p)", (const char*)window, (const char*)skb); + + if (pgm_txw_is_full (window)) + { +/* transmit window advancement scheme dependent action here */ + pgm_txw_remove_tail (window); + } + +/* generate new sequence number */ + pgm_atomic_inc32 (&window->lead); + skb->sequence = window->lead; + +/* add skb to window */ + const uint_fast32_t index_ = skb->sequence % pgm_txw_max_length (window); + window->pdata[index_] = skb; + +/* statistics */ + window->size += skb->len; + +/* post-conditions */ + pgm_assert_cmpuint (pgm_txw_length (window), >, 0); + pgm_assert_cmpuint (pgm_txw_length (window), <=, pgm_txw_max_length (window)); +} + +/* peek an entry from the window for retransmission. + * + * returns pointer to skbuff on success, returns NULL on invalid parameters. + */ + +struct pgm_sk_buff_t* +pgm_txw_peek ( + const pgm_txw_t*const window, + const uint32_t sequence + ) +{ + pgm_debug ("peek (window:%p sequence:%" PRIu32 ")", + (const void*)window, sequence); + return _pgm_txw_peek (window, sequence); +} + +/* remove an entry from the trailing edge of the transmit window. + */ + +static +void +pgm_txw_remove_tail ( + pgm_txw_t* const window + ) +{ + struct pgm_sk_buff_t* skb; + pgm_txw_state_t* state; + + pgm_debug ("pgm_txw_remove_tail (window:%p)", (const void*)window); + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (!pgm_txw_is_empty (window)); + + skb = _pgm_txw_peek (window, pgm_txw_trail (window)); + pgm_assert (NULL != skb); + pgm_assert (pgm_skb_is_valid (skb)); + pgm_assert (pgm_tsi_is_null (&skb->tsi)); + + state = (pgm_txw_state_t*)&skb->cb; + if (state->waiting_retransmit) { + pgm_queue_unlink (&window->retransmit_queue, (pgm_list_t*)skb); + state->waiting_retransmit = 0; + } + +/* statistics */ + window->size -= skb->len; + if (state->retransmit_count > 0) { + PGM_HISTOGRAM_COUNTS("Tx.RetransmitCount", state->retransmit_count); + } + if (state->nak_elimination_count > 0) { + PGM_HISTOGRAM_COUNTS("Tx.NakEliminationCount", state->nak_elimination_count); + } + +/* remove reference to skb */ + if (PGM_UNLIKELY(pgm_mem_gc_friendly)) { + const uint_fast32_t index_ = skb->sequence % pgm_txw_max_length (window); + window->pdata[index_] = NULL; + } + pgm_free_skb (skb); + +/* advance trailing pointer */ + pgm_atomic_inc32 (&window->trail); + +/* post-conditions */ + pgm_assert (!pgm_txw_is_full (window)); +} + +/* Try to add a sequence number to the retransmit queue, ignore if + * already there or no longer in the transmit window. + * + * For parity NAKs, we deal on the transmission group sequence number + * rather than the packet sequence number. To simplify managment we + * use the leading window packet to store the details of the entire + * transmisison group. Parity NAKs are ignored if the packet count is + * less than or equal to the count already queued for retransmission. + * + * returns FALSE if request was eliminated, returns TRUE if request was + * added to queue. + */ + +bool +pgm_txw_retransmit_push ( + pgm_txw_t* const window, + const uint32_t sequence, + const bool is_parity, /* parity NAK ⇒ sequence_number = transmission group | packet count */ + const uint8_t tg_sqn_shift + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert_cmpuint (tg_sqn_shift, <, 8 * sizeof(uint32_t)); + + pgm_debug ("retransmit_push (window:%p sequence:%" PRIu32 " is_parity:%s tg_sqn_shift:%u)", + (const void*)window, sequence, is_parity ? "TRUE" : "FALSE", tg_sqn_shift); + +/* early elimination */ + if (pgm_txw_is_empty (window)) + return FALSE; + + if (is_parity) + { + return pgm_txw_retransmit_push_parity (window, sequence, tg_sqn_shift); + } + else + { + return pgm_txw_retransmit_push_selective (window, sequence); + } +} + +static +bool +pgm_txw_retransmit_push_parity ( + pgm_txw_t* const window, + const uint32_t sequence, + const uint8_t tg_sqn_shift + ) +{ + struct pgm_sk_buff_t* skb; + pgm_txw_state_t* state; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert_cmpuint (tg_sqn_shift, <, 8 * sizeof(uint32_t)); + + const uint32_t tg_sqn_mask = 0xffffffff << tg_sqn_shift; + const uint32_t nak_tg_sqn = sequence & tg_sqn_mask; /* left unshifted */ + const uint32_t nak_pkt_cnt = sequence & ~tg_sqn_mask; + skb = _pgm_txw_peek (window, nak_tg_sqn); + + if (NULL == skb) { + pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Transmission group lead #%" PRIu32 " not in window."), nak_tg_sqn); + return FALSE; + } + + pgm_assert (pgm_skb_is_valid (skb)); + pgm_assert (pgm_tsi_is_null (&skb->tsi)); + state = (pgm_txw_state_t*)&skb->cb; + +/* check if request can be eliminated */ + if (state->waiting_retransmit) + { + pgm_assert (NULL != ((const pgm_list_t*)skb)->next); + pgm_assert (NULL != ((const pgm_list_t*)skb)->prev); + if (state->pkt_cnt_requested < nak_pkt_cnt) { +/* more parity packets requested than currently scheduled, simply bump up the count */ + state->pkt_cnt_requested = nak_pkt_cnt; + } + state->nak_elimination_count++; + return FALSE; + } + else + { + pgm_assert (((const pgm_list_t*)skb)->next == NULL); + pgm_assert (((const pgm_list_t*)skb)->prev == NULL); + } + +/* new request */ + state->pkt_cnt_requested++; + pgm_queue_push_head_link (&window->retransmit_queue, (pgm_list_t*)skb); + pgm_assert (!pgm_queue_is_empty (&window->retransmit_queue)); + state->waiting_retransmit = 1; + return TRUE; +} + +static +bool +pgm_txw_retransmit_push_selective ( + pgm_txw_t* const window, + const uint32_t sequence + ) +{ + struct pgm_sk_buff_t* skb; + pgm_txw_state_t* state; + +/* pre-conditions */ + pgm_assert (NULL != window); + + skb = _pgm_txw_peek (window, sequence); + if (NULL == skb) { + pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Requested packet #%" PRIu32 " not in window."), sequence); + return FALSE; + } + + pgm_assert (pgm_skb_is_valid (skb)); + pgm_assert (pgm_tsi_is_null (&skb->tsi)); + state = (pgm_txw_state_t*)&skb->cb; + +/* check if request can be eliminated */ + if (state->waiting_retransmit) { + pgm_assert (!pgm_queue_is_empty (&window->retransmit_queue)); + state->nak_elimination_count++; + return FALSE; + } + + pgm_assert (((const pgm_list_t*)skb)->next == NULL); + pgm_assert (((const pgm_list_t*)skb)->prev == NULL); + +/* new request */ + pgm_queue_push_head_link (&window->retransmit_queue, (pgm_list_t*)skb); + pgm_assert (!pgm_queue_is_empty (&window->retransmit_queue)); + state->waiting_retransmit = 1; + return TRUE; +} + +/* try to peek a request from the retransmit queue + * + * return pointer of first skb in queue, or return NULL if the queue is empty. + */ + +struct pgm_sk_buff_t* +pgm_txw_retransmit_try_peek ( + pgm_txw_t* const window + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + + pgm_debug ("retransmit_try_peek (window:%p)", (const void*)window); + +/* no lock required to detect presence of a request */ + pgm_list_t* tail_link = pgm_queue_peek_tail_link (&window->retransmit_queue); + if (PGM_UNLIKELY(NULL == tail_link)) { + pgm_debug ("retransmit queue empty on peek."); + return NULL; + } + + struct pgm_sk_buff_t* skb = (struct pgm_sk_buff_t*)tail_link; + pgm_assert (pgm_skb_is_valid (skb)); + pgm_txw_state_t* state = (pgm_txw_state_t*)&skb->cb; + + if (!state->waiting_retransmit) { + pgm_assert (((const pgm_list_t*)skb)->next == NULL); + pgm_assert (((const pgm_list_t*)skb)->prev == NULL); + } +/* packet payload still in transit */ + if (PGM_UNLIKELY(1 != pgm_atomic_read32 (&skb->users))) { + pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Retransmit sqn #%" PRIu32 " is still in transit in transmit thread."), skb->sequence); + return NULL; + } + if (!state->pkt_cnt_requested) { + return skb; + } + +/* generate parity packet to satisify request */ + const uint8_t rs_h = state->pkt_cnt_sent % (window->rs.n - window->rs.k); + const uint32_t tg_sqn_mask = 0xffffffff << window->tg_sqn_shift; + const uint32_t tg_sqn = skb->sequence & tg_sqn_mask; + bool is_var_pktlen = FALSE; + bool is_op_encoded = FALSE; + uint16_t parity_length = 0; + const pgm_gf8_t* src[ window->rs.k ]; + for (uint_fast8_t i = 0; i < window->rs.k; i++) + { + const struct pgm_sk_buff_t* odata_skb = pgm_txw_peek (window, tg_sqn + i); + const uint16_t odata_tsdu_length = ntohs (odata_skb->pgm_header->pgm_tsdu_length); + if (!parity_length) + { + parity_length = odata_tsdu_length; + } + else if (odata_tsdu_length != parity_length) + { + is_var_pktlen = TRUE; + if (odata_tsdu_length > parity_length) + parity_length = odata_tsdu_length; + } + + src[i] = odata_skb->data; + if (odata_skb->pgm_header->pgm_options & PGM_OPT_PRESENT) { + is_op_encoded = TRUE; + } + } + +/* construct basic PGM header to be completed by send_rdata() */ + skb = window->parity_buffer; + skb->data = skb->tail = skb->head = skb + 1; + +/* space for PGM header */ + pgm_skb_put (skb, sizeof(struct pgm_header)); + + skb->pgm_header = skb->data; + skb->pgm_data = (void*)( skb->pgm_header + 1 ); + memcpy (skb->pgm_header->pgm_gsi, &window->tsi->gsi, sizeof(pgm_gsi_t)); + skb->pgm_header->pgm_options = PGM_OPT_PARITY; + +/* append actual TSDU length if variable length packets, zero pad as necessary. + */ + if (is_var_pktlen) + { + skb->pgm_header->pgm_options |= PGM_OPT_VAR_PKTLEN; + + for (uint_fast8_t i = 0; i < window->rs.k; i++) + { + struct pgm_sk_buff_t* odata_skb = pgm_txw_peek (window, tg_sqn + i); + const uint16_t odata_tsdu_length = ntohs (odata_skb->pgm_header->pgm_tsdu_length); + + pgm_assert (odata_tsdu_length == odata_skb->len); + pgm_assert (parity_length >= odata_tsdu_length); + + if (!odata_skb->zero_padded) { + memset (odata_skb->tail, 0, parity_length - odata_tsdu_length); + *(uint16_t*)((char*)odata_skb->data + parity_length) = odata_tsdu_length; + odata_skb->zero_padded = 1; + } + } + parity_length += 2; + } + + skb->pgm_header->pgm_tsdu_length = htons (parity_length); + +/* space for DATA */ + pgm_skb_put (skb, sizeof(struct pgm_data) + parity_length); + + skb->pgm_data->data_sqn = htonl ( tg_sqn | rs_h ); + + void* data_bytes = skb->pgm_data + 1; + +/* encode every option separately, currently only one applies: opt_fragment + */ + if (is_op_encoded) + { + skb->pgm_header->pgm_options |= PGM_OPT_PRESENT; + + struct pgm_opt_fragment null_opt_fragment; + const pgm_gf8_t* opt_src[ window->rs.k ]; + memset (&null_opt_fragment, 0, sizeof(null_opt_fragment)); + *(uint8_t*)&null_opt_fragment |= PGM_OP_ENCODED_NULL; + for (uint_fast8_t i = 0; i < window->rs.k; i++) + { + const struct pgm_sk_buff_t* odata_skb = pgm_txw_peek (window, tg_sqn + i); + + if (odata_skb->pgm_opt_fragment) + { + pgm_assert (odata_skb->pgm_header->pgm_options & PGM_OPT_PRESENT); +/* skip three bytes of header */ + opt_src[i] = (pgm_gf8_t*)((char*)odata_skb->pgm_opt_fragment + sizeof (struct pgm_opt_header)); + } + else + { + opt_src[i] = (pgm_gf8_t*)&null_opt_fragment; + } + } + +/* add options to this rdata packet */ + const uint16_t opt_total_length = sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment); + +/* add space for PGM options */ + pgm_skb_put (skb, opt_total_length); + + struct pgm_opt_length* opt_len = data_bytes; + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( opt_total_length ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_FRAGMENT | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_fragment); + opt_header->opt_reserved = PGM_OP_ENCODED; + struct pgm_opt_fragment* opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); + +/* The cast below is the correct way to handle the problem. + * The (void *) cast is to avoid a GCC warning like: + * + * "warning: dereferencing type-punned pointer will break strict-aliasing rules" + */ + pgm_rs_encode (&window->rs, + opt_src, + window->rs.k + rs_h, + (pgm_gf8_t*)((char*)opt_fragment + sizeof(struct pgm_opt_header)), + sizeof(struct pgm_opt_fragment) - sizeof(struct pgm_opt_header)); + + data_bytes = opt_fragment + 1; + } + +/* encode payload */ + pgm_rs_encode (&window->rs, + src, + window->rs.k + rs_h, + data_bytes, + parity_length); + +/* calculate partial checksum */ + const uint16_t tsdu_length = ntohs (skb->pgm_header->pgm_tsdu_length); + state->unfolded_checksum = pgm_csum_partial ((char*)skb->tail - tsdu_length, tsdu_length, 0); + return skb; +} + +/* remove head entry from retransmit queue, will fail on assertion if queue is empty. + */ + +void +pgm_txw_retransmit_remove_head ( + pgm_txw_t* const window + ) +{ + struct pgm_sk_buff_t* skb; + pgm_txw_state_t* state; + +/* pre-conditions */ + pgm_assert (NULL != window); + + pgm_debug ("retransmit_remove_head (window:%p)", + (const void*)window); + +/* tail link is valid without lock */ + pgm_list_t* tail_link = pgm_queue_peek_tail_link (&window->retransmit_queue); + +/* link must be valid for pop */ + pgm_assert (NULL != tail_link); + + skb = (struct pgm_sk_buff_t*)tail_link; + pgm_assert (pgm_skb_is_valid (skb)); + pgm_assert (pgm_tsi_is_null (&skb->tsi)); + state = (pgm_txw_state_t*)&skb->cb; + if (!state->waiting_retransmit) + { + pgm_assert (((const pgm_list_t*)skb)->next == NULL); + pgm_assert (((const pgm_list_t*)skb)->prev == NULL); + } + if (state->pkt_cnt_requested) + { + state->pkt_cnt_sent++; + +/* remove if all requested parity packets have been sent */ + if (state->pkt_cnt_sent == state->pkt_cnt_requested) { + pgm_queue_pop_tail_link (&window->retransmit_queue); + state->waiting_retransmit = 0; + } + } + else /* selective request */ + { + pgm_queue_pop_tail_link (&window->retransmit_queue); + state->waiting_retransmit = 0; + } +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/txw_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/txw_unittest.c new file mode 100644 index 0000000..bec079b --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/txw_unittest.c @@ -0,0 +1,743 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for transmit window. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + + +/* mock global */ + +#define pgm_histogram_add mock_pgm_histogram_add +#define pgm_rs_create mock_pgm_rs_create +#define pgm_rs_destroy mock_pgm_rs_destroy +#define pgm_rs_encode mock_pgm_rs_encode +#define pgm_compat_csum_partial mock_pgm_compat_csum_partial +#define pgm_histogram_init mock_pgm_histogram_init + +#define TXW_DEBUG +#include "txw.c" + + +/** reed-solomon module */ +void +mock_pgm_rs_create ( + pgm_rs_t* rs, + uint8_t n, + uint8_t k + ) +{ +} + +void +mock_pgm_rs_destroy ( + pgm_rs_t* rs + ) +{ +} + +void +mock_pgm_rs_encode( + pgm_rs_t* rs, + const pgm_gf8_t** src, + const uint8_t offset, + pgm_gf8_t* dst, + const uint16_t len + ) +{ +} + +/** checksum module */ +uint32_t +mock_pgm_compat_csum_partial ( + const void* addr, + uint16_t len, + uint32_t csum + ) +{ + return 0x0; +} + +void +mock_pgm_histogram_init ( + pgm_histogram_t* histogram + ) +{ +} + +void +mock_pgm_histogram_add ( + pgm_histogram_t* histogram, + int value + ) +{ +} + + +/* mock functions for external references */ + +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return 0; +} + + +/* generate valid skb, data pointer pointing to PGM payload + */ +static +struct pgm_sk_buff_t* +generate_valid_skb (void) +{ + const guint16 tsdu_length = 1000; + const guint16 header_length = sizeof(struct pgm_header) + sizeof(struct pgm_data); + struct pgm_sk_buff_t* skb = pgm_alloc_skb (1500); +/* fake but valid transport and timestamp */ + skb->sock = (pgm_sock_t*)0x1; + skb->tstamp = 1; +/* header */ + pgm_skb_reserve (skb, header_length); + memset (skb->head, 0, header_length); + skb->pgm_header = (struct pgm_header*)skb->head; + skb->pgm_data = (struct pgm_data*)(skb->pgm_header + 1); + skb->pgm_header->pgm_type = PGM_ODATA; + skb->pgm_header->pgm_tsdu_length = g_htons (tsdu_length); +/* DATA */ + pgm_skb_put (skb, tsdu_length); + return skb; +} + +/* target: + * pgm_txw_t* + * pgm_txw_create ( + * const pgm_tsi_t* const tsi, + * const guint16 tpdu_size, + * const guint32 sqns, + * const guint secs, + * const guint max_rte, + * const gboolean use_fec, + * const guint rs_n, + * const guint rs_k + * ) + */ + +/* vanilla sequence count window */ +START_TEST (test_create_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + fail_if (NULL == pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0), "create failed"); +} +END_TEST + +/* vanilla time based window */ +START_TEST (test_create_pass_002) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + fail_if (NULL == pgm_txw_create (&tsi, 1500, 0, 60, 800000, FALSE, 0, 0), "create failed"); +} +END_TEST + +/* jumbo frame */ +START_TEST (test_create_pass_003) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + fail_if (NULL == pgm_txw_create (&tsi, 9000, 0, 60, 800000, FALSE, 0, 0), "create failed"); +} +END_TEST + +/* max frame */ +START_TEST (test_create_pass_004) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + fail_if (NULL == pgm_txw_create (&tsi, UINT16_MAX, 0, 60, 800000, FALSE, 0, 0), "create failed"); +} +END_TEST + +/* invalid tpdu size */ +START_TEST (test_create_fail_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const pgm_txw_t* window = pgm_txw_create (&tsi, 0, 0, 60, 800000, FALSE, 0, 0); + fail ("reached"); +} +END_TEST + +/* no specified sequence count or time value */ +START_TEST (test_create_fail_002) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const pgm_txw_t* window = pgm_txw_create (&tsi, 0, 0, 0, 800000, FALSE, 0, 0); + fail ("reached"); +} +END_TEST + +/* no specified rate */ +START_TEST (test_create_fail_003) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const pgm_txw_t* window = pgm_txw_create (&tsi, 0, 0, 60, 0, FALSE, 0, 0); + fail ("reached"); +} +END_TEST + +/* all invalid */ +START_TEST (test_create_fail_004) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const pgm_txw_t* window = pgm_txw_create (NULL, 0, 0, 0, 0, FALSE, 0, 0); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_txw_shutdown ( + * pgm_txw_t* const window + * ) + */ + +START_TEST (test_shutdown_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + pgm_txw_shutdown (window); +} +END_TEST + +START_TEST (test_shutdown_fail_001) +{ + pgm_txw_shutdown (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_txw_add ( + * pgm_txw_t* const window, + * struct pgm_sk_buff_t* const skb + * ) + * failures raise assert errors and stop process execution. + */ + +START_TEST (test_add_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + pgm_txw_shutdown (window); +} +END_TEST + +/* null skb */ +START_TEST (test_add_fail_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + pgm_txw_add (window, NULL); + fail ("reached"); +} +END_TEST + +/* null window */ +START_TEST (test_add_fail_002) +{ + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (NULL, skb); + fail ("reached"); +} +END_TEST + +/* null skb content */ +START_TEST (test_add_fail_003) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + char buffer[1500]; + memset (buffer, 0, sizeof(buffer)); + pgm_txw_add (window, (struct pgm_sk_buff_t*)buffer); + fail ("reached"); +} +END_TEST + +/* target: + * struct pgm_sk_buff_t* + * pgm_txw_peek ( + * pgm_txw_t* const window, + * const guint32 sequence + * ) + */ + +START_TEST (test_peek_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + fail_unless (skb == pgm_txw_peek (window, window->trail), "peek failed"); + pgm_txw_shutdown (window); +} +END_TEST + +/* null window */ +START_TEST (test_peek_fail_001) +{ + const struct pgm_sk_buff_t* skb = pgm_txw_peek (NULL, 0); + fail ("reached"); +} +END_TEST + +/* empty window */ +START_TEST (test_peek_fail_002) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + fail_unless (NULL == pgm_txw_peek (window, window->trail), "peek failed"); + pgm_txw_shutdown (window); +} +END_TEST + +/** inline function tests **/ +/* pgm_txw_max_length () + */ +START_TEST (test_max_length_pass_001) +{ + const guint window_length = 100; + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, window_length, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + fail_unless (window_length == pgm_txw_max_length (window), "max_length failed"); + pgm_txw_shutdown (window); +} +END_TEST + +START_TEST (test_max_length_fail_001) +{ + const size_t answer = pgm_txw_max_length (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_txw_length () + */ +START_TEST (test_length_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + fail_unless (0 == pgm_txw_length (window), "length failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + fail_unless (1 == pgm_txw_length (window), "length failed"); + pgm_txw_shutdown (window); +} +END_TEST + +START_TEST (test_length_fail_001) +{ + const uint32_t answer = pgm_txw_length (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_txw_size () + */ +START_TEST (test_size_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + fail_unless (0 == pgm_txw_size (window), "size failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + fail_unless (1000 == pgm_txw_size (window), "size failed"); + pgm_txw_shutdown (window); +} +END_TEST + +START_TEST (test_size_fail_001) +{ + const size_t answer = pgm_txw_size (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_txw_is_empty + */ +START_TEST (test_is_empty_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + fail_unless (pgm_txw_is_empty (window), "is_empty failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + fail_if (pgm_txw_is_empty (window), "is_empty failed"); + pgm_txw_shutdown (window); +} +END_TEST + +START_TEST (test_is_empty_fail_001) +{ + const bool answer = pgm_txw_is_empty (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_txw_is_full + */ +START_TEST (test_is_full_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 1, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + fail_if (pgm_txw_is_full (window), "is_full failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + fail_unless (pgm_txw_is_full (window), "is_full failed"); + pgm_txw_shutdown (window); +} +END_TEST + +START_TEST (test_is_full_fail_001) +{ + const bool answer = pgm_txw_is_full (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_txw_lead + */ +START_TEST (test_lead_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + guint32 lead = pgm_txw_lead (window); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + fail_unless (lead + 1 == pgm_txw_lead (window), "lead failed"); + pgm_txw_shutdown (window); +} +END_TEST + +START_TEST (test_lead_fail_001) +{ + const uint32_t answer = pgm_txw_lead (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_txw_next_lead + */ +START_TEST (test_next_lead_pass_001) +{ + const guint window_length = 100; + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, window_length, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + guint32 next_lead = pgm_txw_next_lead (window); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + fail_unless (next_lead == pgm_txw_lead (window), "lead failed"); + pgm_txw_shutdown (window); +} +END_TEST + +START_TEST (test_next_lead_fail_001) +{ + const uint32_t answer = pgm_txw_next_lead (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_txw_trail + */ +START_TEST (test_trail_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 1, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); +/* does not advance with adding skb */ + guint32 trail = pgm_txw_trail (window); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + fail_unless (trail == pgm_txw_trail (window), "trail failed"); +/* does advance when filling up window */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + fail_if (trail == pgm_txw_trail (window), "trail failed"); + pgm_txw_shutdown (window); +} +END_TEST + +START_TEST (test_trail_fail_001) +{ + const uint32_t answer = pgm_txw_trail (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_txw_retransmit_push ( + * pgm_txw_t* const window, + * const uint32_t sequence, + * const bool is_parity, + * const uint8_t tg_sqn_shift + * ) + */ + +START_TEST (test_retransmit_push_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); +/* empty window invalidates all requests */ + fail_unless (FALSE == pgm_txw_retransmit_push (window, window->trail, FALSE, 0), "retransmit_push failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); +/* first request */ + fail_unless (TRUE == pgm_txw_retransmit_push (window, window->trail, FALSE, 0), "retransmit_push failed"); +/* second request eliminated */ + fail_unless (FALSE == pgm_txw_retransmit_push (window, window->trail, FALSE, 0), "retransmit_push failed"); + pgm_txw_shutdown (window); +} +END_TEST + +START_TEST (test_retransmit_push_fail_001) +{ + const bool answer = pgm_txw_retransmit_push (NULL, 0, FALSE, 0); + fail ("reached"); +} +END_TEST + +/* target: + * struct pgm_sk_buff_t* + * pgm_txw_retransmit_try_peek ( + * pgm_txw_t* const window + * ) + */ + +START_TEST (test_retransmit_try_peek_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + fail_unless (1 == pgm_txw_retransmit_push (window, window->trail, FALSE, 0), "retransmit_push failed"); + fail_unless (NULL != pgm_txw_retransmit_try_peek (window), "retransmit_try_peek failed"); + pgm_txw_shutdown (window); +} +END_TEST + +/* null window */ +START_TEST (test_retransmit_try_peek_fail_001) +{ + const struct pgm_sk_buff_t* skb = pgm_txw_retransmit_try_peek (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_txw_retransmit_remove_head ( + * pgm_txw_t* const window + * ) + */ + +START_TEST (test_retransmit_remove_head_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + fail_unless (1 == pgm_txw_retransmit_push (window, window->trail, FALSE, 0), "retransmit_push failed"); + fail_unless (NULL != pgm_txw_retransmit_try_peek (window), "retransmit_try_peek failed"); + pgm_txw_retransmit_remove_head (window); + pgm_txw_shutdown (window); +} +END_TEST + +/* null window */ +START_TEST (test_retransmit_remove_head_fail_001) +{ + pgm_txw_retransmit_remove_head (NULL); + fail ("reached"); +} +END_TEST + +/* empty retransmit queue */ +START_TEST (test_retransmit_remove_head_fail_002) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + pgm_txw_retransmit_remove_head (window); + fail ("reached"); +} +END_TEST + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_create = tcase_create ("create"); + suite_add_tcase (s, tc_create); + tcase_add_test (tc_create, test_create_pass_001); + tcase_add_test (tc_create, test_create_pass_002); + tcase_add_test (tc_create, test_create_pass_003); + tcase_add_test (tc_create, test_create_pass_004); + tcase_add_test_raise_signal (tc_create, test_create_fail_001, SIGABRT); + tcase_add_test_raise_signal (tc_create, test_create_fail_002, SIGABRT); + tcase_add_test_raise_signal (tc_create, test_create_fail_003, SIGABRT); + tcase_add_test_raise_signal (tc_create, test_create_fail_004, SIGABRT); + + TCase* tc_shutdown = tcase_create ("shutdown"); + suite_add_tcase (s, tc_shutdown); + tcase_add_test (tc_shutdown, test_shutdown_pass_001); + tcase_add_test_raise_signal (tc_shutdown, test_shutdown_fail_001, SIGABRT); + + TCase* tc_add = tcase_create ("add"); + suite_add_tcase (s, tc_add); + tcase_add_test (tc_add, test_add_pass_001); + tcase_add_test_raise_signal (tc_add, test_add_fail_001, SIGABRT); + tcase_add_test_raise_signal (tc_add, test_add_fail_002, SIGABRT); + tcase_add_test_raise_signal (tc_add, test_add_fail_003, SIGABRT); + + TCase* tc_peek = tcase_create ("peek"); + suite_add_tcase (s, tc_peek); + tcase_add_test (tc_peek, test_peek_pass_001); + tcase_add_test_raise_signal (tc_peek, test_peek_fail_001, SIGABRT); +/* logical not fatal errors */ + tcase_add_test (tc_peek, test_peek_fail_002); + + TCase* tc_max_length = tcase_create ("max-length"); + suite_add_tcase (s, tc_max_length); + tcase_add_test (tc_max_length, test_max_length_pass_001); + tcase_add_test_raise_signal (tc_max_length, test_max_length_fail_001, SIGABRT); + + TCase* tc_length = tcase_create ("length"); + suite_add_tcase (s, tc_length); + tcase_add_test (tc_length, test_length_pass_001); + tcase_add_test_raise_signal (tc_length, test_length_fail_001, SIGABRT); + + TCase* tc_size = tcase_create ("size"); + suite_add_tcase (s, tc_size); + tcase_add_test (tc_size, test_size_pass_001); + tcase_add_test_raise_signal (tc_size, test_size_fail_001, SIGABRT); + + TCase* tc_is_empty = tcase_create ("is-empty"); + suite_add_tcase (s, tc_is_empty); + tcase_add_test (tc_is_empty, test_is_empty_pass_001); + tcase_add_test_raise_signal (tc_is_empty, test_is_empty_fail_001, SIGABRT); + TCase* tc_is_full = tcase_create ("is-full"); + suite_add_tcase (s, tc_is_full); + tcase_add_test (tc_is_full, test_is_full_pass_001); + tcase_add_test_raise_signal (tc_is_full, test_is_full_fail_001, SIGABRT); + + TCase* tc_lead = tcase_create ("lead"); + suite_add_tcase (s, tc_lead); + tcase_add_test (tc_lead, test_lead_pass_001); + tcase_add_test_raise_signal (tc_lead, test_lead_fail_001, SIGABRT); + + TCase* tc_next_lead = tcase_create ("next-lead"); + suite_add_tcase (s, tc_next_lead); + tcase_add_test (tc_next_lead, test_next_lead_pass_001); + tcase_add_test_raise_signal (tc_next_lead, test_next_lead_fail_001, SIGABRT); + + TCase* tc_trail = tcase_create ("trail"); + suite_add_tcase (s, tc_trail); + tcase_add_test (tc_trail, test_trail_pass_001); + tcase_add_test_raise_signal (tc_trail, test_trail_fail_001, SIGABRT); + + TCase* tc_retransmit_push = tcase_create ("retransmit-push"); + suite_add_tcase (s, tc_retransmit_push); + tcase_add_test (tc_retransmit_push, test_retransmit_push_pass_001); + tcase_add_test_raise_signal (tc_retransmit_push, test_retransmit_push_fail_001, SIGABRT); + + TCase* tc_retransmit_try_peek = tcase_create ("retransmit-try-peek"); + suite_add_tcase (s, tc_retransmit_try_peek); + tcase_add_test (tc_retransmit_try_peek, test_retransmit_try_peek_pass_001); + tcase_add_test_raise_signal (tc_retransmit_try_peek, test_retransmit_try_peek_fail_001, SIGABRT); + + TCase* tc_retransmit_remove_head = tcase_create ("retransmit-remove-head"); + suite_add_tcase (s, tc_retransmit_remove_head); + tcase_add_test (tc_retransmit_remove_head, test_retransmit_remove_head_pass_001); + tcase_add_test_raise_signal (tc_retransmit_remove_head, test_retransmit_remove_head_fail_001, SIGABRT); + tcase_add_test_raise_signal (tc_retransmit_remove_head, test_retransmit_remove_head_fail_002, SIGABRT); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/valgrind.supp b/3rdparty/openpgm-svn-r1085/pgm/valgrind.supp new file mode 100644 index 0000000..ea74d2d --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/valgrind.supp @@ -0,0 +1,147 @@ +##----------------------------------------------------------------------## +## Suppressions to run OpenPGM + +{ + miru-glib-hack-1 + Memcheck:Leak + fun:memalign + fun:posix_memalign + obj:/usr/lib/libglib-2.0.so* + fun:g_slice_alloc +} + +{ + miru-glib-hack-2 + Memcheck:Leak + fun:calloc + fun:g_malloc0 + obj:/usr/lib/libglib-2.0.so* + fun:g_slice_alloc +} + +{ + miru-glib-hack-2b + Memcheck:Leak + fun:malloc + fun:g_malloc + obj:/usr/lib/libglib-2.0.so* + fun:g_slice_alloc +} + +{ + miru-glib-hack-3 + Memcheck:Leak + fun:malloc + fun:realloc + fun:g_realloc + obj:/usr/lib/libglib-2.0.so* + fun:g_ptr_array_add + fun:g_main_context_check + obj:/usr/lib/libglib-2.0.so* + fun:g_main_loop_run +} + +{ + miru-glib-hack-4 + Memcheck:Leak + fun:realloc + fun:g_realloc + obj:/usr/lib/libglib-2.0.so* + fun:g_array_set_size + fun:g_static_private_set + fun:g_get_language_names +} + +{ + miru-glib-hack-5 + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_slice_alloc + fun:g_array_sized_new + fun:g_static_private_set + fun:g_get_charset + fun:g_log_default_handler + fun:g_logv + fun:g_log +} + +{ + miru-glib-hack-6 + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_log_set_handler +} + +{ + miru-glib-hack-7 + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:g_thread_self + fun:g_thread_init_glib +} + + + +## Annoying libc errors + +{ + miru-libc-hack-1 + Memcheck:Addr8 + obj:/lib/ld-2.*.so + obj:/lib/ld-2.*.so + obj:/lib/libc-2.*.so + obj:/lib/ld-2.*.so + fun:__libc_dlopen_mode + fun:__nss_lookup_function + obj:/lib/libc-2.*.so + fun:getprotobyname_r +} + +{ + miru-libc-hack-1b + Memcheck:Addr8 + obj:/lib/ld-2.*.so + obj:/lib/ld-2.*.so + obj:/lib/ld-2.*.so + obj:/lib/ld-2.*.so + obj:/lib/ld-2.*.so + obj:/lib/ld-2.*.so + obj:/lib/ld-2.*.so + obj:/lib/libc-2.*.so + obj:/lib/ld-2.*.so + fun:__libc_dlopen_mode + fun:__nss_lookup_function + obj:/lib/libc-2.*.so + fun:getprotobyname_r +} + +{ + miru-libc-hack-2 + Memcheck:Cond + obj:/lib/ld-2.*.so + obj:/lib/ld-2.*.so + obj:/lib/libc-2.*.so + obj:/lib/ld-2.*.so + fun:__libc_dlsym + fun:__nss_lookup_function + obj:/lib/libc-2.*.so + fun:getaddrinfo +} + +{ + miru-libc-hack-3 + Memcheck:Addr8 + obj:/lib/ld-2.*.so + obj:/lib/ld-2.*.so + obj:/lib/ld-2.*.so + obj:/lib/ld-2.*.so + obj:/lib/libc-2.*.so + obj:/lib/ld-2.*.so + fun:__libc_dlsym + fun:__nss_lookup_function + obj:/lib/libc-2.*.so + fun:getaddrinfo +} diff --git a/3rdparty/openpgm-svn-r1085/pgm/version_generator.py b/3rdparty/openpgm-svn-r1085/pgm/version_generator.py new file mode 100755 index 0000000..0db781b --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/version_generator.py @@ -0,0 +1,52 @@ +#!/usr/bin/python + +import os +import platform +import time + +build_date = time.strftime ("%Y-%m-%d") +build_time = time.strftime ("%H:%M:%S") +build_rev = os.popen('svnversion -n .').read(); + +print """ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * OpenPGM version. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + + +/* globals */ + +const unsigned pgm_major_version = 5; +const unsigned pgm_minor_version = 0; +const unsigned pgm_micro_version = 70; +const char* pgm_build_date = "%s"; +const char* pgm_build_time = "%s"; +const char* pgm_build_system = "%s"; +const char* pgm_build_machine = "%s"; +const char* pgm_build_revision = "%s"; + + +/* eof */ +"""%(build_date, build_time, platform.system(), platform.machine(), build_rev) + +# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/win/mingw32-runtime_3.13-1openpgm3.diff b/3rdparty/openpgm-svn-r1085/pgm/win/mingw32-runtime_3.13-1openpgm3.diff new file mode 100644 index 0000000..5b860a1 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/win/mingw32-runtime_3.13-1openpgm3.diff @@ -0,0 +1,136 @@ +diff -urN include-original/mswsock.h include/mswsock.h +--- include-original/mswsock.h 2009-08-21 22:41:22.000000000 +0800 ++++ include/mswsock.h 2010-01-21 17:31:14.662159471 +0800 +@@ -83,23 +83,19 @@ + } WSAMSG, *PWSAMSG, *LPWSAMSG; + + +-/* According to MSDN docs, the WSAMSG.Control buffer starts with a +- cmsghdr header of the following form. See also RFC 2292. */ +- +-typedef struct wsacmsghdr { +- UINT cmsg_len; +- INT cmsg_level; +- INT cmsg_type; +- /* followed by UCHAR cmsg_data[]; */ +-} WSACMSGHDR; +- +-/* TODO: Standard Posix.1g macros as per RFC 2292, with WSA_uglification. */ +-#if 0 +-#define WSA_CMSG_FIRSTHDR(mhdr) +-#define WSA_CMSG_NXTHDR(mhdr, cmsg) +-#define WSA_CMSG_SPACE(length) +-#define WSA_CMSG_LEN(length) +-#endif ++ typedef struct _WSACMSGHDR { ++ SIZE_T cmsg_len; ++ INT cmsg_level; ++ INT cmsg_type; ++ } WSACMSGHDR,*PWSACMSGHDR,*LPWSACMSGHDR; ++ ++#define WSA_CMSGHDR_ALIGN(length) (((length) + TYPE_ALIGNMENT(WSACMSGHDR)-1) & (~(TYPE_ALIGNMENT(WSACMSGHDR)-1))) ++#define WSA_CMSGDATA_ALIGN(length) (((length) + MAX_NATURAL_ALIGNMENT-1) & (~(MAX_NATURAL_ALIGNMENT-1))) ++#define WSA_CMSG_FIRSTHDR(msg) (((msg)->Control.len >= sizeof(WSACMSGHDR)) ? (LPWSACMSGHDR)(msg)->Control.buf : (LPWSACMSGHDR)NULL) ++#define WSA_CMSG_NXTHDR(msg,cmsg) ((!(cmsg)) ? WSA_CMSG_FIRSTHDR(msg) : ((((u_char *)(cmsg) + WSA_CMSGHDR_ALIGN((cmsg)->cmsg_len) + sizeof(WSACMSGHDR)) > (u_char *)((msg)->Control.buf) + (msg)->Control.len) ? (LPWSACMSGHDR)NULL : (LPWSACMSGHDR)((u_char *)(cmsg) + WSA_CMSGHDR_ALIGN((cmsg)->cmsg_len)))) ++#define WSA_CMSG_DATA(cmsg) ((u_char *)(cmsg) + WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR))) ++#define WSA_CMSG_SPACE(length) (WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR) + WSA_CMSGHDR_ALIGN(length))) ++#define WSA_CMSG_LEN(length) (WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR)) + length) ++ ++typedef INT (WINAPI * LPFN_WSARECVMSG)(SOCKET, LPWSAMSG, LPDWORD, LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE); ++ + BOOL PASCAL DisconnectEx(SOCKET,LPOVERLAPPED,DWORD,DWORD); + int PASCAL WSARecvMsg(SOCKET,LPWSAMSG,LPDWORD,LPWSAOVERLAPPED,LPWSAOVERLAPPED_COMPLETION_ROUTINE); + +diff -urN include-original/ws2tcpip.h include/ws2tcpip.h +--- include-original/ws2tcpip.h 2009-08-21 22:41:42.000000000 +0800 ++++ include/ws2tcpip.h 2009-08-21 22:42:15.000000000 +0800 +@@ -78,6 +78,18 @@ + + #define UDP_NOCHECKSUM 1 + ++/* RFC 3768 */ ++#define MCAST_JOIN_GROUP 41 ++#define MCAST_LEAVE_GROUP 42 ++#define MCAST_BLOCK_SOURCE 43 ++#define MCAST_UNBLOCK_SOURCE 44 ++#define MCAST_JOIN_SOURCE_GROUP 45 ++#define MCAST_LEAVE_SOURCE_GROUP 46 ++#define MCAST_MSFILTER 47 ++ ++#define MCAST_EXCLUDE 0 ++#define MCAST_INCLUDE 1 ++ + /* INTERFACE_INFO iiFlags */ + #define IFF_UP 1 + #define IFF_BROADCAST 2 +@@ -104,6 +116,7 @@ + #define AI_PASSIVE 1 + #define AI_CANONNAME 2 + #define AI_NUMERICHOST 4 ++#define AI_ADDRCONFIG 0x20 + + /* getaddrinfo error codes */ + #define EAI_AGAIN WSATRY_AGAIN +@@ -132,6 +145,25 @@ + struct in_addr imr_interface; + }; + ++struct group_req { ++ u_long gr_interface; ++ struct sockaddr_storage gr_group; ++}; ++ ++struct group_source_req { ++ u_long gsr_interface; ++ struct sockaddr_storage gsr_group; ++ struct sockaddr_storage gsr_source; ++}; ++ ++struct group_filter { ++ u_long gf_interface; ++ struct sockaddr_storage gf_group; ++ u_long gf_fmode; ++ u_long gf_numsrc; ++ struct in_addr gf_slist[1]; ++}; ++ + struct ip_msfilter { + struct in_addr imsf_multiaddr; + struct in_addr imsf_interface; +@@ -356,6 +388,13 @@ + sockaddr_gen iiNetmask; + } INTERFACE_INFO, *LPINTERFACE_INFO; + ++typedef struct _INTERFACE_INFO_EX { ++ u_long iiFlags; ++ SOCKET_ADDRESS iiAddress; ++ SOCKET_ADDRESS iiBroadcastAddress; ++ SOCKET_ADDRESS iiNetmask; ++} INTERFACE_INFO_EX, *_LPINTERFACE_INFO_EX; ++ + /* + The definition above can cause problems on NT4,prior to sp4. + To workaround, include the following struct and typedef and +--- include-original/winnt.h 2009-08-21 22:41:42.000000000 +0800 ++++ include/winnt.h 2010-01-21 17:33:56.366162880 +0800 +@@ -43,6 +43,20 @@ + #define UNALIGNED + #endif + ++#ifdef _WIN64 ++#define MAX_NATURAL_ALIGNMENT sizeof(ULONGLONG) ++#define MEMORY_ALLOCATION_ALIGNMENT 16 ++#else ++#define MAX_NATURAL_ALIGNMENT sizeof(DWORD) ++#define MEMORY_ALLOCATION_ALIGNMENT 8 ++#endif ++ ++#ifdef __cplusplus ++#define TYPE_ALIGNMENT(t) __alignof__ (t) ++#else ++#define TYPE_ALIGNMENT(t) FIELD_OFFSET(struct { char x; t test; },test) ++#endif ++ + #ifndef DECLSPEC_ALIGN + #ifdef __GNUC__ + #define DECLSPEC_ALIGN(x) __attribute__((aligned(x))) diff --git a/3rdparty/openpgm-svn-r1085/pgm/win/mingw32-runtime_3.15.2-0openpgm1.diff b/3rdparty/openpgm-svn-r1085/pgm/win/mingw32-runtime_3.15.2-0openpgm1.diff new file mode 100644 index 0000000..b6e3d11 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/win/mingw32-runtime_3.15.2-0openpgm1.diff @@ -0,0 +1,135 @@ +diff -urN include-original/mswsock.h include/mswsock.h +--- include-original/mswsock.h 2009-06-30 16:32:31.000000000 +0800 ++++ include/mswsock.h 2010-03-23 20:34:12.000000000 +0800 +@@ -83,23 +83,20 @@ + } WSAMSG, *PWSAMSG, *LPWSAMSG; + + +-/* According to MSDN docs, the WSAMSG.Control buffer starts with a +- cmsghdr header of the following form. See also RFC 2292. */ +- +-typedef struct wsacmsghdr { +- UINT cmsg_len; +- INT cmsg_level; +- INT cmsg_type; +- /* followed by UCHAR cmsg_data[]; */ +-} WSACMSGHDR; +- +-/* TODO: Standard Posix.1g macros as per RFC 2292, with WSA_uglification. */ +-#if 0 +-#define WSA_CMSG_FIRSTHDR(mhdr) +-#define WSA_CMSG_NXTHDR(mhdr, cmsg) +-#define WSA_CMSG_SPACE(length) +-#define WSA_CMSG_LEN(length) +-#endif ++typedef struct _WSACMSGHDR { ++ SIZE_T cmsg_len; ++ INT cmsg_level; ++ INT cmsg_type; ++} WSACMSGHDR,*PWSACMSGHDR,*LPWSACMSGHDR; ++ ++#define WSA_CMSGHDR_ALIGN(length) (((length) + TYPE_ALIGNMENT(WSACMSGHDR)-1) & (~(TYPE_ALIGNMENT(WSACMSGHDR)-1))) ++#define WSA_CMSGDATA_ALIGN(length) (((length) + MAX_NATURAL_ALIGNMENT-1) & (~(MAX_NATURAL_ALIGNMENT-1))) ++#define WSA_CMSG_FIRSTHDR(msg) (((msg)->Control.len >= sizeof(WSACMSGHDR)) ? (LPWSACMSGHDR)(msg)->Control.buf : (LPWSACMSGHDR)NULL) ++#define WSA_CMSG_NXTHDR(msg,cmsg) ((!(cmsg)) ? WSA_CMSG_FIRSTHDR(msg) : ((((u_char *)(cmsg) + WSA_CMSGHDR_ALIGN((cmsg)->cmsg_len) + sizeof(WSACMSGHDR)) > (u_char *)((msg)->Control.buf) + (msg)->Control.len) ? (LPWSACMSGHDR)NULL : (LPWSACMSGHDR)((u_char *)(cmsg) + WSA_CMSGHDR_ALIGN((cmsg)->cmsg_len)))) ++#define WSA_CMSG_DATA(cmsg) ((u_char *)(cmsg) + WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR))) ++#define WSA_CMSG_SPACE(length) (WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR) + WSA_CMSGHDR_ALIGN(length))) ++#define WSA_CMSG_LEN(length) (WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR)) + length) ++typedef INT (WINAPI * LPFN_WSARECVMSG)(SOCKET, LPWSAMSG, LPDWORD, LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE); + + BOOL PASCAL DisconnectEx(SOCKET,LPOVERLAPPED,DWORD,DWORD); + int PASCAL WSARecvMsg(SOCKET,LPWSAMSG,LPDWORD,LPWSAOVERLAPPED,LPWSAOVERLAPPED_COMPLETION_ROUTINE); +diff -urN include-original/winnt.h include/winnt.h +--- include-original/winnt.h 2009-06-30 16:32:32.000000000 +0800 ++++ include/winnt.h 2010-03-23 20:36:29.000000000 +0800 +@@ -43,6 +43,20 @@ + #define UNALIGNED + #endif + ++#ifdef _WIN64 ++#define MAX_NATURAL_ALIGNMENT sizeof(ULONGLONG) ++#define MEMORY_ALLOCATION_ALIGNMENT 16 ++#else ++#define MAX_NATURAL_ALIGNMENT sizeof(DWORD) ++#define MEMORY_ALLOCATION_ALIGNMENT 8 ++#endif ++ ++#ifdef __cplusplus ++#define TYPE_ALIGNMENT(t) __alignof__ (t) ++#else ++#define TYPE_ALIGNMENT(t) FIELD_OFFSET(struct { char x; t test; },test) ++#endif ++ + #ifndef DECLSPEC_ALIGN + #ifdef __GNUC__ + #define DECLSPEC_ALIGN(x) __attribute__((aligned(x))) +diff -urN include-original/ws2tcpip.h include/ws2tcpip.h +--- include-original/ws2tcpip.h 2009-06-30 16:32:32.000000000 +0800 ++++ include/ws2tcpip.h 2010-03-23 20:35:59.000000000 +0800 +@@ -78,6 +78,18 @@ + + #define UDP_NOCHECKSUM 1 + ++/* RFC 3768 */ ++#define MCAST_JOIN_GROUP 41 ++#define MCAST_LEAVE_GROUP 42 ++#define MCAST_BLOCK_SOURCE 43 ++#define MCAST_UNBLOCK_SOURCE 44 ++#define MCAST_JOIN_SOURCE_GROUP 45 ++#define MCAST_LEAVE_SOURCE_GROUP 46 ++#define MCAST_MSFILTER 47 ++ ++#define MCAST_EXCLUDE 0 ++#define MCAST_INCLUDE 1 ++ + /* INTERFACE_INFO iiFlags */ + #define IFF_UP 1 + #define IFF_BROADCAST 2 +@@ -104,6 +116,7 @@ + #define AI_PASSIVE 1 + #define AI_CANONNAME 2 + #define AI_NUMERICHOST 4 ++#define AI_ADDRCONFIG 0x20 + + /* getaddrinfo error codes */ + #define EAI_AGAIN WSATRY_AGAIN +@@ -132,6 +145,25 @@ + struct in_addr imr_interface; + }; + ++struct group_req { ++ u_long gr_interface; ++ struct sockaddr_storage gr_group; ++}; ++ ++struct group_source_req { ++ u_long gsr_interface; ++ struct sockaddr_storage gsr_group; ++ struct sockaddr_storage gsr_source; ++}; ++ ++struct group_filter { ++ u_long gf_interface; ++ struct sockaddr_storage gf_group; ++ u_long gf_fmode; ++ u_long gf_numsrc; ++ struct in_addr gf_slist[1]; ++}; ++ + struct ip_msfilter { + struct in_addr imsf_multiaddr; + struct in_addr imsf_interface; +@@ -356,6 +388,13 @@ + sockaddr_gen iiNetmask; + } INTERFACE_INFO, *LPINTERFACE_INFO; + ++typedef struct _INTERFACE_INFO_EX { ++ u_long iiFlags; ++ SOCKET_ADDRESS iiAddress; ++ SOCKET_ADDRESS iiBroadcastAddress; ++ SOCKET_ADDRESS iiNetmask; ++} INTERFACE_INFO_EX, *_LPINTERFACE_INFO_EX; ++ + /* + The definition above can cause problems on NT4,prior to sp4. + To workaround, include the following struct and typedef and diff --git a/3rdparty/openpgm-svn-r1085/pgm/win64/mingw-w64-bin_x86-64-linux_4.4.1-1openpgm1.diff b/3rdparty/openpgm-svn-r1085/pgm/win64/mingw-w64-bin_x86-64-linux_4.4.1-1openpgm1.diff new file mode 100644 index 0000000..237bcb3 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/win64/mingw-w64-bin_x86-64-linux_4.4.1-1openpgm1.diff @@ -0,0 +1,53 @@ +diff -urN include-original/./ws2tcpip.h x86_64-w64-mingw32/include/./ws2tcpip.h +--- include-original/./ws2tcpip.h 2009-09-10 13:36:49.000000000 +0800 ++++ x86_64-w64-mingw32/include/./ws2tcpip.h 2010-01-21 14:59:13.000000000 +0800 +@@ -12,6 +12,25 @@ + + #include + ++struct group_req { ++ u_long gr_interface; ++ struct sockaddr_storage gr_group; ++}; ++ ++struct group_source_req { ++ u_long gsr_interface; ++ struct sockaddr_storage gsr_group; ++ struct sockaddr_storage gsr_source; ++}; ++ ++struct group_filter { ++ u_long gf_interface; ++ struct sockaddr_storage gf_group; ++ u_long gf_fmode; ++ u_long gf_numsrc; ++ struct in_addr gf_slist[1]; ++}; ++ + struct ip_msfilter { + struct in_addr imsf_multiaddr; + struct in_addr imsf_interface; +@@ -22,6 +41,15 @@ + + #define IP_MSFILTER_SIZE(numsrc) (sizeof(struct ip_msfilter)-sizeof(struct in_addr) + (numsrc)*sizeof(struct in_addr)) + ++/* RFC 3768 */ ++#define MCAST_JOIN_GROUP 41 ++#define MCAST_LEAVE_GROUP 42 ++#define MCAST_BLOCK_SOURCE 43 ++#define MCAST_UNBLOCK_SOURCE 44 ++#define MCAST_JOIN_SOURCE_GROUP 45 ++#define MCAST_LEAVE_SOURCE_GROUP 46 ++#define MCAST_MSFILTER 47 ++ + #define MCAST_INCLUDE 0 + #define MCAST_EXCLUDE 1 + +@@ -277,6 +305,7 @@ + #define AI_PASSIVE 0x1 + #define AI_CANONNAME 0x2 + #define AI_NUMERICHOST 0x4 ++#define AI_ADDRCONFIG 0x20 + + #ifdef __cplusplus + extern "C" { diff --git a/3rdparty/openpgm-svn-r1085/pgm/wsastrerror.c b/3rdparty/openpgm-svn-r1085/pgm/wsastrerror.c new file mode 100644 index 0000000..2e21449 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/wsastrerror.c @@ -0,0 +1,372 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Winsock Error strings. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#ifdef _WIN32 +# include + + +char* +pgm_wsastrerror ( + const int wsa_errno + ) +{ + switch (wsa_errno) { +#ifdef WSA_INVALID_HANDLE + case WSA_INVALID_HANDLE: return _("Specified event object handle is invalid."); +#endif +#ifdef WSA_NOT_ENOUGH_MEMORY + case WSA_NOT_ENOUGH_MEMORY: return _("Insufficient memory available."); +#endif +#ifdef WSA_INVALID_PARAMETER + case WSA_INVALID_PARAMETER: return _("One or more parameters are invalid."); +#endif +#ifdef WSA_OPERATION_ABORTED + case WSA_OPERATION_ABORTED: return _("Overlapped operation aborted."); +#endif +#ifdef WSA_IO_INCOMPLETE + case WSA_IO_INCOMPLETE: return _("Overlapped I/O event object not in signaled state."); +#endif +#ifdef WSA_IO_PENDING + case WSA_IO_PENDING: return _("Overlapped operations will complete later."); +#endif +#ifdef WSAEINTR + case WSAEINTR: return _("Interrupted function call."); +#endif +#ifdef WSAEBADF + case WSAEBADF: return _("File handle is not valid."); +#endif +#ifdef WSAEACCES + case WSAEACCES: return _("Permission denied."); +#endif +#ifdef WSAEFAULT + case WSAEFAULT: return _("Bad address."); +#endif +#ifdef WSAEINVAL + case WSAEINVAL: return _("Invalid argument."); +#endif +#ifdef WSAEMFILE + case WSAEMFILE: return _("Too many open files."); +#endif +#ifdef WSAEWOULDBLOCK + case WSAEWOULDBLOCK: return _("Resource temporarily unavailable."); +#endif +#ifdef WSAEINPROGRESS + case WSAEINPROGRESS: return _("Operation now in progress."); +#endif +#ifdef WSAEALREADY + case WSAEALREADY: return _("Operation already in progress."); +#endif +#ifdef WSAENOTSOCK + case WSAENOTSOCK: return _("Socket operation on nonsocket."); +#endif +#ifdef WSAEDESTADDRREQ + case WSAEDESTADDRREQ: return _("Destination address required."); +#endif +#ifdef WSAEMSGSIZE + case WSAEMSGSIZE: return _("Message too long."); +#endif +#ifdef WSAEPROTOTYPE + case WSAEPROTOTYPE: return _("Protocol wrong type for socket."); +#endif +#ifdef WSAENOPROTOOPT + case WSAENOPROTOOPT: return _("Bad protocol option."); +#endif +#ifdef WSAEPROTONOSUPPORT + case WSAEPROTONOSUPPORT: return _("Protocol not supported."); +#endif +#ifdef WSAESOCKTNOSUPPORT + case WSAESOCKTNOSUPPORT: return _("Socket type not supported."); +#endif +#ifdef WSAEOPNOTSUPP + case WSAEOPNOTSUPP: return _("Operation not supported."); +#endif +#ifdef WSAEPFNOSUPPORT + case WSAEPFNOSUPPORT: return _("Protocol family not supported."); +#endif +#ifdef WSAEAFNOSUPPORT + case WSAEAFNOSUPPORT: return _("Address family not supported by protocol family."); +#endif +#ifdef WSAEADDRINUSE + case WSAEADDRINUSE: return _("Address already in use."); +#endif +#ifdef WSAEADDRNOTAVAIL + case WSAEADDRNOTAVAIL: return _("Cannot assign requested address."); +#endif +#ifdef WSAENETDOWN + case WSAENETDOWN: return _("Network is down."); +#endif +#ifdef WSAENETUNREACH + case WSAENETUNREACH: return _("Network is unreachable."); +#endif +#ifdef WSAENETRESET + case WSAENETRESET: return _("Network dropped connection on reset."); +#endif +#ifdef WSAECONNABORTED + case WSAECONNABORTED: return _("Software caused connection abort."); +#endif +#ifdef WSAECONNRESET + case WSAECONNRESET: return _("Connection reset by peer."); +#endif +#ifdef WSAENOBUFS + case WSAENOBUFS: return _("No buffer space available."); +#endif +#ifdef WSAEISCONN + case WSAEISCONN: return _("Socket is already connected."); +#endif +#ifdef WSAENOTCONN + case WSAENOTCONN: return _("Socket is not connected."); +#endif +#ifdef WSAESHUTDOWN + case WSAESHUTDOWN: return _("Cannot send after socket shutdown."); +#endif +#ifdef WSAETOOMANYREFS + case WSAETOOMANYREFS: return _("Too many references."); +#endif +#ifdef WSAETIMEDOUT + case WSAETIMEDOUT: return _("Connection timed out."); +#endif +#ifdef WSAECONNREFUSED + case WSAECONNREFUSED: return _("Connection refused."); +#endif +#ifdef WSAELOOP + case WSAELOOP: return _("Cannot translate name."); +#endif +#ifdef WSAENAMETOOLONG + case WSAENAMETOOLONG: return _("Name too long."); +#endif +#ifdef WSAEHOSTDOWN + case WSAEHOSTDOWN: return _("Host is down."); +#endif +#ifdef WSAEHOSTUNREACH + case WSAEHOSTUNREACH: return _("No route to host."); +#endif +#ifdef WSAENOTEMPTY + case WSAENOTEMPTY: return _("Directory not empty."); +#endif +#ifdef WSAEPROCLIM + case WSAEPROCLIM: return _("Too many processes."); +#endif +#ifdef WSAEUSERS + case WSAEUSERS: return _("User quota exceeded."); +#endif +#ifdef WSAEDQUOT + case WSAEDQUOT: return _("Disk quota exceeded."); +#endif +#ifdef WSAESTALE + case WSAESTALE: return _("Stale file handle reference."); +#endif +#ifdef WSAEREMOTE + case WSAEREMOTE: return _("Item is remote."); +#endif +#ifdef WSASYSNOTREADY + case WSASYSNOTREADY: return _("Network subsystem is unavailable."); +#endif +#ifdef WSAVERNOTSUPPORTED + case WSAVERNOTSUPPORTED: return _("Winsock.dll version out of range."); +#endif +#ifdef WSANOTINITIALISED + case WSANOTINITIALISED: return _("Successful WSAStartup not yet performed."); +#endif +#ifdef WSAEDISCON + case WSAEDISCON: return _("Graceful shutdown in progress."); +#endif +#ifdef WSAENOMORE + case WSAENOMORE: return _("No more results."); +#endif +#ifdef WSAECANCELLED + case WSAECANCELLED: return _("Call has been canceled."); +#endif +#ifdef WSAEINVALIDPROCTABLE + case WSAEINVALIDPROCTABLE: return _("Procedure call table is invalid."); +#endif +#ifdef WSAEINVALIDPROVIDER + case WSAEINVALIDPROVIDER: return _("Service provider is invalid."); +#endif +#ifdef WSAEPROVIDERFAILEDINIT + case WSAEPROVIDERFAILEDINIT: return _("Service provider failed to initialize."); +#endif +#ifdef WSASYSCALLFAILURE + case WSASYSCALLFAILURE: return _("System call failure."); +#endif +#ifdef WSASERVICE_NOT_FOUND + case WSASERVICE_NOT_FOUND: return _("Service not found."); +#endif +#ifdef WSATYPE_NOT_FOUND + case WSATYPE_NOT_FOUND: return _("Class type not found."); +#endif +#ifdef WSA_E_NO_MORE + case WSA_E_NO_MORE: return _("No more results."); +#endif +#ifdef WSA_E_CANCELLED + case WSA_E_CANCELLED: return _("Call was canceled."); +#endif +#ifdef WSAEREFUSED + case WSAEREFUSED: return _("Database query was refused."); +#endif +#ifdef WSAHOST_NOT_FOUND + case WSAHOST_NOT_FOUND: return _("Host not found."); +#endif +#ifdef WSATRY_AGAIN + case WSATRY_AGAIN: return _("Nonauthoritative host not found."); +#endif +#ifdef WSANO_RECOVERY + case WSANO_RECOVERY: return _("This is a nonrecoverable error."); +#endif +#ifdef WSANO_DATA + case WSANO_DATA: return _("Valid name, no data record of requested type."); +#endif +#ifdef WSA_QOS_RECEIVERS + case WSA_QOS_RECEIVERS: return _("QOS receivers."); +#endif +#ifdef WSA_QOS_SENDERS + case WSA_QOS_SENDERS: return _("QOS senders."); +#endif +#ifdef WSA_QOS_NO_SENDERS + case WSA_QOS_NO_SENDERS: return _("No QOS senders."); +#endif +#ifdef WSA_QOS_NO_RECEIVERS + case WSA_QOS_NO_RECEIVERS: return _("QOS no receivers."); +#endif +#ifdef WSA_QOS_REQUEST_CONFIRMED + case WSA_QOS_REQUEST_CONFIRMED: return _("QOS request confirmed."); +#endif +#ifdef WSA_QOS_ADMISSION_FAILURE + case WSA_QOS_ADMISSION_FAILURE: return _("QOS admission error."); +#endif +#ifdef WSA_QOS_POLICY_FAILURE + case WSA_QOS_POLICY_FAILURE: return _("QOS policy failure."); +#endif +#ifdef WSA_QOS_BAD_STYLE + case WSA_QOS_BAD_STYLE: return _("QOS bad style."); +#endif +#ifdef WSA_QOS_BAD_OBJECT + case WSA_QOS_BAD_OBJECT: return _("QOS bad object."); +#endif +#ifdef WSA_QOS_TRAFFIC_CTRL_ERROR + case WSA_QOS_TRAFFIC_CTRL_ERROR: return _("QOS traffic control error."); +#endif +#ifdef WSA_QOS_GENERIC_ERROR + case WSA_QOS_GENERIC_ERROR: return _("QOS generic error."); +#endif +#ifdef WSA_QOS_ESERVICETYPE + case WSA_QOS_ESERVICETYPE: return _("QOS service type error."); +#endif +#ifdef WSA_QOS_EFLOWSPEC + case WSA_QOS_EFLOWSPEC: return _("QOS flowspec error."); +#endif +#ifdef WSA_QOS_EPROVSPECBUF + case WSA_QOS_EPROVSPECBUF: return _("Invalid QOS provider buffer."); +#endif +#ifdef WSA_QOS_EFILTERSTYLE + case WSA_QOS_EFILTERSTYLE: return _("Invalid QOS filter style."); +#endif +#ifdef WSA_QOS_EFILTERTYPE + case WSA_QOS_EFILTERTYPE: return _("Invalid QOS filter type."); +#endif +#ifdef WSA_QOS_EFILTERCOUNT + case WSA_QOS_EFILTERCOUNT: return _("Incorrect QOS filter count."); +#endif +#ifdef WSA_QOS_EOBJLENGTH + case WSA_QOS_EOBJLENGTH: return _("Invalid QOS object length."); +#endif +#ifdef WSA_QOS_EFLOWCOUNT + case WSA_QOS_EFLOWCOUNT: return _("Incorrect QOS flow count."); +#endif +#ifdef WSA_QOS_EUNKOWNPSOBJ + case WSA_QOS_EUNKOWNPSOBJ: return _("Unrecognized QOS object."); +#endif +#ifdef WSA_QOS_EPOLICYOBJ + case WSA_QOS_EPOLICYOBJ: return _("Invalid QOS policy object."); +#endif +#ifdef WSA_QOS_EFLOWDESC + case WSA_QOS_EFLOWDESC: return _("Invalid QOS flow descriptor."); +#endif +#ifdef WSA_QOS_EPSFLOWSPEC + case WSA_QOS_EPSFLOWSPEC: return _("Invalid QOS provider-specific flowspec."); +#endif +#ifdef WSA_QOS_EPSFILTERSPEC + case WSA_QOS_EPSFILTERSPEC: return _("Invalid QOS provider-specific filterspec."); +#endif +#ifdef WSA_QOS_ESDMODEOBJ + case WSA_QOS_ESDMODEOBJ: return _("Invalid QOS shape discard mode object."); +#endif +#ifdef WSA_QOS_ESHAPERATEOBJ + case WSA_QOS_ESHAPERATEOBJ: return _("Invalid QOS shaping rate object."); +#endif +#ifdef WSA_QOS_RESERVED_PETYPE + case WSA_QOS_RESERVED_PETYPE: return _("Reserved policy QOS element type."); +#endif + default: return _("Unknown."); + } +} + +char* +pgm_adapter_strerror ( + const int adapter_errno + ) +{ + switch (adapter_errno) { +#ifdef ERROR_ADDRESS_NOT_ASSOCIATED + case ERROR_ADDRESS_NOT_ASSOCIATED: return _("DHCP lease information was available."); +#endif +#ifdef ERROR_BUFFER_OVERFLOW + case ERROR_BUFFER_OVERFLOW: return _("The buffer to receive the adapter information is too small."); +#endif +#ifdef ERROR_INVALID_DATA + case ERROR_INVALID_DATA: return _("Invalid adapter information was retrieved."); +#endif +#ifdef ERROR_INVALID_PARAMETER + case ERROR_INVALID_PARAMETER: return _("One of the parameters is invalid."); +#endif +#ifdef ERROR_NOT_ENOUGH_MEMORY + case ERROR_NOT_ENOUGH_MEMORY: return _("Insufficient memory resources are available to complete the operation."); +#endif +#ifdef ERROR_NO_DATA + case ERROR_NO_DATA: return _("No adapter information exists for the local computer."); +#endif +#ifdef ERROR_NOT_SUPPORTED + case ERROR_NOT_SUPPORTED: return _("The GetAdaptersInfo function is not supported by the operating system running on the local computer.."); +#endif + default: return _("Other."); + } +} + +char* +pgm_win_strerror ( + char* buf, + size_t buflen, + const int win_errno + ) +{ + const DWORD nSize = buflen; + FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, + NULL, /* source */ + win_errno, /* message id */ + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), /* language id */ + (LPTSTR)buf, + buflen, + NULL); /* arguments */ + return buf; +} +#endif /* _WIN32 */ + +/* eof */ diff --git a/OpenPGMConfig.cmake b/OpenPGMConfig.cmake index 074e65b..2729590 100644 --- a/OpenPGMConfig.cmake +++ b/OpenPGMConfig.cmake @@ -1,3 +1,8 @@ +# Set up build +SET(pgm_VERSION + svn-r1085 +) + INCLUDE(${CMAKE_ROOT}/Modules/FindPkgConfig.cmake) PKG_CHECK_MODULES(GLIB glib-2.0>=2.10) @@ -51,3 +56,8 @@ IF(NOT _SYSTEM_SPECIFICS_SET) MESSAGE(FATAL_ERROR "Can only build libpgm on Unix with gcc.") ENDIF(NOT _SYSTEM_SPECIFICS_SET) +INCLUDE_DIRECTORIES( + ${CMAKE_SOURCE_DIR}/3rdparty/openpgm-${pgm_VERSION}/pgm/include + ${GLIB_INCLUDES_DIR} +) + diff --git a/src/net/mcast/CMakeLists.txt b/src/net/mcast/CMakeLists.txt index e418c64..914c9d3 100644 --- a/src/net/mcast/CMakeLists.txt +++ b/src/net/mcast/CMakeLists.txt @@ -34,10 +34,6 @@ SET(pvsmcast_SRCS McastSender.cpp ) -INCLUDE_DIRECTORIES( - ${CMAKE_BINARY_DIR}/3rdparty/libpgm-src/openpgm/pgm/include -) - QT4_WRAP_CPP( pvsmcast_MOC_SRCS ${pvsmcast_MOC_HDRS} diff --git a/src/net/mcast/McastConfiguration.h b/src/net/mcast/McastConfiguration.h index 4e0e2ad..f10d487 100644 --- a/src/net/mcast/McastConfiguration.h +++ b/src/net/mcast/McastConfiguration.h @@ -156,7 +156,7 @@ public: quint16 multicastUDPMPort() const { - return _multicastUDPPortBase + 1; + return _multicastUDPPortBase; } void commit() diff --git a/src/net/mcast/McastConstants.h b/src/net/mcast/McastConstants.h index b4c71a5..624e195 100644 --- a/src/net/mcast/McastConstants.h +++ b/src/net/mcast/McastConstants.h @@ -26,10 +26,11 @@ #define DEFAULT_MULTICAST_DPORT 6965 #define DEFAULT_MULTICAST_USEUDP true #define DEFAULT_MULTICAST_UDPPORT 6966 +#define DEFAULT_MULTICAST_WSIZ 30 #define DEFAULT_MULTICAST_RATE (100*1024) -#define DEFAULT_MULTICAST_WSIZ 3000 #define DEFAULT_MULTICAST_MTU 1400 #define DEFAULT_MULTICAST_APDU 1200 #define DEFAULT_MULTICAST_CHUNK 1024 +#define DEFAULT_MULTICAST_SHUTDOWN_TIMEOUT 10000 #endif /* MCASTMAGIC_H_ */ diff --git a/src/net/mcast/McastPGMSocket.cpp b/src/net/mcast/McastPGMSocket.cpp index ba51444..8aee209 100644 --- a/src/net/mcast/McastPGMSocket.cpp +++ b/src/net/mcast/McastPGMSocket.cpp @@ -16,31 +16,27 @@ #include +#include +#include + #include +#include #include #include #include #include #include -// #include -// #define SIZE_MAX UINT64_MAX -// #include -// pgm redefined bool to int. Undo that. -#undef bool - -#include #include "McastPGMSocket.h" +using namespace std; + class McastPGMSocket_priv { public: McastPGMSocket_priv() : socket(0), - recv_notif(0), - repair_notif(0), - pending_notif(0), send_notif(0) { } @@ -48,37 +44,28 @@ public: { if (socket) pgm_close(socket, 0); - if (recv_notif) - delete recv_notif; - if (repair_notif) - delete repair_notif; - if (pending_notif) - delete pending_notif; + Q_FOREACH(QSocketNotifier* notif, _notifs) + { + delete notif; + } if (send_notif) delete send_notif; } pgm_sock_t* socket; McastPGMSocket::Direction direction; - QSocketNotifier* recv_notif; - QSocketNotifier* repair_notif; - QSocketNotifier* pending_notif; QSocketNotifier* send_notif; + QList _notifs; QSocketNotifier* notifier_for(int fd) { - if (recv_notif && (fd == recv_notif->socket())) - { - return recv_notif; - } - else if (repair_notif && (fd == repair_notif->socket())) - { - return repair_notif; - } - else if (pending_notif && (fd == pending_notif->socket())) - { - return pending_notif; - } - return 0; + Q_FOREACH(QSocketNotifier* notif, _notifs) + { + if(notif->socket() == fd) + { + return notif; + } + } + return 0; } }; @@ -102,7 +89,9 @@ McastPGMSocket::McastPGMSocket(QObject* parent) : _finished(false), _nakTimeout(new QTimer()), _dataTimeout(new QTimer()), - _sendTimeout(new QTimer()) + _sendTimeout(new QTimer()), + _shutdownTimer(0), + _shutdown_timeout(0) { _ensurePGMInited(); @@ -166,9 +155,11 @@ bool McastPGMSocket::open(McastConfiguration const* config, Direction direction) // write-only socket const int send_only = 1, spm_heartbeat[] = - { 16 * 1000, 16 * 1000, 16 * 1000, 16 * 1000, 32 * 1000, 64 * 1000, 128 - * 1000, 256 * 1000, 512 * 1000, 1024 * 1000, 2048 * 1000, 4096 - * 1000 }, + { 256 * 1000, + 512 * 1000, + 1024 * 1000, + 2048 * 1000, + 4096 * 1000 }, max_rate = config->multicastRate(), max_window = config->multicastWinSize(); // const int max_window_sqns = 3000; @@ -182,8 +173,8 @@ bool McastPGMSocket::open(McastConfiguration const* config, Direction direction) // Transmit window pgm_setsockopt(_priv->socket, PGM_TXW_MAX_RTE, &max_rate, sizeof(max_rate)); - // pgm_setsockopt(_priv->socket, PGM_TXW_SECS, &max_window, sizeof(max_window)); - pgm_setsockopt(_priv->socket, PGM_TXW_SQNS, &max_window, sizeof(max_window)); + pgm_setsockopt(_priv->socket, PGM_TXW_SECS, &max_window, sizeof(max_window)); + // pgm_setsockopt(_priv->socket, PGM_TXW_SQNS, &max_window, sizeof(max_window)); } else { @@ -191,41 +182,36 @@ bool McastPGMSocket::open(McastConfiguration const* config, Direction direction) const int recv_only = 1, passive = 0, max_window = config->multicastWinSize(), - max_winsqns = 0, + max_rate = config->multicastRate(), peer_expiry = ambient_spm * 5, spmr_expiry = 250 * 1000, nak_bo_ivl = 100 * 1000, - nak_rpt_ivl = 100 * 1000, - nak_rdata_ivl = 200 * 1000, + nak_rpt_ivl = 400 * 1000, + nak_rdata_ivl = 400 * 1000, nak_data_retries = 50, - nak_ncf_retries = 50; + nak_ncf_retries = 50, + no_rxw_sqns = 0; pgm_setsockopt(_priv->socket, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); pgm_setsockopt(_priv->socket, PGM_PASSIVE, &passive, sizeof(passive)); - // pgm_setsockopt(_priv->socket, PGM_RXW_SECS, &max_window, sizeof(max_window)); - pgm_setsockopt(_priv->socket, PGM_RXW_SQNS, &max_window, sizeof(max_window)); + pgm_setsockopt(_priv->socket, PGM_RXW_MAX_RTE, &max_rate, sizeof(max_rate)); + pgm_setsockopt(_priv->socket, PGM_RXW_SECS, &max_window, sizeof(max_window)); + pgm_setsockopt(_priv->socket, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); pgm_setsockopt(_priv->socket, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); - pgm_setsockopt(_priv->socket, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); pgm_setsockopt(_priv->socket, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); pgm_setsockopt(_priv->socket, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); pgm_setsockopt(_priv->socket, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); pgm_setsockopt(_priv->socket, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); pgm_setsockopt(_priv->socket, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + pgm_setsockopt(_priv->socket, PGM_RXW_SQNS, &no_rxw_sqns, sizeof(no_rxw_sqns)); } - // MTU + const int use_pgmcc = 1; + pgm_setsockopt(_priv->socket, PGM_USE_PGMCC, &use_pgmcc, sizeof(use_pgmcc)); + + // MTU int const mtu = config->multicastMTU(); pgm_setsockopt(_priv->socket, PGM_MTU, &mtu, sizeof(mtu)); - // UDP Encapsulation - if(config->multicastUseUDP()) - { - const quint16 uport = config->multicastUDPUPort(); - const quint16 mport = config->multicastUDPMPort(); - - pgm_setsockopt(_priv->socket, PGM_UDP_ENCAP_MCAST_PORT, &mport, sizeof(mport)); - pgm_setsockopt(_priv->socket, PGM_UDP_ENCAP_UCAST_PORT, &uport, sizeof(uport)); - } - pgm_sockaddr_t addr; addr.sa_addr.sport = config->multicastSPort(); addr.sa_port = config->multicastDPort(); @@ -237,7 +223,25 @@ bool McastPGMSocket::open(McastConfiguration const* config, Direction direction) return false; } - good = pgm_bind3(_priv->socket, &addr, sizeof(addr), (struct group_req*)&addrinfo->ai_send_addrs[0], sizeof(struct group_req), (struct group_req*)&addrinfo->ai_recv_addrs[0], sizeof(struct group_req), &err); + struct pgm_interface_req_t ifreq; + ifreq.ir_interface = addrinfo->ai_send_addrs[0].gsr_interface; + ifreq.ir_scope_id = 0; + if (AF_INET6 == family) + { + ifreq.ir_scope_id = ((struct sockaddr_in6*)&addrinfo->ai_send_addrs[0])->sin6_scope_id; + } + + // UDP Encapsulation + if(config->multicastUseUDP()) + { + const int uport = config->multicastUDPUPort(); + const int mport = config->multicastUDPMPort(); + + pgm_setsockopt(_priv->socket, PGM_UDP_ENCAP_MCAST_PORT, &mport, sizeof(mport)); + pgm_setsockopt(_priv->socket, PGM_UDP_ENCAP_UCAST_PORT, &uport, sizeof(uport)); + } + + good = pgm_bind3(_priv->socket, &addr, sizeof(addr), &ifreq , sizeof(ifreq), &ifreq, sizeof(ifreq), &err); if (!good) { qCritical() << "Could not bind socket: PGM Error: " << err->message; @@ -290,20 +294,15 @@ void McastPGMSocket::setupNotifiers() int recv_sock, repair_sock, pending_sock; char const* slotname = (_priv->direction == PSOCK_WRITE) ? SLOT(handleNak(int)) : SLOT(handleData(int)); - pgm_getsockopt(_priv->socket, PGM_RECV_SOCK, &recv_sock, sizeof(recv_sock)); - _priv->recv_notif = new QSocketNotifier(recv_sock, QSocketNotifier::Read, - this); - connect(_priv->recv_notif, SIGNAL(activated(int)), this, slotname); - - pgm_getsockopt(_priv->socket, PGM_REPAIR_SOCK, &repair_sock, sizeof(repair_sock)); - _priv->repair_notif = new QSocketNotifier(repair_sock, - QSocketNotifier::Read, this); - connect(_priv->repair_notif, SIGNAL(activated(int)), this, slotname); - - pgm_getsockopt(_priv->socket, PGM_PENDING_SOCK, &pending_sock, sizeof(pending_sock)); - _priv->pending_notif = new QSocketNotifier(pending_sock, - QSocketNotifier::Read, this); - connect(_priv->pending_notif, SIGNAL(activated(int)), this, slotname); + struct pollfd pollin[10]; + int in_nfds = 10; + pgm_poll_info(_priv->socket, pollin, &in_nfds, POLLIN); + for(int i = 0; i < in_nfds; i++) + { + QSocketNotifier* notif = new QSocketNotifier(pollin[i].fd, QSocketNotifier::Read, this); + _priv->_notifs.append(notif); + connect(notif, SIGNAL(activated(int)), this, slotname); + } if(_priv->direction == PSOCK_WRITE) { @@ -317,11 +316,17 @@ void McastPGMSocket::setupNotifiers() void McastPGMSocket::handleNak(int fd) { - qDebug() << "handleNak(int)"; + qDebug() << "handleNak(" << fd << ")"; QSocketNotifier* notif = _priv->notifier_for(fd); notif->setEnabled(false); + if (_shutdownTimer) + { + _shutdownTimer->start(_shutdown_timeout); + qDebug() << "Started shutdown timer"; + } + handleNak(); notif->setEnabled(true); @@ -334,7 +339,7 @@ void McastPGMSocket::handleNak() qDebug() << "handleNak()"; - QTimer::singleShot(1000, this, SLOT(handleNakTimeout())); + // QTimer::singleShot(1000, this, SLOT(handleNakTimeout())); // to handle NAKs in OpenPGM, we need to pgm_recv: char buf[4096]; @@ -349,7 +354,8 @@ void McastPGMSocket::handleNak() if(status == PGM_IO_STATUS_TIMER_PENDING) { struct timeval tv; - pgm_getsockopt(_priv->socket, PGM_TIME_REMAIN, &tv, sizeof(tv)); + socklen_t size = sizeof(tv); + pgm_getsockopt(_priv->socket, PGM_TIME_REMAIN, &tv, &size); const int msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); qDebug() << " timer pending: " << msecs << "ms"; _nakTimeout->start(msecs); @@ -358,7 +364,8 @@ void McastPGMSocket::handleNak() else if(status == PGM_IO_STATUS_RATE_LIMITED) { struct timeval tv; - pgm_getsockopt(_priv->socket, PGM_RATE_REMAIN, &tv, sizeof(tv)); + socklen_t size = sizeof(tv); + pgm_getsockopt(_priv->socket, PGM_RATE_REMAIN, &tv, &size); const int msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); qDebug() << " rate limited: " << msecs << "ms"; _nakTimeout->start(msecs); @@ -437,7 +444,8 @@ void McastPGMSocket::handleData() else if (status == PGM_IO_STATUS_TIMER_PENDING) { struct timeval tv; - pgm_getsockopt(_priv->socket, PGM_TIME_REMAIN, &tv, sizeof(tv)); + socklen_t size = sizeof(tv); + pgm_getsockopt(_priv->socket, PGM_TIME_REMAIN, &tv, &size); const int msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); qDebug() << " timer pending: " << msecs << "ms"; _dataTimeout->start(msecs); @@ -446,7 +454,8 @@ void McastPGMSocket::handleData() else if (status == PGM_IO_STATUS_RATE_LIMITED) { struct timeval tv; - pgm_getsockopt(_priv->socket, PGM_RATE_REMAIN, &tv, sizeof(tv)); + socklen_t size = sizeof(tv); + pgm_getsockopt(_priv->socket, PGM_RATE_REMAIN, &tv, &size); const int msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); qDebug() << " rate limit pending: " << msecs << "ms"; _dataTimeout->start(msecs); @@ -493,113 +502,80 @@ void McastPGMSocket::canSend() if (_finished) return; - qDebug() << "canSend()"; + // qDebug() << "canSend()"; if (_priv->send_notif) { _priv->send_notif->setEnabled(false); } - bool reenable = true; - - while (!_q.isEmpty()) - { - int status; - QByteArray const nextPacket(_q.head()); - status = pgm_send(_priv->socket, nextPacket.constData(), nextPacket.size(), 0); - if (status == PGM_IO_STATUS_ERROR || status == PGM_IO_STATUS_RESET) - { - qCritical() << "Could not send packet: PGM Error."; - continue; - } - else if (status == PGM_IO_STATUS_WOULD_BLOCK) - { - qDebug() << " would block"; - break; - } - else if (status == PGM_IO_STATUS_RATE_LIMITED) - { - struct timeval tv; - pgm_getsockopt(_priv->socket, PGM_RATE_REMAIN, &tv, sizeof(tv)); - const int msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); - qDebug() << " rate limited:" << msecs << "ms"; - _sendTimeout->start((msecs > 0) ? msecs : 1); - reenable = false; - break; - } - else if (status == PGM_IO_STATUS_NORMAL) - { - qDebug() << " sent"; - _q.dequeue(); - continue; - } - else - { - qCritical() << "Unhandled condition in McastPGMSocket::canSend()"; - } - } - - if (_priv->send_notif && reenable) + if(_q.isEmpty()) { - emit readyToSend(); + emit readyToSend(); + } + else + { + QByteArray const packet(_q.head()); + int status; + + status = pgm_send(_priv->socket, packet.constData(), packet.size(), 0); - qDebug() << " reenable notifier"; - _priv->send_notif->setEnabled(true); + if(status == PGM_IO_STATUS_NORMAL) + { + _q.dequeue(); + if(!_q.isEmpty()) + { + _priv->send_notif->setEnabled(true); + } + else + { + emit readyToSend(); + } + } + else if(status == PGM_IO_STATUS_WOULD_BLOCK) + { + _priv->send_notif->setEnabled(true); + } + else if(status == PGM_IO_STATUS_RATE_LIMITED) + { + struct timeval tv; + socklen_t size = sizeof(tv); + pgm_getsockopt(_priv->socket, PGM_RATE_REMAIN, &tv, &size); + int msecs = (tv.tv_sec * 1000) + ((tv.tv_sec + 999) / 1000); + _sendTimeout->start(msecs); + } + else + { + qCritical() << "Unhandled status in canSend()"; + } } + + if (_shutdownTimer) + _shutdownTimer->start(_shutdown_timeout); } void McastPGMSocket::sendPacket(QByteArray const& bytes) { - if(_q.isEmpty()) - { - int status = pgm_send(_priv->socket, bytes.constData(), bytes.size(), 0); + if(_shutdownTimer) + { + qCritical() << "Logic error: sendPacket() after shutdown()"; + } - if (status == PGM_IO_STATUS_ERROR || status == PGM_IO_STATUS_RESET) - { - qCritical() << "Could not send packet: PGM Error."; - return; - } - else if (status == PGM_IO_STATUS_WOULD_BLOCK) - { - _q.enqueue(bytes); - } - else if (status == PGM_IO_STATUS_RATE_LIMITED) - { - _q.enqueue(bytes); - struct timeval tv; - pgm_getsockopt(_priv->socket, PGM_RATE_REMAIN, &tv, sizeof(tv)); - _dataTimeout->start((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); - } - else if (status == PGM_IO_STATUS_NORMAL) - { - return; - } - else - { - qCritical() << "Unhandled condition in McastPGMSocket::sendPacket()"; - } - } else { - _q.enqueue(bytes); - } + _q.enqueue(bytes); + _priv->send_notif->setEnabled(true); } void McastPGMSocket::finish() { - if(_priv->pending_notif) - { - delete _priv->pending_notif; - _priv->pending_notif = 0; - } - if(_priv->recv_notif) - { - delete _priv->recv_notif; - _priv->recv_notif = 0; - } - if(_priv->repair_notif) + qDebug() << "finish()"; + + Q_FOREACH(QSocketNotifier* notif, _priv->_notifs) { - delete _priv->repair_notif; - _priv->repair_notif = 0; + notif->setEnabled(false); + delete notif; } + _priv->_notifs.clear(); + if(_priv->send_notif) { delete _priv->send_notif; @@ -610,9 +586,28 @@ void McastPGMSocket::finish() _priv->socket = 0; _finished = true; + + emit connectionFinished(); + + qDebug() << "Socket finished"; } bool McastPGMSocket::finished() const { return _finished; } + +void McastPGMSocket::shutdown(int interval) +{ + if(_priv->direction == PSOCK_READ) + return; + + _shutdown_timeout = interval; + _shutdownTimer = new QTimer(this); + connect(_shutdownTimer, SIGNAL(timeout()), this, SLOT(finish())); + if (_q.isEmpty()) + { + _shutdownTimer->start(_shutdown_timeout); + qDebug() << "Started shutdown timer"; + } +} diff --git a/src/net/mcast/McastPGMSocket.h b/src/net/mcast/McastPGMSocket.h index b0007a7..ad42aa5 100644 --- a/src/net/mcast/McastPGMSocket.h +++ b/src/net/mcast/McastPGMSocket.h @@ -22,6 +22,7 @@ #include #include +#include class McastPGMSocket_priv; class QTimer; @@ -40,12 +41,14 @@ public: bool open(McastConfiguration const* config, Direction direction); bool finished() const; + void shutdown(int interval = DEFAULT_MULTICAST_SHUTDOWN_TIMEOUT); signals: void readyToSend(); void receivedPacket(QByteArray const& bytes); void connectionReset(); void connectionFinished(); + void shutdownComplete(); public slots: void sendPacket(QByteArray const& bytes); @@ -67,6 +70,8 @@ private: QTimer* _nakTimeout; QTimer* _dataTimeout; QTimer* _sendTimeout; + QTimer* _shutdownTimer; + int _shutdown_timeout; void setupNotifiers(); }; diff --git a/src/net/mcast/McastSender.cpp b/src/net/mcast/McastSender.cpp index 24a629c..f49c0df 100644 --- a/src/net/mcast/McastSender.cpp +++ b/src/net/mcast/McastSender.cpp @@ -61,36 +61,44 @@ void McastSender::readyToSend() strm << qChecksum(fpdu.constData(), fpdu.size()); _socket->sendPacket(fpdu); - // _socket->finish(); + connect(_socket, SIGNAL(connectionFinished()), this, SLOT(socketFinished())); + _socket->shutdown(); _finished = true; - emit finished(); - return; + _iodev->close(); } + else + { + QByteArray barr(DEFAULT_MULTICAST_APDU, '\0'); + qint64 len_read; + len_read = _iodev->read(barr.data(), barr.capacity()); + barr.resize((int)len_read); - QByteArray barr(DEFAULT_MULTICAST_APDU, '\0'); - qint64 len_read; - len_read = _iodev->read(barr.data(), barr.capacity()); - barr.resize((int)len_read); - - _hash.addData(barr); + _hash.addData(barr); - QByteArray pdu; - QDataStream strm(&pdu, QIODevice::WriteOnly); - strm.setByteOrder(QDataStream::BigEndian); + QByteArray pdu; + QDataStream strm(&pdu, QIODevice::WriteOnly); + strm.setByteOrder(QDataStream::BigEndian); - strm << (quint64)MCASTFT_MAGIC << _curoffs; - strm << barr; - quint16 checksum = qChecksum(pdu.constData(), pdu.size()); - strm << checksum; + strm << (quint64)MCASTFT_MAGIC << _curoffs; + strm << barr; + quint16 checksum = qChecksum(pdu.constData(), pdu.size()); + strm << checksum; - _curoffs += len_read; + _curoffs += len_read; - _socket->sendPacket(pdu); + _socket->sendPacket(pdu); + } } void McastSender::close() { _socket->finish(); } + +void McastSender::socketFinished() +{ + delete _socket; + emit finished(); +} diff --git a/src/net/mcast/McastSender.h b/src/net/mcast/McastSender.h index e713886..5c62fad 100644 --- a/src/net/mcast/McastSender.h +++ b/src/net/mcast/McastSender.h @@ -55,6 +55,7 @@ public slots: private slots: void readyToSend(); + void socketFinished(); private: McastConfiguration* _config; diff --git a/src/net/mcast/trial_programs/mcastsend.cpp b/src/net/mcast/trial_programs/mcastsend.cpp index da8ecf4..f78a9ce 100644 --- a/src/net/mcast/trial_programs/mcastsend.cpp +++ b/src/net/mcast/trial_programs/mcastsend.cpp @@ -118,6 +118,5 @@ void McastSend::run() void McastSend::finished() { cerr << "finished." << endl; - // QTimer::singleShot(30000, QCoreApplication::instance(), SLOT(quit())); - // QCoreApplication::quit(); + QCoreApplication::quit(); } -- cgit v1.2.3-55-g7522 From ef77541b30b5025ac1882ad8b963e535f2cc8ad5 Mon Sep 17 00:00:00 2001 From: brs Date: Thu, 15 Jul 2010 09:40:49 +0200 Subject: Whitespace fixes --- src/net/mcast/McastConfiguration.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/net/mcast/McastConfiguration.h b/src/net/mcast/McastConfiguration.h index f10d487..2e64678 100644 --- a/src/net/mcast/McastConfiguration.h +++ b/src/net/mcast/McastConfiguration.h @@ -25,7 +25,7 @@ class McastConfiguration: public QObject { -Q_OBJECT + Q_OBJECT public: McastConfiguration(QObject* parent = 0) : QObject(parent), @@ -42,16 +42,16 @@ public: } McastConfiguration(McastConfiguration const& other) : + QObject(), _multicastInterface(other._multicastInterface), - QObject(), - _multicastAddress(other._multicastAddress), - _multicastRate(other._multicastRate), - _multicastSPort(other._multicastSPort), - _multicastDPort(other._multicastDPort), - _multicastWinSize(other._multicastWinSize), - _multicastMTU(other._multicastMTU), - _multicastUDPPortBase(other._multicastUDPPortBase), - _multicastUseUDP(other._multicastUseUDP) + _multicastAddress(other._multicastAddress), + _multicastRate(other._multicastRate), + _multicastSPort(other._multicastSPort), + _multicastDPort(other._multicastDPort), + _multicastWinSize(other._multicastWinSize), + _multicastMTU(other._multicastMTU), + _multicastUDPPortBase(other._multicastUDPPortBase), + _multicastUseUDP(other._multicastUseUDP) { } -- cgit v1.2.3-55-g7522 From 03f286eceb5ed317f61544a083d4ee4fe2108785 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Thu, 15 Jul 2010 10:55:50 +0200 Subject: Disable PGMCC as it is buggy --- src/net/mcast/McastPGMSocket.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/net/mcast/McastPGMSocket.cpp b/src/net/mcast/McastPGMSocket.cpp index 8aee209..f2aa91f 100644 --- a/src/net/mcast/McastPGMSocket.cpp +++ b/src/net/mcast/McastPGMSocket.cpp @@ -205,9 +205,6 @@ bool McastPGMSocket::open(McastConfiguration const* config, Direction direction) pgm_setsockopt(_priv->socket, PGM_RXW_SQNS, &no_rxw_sqns, sizeof(no_rxw_sqns)); } - const int use_pgmcc = 1; - pgm_setsockopt(_priv->socket, PGM_USE_PGMCC, &use_pgmcc, sizeof(use_pgmcc)); - // MTU int const mtu = config->multicastMTU(); pgm_setsockopt(_priv->socket, PGM_MTU, &mtu, sizeof(mtu)); -- cgit v1.2.3-55-g7522 From 7683c023be91cfe830178f63bb353d09349abbf7 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Thu, 15 Jul 2010 16:43:33 +0200 Subject: Add isOpen() test to McastPGMSocket --- src/net/mcast/McastPGMSocket.cpp | 8 ++++++++ src/net/mcast/McastPGMSocket.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/src/net/mcast/McastPGMSocket.cpp b/src/net/mcast/McastPGMSocket.cpp index f2aa91f..105bea9 100644 --- a/src/net/mcast/McastPGMSocket.cpp +++ b/src/net/mcast/McastPGMSocket.cpp @@ -86,6 +86,7 @@ static void _ensurePGMInited() McastPGMSocket::McastPGMSocket(QObject* parent) : QObject(parent), _priv(new McastPGMSocket_priv), + _opened(false), _finished(false), _nakTimeout(new QTimer()), _dataTimeout(new QTimer()), @@ -279,6 +280,8 @@ bool McastPGMSocket::open(McastConfiguration const* config, Direction direction) return false; } + _opened = true; + setupNotifiers(); pgm_freeaddrinfo(addrinfo); @@ -594,6 +597,11 @@ bool McastPGMSocket::finished() const return _finished; } +bool McastPGMSocket::isOpen() const +{ + return _opened && !_finished; +} + void McastPGMSocket::shutdown(int interval) { if(_priv->direction == PSOCK_READ) diff --git a/src/net/mcast/McastPGMSocket.h b/src/net/mcast/McastPGMSocket.h index ad42aa5..4ccf931 100644 --- a/src/net/mcast/McastPGMSocket.h +++ b/src/net/mcast/McastPGMSocket.h @@ -41,6 +41,7 @@ public: bool open(McastConfiguration const* config, Direction direction); bool finished() const; + bool isOpen() const; void shutdown(int interval = DEFAULT_MULTICAST_SHUTDOWN_TIMEOUT); signals: @@ -67,6 +68,7 @@ private: McastPGMSocket_priv* _priv; QQueue _q; bool _finished; + bool _opened; QTimer* _nakTimeout; QTimer* _dataTimeout; QTimer* _sendTimeout; -- cgit v1.2.3-55-g7522 From 20f6abcccd9703cb9993432a9fa6dcbf2d0b3a50 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Thu, 15 Jul 2010 16:45:11 +0200 Subject: Add progress indication to McastSender and McastReceiver --- src/net/mcast/McastReceiver.cpp | 2 ++ src/net/mcast/McastReceiver.h | 1 + src/net/mcast/McastSender.cpp | 4 ++++ src/net/mcast/McastSender.h | 2 ++ 4 files changed, 9 insertions(+) diff --git a/src/net/mcast/McastReceiver.cpp b/src/net/mcast/McastReceiver.cpp index 7480ac2..6070208 100644 --- a/src/net/mcast/McastReceiver.cpp +++ b/src/net/mcast/McastReceiver.cpp @@ -121,6 +121,8 @@ void McastReceiver::receivedPacket(QByteArray const& bytes) _hash.addData(contents); _iodev->write(contents); + + emit progress(_curoffs); } void McastReceiver::connectionReset() diff --git a/src/net/mcast/McastReceiver.h b/src/net/mcast/McastReceiver.h index 38e1219..48ff7c5 100644 --- a/src/net/mcast/McastReceiver.h +++ b/src/net/mcast/McastReceiver.h @@ -54,6 +54,7 @@ public: signals: void finished(int result); + void progress(quint64 offset); public slots: void start(); diff --git a/src/net/mcast/McastSender.cpp b/src/net/mcast/McastSender.cpp index f49c0df..294fc55 100644 --- a/src/net/mcast/McastSender.cpp +++ b/src/net/mcast/McastSender.cpp @@ -67,6 +67,8 @@ void McastSender::readyToSend() _finished = true; _iodev->close(); + + emit allSent(); } else { @@ -89,6 +91,8 @@ void McastSender::readyToSend() _curoffs += len_read; _socket->sendPacket(pdu); + + emit progress(_curoffs); } } diff --git a/src/net/mcast/McastSender.h b/src/net/mcast/McastSender.h index 5c62fad..eaaf216 100644 --- a/src/net/mcast/McastSender.h +++ b/src/net/mcast/McastSender.h @@ -48,6 +48,8 @@ public: signals: void finished(); + void progress(quint64 offset); + void allSent(); public slots: void start(); -- cgit v1.2.3-55-g7522 From 5f023e04d880a65c32fcfbf3cddd1281fefb167a Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Fri, 16 Jul 2010 18:04:49 +0200 Subject: Make McastSender work with a pre-opened socket --- src/net/mcast/McastSender.cpp | 8 ++++++++ src/net/mcast/McastSender.h | 1 + 2 files changed, 9 insertions(+) diff --git a/src/net/mcast/McastSender.cpp b/src/net/mcast/McastSender.cpp index 294fc55..322b751 100644 --- a/src/net/mcast/McastSender.cpp +++ b/src/net/mcast/McastSender.cpp @@ -46,6 +46,14 @@ void McastSender::start() _socket->open(_config, McastPGMSocket::PSOCK_WRITE); } +void McastSender::start(McastPGMSocket* socket) +{ + _socket = socket; + connect(_socket, SIGNAL(readyToSend()), this, SLOT(readyToSend())); + Q_ASSERT(_socket->isOpen()); + readyToSend(); +} + void McastSender::readyToSend() { if(_finished) diff --git a/src/net/mcast/McastSender.h b/src/net/mcast/McastSender.h index eaaf216..dd5154c 100644 --- a/src/net/mcast/McastSender.h +++ b/src/net/mcast/McastSender.h @@ -53,6 +53,7 @@ signals: public slots: void start(); + void start(McastPGMSocket* openSocket); void close(); private slots: -- cgit v1.2.3-55-g7522 From 2ad0ca683dfade47078a2aafce9921ca238a9436 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Sat, 17 Jul 2010 16:43:08 +0200 Subject: Implement UI and pvsDaemon components for outgoing Multicast Transfer --- CMakeLists.txt | 7 +- src/gui/clientFileSendDialog.cpp | 201 ++++++++++++++++++++++++++--- src/gui/clientFileSendDialog.h | 21 ++++ src/gui/ui/clientFileSendDialog.ui | 15 ++- src/gui/ui/clientNicklistDialog.ui | 7 ++ src/net/mcast/McastConfiguration.cpp | 39 ++++++ src/net/mcast/McastConfiguration.h | 5 + src/net/mcast/McastPGMSocket.cpp | 12 +- src/net/mcast/McastSender.cpp | 2 +- src/net/pvsOutgoingMulticastTransfer.cpp | 209 +++++++++++++++++++++++++++++++ src/net/pvsOutgoingMulticastTransfer.h | 92 ++++++++++++++ src/net/pvsServerConnection.h | 7 +- src/pvs.cpp | 60 +++++++++ src/pvs.h | 17 +++ 14 files changed, 661 insertions(+), 33 deletions(-) create mode 100644 src/net/pvsOutgoingMulticastTransfer.cpp create mode 100644 src/net/pvsOutgoingMulticastTransfer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8eb1961..93e281e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,6 +99,7 @@ SET( PVS_SRCS src/net/pvsDiscoveredServer.cpp src/util/TextFile.cpp src/util/serviceDiscoveryUtil.cpp + src/net/pvsOutgoingMulticastTransfer.cpp ) # pvsgui @@ -192,6 +193,7 @@ SET( PVS_MOC_HDRS src/net/pvsServerConnection.h src/net/pvsServiceDiscovery.h src/net/pvsDiscoveredServer.h + src/net/pvsOutgoingMulticastTransfer.h ) SET( PVSGUI_MOC_HDRS @@ -313,27 +315,24 @@ TARGET_LINK_LIBRARIES( pvsmgr ${QT_LIBRARIES} ${VNC_LIBRARIES} ${X11_LIBRARIES} - pgm ) TARGET_LINK_LIBRARIES( pvsmgrtouch ${QT_LIBRARIES} ${VNC_LIBRARIES} ${X11_LIBRARIES} - pgm ) TARGET_LINK_LIBRARIES( pvs ${QT_LIBRARIES} ${VNC_LIBRARIES} ${X11_LIBRARIES} - pgm + pvsmcast ) TARGET_LINK_LIBRARIES( pvsgui ${QT_LIBRARIES} ${VNC_LIBRARIES} - pgm ) SET_PROPERTY(TARGET pvsmgrtouch PROPERTY COMPILE_DEFINITIONS MAINWINDOW_USE_TOUCHGUI) diff --git a/src/gui/clientFileSendDialog.cpp b/src/gui/clientFileSendDialog.cpp index ccb44b3..25e3eaa 100644 --- a/src/gui/clientFileSendDialog.cpp +++ b/src/gui/clientFileSendDialog.cpp @@ -22,6 +22,10 @@ ClientFileSendDialog::ClientFileSendDialog(QWidget *parent) : { setupUi(this); + _transferID = 0; + _error = false; + _isMulticast = false; + _file = NULL; _socket = NULL; _clientNicklistDialog = new ClientNicklistDialog(this); @@ -60,7 +64,15 @@ void ClientFileSendDialog::open() reject(); return; } - open(_clientNicklistDialog->getNick()); + + if (_clientNicklistDialog->isSendToAll()) + { + sendToAll(); + } + else + { + open(_clientNicklistDialog->getNick()); + } } void ClientFileSendDialog::open(QString nick) @@ -75,6 +87,70 @@ void ClientFileSendDialog::open(QString nick) open(nick, filename); } +void ClientFileSendDialog::sendToAll() +{ + QString filename = QFileDialog::getOpenFileName(this, tr("Send File"), QDir::homePath(), ""); + if (filename == "") + { + reject(); + return; + } + sendToAll(filename); +} + +void ClientFileSendDialog::sendToAll(QString filename) +{ + _isMulticast = true; + + connect(_ifaceDBus, SIGNAL(outgoingMulticastTransferStarted(qulonglong)), SLOT(multicastTransferStarted(qulonglong))); + connect(_ifaceDBus, SIGNAL(outgoingMulticastTransferProgress(qulonglong,qulonglong,qulonglong)), SLOT(multicastTransferProgress(qulonglong, qulonglong, qulonglong))); + connect(_ifaceDBus, SIGNAL(outgoingMulticastTransferFinished(qulonglong)), SLOT(multicastTransferFinished(qulonglong))); + connect(_ifaceDBus, SIGNAL(outgoingMulticastTransferFailed(qulonglong, QString const&)), SLOT(multicastTransferFailed(qulonglong, QString const&))); + + filenameLabel->setText(filename); + progressBar->setRange(0, 0); + labelNick->setText(tr("all")); + labelStatus->setText(tr("Waiting to start")); + + QString errorMessage("Backend error"); + + // We need to jump through a lot of hoops because this call is prone to time out, and + // QtDBus does not support specifying timeouts in generated interfaces. + QDBusMessage call = QDBusMessage::createMethodCall("org.openslx.pvs", "/", "org.openslx.pvs", "createMulticastTransfer"); + call << filename; + + QDBusMessage reply = _ifaceDBus->connection().call(call, QDBus::Block, 5000); + if (reply.type() == QDBusMessage::ErrorMessage) + { + QMessageBox::critical(this, tr("File Send error"), tr("Error communicating with backend: %1: %2").arg(reply.errorName()).arg(reply.errorMessage())); + reject(); + return; + } + else if (reply.type() == QDBusMessage::InvalidMessage) + { + QMessageBox::critical(this, tr("File Send error"), tr("Something went wrong while communicating with backend, but I don't know what.")); + reject(); + return; + } + else if (reply.type() == QDBusMessage::ReplyMessage) + { + QList args = reply.arguments(); + bool created = args.at(0).toBool(); + _transferID = args.at(1).toULongLong(); + QString errorMessage = args.at(2).toString(); + if (!created) + { + QMessageBox::critical(this, tr("File Send error"), tr("Could not create a multicast transfer: %1").arg(errorMessage)); + reject(); + return; + } + } + + connect(cancelButton, SIGNAL(clicked()), SLOT(canceled())); + + show(); +} + void ClientFileSendDialog::open(QString nick, QString filename) { // open file @@ -129,6 +205,8 @@ void ClientFileSendDialog::receiveAck() QString ack = QString::fromUtf8(_socket->readLine()); if (ack != "ok\n") { + _error = true; + _reason = tr("Receiver declined"); qDebug("[%s] Received nack!", metaObject()->className()); close(); return; @@ -160,27 +238,30 @@ void ClientFileSendDialog::sendFile() void ClientFileSendDialog::close() { - if (_file && _file->isOpen()) - { - _file->close(); - qDebug("[%s] File closed.", metaObject()->className()); - } - - if (_socket && _socket->isOpen()) + if (!_isMulticast) { - disconnect(_socket, SIGNAL(readyRead()), this, SLOT(receiveAck())); - disconnect(_socket, SIGNAL(bytesWritten(qint64)), this, SLOT(sendFile())); - disconnect(_socket, SIGNAL(disconnected()), this, SLOT(close())); - disconnect(_socket, SIGNAL(connected()), this, SLOT(sendHeader())); - disconnect(_socket, SIGNAL(error(QAbstractSocket::SocketError)), - this, SLOT(error(QAbstractSocket::SocketError))); - _socket->disconnectFromHost(); - qDebug("[%s] Connection closed.", metaObject()->className()); + if (_file && _file->isOpen()) + { + _file->close(); + qDebug("[%s] File closed.", metaObject()->className()); + } + + if (_socket && _socket->isOpen()) + { + disconnect(_socket, SIGNAL(readyRead()), this, SLOT(receiveAck())); + disconnect(_socket, SIGNAL(bytesWritten(qint64)), this, SLOT(sendFile())); + disconnect(_socket, SIGNAL(disconnected()), this, SLOT(close())); + disconnect(_socket, SIGNAL(connected()), this, SLOT(sendHeader())); + disconnect(_socket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(error(QAbstractSocket::SocketError))); + _socket->disconnectFromHost(); + qDebug("[%s] Connection closed.", metaObject()->className()); + } } - disconnect(cancelButton, SIGNAL(clicked()), this, SLOT(close())); + disconnect(cancelButton, SIGNAL(clicked()), this, SLOT(canceled())); - if (_bytesToWrite == 0) + if (!_error) { accept(); QMessageBox::information(0, tr("PVS - File Transfer"), @@ -190,12 +271,26 @@ void ClientFileSendDialog::close() { reject(); QMessageBox::warning(0, tr("PVS - File Transfer"), - tr("File Transfer canceled!")); + tr("File Transfer canceled: %1").arg(_reason)); } } +void ClientFileSendDialog::canceled() +{ + if(_isMulticast) + { + _ifaceDBus->cancelOutgoingMulticastTransfer(_transferID); + } + + _error = true; + _reason = tr("You clicked 'Cancel'"); + close(); +} + void ClientFileSendDialog::error(QAbstractSocket::SocketError error) { + _error = true; + _reason = tr("Socket Error"); qDebug("[%s] Socket error: %i", metaObject()->className(), error); close(); } @@ -212,6 +307,55 @@ QString ClientFileSendDialog::formatSize(qint64 size) return QString("%1B").arg((qreal)size, 0, 'f',1); } +void ClientFileSendDialog::multicastTransferStarted(qulonglong transferID) +{ + qDebug() << "multicastTransferStarted(" << transferID << ")"; + if (transferID != _transferID) + { + return; + } + labelStatus->setText("Started"); +} + +void ClientFileSendDialog::multicastTransferProgress(qulonglong transferID, qulonglong bytes, qulonglong of) +{ + qDebug() << "multicastTransferProgress(" << transferID << bytes << of << ")"; + if (transferID != _transferID) + { + return; + } + + if(bytes < of) + { + labelStatus->setText("Transferring"); + progressBar->setRange(0, of); + progressBar->setValue(bytes); + } + else + { + labelStatus->setText("Waiting to finish"); + progressBar->setRange(0, 0); + } + + labelA->setText(formatSize(bytes)); + labelB->setText(formatSize(of)); +} + +void ClientFileSendDialog::multicastTransferFinished(quint64 transferID) +{ + qDebug() << "multicastTransferFinished(" << transferID << ")"; + qDebug("[%s] MulticastTransfer finished", metaObject()->className()); + close(); +} + +void ClientFileSendDialog::multicastTransferFailed(quint64 transferID, QString const& reason) +{ + qDebug() << "multicastTransferFailed(" << transferID << reason << ")"; + _error = true; + _reason = reason; + close(); +} + //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// @@ -235,6 +379,25 @@ ClientNicklistDialog::ClientNicklistDialog(QWidget *parent) : listWidget->addItems(nicknames); listWidget->setCurrentRow(0); + + connect(sendToAllCheckBox, SIGNAL(stateChanged(int)), SLOT(sendToAllStateChanged(int))); + + sendToAllCheckBox->setCheckState(Qt::Unchecked); + _isSendToAll = false; +} + +void ClientNicklistDialog::sendToAllStateChanged(int state) +{ + if (state) + { + listWidget->setEnabled(false); + _isSendToAll = true; + } + else + { + listWidget->setEnabled(true); + _isSendToAll = false; + } } ClientNicklistDialog::~ClientNicklistDialog() diff --git a/src/gui/clientFileSendDialog.h b/src/gui/clientFileSendDialog.h index d8afc3a..7abdcc7 100644 --- a/src/gui/clientFileSendDialog.h +++ b/src/gui/clientFileSendDialog.h @@ -30,9 +30,17 @@ public: ~ClientNicklistDialog(); QString getNick(); + bool isSendToAll() const + { + return _isSendToAll; + } + +private Q_SLOTS: + void sendToAllStateChanged(int state); private: OrgOpenslxPvsInterface *_ifaceDBus; + bool _isSendToAll; }; @@ -51,14 +59,22 @@ public: void open(); void open(QString nick); void open(QString nick, QString filename); + void sendToAll(); + void sendToAll(QString filename); private Q_SLOTS: void sendHeader(); void receiveAck(); void sendFile(); void close(); + void canceled(); void error(QAbstractSocket::SocketError error); + void multicastTransferStarted(qulonglong transferID); + void multicastTransferProgress(qulonglong transferID, qulonglong bytes, qulonglong of); + void multicastTransferFinished(qulonglong transferID); + void multicastTransferFailed(qulonglong transferID, QString const& reason); + private: QString formatSize(qint64 size); @@ -71,6 +87,11 @@ private: OrgOpenslxPvsInterface *_ifaceDBus; QString _nickname; + quint64 _transferID; + bool _error; + QString _reason; + bool _isMulticast; + }; #endif /* CLIENTFILESENDDIALOG_H_ */ diff --git a/src/gui/ui/clientFileSendDialog.ui b/src/gui/ui/clientFileSendDialog.ui index d2d9c75..85462ba 100644 --- a/src/gui/ui/clientFileSendDialog.ui +++ b/src/gui/ui/clientFileSendDialog.ui @@ -6,8 +6,8 @@ 0 0 - 186 - 108 + 528 + 144 @@ -33,7 +33,7 @@ - + @@ -61,10 +61,17 @@ + + + + + + + - + diff --git a/src/gui/ui/clientNicklistDialog.ui b/src/gui/ui/clientNicklistDialog.ui index afd84f1..3679b55 100644 --- a/src/gui/ui/clientNicklistDialog.ui +++ b/src/gui/ui/clientNicklistDialog.ui @@ -28,6 +28,13 @@ + + + + Send to &all + + + diff --git a/src/net/mcast/McastConfiguration.cpp b/src/net/mcast/McastConfiguration.cpp index 1a1c0a8..489eb3f 100644 --- a/src/net/mcast/McastConfiguration.cpp +++ b/src/net/mcast/McastConfiguration.cpp @@ -5,5 +5,44 @@ * Author: brs */ +#include + #include "McastConfiguration.h" +void McastConfiguration::loadFrom(QSettings* _settings, char const* group) +{ + if (group) + _settings->beginGroup(group); + + _multicastAddress = _settings->value("groupAddress", DEFAULT_MULTICAST_ADDRESS).toString(); + _multicastInterface = _settings->value("interface", DEFAULT_MULTICAST_INTERFACE).toString(); + _multicastMTU = _settings->value("mtu", DEFAULT_MULTICAST_MTU).value(); + _multicastRate = _settings->value("rate", DEFAULT_MULTICAST_RATE).value(); + _multicastUseUDP = _settings->value("use-udp", DEFAULT_MULTICAST_USEUDP).toBool(); + _multicastWinSize = _settings->value("winsize", DEFAULT_MULTICAST_WSIZ).value(); + _multicastUDPPortBase = _settings->value("portbase", DEFAULT_MULTICAST_UDPPORT).value(); + _multicastDPort = _settings->value("dport", DEFAULT_MULTICAST_DPORT).value(); + _multicastSPort = _settings->value("sport", DEFAULT_MULTICAST_SPORT).value(); + + if (group) + _settings->endGroup(); +} + +void McastConfiguration::writeTo(QSettings* _settings, char const* group) const +{ + if (group) + _settings->beginGroup(group); + + _settings->setValue("groupAddress", _multicastAddress); + _settings->setValue("interface", _multicastInterface); + _settings->setValue("mtu", _multicastMTU); + _settings->setValue("rate", _multicastRate); + _settings->setValue("use-udp", _multicastUseUDP); + _settings->setValue("winsize", _multicastWinSize); + _settings->setValue("portbase", _multicastUDPPortBase); + _settings->setValue("dport", _multicastDPort); + _settings->setValue("sport", _multicastSPort); + + if (group) + _settings->endGroup(); +} diff --git a/src/net/mcast/McastConfiguration.h b/src/net/mcast/McastConfiguration.h index 2e64678..b010f60 100644 --- a/src/net/mcast/McastConfiguration.h +++ b/src/net/mcast/McastConfiguration.h @@ -23,6 +23,8 @@ #include "McastConstants.h" +class QSettings; + class McastConfiguration: public QObject { Q_OBJECT @@ -164,6 +166,9 @@ public: emit changed(); } + void loadFrom(QSettings* settings, char const* group = 0); + void writeTo(QSettings* settings, char const* group = 0) const; + signals: void changed(); diff --git a/src/net/mcast/McastPGMSocket.cpp b/src/net/mcast/McastPGMSocket.cpp index 105bea9..7952f00 100644 --- a/src/net/mcast/McastPGMSocket.cpp +++ b/src/net/mcast/McastPGMSocket.cpp @@ -567,6 +567,11 @@ void McastPGMSocket::sendPacket(QByteArray const& bytes) void McastPGMSocket::finish() { + if (_finished) + { + return; + } + qDebug() << "finish()"; Q_FOREACH(QSocketNotifier* notif, _priv->_notifs) @@ -582,8 +587,11 @@ void McastPGMSocket::finish() _priv->send_notif = 0; } - pgm_close(_priv->socket, 1); - _priv->socket = 0; + if (_priv->socket) + { + pgm_close(_priv->socket, 1); + _priv->socket = 0; + } _finished = true; diff --git a/src/net/mcast/McastSender.cpp b/src/net/mcast/McastSender.cpp index 322b751..e25ec86 100644 --- a/src/net/mcast/McastSender.cpp +++ b/src/net/mcast/McastSender.cpp @@ -111,6 +111,6 @@ void McastSender::close() void McastSender::socketFinished() { - delete _socket; + _socket->deleteLater(); emit finished(); } diff --git a/src/net/pvsOutgoingMulticastTransfer.cpp b/src/net/pvsOutgoingMulticastTransfer.cpp new file mode 100644 index 0000000..2f24d49 --- /dev/null +++ b/src/net/pvsOutgoingMulticastTransfer.cpp @@ -0,0 +1,209 @@ +/* +# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# src/net/pvsOutgoingMulticastTransfer.cpp +# - wrap McastSender functionality in PVS daemon +# ----------------------------------------------------------------------------- +*/ + +#include "pvsOutgoingMulticastTransfer.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +PVSOutgoingMulticastTransfer::PVSOutgoingMulticastTransfer(QString senderName, quint64 id, QString filename, QObject* parent) : + QObject(parent), + _file(0), + _progress(0), + _config(0), + _socket(0), + _progressTimer(0), + _prepareTimer(0), + _senderName(senderName), + _id(id), + _error(false) +{ + QFileInfo finfo(filename); + if(!finfo.exists()) + { + error("File does not exist"); + } + + if(!finfo.isReadable()) + { + error("File is not readable"); + } + + _file = new QFile(filename, this); + _length = _file->size(); +} + +PVSOutgoingMulticastTransfer::~PVSOutgoingMulticastTransfer() +{ + if(_file) + delete _file; + if(_config) + delete _config; +} + +void PVSOutgoingMulticastTransfer::prepare() +{ + if (_socket && !_socketInacceptable) + { + _prepareTimer->stop(); + delete _prepareTimer; + _prepareTimer = 0; + + QTimer::singleShot(0, this, SLOT(doStart())); + return; + } + else if (_socket) + { + delete _socket; + _socket = 0; + } + + QSettings settings; + quint16 portbase = settings.value("multicast/port-base", "6966").value(); + quint16 portlimit = settings.value("multicast/port-limit", "7966").value(); + + int tries_remaining = 5; + while(tries_remaining > 0) + { + quint16 port = portbase + (qrand() % (portlimit - portbase + 1)); + + if (!_config) + { + _config = new McastConfiguration(); + } + _config->loadFrom(&settings, "multicast"); + _config->multicastUDPPortBase(port); + + _socket = new McastPGMSocket(this); + if(_socket->open(_config, McastPGMSocket::PSOCK_WRITE)) + { + break; + } + else + { + delete _socket; + _socket = 0; + } + } + + if (!_socket) + { + emit failed(_id, "Could not open socket"); + delete _prepareTimer; + _prepareTimer = 0; + return; + } + else + { + _socketInacceptable = false; + // announce the transfer: + QFileInfo info(*_file); + QString message = QString("%1:%2:%3:%4:%5").arg(_senderName).arg(_id).arg(info.baseName()).arg(info.size()).arg(_config->multicastUDPPortBase()); + PVSMsg msg(PVSCOMMAND, "MCASTFTANNOUNCE", message); + emit announce(msg); + _prepareTimer->start(5000); + } +} + +void PVSOutgoingMulticastTransfer::doStart() +{ + ConsoleLog writeLine(QString("Starting multicast transfer %1").arg(_id)); + + _file->open(QIODevice::ReadOnly); + + _sender = new McastSender(_file, _config, this); + connect(_sender, SIGNAL(finished()), SLOT(senderFinished())); + connect(_sender, SIGNAL(progress(quint64)), SLOT(senderProgress(quint64))); + // connect(_sender, SIGNAL(allSent()), SIGNAL(allSent())); + _socket->setParent(_sender); + _sender->start(_socket); + + emit started(_id); + + _progressTimer = new QTimer(this); + connect(_progressTimer, SIGNAL(timeout()), SLOT(reportTimeout())); + _progressTimer->setInterval(333); + _progressTimer->start(); +} + +void PVSOutgoingMulticastTransfer::senderProgress(quint64 bytes) + +{ + _progress = bytes; +} + +void PVSOutgoingMulticastTransfer::senderFinished() +{ + if(_progressTimer) + { + _progressTimer->stop(); + delete _progressTimer; + _progressTimer = 0; + } + emit finished(_id); + _sender->close(); + delete _sender; +} + +void PVSOutgoingMulticastTransfer::reportTimeout() +{ + emit progress(_id, _progress, _length); +} + +void PVSOutgoingMulticastTransfer::retry() +{ + bool first = !_socketInacceptable; + _socketInacceptable = true; + + if(first) + { + _prepareTimer->setInterval(1000); + } +} + +void PVSOutgoingMulticastTransfer::start() +{ + if (!_prepareTimer) + { + _prepareTimer = new QTimer(this); + _prepareTimer->setSingleShot(true); + connect(_prepareTimer, SIGNAL(timeout()), SLOT(prepare())); + } + QTimer::singleShot(0, this, SLOT(prepare())); +} + +void PVSOutgoingMulticastTransfer::abort() +{ + if (_sender) + _sender->close(); +} + +void PVSOutgoingMulticastTransfer::error(QString const& reason) +{ + qCritical() << "Could not create an outgoing mcast transfer: " << reason; + _error = true; + _reason = reason; +} diff --git a/src/net/pvsOutgoingMulticastTransfer.h b/src/net/pvsOutgoingMulticastTransfer.h new file mode 100644 index 0000000..5fd6a3d --- /dev/null +++ b/src/net/pvsOutgoingMulticastTransfer.h @@ -0,0 +1,92 @@ +/* +# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# src/net/pvsOutgoingMulticastTransfer.h +# - wrap McastSender functionality in PVS daemon +# ----------------------------------------------------------------------------- +*/ + +#ifndef PVSOUTGOINGMULTICASTTRANSFER_H_ +#define PVSOUTGOINGMULTICASTTRANSFER_H_ + +#include +#include +#include + +#include + +class QFile; +class QTimer; +class McastConfiguration; +class McastPGMSocket; +class McastSender; + +class PVSOutgoingMulticastTransfer : public QObject +{ + Q_OBJECT +public: + PVSOutgoingMulticastTransfer(QString senderName, quint64 id, QString filename, QObject* parent = 0); + virtual ~PVSOutgoingMulticastTransfer(); + + quint64 id() const + { + return _id; + } + + bool isError() const + { + return _error; + } + + QString reason() const + { + return _reason; + } + +signals: + void started(qulonglong id); + void progress(qulonglong id, qulonglong bytes, qulonglong of); + void allSent(qulonglong id); + void finished(qulonglong id); + void failed(qulonglong id, QString const reason); + void announce(PVSMsg announcement); + +private slots: + void senderProgress(quint64 bytes); + void senderFinished(); + void reportTimeout(); + void doStart(); + void prepare(); + +public slots: + void start(); + void abort(); + void retry(); + +private: + QFile* _file; + quint64 _length; + quint64 _progress; + McastConfiguration* _config; + McastPGMSocket* _socket; + McastSender* _sender; + QTimer* _progressTimer; + QTimer* _prepareTimer; + QString _senderName; + quint64 _id; + bool _error; + QString _reason; + bool _socketInacceptable; + + void error(QString const& reason); +}; + +#endif /* PVSOUTGOINGMULTICASTTRANSFER_H_ */ diff --git a/src/net/pvsServerConnection.h b/src/net/pvsServerConnection.h index 0669d88..c6ef015 100644 --- a/src/net/pvsServerConnection.h +++ b/src/net/pvsServerConnection.h @@ -11,9 +11,9 @@ #define _PVSSERVERCONNECTION_H_ #include "src/util/dispatcher.h" +#include "src/net/pvsMsg.h" #include -class PVSMsg; class PVS; class PVSDiscoveredServer; @@ -30,8 +30,6 @@ public: return _socket != NULL && _socket->state() == QAbstractSocket::ConnectedState; } - void sendMessage(PVSMsg newMessage); - void ping(); QString getServerName(); @@ -63,6 +61,9 @@ public: _commandDispatcher.removeListener(ident, who, func); }; +public Q_SLOTS: + void sendMessage(PVSMsg newMessage); + protected: void timerEvent(QTimerEvent *event); diff --git a/src/pvs.cpp b/src/pvs.cpp index 2069e36..c7fe5ce 100644 --- a/src/pvs.cpp +++ b/src/pvs.cpp @@ -15,6 +15,8 @@ #include "src/net/pvsMsg.h" #include "src/net/pvsServiceDiscovery.h" #include "src/net/pvsDiscoveredServer.h" +#include "src/net/mcast/McastConfiguration.h" +#include "src/net/pvsOutgoingMulticastTransfer.h" // D-Bus #include "pvsadaptor.h" @@ -38,6 +40,7 @@ PVS::PVS() : loadCommands(); _blankScreen = NULL; _vncPort = -1; + _masterMcastConfig = new McastConfiguration(this); // add a notify to the allow file, so we get informed when the file is changed QString watchPath("/home/"); @@ -176,6 +179,17 @@ void PVS::onCommand(PVSMsg cmdMessage) unlock(); return; } + if (ident.compare("MCASTFTRETRY") == 0) + { + QStringList fields = message.split(':'); + if (fields[0].compare(getUserName()) == 0) + { + quint64 id = fields[0].toULongLong(); + PVSOutgoingMulticastTransfer* transfer = _outgoingTransfers.value(id, 0); + if (transfer) + transfer->retry(); + } + } #ifdef never // prototype @@ -627,3 +641,49 @@ void PVS::signalHandler(int signal) } +bool PVS::createMulticastTransfer(QString const& objectPath, quint64& transferID, QString& errorReason) +{ + transferID = generateMcastTransferID(); + + PVSOutgoingMulticastTransfer* transfer = new PVSOutgoingMulticastTransfer(getUserName(), transferID, objectPath, this); + if (transfer->isError()) + { + errorReason = transfer->reason(); + delete transfer; + return false; + } + + _outgoingTransfers.insert(transferID, transfer); + connect(transfer, SIGNAL(started(qulonglong)), SIGNAL(outgoingMulticastTransferStarted(qulonglong))); + connect(transfer, SIGNAL(finished(qulonglong)), SIGNAL(outgoingMulticastTransferFinished(qulonglong))); + connect(transfer, SIGNAL(failed(qulonglong, QString const)), SIGNAL(outgoingMulticastTransferFailed(qulonglong, QString const))); + connect(transfer, SIGNAL(progress(qulonglong, qulonglong, qulonglong)), SIGNAL(outgoingMulticastTransferProgress(qulonglong, qulonglong, qulonglong))); + connect(transfer, SIGNAL(announce(PVSMsg)), _pvsServerConnection, SLOT(sendMessage(PVSMsg))); + QTimer::singleShot(0, transfer, SLOT(start())); + errorReason = ""; + return true; +} + +void PVS::cancelOutgoingMulticastTransfer(quint64 transferID) +{ + PVSOutgoingMulticastTransfer* transfer = _outgoingTransfers.value(transferID, 0); + + if (transfer) + { + delete transfer; + } +} + +quint64 PVS::generateMcastTransferID() +{ + static quint64 nodeID = 0; + static quint16 counter = 0; + + if (!nodeID) + { + QDataStream(QCryptographicHash::hash(getUserName().toLocal8Bit(), QCryptographicHash::Md5)) >> nodeID; + } + + return (nodeID & Q_UINT64_C(0xffffffffffff0000)) | (quint64)(++counter); +} + diff --git a/src/pvs.h b/src/pvs.h index 4b1e29e..93596d2 100644 --- a/src/pvs.h +++ b/src/pvs.h @@ -28,6 +28,8 @@ class PVSServiceDiscovery; class PVSDiscoveredServer; +class McastConfiguration; +class PVSOutgoingMulticastTransfer; /** * PVSClient @@ -80,6 +82,11 @@ public Q_SLOTS: QStringList getAvailableHosts(); QString getIpByNick(QString nick); + // Multicast File Transfer + bool createMulticastTransfer(QString const& objectPath, quint64& transferID, QString& errorReason); + void cancelOutgoingMulticastTransfer(quint64 transferID); + + Q_SIGNALS: void project(QString host, int port, QString passwd, bool fullscreen, bool smoothTransformation, int quality); @@ -93,6 +100,12 @@ Q_SIGNALS: void addHost(QString host); void delHost(QString host); + // Multicast File Transfer + void outgoingMulticastTransferStarted(qulonglong transferID); + void outgoingMulticastTransferProgress(qulonglong transferID, qulonglong bytes, qulonglong of); + void outgoingMulticastTransferFinished(qulonglong transferID); + void outgoingMulticastTransferFailed(qulonglong transferID, QString reason); + protected: void timerEvent(QTimerEvent *event); @@ -142,5 +155,9 @@ private: int _timerLockTest; int _timerLockDelay; + McastConfiguration* _masterMcastConfig; + QHash _outgoingTransfers; + + static quint64 generateMcastTransferID(); }; #endif /* PVSCLIENT_H_ */ -- cgit v1.2.3-55-g7522 From 475a217d321076a6438813252a8cfc818977a151 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Sat, 17 Jul 2010 16:43:31 +0200 Subject: Fix License header for McastConfiguration.cpp --- src/net/mcast/McastConfiguration.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/net/mcast/McastConfiguration.cpp b/src/net/mcast/McastConfiguration.cpp index 489eb3f..6c5e620 100644 --- a/src/net/mcast/McastConfiguration.cpp +++ b/src/net/mcast/McastConfiguration.cpp @@ -1,9 +1,18 @@ /* - * McastConfiguration.cpp - * - * Created on: Jul 10, 2010 - * Author: brs - */ +# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# src/net/mcast/McastConfiguration.cpp +# - hold Multicast protocol configuration data +# ----------------------------------------------------------------------------- +*/ #include -- cgit v1.2.3-55-g7522 From 6f686ba9729a6539eb192b9756b9d05a9f5887ba Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Sat, 17 Jul 2010 16:43:49 +0200 Subject: Implement --no-fork/-F command line argument for pvs --- src/pvsDaemon.cpp | 97 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 38 deletions(-) diff --git a/src/pvsDaemon.cpp b/src/pvsDaemon.cpp index e87bfe7..591d43b 100644 --- a/src/pvsDaemon.cpp +++ b/src/pvsDaemon.cpp @@ -66,6 +66,9 @@ int main(int argc, char** argv) { int frequency = 5; int port = -1; +#ifdef as_daemon + bool no_fork = false; +#endif QFileInfo script; script.setFile("/usr/bin/pvs-vncsrv"); @@ -107,11 +110,14 @@ int main(int argc, char** argv) { "freq", required_argument, 0, 'f' }, { "client", required_argument, 0, 'e' }, { "script", required_argument, 0, 's' }, +#ifdef as_daemon + { "no-fork", no_argument, 0, 'F' }, +#endif { 0, 0, 0, 0 }, }; /* getopt_long stores the option index here. */ - int c = getopt_long(argc, argv, "hvoc:f:e:s:p:", long_options, + int c = getopt_long(argc, argv, "hvoFc:f:e:s:p:", long_options, &option_index); option_index++; if (c == -1) @@ -267,6 +273,13 @@ int main(int argc, char** argv) } break; } +#ifdef as_daemon + case 'F': + { + no_fork = true; + break; + } +#endif case '?': { ConsoleLog writeError( @@ -293,51 +306,59 @@ int main(int argc, char** argv) #ifdef as_daemon - /* Our process ID and Session ID */ - pid_t pid, sid; - - /* Fork off the parent process */ - pid = fork(); - if (pid < 0) - { - exit(-1); - } - /* If we got a good PID, then - we can exit the parent process. */ - if (pid > 0) + if (!no_fork) { - exit(0); - } + /* Our process ID and Session ID */ + pid_t pid, sid; - /* Change the file mode mask */ - umask(0); + /* Fork off the parent process */ + pid = fork(); + if (pid < 0) + { + exit(-1); + } + /* If we got a good PID, then + we can exit the parent process. */ + if (pid > 0) + { + exit(0); + } - /* Open any logs here */ + /* Change the file mode mask */ + umask(0); - /* Create a new SID for the child process */ - sid = setsid(); - if (sid < 0) - { - /* Log the failure */ - exit(-1); - } + /* Open any logs here */ - /* Change the current working directory */ - if ((chdir("/")) < 0) - { - /* Log the failure */ - exit(-1); - } + /* Create a new SID for the child process */ + sid = setsid(); + if (sid < 0) + { + /* Log the failure */ + exit(-1); + } + + /* Change the current working directory */ + if ((chdir("/")) < 0) + { + /* Log the failure */ + exit(-1); + } - /* Close out the standard file descriptors */ - close(STDIN_FILENO); - freopen ((QString("/home/").append(getUserName().append(QString("/.pvs/dump")))).toUtf8().data(),"w",stdout); - //close(STDOUT_FILENO); - close(STDERR_FILENO); + /* Close out the standard file descriptors */ + close(STDIN_FILENO); + freopen ((QString("/home/").append(getUserName().append(QString("/.pvs/dump")))).toUtf8().data(),"w",stdout); + //close(STDOUT_FILENO); + close(STDERR_FILENO); - /* Daemon-specific initialization goes here */ + /* Daemon-specific initialization goes here */ - /* The Big Loop */ + /* The Big Loop */ + } + else + { + /* just the umask(), please */ + umask(0); + } #endif -- cgit v1.2.3-55-g7522 From 58fb0517b78c1d9715ce2e3c8faf6bc02514b310 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Sat, 17 Jul 2010 17:23:29 +0200 Subject: Remember to delete outgoing transfers when they are finished or failed --- src/pvs.cpp | 11 +++++++++++ src/pvs.h | 3 +++ 2 files changed, 14 insertions(+) diff --git a/src/pvs.cpp b/src/pvs.cpp index c7fe5ce..af5958e 100644 --- a/src/pvs.cpp +++ b/src/pvs.cpp @@ -659,6 +659,8 @@ bool PVS::createMulticastTransfer(QString const& objectPath, quint64& transferID connect(transfer, SIGNAL(failed(qulonglong, QString const)), SIGNAL(outgoingMulticastTransferFailed(qulonglong, QString const))); connect(transfer, SIGNAL(progress(qulonglong, qulonglong, qulonglong)), SIGNAL(outgoingMulticastTransferProgress(qulonglong, qulonglong, qulonglong))); connect(transfer, SIGNAL(announce(PVSMsg)), _pvsServerConnection, SLOT(sendMessage(PVSMsg))); + connect(transfer, SIGNAL(finished(qulonglong)), SLOT(outgoingMulticastTransferDelete(qulonglong))); + connect(transfer, SIGNAL(failed(qulonglong, QString const)), SLOT(outgoingMulticastTransferDelete(qulonglong))); QTimer::singleShot(0, transfer, SLOT(start())); errorReason = ""; return true; @@ -687,3 +689,12 @@ quint64 PVS::generateMcastTransferID() return (nodeID & Q_UINT64_C(0xffffffffffff0000)) | (quint64)(++counter); } +void PVS::outgoingMulticastTransferDelete(qulonglong transferID) +{ + PVSOutgoingMulticastTransfer* transfer = _outgoingTransfers.value(transferID, 0); + if (!transfer) + return; + + _outgoingTransfers.remove(transferID); + transfer->deleteLater(); +} diff --git a/src/pvs.h b/src/pvs.h index 93596d2..4e07fd8 100644 --- a/src/pvs.h +++ b/src/pvs.h @@ -159,5 +159,8 @@ private: QHash _outgoingTransfers; static quint64 generateMcastTransferID(); +private Q_SLOTS: + // housekeeping + void outgoingMulticastTransferDelete(qulonglong transferID); }; #endif /* PVSCLIENT_H_ */ -- cgit v1.2.3-55-g7522 From 755f07b1a25c414b0c7cbe28db4a7ecbc32975c7 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Mon, 19 Jul 2010 11:53:55 +0200 Subject: Implement initial multicast receive functionality in PVS daemon --- CMakeLists.txt | 2 + src/net/mcast/McastReceiver.cpp | 49 +++++++++-- src/net/mcast/McastReceiver.h | 5 +- src/net/pvsIncomingMulticastTransfer.cpp | 134 +++++++++++++++++++++++++++++++ src/net/pvsIncomingMulticastTransfer.h | 71 ++++++++++++++++ src/pvs.cpp | 85 ++++++++++++++++++++ src/pvs.h | 11 +++ 7 files changed, 351 insertions(+), 6 deletions(-) create mode 100644 src/net/pvsIncomingMulticastTransfer.cpp create mode 100644 src/net/pvsIncomingMulticastTransfer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 93e281e..3f2089a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,6 +100,7 @@ SET( PVS_SRCS src/util/TextFile.cpp src/util/serviceDiscoveryUtil.cpp src/net/pvsOutgoingMulticastTransfer.cpp + src/net/pvsIncomingMulticastTransfer.cpp ) # pvsgui @@ -194,6 +195,7 @@ SET( PVS_MOC_HDRS src/net/pvsServiceDiscovery.h src/net/pvsDiscoveredServer.h src/net/pvsOutgoingMulticastTransfer.h + src/net/pvsIncomingMulticastTransfer.h ) SET( PVSGUI_MOC_HDRS diff --git a/src/net/mcast/McastReceiver.cpp b/src/net/mcast/McastReceiver.cpp index 6070208..1f27127 100644 --- a/src/net/mcast/McastReceiver.cpp +++ b/src/net/mcast/McastReceiver.cpp @@ -27,7 +27,7 @@ McastReceiver::McastReceiver(QIODevice* iodev, McastConfiguration* config, QObject* parent) : QObject(parent), - _config(config ? new McastConfiguration(*config) : new McastConfiguration()), + _config(config ? new McastConfiguration(*config) : 0), _socket(0), _curoffs(0), _closed(false), @@ -43,13 +43,52 @@ McastReceiver::~McastReceiver() delete _config; } -void McastReceiver::start() +void McastReceiver::config(McastConfiguration const* config) { + if (_config) + delete _config; + _config = new McastConfiguration(*config, this); +} + +bool McastReceiver::start() +{ + McastConfiguration *config = _config; + if (!config) + config = new McastConfiguration(); + + if (_socket) + { + delete _socket; + } _socket = new McastPGMSocket(this); - connect(_socket, SIGNAL(receivedPacket(QByteArray)), this, SLOT(receivedPacket(QByteArray))); - connect(_socket, SIGNAL(connectionReset()), this, SLOT(connectionReset())); + + connect(_socket, SIGNAL(receivedPacket(QByteArray)), SLOT(receivedPacket(QByteArray))); + connect(_socket, SIGNAL(connectionReset()), SLOT(connectionReset())); // connect(_socket, SIGNAL(connectionFinished()), this, SLOT(connectionFinished())); - _socket->open(_config, McastPGMSocket::PSOCK_READ); + if (_socket->open(_config, McastPGMSocket::PSOCK_READ)) + { + return true; + } + else + { + disconnect(_socket, SIGNAL(receivedPacket(QByteArray)), this, SLOT(receivedPacket(QByteArray))); + disconnect(_socket, SIGNAL(connectionReset()), this, SLOT(connectionReset())); + return false; + } +} + +void McastReceiver::abort() +{ + if (_socket) + { + delete _socket; + _socket = 0; + } + + if (_iodev) + { + _iodev->close(); + } } void McastReceiver::receivedPacket(QByteArray const& bytes) diff --git a/src/net/mcast/McastReceiver.h b/src/net/mcast/McastReceiver.h index 48ff7c5..247733d 100644 --- a/src/net/mcast/McastReceiver.h +++ b/src/net/mcast/McastReceiver.h @@ -47,6 +47,8 @@ public: return _config; } + void config(McastConfiguration const* config); + static inline bool is_error(Result result) { return result != RES_OK; @@ -57,7 +59,8 @@ signals: void progress(quint64 offset); public slots: - void start(); + bool start(); + void abort(); private: McastConfiguration* _config; diff --git a/src/net/pvsIncomingMulticastTransfer.cpp b/src/net/pvsIncomingMulticastTransfer.cpp new file mode 100644 index 0000000..01507a9 --- /dev/null +++ b/src/net/pvsIncomingMulticastTransfer.cpp @@ -0,0 +1,134 @@ +/* +# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# src/net/pcsIncomingMulticastTransfer.h +# - wrap McastReceiver functionality in PVS daemon +# ----------------------------------------------------------------------------- +*/ + +#include +#include +#include + +#include "pvsIncomingMulticastTransfer.h" +#include + +PVSIncomingMulticastTransfer::PVSIncomingMulticastTransfer(QString const& sender, qulonglong transferID, qulonglong size, + ushort port, McastConfiguration const* configTemplate, QObject* parent) : + QObject(parent), + _sender(sender), + _transferID(transferID), + _bytes(0), + _size(size), + _port(port), + _temporaryFile(new QTemporaryFile(QFileInfo(QDir::homePath(), "incoming.mcastft.XXXXXX").absolutePath(), this)), + _finalFile(0), + _receiver(0), + _config(configTemplate ? + new McastConfiguration(*configTemplate) : + new McastConfiguration()), + _progressTimer(new QTimer(this)) +{ + _config->multicastUDPPortBase(port); + _config->multicastDPort(port); + _config->multicastSPort(port); + + connect(_progressTimer, SIGNAL(timeout()), SLOT(updateProgress())); +} + +PVSIncomingMulticastTransfer::~PVSIncomingMulticastTransfer() +{ + // TODO Auto-generated destructor stub +} + +bool PVSIncomingMulticastTransfer::start() +{ + QFile *dest = _finalFile ? _finalFile : _temporaryFile; + _receiver = new McastReceiver(dest, new McastConfiguration(*_config), this); + connect(_receiver, SIGNAL(finished(int)), SLOT(receiverFinished(int))); + connect(_receiver, SIGNAL(progress(quint64)), SLOT(receiverProgress(quint64))); + + if (!_receiver->start()) + { + emit retry(_sender, _transferID); + return false; + } + else + { + _progressTimer->start(333); + return true; + } +} + +void PVSIncomingMulticastTransfer::abort() +{ + delete _receiver; + _receiver = 0; + + delete _progressTimer; + _progressTimer = 0; + + if (_temporaryFile) + { + _temporaryFile->remove(); + } + delete _temporaryFile; + + if (_finalFile) + { + _finalFile->remove(); + } + delete _finalFile; +} + +void PVSIncomingMulticastTransfer::updatePort(ushort port) +{ + _config->multicastUDPPortBase(port); + _config->multicastSPort(port); + _config->multicastDPort(port); +} + +void PVSIncomingMulticastTransfer::receiverProgressed(quint64 bytes) +{ + _bytes = bytes; +} + +void PVSIncomingMulticastTransfer::receiverFinished(int how) +{ + switch(how) + { + case McastReceiver::RES_OK: + emit finished(_transferID); + break; + case McastReceiver::RES_ABORTED: + emit failed(_transferID, tr("Aborted")); + break; + case McastReceiver::RES_MD5_MISMATCH: + case McastReceiver::RES_CHECKSUM_MISMATCH: + emit failed(_transferID, tr("Unrecoverable data corruption")); + break; + case McastReceiver::RES_CONNECTION_RESET: + emit failed(_transferID, tr("Connection was reset")); + break; + case McastReceiver::RES_OFFSET_MISMATCH: + emit failed(_transferID, tr("Unrecoverable data loss. Try a lower transfer rate")); + break; + } +} + +void PVSIncomingMulticastTransfer::updateProgress() +{ + if (!_started) + { + emit started(_transferID); + } + emit progress(_transferID, _bytes, _size); +} diff --git a/src/net/pvsIncomingMulticastTransfer.h b/src/net/pvsIncomingMulticastTransfer.h new file mode 100644 index 0000000..9a33348 --- /dev/null +++ b/src/net/pvsIncomingMulticastTransfer.h @@ -0,0 +1,71 @@ +/* +# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# src/net/pcsIncomingMulticastTransfer.h +# - wrap McastReceiver functionality in PVS daemon +# ----------------------------------------------------------------------------- +*/ + +#ifndef PVSINCOMINGMULTICASTTRANSFER_H_ +#define PVSINCOMINGMULTICASTTRANSFER_H_ + +#include +#include + +#include + +class McastConfiguration; +class McastReceiver; +class QFile; +class QTimer; + +class PVSIncomingMulticastTransfer : public QObject +{ + Q_OBJECT +public: + PVSIncomingMulticastTransfer(QString const& sender, qulonglong transferID, qulonglong size, ushort port, + McastConfiguration const* configTemplate, QObject* parent = 0); + virtual ~PVSIncomingMulticastTransfer(); + + void setFinalFile(QString const& filename); + +signals: + void retry(QString const& sender, qulonglong transferID); + void started(qulonglong transferID); + void progress(qulonglong transferID, qulonglong bytes, qulonglong of); + void finished(qulonglong transferID); + void failed(qulonglong transferID, QString const& reason); + +public slots: + void updatePort(ushort port); + bool start(); + void abort(); + +private slots: + void receiverProgressed(quint64 bytes); + void receiverFinished(int reason); + void updateProgress(); + +private: + QString _sender; + qulonglong _transferID; + qulonglong _bytes; + qulonglong _size; + ushort _port; + QFile* _temporaryFile; + QFile* _finalFile; + McastReceiver* _receiver; + McastConfiguration* _config; + bool _started; + QTimer* _progressTimer; +}; + +#endif /* PVSINCOMINGMULTICASTTRANSFER_H_ */ diff --git a/src/pvs.cpp b/src/pvs.cpp index af5958e..8f06f74 100644 --- a/src/pvs.cpp +++ b/src/pvs.cpp @@ -17,6 +17,7 @@ #include "src/net/pvsDiscoveredServer.h" #include "src/net/mcast/McastConfiguration.h" #include "src/net/pvsOutgoingMulticastTransfer.h" +#include "src/net/pvsIncomingMulticastTransfer.h" // D-Bus #include "pvsadaptor.h" @@ -190,6 +191,45 @@ void PVS::onCommand(PVSMsg cmdMessage) transfer->retry(); } } + if (ident.compare("MCASTFTANNOUNCE") == 0) + { + QStringList fields = message.split(':'); + bool ok; + QString sender; + qulonglong transferID; + QString basename; + qulonglong size; + ushort port; + + if (!fields.size() == 5) + { + goto malformedAnnounce; + } + sender = fields[0]; + transferID = fields[1].toULongLong(&ok); + if (!ok) + { + goto malformedAnnounce; + } + basename = fields[2]; + size = fields[3].toULongLong(&ok); + if (!ok) + { + goto malformedAnnounce; + } + port = fields[4].toUShort(&ok); + if (!ok) + { + goto malformedAnnounce; + } + + onIncomingMulticastTransfer(sender, transferID, basename, size, port); + return; + + malformedAnnounce: + qDebug() << "Ignoring malformed MCASTFTANNOUNCE command: " << message; + return; + } #ifdef never // prototype @@ -676,6 +716,39 @@ void PVS::cancelOutgoingMulticastTransfer(quint64 transferID) } } +void PVS::onIncomingMulticastTransfer(QString const& sender, qulonglong transferID, + QString const& basename, qulonglong size, ushort port) +{ + PVSIncomingMulticastTransfer* transfer; + if (_incomingTransfers.value(transferID, 0)) + { + transfer->updatePort(port); + QTimer::singleShot(0, transfer, SLOT(start())); + } + else + { + transfer = new PVSIncomingMulticastTransfer(sender, transferID, size, port, _masterMcastConfig, this); + _incomingTransfers.insert(transferID, transfer); + + connect(transfer, SIGNAL(retry(QString const&, qulonglong)), SLOT(onIncomingMulticastTransferRetry(QString const&, qulonglong))); + connect(transfer, SIGNAL(started(qulonglong)), SIGNAL(incomingMulticastTransferStarted(qulonglong))); + connect(transfer, SIGNAL(progress(qulonglong, qulonglong, qulonglong)), SIGNAL(incomingMulticastTransferProgress(qulonglong, qulonglong, qulonglong))); + connect(transfer, SIGNAL(finished(qulonglong)), SIGNAL(incomingMulticastTransferFinished(qulonglong))); + connect(transfer, SIGNAL(failed(qulonglong, QString const&)), SIGNAL(incomingMulticastTransferFailed(qulonglong, QString))); + connect(transfer, SIGNAL(finished(qulonglong)), SLOT(incomingMulticastTransferDelete(qulonglong))); + connect(transfer, SIGNAL(failed(qulonglong, QString const&)), SLOT(incomingMulticastTransferDelete(qulonglong))); + + emit incomingMulticastTransferNew(transferID, sender, basename, size); + QTimer::singleShot(0, transfer, SLOT(start())); + } +} + +void PVS::onIncomingMulticastTransferRetry(QString const& sender, qulonglong transferID) +{ + PVSMsg retryMessage(PVSCOMMAND, "MCASTFTRETRY", QString("%1:%2").arg(sender).arg(transferID)); + _pvsServerConnection->sendMessage(retryMessage); +} + quint64 PVS::generateMcastTransferID() { static quint64 nodeID = 0; @@ -698,3 +771,15 @@ void PVS::outgoingMulticastTransferDelete(qulonglong transferID) _outgoingTransfers.remove(transferID); transfer->deleteLater(); } + +void PVS::incomingMulticastTransferDelete(qulonglong transferID) +{ + PVSIncomingMulticastTransfer* transfer = _incomingTransfers.value(transferID, 0); + if (!transfer) + { + return; + } + + _incomingTransfers.remove(transferID); + transfer->deleteLater(); +} diff --git a/src/pvs.h b/src/pvs.h index 4e07fd8..dc272f0 100644 --- a/src/pvs.h +++ b/src/pvs.h @@ -30,6 +30,7 @@ class PVSServiceDiscovery; class PVSDiscoveredServer; class McastConfiguration; class PVSOutgoingMulticastTransfer; +class PVSIncomingMulticastTransfer; /** * PVSClient @@ -105,6 +106,11 @@ Q_SIGNALS: void outgoingMulticastTransferProgress(qulonglong transferID, qulonglong bytes, qulonglong of); void outgoingMulticastTransferFinished(qulonglong transferID); void outgoingMulticastTransferFailed(qulonglong transferID, QString reason); + void incomingMulticastTransferNew(qulonglong transferID, QString sender, QString basename, qulonglong size); + void incomingMulticastTransferStarted(qulonglong transferID); + void incomingMulticastTransferProgress(qulonglong transferID, qulonglong bytes, qulonglong of); + void incomingMulticastTransferFinished(qulonglong transferID); + void incomingMulticastTransferFailed(qulonglong transferID, QString reason); protected: @@ -157,10 +163,15 @@ private: McastConfiguration* _masterMcastConfig; QHash _outgoingTransfers; + QHash _incomingTransfers; + void onIncomingMulticastTransfer(QString const& sender, qulonglong transferID, QString const& basename, qulonglong size, ushort port); + void onIncomingMulticastTransferRetry(QString const& sender, qulonglong transferID); static quint64 generateMcastTransferID(); + private Q_SLOTS: // housekeeping void outgoingMulticastTransferDelete(qulonglong transferID); + void incomingMulticastTransferDelete(qulonglong transferID); }; #endif /* PVSCLIENT_H_ */ -- cgit v1.2.3-55-g7522 From 6bddb17e02c8a1e9ce64e3881946e6f9c463feb1 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Mon, 19 Jul 2010 11:54:18 +0200 Subject: Fix parsing of incoming MCASTFTRETRY messages --- src/pvs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pvs.cpp b/src/pvs.cpp index 8f06f74..0e5aaf5 100644 --- a/src/pvs.cpp +++ b/src/pvs.cpp @@ -185,7 +185,7 @@ void PVS::onCommand(PVSMsg cmdMessage) QStringList fields = message.split(':'); if (fields[0].compare(getUserName()) == 0) { - quint64 id = fields[0].toULongLong(); + quint64 id = fields[1].toULongLong(); PVSOutgoingMulticastTransfer* transfer = _outgoingTransfers.value(id, 0); if (transfer) transfer->retry(); -- cgit v1.2.3-55-g7522 From 1a3c2971c24583c6d6e33aad65169ac8cecd949c Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Mon, 19 Jul 2010 11:54:55 +0200 Subject: McastConfiguration's copy constructor should allow specifying a parent QObject --- src/net/mcast/McastConfiguration.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/net/mcast/McastConfiguration.h b/src/net/mcast/McastConfiguration.h index b010f60..6884036 100644 --- a/src/net/mcast/McastConfiguration.h +++ b/src/net/mcast/McastConfiguration.h @@ -43,8 +43,8 @@ public: { } - McastConfiguration(McastConfiguration const& other) : - QObject(), + McastConfiguration(McastConfiguration const& other, QObject* parent = 0) : + QObject(parent), _multicastInterface(other._multicastInterface), _multicastAddress(other._multicastAddress), _multicastRate(other._multicastRate), -- cgit v1.2.3-55-g7522 From f4474ef4943890548926190e8ec1b1e779c8e647 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Mon, 19 Jul 2010 11:57:29 +0200 Subject: Forward MCASTFTANNOUNCE and MCASTFTRETRY messages in pvsmgr and pvsmgrtouch. --- src/core/pvsConnectionManager.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/core/pvsConnectionManager.cpp b/src/core/pvsConnectionManager.cpp index 8a35ca9..5bf6418 100644 --- a/src/core/pvsConnectionManager.cpp +++ b/src/core/pvsConnectionManager.cpp @@ -218,6 +218,25 @@ void PVSConnectionManager::onCommand(PVSMsg command) break; } } + else if (ident == "MCASTFTANNOUNCE") + { + _pvsServer.sendToAll(command); + } + else if (ident == "MCASTFTRETRY") + { + QStringList fields = message.split(':'); + if (!fields.size() == 2) + { + qDebug() << "Malformed MCASTFTRETRY message:" << message; + return; + } + + PVSClient* client = getClientFromUsername(fields[0]); + if (client) + { + client->sendMessage(command); + } + } } void PVSConnectionManager::onChat(PVSMsg chatMsg) { -- cgit v1.2.3-55-g7522 From 88dbb997a823ff77f752ac08105d12474345dfcb Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 4 Aug 2010 17:24:21 +0200 Subject: Implement GUI for receiving incoming multicast transfers --- src/gui/clientFileReceiveDialog.cpp | 83 +++++++++++++++++++++++++++++++- src/gui/clientFileReceiveDialog.h | 14 ++++++ src/gui/ui/clientFileReceiveDialog.ui | 11 ++++- src/net/pvsIncomingMulticastTransfer.cpp | 37 +++++++------- src/net/pvsIncomingMulticastTransfer.h | 6 +-- src/pvs.cpp | 19 +++++++- src/pvs.h | 5 +- src/pvsgui.cpp | 7 +++ src/pvsgui.h | 1 + 9 files changed, 154 insertions(+), 29 deletions(-) diff --git a/src/gui/clientFileReceiveDialog.cpp b/src/gui/clientFileReceiveDialog.cpp index 669ca81..ff3226a 100644 --- a/src/gui/clientFileReceiveDialog.cpp +++ b/src/gui/clientFileReceiveDialog.cpp @@ -16,6 +16,9 @@ */ #include "clientFileReceiveDialog.h" +#include +#include +#include ClientFileReceiveDialog::ClientFileReceiveDialog(QTcpSocket *socket, QWidget *parent) : QDialog(parent) @@ -37,9 +40,44 @@ ClientFileReceiveDialog::ClientFileReceiveDialog(QTcpSocket *socket, QWidget *pa connect(this, SIGNAL(finished(int)), this, SLOT(deleteLater())); } +ClientFileReceiveDialog::ClientFileReceiveDialog(QString const& sender, qulonglong transferID, + QString const& filename, qulonglong size, OrgOpenslxPvsInterface* ifaceDBus, QWidget* parent) +{ + setupUi(this); + + _transferID = transferID; + _filename = filename; + _ifaceDBus = ifaceDBus; + _bytesToRead = size; + _socket = 0; + _file = 0; + div = 1; + while((size / div) > INT_MAX) + { + div <<= 1; + } + + connect(ifaceDBus, SIGNAL(incomingMulticastTransferStarted(qulonglong)), SLOT(mcastTransferStarted(qulonglong))); + connect(ifaceDBus, SIGNAL(incomingMulticastTransferProgress(qulonglong, qulonglong, qulonglong)), + SLOT(mcastTransferProgress(qulonglong, qulonglong, qulonglong))); + connect(ifaceDBus, SIGNAL(incomingMulticastTransferFinished(qulonglong)), SLOT(mcastTransferFinished(qulonglong))); + connect(ifaceDBus, SIGNAL(incomingMulticastTransferFailed(qulonglong, QString)), SLOT(mcastTransferFailed(qulonglong, QString))); + connect(cancelButton, SIGNAL(clicked()), SLOT(cancelTransfer())); + + qDebug("[%s] New multicast incoming transfer: %s from %s", metaObject()->className(), + filename.toLocal8Bit().constData(), sender.toLocal8Bit().constData()); + + + filenameLabel->setText(QFileInfo(filename).baseName()); + labelNick->setText(sender); + progressBar->setRange(0, 0); +} + + ClientFileReceiveDialog::~ClientFileReceiveDialog() { - _socket->deleteLater(); + if(_socket) + _socket->deleteLater(); qDebug("[%s] Deleted!", metaObject()->className()); } @@ -168,6 +206,49 @@ void ClientFileReceiveDialog::error(QAbstractSocket::SocketError error) close(); } +void ClientFileReceiveDialog::mcastTransferStarted(qulonglong transferID) +{ + if(transferID != _transferID) + return; +} + +void ClientFileReceiveDialog::mcastTransferProgress(qulonglong transferID, qulonglong bytes, qulonglong of) +{ + if(transferID != _transferID) + return; + + progressBar->setRange(0, of); + progressBar->setValue(bytes); +} + +void ClientFileReceiveDialog::mcastTransferFinished(qulonglong transferID) +{ + if(transferID != _transferID) + return; + + QString filename = QFileDialog::getSaveFileName(this, tr("Where should I save %1?").arg(_filename), _filename); + QFile* file = new QFile(_filename); + if(!file->rename(filename)) + { + QMessageBox::warning(this, tr("Could not rename file"), tr("Failed to rename %1 to %2").arg(_filename).arg(filename)); + } + accept(); +} + +void ClientFileReceiveDialog::mcastTransferFailed(qulonglong transferID, QString reason) +{ + if(transferID != _transferID) + return; + + QMessageBox::warning(this, tr("File transfer failed"), tr("File transfer failed for the following reason:\n%1").arg(reason)); + reject(); +} + +void ClientFileReceiveDialog::cancelTransfer() +{ + _ifaceDBus->cancelIncomingMulticastTransfer(_transferID); +} + QString ClientFileReceiveDialog::formatSize(qint64 size) { if (size >= 1000000000) // GB diff --git a/src/gui/clientFileReceiveDialog.h b/src/gui/clientFileReceiveDialog.h index c13d7b7..c9ed220 100644 --- a/src/gui/clientFileReceiveDialog.h +++ b/src/gui/clientFileReceiveDialog.h @@ -18,6 +18,8 @@ #include #include "ui_clientFileReceiveDialog.h" +class OrgOpenslxPvsInterface; + class ClientFileReceiveDialog: public QDialog, private Ui::ClientFileReceiveDialogClass { @@ -25,6 +27,7 @@ Q_OBJECT public: ClientFileReceiveDialog(QTcpSocket *socket, QWidget *parent = 0); + ClientFileReceiveDialog(QString const& sender, qulonglong transferID, QString const& basename, qulonglong size, OrgOpenslxPvsInterface* ifaceDBus, QWidget* parent = 0); ~ClientFileReceiveDialog(); private Q_SLOTS: @@ -33,6 +36,13 @@ private Q_SLOTS: void close(); void error(QAbstractSocket::SocketError error); + // multicast: + void mcastTransferStarted(qulonglong transferID); + void mcastTransferProgress(qulonglong transferID, qulonglong bytes, qulonglong of); + void mcastTransferFinished(qulonglong transferID); + void mcastTransferFailed(qulonglong transferID, QString reason); + void cancelTransfer(); + private: void sendAck(bool b); QString formatSize(qint64 size); @@ -42,6 +52,10 @@ private: qint64 _bytesToRead; int div; + // multicast: + OrgOpenslxPvsInterface* _ifaceDBus; + QString _filename; + qulonglong _transferID; }; #endif /* CLIENTFILERECEIVEDIALOG_H_ */ diff --git a/src/gui/ui/clientFileReceiveDialog.ui b/src/gui/ui/clientFileReceiveDialog.ui index af3a135..a137def 100644 --- a/src/gui/ui/clientFileReceiveDialog.ui +++ b/src/gui/ui/clientFileReceiveDialog.ui @@ -6,8 +6,8 @@ 0 0 - 208 - 108 + 528 + 117 @@ -61,6 +61,13 @@ + + + + + + + diff --git a/src/net/pvsIncomingMulticastTransfer.cpp b/src/net/pvsIncomingMulticastTransfer.cpp index 01507a9..10b5307 100644 --- a/src/net/pvsIncomingMulticastTransfer.cpp +++ b/src/net/pvsIncomingMulticastTransfer.cpp @@ -21,7 +21,7 @@ #include "pvsIncomingMulticastTransfer.h" #include -PVSIncomingMulticastTransfer::PVSIncomingMulticastTransfer(QString const& sender, qulonglong transferID, qulonglong size, +PVSIncomingMulticastTransfer::PVSIncomingMulticastTransfer(QString const& sender, qulonglong transferID, qulonglong size, QString const& filename, ushort port, McastConfiguration const* configTemplate, QObject* parent) : QObject(parent), _sender(sender), @@ -29,19 +29,21 @@ PVSIncomingMulticastTransfer::PVSIncomingMulticastTransfer(QString const& sender _bytes(0), _size(size), _port(port), - _temporaryFile(new QTemporaryFile(QFileInfo(QDir::homePath(), "incoming.mcastft.XXXXXX").absolutePath(), this)), - _finalFile(0), + _file(new QFile(filename, this)), _receiver(0), _config(configTemplate ? new McastConfiguration(*configTemplate) : new McastConfiguration()), _progressTimer(new QTimer(this)) { + _file->open(QIODevice::WriteOnly); + _config->multicastUDPPortBase(port); - _config->multicastDPort(port); - _config->multicastSPort(port); + // _config->multicastDPort(port+1); + // _config->multicastSPort(port+2); connect(_progressTimer, SIGNAL(timeout()), SLOT(updateProgress())); + connect(this, SIGNAL(failed(qulonglong, QString const&)), SLOT(removeFile())); } PVSIncomingMulticastTransfer::~PVSIncomingMulticastTransfer() @@ -51,10 +53,10 @@ PVSIncomingMulticastTransfer::~PVSIncomingMulticastTransfer() bool PVSIncomingMulticastTransfer::start() { - QFile *dest = _finalFile ? _finalFile : _temporaryFile; - _receiver = new McastReceiver(dest, new McastConfiguration(*_config), this); + _file->open(QIODevice::WriteOnly); + _receiver = new McastReceiver(_file, new McastConfiguration(*_config), this); connect(_receiver, SIGNAL(finished(int)), SLOT(receiverFinished(int))); - connect(_receiver, SIGNAL(progress(quint64)), SLOT(receiverProgress(quint64))); + connect(_receiver, SIGNAL(progress(quint64)), SLOT(receiverProgressed(quint64))); if (!_receiver->start()) { @@ -76,17 +78,8 @@ void PVSIncomingMulticastTransfer::abort() delete _progressTimer; _progressTimer = 0; - if (_temporaryFile) - { - _temporaryFile->remove(); - } - delete _temporaryFile; - - if (_finalFile) - { - _finalFile->remove(); - } - delete _finalFile; + if(_file) + delete _file; } void PVSIncomingMulticastTransfer::updatePort(ushort port) @@ -124,6 +117,12 @@ void PVSIncomingMulticastTransfer::receiverFinished(int how) } } +void PVSIncomingMulticastTransfer::removeFile() +{ + if(_file) + _file->remove(); +} + void PVSIncomingMulticastTransfer::updateProgress() { if (!_started) diff --git a/src/net/pvsIncomingMulticastTransfer.h b/src/net/pvsIncomingMulticastTransfer.h index 9a33348..f96e176 100644 --- a/src/net/pvsIncomingMulticastTransfer.h +++ b/src/net/pvsIncomingMulticastTransfer.h @@ -31,7 +31,7 @@ class PVSIncomingMulticastTransfer : public QObject { Q_OBJECT public: - PVSIncomingMulticastTransfer(QString const& sender, qulonglong transferID, qulonglong size, ushort port, + PVSIncomingMulticastTransfer(QString const& sender, qulonglong transferID, qulonglong size, QString const& filename, ushort port, McastConfiguration const* configTemplate, QObject* parent = 0); virtual ~PVSIncomingMulticastTransfer(); @@ -53,6 +53,7 @@ private slots: void receiverProgressed(quint64 bytes); void receiverFinished(int reason); void updateProgress(); + void removeFile(); private: QString _sender; @@ -60,8 +61,7 @@ private: qulonglong _bytes; qulonglong _size; ushort _port; - QFile* _temporaryFile; - QFile* _finalFile; + QFile* _file; McastReceiver* _receiver; McastConfiguration* _config; bool _started; diff --git a/src/pvs.cpp b/src/pvs.cpp index 0e5aaf5..852c9f4 100644 --- a/src/pvs.cpp +++ b/src/pvs.cpp @@ -716,9 +716,23 @@ void PVS::cancelOutgoingMulticastTransfer(quint64 transferID) } } +void PVS::cancelIncomingMulticastTransfer(qulonglong transferID) +{ + PVSIncomingMulticastTransfer* transfer = _incomingTransfers.value(transferID, 0); + + if(transfer) + { + _incomingTransfers.remove(transferID); + delete transfer; + } +} + void PVS::onIncomingMulticastTransfer(QString const& sender, qulonglong transferID, QString const& basename, qulonglong size, ushort port) { + if (_outgoingTransfers.contains(transferID)) + return; + PVSIncomingMulticastTransfer* transfer; if (_incomingTransfers.value(transferID, 0)) { @@ -727,7 +741,8 @@ void PVS::onIncomingMulticastTransfer(QString const& sender, qulonglong transfer } else { - transfer = new PVSIncomingMulticastTransfer(sender, transferID, size, port, _masterMcastConfig, this); + QString filename = QFileInfo(QDir::home(), QString("%1.part.%2").arg(basename).arg(transferID, 0, 16)).absoluteFilePath(); + transfer = new PVSIncomingMulticastTransfer(sender, transferID, size, filename, port, _masterMcastConfig, this); _incomingTransfers.insert(transferID, transfer); connect(transfer, SIGNAL(retry(QString const&, qulonglong)), SLOT(onIncomingMulticastTransferRetry(QString const&, qulonglong))); @@ -738,7 +753,7 @@ void PVS::onIncomingMulticastTransfer(QString const& sender, qulonglong transfer connect(transfer, SIGNAL(finished(qulonglong)), SLOT(incomingMulticastTransferDelete(qulonglong))); connect(transfer, SIGNAL(failed(qulonglong, QString const&)), SLOT(incomingMulticastTransferDelete(qulonglong))); - emit incomingMulticastTransferNew(transferID, sender, basename, size); + emit incomingMulticastTransferNew(transferID, sender, filename, size); QTimer::singleShot(0, transfer, SLOT(start())); } } diff --git a/src/pvs.h b/src/pvs.h index dc272f0..ef6454e 100644 --- a/src/pvs.h +++ b/src/pvs.h @@ -86,6 +86,7 @@ public Q_SLOTS: // Multicast File Transfer bool createMulticastTransfer(QString const& objectPath, quint64& transferID, QString& errorReason); void cancelOutgoingMulticastTransfer(quint64 transferID); + void cancelIncomingMulticastTransfer(qulonglong transferID); Q_SIGNALS: @@ -106,7 +107,7 @@ Q_SIGNALS: void outgoingMulticastTransferProgress(qulonglong transferID, qulonglong bytes, qulonglong of); void outgoingMulticastTransferFinished(qulonglong transferID); void outgoingMulticastTransferFailed(qulonglong transferID, QString reason); - void incomingMulticastTransferNew(qulonglong transferID, QString sender, QString basename, qulonglong size); + void incomingMulticastTransferNew(qulonglong transferID, QString sender, QString filename, qulonglong size); void incomingMulticastTransferStarted(qulonglong transferID); void incomingMulticastTransferProgress(qulonglong transferID, qulonglong bytes, qulonglong of); void incomingMulticastTransferFinished(qulonglong transferID); @@ -166,12 +167,12 @@ private: QHash _incomingTransfers; void onIncomingMulticastTransfer(QString const& sender, qulonglong transferID, QString const& basename, qulonglong size, ushort port); - void onIncomingMulticastTransferRetry(QString const& sender, qulonglong transferID); static quint64 generateMcastTransferID(); private Q_SLOTS: // housekeeping void outgoingMulticastTransferDelete(qulonglong transferID); void incomingMulticastTransferDelete(qulonglong transferID); + void onIncomingMulticastTransferRetry(QString const& sender, qulonglong transferID); }; #endif /* PVSCLIENT_H_ */ diff --git a/src/pvsgui.cpp b/src/pvsgui.cpp index 25f1cd6..e949d5b 100644 --- a/src/pvsgui.cpp +++ b/src/pvsgui.cpp @@ -119,6 +119,7 @@ PVSGUI::PVSGUI(QWidget *parent) : connect(_ifaceDBus, SIGNAL(disconnected()), this, SLOT(disconnected())); connect(_ifaceDBus, SIGNAL(addHost(QString)), this, SLOT(addHost(QString))); connect(_ifaceDBus, SIGNAL(delHost(QString)), this, SLOT(delHost(QString))); + connect(_ifaceDBus, SIGNAL(incomingMulticastTransferNew(qulonglong, QString, QString, qulonglong)), SLOT(incomingMulticastFile(qulonglong, QString, QString, qulonglong))); // show toolbar setWindowFlags(Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint); @@ -421,6 +422,12 @@ void PVSGUI::receiveFile() d->open(); } +void PVSGUI::incomingMulticastFile(qulonglong transferID, QString sender, QString basename, qulonglong size) +{ + ClientFileReceiveDialog *d = new ClientFileReceiveDialog(sender, transferID, basename, size, _ifaceDBus, this); + d->open(); +} + //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// diff --git a/src/pvsgui.h b/src/pvsgui.h index f9a0ab8..2883b60 100644 --- a/src/pvsgui.h +++ b/src/pvsgui.h @@ -65,6 +65,7 @@ private Q_SLOTS: void setVncAllow(int i); void sendFile(); void receiveFile(); + void incomingMulticastFile(qulonglong, QString sender, QString basename, qulonglong size); private: void setupMenu(); -- cgit v1.2.3-55-g7522 From 235f5e82c4be3ceea54e4492c2f72be8218f4bef Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 4 Aug 2010 17:24:56 +0200 Subject: Fix a bug where transfer IDs would linger in the pvs daemon's data structures after cancellation. --- src/pvs.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pvs.cpp b/src/pvs.cpp index 852c9f4..202aa32 100644 --- a/src/pvs.cpp +++ b/src/pvs.cpp @@ -712,6 +712,7 @@ void PVS::cancelOutgoingMulticastTransfer(quint64 transferID) if (transfer) { + _outgoingTransfers.remove(transferID); delete transfer; } } -- cgit v1.2.3-55-g7522 From f8e6e94b25a092d51acdec600465074b4eb32cb8 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 4 Aug 2010 17:28:45 +0200 Subject: Make PGM interface configurable --- src/gui/clientConfigDialog.cpp | 61 +++++++++++++++++++++++++++++++++++++- src/gui/clientConfigDialog.h | 2 ++ src/gui/ui/clientConfigDialog.ui | 64 ++++++++++++++++++++++++++++++++++++++-- src/pvs.cpp | 7 +++++ src/pvs.h | 2 +- src/pvsgui.cpp | 3 ++ 6 files changed, 134 insertions(+), 5 deletions(-) diff --git a/src/gui/clientConfigDialog.cpp b/src/gui/clientConfigDialog.cpp index 76b4f5e..3867118 100644 --- a/src/gui/clientConfigDialog.cpp +++ b/src/gui/clientConfigDialog.cpp @@ -16,15 +16,32 @@ # ----------------------------------------------------------------------------- */ +#include #include "clientConfigDialog.h" +#include +#include +#include + +// For getting the network interface list: +#ifdef __linux +# include +# include +#endif + +using namespace std; ClientConfigDialog::ClientConfigDialog(QWidget *parent) : - QDialog(parent) + QDialog(parent), + _interfaceListModel(0) { setupUi(this); connect(this, SIGNAL(accepted()), this, SLOT(writeSettings())); connect(radioButtonOtherRO, SIGNAL(clicked()), this, SLOT(checkPermissions())); + connect(reloadInterfaceListButton, SIGNAL(clicked()), this, SLOT(reloadNetworkInterfaceList())); + reloadNetworkInterfaceList(); + interfaceList->setModel(_interfaceListModel); + interfaceList->setModelColumn(0); } @@ -66,6 +83,9 @@ void ClientConfigDialog::readSettings() checkBoxAllowFiletransfer->setChecked(_settings.value( "Permissions/allow_filetransfer").toBool()); + if(!_settings.value("Muticast/interface").isNull()) + interfaceList->setEditText(_settings.value("Multicast/interface").toString()); + qDebug("[%s] Setting read from: '%s'", metaObject()->className(), qPrintable(_settings.fileName())); } @@ -88,6 +108,7 @@ void ClientConfigDialog::writeSettings() _settings.setValue("Permissions/allow_chat", checkBoxAllowChat->isChecked()); _settings.setValue("Permissions/allow_filetransfer", checkBoxAllowFiletransfer->isChecked()); + _settings.setValue("Multicast/interface", interfaceList->currentText()); _settings.sync(); emit configChanged(); @@ -103,3 +124,41 @@ void ClientConfigDialog::checkPermissions() if (radioButtonLecturerNO->isChecked() && radioButtonOtherRO->isChecked()) radioButtonLecturerRO->setChecked(true); } + +void ClientConfigDialog::reloadNetworkInterfaceList() +{ +#ifdef __linux + static struct ifreq ifreqs[20]; + ifconf ifconfigs; + memset(&ifconfigs, 0, sizeof(ifconfigs)); + ifconfigs.ifc_len = sizeof(ifreqs); + ifconfigs.ifc_buf = (char*)&ifreqs; + + int nosock = socket(AF_INET, SOCK_STREAM, 0); + if (nosock < 0) + { + qWarning() << "Could not get a socket descriptor:" << strerror(errno); + } + int retval; + if ((retval = ioctl(nosock, SIOCGIFCONF, (char*)(&ifconfigs))) < 0) + { + qWarning() << "Could not get the list of interfaces:" << strerror(errno); + return; + } + + QStringList interfaces; + for(int i = 0; i < ifconfigs.ifc_len/sizeof(struct ifreq); i++) + { + char ifname[IFNAMSIZ + 1]; + strncpy(ifname, ifreqs[i].ifr_name, IFNAMSIZ); + ifname[IFNAMSIZ] = '\0'; + interfaces << QString::fromLocal8Bit(ifname); + } + if(!_interfaceListModel) + _interfaceListModel = new QStringListModel(interfaces, this); + else + _interfaceListModel->setStringList(interfaces); +#else +# warning "We have no way to get your system's network interface list. Some porting may be required." +#endif +} diff --git a/src/gui/clientConfigDialog.h b/src/gui/clientConfigDialog.h index 706bd8a..803f2c8 100644 --- a/src/gui/clientConfigDialog.h +++ b/src/gui/clientConfigDialog.h @@ -35,9 +35,11 @@ Q_SIGNALS: private Q_SLOTS: void checkPermissions(); + void reloadNetworkInterfaceList(); private: QSettings _settings; + QStringListModel* _interfaceListModel; }; diff --git a/src/gui/ui/clientConfigDialog.ui b/src/gui/ui/clientConfigDialog.ui index 3262b6b..bb4bdc9 100644 --- a/src/gui/ui/clientConfigDialog.ui +++ b/src/gui/ui/clientConfigDialog.ui @@ -6,8 +6,8 @@ 0 0 - 438 - 257 + 445 + 266 @@ -27,7 +27,7 @@ QTabWidget::North - 0 + 2 @@ -197,6 +197,64 @@ + + + + 0 + 0 + + + + false + + + Network + + + + + + + 0 + 0 + + + + true + + + QComboBox::NoInsert + + + true + + + + + + + + 0 + 0 + + + + Network Interface + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Reload List + + + + + diff --git a/src/pvs.cpp b/src/pvs.cpp index 202aa32..c217d52 100644 --- a/src/pvs.cpp +++ b/src/pvs.cpp @@ -728,6 +728,13 @@ void PVS::cancelIncomingMulticastTransfer(qulonglong transferID) } } +void PVS::setMulticastInterface(QString const& interfaceName) +{ + QSettings settings; + settings.setValue("multicast/interface", interfaceName); + settings.sync(); +} + void PVS::onIncomingMulticastTransfer(QString const& sender, qulonglong transferID, QString const& basename, qulonglong size, ushort port) { diff --git a/src/pvs.h b/src/pvs.h index ef6454e..4c61ffd 100644 --- a/src/pvs.h +++ b/src/pvs.h @@ -87,7 +87,7 @@ public Q_SLOTS: bool createMulticastTransfer(QString const& objectPath, quint64& transferID, QString& errorReason); void cancelOutgoingMulticastTransfer(quint64 transferID); void cancelIncomingMulticastTransfer(qulonglong transferID); - + void setMulticastInterface(QString const& interfaceName); Q_SIGNALS: void project(QString host, int port, QString passwd, bool fullscreen, diff --git a/src/pvsgui.cpp b/src/pvsgui.cpp index e949d5b..40112b4 100644 --- a/src/pvsgui.cpp +++ b/src/pvsgui.cpp @@ -143,6 +143,9 @@ void PVSGUI::updateConfig() setLocation(POSITION_TOP_CENTER); else setLocation(_settings.value("Display/location").toInt()); + + if (!_settings.value("Multicast/interface").isNull()) + _ifaceDBus->setMulticastInterface(_settings.value("Multicast/interface").toString()); } //////////////////////////////////////////////////////////////////////////////// -- cgit v1.2.3-55-g7522 From 442bbd9c203ab5cd6998aebb4b25918ce4ceda9f Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 25 Aug 2010 19:51:41 +0200 Subject: Add patch for OpenPGM to fix switch() fallthrough --- 3rdparty/01-libpgm-fix-switch-fallthrough.patch | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 3rdparty/01-libpgm-fix-switch-fallthrough.patch diff --git a/3rdparty/01-libpgm-fix-switch-fallthrough.patch b/3rdparty/01-libpgm-fix-switch-fallthrough.patch new file mode 100644 index 0000000..23dfc9c --- /dev/null +++ b/3rdparty/01-libpgm-fix-switch-fallthrough.patch @@ -0,0 +1,11 @@ +diff -Naur pgm/socket.c pgm-fixed/socket.c +--- pgm/socket.c 2010-05-22 06:42:00.000000000 +0200 ++++ pgm-fixed/socket.c 2010-07-14 19:24:43.710669589 +0200 +@@ -641,6 +641,7 @@ + break; + sock->spmr_expiry = *(const int*)optval; + status = TRUE; ++ break; + + /* size of receive window in sequence numbers. + * 0 < rxw_sqns < one less than half sequence space -- cgit v1.2.3-55-g7522 From ffee0868ef1341cfb7622821431cb73c52590962 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Sun, 3 Oct 2010 16:14:44 +0200 Subject: Assorted Multicast Fixes: - Upgrade bundled OpenPGM to SVN r1135 - Timing fixes: Make all rate-limited and timer-pending operation wait for at least 1ms to avoid busy-waiting - No distinction between sending and receiving sockets when setting up socket options (Receivers need to be able to send anyway when using PGMCC). - Switch from fixed-rate transmission to using PGMCC for congestion control. - Remove some obnoxious debugging outputs - Some white space fixes - Introduce a short waiting time before actually starting file transmission in order to allow enough SPM messages to be sent so that receivers can initialize properly. - Fix MCASTFTANNOUNCE message to include full file name instead of basename. - Fix generateMcastTransferID in order to gather more random IDs. PVSGUI may become confused if transfer IDs are reused. - Properly dispose of clientFileReceiveDialog when multicast transfer is finished. - Properly display transfer size in clientFileReceiveDialog --- 3rdparty/01-libpgm-fix-switch-fallthrough.patch | 11 - .../doc/draft-ietf-rmt-bb-pgmcc-03.txt | 1226 ---- 3rdparty/openpgm-svn-r1085/doc/rfc3208.txt | 6219 -------------------- 3rdparty/openpgm-svn-r1085/pgm/COPYING | 504 -- 3rdparty/openpgm-svn-r1085/pgm/INSTALL | 4 - 3rdparty/openpgm-svn-r1085/pgm/LICENSE | 19 - 3rdparty/openpgm-svn-r1085/pgm/README | 7 - 3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgm | 170 - 3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgm89 | 80 - 3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmex | 18 - .../openpgm-svn-r1085/pgm/SConscript.libpgmhttp | 53 - .../openpgm-svn-r1085/pgm/SConscript.libpgmsnmp | 35 - 3rdparty/openpgm-svn-r1085/pgm/SConstruct | 326 - 3rdparty/openpgm-svn-r1085/pgm/SConstruct.097 | 321 - .../openpgm-svn-r1085/pgm/SConstruct.097.intelc | 331 -- .../openpgm-svn-r1085/pgm/SConstruct.097.mingw64 | 325 - .../openpgm-svn-r1085/pgm/SConstruct.097.sunstudio | 312 - 3rdparty/openpgm-svn-r1085/pgm/SConstruct.Debian4 | 281 - .../openpgm-svn-r1085/pgm/SConstruct.FreeBSD80 | 337 -- .../openpgm-svn-r1085/pgm/SConstruct.OpenSolaris | 310 - 3rdparty/openpgm-svn-r1085/pgm/SConstruct.RHEL4 | 279 - .../openpgm-svn-r1085/pgm/SConstruct.Solaris.gcc64 | 324 - .../pgm/SConstruct.Solaris.sungcc | 321 - .../pgm/SConstruct.Solaris.sunstudio | 306 - 3rdparty/openpgm-svn-r1085/pgm/SConstruct.clang | 336 -- 3rdparty/openpgm-svn-r1085/pgm/SConstruct.mingw | 330 -- .../openpgm-svn-r1085/pgm/SConstruct.mingw-wine | 339 -- 3rdparty/openpgm-svn-r1085/pgm/atomic_unittest.c | 166 - 3rdparty/openpgm-svn-r1085/pgm/backtrace.c | 69 - 3rdparty/openpgm-svn-r1085/pgm/checksum.c | 941 --- 3rdparty/openpgm-svn-r1085/pgm/checksum_perftest.c | 807 --- 3rdparty/openpgm-svn-r1085/pgm/checksum_unittest.c | 278 - 3rdparty/openpgm-svn-r1085/pgm/crossmingw.py | 144 - 3rdparty/openpgm-svn-r1085/pgm/crossmingw64.py | 140 - 3rdparty/openpgm-svn-r1085/pgm/engine.c | 277 - 3rdparty/openpgm-svn-r1085/pgm/engine_unittest.c | 232 - 3rdparty/openpgm-svn-r1085/pgm/error.c | 518 -- 3rdparty/openpgm-svn-r1085/pgm/error_unittest.c | 292 - 3rdparty/openpgm-svn-r1085/pgm/examples/SConscript | 88 - .../openpgm-svn-r1085/pgm/examples/SConscript89 | 41 - 3rdparty/openpgm-svn-r1085/pgm/examples/async.c | 441 -- 3rdparty/openpgm-svn-r1085/pgm/examples/async.h | 82 - .../openpgm-svn-r1085/pgm/examples/blocksyncrecv.c | 350 -- 3rdparty/openpgm-svn-r1085/pgm/examples/daytime.c | 546 -- .../pgm/examples/enonblocksyncrecv.c | 382 -- .../pgm/examples/enonblocksyncrecvmsg.c | 382 -- .../pgm/examples/enonblocksyncrecvmsgv.c | 397 -- 3rdparty/openpgm-svn-r1085/pgm/examples/getopt.c | 110 - 3rdparty/openpgm-svn-r1085/pgm/examples/getopt.h | 62 - 3rdparty/openpgm-svn-r1085/pgm/examples/pgmdump.c | 279 - 3rdparty/openpgm-svn-r1085/pgm/examples/pgmping.cc | 1059 ---- 3rdparty/openpgm-svn-r1085/pgm/examples/pgmrecv.c | 649 -- 3rdparty/openpgm-svn-r1085/pgm/examples/pgmsend.c | 305 - 3rdparty/openpgm-svn-r1085/pgm/examples/pgmtop.c | 1031 ---- 3rdparty/openpgm-svn-r1085/pgm/examples/ping.proto | 47 - .../pgm/examples/pnonblocksyncrecv.c | 385 -- .../openpgm-svn-r1085/pgm/examples/purinrecv.c | 474 -- .../openpgm-svn-r1085/pgm/examples/purinrecvcc.cc | 434 -- .../openpgm-svn-r1085/pgm/examples/purinsend.c | 280 - .../openpgm-svn-r1085/pgm/examples/purinsendcc.cc | 269 - .../openpgm-svn-r1085/pgm/examples/shortcakerecv.c | 430 -- .../pgm/examples/snonblocksyncrecv.c | 435 -- 3rdparty/openpgm-svn-r1085/pgm/fec-block.txt | 66 - 3rdparty/openpgm-svn-r1085/pgm/fec.txt | 77 - 3rdparty/openpgm-svn-r1085/pgm/galois_generator.pl | 139 - 3rdparty/openpgm-svn-r1085/pgm/gcov-parse.pl | 41 - 3rdparty/openpgm-svn-r1085/pgm/gcov-seed.sh | 33 - 3rdparty/openpgm-svn-r1085/pgm/gcov.sh | 29 - 3rdparty/openpgm-svn-r1085/pgm/getifaddrs.c | 840 --- .../openpgm-svn-r1085/pgm/getifaddrs_unittest.c | 262 - 3rdparty/openpgm-svn-r1085/pgm/getnodeaddr.c | 196 - .../openpgm-svn-r1085/pgm/getnodeaddr_unittest.c | 573 -- 3rdparty/openpgm-svn-r1085/pgm/gsi.c | 227 - 3rdparty/openpgm-svn-r1085/pgm/gsi_unittest.c | 350 -- 3rdparty/openpgm-svn-r1085/pgm/hashtable.c | 327 - 3rdparty/openpgm-svn-r1085/pgm/histogram.c | 414 -- 3rdparty/openpgm-svn-r1085/pgm/htdocs/404.html | 11 - 3rdparty/openpgm-svn-r1085/pgm/htdocs/base.css | 136 - .../pgm/htdocs/convert_to_macro.pl | 20 - 3rdparty/openpgm-svn-r1085/pgm/htdocs/robots.txt | 2 - .../pgm/htdocs/xhtml10_strict.doctype | 3 - 3rdparty/openpgm-svn-r1085/pgm/http.c | 1718 ------ 3rdparty/openpgm-svn-r1085/pgm/http_unittest.c | 186 - 3rdparty/openpgm-svn-r1085/pgm/if.c | 1595 ----- 3rdparty/openpgm-svn-r1085/pgm/if_unittest.c | 1495 ----- .../openpgm-svn-r1085/pgm/include/impl/checksum.h | 75 - .../openpgm-svn-r1085/pgm/include/impl/engine.h | 43 - .../openpgm-svn-r1085/pgm/include/impl/features.h | 42 - .../openpgm-svn-r1085/pgm/include/impl/fixed.h | 140 - .../openpgm-svn-r1085/pgm/include/impl/framework.h | 76 - .../openpgm-svn-r1085/pgm/include/impl/galois.h | 138 - .../pgm/include/impl/getifaddrs.h | 73 - .../pgm/include/impl/getnodeaddr.h | 41 - .../openpgm-svn-r1085/pgm/include/impl/hashtable.h | 58 - .../openpgm-svn-r1085/pgm/include/impl/histogram.h | 129 - 3rdparty/openpgm-svn-r1085/pgm/include/impl/i18n.h | 32 - .../pgm/include/impl/indextoaddr.h | 41 - .../pgm/include/impl/indextoname.h | 37 - .../pgm/include/impl/inet_network.h | 41 - 3rdparty/openpgm-svn-r1085/pgm/include/impl/ip.h | 150 - 3rdparty/openpgm-svn-r1085/pgm/include/impl/list.h | 43 - 3rdparty/openpgm-svn-r1085/pgm/include/impl/math.h | 75 - 3rdparty/openpgm-svn-r1085/pgm/include/impl/md5.h | 61 - 3rdparty/openpgm-svn-r1085/pgm/include/impl/mem.h | 34 - .../openpgm-svn-r1085/pgm/include/impl/messages.h | 352 -- .../pgm/include/impl/nametoindex.h | 40 - 3rdparty/openpgm-svn-r1085/pgm/include/impl/net.h | 38 - .../openpgm-svn-r1085/pgm/include/impl/notify.h | 298 - .../pgm/include/impl/packet_parse.h | 45 - .../pgm/include/impl/packet_test.h | 40 - .../openpgm-svn-r1085/pgm/include/impl/pgmMIB.h | 28 - .../pgm/include/impl/pgmMIB_columns.h | 372 -- .../pgm/include/impl/pgmMIB_enums.h | 64 - .../openpgm-svn-r1085/pgm/include/impl/processor.h | 61 - .../openpgm-svn-r1085/pgm/include/impl/queue.h | 51 - 3rdparty/openpgm-svn-r1085/pgm/include/impl/rand.h | 50 - .../pgm/include/impl/rate_control.h | 54 - .../openpgm-svn-r1085/pgm/include/impl/receiver.h | 142 - .../pgm/include/impl/reed_solomon.h | 51 - 3rdparty/openpgm-svn-r1085/pgm/include/impl/rxw.h | 220 - .../openpgm-svn-r1085/pgm/include/impl/slist.h | 52 - 3rdparty/openpgm-svn-r1085/pgm/include/impl/sn.h | 186 - .../openpgm-svn-r1085/pgm/include/impl/sockaddr.h | 105 - .../openpgm-svn-r1085/pgm/include/impl/socket.h | 182 - .../openpgm-svn-r1085/pgm/include/impl/source.h | 75 - .../openpgm-svn-r1085/pgm/include/impl/sqn_list.h | 38 - .../openpgm-svn-r1085/pgm/include/impl/string.h | 59 - .../openpgm-svn-r1085/pgm/include/impl/thread.h | 210 - 3rdparty/openpgm-svn-r1085/pgm/include/impl/time.h | 51 - .../openpgm-svn-r1085/pgm/include/impl/timer.h | 58 - 3rdparty/openpgm-svn-r1085/pgm/include/impl/tsi.h | 39 - 3rdparty/openpgm-svn-r1085/pgm/include/impl/txw.h | 204 - .../pgm/include/impl/wsastrerror.h | 37 - .../openpgm-svn-r1085/pgm/include/pgm/atomic.h | 140 - .../openpgm-svn-r1085/pgm/include/pgm/backtrace.h | 33 - .../openpgm-svn-r1085/pgm/include/pgm/engine.h | 37 - 3rdparty/openpgm-svn-r1085/pgm/include/pgm/error.h | 109 - 3rdparty/openpgm-svn-r1085/pgm/include/pgm/gsi.h | 49 - 3rdparty/openpgm-svn-r1085/pgm/include/pgm/http.h | 37 - 3rdparty/openpgm-svn-r1085/pgm/include/pgm/if.h | 33 - .../openpgm-svn-r1085/pgm/include/pgm/ip/pgm.hh | 100 - .../pgm/include/pgm/ip/pgm_endpoint.hh | 171 - 3rdparty/openpgm-svn-r1085/pgm/include/pgm/list.h | 40 - 3rdparty/openpgm-svn-r1085/pgm/include/pgm/log.h | 33 - .../openpgm-svn-r1085/pgm/include/pgm/macros.h | 171 - 3rdparty/openpgm-svn-r1085/pgm/include/pgm/mem.h | 60 - .../openpgm-svn-r1085/pgm/include/pgm/messages.h | 66 - 3rdparty/openpgm-svn-r1085/pgm/include/pgm/msgv.h | 54 - .../openpgm-svn-r1085/pgm/include/pgm/packet.h | 472 -- 3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm.h | 42 - 3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm.hh | 33 - .../pgm/include/pgm/pgm_socket.hh | 157 - .../openpgm-svn-r1085/pgm/include/pgm/signal.h | 36 - .../openpgm-svn-r1085/pgm/include/pgm/skbuff.h | 243 - 3rdparty/openpgm-svn-r1085/pgm/include/pgm/snmp.h | 35 - .../openpgm-svn-r1085/pgm/include/pgm/socket.h | 170 - 3rdparty/openpgm-svn-r1085/pgm/include/pgm/time.h | 54 - 3rdparty/openpgm-svn-r1085/pgm/include/pgm/tsi.h | 47 - 3rdparty/openpgm-svn-r1085/pgm/include/pgm/types.h | 56 - .../openpgm-svn-r1085/pgm/include/pgm/version.h | 41 - .../openpgm-svn-r1085/pgm/include/pgm/winint.h | 198 - .../pgm/include/pgm/wininttypes.h | 254 - 3rdparty/openpgm-svn-r1085/pgm/indextoaddr.c | 98 - .../openpgm-svn-r1085/pgm/indextoaddr_unittest.c | 302 - 3rdparty/openpgm-svn-r1085/pgm/indextoname.c | 52 - 3rdparty/openpgm-svn-r1085/pgm/inet_network.c | 237 - .../openpgm-svn-r1085/pgm/inet_network_unittest.c | 203 - 3rdparty/openpgm-svn-r1085/pgm/ip_unittest.c | 362 -- 3rdparty/openpgm-svn-r1085/pgm/list.c | 146 - 3rdparty/openpgm-svn-r1085/pgm/log.c | 151 - 3rdparty/openpgm-svn-r1085/pgm/math.c | 75 - 3rdparty/openpgm-svn-r1085/pgm/md5.c | 368 -- 3rdparty/openpgm-svn-r1085/pgm/md5_unittest.c | 189 - 3rdparty/openpgm-svn-r1085/pgm/mem.c | 249 - 3rdparty/openpgm-svn-r1085/pgm/memcheck | 13 - 3rdparty/openpgm-svn-r1085/pgm/messages.c | 173 - .../pgm/mibs/PGM-MIB-petrova-01.txt | 5459 ----------------- 3rdparty/openpgm-svn-r1085/pgm/mld-semantics.txt | 52 - 3rdparty/openpgm-svn-r1085/pgm/msfec.txt | 33 - 3rdparty/openpgm-svn-r1085/pgm/nametoindex.c | 249 - 3rdparty/openpgm-svn-r1085/pgm/net-snmp.txt | 34 - 3rdparty/openpgm-svn-r1085/pgm/net.c | 175 - 3rdparty/openpgm-svn-r1085/pgm/net_unittest.c | 375 -- 3rdparty/openpgm-svn-r1085/pgm/options.txt | 158 - 3rdparty/openpgm-svn-r1085/pgm/packet_parse.c | 615 -- .../openpgm-svn-r1085/pgm/packet_parse_unittest.c | 382 -- 3rdparty/openpgm-svn-r1085/pgm/packet_test.c | 1158 ---- .../openpgm-svn-r1085/pgm/packet_test_unittest.c | 169 - 3rdparty/openpgm-svn-r1085/pgm/pgmMIB.c | 3212 ---------- 3rdparty/openpgm-svn-r1085/pgm/pgmMIB_unittest.c | 257 - 3rdparty/openpgm-svn-r1085/pgm/plan.txt | 238 - 3rdparty/openpgm-svn-r1085/pgm/queue.c | 110 - 3rdparty/openpgm-svn-r1085/pgm/rand.c | 137 - 3rdparty/openpgm-svn-r1085/pgm/rate_control.c | 158 - .../openpgm-svn-r1085/pgm/rate_control_unittest.c | 241 - 3rdparty/openpgm-svn-r1085/pgm/receiver.c | 2268 ------- 3rdparty/openpgm-svn-r1085/pgm/receiver_unittest.c | 857 --- 3rdparty/openpgm-svn-r1085/pgm/recv.c | 1059 ---- 3rdparty/openpgm-svn-r1085/pgm/recv_unittest.c | 1600 ----- 3rdparty/openpgm-svn-r1085/pgm/reed_solomon.c | 576 -- .../openpgm-svn-r1085/pgm/reed_solomon_unittest.c | 305 - 3rdparty/openpgm-svn-r1085/pgm/rxw.c | 2229 ------- 3rdparty/openpgm-svn-r1085/pgm/rxw_unittest.c | 1844 ------ 3rdparty/openpgm-svn-r1085/pgm/signal.c | 176 - 3rdparty/openpgm-svn-r1085/pgm/signal_unittest.c | 115 - 3rdparty/openpgm-svn-r1085/pgm/skbuff.c | 115 - 3rdparty/openpgm-svn-r1085/pgm/slist.c | 166 - 3rdparty/openpgm-svn-r1085/pgm/snmp.c | 222 - 3rdparty/openpgm-svn-r1085/pgm/snmp_unittest.c | 184 - 3rdparty/openpgm-svn-r1085/pgm/sockaddr.c | 1193 ---- 3rdparty/openpgm-svn-r1085/pgm/socket.c | 2046 ------- 3rdparty/openpgm-svn-r1085/pgm/socket_unittest.c | 1186 ---- 3rdparty/openpgm-svn-r1085/pgm/source.c | 2339 -------- 3rdparty/openpgm-svn-r1085/pgm/source_unittest.c | 1216 ---- 3rdparty/openpgm-svn-r1085/pgm/string.c | 486 -- 3rdparty/openpgm-svn-r1085/pgm/test/PGM/Test.pm | 394 -- 3rdparty/openpgm-svn-r1085/pgm/test/SConscript | 15 - 3rdparty/openpgm-svn-r1085/pgm/test/ambient_spm.pl | 86 - 3rdparty/openpgm-svn-r1085/pgm/test/apdu.pl | 56 - 3rdparty/openpgm-svn-r1085/pgm/test/apdu_parity.pl | 59 - 3rdparty/openpgm-svn-r1085/pgm/test/app.c | 904 --- 3rdparty/openpgm-svn-r1085/pgm/test/async.c | 572 -- 3rdparty/openpgm-svn-r1085/pgm/test/async.h | 76 - 3rdparty/openpgm-svn-r1085/pgm/test/dump-json.c | 1292 ---- 3rdparty/openpgm-svn-r1085/pgm/test/dump-json.h | 33 - .../openpgm-svn-r1085/pgm/test/heartbeat_spm.pl | 57 - 3rdparty/openpgm-svn-r1085/pgm/test/monitor.c | 349 -- 3rdparty/openpgm-svn-r1085/pgm/test/nak.pl | 66 - .../openpgm-svn-r1085/pgm/test/nak_cancellation.pl | 161 - 3rdparty/openpgm-svn-r1085/pgm/test/nak_list.pl | 70 - 3rdparty/openpgm-svn-r1085/pgm/test/nak_parity.pl | 75 - 3rdparty/openpgm-svn-r1085/pgm/test/nak_repeat.pl | 69 - 3rdparty/openpgm-svn-r1085/pgm/test/ncf.pl | 66 - .../openpgm-svn-r1085/pgm/test/ncf_cancellation.pl | 147 - 3rdparty/openpgm-svn-r1085/pgm/test/ncf_list.pl | 72 - .../openpgm-svn-r1085/pgm/test/ncf_suppression.pl | 101 - 3rdparty/openpgm-svn-r1085/pgm/test/odata.pl | 44 - .../openpgm-svn-r1085/pgm/test/odata_completion.pl | 87 - 3rdparty/openpgm-svn-r1085/pgm/test/odata_jump.pl | 71 - .../pgm/test/odata_jump_parity.pl | 83 - .../openpgm-svn-r1085/pgm/test/odata_number.pl | 59 - 3rdparty/openpgm-svn-r1085/pgm/test/odata_rate.pl | 68 - .../openpgm-svn-r1085/pgm/test/odata_reception.pl | 59 - .../openpgm-svn-r1085/pgm/test/on-demand_spm.pl | 49 - .../openpgm-svn-r1085/pgm/test/outofwindow_ncf.pl | 103 - .../openpgm-svn-r1085/pgm/test/rdata_completion.pl | 87 - .../pgm/test/rdata_completion_parity.pl | 97 - .../pgm/test/rdata_completion_parity_var_pktlen.pl | 97 - 3rdparty/openpgm-svn-r1085/pgm/test/rdata_jump.pl | 71 - .../openpgm-svn-r1085/pgm/test/rdata_reception.pl | 59 - 3rdparty/openpgm-svn-r1085/pgm/test/sim.c | 1924 ------ 3rdparty/openpgm-svn-r1085/pgm/test/spm.pl | 42 - 3rdparty/openpgm-svn-r1085/pgm/test/spm_jump.pl | 60 - 3rdparty/openpgm-svn-r1085/pgm/test/spm_jump2.pl | 59 - .../openpgm-svn-r1085/pgm/test/spm_reception.pl | 58 - 3rdparty/openpgm-svn-r1085/pgm/test/spmr.pl | 73 - .../openpgm-svn-r1085/pgm/test/spmr_after_spm.pl | 78 - .../openpgm-svn-r1085/pgm/test/spmr_from_odata.pl | 53 - .../openpgm-svn-r1085/pgm/test/spmr_suppression.pl | 58 - .../openpgm-svn-r1085/pgm/test/sudoers.example | 26 - 3rdparty/openpgm-svn-r1085/pgm/test/test.conf.pl | 27 - 3rdparty/openpgm-svn-r1085/pgm/thread.c | 457 -- 3rdparty/openpgm-svn-r1085/pgm/time.c | 770 --- 3rdparty/openpgm-svn-r1085/pgm/time_unittest.c | 188 - 3rdparty/openpgm-svn-r1085/pgm/timer.c | 223 - 3rdparty/openpgm-svn-r1085/pgm/timer_unittest.c | 355 -- .../pgm/token and leaky bucket.txt | 12 - 3rdparty/openpgm-svn-r1085/pgm/tsi.c | 119 - 3rdparty/openpgm-svn-r1085/pgm/tsi_unittest.c | 185 - 3rdparty/openpgm-svn-r1085/pgm/txw.c | 763 --- 3rdparty/openpgm-svn-r1085/pgm/txw_unittest.c | 743 --- 3rdparty/openpgm-svn-r1085/pgm/valgrind.supp | 147 - .../openpgm-svn-r1085/pgm/version_generator.py | 52 - .../pgm/win/mingw32-runtime_3.13-1openpgm3.diff | 136 - .../pgm/win/mingw32-runtime_3.15.2-0openpgm1.diff | 135 - ...mingw-w64-bin_x86-64-linux_4.4.1-1openpgm1.diff | 53 - 3rdparty/openpgm-svn-r1085/pgm/wsastrerror.c | 372 -- .../openpgm-svn-r1135-0001-sigsegv-in-txw.patch | 28 + ...n-r1135-0002-correct-checksum-calculation.patch | 17 + ...n-r1135-0003-fix-rdata-congestion-control.patch | 42 + .../doc/draft-ietf-rmt-bb-pgmcc-03.txt | 1226 ++++ 3rdparty/openpgm-svn-r1135/doc/rfc3208.txt | 6219 ++++++++++++++++++++ 3rdparty/openpgm-svn-r1135/pgm/COPYING | 504 ++ 3rdparty/openpgm-svn-r1135/pgm/INSTALL | 4 + 3rdparty/openpgm-svn-r1135/pgm/LICENSE | 19 + 3rdparty/openpgm-svn-r1135/pgm/README | 7 + 3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgm | 171 + 3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgm89 | 83 + 3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgmex | 18 + .../openpgm-svn-r1135/pgm/SConscript.libpgmhttp | 53 + .../openpgm-svn-r1135/pgm/SConscript.libpgmsnmp | 35 + 3rdparty/openpgm-svn-r1135/pgm/SConstruct | 345 ++ 3rdparty/openpgm-svn-r1135/pgm/SConstruct.097 | 332 ++ .../openpgm-svn-r1135/pgm/SConstruct.097.intelc | 387 ++ .../openpgm-svn-r1135/pgm/SConstruct.097.mingw64 | 338 ++ .../openpgm-svn-r1135/pgm/SConstruct.097.sunstudio | 332 ++ 3rdparty/openpgm-svn-r1135/pgm/SConstruct.Debian4 | 281 + .../openpgm-svn-r1135/pgm/SConstruct.FreeBSD80 | 351 ++ .../openpgm-svn-r1135/pgm/SConstruct.OpenSolaris | 310 + 3rdparty/openpgm-svn-r1135/pgm/SConstruct.RHEL4 | 279 + .../openpgm-svn-r1135/pgm/SConstruct.Solaris.gcc64 | 350 ++ .../pgm/SConstruct.Solaris.sungcc | 337 ++ .../pgm/SConstruct.Solaris.sunstudio | 322 + 3rdparty/openpgm-svn-r1135/pgm/SConstruct.clang | 351 ++ 3rdparty/openpgm-svn-r1135/pgm/SConstruct.mingw | 331 ++ .../openpgm-svn-r1135/pgm/SConstruct.mingw-wine | 339 ++ 3rdparty/openpgm-svn-r1135/pgm/atomic_unittest.c | 166 + 3rdparty/openpgm-svn-r1135/pgm/backtrace.c | 69 + 3rdparty/openpgm-svn-r1135/pgm/checksum.c | 941 +++ 3rdparty/openpgm-svn-r1135/pgm/checksum_perftest.c | 807 +++ 3rdparty/openpgm-svn-r1135/pgm/checksum_unittest.c | 278 + 3rdparty/openpgm-svn-r1135/pgm/crossmingw.py | 144 + 3rdparty/openpgm-svn-r1135/pgm/crossmingw64.py | 140 + 3rdparty/openpgm-svn-r1135/pgm/engine.c | 282 + 3rdparty/openpgm-svn-r1135/pgm/engine.c.c89.patch | 58 + 3rdparty/openpgm-svn-r1135/pgm/engine_unittest.c | 234 + 3rdparty/openpgm-svn-r1135/pgm/error.c | 518 ++ 3rdparty/openpgm-svn-r1135/pgm/error_unittest.c | 292 + 3rdparty/openpgm-svn-r1135/pgm/examples/SConscript | 88 + .../openpgm-svn-r1135/pgm/examples/SConscript89 | 41 + 3rdparty/openpgm-svn-r1135/pgm/examples/async.c | 441 ++ 3rdparty/openpgm-svn-r1135/pgm/examples/async.h | 82 + .../openpgm-svn-r1135/pgm/examples/blocksyncrecv.c | 350 ++ 3rdparty/openpgm-svn-r1135/pgm/examples/daytime.c | 546 ++ .../pgm/examples/enonblocksyncrecv.c | 382 ++ .../pgm/examples/enonblocksyncrecvmsg.c | 382 ++ .../pgm/examples/enonblocksyncrecvmsgv.c | 397 ++ 3rdparty/openpgm-svn-r1135/pgm/examples/getopt.c | 110 + 3rdparty/openpgm-svn-r1135/pgm/examples/getopt.h | 62 + 3rdparty/openpgm-svn-r1135/pgm/examples/pgmdump.c | 279 + 3rdparty/openpgm-svn-r1135/pgm/examples/pgmping.cc | 1225 ++++ 3rdparty/openpgm-svn-r1135/pgm/examples/pgmrecv.c | 649 ++ 3rdparty/openpgm-svn-r1135/pgm/examples/pgmsend.c | 305 + 3rdparty/openpgm-svn-r1135/pgm/examples/pgmtop.c | 1031 ++++ 3rdparty/openpgm-svn-r1135/pgm/examples/ping.proto | 47 + .../pgm/examples/pnonblocksyncrecv.c | 385 ++ .../openpgm-svn-r1135/pgm/examples/purinrecv.c | 479 ++ .../openpgm-svn-r1135/pgm/examples/purinrecvcc.cc | 434 ++ .../openpgm-svn-r1135/pgm/examples/purinsend.c | 284 + .../openpgm-svn-r1135/pgm/examples/purinsendcc.cc | 269 + .../openpgm-svn-r1135/pgm/examples/shortcakerecv.c | 430 ++ .../pgm/examples/snonblocksyncrecv.c | 435 ++ 3rdparty/openpgm-svn-r1135/pgm/fec-block.txt | 66 + 3rdparty/openpgm-svn-r1135/pgm/fec.txt | 77 + 3rdparty/openpgm-svn-r1135/pgm/galois_generator.pl | 139 + 3rdparty/openpgm-svn-r1135/pgm/gcov-parse.pl | 41 + 3rdparty/openpgm-svn-r1135/pgm/gcov-seed.sh | 33 + 3rdparty/openpgm-svn-r1135/pgm/gcov.sh | 29 + 3rdparty/openpgm-svn-r1135/pgm/getifaddrs.c | 840 +++ .../openpgm-svn-r1135/pgm/getifaddrs.c.c89.patch | 218 + .../openpgm-svn-r1135/pgm/getifaddrs_unittest.c | 262 + 3rdparty/openpgm-svn-r1135/pgm/getnodeaddr.c | 196 + .../openpgm-svn-r1135/pgm/getnodeaddr.c.c89.patch | 59 + .../openpgm-svn-r1135/pgm/getnodeaddr_unittest.c | 573 ++ 3rdparty/openpgm-svn-r1135/pgm/gsi.c | 232 + 3rdparty/openpgm-svn-r1135/pgm/gsi.c.c89.patch | 53 + 3rdparty/openpgm-svn-r1135/pgm/gsi_unittest.c | 350 ++ 3rdparty/openpgm-svn-r1135/pgm/hashtable.c | 327 + .../openpgm-svn-r1135/pgm/hashtable.c.c89.patch | 47 + 3rdparty/openpgm-svn-r1135/pgm/histogram.c | 414 ++ .../openpgm-svn-r1135/pgm/histogram.c.c89.patch | 212 + 3rdparty/openpgm-svn-r1135/pgm/htdocs/404.html | 11 + 3rdparty/openpgm-svn-r1135/pgm/htdocs/base.css | 136 + .../pgm/htdocs/convert_to_macro.pl | 20 + 3rdparty/openpgm-svn-r1135/pgm/htdocs/robots.txt | 2 + .../pgm/htdocs/xhtml10_strict.doctype | 3 + 3rdparty/openpgm-svn-r1135/pgm/http.c | 1735 ++++++ 3rdparty/openpgm-svn-r1135/pgm/http_unittest.c | 186 + 3rdparty/openpgm-svn-r1135/pgm/if.c | 1598 +++++ 3rdparty/openpgm-svn-r1135/pgm/if.c.c89.patch | 376 ++ 3rdparty/openpgm-svn-r1135/pgm/if_unittest.c | 1497 +++++ .../openpgm-svn-r1135/pgm/include/impl/checksum.h | 75 + .../openpgm-svn-r1135/pgm/include/impl/engine.h | 43 + .../openpgm-svn-r1135/pgm/include/impl/features.h | 42 + .../openpgm-svn-r1135/pgm/include/impl/fixed.h | 156 + .../openpgm-svn-r1135/pgm/include/impl/framework.h | 76 + .../openpgm-svn-r1135/pgm/include/impl/galois.h | 153 + .../pgm/include/impl/getifaddrs.h | 76 + .../pgm/include/impl/getnodeaddr.h | 41 + .../openpgm-svn-r1135/pgm/include/impl/hashtable.h | 58 + .../openpgm-svn-r1135/pgm/include/impl/histogram.h | 129 + 3rdparty/openpgm-svn-r1135/pgm/include/impl/i18n.h | 32 + .../pgm/include/impl/indextoaddr.h | 41 + .../pgm/include/impl/indextoname.h | 37 + .../pgm/include/impl/inet_network.h | 41 + 3rdparty/openpgm-svn-r1135/pgm/include/impl/ip.h | 150 + 3rdparty/openpgm-svn-r1135/pgm/include/impl/list.h | 43 + 3rdparty/openpgm-svn-r1135/pgm/include/impl/math.h | 86 + 3rdparty/openpgm-svn-r1135/pgm/include/impl/md5.h | 61 + 3rdparty/openpgm-svn-r1135/pgm/include/impl/mem.h | 34 + .../openpgm-svn-r1135/pgm/include/impl/messages.h | 352 ++ .../pgm/include/impl/nametoindex.h | 40 + 3rdparty/openpgm-svn-r1135/pgm/include/impl/net.h | 53 + .../openpgm-svn-r1135/pgm/include/impl/notify.h | 305 + .../pgm/include/impl/packet_parse.h | 45 + .../pgm/include/impl/packet_test.h | 40 + .../openpgm-svn-r1135/pgm/include/impl/pgmMIB.h | 28 + .../pgm/include/impl/pgmMIB_columns.h | 372 ++ .../pgm/include/impl/pgmMIB_enums.h | 64 + .../openpgm-svn-r1135/pgm/include/impl/processor.h | 61 + .../openpgm-svn-r1135/pgm/include/impl/queue.h | 51 + 3rdparty/openpgm-svn-r1135/pgm/include/impl/rand.h | 50 + .../pgm/include/impl/rate_control.h | 54 + .../openpgm-svn-r1135/pgm/include/impl/receiver.h | 142 + .../pgm/include/impl/reed_solomon.h | 51 + 3rdparty/openpgm-svn-r1135/pgm/include/impl/rxw.h | 223 + .../openpgm-svn-r1135/pgm/include/impl/slist.h | 52 + 3rdparty/openpgm-svn-r1135/pgm/include/impl/sn.h | 174 + .../openpgm-svn-r1135/pgm/include/impl/sockaddr.h | 105 + .../openpgm-svn-r1135/pgm/include/impl/socket.h | 182 + .../openpgm-svn-r1135/pgm/include/impl/source.h | 75 + .../openpgm-svn-r1135/pgm/include/impl/sqn_list.h | 38 + .../openpgm-svn-r1135/pgm/include/impl/string.h | 59 + .../openpgm-svn-r1135/pgm/include/impl/thread.h | 211 + 3rdparty/openpgm-svn-r1135/pgm/include/impl/time.h | 51 + .../openpgm-svn-r1135/pgm/include/impl/timer.h | 58 + 3rdparty/openpgm-svn-r1135/pgm/include/impl/tsi.h | 39 + 3rdparty/openpgm-svn-r1135/pgm/include/impl/txw.h | 207 + .../pgm/include/impl/wsastrerror.h | 37 + .../openpgm-svn-r1135/pgm/include/pgm/atomic.h | 140 + .../openpgm-svn-r1135/pgm/include/pgm/backtrace.h | 33 + .../openpgm-svn-r1135/pgm/include/pgm/engine.h | 37 + 3rdparty/openpgm-svn-r1135/pgm/include/pgm/error.h | 109 + 3rdparty/openpgm-svn-r1135/pgm/include/pgm/gsi.h | 51 + 3rdparty/openpgm-svn-r1135/pgm/include/pgm/http.h | 37 + 3rdparty/openpgm-svn-r1135/pgm/include/pgm/if.h | 33 + .../openpgm-svn-r1135/pgm/include/pgm/ip/pgm.hh | 100 + .../pgm/include/pgm/ip/pgm_endpoint.hh | 171 + 3rdparty/openpgm-svn-r1135/pgm/include/pgm/list.h | 40 + 3rdparty/openpgm-svn-r1135/pgm/include/pgm/log.h | 33 + .../openpgm-svn-r1135/pgm/include/pgm/macros.h | 171 + 3rdparty/openpgm-svn-r1135/pgm/include/pgm/mem.h | 60 + .../openpgm-svn-r1135/pgm/include/pgm/messages.h | 66 + 3rdparty/openpgm-svn-r1135/pgm/include/pgm/msgv.h | 54 + .../openpgm-svn-r1135/pgm/include/pgm/packet.h | 475 ++ 3rdparty/openpgm-svn-r1135/pgm/include/pgm/pgm.h | 42 + 3rdparty/openpgm-svn-r1135/pgm/include/pgm/pgm.hh | 33 + .../pgm/include/pgm/pgm_socket.hh | 157 + .../openpgm-svn-r1135/pgm/include/pgm/signal.h | 36 + .../openpgm-svn-r1135/pgm/include/pgm/skbuff.h | 258 + 3rdparty/openpgm-svn-r1135/pgm/include/pgm/snmp.h | 35 + .../openpgm-svn-r1135/pgm/include/pgm/socket.h | 172 + 3rdparty/openpgm-svn-r1135/pgm/include/pgm/time.h | 54 + 3rdparty/openpgm-svn-r1135/pgm/include/pgm/tsi.h | 49 + 3rdparty/openpgm-svn-r1135/pgm/include/pgm/types.h | 66 + .../openpgm-svn-r1135/pgm/include/pgm/version.h | 41 + .../openpgm-svn-r1135/pgm/include/pgm/winint.h | 198 + .../pgm/include/pgm/wininttypes.h | 254 + 3rdparty/openpgm-svn-r1135/pgm/indextoaddr.c | 98 + .../openpgm-svn-r1135/pgm/indextoaddr.c.c89.patch | 44 + .../openpgm-svn-r1135/pgm/indextoaddr_unittest.c | 302 + 3rdparty/openpgm-svn-r1135/pgm/indextoname.c | 52 + .../openpgm-svn-r1135/pgm/indextoname.c.c89.patch | 29 + 3rdparty/openpgm-svn-r1135/pgm/inet_network.c | 237 + .../openpgm-svn-r1135/pgm/inet_network.c.c89.patch | 76 + .../openpgm-svn-r1135/pgm/inet_network_unittest.c | 203 + 3rdparty/openpgm-svn-r1135/pgm/ip_unittest.c | 367 ++ 3rdparty/openpgm-svn-r1135/pgm/list.c | 146 + 3rdparty/openpgm-svn-r1135/pgm/log.c | 151 + 3rdparty/openpgm-svn-r1135/pgm/math.c | 75 + 3rdparty/openpgm-svn-r1135/pgm/math.c.c89.patch | 16 + 3rdparty/openpgm-svn-r1135/pgm/md5.c | 368 ++ 3rdparty/openpgm-svn-r1135/pgm/md5.c.c89.patch | 34 + 3rdparty/openpgm-svn-r1135/pgm/md5_unittest.c | 189 + 3rdparty/openpgm-svn-r1135/pgm/mem.c | 250 + 3rdparty/openpgm-svn-r1135/pgm/mem.c.c89.patch | 145 + 3rdparty/openpgm-svn-r1135/pgm/memcheck | 13 + 3rdparty/openpgm-svn-r1135/pgm/messages.c | 173 + .../openpgm-svn-r1135/pgm/messages.c.c89.patch | 91 + .../pgm/mibs/PGM-MIB-petrova-01.txt | 5459 +++++++++++++++++ 3rdparty/openpgm-svn-r1135/pgm/mld-semantics.txt | 52 + 3rdparty/openpgm-svn-r1135/pgm/msfec.txt | 33 + 3rdparty/openpgm-svn-r1135/pgm/nametoindex.c | 249 + .../openpgm-svn-r1135/pgm/nametoindex.c.c89.patch | 89 + 3rdparty/openpgm-svn-r1135/pgm/net-snmp.txt | 34 + 3rdparty/openpgm-svn-r1135/pgm/net.c | 181 + 3rdparty/openpgm-svn-r1135/pgm/net.c.c89.patch | 67 + 3rdparty/openpgm-svn-r1135/pgm/net_unittest.c | 375 ++ 3rdparty/openpgm-svn-r1135/pgm/options.txt | 158 + 3rdparty/openpgm-svn-r1135/pgm/packet_parse.c | 619 ++ .../openpgm-svn-r1135/pgm/packet_parse.c.c89.patch | 121 + .../openpgm-svn-r1135/pgm/packet_parse_unittest.c | 382 ++ 3rdparty/openpgm-svn-r1135/pgm/packet_test.c | 1162 ++++ .../openpgm-svn-r1135/pgm/packet_test.c.c89.patch | 401 ++ .../openpgm-svn-r1135/pgm/packet_test_unittest.c | 169 + 3rdparty/openpgm-svn-r1135/pgm/pgmMIB.c | 3212 ++++++++++ 3rdparty/openpgm-svn-r1135/pgm/pgmMIB_unittest.c | 257 + 3rdparty/openpgm-svn-r1135/pgm/plan.txt | 238 + 3rdparty/openpgm-svn-r1135/pgm/queue.c | 110 + 3rdparty/openpgm-svn-r1135/pgm/rand.c | 137 + 3rdparty/openpgm-svn-r1135/pgm/rand.c.c89.patch | 34 + 3rdparty/openpgm-svn-r1135/pgm/rate_control.c | 158 + .../openpgm-svn-r1135/pgm/rate_control.c.c89.patch | 62 + .../openpgm-svn-r1135/pgm/rate_control_unittest.c | 241 + 3rdparty/openpgm-svn-r1135/pgm/receiver.c | 2292 ++++++++ .../openpgm-svn-r1135/pgm/receiver.c.c89.patch | 887 +++ 3rdparty/openpgm-svn-r1135/pgm/receiver.c.rej | 37 + 3rdparty/openpgm-svn-r1135/pgm/receiver_unittest.c | 902 +++ 3rdparty/openpgm-svn-r1135/pgm/recv.c | 1062 ++++ 3rdparty/openpgm-svn-r1135/pgm/recv.c.c89.patch | 404 ++ 3rdparty/openpgm-svn-r1135/pgm/recv_unittest.c | 1600 +++++ 3rdparty/openpgm-svn-r1135/pgm/reed_solomon.c | 576 ++ .../openpgm-svn-r1135/pgm/reed_solomon.c.c89.patch | 467 ++ .../openpgm-svn-r1135/pgm/reed_solomon_unittest.c | 305 + 3rdparty/openpgm-svn-r1135/pgm/rxw.c | 2233 +++++++ 3rdparty/openpgm-svn-r1135/pgm/rxw.c.c89.patch | 565 ++ 3rdparty/openpgm-svn-r1135/pgm/rxw_unittest.c | 1844 ++++++ 3rdparty/openpgm-svn-r1135/pgm/signal.c | 179 + 3rdparty/openpgm-svn-r1135/pgm/signal_unittest.c | 115 + 3rdparty/openpgm-svn-r1135/pgm/skbuff.c | 115 + 3rdparty/openpgm-svn-r1135/pgm/slist.c | 166 + 3rdparty/openpgm-svn-r1135/pgm/snmp.c | 222 + 3rdparty/openpgm-svn-r1135/pgm/snmp_unittest.c | 184 + 3rdparty/openpgm-svn-r1135/pgm/sockaddr.c | 1193 ++++ .../openpgm-svn-r1135/pgm/sockaddr.c.c89.patch | 75 + 3rdparty/openpgm-svn-r1135/pgm/socket.c | 2150 +++++++ 3rdparty/openpgm-svn-r1135/pgm/socket.c.c89.patch | 403 ++ 3rdparty/openpgm-svn-r1135/pgm/socket_unittest.c | 1293 ++++ 3rdparty/openpgm-svn-r1135/pgm/source.c | 2345 ++++++++ 3rdparty/openpgm-svn-r1135/pgm/source.c.c89.patch | 1021 ++++ 3rdparty/openpgm-svn-r1135/pgm/source.c.orig | 2344 ++++++++ 3rdparty/openpgm-svn-r1135/pgm/source.c.rej | 17 + 3rdparty/openpgm-svn-r1135/pgm/source_unittest.c | 1248 ++++ 3rdparty/openpgm-svn-r1135/pgm/string.c | 486 ++ 3rdparty/openpgm-svn-r1135/pgm/string.c.c89.patch | 39 + 3rdparty/openpgm-svn-r1135/pgm/test/PGM/Test.pm | 394 ++ 3rdparty/openpgm-svn-r1135/pgm/test/SConscript | 15 + 3rdparty/openpgm-svn-r1135/pgm/test/ambient_spm.pl | 87 + 3rdparty/openpgm-svn-r1135/pgm/test/apdu.pl | 58 + 3rdparty/openpgm-svn-r1135/pgm/test/apdu_parity.pl | 59 + 3rdparty/openpgm-svn-r1135/pgm/test/app.c | 1068 ++++ 3rdparty/openpgm-svn-r1135/pgm/test/async.c | 579 ++ 3rdparty/openpgm-svn-r1135/pgm/test/async.h | 76 + 3rdparty/openpgm-svn-r1135/pgm/test/dump-json.c | 1292 ++++ 3rdparty/openpgm-svn-r1135/pgm/test/dump-json.h | 33 + .../openpgm-svn-r1135/pgm/test/heartbeat_spm.pl | 58 + 3rdparty/openpgm-svn-r1135/pgm/test/monitor.c | 349 ++ 3rdparty/openpgm-svn-r1135/pgm/test/nak.pl | 68 + .../openpgm-svn-r1135/pgm/test/nak_cancellation.pl | 163 + 3rdparty/openpgm-svn-r1135/pgm/test/nak_list.pl | 72 + 3rdparty/openpgm-svn-r1135/pgm/test/nak_parity.pl | 75 + 3rdparty/openpgm-svn-r1135/pgm/test/nak_repeat.pl | 71 + 3rdparty/openpgm-svn-r1135/pgm/test/ncf.pl | 68 + .../openpgm-svn-r1135/pgm/test/ncf_cancellation.pl | 149 + 3rdparty/openpgm-svn-r1135/pgm/test/ncf_list.pl | 74 + .../openpgm-svn-r1135/pgm/test/ncf_suppression.pl | 103 + 3rdparty/openpgm-svn-r1135/pgm/test/odata.pl | 45 + .../openpgm-svn-r1135/pgm/test/odata_completion.pl | 89 + 3rdparty/openpgm-svn-r1135/pgm/test/odata_jump.pl | 73 + .../pgm/test/odata_jump_parity.pl | 83 + .../openpgm-svn-r1135/pgm/test/odata_number.pl | 60 + 3rdparty/openpgm-svn-r1135/pgm/test/odata_rate.pl | 69 + .../openpgm-svn-r1135/pgm/test/odata_reception.pl | 61 + .../openpgm-svn-r1135/pgm/test/on-demand_spm.pl | 49 + .../openpgm-svn-r1135/pgm/test/outofwindow_ncf.pl | 105 + .../openpgm-svn-r1135/pgm/test/rdata_completion.pl | 89 + .../pgm/test/rdata_completion_parity.pl | 97 + .../pgm/test/rdata_completion_parity_var_pktlen.pl | 97 + 3rdparty/openpgm-svn-r1135/pgm/test/rdata_jump.pl | 73 + .../openpgm-svn-r1135/pgm/test/rdata_reception.pl | 61 + 3rdparty/openpgm-svn-r1135/pgm/test/sim.c | 2301 ++++++++ 3rdparty/openpgm-svn-r1135/pgm/test/spm.pl | 43 + 3rdparty/openpgm-svn-r1135/pgm/test/spm_jump.pl | 62 + 3rdparty/openpgm-svn-r1135/pgm/test/spm_jump2.pl | 61 + .../openpgm-svn-r1135/pgm/test/spm_reception.pl | 60 + 3rdparty/openpgm-svn-r1135/pgm/test/spmr.pl | 75 + .../openpgm-svn-r1135/pgm/test/spmr_after_spm.pl | 80 + .../openpgm-svn-r1135/pgm/test/spmr_from_odata.pl | 55 + .../openpgm-svn-r1135/pgm/test/spmr_suppression.pl | 60 + .../openpgm-svn-r1135/pgm/test/sudoers.example | 26 + 3rdparty/openpgm-svn-r1135/pgm/test/test.conf.pl | 27 + 3rdparty/openpgm-svn-r1135/pgm/thread.c | 457 ++ 3rdparty/openpgm-svn-r1135/pgm/thread.c.c89.patch | 137 + 3rdparty/openpgm-svn-r1135/pgm/time.c | 774 +++ 3rdparty/openpgm-svn-r1135/pgm/time.c.c89.patch | 115 + 3rdparty/openpgm-svn-r1135/pgm/time_unittest.c | 188 + 3rdparty/openpgm-svn-r1135/pgm/timer.c | 227 + 3rdparty/openpgm-svn-r1135/pgm/timer.c.c89.patch | 63 + 3rdparty/openpgm-svn-r1135/pgm/timer_unittest.c | 355 ++ .../pgm/token and leaky bucket.txt | 12 + 3rdparty/openpgm-svn-r1135/pgm/tsi.c | 119 + 3rdparty/openpgm-svn-r1135/pgm/tsi.c.c89.patch | 33 + 3rdparty/openpgm-svn-r1135/pgm/tsi_unittest.c | 185 + 3rdparty/openpgm-svn-r1135/pgm/txw.c | 767 +++ 3rdparty/openpgm-svn-r1135/pgm/txw.c.c89.patch | 223 + 3rdparty/openpgm-svn-r1135/pgm/txw_unittest.c | 743 +++ 3rdparty/openpgm-svn-r1135/pgm/valgrind.supp | 147 + .../openpgm-svn-r1135/pgm/version_generator.py | 52 + .../pgm/win/mingw32-runtime_3.13-1openpgm3.diff | 136 + .../pgm/win/mingw32-runtime_3.15.2-0openpgm1.diff | 135 + ...mingw-w64-bin_x86-64-linux_4.4.1-1openpgm1.diff | 53 + 3rdparty/openpgm-svn-r1135/pgm/wsastrerror.c | 372 ++ OpenPGMConfig.cmake | 13 +- src/gui/clientFileReceiveDialog.cpp | 5 + src/net/mcast/CMakeLists.txt | 8 +- src/net/mcast/McastPGMSocket.cpp | 206 +- src/net/mcast/McastSender.cpp | 17 +- src/net/mcast/McastSender.h | 1 + src/net/pvsOutgoingMulticastTransfer.cpp | 2 +- src/pvs.cpp | 10 +- 600 files changed, 101902 insertions(+), 91218 deletions(-) delete mode 100644 3rdparty/01-libpgm-fix-switch-fallthrough.patch delete mode 100644 3rdparty/openpgm-svn-r1085/doc/draft-ietf-rmt-bb-pgmcc-03.txt delete mode 100644 3rdparty/openpgm-svn-r1085/doc/rfc3208.txt delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/COPYING delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/INSTALL delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/LICENSE delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/README delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgm delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgm89 delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmex delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmhttp delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmsnmp delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.097 delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.intelc delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.mingw64 delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.sunstudio delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.Debian4 delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.FreeBSD80 delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.OpenSolaris delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.RHEL4 delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.gcc64 delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.sungcc delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.sunstudio delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.clang delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.mingw delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/SConstruct.mingw-wine delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/atomic_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/backtrace.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/checksum.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/checksum_perftest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/checksum_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/crossmingw.py delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/crossmingw64.py delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/engine.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/engine_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/error.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/error_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/SConscript delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/SConscript89 delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/async.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/async.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/blocksyncrecv.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/daytime.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecv.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecvmsg.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecvmsgv.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/getopt.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/getopt.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/pgmdump.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/pgmping.cc delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/pgmrecv.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/pgmsend.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/pgmtop.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/ping.proto delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/pnonblocksyncrecv.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/purinrecv.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/purinrecvcc.cc delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/purinsend.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/purinsendcc.cc delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/shortcakerecv.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/examples/snonblocksyncrecv.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/fec-block.txt delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/fec.txt delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/galois_generator.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/gcov-parse.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/gcov-seed.sh delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/gcov.sh delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/getifaddrs.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/getifaddrs_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/getnodeaddr.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/getnodeaddr_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/gsi.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/gsi_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/hashtable.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/histogram.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/htdocs/404.html delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/htdocs/base.css delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/htdocs/convert_to_macro.pl delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/htdocs/robots.txt delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/htdocs/xhtml10_strict.doctype delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/http.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/http_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/if.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/if_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/checksum.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/engine.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/features.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/fixed.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/framework.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/galois.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/getifaddrs.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/getnodeaddr.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/hashtable.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/histogram.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/i18n.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/indextoaddr.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/indextoname.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/inet_network.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/ip.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/list.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/math.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/md5.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/mem.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/messages.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/nametoindex.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/net.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/notify.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/packet_parse.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/packet_test.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB_columns.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB_enums.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/processor.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/queue.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/rand.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/rate_control.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/receiver.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/reed_solomon.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/rxw.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/slist.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/sn.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/sockaddr.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/socket.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/source.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/sqn_list.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/string.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/thread.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/time.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/timer.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/tsi.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/txw.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/impl/wsastrerror.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/atomic.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/backtrace.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/engine.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/error.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/gsi.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/http.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/if.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/ip/pgm.hh delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/ip/pgm_endpoint.hh delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/list.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/log.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/macros.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/mem.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/messages.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/msgv.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/packet.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm.hh delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm_socket.hh delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/signal.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/skbuff.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/snmp.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/socket.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/time.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/tsi.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/types.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/version.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/winint.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/include/pgm/wininttypes.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/indextoaddr.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/indextoaddr_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/indextoname.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/inet_network.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/inet_network_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/ip_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/list.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/log.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/math.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/md5.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/md5_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/mem.c delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/memcheck delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/messages.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/mibs/PGM-MIB-petrova-01.txt delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/mld-semantics.txt delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/msfec.txt delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/nametoindex.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/net-snmp.txt delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/net.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/net_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/options.txt delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/packet_parse.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/packet_parse_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/packet_test.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/packet_test_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/pgmMIB.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/pgmMIB_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/plan.txt delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/queue.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/rand.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/rate_control.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/rate_control_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/receiver.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/receiver_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/recv.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/recv_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/reed_solomon.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/reed_solomon_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/rxw.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/rxw_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/signal.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/signal_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/skbuff.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/slist.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/snmp.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/snmp_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/sockaddr.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/socket.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/socket_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/source.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/source_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/string.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/test/PGM/Test.pm delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/test/SConscript delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/ambient_spm.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/apdu.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/apdu_parity.pl delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/test/app.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/test/async.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/test/async.h delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/test/dump-json.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/test/dump-json.h delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/heartbeat_spm.pl delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/test/monitor.c delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/nak.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/nak_cancellation.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/nak_list.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/nak_parity.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/nak_repeat.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/ncf.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/ncf_cancellation.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/ncf_list.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/ncf_suppression.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/odata.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/odata_completion.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/odata_jump.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/odata_jump_parity.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/odata_number.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/odata_rate.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/odata_reception.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/on-demand_spm.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/outofwindow_ncf.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion_parity.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion_parity_var_pktlen.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/rdata_jump.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/rdata_reception.pl delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/test/sim.c delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/spm.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/spm_jump.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/spm_jump2.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/spm_reception.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/spmr.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/spmr_after_spm.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/spmr_from_odata.pl delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/test/spmr_suppression.pl delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/test/sudoers.example delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/test/test.conf.pl delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/thread.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/time.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/time_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/timer.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/timer_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/token and leaky bucket.txt delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/tsi.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/tsi_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/txw.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/txw_unittest.c delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/valgrind.supp delete mode 100755 3rdparty/openpgm-svn-r1085/pgm/version_generator.py delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/win/mingw32-runtime_3.13-1openpgm3.diff delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/win/mingw32-runtime_3.15.2-0openpgm1.diff delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/win64/mingw-w64-bin_x86-64-linux_4.4.1-1openpgm1.diff delete mode 100644 3rdparty/openpgm-svn-r1085/pgm/wsastrerror.c create mode 100644 3rdparty/openpgm-svn-r1135-0001-sigsegv-in-txw.patch create mode 100644 3rdparty/openpgm-svn-r1135-0002-correct-checksum-calculation.patch create mode 100644 3rdparty/openpgm-svn-r1135-0003-fix-rdata-congestion-control.patch create mode 100644 3rdparty/openpgm-svn-r1135/doc/draft-ietf-rmt-bb-pgmcc-03.txt create mode 100644 3rdparty/openpgm-svn-r1135/doc/rfc3208.txt create mode 100644 3rdparty/openpgm-svn-r1135/pgm/COPYING create mode 100644 3rdparty/openpgm-svn-r1135/pgm/INSTALL create mode 100644 3rdparty/openpgm-svn-r1135/pgm/LICENSE create mode 100644 3rdparty/openpgm-svn-r1135/pgm/README create mode 100644 3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgm create mode 100644 3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgm89 create mode 100644 3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgmex create mode 100644 3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgmhttp create mode 100644 3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgmsnmp create mode 100644 3rdparty/openpgm-svn-r1135/pgm/SConstruct create mode 100644 3rdparty/openpgm-svn-r1135/pgm/SConstruct.097 create mode 100644 3rdparty/openpgm-svn-r1135/pgm/SConstruct.097.intelc create mode 100644 3rdparty/openpgm-svn-r1135/pgm/SConstruct.097.mingw64 create mode 100644 3rdparty/openpgm-svn-r1135/pgm/SConstruct.097.sunstudio create mode 100644 3rdparty/openpgm-svn-r1135/pgm/SConstruct.Debian4 create mode 100644 3rdparty/openpgm-svn-r1135/pgm/SConstruct.FreeBSD80 create mode 100644 3rdparty/openpgm-svn-r1135/pgm/SConstruct.OpenSolaris create mode 100644 3rdparty/openpgm-svn-r1135/pgm/SConstruct.RHEL4 create mode 100644 3rdparty/openpgm-svn-r1135/pgm/SConstruct.Solaris.gcc64 create mode 100644 3rdparty/openpgm-svn-r1135/pgm/SConstruct.Solaris.sungcc create mode 100644 3rdparty/openpgm-svn-r1135/pgm/SConstruct.Solaris.sunstudio create mode 100644 3rdparty/openpgm-svn-r1135/pgm/SConstruct.clang create mode 100644 3rdparty/openpgm-svn-r1135/pgm/SConstruct.mingw create mode 100644 3rdparty/openpgm-svn-r1135/pgm/SConstruct.mingw-wine create mode 100644 3rdparty/openpgm-svn-r1135/pgm/atomic_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/backtrace.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/checksum.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/checksum_perftest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/checksum_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/crossmingw.py create mode 100644 3rdparty/openpgm-svn-r1135/pgm/crossmingw64.py create mode 100644 3rdparty/openpgm-svn-r1135/pgm/engine.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/engine.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/engine_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/error.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/error_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/SConscript create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/SConscript89 create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/async.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/async.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/blocksyncrecv.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/daytime.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/enonblocksyncrecv.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/enonblocksyncrecvmsg.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/enonblocksyncrecvmsgv.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/getopt.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/getopt.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/pgmdump.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/pgmping.cc create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/pgmrecv.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/pgmsend.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/pgmtop.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/ping.proto create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/pnonblocksyncrecv.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/purinrecv.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/purinrecvcc.cc create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/purinsend.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/purinsendcc.cc create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/shortcakerecv.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/examples/snonblocksyncrecv.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/fec-block.txt create mode 100644 3rdparty/openpgm-svn-r1135/pgm/fec.txt create mode 100755 3rdparty/openpgm-svn-r1135/pgm/galois_generator.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/gcov-parse.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/gcov-seed.sh create mode 100755 3rdparty/openpgm-svn-r1135/pgm/gcov.sh create mode 100644 3rdparty/openpgm-svn-r1135/pgm/getifaddrs.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/getifaddrs.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/getifaddrs_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/getnodeaddr.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/getnodeaddr.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/getnodeaddr_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/gsi.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/gsi.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/gsi_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/hashtable.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/hashtable.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/histogram.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/histogram.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/htdocs/404.html create mode 100644 3rdparty/openpgm-svn-r1135/pgm/htdocs/base.css create mode 100755 3rdparty/openpgm-svn-r1135/pgm/htdocs/convert_to_macro.pl create mode 100644 3rdparty/openpgm-svn-r1135/pgm/htdocs/robots.txt create mode 100644 3rdparty/openpgm-svn-r1135/pgm/htdocs/xhtml10_strict.doctype create mode 100644 3rdparty/openpgm-svn-r1135/pgm/http.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/http_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/if.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/if.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/if_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/checksum.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/engine.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/features.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/fixed.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/framework.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/galois.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/getifaddrs.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/getnodeaddr.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/hashtable.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/histogram.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/i18n.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/indextoaddr.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/indextoname.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/inet_network.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/ip.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/list.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/math.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/md5.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/mem.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/messages.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/nametoindex.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/net.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/notify.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/packet_parse.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/packet_test.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/pgmMIB.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/pgmMIB_columns.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/pgmMIB_enums.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/processor.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/queue.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/rand.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/rate_control.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/receiver.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/reed_solomon.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/rxw.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/slist.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/sn.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/sockaddr.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/socket.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/source.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/sqn_list.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/string.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/thread.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/time.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/timer.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/tsi.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/txw.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/impl/wsastrerror.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/atomic.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/backtrace.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/engine.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/error.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/gsi.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/http.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/if.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/ip/pgm.hh create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/ip/pgm_endpoint.hh create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/list.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/log.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/macros.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/mem.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/messages.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/msgv.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/packet.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/pgm.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/pgm.hh create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/pgm_socket.hh create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/signal.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/skbuff.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/snmp.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/socket.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/time.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/tsi.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/types.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/version.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/winint.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/include/pgm/wininttypes.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/indextoaddr.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/indextoaddr.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/indextoaddr_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/indextoname.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/indextoname.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/inet_network.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/inet_network.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/inet_network_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/ip_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/list.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/log.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/math.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/math.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/md5.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/md5.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/md5_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/mem.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/mem.c.c89.patch create mode 100755 3rdparty/openpgm-svn-r1135/pgm/memcheck create mode 100644 3rdparty/openpgm-svn-r1135/pgm/messages.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/messages.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/mibs/PGM-MIB-petrova-01.txt create mode 100644 3rdparty/openpgm-svn-r1135/pgm/mld-semantics.txt create mode 100644 3rdparty/openpgm-svn-r1135/pgm/msfec.txt create mode 100644 3rdparty/openpgm-svn-r1135/pgm/nametoindex.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/nametoindex.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/net-snmp.txt create mode 100644 3rdparty/openpgm-svn-r1135/pgm/net.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/net.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/net_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/options.txt create mode 100644 3rdparty/openpgm-svn-r1135/pgm/packet_parse.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/packet_parse.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/packet_parse_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/packet_test.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/packet_test.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/packet_test_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/pgmMIB.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/pgmMIB_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/plan.txt create mode 100644 3rdparty/openpgm-svn-r1135/pgm/queue.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/rand.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/rand.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/rate_control.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/rate_control.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/rate_control_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/receiver.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/receiver.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/receiver.c.rej create mode 100644 3rdparty/openpgm-svn-r1135/pgm/receiver_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/recv.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/recv.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/recv_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/reed_solomon.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/reed_solomon.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/reed_solomon_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/rxw.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/rxw.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/rxw_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/signal.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/signal_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/skbuff.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/slist.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/snmp.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/snmp_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/sockaddr.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/sockaddr.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/socket.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/socket.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/socket_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/source.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/source.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/source.c.orig create mode 100644 3rdparty/openpgm-svn-r1135/pgm/source.c.rej create mode 100644 3rdparty/openpgm-svn-r1135/pgm/source_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/string.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/string.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/test/PGM/Test.pm create mode 100644 3rdparty/openpgm-svn-r1135/pgm/test/SConscript create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/ambient_spm.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/apdu.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/apdu_parity.pl create mode 100644 3rdparty/openpgm-svn-r1135/pgm/test/app.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/test/async.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/test/async.h create mode 100644 3rdparty/openpgm-svn-r1135/pgm/test/dump-json.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/test/dump-json.h create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/heartbeat_spm.pl create mode 100644 3rdparty/openpgm-svn-r1135/pgm/test/monitor.c create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/nak.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/nak_cancellation.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/nak_list.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/nak_parity.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/nak_repeat.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/ncf.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/ncf_cancellation.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/ncf_list.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/ncf_suppression.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/odata.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/odata_completion.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/odata_jump.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/odata_jump_parity.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/odata_number.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/odata_rate.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/odata_reception.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/on-demand_spm.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/outofwindow_ncf.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/rdata_completion.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/rdata_completion_parity.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/rdata_completion_parity_var_pktlen.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/rdata_jump.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/rdata_reception.pl create mode 100644 3rdparty/openpgm-svn-r1135/pgm/test/sim.c create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/spm.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/spm_jump.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/spm_jump2.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/spm_reception.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/spmr.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/spmr_after_spm.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/spmr_from_odata.pl create mode 100755 3rdparty/openpgm-svn-r1135/pgm/test/spmr_suppression.pl create mode 100644 3rdparty/openpgm-svn-r1135/pgm/test/sudoers.example create mode 100644 3rdparty/openpgm-svn-r1135/pgm/test/test.conf.pl create mode 100644 3rdparty/openpgm-svn-r1135/pgm/thread.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/thread.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/time.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/time.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/time_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/timer.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/timer.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/timer_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/token and leaky bucket.txt create mode 100644 3rdparty/openpgm-svn-r1135/pgm/tsi.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/tsi.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/tsi_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/txw.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/txw.c.c89.patch create mode 100644 3rdparty/openpgm-svn-r1135/pgm/txw_unittest.c create mode 100644 3rdparty/openpgm-svn-r1135/pgm/valgrind.supp create mode 100755 3rdparty/openpgm-svn-r1135/pgm/version_generator.py create mode 100644 3rdparty/openpgm-svn-r1135/pgm/win/mingw32-runtime_3.13-1openpgm3.diff create mode 100644 3rdparty/openpgm-svn-r1135/pgm/win/mingw32-runtime_3.15.2-0openpgm1.diff create mode 100644 3rdparty/openpgm-svn-r1135/pgm/win64/mingw-w64-bin_x86-64-linux_4.4.1-1openpgm1.diff create mode 100644 3rdparty/openpgm-svn-r1135/pgm/wsastrerror.c diff --git a/3rdparty/01-libpgm-fix-switch-fallthrough.patch b/3rdparty/01-libpgm-fix-switch-fallthrough.patch deleted file mode 100644 index 23dfc9c..0000000 --- a/3rdparty/01-libpgm-fix-switch-fallthrough.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Naur pgm/socket.c pgm-fixed/socket.c ---- pgm/socket.c 2010-05-22 06:42:00.000000000 +0200 -+++ pgm-fixed/socket.c 2010-07-14 19:24:43.710669589 +0200 -@@ -641,6 +641,7 @@ - break; - sock->spmr_expiry = *(const int*)optval; - status = TRUE; -+ break; - - /* size of receive window in sequence numbers. - * 0 < rxw_sqns < one less than half sequence space diff --git a/3rdparty/openpgm-svn-r1085/doc/draft-ietf-rmt-bb-pgmcc-03.txt b/3rdparty/openpgm-svn-r1085/doc/draft-ietf-rmt-bb-pgmcc-03.txt deleted file mode 100644 index 6f1869c..0000000 --- a/3rdparty/openpgm-svn-r1085/doc/draft-ietf-rmt-bb-pgmcc-03.txt +++ /dev/null @@ -1,1226 +0,0 @@ - -Internet Engineering Task Force RMT WG -INTERNET-DRAFT Luigi Rizzo/U. Pisa -draft-ietf-rmt-bb-pgmcc-03.txt Gianluca Iannaccone/Intel - Lorenzo Vicisano/Cisco - Mark Handley/UCL - 12 July 2004 - Expires: January 2005 - - - PGMCC single rate multicast congestion control: - Protocol Specification - - - -Status of this Document - -This document is an Internet-Draft and is in full conformance with all -provisions of Section 10 of RFC2026. - -Internet-Drafts are working documents of the Internet Engineering Task -Force (IETF), its areas, and its working groups. Note that other groups -may also distribute working documents as Internet-Drafts. - -Internet-Drafts are valid for a maximum of six months and may be -updated, replaced, or obsoleted by other documents at any time. It is -inappropriate to use Internet-Drafts as reference material or to cite -them other than as a "work in progress". - -The list of current Internet-Drafts can be accessed at -http://www.ietf.org/ietf/1id-abstracts.txt - -To view the list Internet-Draft Shadow Directories, see -http://www.ietf.org/shadow.html. - -This document is a product of the IETF RMT WG. Comments should be -addressed to the authors, or the WG's mailing list at rmt@lbl.gov. - - - Abstract - - - This document describes PGMCC, a single rate multicast - congestion control scheme which is TCP-friendly and achieves - scalability, stability and fast response to variations in - network conditions. PGMCC is suitable for both non-reliable - - - -Rizzo/Iannaccone/Vicisano/Handley [Page 1] - -INTERNET-DRAFT Expires: January 2005 July 2004 - - - and reliable data transfers. It is mainly designed for NAK- - based multicast protocols, and uses a window-based, TCP-like - control loop using positive ACKs between one representative of - the receiver group (the ACKER) and the sender. The ACKER is - selected dynamically and may change over time. - - PGMCC is made of two components: a window-based control loop, - which closely mimics TCP behavior, and a fast and low-overhead - procedure to select (and track changes of) the ACKER. The - scheme is robust to measurement errors, and supports fast - response to changes in the receiver set and/or network - conditions. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Rizzo/Iannaccone/Vicisano/Handley [Page 2] - -INTERNET-DRAFT Expires: January 2005 July 2004 - - - Table of Contents - - - 1. Introduction. . . . . . . . . . . . . . . . . . . . . . 4 - 1.1. Terminology. . . . . . . . . . . . . . . . . . . . . 4 - 2. Protocol Overview . . . . . . . . . . . . . . . . . . . 4 - 2.1. Packet Contents. . . . . . . . . . . . . . . . . . . 6 - 2.1.1. Data Packets. . . . . . . . . . . . . . . . . . . 6 - 2.1.2. Feedback Packets. . . . . . . . . . . . . . . . . 7 - 2.1.3. Field sizes and formats . . . . . . . . . . . . . 8 - 2.2. Window-based controller. . . . . . . . . . . . . . . 9 - 2.3. Acker Selection. . . . . . . . . . . . . . . . . . . 11 - 2.3.1. Initial Acker election. . . . . . . . . . . . . . 11 - 2.3.2. Acker dropouts. . . . . . . . . . . . . . . . . . 12 - 2.4. TCP Throughput Equation. . . . . . . . . . . . . . . 12 - 2.5. RTT measurement. . . . . . . . . . . . . . . . . . . 13 - 2.5.1. Explicit Timestamp. . . . . . . . . . . . . . . . 13 - 2.5.2. Implicit timestamp. . . . . . . . . . . . . . . . 13 - 2.5.3. Sequence numbers. . . . . . . . . . . . . . . . . 14 - 2.5.4. Recommendations . . . . . . . . . . . . . . . . . 15 - 2.6. Loss rate measurement. . . . . . . . . . . . . . . . 15 - 2.7. Timeouts . . . . . . . . . . . . . . . . . . . . . . 16 - 2.8. Interaction with feedback suppression - schemes . . . . . . . . . . . . . . . . . . . . . . . . . 16 - 2.9. Interaction with ECN . . . . . . . . . . . . . . . . 17 - 3. Procedures - Sender . . . . . . . . . . . . . . . . . . 17 - 4. Procedures -- Receiver. . . . . . . . . . . . . . . . . 18 - 5. Security Considerations . . . . . . . . . . . . . . . . 19 - 6. Authors' Addresses. . . . . . . . . . . . . . . . . . . 20 - 7. Acknowledgments . . . . . . . . . . . . . . . . . . . . 20 - 8. Full Copyright Statement. . . . . . . . . . . . . . . . 21 - - - - - - - - - - - - - - - - - - - - -Rizzo/Iannaccone/Vicisano/Handley [Page 3] - -INTERNET-DRAFT Expires: January 2005 July 2004 - - -1. Introduction - -This document describes PGMCC, a single rate multicast congestion -control scheme which is TCP-friendly and achieves scalability, stability -and fast response to variations in network conditions. - -PGMCC is designed for multicast sessions with one sender and one or more -receivers, and is a good match for transport protocols using negative -acknowledgements (NAKs) to collect feedback from the receivers. The -congestion control scheme implemented by PGMCC closely mimics the -congestion-control behavior of TCP, as it uses a window-based control -loop which is run between the sender and a selected receiver called the -ACKER. The role of the ACKER is to provide timely feedback in the same -way as a TCP receiver; additionally, the ACKER is selected dynamically -among the receivers as the one which would experience the lowest -throughput if separate TCP sessions were run between the sender and each -of the receivers. - -Scalability in PGMCC comes from the use of negative acknowledgements -(NAKs) for collecting feedback from receivers other than the ACKER. As -a consequence, the usual techniques for NAK suppression and aggregation -can be used to reduce the amount of feedback to the source and improve -the scalability of the scheme. - -PGMCC is designed to completely decouple congestion control from data -integrity. As a consequence, the scheme can work with both reliable data -transfer and unreliable communication protocols such as those used for -video or audio streaming. - -While designed with multicast in mind, PGMCC can be equally used as a -replacement for TCP for unicast sessions which require a lower degree of -reliability than what TCP offers. - - -1.1. Terminology - -In this document, the key words "MUST", "MUST NOT", "REQUIRED", "SHALL", -"SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and -"OPTIONAL" are to be interpreted as described in RFC 2119 and indicate -requirement levels for compliant PGMCC implementations. - - -2. Protocol Overview - -PGMCC is based on two separate but complementary mechanisms: - - o A window-based control loop which closely emulates TCP congestion - control. - - - -Rizzo/Iannaccone/Vicisano/Handley Section 2. [Page 4] - -INTERNET-DRAFT Expires: January 2005 July 2004 - - - The window-based control loop is simply an adaptation of the TCP - congestion control scheme to transport protocols where missing - (because of network errors or congestion) data packets are not - necessarily retransmitted, and so the congestion control scheme - cannot rely on cumulative acknowledgements. In PGMCC, the - ``congestion window'' is simulated using a token-based scheme which - permits congestion control to be decoupled from retransmission - state. One of the receivers in the group operates as the ACKER, i.e. - the node in charge of sending positive acknowledgements back to the - source and thus controlling the rate of the transfer. - - - o A procedure to select the ACKER. - The purpose of this procedure is to make sure that, in presence of - multiple receivers, the ACKER is dynamically selected to be the - receiver which would have the lowest throughput if separate TCP - sessions were run between the sender and each receiver. - For the acker selection mechanism, PGMCC uses a throughput equation - to determine the expected throughput for a given receiver as a - function of the loss rate and round-trip time. Unlike other schemes - [2], the TCP throughput equation is not used to determine the actual - sending rate, which is completely controlled by the window-based - control loop. - - -In principle, PGMCC's congestion control mechanism works as follows: - - - o Receivers measure the loss rate and feed this information back to - the sender, either in ACK or NAK messages. - - - o The sender also uses these feedback messages to measure the round- - trip time (RTT) to each receiver. - - - o The loss rate and RTT are then fed into PGMCC's throughput equation, - to determine the expected throughput to that receiver. - - - o The sender then selects as the acker the receiver with the lowest - expected throughput, as computed by the equation. - -The dynamics of the acker selection mechanism are sensitive to how the -measurements are performed and applied. In the rest of this document we -suggest specific mechanisms to perform and apply these measurements. -Other mechanisms are possible, but it is important to understand how the -interactions between mechanisms affect the dynamics of PGMCC. - - - -Rizzo/Iannaccone/Vicisano/Handley Section 2. [Page 5] - -INTERNET-DRAFT Expires: January 2005 July 2004 - - -2.1. Packet Contents - -Before specifying the sender and receiver functionality, we describe the -information required by PGMCC to perform its tasks. This information is -carried in the data packets sent by the sender, and in the feedback -packets sent by the receiver. As PGMCC will be used along with some -transport protocol, the actual data and feedback packets will contain -further information for use by the protocol itself. For this reason, we -do not specify packet formats, as these depend on the details of the -transport protocol used. - -Note that the requirements of the transport protocol in terms of packet -generation may differ from those of PGMCC. As an example, most NAK-based -reliable multicast protocols do not use positive acknowledgements, but -PGMCC requires ACKs for clocking out data packets; unreliable transport -protocols might have no interest in generating NAKs for data integrity -purposes, yet PGMCC depends on NAKs reaching the data sender in order to -elect the ACKER. - - -Implementors may decide to insert PGMCC-related information in already -existing protocol packets whenever possible, but in cases such as the -ones described in the previous paragraph, it might be necessary to -define and generate new packets exclusively for congestion control -purposes. As an example, in a prototype implementation of PGMCC on top -of the PGM protocol [7], some of the information used by PGMCC is -already present in the original protocol packets, and PGMCC-specific -information is carried as PGM options in ODATA and NAK packets. However, -a new packet type has been defined for ACKs, which are generated -according to the rules defined in this document. - - -2.1.1. Data Packets - -Each data packet sent by the data sender contains the following -information: - - - o A SEQUENCE NUMBER. This number is incremented by one for each data - packet transmitted. The field must be sufficiently large that it - does not wrap causing two different packets with the same sequence - number to be in the receiver's recent packet history at the same - time. - - - o A TIMESTAMP (or equivalent information, see Section 2.5) indicating - when the packet with this sequence number has been sent. There is - no requirement for synchronized clocks between the sender and the - - - -Rizzo/Iannaccone/Vicisano/Handley Section 2.1.1. [Page 6] - -INTERNET-DRAFT Expires: January 2005 July 2004 - - - receivers. The timestamp is used to measure network round-trip - times, so needs sufficient resolution for this task. A resolution - of 1ms would be adequate. - - - o The ACKER IDENTITY, i.e. the identity of the receiver in charge of - sending an acknowledgement for this data packet. The ACKER is - elected as a result of the process described in Section 2.3. - A special value is used to indicate that no ACKER is designated for - this packet -- this can happen at the beginning of a session or when - the current ACKER leaves the group. Receivers interpret this value - as a request to elect a new acker. - - -2.1.2. Feedback Packets - -There are two types of feedback packets used by PGMCC: ACK packets and -NAK packets. -ACK packets are generated by the current ACKER, and are used to detect -loss or successful delivery of packets, and to regulate the throughput -accordingly. ACK packets also contain information used to determine the -TCP-equivalent throughput for the ACKER. -NAK packets are sent by any receiver who experiences loss. They contain -information used to determine the TCP-equivalent throughput for that -receiver. In an actual protocol instantiation (such as PGM [7]), NAK -packets might also be used by the protocol to request the retransmission -of specific packets, and indicate the identity of the packet being -requested. - -Both ACK and NAK packets are sent by data receivers, and contain the -following information: - - - o The TIMESTAMP (or equivalent information) derived from the most - recently received data packet according to one of the techniques - described in Section 2.5. - This value is used by the sender to measure the RTT to the receiver - who generated this feedback packet. - - - o ``p'', the receiver's current estimate of the LOSS RATE. The loss - rate is measured by receivers as described in Section 2.6 - -In addition to the above, ACK packets (sent by the acker designated in -the corresponding data packets) must also contain the following -information: - - - - - -Rizzo/Iannaccone/Vicisano/Handley Section 2.1.2. [Page 7] - -INTERNET-DRAFT Expires: January 2005 July 2004 - - - o RX_MAX, the highest sequence number among received data packets - (taking care to deal with sequence number wrapping correctly). - - o ACK_BITMAP, a bitmap indicating the receive status of the latest N - (typically N=32) data packets with sequence numbers RX_MAX-(N-1) to - RX_MAX. - - -This information is used by the sender to record which packets have been -received or lost, and manipulate the transmit window accordingly. Note -that each ACK packet contains information about multiple packets, and -this increases the robustness of the scheme to loss of ACK packets. -This is necessary because ACKs are not sent reliably (unlike TCP's ACKs, -which are cumulative). - - -2.1.3. Field sizes and formats - -The following sizes and formats are suggested for the various fields -used by PGMCC and transmitted over the network: - - - o SEQUENCE NUMBERS - 32 bit, unsigned, network order. - - - o TIMESTAMPS - 32 bit, unsigned, network order. A resolution of 1ms or better is - desirable. - - - o ACKER IDENTITY - Same size and format of a network layer address (e.g. 32 bit for - IPv4). Note though that using an IP address for the Acker Identify - will cause problems with NAT traversal. Transport protocol - designers might examine the SSRC mechanism used by RTP [6] as an - alternative form of node identifier that could be used as Acker - Identity. - - - o LOSS RATE (``p'') - 16-bit unsigned integer, in network format, with 0 indicating no - loss and 2^16-1 indicating 100% loss. - - - o ACK BITMAP - 32-bit, in network format, with least significant bit indicating - receive status of packet RX_MAX. - - - -Rizzo/Iannaccone/Vicisano/Handley Section 2.1.3. [Page 8] - -INTERNET-DRAFT Expires: January 2005 July 2004 - - -2.2. Window-based controller - -In a window-based congestion control scheme such as TCP, the -``congestion window'' represents, among other things, the maximum amount -of packets in flight at any time, which in turn controls the throughput -of the session. The sender keeps track of the actual number of packets -in flight, basing on its transmissions and the reception of -acknowledgements. - -The sender may dynamically change the size of the window, according to -the congestion control scheme being used. In TCP, and PGMCC, an -``Additive Increase Multiplicative Decrease'' (AIMD) scheme is used: in -absence of loss, the window is increased by some fixed amount (typically -one packet) per round trip time (RTT), whereas upon loss the window is -reduced to a fraction of its original value (typically halved) in each -RTT in which a loss event is experienced. - -In PGMCC the window is managed using a token-based mechanism, controlled -by two variables: - - o A ``Window Size'', W, which describes the current window size in - packets. - - o A ``Token Count'', T, which indicates the number of packets that can - be transmitted. T is bounded above by W. It is decremented every - time a packet is transmitted, and incremented every time an ACK is - received, according to the rules below. - -Note that these two variables need to hold non-integer data. Typically -a fixed point representation with at least 16 bits for both integer and -fractional parts would be acceptable for implementation purposes. - -The information contained in each ACK is used to determine how many new -packets are acknowledged by that ACK, and whether there are -unacknowledged packets which were not reported in previous ACKs. The -sender also schedules a timeout to react in case no ACKs are received. - -The sender behaves as follows: - - - o INITIALIZATION - At startup, or after a timeout, both W and T are set to 1. - - - o ACK RECEPTION, NO LOSS DETECTED - If the incoming ACK reports new acknowledged packets, and no loss - (as defined in the next paragraph) is detected, then the window is - inflated by one packet per RTT. - - - -Rizzo/Iannaccone/Vicisano/Handley Section 2.2. [Page 9] - -INTERNET-DRAFT Expires: January 2005 July 2004 - - - NOTE: during the slow-start phase, TCP opens the window - exponentially up to the SSTHRESH value, which is computed by TCP - according to the dynamics of the session and updated upon losses. - - We do recommend that PGMCC uses a similar strategy, but using a - fixed, small value for SSTHRESH (e.g. 4 packets). In fact, due to - the dynamicity of the ACKER, which might change on every single - packet, it is hard to compute a reliable estimate of the SSTHRESH - without keeping state for multiple receivers, and the benefits are - small in any event. - - In summary, the reaction to ACK reception on no loss modifies T and - W as follows (here, N is the number of new packets acknowledged by - the incoming ACK): - - if (W < SSTHRESH) then - D = min(N, SSTHRESH - W) // use the first D acks for - exp.opening - N = N - D // and the remaining ones for - linear opening - T = T + 2*D - W = W + D - endif - // do linear window opening with the remaining acks - T = T + N * ( 1 + 1/W ) - W = W + N/W - - - o PACKET TRANSMISSION - One token is consumed for each packet transmitted: - - T = T - 1 - - - o ACK RECEPTION, LOSS DETECTED - If the incoming ACK reports an unacknowledged data packet which is - followed by at least 3 acknowledged data packets, then the packet is - assumed to be lost and PGMCC reacts by halving the window, in the - same way as TCP after 3 duplicate acknowledgements. This is - achieved by modifying T and W as follows: - - T = T - W/2 , W = W/2 - - to simulate the multiplicative decrease. - Additionally, all window manipulation is suspended for the - subsequent RTT. This is achieved by recording the current transmit - sequence number, and canceling any further manipulation of the - window until feedback is received for the next transmitted packet, - - - -Rizzo/Iannaccone/Vicisano/Handley Section 2.2. [Page 10] - -INTERNET-DRAFT Expires: January 2005 July 2004 - - - or until a timeout occurs. - - - - -2.3. Acker Selection - -The ACKER selection process in PGMCC aims at locating the receiver which -would have the lowest throughput if each receiver were using a separate -TCP connection to transfer data. - -Because the steady-state throughput of a TCP connection can be -characterized in a reasonably accurate way in terms of its loss rate and -round trip time [3], the throughput for each receiver can be estimated -by using these two parameters. - -Whenever an ACK or NAK packet from any of the receivers reaches it, the -sender is able to compute the expected throughput T_i for that receiver -by using the equation shown in Section 2.4, with the round trip time RTT -and loss rate p and measured as described in Sections 2.5 and 2.6, -respectively. At any given time, the sender stores the expected -throughput for the current ACKER, T_acker. This value is updated every -time an ACK or NAK from the current ACKER is received (note that, after -a new ACKER is selected, the sender will typically receive ACKs from the -old ACKER for one RTT, and the feedback from different ACKERs might be -interleaved if the paths leading to them have different round trip -times). - -Whenever an ACK or NAK is received from another node i (a previous ACKER -or some other receiver), the expected throughput T_i for that node is -computed, and compared with T_acker. Node i is selected as the new -acker if - - T_i < C * T_acker - -where the constant C between 0 and 1 provides some hysteresis and avoids -too frequent oscillations in the choice of the ACKER. A suggested value -for C is 0.75. - -Note that, from an implementation point of view (see Section 2.4), it is -more convenient to compute T_i ^(-2), so the above equation must be -modified accordingly. - - -2.3.1. Initial Acker election - -Upon reception of a data packet reporting that no acker is currently -selected, receivers generate a dummy NAK report which is used to elect - - - -Rizzo/Iannaccone/Vicisano/Handley Section 2.3.1. [Page 11] - -INTERNET-DRAFT Expires: January 2005 July 2004 - - -the initial acker. The NAK is sent with the usual feedback suppression -mechanism dictated by the transport protocol (possibly with shorter time -constants) to avoid feedback implosion, and the sender will select the -source of the first incoming NAK as the new ACKER. - - -2.3.2. Acker dropouts - - -If the ACKER decides to disconnect from the session, it can cause the -session to stop. To avoid this, it is recommended that an ACKER deciding -to leave the session informs the sender by sending an ACK packet (or a -duplicate) carrying an "ACKER_LEAVING" option. The reception of this -packet by the sender will in turn trigger an initial acker election -phase. - - - -2.4. TCP Throughput Equation - -Any realistic equation of TCP throughput as a function of loss event -rate and RTT should be suitable for use in PGMCC. Unlike other schemes -[2] where the throughput equation directly controls the transmit rate, -in PGMCC the equation is used only for acker selection purposes, and the -throughput values are only compared among themselves. As a consequence, -we can use the following equation, derived from the one presented in [3] -by setting RTO = 4 * RTT (as it is common practice): - - M = 1/T = RTT_i * sqrt(p) * (1 + 9*p * (1 + 32*(p)^2)) - - -where - - M = 1/T is proportional to the inverse of the throughput for the - receiver under consideration; - - RTT is the round trip time for the receiver under consideration; - - p is the loss rate for the receiver under consideration, between 0 - and 1.0; - -and multiplying constants are omitted. - -The above equation is accurate on a wide range of loss rates, and also -covers situations where retransmission timeouts have a significant -impact on the throughput of the protocol. - -Note that when p=0, the equation yields 1/T = M = 0. This does not - - - -Rizzo/Iannaccone/Vicisano/Handley Section 2.4. [Page 12] - -INTERNET-DRAFT Expires: January 2005 July 2004 - - -constitute a problem as we can still compare the M values computed for -different receivers to determine the acker. Also note that it is easier -to compute M^2 instead of M, because the former does not require the use -of sqrt(). - -In future, different throughput equations may be substituted for this -equation. The requirement is that the throughput equation be a -reasonable approximation of the sending rate of TCP for conformant TCP -congestion control. - -The parameters p and RTT need to be measured or calculated by a PGMCC -implementation. The measurement of RTT is specified in Section 2.5; the -measurement of p is specified in Section 2.6. - -2.5. RTT measurement - -In PGMCC, the RTT is measured by the sender making use of the timestamp -(or equivalent information) echoed back by each receiver in feedback -messages. Three procedures are possible to measure the RTT, as follows. -In no case is it required to have clock synchronization between sender -and receivers. - - -2.5.1. Explicit Timestamp - -This first technique relies on the transmission of a timestamp TS_j with -each data packet j. -The receiver will record the most recently received timestamp, and will -echo it back to the source when generating an ACK or a NAK. If the -feedback is delayed, the time elapsed between the reception of the -timestamp and the generation of the feedback should be added to the -echoed timestamp. -The sender computes the RTT by subtracting the received timestamp from -the current value of the clock. - -The resolution of the timestamp value should be good enough for -reasonable precision measurement of typical network round trip times. If -receivers need to apply correction for delayed feedback, it is necessary -that receivers know the resolution of the timestamp clock. A suggested -value is 1ms. - - -2.5.2. Implicit timestamp - -With this technique, the sender will record a timestamp TS_j for each -transmitted data packet j, but the timestamp will not be transmitted -with the packet itself. -The receiver will record the most recently received sequence number, and - - - -Rizzo/Iannaccone/Vicisano/Handley Section 2.5.2. [Page 13] - -INTERNET-DRAFT Expires: January 2005 July 2004 - - -will echo it back to the source when generating an ACK or a NAK. -The sender computes the RTT by looking up the timestamp associated with -the sequence number received in the feedback packet, and subtracting it -from the current clock value. - -If the feedback from the receiver is delayed, as it is commonly the case -for NAKs, the receiver can compute, and send back to the source, a -correction term corresponding to the time elapsed between the reception -of the timestamp and the generation of the feedback. The correction term -will then be subtracted by the sender in order to obtain the correct -estimate of the RTT. - -This RTT measurement technique is equivalent to the previous one, but it -saves some space in data packets as the timestamp does not need to be -sent explicitly. Feedback packets might become larger if the correction -value is transmitted explicitly; but in many cases, the sequence number -will already be present for other reasons (e.g. ACK packets), and -wherever space is a concern the sequence number and the correction term -can be packed in a single 32-bit word without loss of precision. - - -2.5.3. Sequence numbers - -This technique is the least precise, but it does not rely on the -presence of a high resolution clock on the nodes. -The sender will not compute any timestamp, and just send data packets -with their sequence number j. -The receiver will record the most recently received sequence number, and -will echo it back to the source when generating an ACK or a NAK. -The sender computes the RTT as the difference between the most recently -sent sequence number and the sequence number received from the ACK or -NAK packet. - -Note that in this case the RTT is not measured in seconds, but in -"sequence numbers", which are monotonically, but not uniformly, -increasing with time. The two measurements are equivalent if the sender -transmits at a constant rate. When the data rate changes over time (as -it is normally happens, given that PGMCC controls the actual data rate), -then the "measured" RTT values grow with the actual transmit rate. This -can influence the correctness of the results when comparing two -measurement done over different and only partially overlapping time (and -sequence number) intervals where the transmit rate incurs a significant -change. - - - - - - - - -Rizzo/Iannaccone/Vicisano/Handley Section 2.5.3. [Page 14] - -INTERNET-DRAFT Expires: January 2005 July 2004 - - -2.5.4. Recommendations - -Whenever possible, the measurement of the RTT should be carried out -using either explicit or implicit timestamps, and by keeping track of -the "correction term" (the delay between data reception and feedback -generation). - -If the receiver does not have a clock with a suitable resolution, the -correction term might not be present (or be inaccurate). In this case -the timestamps received by the sender on NAK packets might be in error, -in the worst case, by as much as the packet interarrival time. This -error will normally not be present on ACK packets, which are sent -immediately. A suitable correction should be applied by the sender in -order to avoid systematic errors. - -The measurement based on sequence numbers is less accurate, but also -less sensitive to errors due to the lack of the correction term. In -fact, the measurement error induced by the lack of the correction term -can be at most one unit. This suggests that, when the correction term -is not available, measurements based on sequence numbers should be -favoured. Simulations have shown that the acker selection mechanism -performs moderately better when the RTT measurement is based on -timestamps, but performance is reasonably good also with measurements -based on sequence numbers. - - - -2.6. Loss rate measurement - - -The loss measurement in PGMCC is entirely performed by receivers. The -measurement results do not directly influence the transmit rate, but are -only used for comparison purposes. As a consequence, the scheme is -reasonably robust to different measurement techniques, as long as they -are not influenced too strongly by single loss events. - -The main method suggested for loss measurement is Exponentially Weighted -Moving Average (EWMA), which is formally equivalent to a single-pole -digital low pass filter applied to a binary signal x_i, where x_i = 1 if -packet i is lost, x_i = 0 if packet i is successfully received. - -The loss rate p_i upon reception or detection of loss of packet i is -computed as - - - p_i = c_p * p_{i-1} + (1 - c_p ) * p_i - where the constant c_p between 0 and 1 is related to the bandpass of -the filter. Experiments have shown good performance with c = 500/65536, - - - -Rizzo/Iannaccone/Vicisano/Handley Section 2.6. [Page 15] - -INTERNET-DRAFT Expires: January 2005 July 2004 - - -and computations performed with fixed point arithmetic and 16 fractional -digits. - -As an alternative to EWMA, the technique used in TFRC [2] can be -adopted. Simulations have shown a moderate improvement in the acker -selection mechanism by measuring loss using the TFRC loss estimator, -which is however slightly more expensive to compute than the EWMA loss -estimator in presence of packet reordering. - - -2.7. Timeouts - - -When a packet is transmitted, the sender schedules a timeout to prevent -stalls upon loss of ACKs or disconnection of the ACKER. In TCP, which -has a similar problem, the timeout value is computed by accumulating -statistics (SRTT and RTTVAR) on RTT samples, starting from a default -initial value (3s) when no RTT samples are available. - -PGMCC can use a similar scheme to compute the timeouts, remembering that -upon ACKER changes (which may be very frequent), the computation of SRTT -and RTTVAR must be restarted from the beginning, unless the sender -decides to keep state for at least a small number of recent ackers. - -Because the ACKER can leave the group without notifying the sender, -after a number of successive timeouts the sender MUST force the election -of a new ACKER. We recommend this new election to be performed after -two successive timeouts. - - -2.8. Interaction with feedback suppression schemes - - -Several schemes are used by NAK-based multicast protocols to reduce the -amount of feedback directed toward the source and make the protocol -scale with large populations of receivers. Such schemes typically rely -on randomly delaying NAK generation, and suppressing pending NAKs when -an equivalent NAK or a retransmission is heard; or, intermediate nodes -such as routers can implement some form of feedback aggregation and -filtering. - -Such schemes might prevent NAKs from potential ACKER candidates from -reaching the source. This filtering might impact the speed at which -PGMCC selects the correct ACKER, though initial experience from -simulations seem to suggest that PGMCC behavior is not severely affected -by NAK suppression schemes. - - - - - -Rizzo/Iannaccone/Vicisano/Handley Section 2.8. [Page 16] - -INTERNET-DRAFT Expires: January 2005 July 2004 - - -2.9. Interaction with ECN - - -PGMCC can use ECN notifications in much the same way as actual losses, -and use such notifications to control the throughput of the session. - -At the receiver, ECN-marked data packets can be considered as lost -packets for the purpose of loss rate computation and ACK/NAK generation. -If the ACKER sends an ACK for ECN-marked packets, that ACK MUST report -that the packet being acknowledged that was ECN marked. Similarly the -ACKER must indicate in the ACK packet's received packets bitmap that the -packet was ECN-marked, or that the packet was lost. - -We note that to support use of the ECN nonce, the ACK packet's received -packets bitmap would require two bits per packet being reported. - - -3. Procedures - Sender - -The following pseudo-code specifies the complete behavior of the sender -in PGMCC. - - -initialization: - T = 1 ; W = 1 ; /* initialize window and number of tokens */ - RETRY = 0 ; /* number of consecutive timeouts so far */ - < initialize p, RTT for acker to default values > - ACKER = NO_ACKER; /* no acker is known */ - < initialize sequence numbers > - QUEUED = 0; /* packets waiting to be transmitted */ - -on transmission request: - send_packet() ; - -on timeout expiration : - T = 1 ; W = 1 ; /* initialize window and number of tokens */ - if (RETRY < RETRY_MAX) - RETRY = RETRY + 1 - else - ACKER = NO_ACKER ; /* old acker is not valid anymore */ - send_packet() ; - - - - - - - - - - -Rizzo/Iannaccone/Vicisano/Handley Section 3. [Page 17] - -INTERNET-DRAFT Expires: January 2005 July 2004 - - -on ACK/NAK reception from receiver I : - < compute p and RTT for source of this ACK, see Sec. 2.5 and 2.6 > - RETRY = 0 ; - if (ACKER == NO_ACKER) { /* select current as acker is no other known */ - ACKER = I ; - T = T + 1 ; - } - if (ACKER != I) - < select acker according to Sec. 2.3 > ; - else { - - if (packet_type == ACK) { - < update_window according to Sec.2.2 > - send_packet ; - if (ack_pending) - update_timeout ; - } -} - -send_packet() { - if (QUEUED > 0 && T >= 1) { - < transmit one packet > - T = T - 1 ; - QUEUED = QUEUED - 1 ; - } - if ( ) - -} - - - -4. Procedures -- Receiver - -The following pseudo-code specifies the complete behavior of the -receiver in PGMCC. - -A receiver only transmits an ACK packet when it receives a data packet -for which the receiver is designated as the ACKER by the data packet -itself. A receiver can transmit a NAK packet after it has detected that -a data packet is missing and a suitable delay has passed, as dictated by -the feedback suppression rules of the protocol in use. - -The data packet contains acknowledgement status about the most recent 32 -sequence numbers known to the receiver. - - - - - - - -Rizzo/Iannaccone/Vicisano/Handley Section 4. [Page 18] - -INTERNET-DRAFT Expires: January 2005 July 2004 - - -on initialization/session setup: - < initialize state variables and ACK bitmap > - -on DATA packet reception: - < update p measurement according to Sec.2.6 > - < record timestamp and packet reception time > - if (ACKER == this_node) { - < send an immediate ACK > - } - if ( ) - < schedule a timeout for NAK transmission > - -on NAK reception: - < suppress any pending NAK transmission for the sequence - number indicated in the NAK > - -on timeout: - if ( < there are missing and unacknowledged packets > ) { - < send a NAK for one or more of the missing packets > - < mark such packets as acknowledged > - if ( ) - < schedule a timeout for NAK transmission > - } - - -5. Security Considerations - -PGMCC is not a transport protocol in its own right, but a congestion -control mechanism that is intended to be used in conjunction with a -transport protocol. Therefore security primarily needs to be considered -in the context of a specific transport protocol and its authentication -mechanisms. - -Congestion control mechanisms can potentially be exploited to create -denial of service. This may occur through spoofed feedback. Thus any -transport protocol that uses PGMCC should take care to ensure that -feedback is only accepted from the receiver of the data. The precise -mechanism to achieve this will however depend on the transport protocol -itself. - -In addition, congestion control mechanisms may potentially be -manipulated by a greedy receiver that wishes to receive more than its -fair share of network bandwidth. A receiver might do this by first -reporting inflated loss and RTT samples, in order to get selected as the -ACKER, and then generating ACK at the desired rate (including possibly -claiming to have received packets that in fact were lost due to -congestion). Possible defenses against such a receiver could be based -on the sender verifying the correctness of the loss and RTT samples - - - -Rizzo/Iannaccone/Vicisano/Handley Section 5. [Page 19] - -INTERNET-DRAFT Expires: January 2005 July 2004 - - -supplied by the receiver. A PGMCC sender SHOULD compare the receiver -reports on loss rate and RTT with the information derived directly from -the incoming stream of ACKs. In case of discrepancy of the reports, a -PGMCC sender SHOULD mark the current acker as ineligible and initiate a -new acker election. The decision on how large that discrepancy should be -before initiating a new acker election is left to the implementation. - -Also, the sender MAY include some form of nonce that the receiver must -feed back to the sender to prove receipt. However, the details of such a -nonce would depend on the transport protocol, and in particular on -whether the transport protocol is reliable or unreliable. - - -6. Authors' Addresses - - Luigi Rizzo - luigi@iet.unipi.it - Dip. Ing. dell'Informazione, - Univ. di Pisa - via Diotisalvi 2, 56122 Pisa, Italy - - Gianluca Iannaccone - gianluca.iannaccone@intel.com - Intel Research - 15 JJ Thomson Avenue, Cambridge CB3 0FD, UK - - Lorenzo Vicisano - lorenzo@cisco.com - cisco Systems, Inc. - 170 West Tasman Dr., - San Jose, CA, USA, 95134 - - Mark Handley - m.handley@cs.ucl.ac.uk - University College London, - Gower Street, London WC1E 6BT, UK - - -7. Acknowledgments - -We would like to acknowledge feedback and discussions on equation-based -congestion control with a wide range of people, including members of the -Reliable Multicast Research Group, the Reliable Multicast Transport -Working Group, and the End-to-End Research Group. - - - - - - - -Rizzo/Iannaccone/Vicisano/Handley Section 7. [Page 20] - -INTERNET-DRAFT Expires: January 2005 July 2004 - - -[1] Bradner, S., Key words for use in RFCs to Indicate Requirement -Levels (IETF RFC 2119) http://www.rfc-editor.org/rfc/rfc2119.txt - -[2] Floyd, S., Handley, M., Padhye, J., Widmer, J., "Equation-Based -Congestion Control for Unicast Applications", ACM SIGCOMM 2000, -Stockholm, Aug. 2000 - -[3] Padhye, J. and Firoiu, V. and Towsley, D. and Kurose, J., "Modeling -TCP Throughput: A Simple Model and its Empirical Validation", Proc ACM -SIGCOMM 1998. - -[4] Mankin, A., Romanow, A., Brander, S., Paxson, V., "IETF Criteria for -Evaluating Reliable Multicast Transport and Application Protocols," -RFC2357, June 1998. - -[5] Rizzo, L., "pgmcc: a TCP-friendly single-rate multicast congestion -control scheme", ACM SIGCOMM 2000, Stockholm, Aug.2000 - -[6] Schulzrinne, H., Casner, S., Frederick, R., Jacobson, V., "RTP: A -Transport Protocol for Real-Time Applications", RFC 1889, Jan 1996. - -[7] Speakman, T., Crowcroft, J., Gemmell, J., Farinacci, D. , Lin, S., -Leshchiner, D., Luby, M., Montgomery, T. , Rizzo, L., Tweedly, A., -Bhaskar, N., Edmonstone, R., Sumanasekera, R., Vicisano, L., PGM -Reliable Transport Protocol Specification, RFC 3208, December 2001. -rfc3208.txt also available at ftp://ftp.rfc-editor.org/in- -notes/rfc3208.txt - - - - -8. Full Copyright Statement - -Copyright (C) The Internet Society (2000). All Rights Reserved. - -This document and translations of it may be copied and furnished to -others, and derivative works that comment on or otherwise explain it or -assist in its implementation may be prepared, copied, published and -distributed, in whole or in part, without restriction of any kind, -provided that the above copyright notice and this paragraph are included -on all such copies and derivative works. However, this document itself -may not be modified in any way, such as by removing the copyright notice -or references to the Internet Society or other Internet organizations, -except as needed for the purpose of developing Internet standards in -which case the procedures for copyrights defined in the Internet -languages other than English. - - - - - -Rizzo/Iannaccone/Vicisano/Handley Section 8. [Page 21] - -INTERNET-DRAFT Expires: January 2005 July 2004 - - -The limited permissions granted above are perpetual and will not be -revoked by the Internet Society or its successors or assigns. - -This document and the information contained herein is provided on an "AS -IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK -FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT -LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT -INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR -FITNESS FOR A PARTICULAR PURPOSE." - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Rizzo/Iannaccone/Vicisano/Handley Section 8. [Page 22] diff --git a/3rdparty/openpgm-svn-r1085/doc/rfc3208.txt b/3rdparty/openpgm-svn-r1085/doc/rfc3208.txt deleted file mode 100644 index fb82c26..0000000 --- a/3rdparty/openpgm-svn-r1085/doc/rfc3208.txt +++ /dev/null @@ -1,6219 +0,0 @@ - - - - - - -Network Working Group T. Speakman -Request for Comments: 3208 Cisco Systems -Category: Experimental J. Crowcroft - UCL - J. Gemmell - Microsoft - D. Farinacci - Procket Networks - S. Lin - Juniper Networks - D. Leshchiner - TIBCO Software - M. Luby - Digital Fountain - T. Montgomery - Talarian Corporation - L. Rizzo - University of Pisa - A. Tweedly - N. Bhaskar - R. Edmonstone - R. Sumanasekera - L. Vicisano - Cisco Systems - December 2001 - - - PGM Reliable Transport Protocol Specification - -Status of this Memo - - This memo defines an Experimental Protocol for the Internet - community. It does not specify an Internet standard of any kind. - Discussion and suggestions for improvement are requested. - Distribution of this memo is unlimited. - -Copyright Notice - - Copyright (C) The Internet Society (2001). All Rights Reserved. - -Abstract - - Pragmatic General Multicast (PGM) is a reliable multicast transport - protocol for applications that require ordered or unordered, - duplicate-free, multicast data delivery from multiple sources to - multiple receivers. PGM guarantees that a receiver in the group - either receives all data packets from transmissions and repairs, or - is able to detect unrecoverable data packet loss. PGM is - - - -Speakman, et. al. Experimental [Page 1] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - specifically intended as a workable solution for multicast - applications with basic reliability requirements. Its central design - goal is simplicity of operation with due regard for scalability and - network efficiency. - -Table of Contents - - 1. Introduction and Overview .................................. 3 - 2. Architectural Description .................................. 9 - 3. Terms and Concepts ......................................... 12 - 4. Procedures - General ....................................... 18 - 5. Procedures - Sources ....................................... 19 - 6. Procedures - Receivers ..................................... 22 - 7. Procedures - Network Elements .............................. 27 - 8. Packet Formats ............................................. 31 - 9. Options .................................................... 40 - 10. Security Considerations .................................... 56 - 11. Appendix A - Forward Error Correction ...................... 58 - 12. Appendix B - Support for Congestion Control ................ 72 - 13. Appendix C - SPM Requests .................................. 79 - 14. Appendix D - Poll Mechanism ................................ 82 - 15. Appendix E - Implosion Prevention .......................... 92 - 16. Appendix F - Transmit Window Example ....................... 98 - 17 Appendix G - Applicability Statement ....................... 103 - 18. Abbreviations .............................................. 105 - 19. Acknowledgments ............................................ 106 - 20. References ................................................. 106 - 21. Authors' Addresses.......................................... 108 - 22. Full Copyright Statement ................................... 111 - -Nota Bene: - - The publication of this specification is intended to freeze the - definition of PGM in the interest of fostering both ongoing and - prospective experimentation with the protocol. The intent of that - experimentation is to provide experience with the implementation and - deployment of a reliable multicast protocol of this class so as to be - able to feed that experience back into the longer-term - standardization process underway in the Reliable Multicast Transport - Working Group of the IETF. Appendix G provides more specific detail - on the scope and status of some of this experimentation. Reports of - experiments include [16-23]. Additional results and new - experimentation are encouraged. - - - - - - - - -Speakman, et. al. Experimental [Page 2] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -1. Introduction and Overview - - A variety of reliable protocols have been proposed for multicast data - delivery, each with an emphasis on particular types of applications, - network characteristics, or definitions of reliability ([1], [2], - [3], [4]). In this tradition, Pragmatic General Multicast (PGM) is a - reliable transport protocol for applications that require ordered or - unordered, duplicate-free, multicast data delivery from multiple - sources to multiple receivers. - - PGM is specifically intended as a workable solution for multicast - applications with basic reliability requirements rather than as a - comprehensive solution for multicast applications with sophisticated - ordering, agreement, and robustness requirements. Its central design - goal is simplicity of operation with due regard for scalability and - network efficiency. - - PGM has no notion of group membership. It simply provides reliable - multicast data delivery within a transmit window advanced by a source - according to a purely local strategy. Reliable delivery is provided - within a source's transmit window from the time a receiver joins the - group until it departs. PGM guarantees that a receiver in the group - either receives all data packets from transmissions and repairs, or - is able to detect unrecoverable data packet loss. PGM supports any - number of sources within a multicast group, each fully identified by - a globally unique Transport Session Identifier (TSI), but since these - sources/sessions operate entirely independently of each other, this - specification is phrased in terms of a single source and extends - without modification to multiple sources. - - More specifically, PGM is not intended for use with applications that - depend either upon acknowledged delivery to a known group of - recipients, or upon total ordering amongst multiple sources. - - Rather, PGM is best suited to those applications in which members may - join and leave at any time, and that are either insensitive to - unrecoverable data packet loss or are prepared to resort to - application recovery in the event. Through its optional extensions, - PGM provides specific mechanisms to support applications as disparate - as stock and news updates, data conferencing, low-delay real-time - video transfer, and bulk data transfer. - - In the following text, transport-layer originators of PGM data - packets are referred to as sources, transport-layer consumers of PGM - data packets are referred to as receivers, and network-layer entities - in the intervening network are referred to as network elements. - - - - - -Speakman, et. al. Experimental [Page 3] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Unless otherwise specified, the term "repair" will be used to - indicate both the actual retransmission of a copy of a missing packet - or the transmission of an FEC repair packet. - -Terminology - - The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", - "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this - document are to be interpreted as described in RFC 2119 [14] and - indicate requirement levels for compliant PGM implementations. - -1.1. Summary of Operation - - PGM runs over a datagram multicast protocol such as IP multicast [5]. - In the normal course of data transfer, a source multicasts sequenced - data packets (ODATA), and receivers unicast selective negative - acknowledgments (NAKs) for data packets detected to be missing from - the expected sequence. Network elements forward NAKs PGM-hop-by- - PGM-hop to the source, and confirm each hop by multicasting a NAK - confirmation (NCF) in response on the interface on which the NAK was - received. Repairs (RDATA) may be provided either by the source - itself or by a Designated Local Repairer (DLR) in response to a NAK. - - Since NAKs provide the sole mechanism for reliability, PGM is - particularly sensitive to their loss. To minimize NAK loss, PGM - defines a network-layer hop-by-hop procedure for reliable NAK - forwarding. - - Upon detection of a missing data packet, a receiver repeatedly - unicasts a NAK to the last-hop PGM network element on the - distribution tree from the source. A receiver repeats this NAK until - it receives a NAK confirmation (NCF) multicast to the group from that - PGM network element. That network element responds with an NCF to - the first occurrence of the NAK and any further retransmissions of - that same NAK from any receiver. In turn, the network element - repeatedly forwards the NAK to the upstream PGM network element on - the reverse of the distribution path from the source of the original - data packet until it also receives an NCF from that network element. - Finally, the source itself receives and confirms the NAK by - multicasting an NCF to the group. - - While NCFs are multicast to the group, they are not propagated by PGM - network elements since they act as hop-by-hop confirmations. - - - - - - - - -Speakman, et. al. Experimental [Page 4] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - To avoid NAK implosion, PGM specifies procedures for subnet-based NAK - suppression amongst receivers and NAK elimination within network - elements. The usual result is the propagation of just one copy of a - given NAK along the reverse of the distribution path from any network - with directly connected receivers to a source. - - The net effect is that unicast NAKs return from a receiver to a - source on the reverse of the path on which ODATA was forwarded, that - is, on the reverse of the distribution tree from the source. More - specifically, they return through exactly the same sequence of PGM - network elements through which ODATA was forwarded, but in reverse. - The reasons for handling NAKs this way will become clear in the - discussion of constraining repairs, but first it's necessary to - describe the mechanisms for establishing the requisite source path - state in PGM network elements. - - To establish source path state in PGM network elements, the basic - data transfer operation is augmented by Source Path Messages (SPMs) - from a source, periodically interleaved with ODATA. SPMs function - primarily to establish source path state for a given TSI in all PGM - network elements on the distribution tree from the source. PGM - network elements use this information to address returning unicast - NAKs directly to the upstream PGM network element toward the source, - and thereby insure that NAKs return from a receiver to a source on - the reverse of the distribution path for the TSI. - - SPMs are sent by a source at a rate that serves to maintain up-to- - date PGM neighbor information. In addition, SPMs complement the role - of DATA packets in provoking further NAKs from receivers, and - maintaining receive window state in the receivers. - - As a further efficiency, PGM specifies procedures for the constraint - of repairs by network elements so that they reach only those network - segments containing group members that did not receive the original - transmission. As NAKs traverse the reverse of the ODATA path - (upward), they establish repair state in the network elements which - is used in turn to constrain the (downward) forwarding of the - corresponding RDATA. - - Besides procedures for the source to provide repairs, PGM also - specifies options and procedures that permit designated local - repairers (DLRs) to announce their availability and to redirect - repair requests (NAKs) to themselves rather than to the original - source. In addition to these conventional procedures for loss - recovery through selective ARQ, Appendix A specifies Forward Error - Correction (FEC) procedures for sources to provide and receivers to - request general error correcting parity packets rather than selective - retransmissions. - - - -Speakman, et. al. Experimental [Page 5] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Finally, since PGM operates without regular return traffic from - receivers, conventional feedback mechanisms for transport flow and - congestion control cannot be applied. Appendix B specifies a TCP- - friendly, NE-based solution for PGM congestion control, and cites a - reference to a TCP-friendly, end-to-end solution for PGM congestion - control. - - In its basic operation, PGM relies on a purely rate-limited - transmission strategy in the source to bound the bandwidth consumed - by PGM transport sessions and to define the transmit window - maintained by the source. - - PGM defines four basic packet types: three that flow downstream - (SPMs, DATA, NCFs), and one that flows upstream (NAKs). - -1.2. Design Goals and Constraints - - PGM has been designed to serve that broad range of multicast - applications that have relatively simple reliability requirements, - and to do so in a way that realizes the much advertised but often - unrealized network efficiencies of multicast data transfer. The - usual impediments to realizing these efficiencies are the implosion - of negative and positive acknowledgments from receivers to sources, - repair latency from the source, and the propagation of repairs to - disinterested receivers. - -1.2.1. Reliability. - - Reliable data delivery across an unreliable network is conventionally - achieved through an end-to-end protocol in which a source (implicitly - or explicitly) solicits receipt confirmation from a receiver, and the - receiver responds positively or negatively. While the frequency of - negative acknowledgments is a function of the reliability of the - network and the receiver's resources (and so, potentially quite low), - the frequency of positive acknowledgments is fixed at at least the - rate at which the transmit window is advanced, and usually more - often. - - Negative acknowledgments primarily determine repairs and reliability. - Positive acknowledgments primarily determine transmit buffer - management. - - When these principles are extended without modification to multicast - protocols, the result, at least for positive acknowledgments, is a - burden of positive acknowledgments transmitted to the source that - quickly threatens to overwhelm it as the number of receivers grows. - More succinctly, ACK implosion keeps ACK-based reliable multicast - protocols from scaling well. - - - -Speakman, et. al. Experimental [Page 6] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - One of the goals of PGM is to get as strong a definition of - reliability as possible from as simple a protocol as possible. ACK - implosion can be addressed in a variety of effective but complicated - ways, most of which require re-transmit capability from other than - the original source. - - An alternative is to dispense with positive acknowledgments - altogether, and to resort to other strategies for buffer management - while retaining negative acknowledgments for repairs and reliability. - The approach taken in PGM is to retain negative acknowledgments, but - to dispense with positive acknowledgments and resort instead to - timeouts at the source to manage transmit resources. - - The definition of reliability with PGM is a direct consequence of - this design decision. PGM guarantees that a receiver either receives - all data packets from transmissions and repairs, or is able to detect - unrecoverable data packet loss. - - PGM includes strategies for repeatedly provoking NAKs from receivers, - and for adding reliability to the NAKs themselves. By reinforcing - the NAK mechanism, PGM minimizes the probability that a receiver will - detect a missing data packet so late that the packet is unavailable - for repair either from the source or from a designated local repairer - (DLR). Without ACKs and knowledge of group membership, however, PGM - cannot eliminate this possibility. - -1.2.2. Group Membership - - A second consequence of eliminating ACKs is that knowledge of group - membership is neither required nor provided by the protocol. - Although a source may receive some PGM packets (NAKs for instance) - from some receivers, the identity of the receivers does not figure in - the processing of those packets. Group membership MAY change during - the course of a PGM transport session without the knowledge of or - consequence to the source or the remaining receivers. - -1.2.3. Efficiency - - While PGM avoids the implosion of positive acknowledgments simply by - dispensing with ACKs, the implosion of negative acknowledgments is - addressed directly. - - Receivers observe a random back-off prior to generating a NAK during - which interval the NAK is suppressed (i.e. it is not sent, but the - receiver acts as if it had sent it) by the receiver upon receipt of a - matching NCF. In addition, PGM network elements eliminate duplicate - NAKs received on different interfaces on the same network element. - - - - -Speakman, et. al. Experimental [Page 7] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - The combination of these two strategies usually results in the source - receiving just a single NAK for any given lost data packet. - - Whether a repair is provided from a DLR or the original source, it is - important to constrain that repair to only those network segments - containing members that negatively acknowledged the original - transmission rather than propagating it throughout the group. PGM - specifies procedures for network elements to use the pattern of NAKs - to define a sub-tree within the group upon which to forward the - corresponding repair so that it reaches only those receivers that - missed it in the first place. - -1.2.4. Simplicity - - PGM is designed to achieve the greatest improvement in reliability - (as compared to the usual UDP) with the least complexity. As a - result, PGM does NOT address conference control, global ordering - amongst multiple sources in the group, nor recovery from network - partitions. - -1.2.5. Operability - - PGM is designed to function, albeit with less efficiency, even when - some or all of the network elements in the multicast tree have no - knowledge of PGM. To that end, all PGM data packets can be - conventionally multicast routed by non-PGM network elements with no - loss of functionality, but with some inefficiency in the propagation - of RDATA and NCFs. - - In addition, since NAKs are unicast to the last-hop PGM network - element and NCFs are multicast to the group, NAK/NCF operation is - also consistent across non-PGM network elements. Note that for NAK - suppression to be most effective, receivers should always have a PGM - network element as a first hop network element between themselves and - every path to every PGM source. If receivers are several hops - removed from the first PGM network element, the efficacy of NAK - suppression may degrade. - -1.3. Options - - In addition to the basic data transfer operation described above, PGM - specifies several end-to-end options to address specific application - requirements. PGM specifies options to support fragmentation, late - joining, redirection, Forward Error Correction (FEC), reachability, - and session synchronization/termination/reset. Options MAY be - appended to PGM data packet headers only by their original - transmitters. While they MAY be interpreted by network elements, - options are neither added nor removed by network elements. - - - -Speakman, et. al. Experimental [Page 8] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - All options are receiver-significant (i.e., they must be interpreted - by receivers). Some options are also network-significant (i.e., they - must be interpreted by network elements). - - Fragmentation MAY be used in conjunction with data packets to allow a - transport-layer entity at the source to break up application-layer - data packets into multiple PGM data packets to conform with the - maximum transmission unit (MTU) supported by the network layer. - - Late joining allows a source to indicate whether or not receivers may - request all available repairs when they initially join a particular - transport session. - - Redirection MAY be used in conjunction with Poll Responses to allow a - DLR to respond to normal NCFs or POLLs with a redirecting POLR - advertising its own address as an alternative re-transmitter to the - original source. - - FEC techniques MAY be applied by receivers to use source-provided - parity packets rather than selective retransmissions to effect loss - recovery. - -2. Architectural Description - - As an end-to-end transport protocol, PGM specifies packet formats and - procedures for sources to transmit and for receivers to receive data. - To enhance the efficiency of this data transfer, PGM also specifies - packet formats and procedures for network elements to improve the - reliability of NAKs and to constrain the propagation of repairs. The - division of these functions is described in this section and expanded - in detail in the next section. - -2.1. Source Functions - - Data Transmission - - Sources multicast ODATA packets to the group within the - transmit window at a given transmit rate. - - Source Path State - - Sources multicast SPMs to the group, interleaved with ODATA if - present, to establish source path state in PGM network - elements. - - - - - - - -Speakman, et. al. Experimental [Page 9] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - NAK Reliability - - Sources multicast NCFs to the group in response to any NAKs - they receive. - - Repairs - - Sources multicast RDATA packets to the group in response to - NAKs received for data packets within the transmit window. - - Transmit Window Advance - - Sources MAY advance the trailing edge of the window according - to one of a number of strategies. Implementations MAY support - automatic adjustments such as keeping the window at a fixed - size in bytes, a fixed number of packets or a fixed real time - duration. In addition, they MAY optionally delay window - advancement based on NAK-silence for a certain period. Some - possible strategies are outlined later in this document. - -2.2. Receiver Functions - - Source Path State - - Receivers use SPMs to determine the last-hop PGM network - element for a given TSI to which to direct their NAKs. - - Data Reception - - Receivers receive ODATA within the transmit window and - eliminate any duplicates. - - Repair Requests - - Receivers unicast NAKs to the last-hop PGM network element (and - MAY optionally multicast a NAK with TTL of 1 to the local - group) for data packets within the receive window detected to - be missing from the expected sequence. A receiver MUST - repeatedly transmit a given NAK until it receives a matching - NCF. - - NAK Suppression - - Receivers suppress NAKs for which a matching NCF or NAK is - received during the NAK transmit back-off interval. - - - - - - -Speakman, et. al. Experimental [Page 10] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Receive Window Advance - - Receivers immediately advance their receive windows upon - receipt of any PGM data packet or SPM within the transmit - window that advances the receive window. - -2.3. Network Element Functions - - Network elements forward ODATA without intervention. - - Source Path State - - Network elements intercept SPMs and use them to establish - source path state for the corresponding TSI before multicast - forwarding them in the usual way. - - NAK Reliability - - Network elements multicast NCFs to the group in response to any - NAK they receive. For each NAK received, network elements - create repair state recording the transport session identifier, - the sequence number of the NAK, and the input interface on - which the NAK was received. - - Constrained NAK Forwarding - - Network elements repeatedly unicast forward only the first copy - of any NAK they receive to the upstream PGM network element on - the distribution path for the TSI until they receive an NCF in - response. In addition, they MAY optionally multicast this NAK - upstream with TTL of 1. - - Nota Bene: Once confirmed by an NCF, network elements discard NAK - packets; NAKs are NOT retained in network elements beyond this - forwarding operation, but state about the reception of them is - stored. - - NAK Elimination - - Network elements discard exact duplicates of any NAK for which - they already have repair state (i.e., that has been forwarded - either by themselves or a neighboring PGM network element), and - respond with a matching NCF. - - - - - - - - -Speakman, et. al. Experimental [Page 11] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Constrained RDATA Forwarding - - Network elements use NAKs to maintain repair state consisting - of a list of interfaces upon which a given NAK was received, - and they forward the corresponding RDATA only on these - interfaces. - - NAK Anticipation - - If a network element hears an upstream NCF (i.e., on the - upstream interface for the distribution tree for the TSI), it - establishes repair state without outgoing interfaces in - anticipation of responding to and eliminating duplicates of the - NAK that may arrive from downstream. - -3. Terms and Concepts - - Before proceeding from the preceding overview to the detail in the - subsequent Procedures, this section presents some concepts and - definitions that make that detail more intelligible. - -3.1. Transport Session Identifiers - - Every PGM packet is identified by a: - - TSI transport session identifier - - TSIs MUST be globally unique, and only one source at a time may act - as the source for a transport session. (Note that repairers do not - change the TSI in any RDATA they transmit). TSIs are composed of the - concatenation of a globally unique source identifier (GSI) and a - source-assigned data-source port. - - Since all PGM packets originated by receivers are in response to PGM - packets originated by a source, receivers simply echo the TSI heard - from the source in any corresponding packets they originate. - - Since all PGM packets originated by network elements are in response - to PGM packets originated by a receiver, network elements simply echo - the TSI heard from the receiver in any corresponding packets they - originate. - -3.2. Sequence Numbers - - PGM uses a circular sequence number space from 0 through ((2**32) - - 1) to identify and order ODATA packets. Sources MUST number ODATA - packets in unit increments in the order in which the corresponding - application data is submitted for transmission. Within a transmit or - - - -Speakman, et. al. Experimental [Page 12] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - receive window (defined below), a sequence number x is "less" or - "older" than sequence number y if it numbers an ODATA packet - preceding ODATA packet y, and a sequence number y is "greater" or - "more recent" than sequence number x if it numbers an ODATA packet - subsequent to ODATA packet x. - -3.3. Transmit Window - - The description of the operation of PGM rests fundamentally on the - definition of the source-maintained transmit window. This definition - in turn is derived directly from the amount of transmitted data (in - seconds) a source retains for repair (TXW_SECS), and the maximum - transmit rate (in bytes/second) maintained by a source to regulate - its bandwidth utilization (TXW_MAX_RTE). - - In terms of sequence numbers, the transmit window is the range of - sequence numbers consumed by the source for sequentially numbering - and transmitting the most recent TXW_SECS of ODATA packets. The - trailing (or left) edge of the transmit window (TXW_TRAIL) is defined - as the sequence number of the oldest data packet available for repair - from a source. The leading (or right) edge of the transmit window - (TXW_LEAD) is defined as the sequence number of the most recent data - packet a source has transmitted. - - The size of the transmit window in sequence numbers (TXW_SQNS) (i.e., - the difference between the leading and trailing edges plus one) MUST - be no greater than half the PGM sequence number space less one. - - When TXW_TRAIL is equal to TXW_LEAD, the transmit window size is one. - When TXW_TRAIL is equal to TXW_LEAD plus one, the transmit window - size is empty. - -3.4. Receive Window - - The receive window at the receivers is determined entirely by PGM - packets from the source. That is, a receiver simply obeys what the - source tells it in terms of window state and advancement. - - For a given transport session identified by a TSI, a receiver - maintains: - - RXW_TRAIL the sequence number defining the trailing edge of the - receive window, the sequence number (known from data - packets and SPMs) of the oldest data packet available - for repair from the source - - - - - - -Speakman, et. al. Experimental [Page 13] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - RXW_LEAD the sequence number defining the leading edge of the - receive window, the greatest sequence number of any - received data packet within the transmit window - - The receive window is the range of sequence numbers a receiver is - expected to use to identify receivable ODATA. - - A data packet is described as being "in" the receive window if its - sequence number is in the receive window. - - The receive window is advanced by the receiver when it receives an - SPM or ODATA packet within the transmit window that increments - RXW_TRAIL. Receivers also advance their receive windows upon receipt - of any PGM data packet within the receive window that advances the - receive window. - -3.5. Source Path State - - To establish the repair state required to constrain RDATA, it's - essential that NAKs return from a receiver to a source on the reverse - of the distribution tree from the source. That is, they must return - through the same sequence of PGM network elements through which the - ODATA was forwarded, but in reverse. There are two reasons for this, - the less obvious one being by far the more important. - - The first and obvious reason is that RDATA is forwarded on the same - path as ODATA and so repair state must be established on this path if - it is to constrain the propagation of RDATA. - - The second and less obvious reason is that in the absence of repair - state, PGM network elements do NOT forward RDATA, so the default - behavior is to discard repairs. If repair state is not properly - established for interfaces on which ODATA went missing, then - receivers on those interfaces will continue to NAK for lost data and - ultimately experience unrecoverable data loss. - - The principle function of SPMs is to provide the source path state - required for PGM network elements to forward NAKs from one PGM - network element to the next on the reverse of the distribution tree - for the TSI, establishing repair state each step of the way. This - source path state is simply the address of the upstream PGM network - element on the reverse of the distribution tree for the TSI. That - upstream PGM network element may be more than one subnet hop away. - SPMs establish the identity of the upstream PGM network element on - the distribution tree for each TSI in each group in each PGM network - element, a sort of virtual PGM topology. So although NAKs are - unicast addressed, they are NOT unicast routed by PGM network - elements in the conventional sense. Instead PGM network elements use - - - -Speakman, et. al. Experimental [Page 14] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - the source path state established by SPMs to direct NAKs PGM-hop-by- - PGM-hop toward the source. The idea is to constrain NAKs to the pure - PGM topology spanning the more heterogeneous underlying topology of - both PGM and non-PGM network elements. - - The result is repair state in every PGM network element between the - receiver and the source so that the corresponding RDATA is never - discarded by a PGM network element for lack of repair state. - - SPMs also maintain transmit window state in receivers by advertising - the trailing and leading edges of the transmit window (SPM_TRAIL and - SPM_LEAD). In the absence of data, SPMs MAY be used to close the - transmit window in time by advancing the transmit window until - SPM_TRAIL is equal to SPM_LEAD plus one. - -3.6. Packet Contents - - This section just provides enough short-hand to make the Procedures - intelligible. For the full details of packet contents, please refer - to Packet Formats below. - -3.6.1. Source Path Messages - -3.6.1.1. SPMs - - SPMs are transmitted by sources to establish source-path state in PGM - network elements, and to provide transmit-window state in receivers. - - SPMs are multicast to the group and contain: - - SPM_TSI the source-assigned TSI for the session to which the - SPM corresponds - - SPM_SQN a sequence number assigned sequentially by the source - in unit increments and scoped by SPM_TSI - - Nota Bene: this is an entirely separate sequence than is used to - number ODATA and RDATA. - - SPM_TRAIL the sequence number defining the trailing edge of the - source's transmit window (TXW_TRAIL) - - SPM_LEAD the sequence number defining the leading edge of the - source's transmit window (TXW_LEAD) - - SPM_PATH the network-layer address (NLA) of the interface on - the PGM network element on which the SPM is forwarded - - - - -Speakman, et. al. Experimental [Page 15] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -3.6.2. Data Packets - -3.6.2.1. ODATA - Original Data - - ODATA packets are transmitted by sources to send application data to - receivers. - - ODATA packets are multicast to the group and contain: - - OD_TSI the globally unique source-assigned TSI - - OD_TRAIL the sequence number defining the trailing edge of the - source's transmit window (TXW_TRAIL) - - OD_TRAIL makes the protocol more robust in the face of - lost SPMs. By including the trailing edge of the - transmit window on every data packet, receivers that - have missed any SPMs that advanced the transmit window - can still detect the case, recover the application, - and potentially re-synchronize to the transport - session. - - OD_SQN a sequence number assigned sequentially by the source - in unit increments and scoped by OD_TSI - -3.6.2.2. RDATA - Repair Data - - RDATA packets are repair packets transmitted by sources or DLRs in - response to NAKs. - - RDATA packets are multicast to the group and contain: - - RD_TSI OD_TSI of the ODATA packet for which this is a repair - - RD_TRAIL the sequence number defining the trailing edge of the - source's transmit window (TXW_TRAIL). This is updated - to the most current value when the repair is sent, so - it is not necessarily the same as OD_TRAIL of the - ODATA packet for which this is a repair - - RD_SQN OD_SQN of the ODATA packet for which this is a repair - -3.6.3. Negative Acknowledgments - -3.6.3.1. NAKs - Negative Acknowledgments - - NAKs are transmitted by receivers to request repairs for missing data - packets. - - - -Speakman, et. al. Experimental [Page 16] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - NAKs are unicast (PGM-hop-by-PGM-hop) to the source and contain: - - NAK_TSI OD_TSI of the ODATA packet for which a repair is - requested - - NAK_SQN OD_SQN of the ODATA packet for which a repair is - requested - - NAK_SRC the unicast NLA of the original source of the missing - ODATA. - - NAK_GRP the multicast group NLA - -3.6.3.2. NNAKs - Null Negative Acknowledgments - - NNAKs are transmitted by a DLR that receives NAKs redirected to it by - either receivers or network elements to provide flow-control feed- - back to a source. - - NNAKs are unicast (PGM-hop-by-PGM-hop) to the source and contain: - - NNAK_TSI NAK_TSI of the corresponding re-directed NAK. - - NNAK_SQN NAK_SQN of the corresponding re-directed NAK. - - NNAK_SRC NAK_SRC of the corresponding re-directed NAK. - - NNAK_GRP NAK_GRP of the corresponding re-directed NAK. - -3.6.4. Negative Acknowledgment Confirmations - -3.6.4.1. NCFs - NAK confirmations - - NCFs are transmitted by network elements and sources in response to - NAKs. - - NCFs are multicast to the group and contain: - - NCF_TSI NAK_TSI of the NAK being confirmed - - NCF_SQN NAK_SQN of the NAK being confirmed - - NCF_SRC NAK_SRC of the NAK being confirmed - - NCF_GRP NAK_GRP of the NAK being confirmed - - - - - - -Speakman, et. al. Experimental [Page 17] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -3.6.5. Option Encodings - - OPT_LENGTH 0x00 - Option's Length - - OPT_FRAGMENT 0x01 - Fragmentation - - OPT_NAK_LIST 0x02 - List of NAK entries - - OPT_JOIN 0x03 - Late Joining - - OPT_REDIRECT 0x07 - Redirect - - OPT_SYN 0x0D - Synchronization - - OPT_FIN 0x0E - Session Fin receivers, conventional - feedbackish - - OPT_RST 0x0F - Session Reset - - OPT_PARITY_PRM 0x08 - Forward Error Correction Parameters - - OPT_PARITY_GRP 0x09 - Forward Error Correction Group Number - - OPT_CURR_TGSIZE 0x0A - Forward Error Correction Group Size - - OPT_CR 0x10 - Congestion Report - - OPT_CRQST 0x11 - Congestion Report Request - - OPT_NAK_BO_IVL 0x04 - NAK Back-Off Interval - - OPT_NAK_BO_RNG 0x05 - NAK Back-Off Range - - OPT_NBR_UNREACH 0x0B - Neighbor Unreachable - - OPT_PATH_NLA 0x0C - Path NLA - - OPT_INVALID 0x7F - Option invalidated - -4. Procedures - General - - Since SPMs, NCFs, and RDATA must be treated conditionally by PGM - network elements, they must be distinguished from other packets in - the chosen multicast network protocol if PGM network elements are to - extract them from the usual switching path. - - - - - - -Speakman, et. al. Experimental [Page 18] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - The most obvious way for network elements to achieve this is to - examine every packet in the network for the PGM transport protocol - and packet types. However, the overhead of this approach is costly - for high-performance, multi-protocol network elements. An - alternative, and a requirement for PGM over IP multicast, is that - SPMs, NCFs, and RDATA MUST be transmitted with the IP Router Alert - Option [6]. This option gives network elements a network-layer - indication that a packet should be extracted from IP switching for - more detailed processing. - -5. Procedures - Sources - -5.1. Data Transmission - - Since PGM relies on a purely rate-limited transmission strategy in - the source to bound the bandwidth consumed by PGM transport sessions, - an assortment of techniques is assembled here to make that strategy - as conservative and robust as possible. These techniques are the - minimum REQUIRED of a PGM source. - -5.1.1. Maximum Cumulative Transmit Rate - - A source MUST number ODATA packets in the order in which they are - submitted for transmission by the application. A source MUST - transmit ODATA packets in sequence and only within the transmit - window beginning with TXW_TRAIL at no greater a rate than - TXW_MAX_RTE. - - TXW_MAX_RTE is typically the maximum cumulative transmit rate of SPM, - ODATA, and RDATA. Different transmission strategies MAY define - TXW_MAX_RTE as appropriate for the implementation. - -5.1.2. Transmit Rate Regulation - - To regulate its transmit rate, a source MUST use a token bucket - scheme or any other traffic management scheme that yields equivalent - behavior. A token bucket [7] is characterized by a continually - sustainable data rate (the token rate) and the extent to which the - data rate may exceed the token rate for short periods of time (the - token bucket size). Over any arbitrarily chosen interval, the number - of bytes the source may transmit MUST NOT exceed the token bucket - size plus the product of the token rate and the chosen interval. - - In addition, a source MUST bound the maximum rate at which successive - packets may be transmitted using a leaky bucket scheme drained at a - maximum transmit rate, or equivalent mechanism. - - - - - -Speakman, et. al. Experimental [Page 19] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -5.1.3. Outgoing Packet Ordering - - To preserve the logic of PGM's transmit window, a source MUST - strictly prioritize sending of pending NCFs first, pending SPMs - second, and only send ODATA or RDATA when no NCFs or SPMs are - pending. The priority of RDATA versus ODATA is application - dependent. The sender MAY implement weighted bandwidth sharing - between RDATA and ODATA. Note that strict prioritization of RDATA - over ODATA may stall progress of ODATA if there are receivers that - keep generating NAKs so as to always have RDATA pending (e.g. a - steady stream of late joiners with OPT_JOIN). Strictly prioritizing - ODATA over RDATA may lead to a larger portion of receivers getting - unrecoverable losses. - -5.1.4. Ambient SPMs - - Interleaved with ODATA and RDATA, a source MUST transmit SPMs at a - rate at least sufficient to maintain current source path state in PGM - network elements. Note that source path state in network elements - does not track underlying changes in the distribution tree from a - source until an SPM traverses the altered distribution tree. The - consequence is that NAKs may go unconfirmed both at receivers and - amongst network elements while changes in the underlying distribution - tree take place. - -5.1.5. Heartbeat SPMs - - In the absence of data to transmit, a source SHOULD transmit SPMs at - a decaying rate in order to assist early detection of lost data, to - maintain current source path state in PGM network elements, and to - maintain current receive window state in the receivers. - - In this scheme [8], a source maintains an inter-heartbeat timer - IHB_TMR which times the interval between the most recent packet - (ODATA, RDATA, or SPM) transmission and the next heartbeat - transmission. IHB_TMR is initialized to a minimum interval IHB_MIN - after the transmission of any data packet. If IHB_TMR expires, the - source transmits a heartbeat SPM and initializes IHB_TMR to double - its previous value. The transmission of consecutive heartbeat SPMs - doubles IHB each time up to a maximum interval IHB_MAX. The - transmission of any data packet initializes IHB_TMR to IHB_MIN once - again. The effect is to provoke prompt detection of missing packets - in the absence of data to transmit, and to do so with minimal - bandwidth overhead. - - - - - - - -Speakman, et. al. Experimental [Page 20] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -5.1.6. Ambient and Heartbeat SPMs - - Ambient and heartbeat SPMs are described as driven by separate timers - in this specification to highlight their contrasting functions. - Ambient SPMs are driven by a count-down timer that expires regularly - while heartbeat SPMs are driven by a count-down timer that keeps - being reset by data, and the interval of which changes once it begins - to expire. The ambient SPM timer is just counting down in real-time - while the heartbeat timer is measuring the inter-data-packet - interval. - - In the presence of data, no heartbeat SPMs will be transmitted since - the transmission of data keeps setting the IHB_TMR back to its - initial value. At the same time however, ambient SPMs MUST be - interleaved into the data as a matter of course, not necessarily as a - heartbeat mechanism. This ambient transmission of SPMs is REQUIRED - to keep the distribution tree information in the network current and - to allow new receivers to synchronize with the session. - - An implementation SHOULD de-couple ambient and heartbeat SPM timers - sufficiently to permit them to be configured independently of each - other. - -5.2. Negative Acknowledgment Confirmation - - A source MUST immediately multicast an NCF in response to any NAK it - receives. The NCF is REQUIRED since the alternative of responding - immediately with RDATA would not allow other PGM network elements on - the same subnet to do NAK anticipation, nor would it allow DLRs on - the same subnet to provide repairs. A source SHOULD be able to - detect a NAK storm and adopt countermeasure to protect the network - against a denial of service. A possible countermeasure is to send - the first NCF immediately in response to a NAK and then delay the - generation of further NCFs (for identical NAKs) by a small interval, - so that identical NCFs are rate-limited, without affecting the - ability to suppress NAKs. - -5.3. Repairs - - After multicasting an NCF in response to a NAK, a source MUST then - multicast RDATA (while respecting TXW_MAX_RTE) in response to any NAK - it receives for data packets within the transmit window. - - In the interest of increasing the efficiency of a particular RDATA - packet, a source MAY delay RDATA transmission to accommodate the - arrival of NAKs from the whole loss neighborhood. This delay SHOULD - not exceed twice the greatest propagation delay in the loss - neighborhood. - - - -Speakman, et. al. Experimental [Page 21] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -6. Procedures - Receivers - -6.1. Data Reception - - Initial data reception - - A receiver SHOULD initiate data reception beginning with the first - data packet it receives within the advertised transmit window. This - packet's sequence number (ODATA_SQN) temporarily defines the trailing - edge of the transmit window from the receiver's perspective. That - is, it is assigned to RXW_TRAIL_INIT within the receiver, and until - the trailing edge sequence number advertised in subsequent packets - (SPMs or ODATA or RDATA) increments past RXW_TRAIL_INIT, the receiver - MUST only request repairs for sequence numbers subsequent to - RXW_TRAIL_INIT. Thereafter, it MAY request repairs anywhere in the - transmit window. This temporary restriction on repair requests - prevents receivers from requesting a potentially large amount of - history when they first begin to receive a given PGM transport - session. - - Note that the JOIN option, discussed later, MAY be used to provide a - different value for RXW_TRAIL_INIT. - - Receiving and discarding data packets - - Within a given transport session, a receiver MUST accept any ODATA or - RDATA packets within the receive window. A receiver MUST discard any - data packet that duplicates one already received in the transmit - window. A receiver MUST discard any data packet outside of the - receive window. - - Contiguous data - - Contiguous data is comprised of those data packets within the receive - window that have been received and are in the range from RXW_TRAIL up - to (but not including) the first missing sequence number in the - receive window. The most recently received data packet of contiguous - data defines the leading edge of contiguous data. - - As its default mode of operation, a receiver MUST deliver only - contiguous data packets to the application, and it MUST do so in the - order defined by those data packets' sequence numbers. This provides - applications with a reliable ordered data flow. - - - - - - - - -Speakman, et. al. Experimental [Page 22] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Non contiguous data - - PGM receiver implementations MAY optionally provide a mode of - operation in which data is delivered to an application in the order - received. However, the implementation MUST only deliver complete - application protocol data units (APDUs) to the application. That is, - APDUs that have been fragmented into different TPDUs MUST be - reassembled before delivery to the application. - -6.2. Source Path Messages - - Receivers MUST receive and sequence SPMs for any TSI they are - receiving. An SPM is in sequence if its sequence number is greater - than that of the most recent in-sequence SPM and within half the PGM - number space. Out-of-sequence SPMs MUST be discarded. - - For each TSI, receivers MUST use the most recent SPM to determine the - NLA of the upstream PGM network element for use in NAK addressing. A - receiver MUST NOT initiate repair requests until it has received at - least one SPM for the corresponding TSI. - - Since SPMs require per-hop processing, it is likely that they will be - forwarded at a slower rate than data, and that they will arrive out - of sync with the data stream. In this case, the window information - that the SPMs carry will be out of date. Receivers SHOULD expect - this to be the case and SHOULD detect it by comparing the packet lead - and trail values with the values the receivers have stored for lead - and trail. If the SPM packet values are less, they SHOULD be - ignored, but the rest of the packet SHOULD be processed as normal. - -6.3. Data Recovery by Negative Acknowledgment - - Detecting missing data packets - - Receivers MUST detect gaps in the expected data sequence in the - following manners: - - by comparing the sequence number on the most recently received - ODATA or RDATA packet with the leading edge of contiguous data - - by comparing SPM_LEAD of the most recently received SPM with the - leading edge of contiguous data - - In both cases, if the receiver has not received all intervening data - packets, it MAY initiate selective NAK generation for each missing - sequence number. - - - - - -Speakman, et. al. Experimental [Page 23] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - In addition, a receiver may detect a single missing data packet by - receiving an NCF or multicast NAK for a data packet within the - transmit window which it has not received. In this case it MAY - initiate selective NAK generation for the said sequence number. - - In all cases, receivers SHOULD temper the initiation of NAK - generation to account for simple mis-ordering introduced by the - network. A possible mechanism to achieve this is to assume loss only - after the reception of N packets with sequence numbers higher than - those of the (assumed) lost packets. A possible value for N is 2. - This method SHOULD be complemented with a timeout based mechanism - that handles the loss of the last packet before a pause in the - transmission of the data stream. The leading edge field in SPMs - SHOULD also be taken into account in the loss detection algorithm. - - Generating NAKs - - NAK generation follows the detection of a missing data packet and is - the cycle of: - - waiting for a random period of time (NAK_RB_IVL) while listening - for matching NCFs or NAKs - - transmitting a NAK if a matching NCF or NAK is not heard - - waiting a period (NAK_RPT_IVL) for a matching NCF and recommencing - NAK generation if the matching NCF is not received - - waiting a period (NAK_RDATA_IVL) for data and recommencing NAK - generation if the matching data is not received - - The entire generation process can be summarized by the following - state machine: - - - - - - - - - - - - - - - - - - -Speakman, et. al. Experimental [Page 24] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - | - | detect missing tpdu - | - clear data retry count - | - clear NCF retry count - V - matching NCF |--------------------------| - <---------------| BACK-OFF_STATE | <---------------------- - | | start timer(NAK_RB_IVL) | ^ ^ - | | | | | - | |--------------------------| | | - | matching | | timer expires | | - | NAK | | - send NAK | | - | | | | | - | V V | | - | |--------------------------| | | - | | WAIT_NCF_STATE | | | - | matching NCF | start timer(NAK_RPT_IVL) | | | - |<--------------| |------------> | - | |--------------------------| timer expires | - | | | ^ - increment NCF | - | NAK_NCF_RETRIES | | | retry count | - | exceeded | | | | - | V ----------- | - | Cancelation matching NAK | - | - restart timer(NAK_RPT_IVL) | - | | - | | - V |--------------------------| | - --------------->| WAIT_DATA_STATE |-----------------------> - |start timer(NAK_RDATA_IVL)| timer expires - | | - increment data - |--------------------------| retry count - | | ^ - NAK_DATA_RETRIES | | | - exceeded | | | - | ----------- - | matching NCF or NAK - V - restart timer(NAK_RDATA_IVL) - Cancellation - - In any state, receipt of matching RDATA or ODATA completes data - recovery and successful exit from the state machine. State - transition stops any running timers. - - In any state, if the trailing edge of the window moves beyond the - sequence number, data recovery for that sequence number terminates. - - - - - -Speakman, et. al. Experimental [Page 25] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - During NAK_RB_IVL a NAK is said to be pending. When awaiting data or - an NCF, a NAK is said to be outstanding. - - Backing off NAK transmission - - Before transmitting a NAK, a receiver MUST wait some interval - NAK_RB_IVL chosen randomly over some time period NAK_BO_IVL. During - this period, receipt of a matching NAK or a matching NCF will suspend - NAK generation. NAK_RB_IVL is counted down from the time a missing - data packet is detected. - - A value for NAK_BO_IVL learned from OPT_NAK_BO_IVL (see 16.4.1 below) - MUST NOT be used by a receiver (i.e., the receiver MUST NOT NAK) - unless either NAK_BO_IVL_SQN is zero, or the receiver has seen - POLL_RND == 0 for POLL_SQN =< NAK_BO_IVL_SQN within half the sequence - number space. - - When a parity NAK (Appendix A, FEC) is being generated, the back-off - interval SHOULD be inversely biased with respect to the number of - parity packets requested. This way NAKs requesting larger numbers of - parity packets are likely to be sent first and thus suppress other - NAKs. A NAK for a given transmission group suppresses another NAK - for the same transmission group only if it is requesting an equal or - larger number of parity packets. - - When a receiver has to transmit a sequence of NAKs, it SHOULD - transmit the NAKs in order from oldest to most recent. - - Suspending NAK generation - - Suspending NAK generation just means waiting for either NAK_RB_IVL, - NAK_RPT_IVL or NAK_RDATA_IVL to pass. A receiver MUST suspend NAK - generation if a duplicate of the NAK is already pending from this - receiver or the NAK is already outstanding from this or another - receiver. - - NAK suppression - - A receiver MUST suppress NAK generation and wait at least - NAK_RDATA_IVL before recommencing NAK generation if it hears a - matching NCF or NAK during NAK_RB_IVL. A matching NCF must match - NCF_TSI with NAK_TSI, and NCF_SQN with NAK_SQN. - - Transmitting a NAK - - Upon expiry of NAK_RB_IVL, a receiver MUST unicast a NAK to the - upstream PGM network element for the TSI specifying the transport - session identifier and missing sequence number. In addition, it MAY - - - -Speakman, et. al. Experimental [Page 26] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - multicast a NAK with TTL of 1 to the group, if the PGM parent is not - directly connected. It also records both the address of the source - of the corresponding ODATA and the address of the group in the NAK - header. - - It MUST repeat the NAK at a rate governed by NAK_RPT_IVL up to - NAK_NCF_RETRIES times while waiting for a matching NCF. It MUST then - wait NAK_RDATA_IVL before recommencing NAK generation. If it hears a - matching NCF or NAK during NAK_RDATA_IVL, it MUST wait anew for - NAK_RDATA_IVL before recommencing NAK generation (i.e. matching NCFs - and NAKs restart NAK_RDATA_IVL). - - Completion of NAK generation - - NAK generation is complete only upon the receipt of the matching - RDATA (or even ODATA) packet at any time during NAK generation. - - Cancellation of NAK generation - - NAK generation is cancelled upon the advancing of the receive window - so as to exclude the matching sequence number of a pending or - outstanding NAK, or NAK_DATA_RETRIES / NAK_NCF_RETRIES being - exceeded. Cancellation of NAK generation indicates unrecoverable - data loss. - - Receiving NCFs and multicast NAKs - - A receiver MUST discard any NCFs or NAKs it hears for data packets - outside the transmit window or for data packets it has received. - Otherwise they are treated as appropriate for the current repair - state. - -7. Procedures - Network Elements - -7.1. Source Path State - - Upon receipt of an in-sequence SPM, a network element records the - Source Path Address SPM_PATH with the multicast routing information - for the TSI. If the receiving network element is on the same subnet - as the forwarding network element, this address will be the same as - the address of the immediately upstream network element on the - distribution tree for the TSI. If, however, non-PGM network elements - intervene between the forwarding and the receiving network elements, - this address will be the address of the first PGM network element - across the intervening network elements. - - - - - - -Speakman, et. al. Experimental [Page 27] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - The network element then forwards the SPM on each outgoing interface - for that TSI. As it does so, it encodes the network address of the - outgoing interface in SPM_PATH in each copy of the SPM it forwards. - -7.2. NAK Confirmation - - Network elements MUST immediately transmit an NCF in response to any - unicast NAK they receive. The NCF MUST be multicast to the group on - the interface on which the NAK was received. - - Nota Bene: In order to avoid creating multicast routing state for - PGM network elements across non-PGM-capable clouds, the network- - header source address of NCFs transmitted by network elements MUST - be set to the ODATA source's NLA, not the network element's NLA as - might be expected. - - Network elements should be able to detect a NAK storm and adopt - counter-measure to protect the network against a denial of service. - A possible countermeasure is to send the first NCF immediately in - response to a NAK and then delay the generation of further NCFs (for - identical NAKs) by a small interval, so that identical NCFs are - rate-limited, without affecting the ability to suppress NAKs. - - Simultaneously, network elements MUST establish repair state for the - NAK if such state does not already exist, and add the interface on - which the NAK was received to the corresponding repair interface list - if the interface is not already listed. - -7.3. Constrained NAK Forwarding - - The NAK forwarding procedures for network elements are quite similar - to those for receivers, but three important differences should be - noted. - - First, network elements do NOT back off before forwarding a NAK - (i.e., there is no NAK_BO_IVL) since the resulting delay of the NAK - would compound with each hop. Note that NAK arrivals will be - randomized by the receivers from which they originate, and this - factor in conjunction with NAK anticipation and elimination will - combine to forestall NAK storms on subnets with a dense network - element population. - - Second, network elements do NOT retry confirmed NAKs if RDATA is not - seen; they simply discard the repair state and rely on receivers to - re-request the repair. This approach keeps the repair state in the - network elements relatively ephemeral and responsive to underlying - routing changes. - - - - -Speakman, et. al. Experimental [Page 28] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Third, note that ODATA does NOT cancel NAK forwarding in network - elements since it is switched by network elements without transport- - layer intervention. - - Nota Bene: Once confirmed by an NCF, network elements discard NAK - packets; they are NOT retained in network elements beyond this - forwarding operation. - - NAK forwarding requires that a network element listen to NCFs for the - same transport session. NAK forwarding also requires that a network - element observe two time out intervals for any given NAK (i.e., per - NAK_TSI and NAK_SQN): NAK_RPT_IVL and NAK_RDATA_IVL. - - The NAK repeat interval NAK_RPT_IVL, limits the length of time for - which a network element will repeat a NAK while waiting for a - corresponding NCF. NAK_RPT_IVL is counted down from the transmission - of a NAK. Expiry of NAK_RPT_IVL cancels NAK forwarding (due to - missing NCF). - - The NAK RDATA interval NAK_RDATA_IVL, limits the length of time for - which a network element will wait for the corresponding RDATA. - NAK_RDATA_IVL is counted down from the time a matching NCF is - received. Expiry of NAK_RDATA_IVL causes the network element to - discard the corresponding repair state (due to missing RDATA). - - During NAK_RPT_IVL, a NAK is said to be pending. During - NAK_RDATA_IVL, a NAK is said to be outstanding. - - A Network element MUST forward NAKs only to the upstream PGM network - element for the TSI. - - A network element MUST repeat a NAK at a rate of NAK_RPT_RTE for an - interval of NAK_RPT_IVL until it receives a matching NCF. A matching - NCF must match NCF_TSI with NAK_TSI, and NCF_SQN with NAK_SQN. - - Upon reception of the corresponding NCF, network elements MUST wait - at least NAK_RDATA_IVL for the corresponding RDATA. Receipt of the - corresponding RDATA at any time during NAK forwarding cancels NAK - forwarding and tears down the corresponding repair state in the - network element. - -7.4. NAK elimination - - Two NAKs duplicate each other if they bear the same NAK_TSI and - NAK_SQN. Network elements MUST discard all duplicates of a NAK that - is pending. - - - - - -Speakman, et. al. Experimental [Page 29] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Once a NAK is outstanding, network elements MUST discard all - duplicates of that NAK for NAK_ELIM_IVL. Upon expiry of - NAK_ELIM_IVL, network elements MUST suspend NAK elimination for that - TSI/SQN until the first duplicate of that NAK is seen after the - expiry of NAK_ELIM_IVL. This duplicate MUST be forwarded in the - usual manner. Once this duplicate NAK is outstanding, network - elements MUST once again discard all duplicates of that NAK for - NAK_ELIM_IVL, and so on. NAK_RDATA_IVL MUST be reset each time a NAK - for the corresponding TSI/SQN is confirmed (i.e., each time - NAK_ELIM_IVL is reset). NAK_ELIM_IVL MUST be some small fraction of - NAK_RDATA_IVL. - - NAK_ELIM_IVL acts to balance implosion prevention against repair - state liveness. That is, it results in the elimination of all but at - most one NAK per NAK_ELIM_IVL thereby allowing repeated NAKs to keep - the repair state alive in the PGM network elements. - -7.5. NAK Anticipation - - An unsolicited NCF is one that is received by a network element when - the network element has no corresponding pending or outstanding NAK. - Network elements MUST process unsolicited NCFs differently depending - on the interface on which they are received. - - If the interface on which an NCF is received is the same interface - the network element would use to reach the upstream PGM network - element, the network element simply establishes repair state for - NCF_TSI and NCF_SQN without adding the interface to the repair - interface list, and discards the NCF. If the repair state already - exists, the network element restarts the NAK_RDATA_IVL and - NAK_ELIM_IVL timers and discards the NCF. - - If the interface on which an NCF is received is not the same - interface the network element would use to reach the upstream PGM - network element, the network element does not establish repair state - and just discards the NCF. - - Anticipated NAKs permit the elimination of any subsequent matching - NAKs from downstream. Upon establishing anticipated repair state, - network elements MUST eliminate subsequent NAKs only for a period of - NAK_ELIM_IVL. Upon expiry of NAK_ELIM_IVL, network elements MUST - suspend NAK elimination for that TSI/SQN until the first duplicate of - that NAK is seen after the expiry of NAK_ELIM_IVL. This duplicate - MUST be forwarded in the usual manner. Once this duplicate NAK is - outstanding, network elements MUST once again discard all duplicates - of that NAK for NAK_ELIM_IVL, and so on. NAK_RDATA_IVL MUST be reset - - - - - -Speakman, et. al. Experimental [Page 30] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - each time a NAK for the corresponding TSI/SQN is confirmed (i.e., - each time NAK_ELIM_IVL is reset). NAK_ELIM_IVL must be some small - fraction of NAK_RDATA_IVL. - -7.6. NAK Shedding - - Network elements MAY implement local procedures for withholding NAK - confirmations for receivers detected to be reporting excessive loss. - The result of these procedures would ultimately be unrecoverable data - loss in the receiver. - -7.7. Addressing NAKs - - A PGM network element uses the source and group addresses (NLAs) - contained in the transport header to find the state for the - corresponding TSI, looks up the corresponding upstream PGM network - element's address, uses it to re-address the (unicast) NAK, and - unicasts it on the upstream interface for the distribution tree for - the TSI. - -7.8. Constrained RDATA Forwarding - - Network elements MUST maintain repair state for each interface on - which a given NAK is received at least once. Network elements MUST - then use this list of interfaces to constrain the forwarding of the - corresponding RDATA packet only to those interfaces in the list. An - RDATA packet corresponds to a NAK if it matches NAK_TSI and NAK_SQN. - - Network elements MUST maintain this repair state only until either - the corresponding RDATA is received and forwarded, or NAK_RDATA_IVL - passes after forwarding the most recent instance of a given NAK. - Thereafter, the corresponding repair state MUST be discarded. - - Network elements SHOULD discard and not forward RDATA packets for - which they have no repair state. Note that the consequence of this - procedure is that, while it constrains repairs to the interested - subset of the network, loss of repair state precipitates further NAKs - from neglected receivers. - -8. Packet Formats - - All of the packet formats described in this section are transport- - layer headers that MUST immediately follow the network-layer header - in the packet. Only data packet headers (ODATA and RDATA) may be - followed in the packet by application data. For each packet type, - the network-header source and destination addresses are specified in - - - - - -Speakman, et. al. Experimental [Page 31] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - addition to the format and contents of the transport layer header. - Recall from General Procedures that, for PGM over IP multicast, SPMs, - NCFs, and RDATA MUST also bear the IP Router Alert Option. - - For PGM over IP, the IP protocol number is 113. - - In all packets the descriptions of Data-Source Port, Data-Destination - Port, Type, Options, Checksum, Global Source ID (GSI), and Transport - Service Data Unit (TSDU) Length are: - - Data-Source Port: - - A random port number generated by the source. This port number - MUST be unique within the source. Source Port together with - Global Source ID forms the TSI. - - Data-Destination Port: - - A globally well-known port number assigned to the given PGM - application. - - Type: - - The high-order two bits of the Type field encode a version - number, 0x0 in this instance. The low-order nibble of the type - field encodes the specific packet type. The intervening two - bits (the low-order two bits of the high-order nibble) are - reserved and MUST be zero. - - Within the low-order nibble of the Type field: - - values in the range 0x0 through 0x3 represent SPM-like - packets (i.e., session-specific, sourced by a source, - periodic), - - values in the range 0x4 through 0x7 represent DATA-like - packets (i.e., data and repairs), - - values in the range 0x8 through 0xB represent NAK-like - packets (i.e., hop-by-hop reliable NAK forwarding - procedures), - - and values in the range 0xC through 0xF represent SPMR-like - packets (i.e., session-specific, sourced by a receiver, - asynchronous). - - - - - - -Speakman, et. al. Experimental [Page 32] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Options: - - This field encodes binary indications of the presence and - significance of any options. It also directly encodes some - options. - - bit 0 set => One or more Option Extensions are present - - bit 1 set => One or more Options are network-significant - - Note that this bit is clear when OPT_FRAGMENT and/or - OPT_JOIN are the only options present. - - bit 6 set => Packet is a parity packet for a transmission group - of variable sized packets (OPT_VAR_PKTLEN). Only present when - OPT_PARITY is also present. - - bit 7 set => Packet is a parity packet (OPT_PARITY) - - Bits are numbered here from left (0 = MSB) to right (7 = LSB). - - All the other options (option extensions) are encoded in - extensions to the PGM header. - - Checksum: - - This field is the usual 1's complement of the 1's complement - sum of the entire PGM packet including header. - - The checksum does not include a network-layer pseudo header for - compatibility with network address translation. If the - computed checksum is zero, it is transmitted as all ones. A - value of zero in this field means the transmitter generated no - checksum. - - Note that if any entity between a source and a receiver - modifies the PGM header for any reason, it MUST either - recompute the checksum or clear it. The checksum is mandatory - on data packets (ODATA and RDATA). - - Global Source ID: - - A globally unique source identifier. This ID MUST NOT change - throughout the duration of the transport session. A - RECOMMENDED identifier is the low-order 48 bits of the MD5 [9] - signature of the DNS name of the source. Global Source ID - together with Data-Source Port forms the TSI. - - - - -Speakman, et. al. Experimental [Page 33] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - TSDU Length: - - The length in octets of the transport data unit exclusive of - the transport header. - - Note that those who require the TPDU length must obtain it from - sum of the transport header length (TH) and the TSDU length. - TH length is the sum of the size of the particular PGM packet - header (type_specific_size) plus the length of any options that - might be present. - - Address Family Indicators (AFIs) are as specified in [10]. - -8.1. Source Path Messages - - SPMs are sent by a source to establish source path state in network - elements and to provide transmit window state to receivers. - - The network-header source address of an SPM is the unicast NLA of the - entity that originates the SPM. - - The network-header destination address of an SPM is a multicast group - NLA. - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Source Port | Destination Port | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Options | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Global Source ID ... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | ... Global Source ID | TSDU Length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | SPM's Sequence Number | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Trailing Edge Sequence Number | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Leading Edge Sequence Number | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | NLA AFI | Reserved | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Path NLA ... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ - | Option Extensions when present ... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - - - -Speakman, et. al. Experimental [Page 34] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Source Port: - - SPM_SPORT - - Data-Source Port, together with SPM_GSI forms SPM_TSI - - Destination Port: - - SPM_DPORT - - Data-Destination Port - - Type: - - SPM_TYPE = 0x00 - - Global Source ID: - - SPM_GSI - - Together with SPM_SPORT forms SPM_TSI - - SPM's Sequence Number - - SPM_SQN - - The sequence number assigned to the SPM by the source. - - Trailing Edge Sequence Number: - - SPM_TRAIL - - The sequence number defining the current trailing edge of the - source's transmit window (TXW_TRAIL). - - Leading Edge Sequence Number: - - SPM_LEAD - - The sequence number defining the current leading edge of the - source's transmit window (TXW_LEAD). - - If SPM_TRAIL == 0 and SPM_LEAD == 0x80000000, this indicates that - no window information is present in the packet. - - - - - - - -Speakman, et. al. Experimental [Page 35] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Path NLA: - - SPM_PATH - - The NLA of the interface on the network element on which this SPM - was forwarded. Initialized by a source to the source's NLA, - rewritten by each PGM network element upon forwarding. - -8.2. Data Packets - - Data packets carry application data from a source or a repairer to - receivers. - - ODATA: - - Original data packets transmitted by a source. - - RDATA: - - Repairs transmitted by a source or by a designated local - repairer (DLR) in response to a NAK. - - The network-header source address of a data packet is the unicast NLA - of the entity that originates the data packet. - - The network-header destination address of a data packet is a - multicast group NLA. - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Source Port | Destination Port | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Options | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Global Source ID ... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | ... Global Source ID | TSDU Length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Data Packet Sequence Number | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Trailing Edge Sequence Number | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Option Extensions when present ... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Data ... - +-+-+- ... - - - - -Speakman, et. al. Experimental [Page 36] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Source Port: - - OD_SPORT, RD_SPORT - - Data-Source Port, together with Global Source ID forms: - - OD_TSI, RD_TSI - - Destination Port: - - OD_DPORT, RD_DPORT - - Data-Destination Port - - Type: - - OD_TYPE = 0x04 RD_TYPE = 0x05 - - Global Source ID: - - OD_GSI, RD_GSI - - Together with Source Port forms: - - OD_TSI, RD_TSI - - Data Packet Sequence Number: - - OD_SQN, RD_SQN - - The sequence number originally assigned to the ODATA packet by the - source. - - Trailing Edge Sequence Number: - - OD_TRAIL, RD_TRAIL - - The sequence number defining the current trailing edge of the - source's transmit window (TXW_TRAIL). In RDATA, this MAY not be - the same as OD_TRAIL of the ODATA packet for which it is a repair. - - Data: - - Application data. - - - - - - - -Speakman, et. al. Experimental [Page 37] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -8.3. Negative Acknowledgments and Confirmations - - NAK: - - Negative Acknowledgments are sent by receivers to request the - repair of an ODATA packet detected to be missing from the - expected sequence. - - N-NAK: - - Null Negative Acknowledgments are sent by DLRs to provide flow - control feedback to the source of ODATA for which the DLR has - provided the corresponding RDATA. - - The network-header source address of a NAK is the unicast NLA of the - entity that originates the NAK. The network-header source address of - NAK is rewritten by each PGM network element with its own. - - The network-header destination address of a NAK is initialized by the - originator of the NAK (a receiver) to the unicast NLA of the upstream - PGM network element known from SPMs. The network-header destination - address of a NAK is rewritten by each PGM network element with the - unicast NLA of the upstream PGM network element to which this NAK is - forwarded. On the final hop, the network-header destination address - of a NAK is rewritten by the PGM network element with the unicast NLA - of the original source or the unicast NLA of a DLR. - - NCF: - - NAK Confirmations are sent by network elements and sources to - confirm the receipt of a NAK. - - The network-header source address of an NCF is the ODATA source's - NLA, not the network element's NLA as might be expected. - - The network-header destination address of an NCF is a multicast group - NLA. - - Note that in NAKs and N-NAKs, unlike the other packets, the field - SPORT contains the Data-Destination port and the field DPORT contains - the Data-Source port. As a general rule, the content of SPORT/DPORT - is determined by the direction of the flow: in packets which travel - down-stream SPORT is the port number chosen in the data source - (Data-Source Port) and DPORT is the data destination port number - (Data-Destination Port). The opposite holds for packets which travel - upstream. This makes DPORT the protocol endpoint in the recipient - host, regardless of the direction of the packet. - - - - -Speakman, et. al. Experimental [Page 38] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Source Port | Destination Port | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Options | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Global Source ID ... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | ... Global Source ID | TSDU Length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Requested Sequence Number | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | NLA AFI | Reserved | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Source NLA ... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ - | NLA AFI | Reserved | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Multicast Group NLA ... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ - | Option Extensions when present ... - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... - - Source Port: - - NAK_SPORT, NNAK_SPORT - - Data-Destination Port - - NCF_SPORT - - Data-Source Port, together with Global Source ID forms NCF_TSI - - Destination Port: - - NAK_DPORT, NNAK_DPORT - - Data-Source Port, together with Global Source ID forms: - - NAK_TSI, NNAK_TSI - - NCF_DPORT - - Data-Destination Port - - - - - - -Speakman, et. al. Experimental [Page 39] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Type: - - NAK_TYPE = 0x08 NNAK_TYPE = 0x09 - - NCF_TYPE = 0x0A - - Global Source ID: - - NAK_GSI, NNAK_GSI, NCF_GSI - - Together with Data-Source Port forms - - NAK_TSI, NNAK_TSI, NCF_TSI - - Requested Sequence Number: - - NAK_SQN, NNAK_SQN - - NAK_SQN is the sequence number of the ODATA packet for which a - repair is requested. - - NNAK_SQN is the sequence number of the RDATA packet for which a - repair has been provided by a DLR. - - NCF_SQN - - NCF_SQN is NAK_SQN from the NAK being confirmed. - - Source NLA: - - NAK_SRC, NNAK_SRC, NCF_SRC - - The unicast NLA of the original source of the missing ODATA. - - Multicast Group NLA: - - NAK_GRP, NNAK_GRP, NCF_GRP - - The multicast group NLA. NCFs MAY bear OPT_REDIRECT and/or - OPT_NAK_LIST - -9. Options - - PGM specifies several end-to-end options to address specific - application requirements. PGM specifies options to support - fragmentation, late joining, and redirection. - - - - - -Speakman, et. al. Experimental [Page 40] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Options MAY be appended to PGM data packet headers only by their - original transmitters. While they MAY be interpreted by network - elements, options are neither added nor removed by network elements. - - Options are all in the TLV style, or Type, Length, Value. The Type - field is contained in the first byte, where bit 0 is the OPT_END bit, - followed by 7 bits of type. The OPT_END bit MUST be set in the last - option in the option list, whichever that might be. The Length field - is the total length of the option in bytes, and directly follows the - Type field. Following the Length field are 5 reserved bits, the - OP_ENCODED flag, the 2 Option Extensibility bits OPX and the - OP_ENCODED_NULL flag. Last are 7 bits designated for option specific - information which may be defined on a per-option basis. If not - defined for a particular option, they MUST be set to 0. - - The Option Extensibility bits dictate the desired treatment of an - option if it is unknown to the network element processing it. - - Nota Bene: Only network elements pay any attention to these bits. - - The OPX bits are defined as follows: - - 00 - Ignore the option - - 01 - Invalidate the option by changing the type to OPT_INVALID - = 0x7F - - 10 - Discard the packet - - 11 - Unsupported, and reserved for future use - - Some options present in data packet (ODATA and RDATA) are strictly - associated with the packet content (PGM payload), OPT_FRAGMENT being - an example. These options must be preserved even when the data - packet that would normally contain them is not received, but its the - payload is recovered though the use of FEC. PGM specifies a - mechanism to accomplish this that uses the F (OP_ENCODED) and U - (OP_ENCODED_NULL) bits in the option common header. OP_ENCODED and - OP_ENCODED_NULL MUST be normally set to zero except when the option - is used in FEC packets to preserve original options. See Appendix A - for details. - - There is a limit of 16 options per packet. - - - - - - - - -Speakman, et. al. Experimental [Page 41] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - General Option Format - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |E| Option Type | Option Length |Reserved |F|OPX|U|Opt. Specific| - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Option Value ... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+...+-+-+ - -9.1. Option extension length - OPT_LENGTH - - When option extensions are appended to the standard PGM header, the - extensions MUST be preceded by an option extension length field - specifying the total length of all option extensions. - - In addition, the presence of the options MUST be encoded in the - Options field of the standard PGM header before the Checksum is - computed. - - All network-significant options MUST be appended before any - exclusively receiver-significant options. - - To provide an indication of the end of option extensions, OPT_END - (0x80) MUST be set in the Option Type field of the trailing option - extension. - -9.1.1. OPT_LENGTH - Packet Extension Format - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Option Type | Option Length | Total length of all options | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - Option Type = 0x00 - - Option Length = 4 octets - - Total length of all options - - The total length in octets of all option extensions including - OPT_LENGTH. - - OPT_LENGTH is NOT network-significant. - - - - - - -Speakman, et. al. Experimental [Page 42] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -9.2. Fragmentation Option - OPT_FRAGMENT - - Fragmentation allows transport-layer entities at a source to break up - application protocol data units (APDUs) into multiple PGM data - packets (TPDUs) to conform with the MTU supported by the network - layer. The fragmentation option MAY be applied to ODATA and RDATA - packets only. - - Architecturally, the accumulation of TSDUs into APDUs is applied to - TPDUs that have already been received, duplicate eliminated, and - contiguously sequenced by the receiver. Thus APDUs MAY be - reassembled across increments of the transmit window. - -9.2.1. OPT_FRAGMENT - Packet Extension Contents - - OPT_FRAG_OFF the offset of the fragment from the beginning of the - APDU - - OPT_FRAG_LEN the total length of the original APDU - -9.2.2. OPT_FRAGMENT - Procedures - Sources - - A source fragments APDUs into a contiguous series of fragments no - larger than the MTU supported by the network layer. A source - sequentially and uniquely assigns OD_SQNs to these fragments in the - order in which they occur in the APDU. A source then sets - OPT_FRAG_OFF to the value of the offset of the fragment in the - original APDU (where the first byte of the APDU is at offset 0, and - OPT_FRAG_OFF numbers the first byte in the fragment), and set - OPT_FRAG_LEN to the value of the total length of the original APDU. - -9.2.3. OPT_FRAGMENT - Procedures - Receivers - - Receivers detect and accumulate fragmented packets until they have - received an entire contiguous sequence of packets comprising an APDU. - This sequence begins with the fragment bearing OPT_FRAG_OFF of 0, and - terminates with the fragment whose length added to its OPT_FRAG_OFF - is OPT_FRAG_LEN. - - - - - - - - - - - - - -Speakman, et. al. Experimental [Page 43] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -9.2.4. OPT_FRAGMENT - Packet Extension Format - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |E| Option Type | Option Length |Reserved |F|OPX|U| | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | First Sequence Number | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Offset | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - Option Type = 0x01 - - Option Length = 12 octets - - First Sequence Number - - Sequence Number of the PGM DATA/RDATA packet containing the first - fragment of the APDU. - - Offset - - The byte offset of the fragment from the beginning of the APDU - (OPT_FRAG_OFF). - - Length - - The total length of the original APDU (OPT_FRAG_LEN). - - OPT_FRAGMENT is NOT network-significant. - -9.3. NAK List Option - OPT_NAK_LIST - - The NAK List option MAY be used in conjunction with NAKs to allow - receivers to request transmission for more than one sequence number - with a single NAK packet. The option is limited to 62 listed NAK - entries. The NAK list MUST be unique and duplicate free. It MUST be - ordered, and MUST consist of either a list of selective or a list of - parity NAKs. In general, network elements, sources and receivers - must process a NAK list as if they had received individual NAKs for - each sequence number in the list. The procedures for each are - outlined in detail earlier in this document. Clarifications and - differences are detailed here. - - - - - -Speakman, et. al. Experimental [Page 44] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -9.3.1. OPT_NAK_LIST - Packet Extensions Contents - - A list of sequence numbers for which retransmission is requested. - -9.3.2. OPT_NAK_LIST - Procedures - Receivers - - Receivers MAY append the NAK List option to a NAK to indicate that - they wish retransmission of a number of RDATA. - - Receivers SHOULD proceed to back off NAK transmission in a manner - consistent with the procedures outlined for single sequence number - NAKs. Note that the repair of each separate sequence number will be - completed upon receipt of a separate RDATA packet. - - Reception of an NCF or multicast NAK containing the NAK List option - suspends generation of NAKs for all sequence numbers within the NAK - list, as well as the sequence number within the NAK header. - -9.3.3. OPT_NAK_LIST - Procedures - Network Elements - - Network elements MUST immediately respond to a NAK with an identical - NCF containing the same NAK list as the NAK itself. - - Network elements MUST forward a NAK containing a NAK List option if - any one sequence number specified by the NAK (including that in the - main NAK header) is not currently outstanding. That is, it MUST - forward the NAK, if any one sequence number does not have an - elimination timer running for it. The NAK must be forwarded intact. - - Network elements MUST eliminate a NAK containing the NAK list option - only if all sequence numbers specified by the NAK (including that in - the main NAK header) are outstanding. That is, they are all running - an elimination timer. - - Upon receipt of an unsolicited NCF containing the NAK list option, a - network element MUST anticipate data for every sequence number - specified by the NAK as if it had received an NCF for every sequence - number specified by the NAK. - -9.3.4. OPT_NAK_LIST - Procedures - Sources - - A source MUST immediately respond to a NAK with an identical NCF - containing the same NAK list as the NAK itself. - - It MUST then multicast RDATA (while respecting TXW_MAX_RTE) for every - requested sequence number. - - - - - -Speakman, et. al. Experimental [Page 45] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -9.3.5. OPT_NAK_LIST - Packet Extension Format - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |E| Option Type | Option Length |Reserved |F|OPX|U| | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Requested Sequence Number 1 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | ..... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Requested Sequence Number N | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - Option Type = 0x02 - - Option Length = 4 + (4 * number of SQNs) octets - - Requested Sequence Number - - A list of up to 62 additional sequence numbers to which the NAK - applies. - - OPT_NAK_LIST is network-significant. - -9.4. Late Joining Option - OPT_JOIN - - Late joining allows a source to bound the amount of repair history - receivers may request when they initially join a particular transport - session. - - This option indicates that receivers that join a transport session in - progress MAY request repair of all data as far back as the given - minimum sequence number from the time they join the transport - session. The default is for receivers to receive data only from the - first packet they receive and onward. - -9.4.1. OPT_JOIN - Packet Extensions Contents - - OPT_JOIN_MIN the minimum sequence number for repair - -9.4.2. OPT_JOIN - Procedures - Receivers - - If a PGM packet (ODATA, RDATA, or SPM) bears OPT_JOIN, a receiver MAY - initialize the trailing edge of the receive window (RXW_TRAIL_INIT) - to the given Minimum Sequence Number and proceeds with normal data - reception. - - - - -Speakman, et. al. Experimental [Page 46] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -9.4.3. OPT_JOIN - Packet Extension Format - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |E| Option Type | Option Length |Reserved |F|OPX|U| | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Minimum Sequence Number | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - - Option Type = 0x03 - - Option Length = 8 octets - - Minimum Sequence Number - - The minimum sequence number defining the initial trailing edge of - the receive window for a late joining receiver. - - OPT_JOIN is NOT network-significant. - -9.5. Redirect Option - OPT_REDIRECT - - Redirection MAY be used by a designated local repairer (DLR) to - advertise its own address as an alternative to the original source, - for requesting repairs. - - These procedures allow a PGM Network Element to use a DLR that is one - PGM hop from it either upstream or downstream in the multicast - distribution tree. The former are referred to as upstream DLRs. The - latter are referred to as off-tree DLRs. Off-Tree because even - though they are downstream of the point of loss, they might not lie - on the subtree affected by the loss. - - A DLR MUST receive any PGM sessions for which it wishes to provide - retransmissions. A DLR SHOULD respond to NCFs or POLLs sourced by - its PGM parent with a redirecting POLR response packet containing an - OPT_REDIRECT which provides its own network layer address. - Recipients of redirecting POLRs MAY then direct NAKs for subsequent - ODATA sequence numbers to the DLR rather than to the original source. - In addition, DLRs that receive redirected NAKs for which they have - RDATA MUST send a NULL NAK to provide flow control to the original - source without also provoking a repair from that source. - - - - - - - -Speakman, et. al. Experimental [Page 47] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -9.5.1. OPT_REDIRECT - Packet Extensions Contents - - OPT_REDIR_NLA the DLR's own unicast network-layer address to which - recipients of the redirecting POLR MAY direct - subsequent NAKs for the corresponding TSI. - -9.5.2. OPT_REDIRECT - Procedures - DLRs - - A DLR MUST receive any PGM sessions for which it wishes to provide a - source of repairs. In addition to acting as an ordinary PGM - receiver, a DLR MAY then respond to NCFs or relevant POLLs sourced by - parent network elements (or even by the source itself) by sending a - POLR containing an OPT_REDIRECT providing its own network-layer - address. - - If a DLR can provide FEC repairs it MUST denote this by setting - OPT_PARITY in the PGM header of its POLR response. - -9.5.2.1. Upstream DLRs - - If the NCF completes NAK transmission initiated by the DLR itself, - the DLR MUST NOT send a redirecting POLR. - - When a DLR receives an NCF from its upstream PGM parent, it SHOULD - send a redirecting POLR, multicast to the group. The DLR SHOULD - record that it is acting as an upstream DLR for the said session. - Note that this POLR MUST have both the data source's source address - and the router alert option in its network header. - - An upstream DLR MUST act as an ordinary PGM source in responding to - any NAK it receives (i.e., directed to it). That is, it SHOULD - respond first with a normal NCF and then RDATA as usual. In - addition, an upstream DLR that receives redirected NAKs for which it - has RDATA MUST send a NULL NAK to provide flow control to the - original source. If it cannot provide the RDATA it forwards the NAK - to the upstream PGM neighbor as usual. - - Nota Bene: In order to propagate on exactly the same distribution - tree as ODATA, RDATA and POLR packets transmitted by DLRs MUST - bear the ODATA source's NLA as the network-header source address, - not the DLR's NLA as might be expected. - - - - - - - - - - -Speakman, et. al. Experimental [Page 48] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -9.5.2.2. Off-Tree DLRs - - A DLR that receives a POLL with sub-type PGM_POLL_DLR MUST respond - with a unicast redirecting POLR if it provides the appropriate - service. The DLR SHOULD respond using the rules outlined for polling - in Appendix D of this text. If the DLR responds, it SHOULD record - that it is acting as an off-tree DLR for the said session. - - An off-tree DLR acts in a special way in responding to any NAK it - receives (i.e., directed to it). It MUST respond to a NAK directed - to it from its parent by unicasting an NCF and RDATA to its parent. - The parent will then forward the RDATA down the distribution tree. - The DLR uses its own and the parent's NLA addresses in the network - header for the source and destination respectively. The unicast NCF - and RDATA packets SHOULD not have the router alert option. In all - other ways the RDATA header should be "as if" the packet had come - from the source. - - Again, an off-tree DLR that receives redirected NAKs for which it has - RDATA MUST originate a NULL NAK to provide flow control to the - original source. It MUST originate the NULL NAK before originating - the RDATA. This must be done to reduce the state held in the network - element. - - If it cannot provide the RDATA for a given NAK, an off-tree DLR - SHOULD confirm the NAK with a unicast NCF as normal, then immediately - send a NAK for the said data packet back to its parent. - -9.5.2.3. Simultaneous Upstream and Off-Tree DLR operation - - Note that it is possible for a DLR to provide service to its parent - and to downstream network elements simultaneously. A downstream loss - coupled with a loss for the same data on some other part of the - distribution tree served by its parent could cause this. In this - case it may provide both upstream and off-tree functionality - simultaneously. - - Note that a DLR differentiates between NAKs from an NE downstream or - from its parent by comparing the network-header source address of the - NAK with it's upstream PGM parent's NLA. The DLR knows the parent's - NLA from the session's SPM messages. - - - - - - - - - - -Speakman, et. al. Experimental [Page 49] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -9.5.3. OPT_REDIRECT - Procedures - Network Elements - -9.5.3.1. Discovering DLRs - - When a PGM router receives notification of a loss via a NAK, it - SHOULD first try to use a known DLR to recover the loss. If such a - DLR is not known it SHOULD initiate DLR discovery. DLR discovery may - occur in two ways. If there are upstream DLRs, the NAK transmitted - by this router to its PGM parent will trigger their discovery, via a - redirecting POLR. Also, a network element SHOULD initiate a search - for off-tree DLRs using the PGM polling mechanism, and the sub-type - PGM_POLL_DLR. - - If a DLR can provide FEC repairs it will denote this by setting - OPT_PARITY in the PGM header of its POLR response. A network element - SHOULD only direct parity NAKs to a DLR that can provide FEC repairs. - -9.5.3.2. Redirected Repair - - When it can, a network element SHOULD use upstream DLRs. - - Upon receiving a redirecting POLR, network elements SHOULD record the - redirecting information for the TSI, and SHOULD redirect subsequent - NAKs for the same TSI to the network address provided in the - redirecting POLR rather than to the PGM neighbor known via the SPMs. - Note, however, that a redirecting POLR is NOT regarded as matching - the NAK that provoked it, so it does not complete the transmission of - that NAK. Only a normal matching NCF can complete the transmission - of a NAK. - - For subsequent NAKs, if the network element has recorded redirection - information for the corresponding TSI, it MAY change the destination - network address of those NAKs and attempt to transmit them to the - DLR. No NAK for a specific SQN SHOULD be sent to an off-tree DLR if - a NAK for the SQN has been seen on the interface associated with the - DLR. Instead the NAK SHOULD be forwarded upstream. Subsequent NAKs - for different SQNs MAY be forwarded to the said DLR (again assuming - no NAK for them has been seen on the interface to the DLR). - - If a corresponding NCF is not received from the DLR within - NAK_RPT_IVL, the network element MUST discard the redirecting - information for the TSI and re-attempt to forward the NAK towards the - PGM upstream neighbor. - - - - - - - - -Speakman, et. al. Experimental [Page 50] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - If a NAK is received from the DLR for a requested SQN, the network - element MUST discard the redirecting information for the SQN and re- - attempt to forward the NAK towards the PGM upstream neighbor. The - network element MAY still direct NAKs for different SQNs to the DLR. - - RDATA and NCFs from upstream DLRs will flow down the distribution - tree. However, RDATA and NCFs from off-tree DLRs will be unicast to - the network element. The network element will terminate the NCF, but - MUST put the source's NLA and the group address into the network - header and MUST add router alert before forwarding the RDATA packet - to the distribution subtree. - - NULL NAKs from an off-tree DLR for an RDATA packet requested from - that off-tree DLR MUST always be forwarded upstream. The network - element can assume that these will arrive before the matching RDATA. - Other NULL NAKs are forwarded only if matching repair state has not - already been created. Network elements MUST NOT confirm or retry - NULL NAKs and they MUST NOT add the receiving interface to the repair - state. If a NULL NAK is used to initially create repair state, this - fact must be recorded so that any subsequent non-NULL NAK will not be - eliminated, but rather will be forwarded to provoke an actual repair. - State created by a NULL NAK exists only for NAK_ELIM_IVL. - -9.5.4. OPT_REDIRECT - Procedures - Receivers - - These procedures are intended to be applied in instances where a - receiver's first hop router on the reverse path to the source is not - a PGM Network Element. So, receivers MUST ignore a redirecting POLR - from a DLR on the same IP subnet that the receiver resides on, since - this is likely to suffer identical loss to the receiver and so be - useless. Therefore, these procedures are entirely OPTIONAL. A - receiver MAY choose to ignore all redirecting POLRs since in cases - where its first hop router on the reverse path is PGM capable, it - would ignore them anyway. Also, note that receivers will never learn - of off-tree DLRs. - - Upon receiving a redirecting POLR, receivers SHOULD record the - redirecting information for the TSI, and MAY redirect subsequent NAKs - for the same TSI to the network address provided in the redirecting - POLR rather than to the PGM neighbor for the corresponding ODATA for - which the receiver is requesting repair. Note, however, that a - redirecting POLR is NOT regarded as matching the NAK that provoked - it, so it does not complete the transmission of that NAK. Only a - normal matching NCF can complete the transmission of a NAK. - - For subsequent NAKs, if the receiver has recorded redirection - information for the corresponding TSI, it MAY change the destination - network address of those NAKs and attempt to transmit them to the - - - -Speakman, et. al. Experimental [Page 51] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - DLR. If a corresponding NCF is not received within NAK_RPT_IVL, the - receiver MUST discard the redirecting information for the TSI and - re-attempt to forward the NAK to the PGM neighbor for the original - source of the missing ODATA. - -9.5.5. OPT_REDIRECT - Packet Extension Format - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |E| Option Type | Option Length |Reserved |F|OPX|U| | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | NLA AFI | Reserved | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | DLR's NLA ... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ - - Option Type = 0x07 - - Option Length = 4 + NLA length - - DLR's NLA - - The DLR's own unicast network address to which recipients of the - redirecting POLR may direct subsequent NAKs. - - OPT_REDIRECT is network-significant. - -9.6. OPT_SYN - Synchronization Option - - The SYN option indicates the starting data packet for a session. It - must only appear in ODATA or RDATA packets. - - The SYN option MAY be used to provide a useful abstraction to - applications that can simplify application design by providing stream - start notification. It MAY also be used to let a late joiner to a - session know that it is indeed late (i.e. it would not see the SYN - option). - -9.6.1. OPT_SYN - Procedures - Receivers - - Procedures for receivers are implementation dependent. A receiver - MAY use the SYN to provide its applications with abstractions of the - data stream. - - - - - - - -Speakman, et. al. Experimental [Page 52] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -9.6.2. OPT_SYN - Procedures - Sources - - Sources MAY include OPT_SYN in the first data for a session. That - is, they MAY include the option in: - - the first ODATA sent on a session by a PGM source - - any RDATA sent as a result of loss of this ODATA packet - - all FEC packets for the first transmission group; in this case it - is interpreted as the first packet having the SYN - -9.6.3. OPT_SYN - Procedures - DLRs - - In an identical manner to sources, DLRs MUST provide OPT_SYN in - any retransmitted data that is at the start of a session. - -9.6.4. OPT_SYN - Packet Extension Format - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |E| Option Type | Option Length |Reserved |F|OPX|U| | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - Option Type = 0x0D - - Option Length = 4 - - OPT_SYN is NOT network-significant. - -9.7. OPT_FIN - Session Finish Option - - This FIN option indicates the last data packet for a session and - an orderly close down. - - The FIN option MAY be used to provide an abstraction to - applications that can simplify application design by providing - stream end notification. - - This option MAY be present in the last data packet or transmission - group for a session. The FIN PGM option MUST appear in every SPM - sent after the last ODATA for a session. The SPM_LEAD sequence - number in an SPM with the FIN option indicates the last known data - successfully transmitted for the session. - - - - - - -Speakman, et. al. Experimental [Page 53] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -9.7.1. OPT_FIN - Procedures - Receivers - - A receiver SHOULD use receipt of a FIN to let it know that it can - tear down its data structures for the said session once a suitable - time period has expired (TXW_SECS). It MAY still try to solicit - retransmissions within the existing transmit window. - - Other than this, procedures for receivers are implementation - dependent. A receiver MAY use the FIN to provide its applications - with abstractions of the data stream and to inform its - applications that the session is ending. - - 9.7.2. OPT_FIN - Procedures - Sources - - Sources MUST include OPT_FIN in every SPM sent after it has been - determined that the application has closed gracefully. If a - source is aware at the time of transmission that it is ending a - session the source MAY include OPT_FIN in, - - the last ODATA - - any associated RDATAs for the last data - - FEC packets for the last transmission group; in this case it is - interpreted as the last packet having the FIN - - When a source detects that it needs to send an OPT_FIN it SHOULD - immediately send it. This is done either by appending it to the last - data packet or transmission group or by immediately sending an SPM - and resetting the SPM heartbeat timer (i.e. it does not wait for a - timer to expire before sending the SPM). After sending an OPT_FIN, - the session SHOULD not close and stop sending SPMs until after a time - period equal to TXW_SECS. - -9.7.3. OPT_FIN - Procedures - DLRs - - In an identical manner to sources, DLRs MUST provide OPT_FIN in any - retransmitted data that is at the end of a session. - - - - - - - - - - - - - -Speakman, et. al. Experimental [Page 54] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -9.7.4. OPT_FIN - Packet Extension Format - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |E| Option Type | Option Length |Reserved |F|OPX|U| | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - Option Type = 0x0E - - Option Length = 4 - - OPT_FIN is NOT network-significant. - -9.8. OPT_RST - Session Reset Option - - The RST option MAY appear in every SPM sent after an unrecoverable - error is identified by the source. This acts to notify the receivers - that the session is being aborted. This option MAY appear only in - SPMs. The SPM_LEAD sequence number in an SPM with the RST option - indicates the last known data successfully transmitted for the - session. - -9.8.1. OPT_RST - Procedures - Receivers - - Receivers SHOULD treat the reception of OPT_RST in an SPM as an abort - of the session. - - A receiver that receives an SPM with an OPT_RST with the N bit set - SHOULD not send any more NAKs for the said session towards the - source. If the N bit (see 9.8.5) is not set, the receiver MAY - continue to try to solicit retransmit data within the current - transmit window. - -9.8.2. OPT_RST - Procedures - Sources - - Sources SHOULD include OPT_RST in every SPM sent after it has been - determined that an unrecoverable error condition has occurred. The N - bit of the OPT_RST SHOULD only be sent if the source has determined - that it cannot process NAKs for the session. The cause of the - OPT_RST is set to an implementation specific value. If the error - code is unknown, then the value of 0x00 is used. When a source - detects that it needs to send an OPT_RST it SHOULD immediately send - it. This is done by immediately sending an SPM and resetting the SPM - heartbeat timer (i.e. it does not wait for a timer to expire before - sending the SPM). After sending an OPT_RST, the session SHOULD not - close and stop sending SPMs until after a time period equal to - TXW_SECS. - - - -Speakman, et. al. Experimental [Page 55] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -9.8.3. OPT_RST - Procedures - DLRs - - None. - -9.8.4. OPT_RST - Packet Extension Format - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |E| Option Type | Option Length |Reserved |F|OPX|U|N|Error Code | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - Option Type = 0x0F - - Option Length = 4 - - N bit - - The N bit is set to 1 to indicate that NAKs for previous ODATA - will go unanswered from the source. The application will tell the - source to turn this bit on or off. - - Error Code - - The 6 bit error code field is used to forward an error code down - to the receivers from the source. - - The value of 0x00 indicates an unknown reset reason. Any other - value indicates the application purposely aborted and gave a - reason (the error code value) that may have meaning to the end - receiver application. These values are entirely application - dependent. - - OPT_RST is NOT network-significant. - -10. Security Considerations - - In addition to the usual problems of end-to-end authentication, PGM - is vulnerable to a number of security risks that are specific to the - mechanisms it uses to establish source path state, to establish - repair state, to forward NAKs, to identify DLRs, and to distribute - repairs. These mechanisms expose PGM network elements themselves to - security risks since network elements not only switch but also - interpret SPMs, NAKs, NCFs, and RDATA, all of which may legitimately - be transmitted by PGM sources, receivers, and DLRs. Short of full - authentication of all neighboring sources, receivers, DLRs, and - network elements, the protocol is not impervious to abuse. - - - - -Speakman, et. al. Experimental [Page 56] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - So putting aside the problems of rogue PGM network elements for the - moment, there are enough potential security risks to network elements - associated with sources, receivers, and DLRs alone. These risks - include denial of service through the exhausting of both CPU - bandwidth and memory, as well as loss of (repair) data connectivity - through the muddling of repair state. - - False SPMs may cause PGM network elements to mis-direct NAKs intended - for the legitimate source with the result that the requested RDATA - would not be forthcoming. - - False NAKs may cause PGM network elements to establish spurious - repair state that will expire only upon time-out and could lead to - memory exhaustion in the meantime. - - False NCFs may cause PGM network elements to suspend NAK forwarding - prematurely (or to mis-direct NAKs in the case of redirecting POLRs) - resulting eventually in loss of RDATA. - - False RDATA may cause PGM network elements to tear down legitimate - repair state resulting eventually in loss of legitimate RDATA. - - The development of precautions for network elements to protect - themselves against incidental or unsophisticated versions of these - attacks is work outside of this spec and includes: - - Damping of jitter in the value of either the network-header source - address of SPMs or the path NLA in SPMs. While the network-header - source address is expected to change seldom, the path NLA is - expected to change occasionally as a consequence of changes in - underlying multicast routing information. - - The extension of NAK shedding procedures to control the volume, not - just the rate, of confirmed NAKs. In either case, these procedures - assist network elements in surviving NAK attacks at the expense of - maintaining service. More efficiently, network elements may use the - knowledge of TSIs and their associated transmit windows gleaned from - SPMs to control the proliferation of repair state. - - A three-way handshake between network elements and DLRs that would - permit a network element to ascertain with greater confidence that an - alleged DLR is identified by the alleged network-header source - address, and is PGM conversant. - - - - - - - - -Speakman, et. al. Experimental [Page 57] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -11. Appendix A - Forward Error Correction - -11.1. Introduction - - The following procedures incorporate packet-level Reed Solomon - Erasure correcting techniques as described in [11] and [12] into PGM. - This approach to Forward Error Correction (FEC) is based upon the - computation of h parity packets from k data packets for a total of n - packets such that a receiver can reconstruct the k data packets out - of any k of the n packets. The original k data packets are referred - to as the Transmission Group, and the total n packets as the FEC - Block. - - These procedures permit any combination of pro-active FEC or on- - demand FEC with conventional ARQ (selective retransmission) within a - given TSI to provide any flavor of layered or integrated FEC. The - two approaches can be used by the same or different receivers in a - single transport session without conflict. Once provided by a - source, the actual use of FEC or selective retransmission for loss - recovery in the session is entirely at the discretion of the - receivers. Note however that receivers SHOULD NOT ask for selective - retransmissions when FEC is available, nevertheless sources MUST - provide selective retransmissions in response to selective NAKs from - the leading partial transmission group (i.e. the most recent - transmission group, which is not yet full). For any group that is - full, the source SHOULD provide FEC on demand in response to a - selective NAK. - - Pro-active FEC refers to the technique of computing parity packets at - transmission time and transmitting them as a matter of course - following the data packets. Pro-active FEC is RECOMMENDED for - providing loss recovery over simplex or asymmetric multicast channels - over which returning repair requests is either impossible or costly. - It provides increased reliability at the expense of bandwidth. - - On-demand FEC refers to the technique of computing parity packets at - repair time and transmitting them only upon demand (i.e., receiver- - based loss detection and repair request). On-demand FEC is - RECOMMENDED for providing loss recovery of uncorrelated loss in very - large receiver populations in which the probability of any single - packet being lost is substantial. It provides equivalent reliability - to selective NAKs (ARQ) at no more and typically less expense of - bandwidth. - - Selective NAKs are NAKs that request the retransmission of specific - packets by sequence number corresponding to the sequence number of - any data packets detected to be missing from the expected sequence - (conventional ARQ). Selective NAKs can be used for recovering losses - - - -Speakman, et. al. Experimental [Page 58] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - occurring in leading partial transmission groups, i.e. in the most - recent transmission group, which is not yet full. The RECOMMENDED - way of handling partial transmission groups, however, is for the data - source to use variable-size transmission groups (see below). - - Parity NAKs are NAKs that request the transmission of a specific - number of parity packets by count corresponding to the count of the - number of data packets detected to be missing from a group of k data - packets (on-demand FEC). - - The objective of these procedures is to incorporate these FEC - techniques into PGM so that: - - sources MAY provide parity packets either pro-actively or on- - demand, interchangeably within the same TSI, - - receivers MAY use either selective or parity NAKs interchangeably - within the same TSI (however, in a session where on-demand parity - is available receivers SHOULD only use parity NAKs). - - network elements maintain repair state based on either selective - or parity NAKs in the same data structure, altering only search, - RDATA constraint, and deletion algorithms in either case, - - and only OPTION additions to the basic packet formats are - REQUIRED. - -11.2. Overview - - Advertising FEC parameters in the transport session - - Sources add OPT_PARITY_PRM to SPMs to provide session-specific - parameters such as the number of packets (TGSIZE == k) in a - transmission group. This option lets receivers know how many packets - there are in a transmission group, and it lets network elements sort - repair state by transmission group number. This option includes an - indication of whether pro-active and/or on-demand parity is available - from the source. - - Distinguishing parity packets from data packets - - Sources send pro-active parity packets as ODATA (NEs do not forward - RDATA unless a repair state is present) and on-demand parity packets - as RDATA. A source MUST add OPT_PARITY to the ODATA/RDATA packet - header of parity packets to permit network elements and receivers to - distinguish them from data packets. - - - - - -Speakman, et. al. Experimental [Page 59] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Data and parity packet numbering - - Parity packets MUST be calculated over a fixed number k of data - packets known as the Transmission Group. Grouping of packets into - transmission groups effectively partitions a packet sequence number - into a high-order portion (TG_SQN) specifying the transmission group - (TG), and a low-order portion (PKT_SQN) specifying the packet number - (PKT-NUM in the range 0 through k-1) within that group. From an - implementation point of view, it's handy if k, the TG size, is a - power of 2. If so, then TG_SQN and PKT_SQN can be mapped side-by- - side into the 32 bit SQN. log2(TGSIZE) is then the size in bits of - PKT_SQN. - - This mapping does not reduce the effective sequence number space - since parity packets marked with OPT_PARITY allow the sequence space - (PKT_SQN) to be completely reused in order to number the h parity - packets, as long as h is not greater than k. - - In the case where h is greater than k, a source MUST add - OPT_PARITY_GRP to any parity packet numbered j greater than k-1, - specifying the number m of the group of k parity packets to which the - packet belongs, where m is just the quotient from the integer - division of j by k. Correspondingly, PKT-NUM for such parity packets - is just j modulo k. In other words, when a source needs to generate - more parity packets than there were original data packets (perhaps - because of a particularly lossy line such that a receiver lost not - only the original data but some of the parity RDATA as well), use the - OPT_PARITY_GRP option in order to number and identify the - transmission group of the extra packets that would exceed the normal - sequential number space. - - Note that parity NAKs (and consequently their corresponding parity - NCFs) MUST also contain the OPT_PARITY flag in the options field of - the fixed header, and that in these packets, PKT_SQN MUST contain - PKT_CNT, the number of missing packets, rather than PKT_NUM, the SQN - of a specific missing packet. More on all this later. - - Variable Transmission Group Size - - The transmission group size advertised in the OPT_PARITY_PRM option - on SPMs MUST be a power of 2 and constant for the duration of the - session. However, the actual transmission group size used MAY not be - constant for the duration of the session, and MAY not be a power of - 2. When a TG size different from the one advertised in - OPT_PARITY_PRM is used, the TG size advertised in OPT_PARITY_PRM MUST - be interpreted as specifying the maximum effective size of the TG. - - - - - -Speakman, et. al. Experimental [Page 60] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - When the actual TG size is not a power of 2 or is smaller than the - max TG size, there will be sparse utilization of the sequence number - space since some of the sequence numbers that would have been - consumed in numbering a maximum sized TG will not be assigned to - packets in the smaller TG. The start of the next transmission group - will always begin on the boundary of the maximum TG size as though - each of the sequence numbers had been utilized. - - When the source decides to use a smaller group size than that - advertised in OPT_PARITY_PRM, it appends OPT_CURR_TGSIZE to the last - data packet (ODATA) in the truncated transmission group. This lets - the receiver know that it should not expect any more packets in this - transmission group, and that it may start requesting repairs for any - missing packets. If the last data packet itself went missing, the - receiver will detect the end of the group when it receives a parity - packet for the group, an SPM with SPM_LEAD equal to OD_SQN of the - last data packet, or the first packet of the next group, whichever - comes first. In addition, any parity packet from this TG will also - carry the OPT_CURR_TGSIZE option as will any SPM sent with SPM_LEAD - equal to OD_SQN of the last data packet. - - Variable TSDU length - - If a non constant TSDU length is used within a given transmission - group, the size of parity packets in the corresponding FEC block MUST - be equal to the size of the largest original data packet in the - block. Parity packets MUST be computed by padding the original - packets with zeros up to the size of the largest data packet. Note - that original data packets are transmitted without padding. - - Receivers using a combination of original packets and FEC packets to - rebuild missing packets MUST pad the original packets in the same way - as the source does. The receiver MUST then feed the padded original - packets plus the parity packets to the FEC decoder. The decoder - produces the original packets padded with zeros up to the size of the - largest original packet in the group. In order for the receiver to - eliminate the padding on the reconstructed data packets, the original - size of the packet MUST be known, and this is accomplished as - follows: - - The source, along with the packet payloads, encodes the TSDU - length and appends the 2-byte encoded length to the padded FEC - packets. - - Receivers pad the original packets that they received to the - largest original packet size and then append the TSDU length to - the padded packets. They then pass them and the FEC packets to - the FEC decoder. - - - -Speakman, et. al. Experimental [Page 61] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - The decoder produces padded original packets with their original - TSDU length appended. Receivers MUST now use this length to get - rid of the padding. - - A source that transmits variable size packets MUST take into account - the fact that FEC packets will have a size equal to the maximum size - of the original packets plus the size of the length field (2 bytes). - - If a fixed packet size is used within a transmission group, the - encoded length is not appended to the parity packets. The presence - of the fixed header option flag OPT_VAR_PKTLEN in parity packets - allows receivers to distinguish between transmission groups with - variable sized packets and fixed-size ones, and behave accordingly. - - Payload-specific options - - Some options present in data packet (ODATA and RDATA) are strictly - associated with the packet content (PGM payload), OPT_FRAGMENT being - an example. These options must be preserved even when the data - packet that would normally contain them is not received, but its the - payload is recovered though the use of FEC. - - To achieve this, PGM encodes the content of these options in special - options that are inserted in parity packets. Two flags present in - the the option common-header are used for this process: bit F - (OP_ENCODED) and bit U (OP_ENCODED_NULL). - - Whenever at least one of the original packets of a TG contains a - payload-specific option of a given type, the source MUST include an - encoded version of that option type in all the parity packets it - transmits. The encoded option is computed by applying FEC encoding - to the whole option with the exception of the first three bytes of - the option common-header (E, Option Type, Option Length, OP_ENCODED - and OPX fields). The type, length and OPX of the encoded option are - the same as the type, length and OPX in the original options. - OP_ENCODED is set to 1 (all original option have OP_ENCODED = 0). - - The encoding is performed using the same process that is used to - compute the payload of the parity packet. i.e. the FEC encoder is fed - with one copy of that option type for each original packet in the TG. - If one (or more) original packet of the TG does not contain that - option type, an all zeroes option is used for the encoding process. - To be able to distinguish this "dummy" option from valid options with - all-zeroes payload, OP_ENCODED_NULL is used. OP_ENCODED_NULL is set - to 0 in all the original options, but the value of 1 is used in the - encoding process if the option did not exist in the original packet. - On the receiver side, all option with OP_ENCODED_NULL equal to 1 are - discarded after decoding. - - - -Speakman, et. al. Experimental [Page 62] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - When a receiver recovers a missing packet using FEC repair packets, - it MUST also recover payload-specific options, if any. The presence - of these can be unequivocally detected through the presence of - encoded options in parity packets (encoded options have OP_ENCODED - set to 1). Receivers apply FEC-recovery to encoded options and - possibly original options, as they do to recover packet payloads. - The FEC decoding is applied to the whole option with the exception of - the first three bytes of the option common-header (E, Option Type, - Option Length, OP_ENCODED and OPX fields). Each decoded option is - associated with the relative payload, unless OP_ENCODED_NULL turns - out to be 1, in which case the decoded option is discarded. - - The decoding MUST be performed using the 1st occurrence of a given - option type in original/parity packets. If one or more original - packets do not contain that option type, an option of the same type - with zero value must be used. This option MUST have OP_ENCODED_NULL - equal to 1. - -11.3. Packet Contents - - This section just provides enough short-hand to make the Procedures - intelligible. For the full details of packet contents, please refer - to Packet Formats below. - - OPT_PARITY indicated in pro-active (ODATA) and on-demand - (RDATA) parity packets to distinguish them from - data packets. This option is directly encoded in - the "Option" field of the fixed PGM header - - OPT_VAR_PKTLEN MAY be present in pro-active (ODATA) and on-demand - (RDATA) parity packets to indicate that the - corresponding transmission group is composed of - variable size data packets. This option is - directly encoded in the "Option" field of the fixed - PGM header - - OPT_PARITY_PRM appended by sources to SPMs to specify session- - specific parameters such as the transmission group - size and the availability of pro-active and/or on- - demand parity from the source - - OPT_PARITY_GRP the number of the group (greater than 0) of h - parity packets to which the parity packet belongs - when more than k parity packets are provided by the - source - - - - - - -Speakman, et. al. Experimental [Page 63] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - OPT_CURR_TGSIZE appended by sources to the last data packet and any - parity packets in a variable sized transmission - group to indicate to the receiver the actual size - of a transmission group. May also be appended to - certain SPMs - -11.3.1. Parity NAKs - - NAK_TG_SQN the high-order portion of NAK_SQN specifying the - transmission group for which parity packets are - requested - - NAK_PKT_CNT the low-order portion of NAK_SQN specifying the - number of missing data packets for which parity - packets are requested - - Nota Bene: NAK_PKT_CNT (and NCF_PKT_CNT) are 0-based counters, - meaning that NAK_PKT_CNT = 0 means that 1 FEC RDATA is being - requested, and in general NAK_PKT_CNT = k - 1 means that k FEC - RDATA are being requested. - -11.3.2. Parity NCFs - - NCF_TG_SQN the high-order portion of NCF_SQN specifying the - transmission group for which parity packets were - requested - - NCF_PKT_CNT the low-order portion of NCF_SQN specifying the - number of missing data packets for which parity - packets were requested - - Nota Bene: NCF_PKT_CNT (and NAK_PKT_CNT) are 0-based counters, - meaning that NAK_PKT_CNT = 0 means that 1 FEC RDATA is being - requested, and in general NAK_PKT_CNT = k - 1 means that k FEC - RDATA are being requested. - -11.3.3. On-demand Parity - - RDATA_TG_SQN the high-order portion of RDATA_SQN specifying the - transmission group to which the parity packet - belongs - - RDATA_PKT_SQN the low-order portion of RDATA_SQN specifying the - parity packet sequence number within the - transmission group - - - - - - -Speakman, et. al. Experimental [Page 64] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -11.3.4. Pro-active Parity - - ODATA_TG_SQN the high-order portion of ODATA_SQN specifying the - transmission group to which the parity packet - belongs - - ODATA_PKT_SQN the low-order portion of ODATA_SQN specifying the - parity packet sequence number within the - transmission group - -11.4. Procedures - Sources - - If a source elects to provide parity for a given transport session, - it MUST first provide the transmission group size PARITY_PRM_TGS in - the OPT_PARITY_PRM option of its SPMs. This becomes the maximum - effective transmission group size in the event that the source elects - to send smaller size transmission groups. If a source elects to - provide proactive parity for a given transport session, it MUST set - PARITY_PRM_PRO in the OPT_PARITY_PRM option of its SPMs. If a source - elects to provide on-demand parity for a given transport session, it - MUST set PARITY_PRM_OND in the OPT_PARITY_PRM option of its SPMs. - - A source MUST send any pro-active parity packets for a given - transmission group only after it has first sent all of the - corresponding k data packets in that group. Pro-active parity - packets MUST be sent as ODATA with OPT_PARITY in the fixed header. - - If a source elects to provide on-demand parity, it MUST respond to a - parity NAK for a transmission group with a parity NCF. The source - MUST complete the transmission of the k original data packets and the - proactive parity packets, possibly scheduled, before starting the - transmission of on-demand parity packets. Subsequently, the source - MUST send the number of parity packets requested by that parity NAK. - On-demand parity packets MUST be sent as RDATA with OPT_PARITY in the - fixed header. Previously transmitted pro-active parity packets - cannot be reused as on-demand parity packets, these MUST be computed - with new, previously unused, indexes. - - In either case, the source MUST provide selective retransmissions - only in response to selective NAKs from the leading partial - transmission group. For any group that is full, the source SHOULD - provide FEC on demand in response to a selective retransmission - request. - - In the absence of data to transmit, a source SHOULD prematurely - terminate the current transmission group by including OPT_CURR_TGSIZE - to the last data packet or to any proactive parity packets provided. - - - - -Speakman, et. al. Experimental [Page 65] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - If the last data packet has already been transmitted and there is no - provision for sending proactive parity packets, an SPM with - OPT_CURR_TGSIZE SHOULD be sent. - - A source consolidates requests for on-demand parity in the same - transmission group according to the following procedures. If the - number of pending (i.e., unsent) parity packets from a previous - request for on-demand parity packets is equal to or greater than - NAK_PKT_CNT in a subsequent NAK, that subsequent NAK MUST be - confirmed but MAY otherwise be ignored. If the number of pending - (i.e., unsent) parity packets from a previous request for on-demand - parity packets is less than NAK_PKT_CNT in a subsequent NAK, that - subsequent NAK MUST be confirmed but the source need only increase - the number of pending parity packets to NAK_PKT_CNT. - - When a source provides parity packets relative to a transmission - group with variable sized packets, it MUST compute parity packets by - padding the smaller original packets with zeroes out to the size of - the largest of the original packets. The source MUST also append the - encoded TSDU lengths at the end of any padding or directly to the end - of the largest packet, and add the OPT_VAR_PKTLEN option as specified - in the overview description. - - When a source provides variable sized transmission groups, it SHOULD - append the OPT_CURR_TGSIZE option to the last data packet in the - shortened group, and it MUST append the OPT_CURR_TGSIZE option to any - parity packets it sends within that group. In case the the last data - packet is sent before a determination has been made to shorten the - group and there is no provision for sending proactive parity packets, - an SPM with OPT_CURR_TGSIZE SHOULD be sent. The source MUST also add - OPT_CURR_TGSIZE to any SPM that it sends with SPM_LEAD equal to - OD_SQN of the last data packet. - - A receiver MUST NAK for the entire number of packets missing based on - the maximum TG size, even if it already knows that the actual TG size - is smaller. The source MUST take this into account and compute the - number of packets effectively needed as the difference between - NAK_PKT_CNT and an offset computed as the difference between the max - TG size and the effective TG size. - -11.5. Procedures - Receivers - - If a receiver elects to make use of parity packets for loss recovery, - it MUST first learn the transmission group size PARITY_PRM_TGS from - OPT_PARITY_PRM in the SPMs for the TSI. The transmission group size - is used by a receiver to determine the sequence number boundaries - between transmission groups. - - - - -Speakman, et. al. Experimental [Page 66] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Thereafter, if PARITY_PRM_PRO is also set in the SPMs for the TSI, a - receiver SHOULD use any pro-active parity packets it receives for - loss recovery, and if PARITY_PRM_OND is also set in the SPMs for the - TSI, it MAY solicit on-demand parity packets upon loss detection. If - PARITY_PRM_OND is set, a receiver MUST NOT send selective NAKs, - except in partial transmission groups if the source does not use the - variable transmission-group size option. Parity packets are ODATA - (pro-active) or RDATA (on-demand) packets distinguished by OPT_PARITY - which lets receivers know that ODATA/RDATA_TG_SQN identifies the - group of PARITY_PRM_TGS packets to which the parity may be applied - for loss recovery in the corresponding transmission group, and that - ODATA/RDATA_PKT_SQN is being reused to number the parity packets - within that group. Receivers order parity packets and eliminate - duplicates within a transmission group based on ODATA/RDATA_PKT_SQN - and on OPT_PARITY_GRP if present. - - To solicit on-demand parity packets, a receiver MUST send parity NAKs - upon loss detection. For the purposes of soliciting on-demand - parity, loss detection occurs at transmission group boundaries, i.e. - upon receipt of the last data packet in a transmission group, upon - receipt of any data packet in any subsequent transmission group, or - upon receipt of any parity packet in the current or a subsequent - transmission group. - - A parity NAK is simply a NAK with OPT_PARITY and NAK_PKT_CNT set to - the count of the number of packets detected to be missing from the - transmission group specified by NAK_TG_SQN. Note that this - constrains the receiver to request no more parity packets than there - are data packets in the transmission group. - - A receiver SHOULD bias the value of NAK_BO_IVL for parity NAKs - inversely proportional to NAK_PKT_CNT so that NAKs for larger losses - are likely to be scheduled ahead of NAKs for smaller losses in the - same receiver population. - - A confirming NCF for a parity NAK is a parity NCF with NCF_PKT_CNT - equal to or greater than that specified by the parity NAK. - - A receiver's NAK_RDATA_IVL timer is not cancelled until all requested - parity packets have been received. - - In the absence of data (detected from SPMs bearing SPM_LEAD equal to - RXW_LEAD) on non-transmission-group boundaries, receivers MAY resort - to selective NAKs for any missing packets in that partial - transmission group. - - - - - - -Speakman, et. al. Experimental [Page 67] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - When a receiver handles parity packets belonging to a transmission - group with variable sized packets, (detected from the presence of the - OPT_VAR_PKTLEN option in the parity packets), it MUST decode them as - specified in the overview description and use the decoded TSDU length - to get rid of the padding in the decoded packet. - - If the source was using a variable sized transmission group via the - OPT_CURR_TGSIZE, the receiver might learn this before having - requested (and received) any retransmission. The above happens if it - sees OPT_CURR_TGSIZE in the last data packet of the TG, in any - proactive parity packet or in a SPM. If the receivers learns this - and determines that it has missed one or more packets in the - shortened transmission group, it MAY then NAK for them without - waiting for the start of the next transmission group. Otherwise it - will start NAKing at the start of the next transmission group. - - In both cases, the receiver MUST NAK for the number of packets - missing assuming that the size of the transmission group is the - maximum effective transmission group. In other words, the receivers - cannot exploit the fact that it might already know that the - transmission group was smaller but MUST always NAK for the number of - packets it believes are missing, plus the number of packets required - to bring the total packets up to the maximum effective transmission - group size. - - After the first parity packet has been delivered to the receiver, the - actual TG size is known to him, either because already known or - because discovered via OPT_CURR_TGSIZE contained in the parity - packet. Hence the receiver can decode the whole group as soon as the - minimum number of parity packets needed is received. - -11.6. Procedures - Network Elements - - Pro-active parity packets (ODATA with OPT_PARITY) are switched by - network elements without transport-layer intervention. - - On-demand parity packets (RDATA with OPT_PARITY) necessitate modified - request, confirmation and repair constraint procedures for network - elements. In the context of these procedures, repair state is - maintained per NAK_TSI and NAK_TG_SQN, and in addition to recording - the interfaces on which corresponding NAKs have been received, - records the largest value of NAK_PKT_CNT seen in corresponding NAKs - on each interface. This value is referred to as the known packet - count. The largest of the known packet counts recorded for any - interface in the repair state for the transmit group or carried by an - NCF is referred to as the largest known packet count. - - - - - -Speakman, et. al. Experimental [Page 68] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Upon receipt of a parity NAK, a network element responds with the - corresponding parity NCF. The corresponding parity NCF is just an - NCF formed in the usual way (i.e., a multicast copy of the NAK with - the packet type changed), but with the addition of OPT_PARITY and - with NCF_PKT_CNT set to the larger of NAK_PKT_CNT and the known - packet count for the receiving interface. The network element then - creates repair state in the usual way with the following - modifications. - - If repair state for the receiving interface does not exist, the - network element MUST create it and additionally record NAK_PKT_CNT - from the parity NAK as the known packet count for the receiving - interface. - - If repair state for the receiving interface already exists, the - network element MUST eliminate the NAK only if NAK_ELIM_IVL has not - expired and NAK_PKT_CNT is equal to or less than the largest known - packet count. If NAK_PKT_CNT is greater than the known packet count - for the receiving interface, the network element MUST update the - latter with the larger NAK_PKT_CNT. - - Upon either adding a new interface or updating the known packet count - for an existing interface, the network element MUST determine if - NAK_PKT_CNT is greater than the largest known packet count. If so or - if NAK_ELIM_IVL has expired, the network element MUST forward the - parity NAK in the usual way with a value of NAK_PKT_CNT equal to the - largest known packet count. - - Upon receipt of an on-demand parity packet, a network element MUST - locate existing repair state for the corresponding RDATA_TSI and - RDATA_TG_SQN. If no such repair state exists, the network element - MUST discard the RDATA as usual. - - If corresponding repair state exists, the largest known packet count - MUST be decremented by one, then the network element MUST forward the - RDATA on all interfaces in the existing repair state, and decrement - the known packet count by one for each. Any interfaces whose known - packet count is thereby reduced to zero MUST be deleted from the - repair state. If the number of interfaces is thereby reduced to - zero, the repair state itself MUST be deleted. - - Upon reception of a parity NCF, network elements MUST cancel pending - NAK retransmission only if NCF_PKT_CNT is greater or equal to the - largest known packet count. Network elements MUST use parity NCFs to - anticipate NAKs in the usual way with the addition of recording - NCF_PKT_CNT from the parity NCF as the largest known packet count - with the anticipated state so that any subsequent NAKs received with - NAK_PKT_CNT equal to or less than NCF_PKT_CNT will be eliminated, and - - - -Speakman, et. al. Experimental [Page 69] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - any with NAK_PKT_CNT greater than NCF_PKT_CNT will be forwarded. - Network elements which receive a parity NCF with NCF_PKT_CNT larger - than the largest known packet count MUST also use it to anticipate - NAKs, increasing the largest known packet count to reflect - NCF_PKT_CNT (partial anticipation). - - Parity NNAKs follow the usual elimination procedures with the - exception that NNAKs are eliminated only if existing NAK state has a - NAK_PKT_CNT greater than NNAK_PKT_CNT. - - Network elements must take extra precaution when the source is using - a variable sized transmission group. Network elements learn that the - source is using a TG size smaller than the maximum from - OPT_CURR_TGSIZE in parity RDATAs or in SPMs. When this happens, they - compute a TG size offset as the difference between the maximum TG - size and the actual TG size advertised by OPT_CURR_TGSIZE. Upon - reception of parity RDATA, the TG size offset is used to update the - repair state as follows: - - Any interface whose known packet count is reduced to the TG size - offset is deleted from the repair state. - - This replaces the normal rule for deleting interfaces that applies - when the TG size is equal to the maximum TG size. - -11.7. Procedures - DLRs - - A DLR with the ability to provide FEC repairs MUST indicate this by - setting the OPT_PARITY bit in the redirecting POLR. It MUST then - process any redirected FEC NAKs in the usual way. - -11.8. Packet Formats - -11.8.1. OPT_PARITY_PRM - Packet Extension Format - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |E| Option Type | Option Length |Reserved |F|OPX|U| |P O| - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Transmission Group Size | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - Option Type = 0x08 - - Option Length = 8 octets - - P-bit (PARITY_PRM_PRO) - - - -Speakman, et. al. Experimental [Page 70] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Indicates when set that the source is providing pro-active parity - packets. - - O-bit (PARITY_PRM_OND) - - Indicates when set that the source is providing on-demand parity - packets. - - At least one of PARITY_PRM_PRO and PARITY_PRM_OND MUST be set. - - Transmission Group Size (PARITY_PRM_TGS) - - The number of data packets in the transmission group over which - the parity packets are calculated. If a variable transmission - group size is being used, then this becomes the maximum effective - transmission group size across the session. - - OPT_PARITY_PRM MAY be appended only to SPMs. - - OPT_PARITY_PRM is network-significant. - -11.8.2. OPT_PARITY_GRP - Packet Extension Format - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |E| Option Type | Option Length |Reserved |F|OPX|U| | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Parity Group Number | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - Option Type = 0x09 - - Option Length = 8 octets - - Parity Group Number (PRM_GROUP) - - The number of the group of k parity packets amongst the h parity - packets within the transmission group to which the parity packet - belongs, where the first k parity packets are in group zero. - PRM_GROUP MUST NOT be zero. - - OPT_PARITY_GRP MAY be appended only to parity packets. - - OPT_PARITY_GRP is NOT network-significant. - - - - - - -Speakman, et. al. Experimental [Page 71] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -11.8.3. OPT_CURR_TGSIZE - Packet Extension Format - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |E| Option Type | Option Length |Reserved |F|OPX|U| | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Actual Transmission Group Size | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - Option Type = 0x0A - - Option Length = 8 octets - - Actual Transmission Group Size (PRM_ATGSIZE) - - The actual number of data packets in this transmission group. - This MUST be less than or equal to the maximum transmission group - size PARITY_PRM_TGS in OPT_PARITY_PRM. - - OPT_CURR_TGSIZE MAY be appended to data and parity packets (ODATA or - RDATA) and to SPMs. - - OPT_CURR_TGSIZE is network-significant except when appended to ODATA. - -12. Appendix B - Support for Congestion Control - -12.1. Introduction - - A source MUST implement strategies for congestion avoidance, aimed at - providing overall network stability, fairness among competing PGM - flows, and some degree of fairness towards coexisting TCP flows [13]. - In order to do this, the source must be provided with feedback on the - status of the network in terms of traffic load. This appendix - specifies NE procedures that provide such feedback to the source in a - scalable way. (An alternative TCP-friendly scheme for congestion - control that does not require NE support can be found in [16]). - - The procedures specified in this section enable the collection and - selective forwarding of three types of feedback to the source: - - o Worst link load as measured in network elements. - - o Worst end-to-end path load as measured in network elements. - - o Worst end-to-end path load as reported by receivers. - - - - - -Speakman, et. al. Experimental [Page 72] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - This specification defines in detail NE procedures, receivers - procedures and packet formats. It also defines basic procedures in - receivers for generating congestion reports. This specification does - not define the procedures used by PGM sources to adapt their - transmission rates in response of congestion reports. Those - procedures depend upon the specific congestion control scheme. - - PGM defines a header option that PGM receivers may append to NAKs - (OPT_CR). OPT_CR carries congestion reports in NAKs that propagate - upstream towards the source. - - During the process of hop-by-hop reverse NAK forwarding, NEs examine - OPT_CR and possibly modify its contents prior to forwarding the NAK - upstream. Forwarding CRs also has the side effect of creating - congestion report state in the NE. The presence of OPT_CR and its - contents also influences the normal NAK suppression rules. Both the - modification performed on the congestion report and the additional - suppression rules depend on the content of the congestion report and - on the congestion report state recorded in the NE as detailed below. - - OPT_CR contains the following fields: - - OPT_CR_NE_WL Reports the load in the worst link as detected though - NE internal measurements - - OPT_CR_NE_WP Reports the load in the worst end-to-end path as - detected though NE internal measurements - - OPT_CR_RX_WP Reports the load in the worst end-to-end path as - detected by receivers - - A load report is either a packet drop rate (as measured at an NE's - interfaces) or a packet loss rate (as measured in receivers). Its - value is linearly encoded in the range 0-0xFFFF, where 0xFFFF - represents a 100% loss/drop rate. Receivers that send a NAK bearing - OPT_CR determine which of the three report fields are being reported. - - OPT_CR also contains the following fields: - - OPT_CR_NEL A bit indicating that OPT_CR_NE_WL is being reported. - - OPT_CR_NEP A bit indicating that OPT_CR_NE_WP is being reported. - - OPT_CR_RXP A bit indicating that OPT_CR_RX_WP is being reported. - - - - - - - -Speakman, et. al. Experimental [Page 73] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - OPT_CR_LEAD A SQN in the ODATA space that serves as a temporal - reference for the load report values. This is - initialized by receivers with the leading edge of the - transmit window as known at the moment of transmitting - the NAK. This value MAY be advanced in NEs that - modify the content of OPT_CR. - - OPT_CR_RCVR The identity of the receiver that generated the worst - OPT_CR_RX_WP. - - The complete format of the option is specified later. - -12.2. NE-Based Worst Link Report - - To permit network elements to report worst link, receivers append - OPT_CR to a NAK with bit OPT_CR_NEL set and OPT_CR_NE_WL set to zero. - NEs receiving NAKs that contain OPT_CR_NE_WL process the option and - update per-TSI state related to it as described below. The ultimate - result of the NEs' actions ensures that when a NAK leaves a sub-tree, - OPT_CR_NE_WL contains a congestion report that reflects the load of - the worst link in that sub-tree. To achieve this, NEs rewrite - OPT_CR_NE_WL with the worst value among the loads measured on the - local (outgoing) links for the session and the congestion reports - received from those links. - - Note that the mechanism described in this sub-section does not permit - the monitoring of the load on (outgoing) links at non-PGM-capable - multicast routers. For this reason, NE-Based Worst Link Reports - SHOULD be used in pure PGM topologies only. Otherwise, this - mechanism might fail in detecting congestion. To overcome this - limitation PGM sources MAY use a heuristic that combines NE-Based - Worst Link Reports and Receiver-Based Reports. - -12.3. NE-Based Worst Path Report - - To permit network elements to report a worst path, receivers append - OPT_CR to a NAK with bit OPT_CR_NEP set and OPT_CR_NE_WP set to zero. - The processing of this field is similar to that of OPT_CR_NE_WL with - the difference that, on the reception of a NAK, the value of - OPT_CR_NE_WP is adjusted with the load measured on the interface on - which the NAK was received according to the following formula: - - OPT_CR_NE_WP = if_load + OPT_CR_NE_WP * (100% - if_loss_rate) - - The worst among the adjusted OPT_CR_NE_WP is then written in the - outgoing NAK. This results in a hop-by-hop accumulation of link loss - rates into a path loss rate. - - - - -Speakman, et. al. Experimental [Page 74] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - As with OPT_CR_NE_WL, the congestion report in OPT_CR_NE_WP may be - invalid if the multicast distribution tree includes non-PGM-capable - routers. - -12.4. Receiver-Based Worst Report - - To report a packet loss rate, receivers append OPT_CR to a NAK with - bit OPT_CR_RXP set and OPT_CR_RX_WP set to the packet loss rate. NEs - receiving NAKs that contain OPT_CR_RX_WP process the option and - update per-TSI state related to it as described below. The ultimate - result of the NEs' actions ensures that when a NAK leaves a sub-tree, - OPT_CR_RX_WP contains a congestion report that reflects the load of - the worst receiver in that sub-tree. To achieve this, NEs rewrite - OTP_CR_RE_WP with the worst value among the congestion reports - received on its outgoing links for the session. In addition to this, - OPT_CR_RCVR MUST contain the NLA of the receiver that originally - measured the value of OTP_CR_RE_WP being forwarded. - -12.5. Procedures - Receivers - - To enable the generation of any type of congestion report, receivers - MUST insert OPT_CR in each NAK they generate and provide the - corresponding field (OPT_CR_NE_WL, OPT_CR_NE_WP, OPT_CR_RX_WP). The - specific fields to be reported will be advertised to receivers in - OPT_CRQST on the session's SPMs. Receivers MUST provide only those - options requested in OPT_CRQST. - - Receivers MUST initialize OPT_CR_NE_WL and OPT_CR_NE_WP to 0 and they - MUST initialize OPT_CR_RCVR to their NLA. At the moment of sending - the NAK, they MUST also initialize OPT_CR_LEAD to the leading edge of - the transmission window. - - Additionally, if a receiver generates a NAK with OPT_CR with - OPT_CR_RX_WP, it MUST initialize OPT_CR_RX_WP to the proper value, - internally computed. - -12.6. Procedures - Network Elements - - Network elements start processing each OPT_CR by selecting a - reference SQN in the ODATA space. The reference SQN selected is the - highest SQN known to the NE. Usually this is OPT_CR_LEAD contained - in the NAK received. - - They use the selected SQN to age the value of load measurement as - follows: - - o locally measured load values (e.g. interface loads) are - considered up-to-date - - - -Speakman, et. al. Experimental [Page 75] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - o load values carried in OPT_CR are considered up-to-date and are - not aged so as to be independent of variance in round-trip - times from the network element to the receivers - - o old load values recorded in the NE are exponentially aged - according to the difference between the selected reference SQN - and the reference SQN associated with the old load value. - - The exponential aging is computed so that a recorded value gets - scaled down by a factor exp(-1/2) each time the expected inter-NAK - time elapses. Hence the aging formula must include the current loss - rate as follows: - - aged_loss_rate = loss_rate * exp( - SQN_difference * loss_rate / - 2) - - Note that the quantity 1/loss_rate is the expected SQN_lag between - two NAKs, hence the formula above can also be read as: - - aged_loss_rate = loss_rate * exp( - 1/2 * SQN_difference / - SQN_lag) - - which equates to (loss_rate * exp(-1/2)) when the SQN_difference is - equal to expected SQN_lag between two NAKs. - - All the subsequent computations refer to the aged load values. - - Network elements process OPT_CR by handling the three possible types - of congestion reports independently. - - For each congestion report in an incoming NAK, a new value is - computed to be used in the outgoing NAK: - - o The new value for OPT_CR_NE_WL is the maximum of the load - measured on the outgoing interfaces for the session, the value - of OPT_CR_NE_WL of the incoming NAK, and the value previously - sent upstream (recorded in the NE). All these values are as - obtained after the aging process. - - o The new value for OPT_CR_NE_WP is the maximum of the value - previously sent upstream (after aging) and the value of - OPT_CR_NE_WP in the incoming NAK adjusted with the load on the - interface upon which the NAK was received (as described above). - - o The new value for OPT_CR_RX_WP is the maximum of the value - previously sent upstream (after aging) and the value of - OPT_CR_RX_WP in the incoming NAK. - - - - -Speakman, et. al. Experimental [Page 76] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - o If OPT_CR_RX_WP was selected from the incoming NAK, the new - value for OPT_CR_RCVR is the one in the incoming NAK. - Otherwise it is the value previously sent upstream. - - o The new value for OPT_CR_LEAD is the reference SQN selected for - the aging procedure. - -12.6.1. Overriding Normal Suppression Rules - - Normal suppression rules hold to determine if a NAK should be - forwarded upstream or not. However if any of the outgoing congestion - reports has changed by more than 5% relative to the one previously - sent upstream, this new NAK is not suppressed. - -12.6.2. Link Load Measurement - - PGM routers monitor the load on all their outgoing links and record - it in the form of per-interface loss rate statistics. "load" MUST be - interpreted as the percentage of the packets meant to be forwarded on - the interface that were dropped. Load statistics refer to the - aggregate traffic on the links and not to PGM traffic only. - - This document does not specify the algorithm to be used to collect - such statistics, but requires that such algorithm provide both - accuracy and responsiveness in the measurement process. As far as - accuracy is concerned, the expected measurement error SHOULD be - upper-limited (e.g. less than than 10%). As far as responsiveness is - concerned, the measured load SHOULD converge to the actual value in a - limited time (e.g. converge to 90% of the actual value in less than - 200 milliseconds), when the instantaneous offered load changes. - Whenever both requirements cannot be met at the same time, accuracy - SHOULD be traded for responsiveness. - - - - - - - - - - - - - - - - - - - -Speakman, et. al. Experimental [Page 77] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -12.7. Packet Formats - -12.7.1. OPT_CR - Packet Extension Format - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |E| Option Type | Option Length |Reserved |F|OPX|U| L P R| - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Congestion Report Reference SQN | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | NE Worst Link | NE Worst Path | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Rcvr Worst Path | Reserved | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | NLA AFI | Reserved | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Worst Receiver's NLA ... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ - - Option Type = 0x10 - - Option Length = 20 octets + NLA length - - L OPT_CR_NEL bit : set indicates OPT_CR_NE_WL is being reported - - P OPT_CR_NEP bit : set indicates OPT_CR_NE_WP is being reported - - R OPT_CR_RXP bit : set indicates OPT_CR_RX_WP is being reported - - Congestion Report Reference SQN (OPT_CR_LEAD). - - A SQN in the ODATA space that serves as a temporal reference point - for the load report values. - - NE Worst Link (OPT_CR_NE_WL). - - Reports the load in the worst link as detected though NE internal - measurements - - NE Worst Path (OPT_CR_NE_WP). - - Reports the load in the worst end-to-end path as detected though - NE internal measurements - - - - - - - -Speakman, et. al. Experimental [Page 78] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Rcvr Worst Path (OPT_CR_RX_WP). - - Reports the load in the worst end-to-end path as detected by - receivers - - Worst Receiver's NLA (OPT_CR_RCVR). - - The unicast address of the receiver that generated the worst - OPT_CR_RX_WP. - - OPT_CR MAY be appended only to NAKs. - - OPT-CR is network-significant. - -12.7.2. OPT_CRQST - Packet Extension Format - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |E| Option Type | Option Length |Reserved |F|OPX|U| L P R| - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - Option Type = 0x11 - - Option Length = 4 octets - - L OPT_CRQST_NEL bit : set indicates OPT_CR_NE_WL is being - requested - - P OPT_CRQST_NEP bit : set indicates OPT_CR_NE_WP is being - requested - - R OPT_CRQST_RXP bit : set indicates OPT_CR_RX_WP is being - requested - - OPT_CRQST MAY be appended only to SPMs. - - OPT-CRQST is network-significant. - -13. Appendix C - SPM Requests - -13.1. Introduction - - SPM Requests (SPMRs) MAY be used to solicit an SPM from a source in a - non-implosive way. The typical application is for late-joining - receivers to solicit SPMs directly from a source in order to be able - to NAK for missing packets without having to wait for a regularly - scheduled SPM from that source. - - - -Speakman, et. al. Experimental [Page 79] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -13.2. Overview - - Allowing for SPMR implosion protection procedures, a receiver MAY - unicast an SPMR to a source to solicit the most current session, - window, and path state from that source any time after the receiver - has joined the group. A receiver may learn the TSI and source to - which to direct the SPMR from any other PGM packet it receives in the - group, or by any other means such as from local configuration or - directory services. The receiver MUST use the usual SPM procedures - to glean the unicast address to which it should direct its NAKs from - the solicited SPM. - -13.3. Packet Contents - - This section just provides enough short-hand to make the Procedures - intelligible. For the full details of packet contents, please refer - to Packet Formats below. - -13.3.1. SPM Requests - - SPMRs are transmitted by receivers to solicit SPMs from a source. - - SPMs are unicast to a source and contain: - - SPMR_TSI the source-assigned TSI for the session to which the - SPMR corresponds - -13.4. Procedures - Sources - - A source MUST respond immediately to an SPMR with the corresponding - SPM rate limited to once per IHB_MIN per TSI. The corresponding SPM - matches SPM_TSI to SPMR_TSI and SPM_DPORT to SPMR_DPORT. - -13.5. Procedures - Receivers - - To moderate the potentially implosive behavior of SPMRs at least on a - densely populated subnet, receivers MUST use the following back-off - and suppression procedure based on multicasting the SPMR with a TTL - of 1 ahead of and in addition to unicasting the SPMR to the source. - The role of the multicast SPMR is to suppress the transmission of - identical SPMRs from the subnet. - - More specifically, before unicasting a given SPMR, receivers MUST - choose a random delay on SPMR_BO_IVL (~250 msecs) during which they - listen for a multicast of an identical SPMR. If a receiver does not - see a matching multicast SPMR within its chosen random interval, it - MUST first multicast its own SPMR to the group with a TTL of 1 before - then unicasting its own SPMR to the source. If a receiver does see a - - - -Speakman, et. al. Experimental [Page 80] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - matching multicast SPMR within its chosen random interval, it MUST - refrain from unicasting its SPMR and wait instead for the - corresponding SPM. - - In addition, receipt of the corresponding SPM within this random - interval SHOULD cancel transmission of an SPMR. - - In either case, the receiver MUST wait at least SPMR_SPM_IVL before - attempting to repeat the SPMR by choosing another delay on - SPMR_BO_IVL and repeating the procedure above. - - The corresponding SPMR matches SPMR_TSI to SPMR_TSI and SPMR_DPORT to - SPMR_DPORT. The corresponding SPM matches SPM_TSI to SPMR_TSI and - SPM_DPORT to SPMR_DPORT. - -13.6. SPM Requests - - SPMR: - - SPM Requests are sent by receivers to request the immediate - transmission of an SPM for the given TSI from a source. - - The network-header source address of an SPMR is the unicast NLA of - the entity that originates the SPMR. - - The network-header destination address of an SPMR is the unicast NLA - of the source from which the corresponding SPM is requested. - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Source Port | Destination Port | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Options | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Global Source ID ... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | ... Global Source ID | TSDU Length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Option Extensions when present ... - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... - - Source Port: - - SPMR_SPORT - - Data-Destination Port - - - - -Speakman, et. al. Experimental [Page 81] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Destination Port: - - SPMR_DPORT - - Data-Source Port, together with Global Source ID forms SPMR_TSI - - Type: - - SPMR_TYPE = 0x0C - - Global Source ID: - - SPMR_GSI - - Together with Source Port forms - - SPMR_TSI - -14. Appendix D - Poll Mechanism - -14.1. Introduction - - These procedures provide PGM network elements and sources with the - ability to poll their downstream PGM neighbors to solicit replies - in an implosion-controlled way. - - Both general polls and specific polls are possible. The former - provide a PGM (parent) node with a way to check if there are any - PGM (children) nodes connected to it, both network elements and - receivers, and to estimate their number. The latter may be used - by PGM parent nodes to search for nodes with specific properties - among its PGM children. An example of application for this is DLR - discovery. - - Polling is implemented using two additional PGM packets: - - POLL a Poll Request that PGM parent nodes multicast to the group to - perform the poll. Similarly to NCFs, POLL packets stop at the - first PGM node they reach, as they are not forwarded by PGM - network elements. - - POLR a Poll Response that PGM children nodes (either network elements - or receivers) use to reply to a Poll Request by addressing it - to the NLA of the interface from which the triggering POLL was - sent. - - - - - - -Speakman, et. al. Experimental [Page 82] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - The polling mechanism dictates that PGM children nodes that receive a - POLL packet reply to it only if certain conditions are satisfied and - ignore the POLL otherwise. Two types of condition are possible: a - random condition that defines a probability of replying for the - polled child, and a deterministic condition. Both the random - condition and the deterministic condition are controlled by the - polling PGM parent node by specifying the probability of replying and - defining the deterministic condition(s) respectively. Random-only - poll, deterministic-only poll or a combination of the two are - possible. - - The random condition in polls allows the prevention of implosion of - replies by controlling their number. Given a probability of replying - P and assuming that each receiver makes an independent decision, the - number of expected replies to a poll is P*N where N is the number of - PGM children relative to the polling PGM parent. The polling node - can control the number of expected replies by specifying P in the - POLL packet. - -14.2. Packet Contents - - This section just provides enough short-hand to make the Procedures - intelligible. For the full details of packet contents, please refer - to Packet Formats below. - -14.2.1. POLL (Poll Request) - - POLL_SQN a sequence number assigned sequentially by the polling - parent in unit increments and scoped by POLL_PATH and - the TSI of the session. - - POLL_ROUND a poll round sequence number. Multiple poll rounds - are possible within a POLL_SQN. - - POLL_S_TYPE the sub-type of the poll request - - POLL_PATH the network-layer address (NLA) of the interface on - the PGM network element or source on which the POLL is - transmitted - - POLL_BO_IVL the back-off interval that MUST be used to compute the - random back-off time to wait before sending the - response to a poll. POLL_BO_IVL is expressed in - microseconds. - - POLL_RAND a random string used to implement the randomness in - replying - - - - -Speakman, et. al. Experimental [Page 83] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - POLL_MASK a bit-mask used to determine the probability of random - replies - - Poll request MAY also contain options which specify deterministic - conditions for the reply. No options are currently defined. - -14.2.2. POLR (Poll Response) - - POLR_SQN POLL_SQN of the poll request for which this is a reply - - POLR_ROUND POLL_ROUND of the poll request for which this is a - reply - - Poll response MAY also contain options. No options are currently - defined. - -14.3. Procedures - General - -14.3.1. General Polls - - General Polls may be used to check for and count PGM children that - are 1 PGM hop downstream of an interface of a given node. They have - POLL_S_TYPE equal to PGM_POLL_GENERAL. PGM children that receive a - general poll decide whether to reply to it only based on the random - condition present in the POLL. - - To prevent response implosion, PGM parents that initiate a general - poll SHOULD establish the probability of replying to the poll, P, so - that the expected number of replies is contained. The expected - number of replies is N * P, where N is the number of children. To be - able to compute this number, PGM parents SHOULD already have a rough - estimate of the number of children. If they do not have a recent - estimate of this number, they SHOULD send the first poll with a very - low probability of replying and increase it in subsequent polls in - order to get the desired number of replies. - - To prevent poll-response implosion caused by a sudden increase in the - children population occurring between two consecutive polls with - increasing probability of replying, PGM parents SHOULD use poll - rounds. Poll rounds allow PGM parents to "freeze" the size of the - children population when they start a poll and to maintain it - constant across multiple polls (with the same POLL_SQN but different - POLL_ROUND). This works by allowing PGM children to respond to a - poll only if its POLL_ROUND is zero or if they have previously - received a poll with the same POLL_SQN and POLL_ROUND equal to zero. - - - - - - -Speakman, et. al. Experimental [Page 84] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - In addition to this PGM children MUST observe a random back-off in - replying to a poll. This spreads out the replies in time and allows - a PGM parent to abort the poll if too many replies are being - received. To abort an ongoing poll a PGM parent MUST initiate - another poll with different POLL_SQN. PGM children that receive a - POLL MUST cancel any pending reply for POLLs with POLL_SQN different - from the one of the last POLL received. - - For a given poll with probability of replying P, a PGM parent - estimates the number of children as M / P, where M is the number of - responses received. PGM parents SHOULD keep polling periodically and - use some average of the result of recent polls as their estimate for - the number of children. - -14.3.2. Specific Polls - - Specific polls provide a way to search for PGM children that comply - to specific requisites. As an example specific poll could be used to - search for down-stream DLRs. A specific poll is characterized by a - POLL_S_TYPE different from PGM_POLL_GENERAL. PGM children decide - whether to reply to a specific poll or not based on the POLL_S_TYPE, - on the random condition and on options possibly present in the POLL. - The way options should be interpreted is defined by POLL_S_TYPE. The - random condition MUST be interpreted as an additional condition to be - satisfied. To disable the random condition PGM parents MUST specify - a probability of replying P equal to 1. - - PGM children MUST ignore a POLL packet if they do not understand - POLL_S_TYPE. Some specific POLL_S_TYPE may also require that the - children ignore a POLL if they do not fully understand all the PGM - options present in the packet. - -14.4. Procedures - PGM Parents (Sources or Network Elements) - - A PGM parent (source or network element), that wants to poll the - first PGM-hop children connected to one of its outgoing interfaces - MUST send a POLL packet on that interface with: - - POLL_SQN equal to POLL_SQN of the last POLL sent incremented by - one. If poll rounds are used, this must be equal to - POLL_SQN of the last group of rounds incremented by - one. - - POLL_ROUND The round of the poll. If the poll has a single - round, this must be zero. If the poll has multiple - rounds, this must be one plus the last POLL_ROUND for - the same POLL_SQN, or zero if this is the first round - within this POLL_SQN. - - - -Speakman, et. al. Experimental [Page 85] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - POLL_S_TYPE the type of the poll. For general poll use - PGM_POLL_GENERAL - - POLL_PATH set to the NLA of the outgoing interface - - POLL_BO_IVL set to the wanted reply back-off interval. As far as - the choice of this is concerned, using NAK_BO_IVL is - usually a conservative option, however a smaller value - MAY be used, if the number of expected replies can be - determined with a good confidence or if a - conservatively low probability of reply (P) is being - used (see POLL_MASK next). When the number of - expected replies is unknown, a large POLL_BO_IVL - SHOULD be used, so that the poll can be effectively - aborted if the number of replies being received is too - large. - - POLL_RAND MUST be a random string re-computed each time a new - poll is sent on a given interface - - POLL_MASK determines the probability of replying, P, according - to the relationship P = 1 / ( 2 ^ B ), where B is the - number of bits set in POLL_MASK [15]. If this is a - deterministic poll, B MUST be 0, i.e. POLL_MASK MUST - be a all-zeroes bit-mask. - - Nota Bene: POLLs transmitted by network elements MUST bear the - ODATA source's network-header source address, not the network - element's NLA. POLLs MUST also be transmitted with the IP - - Router Alert Option [6], to be allow PGM network element to - intercept them. - - A PGM parent that has started a poll by sending a POLL packet SHOULD - wait at least POLL_BO_IVL before starting another poll. During this - interval it SHOULD collect all the valid response (the one with - POLR_SQN and POLR_ROUND matching with the outstanding poll) and - process them at the end of the collection interval. - - A PGM parent SHOULD observe the rules mentioned in the description of - general procedures, to prevent implosion of response. These rules - should in general be observed both for generic polls and specific - polls. The latter however can be performed using deterministic poll - (with no implosion prevention) if the expected number of replies is - known to be small. A PGM parent that issue a generic poll with the - intent of estimating the children population size SHOULD use poll - rounds to "freeze" the children that are involved in the measure - process. This allows the sender to "open the door wider" across - - - -Speakman, et. al. Experimental [Page 86] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - subsequent rounds (by increasing the probability of response), - without fear of being flooded by late joiners. Note the use of - rounds has the drawback of introducing additional delay in the - estimate of the population size, as the estimate obtained at the end - of a round-group refers to the condition present at the time of the - first round. - - A PGM parent that has started a poll SHOULD monitor the number of - replies during the collection phase. If this become too large, the - PGM parent SHOULD abort the poll by immediately starting a new poll - (different POLL_SQN) and specifying a very low probability of - replying. - - - When polling is being used to estimate the receiver population for - the purpose of calculating NAK_BO_IVL, OPT_NAK_BO_IVL (see 16.4.1 - below) MUST be appended to SPMs, MAY be appended to NCFs and POLLs, - and in all cases MUST have NAK_BO_IVL_SQN set to POLL_SQN of the most - recent complete round of polls, and MUST bear that round's - corresponding derived value of NAK_BAK_IVL. In this way, - OPT_NAK_BO_IVL provides a current value for NAK_BO_IVL at the same - time as information is being gathered for the calculation of a future - value of NAK_BO_IVL. - -14.5. Procedures - PGM Children (Receivers or Network Elements) - - PGM receivers and network elements MUST compute a 32-bit random node - identifier (RAND_NODE_ID) at startup time. When a PGM child - (receiver or network element) receives a POLL it MUST use its - RAND_NODE_ID to match POLL_RAND of incoming POLLs. The match is - limited to the bits specified by POLL_MASK. If the incoming POLL - contain a POLL_MASK made of all zeroes, the match is successful - despite the content of POLL_RAND (deterministic reply). If the match - fails, then the receiver or network element MUST discard the POLL - without any further action, otherwise it MUST check the fields - POLL_ROUND, POLL_S_TYPE and any PGM option included in the POLL to - determine whether it SHOULD reply to the poll. - - If POLL_ROUND is non-zero and the PGM receiver has not received a - previous poll with the same POLL_SQN and a zero POLL_ROUND, it MUST - discard the poll without further action. - - If POLL_S_TYPE is equal to PGM_POLL_GENERAL, the PGM child MUST - schedule a reply to the POLL despite the presence of PGM options on - the POLL packet. - - - - - - -Speakman, et. al. Experimental [Page 87] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - If POLL_S_TYPE is different from PGM_POLL_GENERAL, the decision on - whether a reply should be scheduled depends on the actual type and on - the options possibly present in the POLL. - - If POLL_S_TYPE is unknown to the recipient of the POLL, it MUST NOT - reply and ignore the poll. Currently the only POLL_S_TYPE defined - are PGM_POLL_GENERAL and PGM_POLL_DLR. - - If a PGM receiver or network element has decided to reply to a POLL, - it MUST schedule the transmission of a single POLR at a random time - in the future. The random delay is chosen in the interval [0, - POLL_BO_IVL]. POLL_BO_IVL is the one contained in the POLL received. - When this timer expires, it MUST send a POLR using POLL_PATH of the - received POLL as destination address. POLR_SQN MUST be equal to - POLL_SQN and POLR_ROUND must be equal to POLL_ROUND. The POLR MAY - contain PGM options according to the semantic of POLL_S_TYPE or the - semantic of PGM options possibly present in the POLL. If POLL_S_TYPE - is PGM_POLL_GENERAL no option is REQUIRED. - - A PGM receiver or network element MUST cancel any pending - transmission of POLRs if a new POLL is received with POLL_SQN - different from POLR_SQN of the poll that scheduled POLRs. - -14.6. Constant Definition - - The POLL_S_TYPE values currently defined are: - - PGM_POLL_GENERAL 0 - - PGM_POLL_DLR 1 - -14.7. Packet Formats - - The packet formats described in this section are transport-layer - headers that MUST immediately follow the network-layer header in the - packet. - - The descriptions of Data-Source Port, Data-Destination Port, Options, - Checksum, Global Source ID (GSI), and TSDU Length are those provided - in Section 8. - -14.7.1. Poll Request - - POLL are sent by PGM parents (sources or network elements) to - initiate a poll among their first PGM-hop children. - - - - - - -Speakman, et. al. Experimental [Page 88] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - POLLs are sent to the ODATA multicast group. The network-header - source address of a POLL is the ODATA source's NLA. POLL MUST be - transmitted with the IP Router Alert Option. - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Source Port | Destination Port | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Options | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Global Source ID ... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | ... Global Source ID | TSDU Length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | POLL's Sequence Number | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | POLL's Round | POLL's Sub-type | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | NLA AFI | Reserved | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Path NLA ... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ - | POLL's Back-off Interval | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Random String | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Matching Bit-Mask | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Option Extensions when present ... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - Source Port: - - POLL_SPORT - - Data-Source Port, together with POLL_GSI forms POLL_TSI - - Destination Port: - - POLL_DPORT - - Data-Destination Port - - Type: - - POLL_TYPE = 0x01 - - - - -Speakman, et. al. Experimental [Page 89] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Global Source ID: - - POLL_GSI - - Together with POLL_SPORT forms POLL_TSI - - POLL's Sequence Number - - POLL_SQN - - The sequence number assigned to the POLL by the originator. - - POLL's Round - - POLL_ROUND - - The round number within the poll sequence number. - - POLL's Sub-type - - POLL_S_TYPE - - The sub-type of the poll request. - - Path NLA: - - POLL_PATH - - The NLA of the interface on the source or network element on which - this POLL was forwarded. - - POLL's Back-off Interval - - POLL_BO_IVL - - The back-off interval used to compute a random back-off for the - reply, expressed in microseconds. - - Random String - - POLL_RAND - - A random string used to implement the random condition in - replying. - - - - - - - -Speakman, et. al. Experimental [Page 90] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Matching Bit-Mask - - POLL_MASK - - A bit-mask used to determine the probability of random replies. - -14.7.2. Poll Response - - POLR are sent by PGM children (receivers or network elements) to - reply to a POLL. - - The network-header source address of a POLR is the unicast NLA of the - entity that originates the POLR. The network-header destination - address of a POLR is initialized by the originator of the POLL to the - unicast NLA of the upstream PGM element (source or network element) - known from the POLL that triggered the POLR. - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Source Port | Destination Port | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Options | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Global Source ID ... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | ... Global Source ID | TSDU Length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | POLR's Sequence Number | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | POLR's Round | reserved | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Option Extensions when present ... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - Source Port: - - POLR_SPORT - - Data-Destination Port - - Destination Port: - - POLR_DPORT - - Data-Source Port, together with Global Source ID forms POLR_TSI - - - - - -Speakman, et. al. Experimental [Page 91] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Type: - - POLR_TYPE = 0x02 - - Global Source ID: - - POLR_GSI - - Together with POLR_DPORT forms POLR_TSI - - POLR's Sequence Number - - POLR_SQN - - The sequence number (POLL_SQN) of the POLL packet for which this - is a reply. - - POLR's Round - - POLR_ROUND - - The round number (POLL_ROUND) of the POLL packet for which this is - a reply. - -15. Appendix E - Implosion Prevention - -15.1. Introduction - - These procedures are intended to prevent NAK implosion and to limit - its extent in case of the loss of all or part of the suppressing - multicast distribution tree. They also provide a means to adaptively - tune the NAK back-off interval, NAK_BO_IVL. - - The PGM virtual topology is established and refreshed by SPMs. - Between one SPM and the next, PGM nodes may have an out-of-date view - of the PGM topology due to multicast routing changes, flapping, or a - link/router failure. If any of the above happens relative to a PGM - parent node, a potential NAK implosion problem arises because the - parent node is unable to suppress the generation of duplicate NAKs as - it cannot reach its children using NCFs. The procedures described - below introduce an alternative way of performing suppression in this - case. They also attempt to prevent implosion by adaptively tuning - NAK_BO_IVL. - - - - - - - - -Speakman, et. al. Experimental [Page 92] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -15.2. Tuning NAK_BO_IVL - - Sources and network elements continuously monitor the number of - duplicated NAKs received and use this observation to tune the NAK - back-off interval (NAK_BO_IVL) for the first PGM-hop receivers - connected to them. Receivers learn the current value of NAK_BO_IVL - through OPT_NAK_BO_IVL appended to NCFs or SPMs. - -15.2.1. Procedures - Sources and Network Elements - - For each TSI, sources and network elements advertise the value of - NAK_BO_IVL that their first PGM-hop receivers should use. They - advertise a separate value on all the outgoing interfaces for the TSI - and keep track of the last values advertised. - - For each interface and TSI, sources and network elements count the - number of NAKs received for a specific repair state (i.e., per - sequence number per TSI) from the time the interface was first added - to the repair state list until the time the repair state is - discarded. Then they use this number to tune the current value of - NAK_BO_IVL as follows: - - Increase the current value NAK_BO_IVL when the first duplicate NAK - is received for a given SQN on a particular interface. - - Decrease the value of NAK_BO_IVL if no duplicate NAKs are received on - a particular interface for the last NAK_PROBE_NUM measurements where - each measurement corresponds to the creation of a new repair state. - - An upper and lower limit are defined for the possible value of - NAK_BO_IVL at any time. These are NAK_BO_IVL_MAX and NAK_BO_IVL_MIN - respectively. The initial value that should be used as a starting - point to tune NAK_BO_IVL is NAK_BO_IVL_DEFAULT. The policies - RECOMMENDED for increasing and decreasing NAK_BO_IVL are multiplying - by two and dividing by two respectively. - - Sources and network elements advertise the current value of - NAK_BO_IVL through the OPT_NAK_BO_IVL that they append to NCFs. They - MAY also append OPT_NAK_BO_IVL to outgoing SPMs. - - In order to avoid forwarding the NAK_BO_IVL advertised by the parent, - network elements must be able to recognize OPT_NAK_BO_IVL. Network - elements that receive SPMs containing OPT_NAK_BO_IVL MUST either - remove the option or over-write its content (NAK_BO_IVL) with the - current value of NAK_BO_IVL for the outgoing interface(s), before - forwarding the SPMs. - - - - - -Speakman, et. al. Experimental [Page 93] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Sources MAY advertise the value of NAK_BO_IVL_MAX and NAK_BO_IVL_MIN - to the session by appending a OPT_NAK_BO_RNG to SPMs. - -15.2.2. Procedures - Receivers - - Receivers learn the value of NAK_BO_IVL to use through the option - OPT_NAK_BO_IVL, when this is present in NCFs or SPMs. A value for - NAK_BO_IVL learned from OPT_NAK_BO_IVL MUST NOT be used by a receiver - unless either NAK_BO_IVL_SQN is zero, or the receiver has seen - POLL_RND == 0 for POLL_SQN =< NAK_BO_IVL_SQN within half the sequence - number space. The initial value of NAK_BO_IVL is set to - NAK_BO_IVL_DEFAULT. - - Receivers that receive an SPM containing OPT_NAK_BO_RNG MUST use its - content to set the local values of NAK_BO_IVL_MAX and NAK_BO_IVL_MIN. - -15.2.3. Adjusting NAK_BO_IVL in the absence of NAKs - - Monitoring the number of duplicate NAKs provides a means to track - indirectly the change in the size of first PGM-hop receiver - population and adjust NAK_BO_IVL accordingly. Note that the number - of duplicate NAKs for a given SQN is related to the number of first - PGM-hop children that scheduled (or forwarded) a NAK and not to the - absolute number of first PGM-hop children. This mechanism, however, - does not work in the absence of packet loss, hence a large number of - duplicate NAKs is possible after a period without NAKs, if many new - receivers have joined the session in the meanwhile. To address this - issue, PGM Sources and network elements SHOULD periodically poll the - number of first PGM-hop children using the "general poll" procedures - described in Appendix D. If the result of the polls shows that the - population size has increased significantly during a period without - NAKs, they SHOULD increase NAK_BO_IVL as a safety measure. - -15.3. Containing Implosion in the Presence of Network Failures - -15.3.1. Detecting Network Failures - - In some cases PGM (parent) network elements can promptly detect the - loss of all or part of the suppressing multicast distribution tree - (due to network failures or route changes) by checking their - multicast connectivity, when they receive NAKs. In some other cases - this is not possible as the connectivity problem might occur at some - other non-PGM node downstream or might take time to reflect in the - multicast routing table. To address these latter cases, PGM uses a - simple heuristic: a failure is assumed for a TSI when the count of - duplicated NAKs received for a repair state reaches the value - DUP_NAK_MAX in one of the interfaces. - - - - -Speakman, et. al. Experimental [Page 94] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -15.3.2. Containing Implosion - - When a PGM source or network element detects or assumes a failure for - which it looses multicast connectivity to down-stream PGM agents - (either receivers or other network elements), it sends unicast NCFs - to them in response to NAKs. Downstream PGM network elements which - receive unicast NCFs and have multicast connectivity to the multicast - session send special SPMs to prevent further NAKs until a regular SPM - sent by the source refreshes the PGM tree. - - Procedures - Sources and Network Elements - - PGM sources or network elements which detect or assume a failure that - prevents them from reaching down-stream PGM agents through multicast - NCFs revert to confirming NAKs through unicast NCFs for a given TSI - on a given interface. If the PGM agent is the source itself, than it - MUST generate an SPM for the TSI, in addition to sending the unicast - NCF. - - Network elements MUST keep using unicast NCFs until they receive a - regular SPM from the source. - - When a unicast NCF is sent for the reasons described above, it MUST - contain the OPT_NBR_UNREACH option and the OPT_PATH_NLA option. - OPT_NBR_UNREACH indicates that the sender is unable to use multicast - to reach downstream PGM agents. OPT_PATH_NLA carries the network - layer address of the NCF sender, namely the NLA of the interface - leading to the unreachable subtree. - - When a PGM network element receives an NCF containing the - OPT_NBR_UNREACH option, it MUST ignore it if OPT_PATH_NLA specifies - an upstream neighbour different from the one currently known to be - the upstream neighbor for the TSI. Assuming the network element - matches the OPT_PATH_NLA of the upstream neighbour address, it MUST - stop forwarding NAKs for the TSI until it receives a regular SPM for - the TSI. In addition, it MUST also generate a special SPM to prevent - downstream receivers from sending more NAKs. This special SPM MUST - contain the OPT_NBR_UNREACH option and SHOULD have a SPM_SQN equal to - SPM_SQN of the last regular SPM forwarded. The OPT_NBR_UNREACH - option invalidates the windowing information in SPMs (SPM_TRAIL and - SPM_LEAD). The PGM network element that adds the OPT_NBR_UNREACH - option SHOULD invalidate the windowing information by setting - SPM_TRAIL to 0 and SPM_LEAD to 0x80000000. - - PGM network elements which receive an SPM containing the - OPT_NBR_UNREACH option and whose SPM_PATH matches the currently known - PGM parent, MUST forward them in the normal way and MUST stop - - - - -Speakman, et. al. Experimental [Page 95] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - forwarding NAKs for the TSI until they receive a regular SPM for the - TSI. If the SPM_PATH does not match the currently known PGM parent, - the SPM containing the OPT_NBR_UNREACH option MUST be ignored. - - Procedures - Receivers - - PGM receivers which receive either an NCF or an SPM containing the - OPT_NBR_UNREACH option MUST stop sending NAKs until a regular SPM is - received for the TSI. - - On reception of a unicast NCF containing the OPT_NBR_UNREACH option - receivers MUST generate a multicast copy of the packet with TTL set - to one on the RPF interface for the data source. This will prevent - other receivers in the same subnet from generating NAKs. - - Receivers MUST ignore windowing information in SPMs which contain the - OPT_NBR_UNREACH option. - - Receivers MUST ignore NCFs containing the OPT_NBR_UNREACH option if - the OPT_PATH_NLA specifies a neighbour different than the one - currently know to be the PGM parent neighbour. Similarly receivers - MUST ignore SPMs containing the OPT_NBR_UNREACH option if SPM_PATH - does not match the current PGM parent. - -15.4. Packet Formats - -15.4.1. OPT_NAK_BO_IVL - Packet Extension Format - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |E| Option Type | Option Length |Reserved |F|OPX|U| | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | NAK Back-Off Interval | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | NAK Back-Off Interval SQN | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - Option Type = 0x04 - - NAK Back-Off Interval - - The value of NAK-generation Back-Off Interval in microseconds. - - - - - - - - -Speakman, et. al. Experimental [Page 96] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - NAK Back-Off Interval Sequence Number - - The POLL_SQN to which this value of NAK_BO_IVL corresponds. Zero - is reserved and means NAK_BO_IVL is NOT being determined through - polling (see Appendix D) and may be used immediately. Otherwise, - NAK_BO_IVL MUST NOT be used unless the receiver has also seen - POLL_ROUND = 0 for POLL_SQN =< NAK_BO_IVL_SQN within half the - sequence number space. - - OPT_NAK_BO_IVL MAY be appended to NCFs, SPMs, or POLLs. - - OPT_NAK_BO_IVL is network-significant. - -15.4.2. OPT_NAK_BO_RNG - Packet Extension Format - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |E| Option Type | Option Length |Reserved |F|OPX|U| | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Maximum NAK Back-Off Interval | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Minimum NAK Back-Off Interval | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - Option Type = 0x05 - - Maximum NAK Back-Off Interval - - The maximum value of NAK-generation Back-Off Interval in - microseconds. - - Minimum NAK Back-Off Interval - - The minimum value of NAK-generation Back-Off Interval in - microseconds. - - OPT_NAK_BO_RNG MAY be appended to SPMs. - - OPT_NAK_BO_RNG is network-significant. - -15.4.3. OPT_NBR_UNREACH - Packet Extension Format - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |E| Option Type | Option Length |Reserved |F|OPX|U| | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - - -Speakman, et. al. Experimental [Page 97] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Option Type = 0x0B - - When present in SPMs, it invalidates the windowing information. - - OPT_NBR_UNREACH MAY be appended to SPMs and NCFs. - - OPT_NBR_UNREACH is network-significant. - -15.4.4. OPT_PATH_NLA - Packet Extension Format - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |E| Option Type | Option Length |Reserved |F|OPX|U| | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Path NLA | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - Option Type = 0x0C - - Path NLA - - The NLA of the interface on the originating PGM network element - that it uses to send multicast SPMs to the recipient of the packet - containing this option. - - OPT_PATH_NLA MAY be appended to NCFs. - - OPT_PATH_NLA is network-significant. - -16. Appendix F - Transmit Window Example - - Nota Bene: The concept of and all references to the increment - window (TXW_INC) and the window increment (TXW_ADV_SECS) - throughout this document are for illustrative purposes only. They - provide the shorthand with which to describe the concept of - advancing the transmit window without also implying any particular - implementation or policy of advancement. - - The size of the transmit window in seconds is simply TXW_SECS. The - size of the transmit window in bytes (TXW_BYTES) is (TXW_MAX_RTE * - TXW_SECS). The size of the transmit window in sequence numbers - (TXW_SQNS) is (TXW_BYTES / bytes-per-packet). - - The fraction of the transmit window size (in seconds of data) by - which the transmit window is advanced (TXW_ADV_SECS) is called the - window increment. The trailing (oldest) such fraction of the - transmit window itself is called the increment window. - - - -Speakman, et. al. Experimental [Page 98] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - In terms of sequence numbers, the increment window is the range of - sequence numbers that will be the first to be expired from the - transmit window. The trailing (or left) edge of the increment window - is just TXW_TRAIL, the trailing (or left) edge of the transmit - window. The leading (or right) edge of the increment window - (TXW_INC) is defined as one less than the sequence number of the - first data packet transmitted by the source TXW_ADV_SECS after - transmitting TXW_TRAIL. - - A data packet is described as being "in" the transmit or increment - window, respectively, if its sequence number is in the range defined - by the transmit or increment window, respectively. - - The transmit window is advanced across the increment window by the - source when it increments TXW_TRAIL to TXW_INC. When the transmit - window is advanced across the increment window, the increment window - is emptied (i.e., TXW_TRAIL is momentarily equal to TXW_INC), begins - to refill immediately as transmission proceeds, is full again - TXW_ADV_SECS later (i.e., TXW_TRAIL is separated from TXW_INC by - TXW_ADV_SECS of data), at which point the transmit window is advanced - again, and so on. - -16.1. Advancing across the Increment Window - - In anticipation of advancing the transmit window, the source starts a - timer TXW_ADV_IVL_TMR which runs for time period TXW_ADV_IVL. - TXW_ADV_IVL has a value in the range (0, TXW_ADV_SECS). The value - MAY be configurable or MAY be determined statically by the strategy - used for advancing the transmit window. - - When TXW_ADV_IVL_TMR is running, a source MAY reset TXW_ADV_IVL_TMR - if NAKs are received for packets in the increment window. In - addition, a source MAY transmit RDATA in the increment window with - priority over other data within the transmit window. - - When TXW_ADV_IVL_TMR expires, a source SHOULD advance the trailing - edge of the transmit window from TXW_TRAIL to TXW_INC. - - Once the transmit window is advanced across the increment window, - SPM_TRAIL, OD_TRAIL and RD_TRAIL are set to the new value of - TXW_TRAIL in all subsequent transmitted packets, until the next - window advancement. - - PGM does not constrain the strategies that a source may use for - advancing the transmit window. The source MAY implement any scheme - or number of schemes. Three suggested strategies are outlined here. - - - - - -Speakman, et. al. Experimental [Page 99] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Consider the following example: - - Assuming a constant transmit rate of 128kbps and a constant data - packet size of 1500 bytes, if a source maintains the past 30 - seconds of data for repair and increments its transmit window in 5 - second increments, then - - TXW_MAX_RTE = 16kBps - TXW_ADV_SECS = 5 seconds, - TXW_SECS = 35 seconds, - TXW_BYTES = 560kB, - TXW_SQNS = 383 (rounded up), - - and the size of the increment window in sequence numbers - (TXW_MAX_RTE * TXW_ADV_SECS / 1500) = 54 (rounded down). - - Continuing this example, the following is a diagram of the transmit - window and the increment window therein in terms of sequence numbers. - - - TXW_TRAIL TXW_LEAD - | | - | | - |--|--------------- Transmit Window -------------|----| - v | | v - v v - n-1 | n | n+1 | ... | n+53 | n+54 | ... | n+381 | n+382 | n+383 - ^ - ^ | ^ - |--- Increment Window|---| - | - | - TXW_INC - - So the values of the sequence numbers defining these windows are: - - TXW_TRAIL = n - TXW_INC = n+53 - TXW_LEAD = n+382 - - Nota Bene: In this example the window sizes in terms of sequence - numbers can be determined only because of the assumption of a - constant data packet size of 1500 bytes. When the data packet - sizes are variable, more or fewer sequence numbers MAY be consumed - transmitting the same amount (TXW_BYTES) of data. - - So, for a given transport session identified by a TSI, a source - maintains: - - - -Speakman, et. al. Experimental [Page 100] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - TXW_MAX_RTE a maximum transmit rate in kBytes per second, the - cumulative transmit rate of some combination of SPMs, - ODATA, and RDATA depending on the transmit window - advancement strategy - - TXW_TRAIL the sequence number defining the trailing edge of the - transmit window, the sequence number of the oldest - data packet available for repair - - TXW_LEAD the sequence number defining the leading edge of the - transmit window, the sequence number of the most - recently transmitted ODATA packet - - TXW_INC the sequence number defining the leading edge of the - increment window, the sequence number of the most - recently transmitted data packet amongst those that - will expire upon the next increment of the transmit - window - - PGM does not constrain the strategies that a source may use for - advancing the transmit window. A source MAY implement any scheme or - number of schemes. This is possible because a PGM receiver must obey - the window provided by the source in its packets. Three strategies - are suggested within this document. - - In the first, called "Advance with Time", the transmit window - maintains the last TXW_SECS of data in real-time, regardless of - whether any data was sent in that real time period or not. The - actual number of bytes maintained at any instant in time will vary - between 0 and TXW_BYTES, depending on traffic during the last - TXW_SECS. In this case, TXW_MAX_RTE is the cumulative transmit rate - of SPMs and ODATA. - - In the second, called "Advance with Data", the transmit window - maintains the last TXW_BYTES bytes of data for repair. That is, it - maintains the theoretical maximum amount of data that could be - transmitted in the time period TXW_SECS, regardless of when they were - transmitted. In this case, TXW_MAX_RTE is the cumulative transmit - rate of SPMs, ODATA, and RDATA. - - The third strategy leaves control of the window in the hands of the - application. The API provided by a source implementation for this, - could allow the application to control the window in terms of APDUs - and to manually step the window. This gives a form of Application - Level Framing (ALF). In this case, TXW_MAX_RTE is the cumulative - transmit rate of SPMs, ODATA, and RDATA. - - - - - -Speakman, et. al. Experimental [Page 101] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -16.2. Advancing with Data - - In the first strategy, TXW_MAX_RTE is calculated from SPMs and both - ODATA and RDATA, and NAKs reset TXW_ADV_IVL_TMR. In this mode of - operation the transmit window maintains the last TXW_BYTES bytes of - data for repair. That is, it maintains the theoretical maximum - amount of data that could be transmitted in the time period TXW_SECS. - This means that the following timers are not treated as real-time - timers, instead they are "data driven". That is, they expire when - the amount of data that could be sent in the time period they define - is sent. They are the SPM ambient time interval, TXW_ADV_SECS, - TXW_SECS, TXW_ADV_IVL, TXW_ADV_IVL_TMR and the join interval. Note - that the SPM heartbeat timers still run in real-time. - - While TXW_ADV_IVL_TMR is running, a source uses the receipt of a NAK - for ODATA within the increment window to reset timer TXW_ADV_IVL_TMR - to TXW_ADV_IVL so that transmit window advancement is delayed until - no NAKs for data in the increment window are seen for TXW_ADV_IVL - seconds. If the transmit window should fill in the meantime, further - transmissions would be suspended until the transmit window can be - advanced. - - A source MUST advance the transmit window across the increment window - only upon expiry of TXW_ADV_IVL_TMR. - - This mode of operation is intended for non-real-time, messaging - applications based on the receipt of complete data at the expense of - delay. - -16.3. Advancing with Time - - This strategy advances the transmit window in real-time. In this - mode of operation, TXW_MAX_RTE is calculated from SPMs and ODATA only - to maintain a constant data throughput rate by consuming extra - bandwidth for repairs. TXW_ADV_IVL has the value 0 which advances - the transmit window without regard for whether NAKs for data in the - increment window are still being received. - - In this mode of operation, all timers are treated as real-time - timers. - - This mode of operation is intended for real-time, streaming - applications based on the receipt of timely data at the expense of - completeness. - - - - - - - -Speakman, et. al. Experimental [Page 102] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -16.4. Advancing under explicit application control - - Some applications may wish more explicit control of the transmit - window than that provided by the advance with data / time strategies - above. An implementation MAY provide this mode of operation and - allow an application to explicitly control the window in terms of - APDUs. - -17. Appendix G - Applicability Statement - - As stated in the introduction, PGM has been designed with a specific - class of applications in mind in recognition of the fact that a - general solution for reliable multicast has proven elusive. The - applicability of PGM is narrowed further, and perhaps more - significantly, by the prototypical nature of at least four of the - transport elements the protocol incorporates. These are congestion - control, router assist, local retransmission, and a programmatic API - for reliable multicast protocols of this class. At the same time as - standardization efforts address each of these elements individually, - this publication is intended to foster experimentation with these - elements in general, and to inform that standardization process with - results from practise. - - This section briefly describes some of the experimental aspects of - PGM and makes non-normative references to some examples of current - practise based upon them. - - At least 3 different approaches to congestion control can be explored - with PGM: a receiver-feedback based approach, a router-assist based - approach, and layer-coding based approach. The first is supported by - the negative acknowledgement mechanism in PGM augmented by an - application-layer acknowledgement mechanism. The second is supported - by the router exception processing mechanism in PGM. The third is - supported by the FEC mechanisms in PGM. An example of a receiver- - feedback based approach is provided in [16], and a proposal for a - router-assist based approach was proposed in [17]. Open issues for - the researchers include how do each of these approaches behave in the - presence of multiple competing sessions of the same discipline or of - different disciplines, TCP most notably; how do each of them behave - over a particular range of topologies, and over a particular range of - loads; and how do each of them scale as a function of the size of the - receiver population. - - Router assist has applications not just to implosion control and - retransmit constraint as described in this specification, but also to - congestion control as described above, and more generally to any - feature which may be enhanced by access to per-network-element state - and processing. The full range of these features is as yet - - - -Speakman, et. al. Experimental [Page 103] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - unexplored, but a general mechanism for providing router assist in a - transport-protocol independent way (GRA) is a topic of active - research [18]. That effort has been primarily informed by the router - assist component of PGM, and implementation and deployment experience - with PGM will continue to be fed back into the specification and - eventual standardization of GRA. Open questions facing the - researchers ([19], [20], [21]) include how router-based state scales - relative to the feature benefit obtained, how system-wide factors - (such as throughput and retransmit latency) vary relative to the - scale and topology of deployed router assistance, and how incremental - deployment considerations affect the tractability of router-assist - based features. Router assist may have additional implications in - the area of congestion control to the extent that it may be applied - in multi-group layered coding schemes to increase the granularity and - reduce the latency of receiver based congestion control. - - GRA itself explicitly incorporates elements of active networking, and - to the extent that the router assist component of PGM is reflected in - GRA, experimentation with the narrowly defined network-element - functionality of PGM will provide some of the first real world - experience with this promising if controversial technology. - - Local retransmission is not a new idea in general in reliable - multicast, but the specific approach taken in PGM of locating re- - transmitters on the distribution tree for the session, diverting - repair requests from network elements to the re-transmitters, and - then propagating repairs downward from the repair point on the - distribution tree raises interesting questions concerning where to - locate re-transmitters in a given topology, and how network elements - locate those re-transmitters and evaluate their efficiency relative - to other available sources of retransmissions, most notably the - source itself. This particular aspect of PGM, while fully specified, - has only been implemented on the network element side, and awaits a - host-side implementation before questions like these can be - addressed. - - PGM presents the opportunity to develop a programming API for - reliable multicast applications that reflects both those - applications' service requirements as well as the services provided - by PGM in support of those applications that may usefully be made - visible above the transport interface. At least a couple of host- - side implementations of PGM and a concomitant API have been developed - for research purposes ([22], [23]), and are available as open source - explicitly for the kind of experimentation described in this section. - - Perhaps the broadest experiment that PGM can enable in a community of - researchers using a reasonable scale experimental transport protocol - is simply in the definition, implementation, and deployment of IP - - - -Speakman, et. al. Experimental [Page 104] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - multicast applications for which the reliability provided by PGM is a - significant enabler. Experience with such applications will not just - illuminate the value of reliable multicast, but will also provoke - practical examination of and responses to the attendant policy issues - (such as peering, billing, access control, firewalls, NATs, etc.), - and, if successful, will ultimately encourage more wide spread - deployment of IP multicast itself. - -18. Abbreviations - - ACK Acknowledgment - AFI Address Family Indicator - ALF Application Level Framing - APDU Application Protocol Data Unit - ARQ Automatic Repeat reQuest - DLR Designated Local Repairer - GSI Globally Unique Source Identifier - FEC Forward Error Correction - MD5 Message-Digest Algorithm - MTU Maximum Transmission Unit - NAK Negative Acknowledgment - NCF NAK Confirmation - NLA Network Layer Address - NNAK Null Negative Acknowledgment - ODATA Original Data - POLL Poll Request - POLR Poll Response - RDATA Repair Data - RSN Receive State Notification - SPM Source Path Message - SPMR SPM Request - TG Transmission Group - TGSIZE Transmission Group Size - TPDU Transport Protocol Data Unit - TSDU Transport Service Data Unit - TSI Transport Session Identifier - TSN Transmit State Notification - - - - - - - - - - - - - - -Speakman, et. al. Experimental [Page 105] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -19. Acknowledgements - - The design and specification of PGM has been substantially influenced - by reviews and revisions provided by several people who took the time - to read and critique this document. These include, in alphabetical - order: - - Bob Albrightson - Joel Bion - Mark Bowles - Steve Deering - Tugrul Firatli - Dan Harkins - Dima Khoury - Gerard Newman - Dave Oran - Denny Page - Ken Pillay - Chetan Rai - Yakov Rekhter - Dave Rossetti - Paul Stirpe - Brian Whetten - Kyle York - -20. References - - [1] B. Whetten, T. Montgomery, S. Kaplan, "A High Performance - Totally Ordered Multicast Protocol", in "Theory and Practice in - Distributed Systems", Springer Verlag LCNS938, 1994. - - [2] S. Floyd, V. Jacobson, C. Liu, S. McCanne, L. Zhang, "A - Reliable Multicast Framework for Light-weight Sessions and - Application Level Framing", ACM Transactions on Networking, - November 1996. - - [3] J. C. Lin, S. Paul, "RMTP: A Reliable Multicast Transport - Protocol", ACM SIGCOMM August 1996. - - [4] Miller, K., Robertson, K., Tweedly, A. and M. White, "Multicast - File Transfer Protocol (MFTP) Specification", Work In Progress. - - [5] Deering, S., "Host Extensions for IP Multicasting", STD 5, RFC - 1112, August 1989. - - [6] Katz, D., "IP Router Alert Option", RFC 2113, February 1997. - - [7] C. Partridge, "Gigabit Networking", Addison Wesley 1994. - - - -Speakman, et. al. Experimental [Page 106] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - [8] H. W. Holbrook, S. K. Singhal, D. R. Cheriton, "Log-Based - Receiver-Reliable Multicast for Distributed Interactive - Simulation", ACM SIGCOMM 1995. - - [9] Rivest, R., "The MD5 Message-Digest Algorithm", RFC 1321, April - 1992. - - [10] Reynolds, J. and J. Postel, "Assigned Numbers", STD 2, RFC - 1700, October 1994. - - [11] J. Nonnenmacher, E. Biersack, D. Towsley, "Parity-Based Loss - Recovery for Reliable Multicast Transmission", ACM SIGCOMM - September 1997. - - [12] L. Rizzo, "Effective Erasure Codes for Reliable Computer - Communication Protocols", Computer Communication Review, April - 1997. - - [13] V. Jacobson, "Congestion Avoidance and Control", ACM SIGCOMM - August 1988. - - [14] Bradner, S., "Key words for use in RFCs to Indicate Requirement - Levels", BCP, 14, RFC 2119, March 1997. - - [15] J. Bolot, T. Turletti, I. Wakeman, "Scalable Feedback Control - for Multicast Video Distribution in the Internet", Proc. - ACM/Sigcomm 94, pp. 58-67. - - [16] L. Rizzo, "pgmcc: A TCP-friendly Single-Rate Multicast - Congestion Control Scheme", Proc. of ACM SIGCOMM August 2000. - - [17] M. Luby, L. Vicisano, T. Speakman. "Heterogeneous multicast - congestion control based on router packet filtering", RMT - working group, June 1999, Pisa, Italy. - - [18] Cain, B., Speakman, T. and D. Towsley, "Generic Router Assist - (GRA) Building Block, Motivation and Architecture", Work In - Progress. - - [19] C. Papadopoulos, and E. Laliotis,"Incremental Deployment of a - Router-assisted Reliable Multicast Scheme,", Proc. of Networked - Group Communications (NGC2000), Stanford University, Palo Alto, - CA. November 2000. - - - - - - - - -Speakman, et. al. Experimental [Page 107] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - [20] C. Papadopoulos, "RAIMS: an Architecture for Router-Assisted - Internet Multicast Services." Presented at ETH, Zurich, - Switzerland, October 23 2000. - - [21] J. Chesterfield, A. Diana, A. Greenhalgh, M. Lad, and M. Lim, - "A BSD Router Implementation of PGM", - http://www.cs.ucl.ac.uk/external/m.lad/rpgm/ - - [22] L. Rizzo, "A PGM Host Implementation for FreeBSD", - http://www.iet.unipi.it/~luigi/pgm.html - - [23] M. Psaltaki, R. Araujo, G. Aldabbagh, P. Kouniakis, and A. - Giannopoulos, "Pragmatic General Multicast (PGM) host - implementation for FreeBSD.", - http://www.cs.ucl.ac.uk/research/darpa/pgm/PGM_FINAL.html - -21. Authors' Addresses - - Tony Speakman - EMail: speakman@cisco.com - - Dino Farinacci - Procket Networks - 3850 North First Street - San Jose, CA 95134 - USA - EMail: dino@procket.com - - Steven Lin - Juniper Networks - 1194 N. Mathilda Ave. - Sunnyvale, CA 94086 - USA - EMail: steven@juniper.net - - Alex Tweedly - EMail: agt@cisco.com - - Nidhi Bhaskar - EMail: nbhaskar@cisco.com - - Richard Edmonstone - EMail: redmonst@cisco.com - - Rajitha Sumanasekera - EMail: rajitha@cisco.com - - - - - -Speakman, et. al. Experimental [Page 108] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Lorenzo Vicisano - Cisco Systems, Inc. - 170 West Tasman Drive, - San Jose, CA 95134 - USA - EMail: lorenzo@cisco.com - - Jon Crowcroft - Department of Computer Science - University College London - Gower Street - London WC1E 6BT - UK - EMail: j.crowcroft@cs.ucl.ac.uk - - Jim Gemmell - Microsoft Bay Area Research Center - 301 Howard Street, #830 - San Francisco, CA 94105 - USA - EMail: jgemmell@microsoft.com - - Dan Leshchiner - Tibco Software - 3165 Porter Dr. - Palo Alto, CA 94304 - USA - EMail: dleshc@tibco.com - - Michael Luby - Digital Fountain, Inc. - 39141 Civic Center Drive - Fremont CA 94538 - USA - EMail: luby@digitalfountain.com - - Todd L. Montgomery - Talarian Corporation - 124 Sherman Ave. - Morgantown, WV 26501 - USA - EMail: todd@talarian.com - - - - - - - - - -Speakman, et. al. Experimental [Page 109] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - - Luigi Rizzo - Dip. di Ing. dell'Informazione - Universita` di Pisa - via Diotisalvi 2 - 56126 Pisa - Italy - EMail: luigi@iet.unipi.it - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Speakman, et. al. Experimental [Page 110] - -RFC 3208 PGM Reliable Transport Protocol December 2001 - - -22. Full Copyright Statement - - Copyright (C) The Internet Society (2001). All Rights Reserved. - - This document and translations of it may be copied and furnished to - others, and derivative works that comment on or otherwise explain it - or assist in its implementation may be prepared, copied, published - and distributed, in whole or in part, without restriction of any - kind, provided that the above copyright notice and this paragraph are - included on all such copies and derivative works. However, this - document itself may not be modified in any way, such as by removing - the copyright notice or references to the Internet Society or other - Internet organizations, except as needed for the purpose of - developing Internet standards in which case the procedures for - copyrights defined in the Internet Standards process must be - followed, or as required to translate it into languages other than - English. - - The limited permissions granted above are perpetual and will not be - revoked by the Internet Society or its successors or assigns. - - This document and the information contained herein is provided on an - "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING - TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING - BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION - HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF - MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. - -Acknowledgement - - Funding for the RFC Editor function is currently provided by the - Internet Society. - - - - - - - - - - - - - - - - - - - -Speakman, et. al. Experimental [Page 111] - diff --git a/3rdparty/openpgm-svn-r1085/pgm/COPYING b/3rdparty/openpgm-svn-r1085/pgm/COPYING deleted file mode 100644 index 5ab7695..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/COPYING +++ /dev/null @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff --git a/3rdparty/openpgm-svn-r1085/pgm/INSTALL b/3rdparty/openpgm-svn-r1085/pgm/INSTALL deleted file mode 100644 index 3ba60e5..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/INSTALL +++ /dev/null @@ -1,4 +0,0 @@ -For building instructions, see: - - http://code.google.com/p/openpgm/wiki/OpenPgm3CReferenceBuildLibrary - diff --git a/3rdparty/openpgm-svn-r1085/pgm/LICENSE b/3rdparty/openpgm-svn-r1085/pgm/LICENSE deleted file mode 100644 index fabbeef..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Most of OpenPGM is licensed under the terms of the GNU Lesser Public License, -the LGPL, with a special exception: - - As a special exception, the copyright holders of this library give - you permission to link this library with independent modules to - produce an executable, regardless of the license terms of these - independent modules, and to copy and distribute the resulting - executable under terms of your choice, provided that you also meet, - for each linked independent module, the terms and conditions of the - license of that module. An independent module is a module which is - not derived from or based on this library. If you modify this - library, you must extend this exception to your version of the - library. - -See the file COPYING for details of the LGPL. - -Hence you should treat the libraries libpgm, libpgmsnmp, and libpgmhttp of -OpenPGM as being LGPL licensed, with the special exception. - diff --git a/3rdparty/openpgm-svn-r1085/pgm/README b/3rdparty/openpgm-svn-r1085/pgm/README deleted file mode 100644 index ece7aa1..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/README +++ /dev/null @@ -1,7 +0,0 @@ -OpenPGM is a library implementing the PGM reliable multicast network protocol. - -For more information about OpenPGM, see: - - http://openpgm.googlecode.com/ - - diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgm b/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgm deleted file mode 100644 index 8dd509d..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgm +++ /dev/null @@ -1,170 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script -# $Id$ - -import os; -Import('env') - -src = Split(""" - thread.c - mem.c - string.c - list.c - slist.c - queue.c - hashtable.c - messages.c - error.c - math.c - packet_parse.c - packet_test.c - sockaddr.c - time.c - if.c - getifaddrs.c - getnodeaddr.c - indextoaddr.c - indextoname.c - nametoindex.c - inet_network.c - md5.c - rand.c - gsi.c - tsi.c - txw.c - rxw.c - skbuff.c - socket.c - source.c - receiver.c - recv.c - engine.c - timer.c - net.c - rate_control.c - checksum.c - reed_solomon.c - galois_tables.c - wsastrerror.c - histogram.c -""") - -e = env.Clone(); -e.Append(CCFLAGS = '-DGETTEXT_PACKAGE=\'"pgm"\''); - -# Galois tables -e.Command ('galois_tables.c', 'galois_generator.pl', "perl $SOURCE > $TARGET"); - -# Version stamping -e.Command ('version.c', 'version_generator.py', "python $SOURCE > $TARGET"); -e.Depends ('version.c', src); -src += ['version.c']; - -e.StaticLibrary('libpgm', src); -e.StaticSharedLibrary('libpgm-pic', src); - -#----------------------------------------------------------------------------- -# unit testing - -if env['WITH_CHECK'] == 'true': - te = e.Clone(); - te.MergeFlags(env['GLIB_FLAGS']); - newCCFLAGS = []; - for flag in te['CCFLAGS']: - if ("-W" != flag[:2]) and ("-pedantic" != flag[:9]): - newCCFLAGS.append(flag); - te['CCFLAGS'] = newCCFLAGS; - te.ParseConfig ('pkg-config --cflags --libs check'); -# log dependencies - tlog = [ e.Object('messages.c'), - e.Object('thread.c'), - e.Object('galois_tables.c'), - e.Object('mem.c'), - e.Object('histogram.c'), - e.Object('string.c'), - e.Object('slist.c') - ]; -# framework - te.Program (['atomic_unittest.c']); - te.Program (['checksum_unittest.c'] + tlog); - te.Program (['error_unittest.c'] + tlog); - te.Program (['md5_unittest.c'] + tlog); - te.Program (['getifaddrs_unittest.c', - e.Object('error.c'), - e.Object('sockaddr.c'), - e.Object('list.c')] + tlog); - te.Program (['getnodeaddr_unittest.c', - e.Object('error.c'), - e.Object('sockaddr.c')] + tlog); - te.Program (['indextoaddr_unittest.c', - e.Object('error.c'), - e.Object('sockaddr.c')] + tlog); - te.Program (['inet_network_unittest.c', - e.Object('sockaddr.c')] + tlog); - te.Program (['rate_control_unittest.c'] + tlog); - te.Program (['reed_solomon_unittest.c'] + tlog); - te.Program (['time_unittest.c', - e.Object('error.c')] + tlog); -# collate - tframework = [ e.Object('checksum.c'), - e.Object('error.c'), - e.Object('galois_tables.c'), - e.Object('getifaddrs.c'), - e.Object('getnodeaddr.c'), - e.Object('hashtable.c'), - e.Object('histogram.c'), - e.Object('indextoaddr.c'), - e.Object('indextoname.c'), - e.Object('inet_network.c'), - e.Object('list.c'), - e.Object('math.c'), - e.Object('md5.c'), - e.Object('mem.c'), - e.Object('messages.c'), - e.Object('nametoindex.c'), - e.Object('queue.c'), - e.Object('rand.c'), - e.Object('rate_control.c'), - e.Object('reed_solomon.c'), - e.Object('slist.c'), - e.Object('sockaddr.c'), - e.Object('string.c'), - e.Object('thread.c'), - e.Object('time.c'), - e.Object('wsastrerror.c') - ]; -# library - te.Program (['txw_unittest.c', - e.Object('tsi.c'), - e.Object('skbuff.c')] + tframework); - te.Program (['rxw_unittest.c', - e.Object('tsi.c'), - e.Object('skbuff.c')] + tframework); - te.Program (['engine_unittest.c', - e.Object('version.c')] + tframework); - te.Program (['gsi_unittest.c', - e.Object('if.c')] + tframework); - te.Program (['tsi_unittest.c'] + tframework); - te.Program (['if_unittest.c'] + tframework); - te.Program (['socket_unittest.c', - e.Object('if.c'), - e.Object('tsi.c')] + tframework); -# te.Program (['source_unittest.c'] + tframework); -# te.Program (['receiver_unittest.c', -# e.Object('tsi.c')] + tframework); - te.Program (['recv_unittest.c', - e.Object('tsi.c'), - e.Object('gsi.c'), - e.Object('skbuff.c')] + tframework); - te.Program (['net_unittest.c'] + tframework); - te.Program (['timer_unittest.c'] + tframework); - te.Program (['packet_parse_unittest.c'] + tframework); - te.Program (['packet_test_unittest.c'] + tframework); - te.Program (['ip_unittest.c', - e.Object('if.c')] + tframework); -# performance tests - te.Program (['checksum_perftest.c', - e.Object('time.c'), - e.Object('error.c')] + tlog); - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgm89 b/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgm89 deleted file mode 100644 index dae46b9..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgm89 +++ /dev/null @@ -1,80 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script -# $Id$ - -import os; -import string; -Import('env') - -src = Split(""" - thread.c - mem.c - string.c - list.c - slist.c - queue.c - hashtable.c - messages.c - error.c - math.c - packet_parse.c - packet_test.c - sockaddr.c - time.c - if.c - getifaddrs.c - getnodeaddr.c - indextoaddr.c - indextoname.c - nametoindex.c - inet_network.c - md5.c - rand.c - gsi.c - tsi.c - txw.c - rxw.c - skbuff.c - socket.c - source.c - receiver.c - recv.c - engine.c - timer.c - net.c - rate_control.c - checksum.c - reed_solomon.c - galois_tables.c - wsastrerror.c - histogram.c -""") - -e = env.Clone(); -e.Append(CCFLAGS = '-DGETTEXT_PACKAGE=\'"pgm"\''); - -# Galois tables -e.Command ('galois_tables.c', 'galois_generator.pl', "perl $SOURCE > $TARGET"); - -# Version stamping -e.Command ('version.c', 'version_generator.py', "python $SOURCE > $TARGET"); -e.Depends ('version.c', src); -src += ['version.c']; - -# C89 degrading -c89source = Builder(action = 'perl -p -e "s/%z(u|d)/%l\\1/g" $SOURCE > $TARGET', - suffix = '.c89.c', - src_suffix = '.c', - single_source = 1); -e.Append(BUILDERS = {'C89Source' : c89source}) - -c89src = [] -for c99file in src: - c89file = c99file.replace('.c', '.c89.c'); - c89src += [ c89file ]; - e.C89Source(c99file); - -e.StaticLibrary('libpgm89', c89src); -e.StaticSharedLibrary('libpgm89-pic', c89src); - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmex b/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmex deleted file mode 100644 index b508a04..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmex +++ /dev/null @@ -1,18 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script -# $Id$ - -Import('env') - -e = env.Clone(); -e.MergeFlags(env['GLIB_FLAGS']); - -src = Split(""" - log.c - backtrace.c - signal.c -""") - -e.StaticLibrary('libpgmex', src); - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmhttp b/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmhttp deleted file mode 100644 index 9824b3c..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmhttp +++ /dev/null @@ -1,53 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script -# $Id$ - -Import('env') - -e = env.Clone() -e.Append(CCFLAGS = '-DGETTEXT_PACKAGE=\'"pgm-http"\''); - -# add binary tree to include path to find embedded htdocs -e.Append(CPPPATH = ['.']); - -src = Split(""" - http.c -""") - -htdocs = Split(""" - htdocs/404.html - htdocs/base.css - htdocs/robots.txt - htdocs/xhtml10_strict.doctype -""") - -pgmhttp = e.StaticLibrary('libpgmhttp', src); -pgmhttppic = e.StaticSharedLibrary('libpgmhttp-pic', src); - -# embed htdocs into C headers -embed_htdoc = Builder(action = './htdocs/convert_to_macro.pl $SOURCE > $TARGET') -e.Append(BUILDERS = {'EmbedHtdoc' : embed_htdoc}) - -for htdoc in htdocs: - embedded = htdoc + '.h' - e.EmbedHtdoc(embedded, htdoc) - -#----------------------------------------------------------------------------- -# unit testing - -if env['WITH_CHECK'] == 'true': - te = e.Clone(); -# add new suffix so we can re-use libpgm objects - te['SHOBJSUFFIX'] = '.http' + te['SHOBJSUFFIX']; - te['OBJSUFFIX'] = '.http' + te['OBJSUFFIX']; - - newCCFLAGS = []; - for flag in te['CCFLAGS']: - if ("-W" != flag[:2]) and ("-pedantic" != flag[:9]): - newCCFLAGS.append(flag); - te['CCFLAGS'] = newCCFLAGS; - te.ParseConfig ('pkg-config --cflags --libs check'); - te.Program (['http_unittest.c', te.Object('sockaddr.c'), te.Object('getifaddrs.c')]); - - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmsnmp b/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmsnmp deleted file mode 100644 index 8e35aab..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmsnmp +++ /dev/null @@ -1,35 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script -# $Id$ - -Import('env') - -e = env.Clone() -e.Append(CCFLAGS = '-DGETTEXT_PACKAGE=\'"pgm-snmp"\''); - -e.MergeFlags(e['SNMP_FLAGS']); - -src = Split(""" - snmp.c - pgmMIB.c -""") - -e.StaticLibrary('libpgmsnmp', src); -e.StaticSharedLibrary('libpgmsnmp-pic', src); - -#----------------------------------------------------------------------------- -# unit testing - -if env['WITH_CHECK'] == 'true': - te = e.Clone(); - newCCFLAGS = []; - for flag in te['CCFLAGS']: - if ("-W" != flag[:2]) and ("-pedantic" != flag[:9]): - newCCFLAGS.append(flag); - te['CCFLAGS'] = newCCFLAGS; - te.ParseConfig ('pkg-config --cflags --libs check'); - te.Program ('snmp_unittest.c'); - te.Program (['pgmMIB_unittest.c', e.Object('snmp.c')]); - - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct b/3rdparty/openpgm-svn-r1085/pgm/SConstruct deleted file mode 100644 index 5754901..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/SConstruct +++ /dev/null @@ -1,326 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script - -import platform -import os -import time -import sys - -EnsureSConsVersion( 1, 0 ) -SConsignFile('scons.signatures' + '-' + platform.system() + '-' + platform.machine()); - -vars = Variables() -vars.AddVariables ( - EnumVariable ('BUILD', 'build environment', 'debug', - allowed_values=('release', 'debug', 'profile')), - EnumVariable ('BRANCH', 'branch prediction', 'none', - allowed_values=('none', 'profile', 'seed')), - EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', - allowed_values=('true', 'false')), - EnumVariable ('COVERAGE', 'test coverage', 'none', - allowed_values=('none', 'full')), - EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_CHECK', 'Check test system', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_TEST', 'Network test system', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_CC', 'C++ examples', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', - allowed_values=('true', 'false')), -) - -#----------------------------------------------------------------------------- -# Dependencies - -env = Environment(); - -def CheckPKGConfig(context, version): - context.Message( 'Checking for pkg-config... ' ) - ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] - context.Result( ret ) - return ret - -def CheckPKG(context, name): - context.Message( 'Checking for %s... ' % name ) - ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] - context.Result( ret ) - return ret - -conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, - 'CheckPKG' : CheckPKG }) - -if not conf.CheckPKGConfig('0.15.0'): - print 'pkg-config >= 0.15.0 not found.' -# Exit(1) - -if not conf.CheckPKG('glib-2.0 >= 2.10'): - print 'glib-2.0 >= 2.10 not found.' -# Exit(1) - -if not conf.CheckPKG('gthread-2.0'): - print 'gthread-2.0 not found.' -# Exit(1) - -env = conf.Finish(); - -#----------------------------------------------------------------------------- -# Platform specifics - -env = Environment( - variables = vars, - ENV = os.environ, - CCFLAGS = [ '-pipe', - '-Wall', - '-Wextra', - '-Wfloat-equal', - '-Wshadow', - '-Wunsafe-loop-optimizations', - '-Wpointer-arith', - '-Wbad-function-cast', - '-Wcast-qual', - '-Wcast-align', - '-Wwrite-strings', - '-Waggregate-return', - '-Wstrict-prototypes', - '-Wold-style-definition', - '-Wmissing-prototypes', - '-Wmissing-declarations', - '-Wmissing-noreturn', - '-Wmissing-format-attribute', - '-Wredundant-decls', - '-Wnested-externs', - '-Winline', - '-pedantic', -# C99 - '-std=gnu99', - '-D_XOPEN_SOURCE=600', - '-D_BSD_SOURCE', -# re-entrant libc - '-D_REENTRANT', -# '-DCONFIG_HAVE_GETPROTOBYNAME_R', - '-DCONFIG_HAVE_GETPROTOBYNAME_R2', -# variadic macros - '-DCONFIG_HAVE_ISO_VARARGS', -# '-DCONFIG_HAVE_GNUC_VARARGS', -# stack memory api header - '-DCONFIG_HAVE_ALLOCA_H', -# optimium checksum implementation -# '-DCONFIG_8BIT_CHECKSUM', - '-DCONFIG_16BIT_CHECKSUM', -# '-DCONFIG_32BIT_CHECKSUM', -# '-DCONFIG_64BIT_CHECKSUM', -# '-DCONFIG_VECTOR_CHECKSUM', -# useful /proc system - '-DCONFIG_HAVE_PROC', -# example: crash handling - '-DCONFIG_HAVE_BACKTRACE', -# timing - '-DCONFIG_HAVE_PSELECT', - '-DCONFIG_HAVE_RTC', - '-DCONFIG_HAVE_TSC', - '-DCONFIG_HAVE_HPET', -# event handling - '-DCONFIG_HAVE_POLL', - '-DCONFIG_HAVE_EPOLL', -# interface enumeration - '-DCONFIG_HAVE_GETIFADDRS', - '-DCONFIG_HAVE_IFR_NETMASK', -# win32 cmsg -# '-DCONFIG_HAVE_WSACMSGHDR', -# multicast - '-DCONFIG_HAVE_MCAST_JOIN', - '-DCONFIG_HAVE_IP_MREQN', -# sprintf - '-DCONFIG_HAVE_SPRINTF_GROUPING', - '-DCONFIG_HAVE_VASPRINTF', -# symbol linking scope - '-DCONFIG_HAVE_DSO_VISIBILITY', -# socket binding - '-DCONFIG_BIND_INADDR_ANY', -# IP header order as per IP(4) on FreeBSD -# '-DCONFIG_HOST_ORDER_IP_LEN', -# '-DCONFIG_HOST_ORDER_IP_OFF', -# optimum galois field multiplication - '-DCONFIG_GALOIS_MUL_LUT', -# Wine limited API support -# '-DCONFIG_TARGET_WINE', -# GNU getopt - '-DCONFIG_HAVE_GETOPT' - ], - LINKFLAGS = [ '-pipe' - ], - LIBS = [ -# histogram math - 'm', -# clock_gettime() - 'rt' - ] -) - -# Branch prediction -if env['BRANCH'] == 'profile': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-fprofile-arcs') -elif env['BRANCH'] == 'seed': - env.Append(CCFLAGS = '-fbranch-probabilities') - -# Coverage analysis -if env['COVERAGE'] == 'full': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(CCFLAGS = '-ftest-coverage') - env.Append(LINKFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-lgcov') - -# Define separate build environments -release = env.Clone(BUILD = 'release') -release.Append(CCFLAGS = '-O2') - -debug = env.Clone(BUILD = 'debug') -debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = '-gdb') - -profile = env.Clone(BUILD = 'profile') -profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = '-pg') - -thirtytwo = release.Clone(BUILD = 'thirtytwo') -thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') - -# choose and environment to build -if env['BUILD'] == 'release': - Export({'env':release}) -elif env['BUILD'] == 'profile': - Export({'env':profile}) -elif env['BUILD'] == 'thirtytwo': - Export({'env':thirtytwo}) -else: - Export({'env':debug}) - -#----------------------------------------------------------------------------- -# Re-analyse dependencies - -Import('env') - -# vanilla environment -if env['WITH_GLIB'] == 'true': - env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); -else: - env['GLIB_FLAGS'] = ''; - -# l10n -if env['WITH_GETTEXT'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); - -# instrumentation -if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); - -# managed environment for libpgmsnmp, libpgmhttp -if env['WITH_SNMP'] == 'true': - env['SNMP_FLAGS'] = env.ParseFlags('!net-snmp-config --agent-libs'); - -def CheckSNMP(context): - context.Message('Checking Net-SNMP...'); - lastLIBS = context.env['LIBS']; - lastCCFLAGS= context.env['CCFLAGS']; - context.env.MergeFlags(env['SNMP_FLAGS']); - result = context.TryLink(""" -int main(int argc, char**argv) -{ - init_agent("PGM"); - return 0; -} -""", '.c'); - context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); - context.Result(not result); - return result; - -def CheckCheck(context): - context.Message('Checking Check unit test framework...'); - result = context.TryAction('pkg-config --cflags --libs check')[0]; - context.Result(result); - return result; - -def CheckEventFD(context): - context.Message('Checking eventfd...'); - result = context.TryLink(""" -#include -int main(int argc, char**argv) -{ - eventfd(0,0); - return 0; -} -""", '.c') - context.Result(result); - return result; - -tests = { - 'CheckCheck': CheckCheck, - 'CheckEventFD': CheckEventFD -} -if env['WITH_SNMP'] == 'true': - tests['CheckSNMP'] = CheckSNMP; -conf = Configure(env, custom_tests = tests); - -if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): - print 'Net-SNMP libraries not compatible.'; - Exit(1); - -if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): - print 'Enabling Check unit tests.'; - conf.env['CHECK'] = 'true'; -else: - print 'Disabling Check unit tests.'; - conf.env['CHECK'] = 'false'; - -if conf.CheckEventFD(): - print 'Enabling kernel eventfd notification mechanism.'; - conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); - -env = conf.Finish(); - -# add builder to create PIC static libraries for including in shared libraries -action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; -if env.Detect('ranlib'): - ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); - action_list.append(ranlib_action); -pic_lib = Builder( action = action_list, - emitter = '$LIBEMITTER', - prefix = '$LIBPREFIX', - suffix = '$LIBSUFFIX', - src_suffix = '$OBJSUFFIX', - src_builder = 'SharedObject') -env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); - - -#----------------------------------------------------------------------------- - -ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '/'; -BuildDir(ref_node, '.', duplicate=0) - -env.Append(CPPPATH = os.getcwd() + '/include'); -env.Append(LIBPATH = os.getcwd() + '/' + ref_node); - -if env['WITH_GLIB'] == 'true': - SConscript(ref_node + 'SConscript.libpgmex'); -SConscript(ref_node + 'SConscript.libpgm'); -if env['WITH_HTTP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmhttp'); -if env['WITH_SNMP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmsnmp'); -if env['WITH_TEST'] == 'true': - SConscript(ref_node + 'test/SConscript'); -if env['WITH_EXAMPLES'] == 'true': - SConscript(ref_node + 'examples/SConscript'); - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097 b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097 deleted file mode 100644 index 9a1d029..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097 +++ /dev/null @@ -1,321 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script - -import platform -import os -import time -import sys - -EnsureSConsVersion( 0, 97 ) -SConsignFile('scons.signatures'+ '-' + platform.system() + '-' + platform.machine()); - -opt = Options(None, ARGUMENTS) -opt.AddOptions ( - (EnumOption ('BUILD', 'build environment', 'debug', ('release', 'debug', 'profile'))), - (EnumOption ('BRANCH', 'branch prediction', 'none', ('none', 'profile', 'seed'))), - (EnumOption ('WITH_GETTEXT', 'l10n support via libintl', 'false', ('true', 'false'))), - (EnumOption ('WITH_GLIB', 'Build GLib dependent modules', 'false', ('true', 'false'))), - (EnumOption ('COVERAGE', 'test coverage', 'none', ('none', 'full'))), - (EnumOption ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', ('true', 'false'))), - (EnumOption ('WITH_HTTP', 'HTTP administration', 'false', ('true', 'false'))), - (EnumOption ('WITH_SNMP', 'SNMP administration', 'false', ('true', 'false'))), - (EnumOption ('WITH_CHECK', 'Check test system', 'false', ('true', 'false'))), - (EnumOption ('WITH_TEST', 'Network test system', 'false', ('true', 'false'))), - (EnumOption ('WITH_CC', 'C++ Examples', 'true', ('true', 'false'))), - (EnumOption ('WITH_EXAMPLES', 'Examples', 'true', ('true', 'false'))), - (EnumOption ('WITH_NCURSES', 'NCURSES examples', 'false', ('true', 'false'))), - (EnumOption ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', ('true', 'false'))), -) - -#----------------------------------------------------------------------------- -# Dependencies - -env = Environment(); - -def CheckPKGConfig(context, version): - context.Message( 'Checking for pkg-config... ' ) - ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] - context.Result( ret ) - return ret - -def CheckPKG(context, name): - context.Message( 'Checking for %s... ' % name ) - ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] - context.Result( ret ) - return ret - -conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, - 'CheckPKG' : CheckPKG }) - -if not conf.CheckPKGConfig('0.15.0'): - print 'pkg-config >= 0.15.0 not found.' -# Exit(1) - -if not conf.CheckPKG('glib-2.0 >= 2.10'): - print 'glib-2.0 >= 2.10 not found.' -# Exit(1) - -if not conf.CheckPKG('gthread-2.0'): - print 'gthread-2.0 not found.' -# Exit(1) - -env = conf.Finish(); - -#----------------------------------------------------------------------------- -# Platform specifics - -env = Environment(ENV = os.environ, - CCFLAGS = [ '-pipe', - '-Wall', - '-Wextra', - '-Wfloat-equal', - '-Wshadow', - '-Wunsafe-loop-optimizations', - '-Wpointer-arith', - '-Wbad-function-cast', - '-Wcast-qual', - '-Wcast-align', - '-Wwrite-strings', - '-Waggregate-return', - '-Wstrict-prototypes', - '-Wold-style-definition', - '-Wmissing-prototypes', - '-Wmissing-declarations', - '-Wmissing-noreturn', - '-Wmissing-format-attribute', - '-Wredundant-decls', - '-Wnested-externs', - '-Winline', - '-pedantic', -# C99 - '-std=gnu99', - '-D_XOPEN_SOURCE=600', -# re-entrant libc - '-D_REENTRANT', -# '-DCONFIG_HAVE_GETPROTOBYNAME_R', - '-DCONFIG_HAVE_GETPROTOBYNAME_R2', -# variadic macros - '-DCONFIG_HAVE_ISO_VARARGS', -# '-DCONFIG_HAVE_GNUC_VARARGS', -# stack memory api header - '-DCONFIG_HAVE_ALLOCA_H', -# optimium checksum implementation -# '-DCONFIG_8BIT_CHECKSUM', - '-DCONFIG_16BIT_CHECKSUM', -# '-DCONFIG_32BIT_CHECKSUM', -# '-DCONFIG_64BIT_CHECKSUM', -# '-DCONFIG_VECTOR_CHECKSUM', -# useful /proc system - '-DCONFIG_HAVE_PROC', -# example: crash handling - '-DCONFIG_HAVE_BACKTRACE', -# timing - '-DCONFIG_HAVE_PSELECT', - '-DCONFIG_HAVE_RTC', - '-DCONFIG_HAVE_TSC', -# event handling - '-DCONFIG_HAVE_POLL', - '-DCONFIG_HAVE_EPOLL', -# interface enumeration - '-DCONFIG_HAVE_GETIFADDRS', - '-DCONFIG_HAVE_IFR_NETMASK', -# win32 cmsg -# '-DCONFIG_HAVE_WSACMSGHDR', -# multicast - '-DCONFIG_HAVE_MCAST_JOIN', - '-DCONFIG_HAVE_IP_MREQN', -# sprintf - '-DCONFIG_HAVE_SPRINTF_GROUPING', - '-DCONFIG_HAVE_VASPRINTF', -# symbol linking scope - '-DCONFIG_HAVE_DSO_VISIBILITY', -# socket binding - '-DCONFIG_BIND_INADDR_ANY', -# IP header order as per IP(4) on FreeBSD -# '-DCONFIG_HOST_ORDER_IP_LEN', -# '-DCONFIG_HOST_ORDER_IP_OFF', -# optimum galois field multiplication - '-DCONFIG_GALOIS_MUL_LUT', -# Wine limited API support -# '-DCONFIG_TARGET_WINE', -# GNU getopt - '-DCONFIG_HAVE_GETOPT' - ], - LINKFLAGS = [ '-pipe' - ], - LIBS = [ -# histogram math - 'm', -# clock_gettime() - 'rt' - ], - PROTOBUF_CCFLAGS = '-I/miru/projects/protobuf/protobuf-2.1.0/src', - PROTOBUF_LIBS = '/miru/projects/protobuf/protobuf-2.1.0/src/.libs/libprotobuf.a', - PROTOBUF_PROTOC = '/miru/projects/protobuf/protobuf-2.1.0/src/protoc' -) -opt.Update (env) - -# Branch prediction -if env['BRANCH'] == 'profile': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-fprofile-arcs') -elif env['BRANCH'] == 'seed': - env.Append(CCFLAGS = '-fbranch-probabilities') - -# Coverage analysis -if env['COVERAGE'] == 'full': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(CCFLAGS = '-ftest-coverage') - env.Append(LINKFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-lgcov') - -# Define separate build environments -release = env.Clone(BUILD = 'release') -release.Append(CCFLAGS = '-O2') - -debug = env.Clone(BUILD = 'debug') -debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = '-gdb') - -profile = env.Clone(BUILD = 'profile') -profile.Append(CCFLAGS = ['-O0','-pg'], LINKFLAGS = '-pg') - -thirtytwo = release.Clone(BUILD = 'thirtytwo') -thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') - -# choose and environment to build -if env['BUILD'] == 'release': - Export({'env':release}) -elif env['BUILD'] == 'profile': - Export({'env':profile}) -elif env['BUILD'] == 'thirtytwo': - Export({'env':thirtytwo}) -else: - Export({'env':debug}) - -#----------------------------------------------------------------------------- -# Re-analyse dependencies - -Import('env') - -# vanilla environment -if env['WITH_GLIB'] == 'true': - env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); -else: - env['GLIB_FLAGS'] = ''; - -# l10n -if env['WITH_GETTEXT'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); - -# instrumentation -if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); - -# managed environment for libpgmsnmp, libpgmhttp -env['SNMP_FLAGS'] = { - 'CCFLAGS' : [], - 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ], -}; - -def CheckSNMP(context): - context.Message('Checking Net-SNMP...'); - lastLIBS = context.env['LIBS']; - lastCCFLAGS= context.env['CCFLAGS']; - context.env.MergeFlags(env['SNMP_FLAGS']); - result = context.TryLink(""" -int main(int argc, char**argv) -{ - init_agent("PGM"); - return 0; -} -""", '.c'); - context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); - context.Result(not result); - return result; - -def CheckCheck(context): - context.Message('Checking Check unit test framework...'); - result = context.TryAction('pkg-config --cflags --libs check')[0]; - context.Result(result); - return result; - -def CheckEventFD(context): - context.Message('Checking eventfd...'); - result = context.TryLink(""" -#include -int main(int argc, char**argv) -{ - eventfd(0,0); - return 0; -} -""", '.c') - context.Result(result); - return result; - -tests = { - 'CheckCheck': CheckCheck, - 'CheckEventFD': CheckEventFD -} -if env['WITH_SNMP'] == 'true': - tests['CheckSNMP'] = CheckSNMP; -conf = Configure(env, custom_tests = tests); - -if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): - print 'Enabling extra Red Hat dependencies for Net-SNMP.'; - conf.env['SNMP_FLAGS']['LIBS'].append(['librpm', 'libsensors', 'libdl', 'libwrap']); - lastLIBS = conf.env['LIBS']; - conf.env.ParseConfig('perl -MExtUtils::Embed -e ldopts'); - conf.env['SNMP_FLAGS']['LIBS'].append(conf.env['LIBS']); - conf.env.Replace(LIBS = lastLIBS); - if not conf.CheckSNMP(): - print 'Net-SNMP libraries not compatible.'; - Exit(1); - -if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): - print 'Enabling Check unit tests.'; - conf.env['CHECK'] = 'true'; -else: - print 'Disabling Check unit tests.'; - conf.env['CHECK'] = 'false'; - -if conf.CheckEventFD(): - print 'Enabling kernel eventfd notification mechanism.'; - conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); - -env = conf.Finish(); - -# add builder to create PIC static libraries for including in shared libraries -action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; -if env.Detect('ranlib'): - ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); - action_list.append(ranlib_action); -pic_lib = Builder( action = action_list, - emitter = '$LIBEMITTER', - prefix = '$LIBPREFIX', - suffix = '$LIBSUFFIX', - src_suffix = '$OBJSUFFIX', - src_builder = 'SharedObject') -env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); - - -#----------------------------------------------------------------------------- - -ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '/'; -BuildDir(ref_node, '.', duplicate=0) - -env.Append(CPPPATH = os.getcwd() + '/include'); -env.Append(LIBPATH = os.getcwd() + '/' + ref_node); - -if env['WITH_GLIB'] == 'true': - SConscript(ref_node + 'SConscript.libpgmex'); -SConscript(ref_node + 'SConscript.libpgm'); -if env['WITH_HTTP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmhttp'); -if env['WITH_SNMP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmsnmp'); -if env['WITH_TEST'] == 'true': - SConscript(ref_node + 'test/SConscript'); -if env['WITH_EXAMPLES'] == 'true': - SConscript(ref_node + 'examples/SConscript'); - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.intelc b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.intelc deleted file mode 100644 index e2099d5..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.intelc +++ /dev/null @@ -1,331 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script - -import platform -import os -import time -import sys - -EnsureSConsVersion( 0, 97 ) -SConsignFile('scons.signatures'+ '-' + platform.system() + '-' + platform.machine() + '-intelc'); - -opt = Options(None, ARGUMENTS) -opt.AddOptions ( - (EnumOption ('BUILD', 'build environment', 'debug', ('release', 'debug', 'profile'))), - (EnumOption ('BRANCH', 'branch prediction', 'none', ('none', 'profile', 'seed'))), - (EnumOption ('WITH_GETTEXT', 'l10n support via libintl', 'false', ('true', 'false'))), - (EnumOption ('WITH_GLIB', 'Build GLib dependent modules', 'false', ('true', 'false'))), - (EnumOption ('COVERAGE', 'test coverage', 'none', ('none', 'full'))), - (EnumOption ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', ('true', 'false'))), - (EnumOption ('WITH_HTTP', 'HTTP administration', 'false', ('true', 'false'))), - (EnumOption ('WITH_SNMP', 'SNMP administration', 'false', ('true', 'false'))), - (EnumOption ('WITH_CHECK', 'Check test system', 'false', ('true', 'false'))), - (EnumOption ('WITH_TEST', 'Network test system', 'false', ('true', 'false'))), - (EnumOption ('WITH_CC', 'C++ Examples', 'true', ('true', 'false'))), - (EnumOption ('WITH_EXAMPLES', 'Examples', 'true', ('true', 'false'))), - (EnumOption ('WITH_NCURSES', 'NCURSES examples', 'false', ('true', 'false'))), - (EnumOption ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', ('true', 'false'))), -) - -#----------------------------------------------------------------------------- -# Dependencies - -def force_intelc(env): - env.Tool('intelc', version='11.1', topdir='/opt/intel/Compiler/11.1/064/bin/intel64'); - -env = Environment(); -force_intelc(env); - -def CheckPKGConfig(context, version): - context.Message( 'Checking for pkg-config... ' ) - ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] - context.Result( ret ) - return ret - -def CheckPKG(context, name): - context.Message( 'Checking for %s... ' % name ) - ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] - context.Result( ret ) - return ret - -conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, - 'CheckPKG' : CheckPKG }) - -if not conf.CheckPKGConfig('0.15.0'): - print 'pkg-config >= 0.15.0 not found.' -# Exit(1) - -if not conf.CheckPKG('glib-2.0 >= 2.10'): - print 'glib-2.0 >= 2.10 not found.' -# Exit(1) - -if not conf.CheckPKG('gthread-2.0'): - print 'gthread-2.0 not found.' -# Exit(1) - -env = conf.Finish(); - -#----------------------------------------------------------------------------- -# Platform specifics - -env = Environment(ENV = os.environ, - CCFLAGS = [ '-pipe', - '-Wall', -# '-Wextra', -# '-Wfloat-equal', - '-Wshadow', -# '-Wunsafe-loop-optimizations', - '-Wpointer-arith', -# '-Wbad-function-cast', -# '-Wcast-qual', -# '-Wcast-align', - '-Wwrite-strings', -# '-Waggregate-return', - '-Wstrict-prototypes', -# '-Wold-style-definition', - '-Wmissing-prototypes', - '-Wmissing-declarations', -# '-Wmissing-noreturn', -# '-Wmissing-format-attribute', -# '-Wredundant-decls', -# '-Wnested-externs', - '-Winline', -# 981: operands are evaluated in unspecified order - '-wd981', -# 2259: non-pointer conversion from "*" to "*" may lose significant bits - '-wd2259', -# '-pedantic', -# C99 - '-std=c99', - '-D_XOPEN_SOURCE=600', - '-gcc-version=420', -# re-entrant libc - '-D_REENTRANT', -# '-DCONFIG_HAVE_GETPROTOBYNAME_R', - '-DCONFIG_HAVE_GETPROTOBYNAME_R2', -# variadic macros - '-DCONFIG_HAVE_ISO_VARARGS', -# '-DCONFIG_HAVE_GNUC_VARARGS', -# stack memory api header - '-DCONFIG_HAVE_ALLOCA_H', -# optimium checksum implementation -# '-DCONFIG_8BIT_CHECKSUM', - '-DCONFIG_16BIT_CHECKSUM', -# '-DCONFIG_32BIT_CHECKSUM', -# '-DCONFIG_64BIT_CHECKSUM', -# '-DCONFIG_VECTOR_CHECKSUM', -# useful /proc system - '-DCONFIG_HAVE_PROC', -# example: crash handling - '-DCONFIG_HAVE_BACKTRACE', -# timing - '-DCONFIG_HAVE_PSELECT', - '-DCONFIG_HAVE_RTC', - '-DCONFIG_HAVE_TSC', -# event handling - '-DCONFIG_HAVE_POLL', - '-DCONFIG_HAVE_EPOLL', -# interface enumeration - '-DCONFIG_HAVE_GETIFADDRS', - '-DCONFIG_HAVE_IFR_NETMASK', -# win32 cmsg -# '-DCONFIG_HAVE_WSACMSGHDR', -# multicast - '-DCONFIG_HAVE_MCAST_JOIN', - '-DCONFIG_HAVE_IP_MREQN', -# sprintf - '-DCONFIG_HAVE_SPRINTF_GROUPING', - '-DCONFIG_HAVE_VASPRINTF', -# symbol linking scope - '-DCONFIG_HAVE_DSO_VISIBILITY', -# socket binding - '-DCONFIG_BIND_INADDR_ANY', -# IP header order as per IP(4) on FreeBSD -# '-DCONFIG_HOST_ORDER_IP_LEN', -# '-DCONFIG_HOST_ORDER_IP_OFF', -# optimum galois field multiplication - '-DCONFIG_GALOIS_MUL_LUT', -# Wine limited API support -# '-DCONFIG_TARGET_WINE', -# GNU getopt - '-DCONFIG_HAVE_GETOPT' - ], - LINKFLAGS = [ '-pipe' - ], - LIBS = [ -# histogram math - 'm', -# clock_gettime() - 'rt' - ], - PROTOBUF_CCFLAGS = '-I/miru/projects/protobuf/protobuf-2.1.0/src', - PROTOBUF_LIBS = '/miru/projects/protobuf/protobuf-2.1.0/src/.libs/libprotobuf.a', - PROTOBUF_PROTOC = '/miru/projects/protobuf/protobuf-2.1.0/src/protoc' -) -opt.Update (env) -force_intelc(env); - -# Branch prediction -if env['BRANCH'] == 'profile': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-fprofile-arcs') -elif env['BRANCH'] == 'seed': - env.Append(CCFLAGS = '-fbranch-probabilities') - -# Coverage analysis -if env['COVERAGE'] == 'full': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(CCFLAGS = '-ftest-coverage') - env.Append(LINKFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-lgcov') - -# Define separate build environments -release = env.Clone(BUILD = 'release') -release.Append(CCFLAGS = '-O2') - -debug = env.Clone(BUILD = 'debug') -debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = '-gdb') - -profile = env.Clone(BUILD = 'profile') -profile.Append(CCFLAGS = ['-O0','-pg'], LINKFLAGS = '-pg') - -thirtytwo = release.Clone(BUILD = 'thirtytwo') -thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') - -# choose and environment to build -if env['BUILD'] == 'release': - Export({'env':release}) -elif env['BUILD'] == 'profile': - Export({'env':profile}) -elif env['BUILD'] == 'thirtytwo': - Export({'env':thirtytwo}) -else: - Export({'env':debug}) - -#----------------------------------------------------------------------------- -# Re-analyse dependencies - -Import('env') - -# vanilla environment -if env['WITH_GLIB'] == 'true': - env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); -else: - env['GLIB_FLAGS'] = ''; - -# l10n -if env['WITH_GETTEXT'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); - -# instrumentation -if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); - -# managed environment for libpgmsnmp, libpgmhttp -env['SNMP_FLAGS'] = { - 'CCFLAGS' : [], - 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ], -}; - -def CheckSNMP(context): - context.Message('Checking Net-SNMP...'); - lastLIBS = context.env['LIBS']; - lastCCFLAGS= context.env['CCFLAGS']; - context.env.MergeFlags(env['SNMP_FLAGS']); - result = context.TryLink(""" -int main(int argc, char**argv) -{ - init_agent("PGM"); - return 0; -} -""", '.c'); - context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); - context.Result(not result); - return result; - -def CheckCheck(context): - context.Message('Checking Check unit test framework...'); - result = context.TryAction('pkg-config --cflags --libs check')[0]; - context.Result(result); - return result; - -def CheckEventFD(context): - context.Message('Checking eventfd...'); - result = context.TryLink(""" -#include -int main(int argc, char**argv) -{ - eventfd(0,0); - return 0; -} -""", '.c') - context.Result(result); - return result; - -tests = { - 'CheckCheck': CheckCheck, - 'CheckEventFD': CheckEventFD -} -if env['WITH_SNMP'] == 'true': - tests['CheckSNMP'] = CheckSNMP; -conf = Configure(env, custom_tests = tests); - -if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): - print 'Enabling extra Red Hat dependencies for Net-SNMP.'; - conf.env['SNMP_FLAGS']['LIBS'].append(['librpm', 'libsensors', 'libdl', 'libwrap']); - lastLIBS = conf.env['LIBS']; - conf.env.ParseConfig('perl -MExtUtils::Embed -e ldopts'); - conf.env['SNMP_FLAGS']['LIBS'].append(conf.env['LIBS']); - conf.env.Replace(LIBS = lastLIBS); - if not conf.CheckSNMP(): - print 'Net-SNMP libraries not compatible.'; - Exit(1); - -if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): - print 'Enabling Check unit tests.'; - conf.env['CHECK'] = 'true'; -else: - print 'Disabling Check unit tests.'; - conf.env['CHECK'] = 'false'; - -if conf.CheckEventFD(): - print 'Enabling kernel eventfd notification mechanism.'; - conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); - -env = conf.Finish(); - -# add builder to create PIC static libraries for including in shared libraries -action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; -if env.Detect('ranlib'): - ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); - action_list.append(ranlib_action); -pic_lib = Builder( action = action_list, - emitter = '$LIBEMITTER', - prefix = '$LIBPREFIX', - suffix = '$LIBSUFFIX', - src_suffix = '$OBJSUFFIX', - src_builder = 'SharedObject') -env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); - - -#----------------------------------------------------------------------------- - -ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '-intelc/'; -BuildDir(ref_node, '.', duplicate=0) - -env.Append(CPPPATH = os.getcwd() + '/include'); -env.Append(LIBPATH = os.getcwd() + '/' + ref_node); - -if env['WITH_GLIB'] == 'true': - SConscript(ref_node + 'SConscript.libpgmex'); -SConscript(ref_node + 'SConscript.libpgm'); -if env['WITH_HTTP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmhttp'); -if env['WITH_SNMP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmsnmp'); -if env['WITH_TEST'] == 'true': - SConscript(ref_node + 'test/SConscript'); -if env['WITH_EXAMPLES'] == 'true': - SConscript(ref_node + 'examples/SConscript'); - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.mingw64 b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.mingw64 deleted file mode 100644 index 59b8063..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.mingw64 +++ /dev/null @@ -1,325 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script - -import platform -import os -import time -import sys - -EnsureSConsVersion( 0, 97 ) -SConsignFile('scons.signatures'+ '-Win64-' + platform.machine()); - -opt = Options(None, ARGUMENTS) -opt.AddOptions ( - (EnumOption ('BUILD', 'build environment', 'debug', ('release', 'debug', 'profile'))), - (EnumOption ('BRANCH', 'branch prediction', 'none', ('none', 'profile', 'seed'))), - (EnumOption ('WITH_GETTEXT', 'l10n support via libintl', 'false', ('true', 'false'))), - (EnumOption ('WITH_GLIB', 'Build GLib dependent modules', 'false', ('true', 'false'))), - (EnumOption ('COVERAGE', 'test coverage', 'none', ('none', 'full'))), - (EnumOption ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', ('true', 'false'))), - (EnumOption ('WITH_HTTP', 'HTTP administration', 'false', ('true', 'false'))), - (EnumOption ('WITH_SNMP', 'SNMP administration', 'false', ('true', 'false'))), - (EnumOption ('WITH_CHECK', 'Check test system', 'false', ('true', 'false'))), - (EnumOption ('WITH_TEST', 'Network test system', 'false', ('true', 'false'))), -# C++ broken scope - (EnumOption ('WITH_CC', 'C++ examples', 'false', ('true', 'false'))), - (EnumOption ('WITH_EXAMPLES', 'Examples', 'true', ('true', 'false'))), - (EnumOption ('WITH_NCURSES', 'NCURSES examples', 'false', ('true', 'false'))), - (EnumOption ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', ('true', 'false'))), -) - -#----------------------------------------------------------------------------- -# Dependencies - -def force_mingw(env): - env.PrependENVPath('PATH', '/opt/mingw/bin'); - env.Tool('crossmingw64', toolpath=['.']); - -env = Environment(); -force_mingw(env); - -def CheckPKGConfig(context, version): - context.Message( 'Checking for pkg-config... ' ) - ret = context.TryAction('PKG_CONFIG_PATH=win64/lib/pkgconfig pkg-config --atleast-pkgconfig-version=%s' % version)[0] - context.Result( ret ) - return ret - -def CheckPKG(context, name): - context.Message( 'Checking for %s... ' % name ) - ret = context.TryAction('PKG_CONFIG_PATH=win64/lib/pkgconfig pkg-config --exists \'%s\'' % name)[0] - context.Result( ret ) - return ret - -conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, - 'CheckPKG' : CheckPKG }) - -if not conf.CheckPKGConfig('0.15.0'): - print 'pkg-config >= 0.15.0 not found.' -# Exit(1) - -if not conf.CheckPKG('glib-2.0 >= 2.10'): - print 'glib-2.0 >= 2.10 not found.' -# Exit(1) - -if not conf.CheckPKG('gthread-2.0'): - print 'gthread-2.0 not found.' -# Exit(1) - -env = conf.Finish(); - -#----------------------------------------------------------------------------- -# Platform specifics - -env = Environment(ENV = os.environ, - CCFLAGS = [ '-pipe', - '-Wall', - '-Wextra', - '-Wfloat-equal', - '-Wshadow', - '-Wunsafe-loop-optimizations', - '-Wpointer-arith', - '-Wbad-function-cast', - '-Wcast-qual', - '-Wcast-align', - '-Wwrite-strings', - '-Waggregate-return', - '-Wstrict-prototypes', - '-Wold-style-definition', - '-Wmissing-prototypes', - '-Wmissing-declarations', - '-Wmissing-noreturn', - '-Wmissing-format-attribute', - '-Wredundant-decls', - '-Wnested-externs', - '-Winline', - '-pedantic', -# C99 - '-std=gnu99', - '-D_GNU_SOURCE', - '-D_WIN32_WINNT=0x0501', -# re-entrant libc - '-D_REENTRANT', -# '-DCONFIG_HAVE_GETPROTOBYNAME_R', -# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', -# variadic macros - '-DCONFIG_HAVE_ISO_VARARGS', -# '-DCONFIG_HAVE_GNUC_VARARGS', -# stack memory api header -# '-DCONFIG_HAVE_ALLOCA_H', -# optimium checksum implementation -# '-DCONFIG_8BIT_CHECKSUM', - '-DCONFIG_16BIT_CHECKSUM', -# '-DCONFIG_32BIT_CHECKSUM', -# '-DCONFIG_64BIT_CHECKSUM', -# '-DCONFIG_VECTOR_CHECKSUM', -# useful /proc system -# '-DCONFIG_HAVE_PROC', -# example: crash handling -# '-DCONFIG_HAVE_BACKTRACE', -# timing -# '-DCONFIG_HAVE_PSELECT', -# '-DCONFIG_HAVE_RTC', - '-DCONFIG_HAVE_TSC', -# event handling -# '-DCONFIG_HAVE_POLL', -# '-DCONFIG_HAVE_EPOLL', -# interface enumeration -# '-DCONFIG_HAVE_GETIFADDRS', -# '-DCONFIG_HAVE_IFR_NETMASK', -# win32 cmsg - '-DCONFIG_HAVE_WSACMSGHDR', -# multicast -# '-DCONFIG_HAVE_MCAST_JOIN', -# '-DCONFIG_HAVE_IP_MREQN', -# sprintf -# '-DCONFIG_HAVE_SPRINTF_GROUPING', -# '-DCONFIG_HAVE_VASPRINTF', -# symbol linking scope - '-DCONFIG_HAVE_DSO_VISIBILITY', -# socket binding - '-DCONFIG_BIND_INADDR_ANY', -# IP header order as per IP(4) on FreeBSD -# '-DCONFIG_HOST_ORDER_IP_LEN', -# '-DCONFIG_HOST_ORDER_IP_OFF', -# optimum galois field multiplication - '-DCONFIG_GALOIS_MUL_LUT', -# Wine limited API support -# '-DCONFIG_TARGET_WINE' -# GNU getopt -# '-DCONFIG_HAVE_GETOPT' - ], - LINKFLAGS = [ '-pipe' - ], - LIBS = [ - 'iphlpapi.lib', - 'ws2_32.lib' - ] -) -opt.Update (env) -force_mingw(env); - -# Branch prediction -if env['BRANCH'] == 'profile': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-fprofile-arcs') -elif env['BRANCH'] == 'seed': - env.Append(CCFLAGS = '-fbranch-probabilities') - -# Coverage analysis -if env['COVERAGE'] == 'full': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(CCFLAGS = '-ftest-coverage') - env.Append(LINKFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-lgcov') - -# Define separate build environments -release = env.Clone(BUILD = 'release') -release.Append(CCFLAGS = '-O2') - -debug = env.Clone(BUILD = 'debug') -debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = '-gdb') - -profile = env.Clone(BUILD = 'profile') -profile.Append(CCFLAGS = ['-O0','-pg'], LINKFLAGS = '-pg') - -thirtytwo = release.Clone(BUILD = 'thirtytwo') -thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') - -# choose and environment to build -if env['BUILD'] == 'release': - Export({'env':release}) -elif env['BUILD'] == 'profile': - Export({'env':profile}) -elif env['BUILD'] == 'thirtytwo': - Export({'env':thirtytwo}) -else: - Export({'env':debug}) - -#----------------------------------------------------------------------------- -# Re-analyse dependencies - -Import('env') - -# vanilla environment -if env['WITH_GLIB'] == 'true': - env['GLIB_FLAGS'] = env.ParseFlags('!PKG_CONFIG_PATH=win64/lib/pkgconfig pkg-config --cflags --libs glib-2.0 gthread-2.0'); - env.MergeFlags('-Iwin/include -Lwin64/lib'); -else: - env['GLIB_FLAGS'] = ''; - -# l10n -if env['WITH_GETTEXT'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); - -# instrumentation -if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); - -# managed environment for libpgmsnmp, libpgmhttp -env['SNMP_FLAGS'] = { - 'CCFLAGS' : [], - 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ], -}; - -def CheckSNMP(context): - context.Message('Checking Net-SNMP...'); - lastLIBS = context.env['LIBS']; - lastCCFLAGS= context.env['CCFLAGS']; - context.env.MergeFlags(env['SNMP_FLAGS']); - result = context.TryLink(""" -int main(int argc, char**argv) -{ - init_agent("PGM"); - return 0; -} -""", '.c'); - context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); - context.Result(not result); - return result; - -def CheckCheck(context): - context.Message('Checking Check unit test framework...'); - result = context.TryAction('PKG_CONFIG_PATH=win64/lib/pkgconfig pkg-config --cflags --libs check')[0]; - context.Result(result); - return result; - -def CheckEventFD(context): - context.Message('Checking eventfd...'); - result = context.TryLink(""" -#include -int main(int argc, char**argv) -{ - eventfd(0,0); - return 0; -} -""", '.c') - context.Result(result); - return result; - -tests = { - 'CheckCheck': CheckCheck, - 'CheckEventFD': CheckEventFD -} -if env['WITH_SNMP'] == 'true': - tests['CheckSNMP'] = CheckSNMP; -conf = Configure(env, custom_tests = tests); - -if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): - print 'Enabling extra Red Hat dependencies for Net-SNMP.'; - conf.env['SNMP_FLAGS']['LIBS'].append(['librpm', 'libsensors', 'libdl', 'libwrap']); - lastLIBS = conf.env['LIBS']; - conf.env.ParseConfig('perl -MExtUtils::Embed -e ldopts'); - conf.env['SNMP_FLAGS']['LIBS'].append(conf.env['LIBS']); - conf.env.Replace(LIBS = lastLIBS); - if not conf.CheckSNMP(): - print 'Net-SNMP libraries not compatible.'; - Exit(1); - -if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): - print 'Enabling Check unit tests.'; - conf.env['CHECK'] = 'true'; -else: - print 'Disabling Check unit tests.'; - conf.env['CHECK'] = 'false'; - -if conf.CheckEventFD(): - print 'Enabling kernel eventfd notification mechanism.'; - conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); - -env = conf.Finish(); - -# add builder to create PIC static libraries for including in shared libraries -action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; -if env.Detect('ranlib'): - ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); - action_list.append(ranlib_action); -pic_lib = Builder( action = action_list, - emitter = '$LIBEMITTER', - prefix = '$LIBPREFIX', - suffix = '$LIBSUFFIX', - src_suffix = '$OBJSUFFIX', - src_builder = 'SharedObject') -env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); - - -#----------------------------------------------------------------------------- - -ref_node = 'ref/' + env['BUILD'] + '-Win64-' + platform.machine() + '/'; -BuildDir(ref_node, '.', duplicate=0) - -env.Append(CPPPATH = os.getcwd() + '/include'); -env.Append(LIBPATH = os.getcwd() + '/' + ref_node); - -if env['WITH_GLIB'] == 'true': - SConscript(ref_node + 'SConscript.libpgmex'); -SConscript(ref_node + 'SConscript.libpgm89'); -if env['WITH_HTTP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmhttp'); -if env['WITH_SNMP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmsnmp'); -if env['WITH_TEST'] == 'true': - SConscript(ref_node + 'test/SConscript'); -if env['WITH_EXAMPLES'] == 'true': - SConscript(ref_node + 'examples/SConscript89'); - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.sunstudio b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.sunstudio deleted file mode 100644 index 1664b9b..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.sunstudio +++ /dev/null @@ -1,312 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script - -import platform -import os -import time -import sys - -EnsureSConsVersion( 0, 97 ) -SConsignFile('scons.signatures'+ '-' + platform.system() + '-' + platform.machine() + '-sunstudio'); - -opt = Options(None, ARGUMENTS) -opt.AddOptions ( - (EnumOption ('BUILD', 'build environment', 'debug', ('release', 'debug', 'profile'))), - (EnumOption ('BRANCH', 'branch prediction', 'none', ('none', 'profile', 'seed'))), - (EnumOption ('WITH_GETTEXT', 'l10n support via libintl', 'false', ('true', 'false'))), - (EnumOption ('WITH_GLIB', 'Build GLib dependent modules', 'false', ('true', 'false'))), - (EnumOption ('COVERAGE', 'test coverage', 'none', ('none', 'full'))), - (EnumOption ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', ('true', 'false'))), - (EnumOption ('WITH_HTTP', 'HTTP administration', 'false', ('true', 'false'))), - (EnumOption ('WITH_SNMP', 'SNMP administration', 'false', ('true', 'false'))), - (EnumOption ('WITH_CHECK', 'Check test system', 'false', ('true', 'false'))), - (EnumOption ('WITH_TEST', 'Network test system', 'false', ('true', 'false'))), - (EnumOption ('WITH_CC', 'C++ Examples', 'true', ('true', 'false'))), - (EnumOption ('WITH_EXAMPLES', 'Examples', 'true', ('true', 'false'))), - (EnumOption ('WITH_NCURSES', 'NCURSES examples', 'false', ('true', 'false'))), - (EnumOption ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', ('true', 'false'))), -) - -#----------------------------------------------------------------------------- -# Dependencies - -def force_sunstudio(env): - env.PrependENVPath('PATH', '/opt/sun/sunstudio12.1/bin'); - env.Tool('sunc++'); - env.Tool('suncc'); - env.Tool('sunlink'); - env.Tool('sunar'); - -env = Environment(); -force_sunstudio(env); - -def CheckPKGConfig(context, version): - context.Message( 'Checking for pkg-config... ' ) - ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] - context.Result( ret ) - return ret - -def CheckPKG(context, name): - context.Message( 'Checking for %s... ' % name ) - ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] - context.Result( ret ) - return ret - -conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, - 'CheckPKG' : CheckPKG }) - -if not conf.CheckPKGConfig('0.15.0'): - print 'pkg-config >= 0.15.0 not found.' -# Exit(1) - -if not conf.CheckPKG('glib-2.0 >= 2.10'): - print 'glib-2.0 >= 2.10 not found.' -# Exit(1) - -if not conf.CheckPKG('gthread-2.0'): - print 'gthread-2.0 not found.' -# Exit(1) - -env = conf.Finish(); - -#----------------------------------------------------------------------------- -# Platform specifics - -env = Environment(ENV = os.environ, - CCFLAGS = [ '-v', -# C99 - '-xc99=all', - '-D_XOPEN_SOURCE=600', - '-D__EXTENSIONS__', - '-DBSD_COMP', - '-D_BSD_SOURCE', -# re-entrant libc - '-D_REENTRANT', -# '-DCONFIG_HAVE_GETPROTOBYNAME_R', - '-DCONFIG_HAVE_GETPROTOBYNAME_R2', -# variadic macros - '-DCONFIG_HAVE_ISO_VARARGS', -# '-DCONFIG_HAVE_GNUC_VARARGS', -# stack memory api header - '-DCONFIG_HAVE_ALLOCA_H', -# optimium checksum implementation -# '-DCONFIG_8BIT_CHECKSUM', - '-DCONFIG_16BIT_CHECKSUM', -# '-DCONFIG_32BIT_CHECKSUM', -# '-DCONFIG_64BIT_CHECKSUM', -# '-DCONFIG_VECTOR_CHECKSUM', -# useful /proc system - '-DCONFIG_HAVE_PROC', -# example: crash handling - '-DCONFIG_HAVE_BACKTRACE', -# timing - '-DCONFIG_HAVE_PSELECT', - '-DCONFIG_HAVE_RTC', - '-DCONFIG_HAVE_TSC', -# event handling - '-DCONFIG_HAVE_POLL', - '-DCONFIG_HAVE_EPOLL', -# interface enumeration - '-DCONFIG_HAVE_GETIFADDRS', - '-DCONFIG_HAVE_IFR_NETMASK', -# win32 cmsg -# '-DCONFIG_HAVE_WSACMSGHDR', -# multicast - '-DCONFIG_HAVE_MCAST_JOIN', - '-DCONFIG_HAVE_IP_MREQN', -# sprintf - '-DCONFIG_HAVE_SPRINTF_GROUPING', - '-DCONFIG_HAVE_VASPRINTF', -# symbol linking scope - '-DCONFIG_HAVE_DSO_VISIBILITY', -# socket binding - '-DCONFIG_BIND_INADDR_ANY', -# IP header order as per IP(4) on FreeBSD -# '-DCONFIG_HOST_ORDER_IP_LEN', -# '-DCONFIG_HOST_ORDER_IP_OFF', -# optimum galois field multiplication - '-DCONFIG_GALOIS_MUL_LUT', -# Wine limited API support -# '-DCONFIG_TARGET_WINE', -# GNU getopt - '-DCONFIG_HAVE_GETOPT' - ], - LINKFLAGS = [ - ], - LIBS = [ -# histogram math - 'm', -# clock_gettime() - 'rt' - ], - PROTOBUF_CCFLAGS = '-I/miru/projects/protobuf/protobuf-2.1.0/src', - PROTOBUF_LIBS = '/miru/projects/protobuf/protobuf-2.1.0/src/.libs/libprotobuf.a', - PROTOBUF_PROTOC = '/miru/projects/protobuf/protobuf-2.1.0/src/protoc' -) -opt.Update (env) -force_sunstudio(env); - -# Branch prediction -if env['BRANCH'] == 'profile': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-fprofile-arcs') -elif env['BRANCH'] == 'seed': - env.Append(CCFLAGS = '-fbranch-probabilities') - -# Coverage analysis -if env['COVERAGE'] == 'full': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(CCFLAGS = '-ftest-coverage') - env.Append(LINKFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-lgcov') - -# Define separate build environments -release = env.Clone(BUILD = 'release') -release.Append(CCFLAGS = '-O2') - -debug = env.Clone(BUILD = 'debug') -debug.Append(CCFLAGS = ['-DPGM_DEBUG','-g'], LINKFLAGS = '-g') - -profile = env.Clone(BUILD = 'profile') -profile.Append(CCFLAGS = ['-O0','-pg'], LINKFLAGS = '-pg') - -thirtytwo = release.Clone(BUILD = 'thirtytwo') -thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') - -# choose and environment to build -if env['BUILD'] == 'release': - Export({'env':release}) -elif env['BUILD'] == 'profile': - Export({'env':profile}) -elif env['BUILD'] == 'thirtytwo': - Export({'env':thirtytwo}) -else: - Export({'env':debug}) - -#----------------------------------------------------------------------------- -# Re-analyse dependencies - -Import('env') - -# vanilla environment -if env['WITH_GLIB'] == 'true': - env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); -else: - env['GLIB_FLAGS'] = ''; - -# l10n -if env['WITH_GETTEXT'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); - -# instrumentation -if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); - -# managed environment for libpgmsnmp, libpgmhttp -env['SNMP_FLAGS'] = { - 'CCFLAGS' : [], - 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ], -}; - -def CheckSNMP(context): - context.Message('Checking Net-SNMP...'); - lastLIBS = context.env['LIBS']; - lastCCFLAGS= context.env['CCFLAGS']; - context.env.MergeFlags(env['SNMP_FLAGS']); - result = context.TryLink(""" -int main(int argc, char**argv) -{ - init_agent("PGM"); - return 0; -} -""", '.c'); - context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); - context.Result(not result); - return result; - -def CheckCheck(context): - context.Message('Checking Check unit test framework...'); - result = context.TryAction('pkg-config --cflags --libs check')[0]; - context.Result(result); - return result; - -def CheckEventFD(context): - context.Message('Checking eventfd...'); - result = context.TryLink(""" -#include -int main(int argc, char**argv) -{ - eventfd(0,0); - return 0; -} -""", '.c') - context.Result(result); - return result; - -tests = { - 'CheckCheck': CheckCheck, - 'CheckEventFD': CheckEventFD -} -if env['WITH_SNMP'] == 'true': - tests['CheckSNMP'] = CheckSNMP; -conf = Configure(env, custom_tests = tests); - -if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): - print 'Enabling extra Red Hat dependencies for Net-SNMP.'; - conf.env['SNMP_FLAGS']['LIBS'].append(['librpm', 'libsensors', 'libdl', 'libwrap']); - lastLIBS = conf.env['LIBS']; - conf.env.ParseConfig('perl -MExtUtils::Embed -e ldopts'); - conf.env['SNMP_FLAGS']['LIBS'].append(conf.env['LIBS']); - conf.env.Replace(LIBS = lastLIBS); - if not conf.CheckSNMP(): - print 'Net-SNMP libraries not compatible.'; - Exit(1); - -if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): - print 'Enabling Check unit tests.'; - conf.env['CHECK'] = 'true'; -else: - print 'Disabling Check unit tests.'; - conf.env['CHECK'] = 'false'; - -if conf.CheckEventFD(): - print 'Enabling kernel eventfd notification mechanism.'; - conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); - -env = conf.Finish(); - -# add builder to create PIC static libraries for including in shared libraries -action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; -if env.Detect('ranlib'): - ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); - action_list.append(ranlib_action); -pic_lib = Builder( action = action_list, - emitter = '$LIBEMITTER', - prefix = '$LIBPREFIX', - suffix = '$LIBSUFFIX', - src_suffix = '$OBJSUFFIX', - src_builder = 'SharedObject') -env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); - - -#----------------------------------------------------------------------------- - -ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '-sunstudio/'; -BuildDir(ref_node, '.', duplicate=0) - -env.Append(CPPPATH = os.getcwd() + '/include'); -env.Append(LIBPATH = os.getcwd() + '/' + ref_node); - -if env['WITH_GLIB'] == 'true': - SConscript(ref_node + 'SConscript.libpgmex'); -SConscript(ref_node + 'SConscript.libpgm'); -if env['WITH_HTTP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmhttp'); -if env['WITH_SNMP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmsnmp'); -if env['WITH_TEST'] == 'true': - SConscript(ref_node + 'test/SConscript'); -if env['WITH_EXAMPLES'] == 'true': - SConscript(ref_node + 'examples/SConscript'); - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Debian4 b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Debian4 deleted file mode 100644 index 952013e..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Debian4 +++ /dev/null @@ -1,281 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script - -import os -import time -import sys - -EnsureSConsVersion( 0, 97 ) -SConsignFile('scons.signatures'); - -opt = Options(None, ARGUMENTS) -opt.AddOptions ( - (EnumOption ('BUILD', 'build environment', 'debug', ('release', 'debug', 'profile'))), - (EnumOption ('BRANCH', 'branch prediction', 'none', ('none', 'profile', 'seed'))), - (EnumOption ('COVERAGE', 'test coverage', 'none', ('none', 'full'))), - (EnumOption ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', ('true', 'false'))), - (EnumOption ('WITH_HTTP', 'HTTP administration', 'true', ('true', 'false'))), - (EnumOption ('WITH_SNMP', 'SNMP administration', 'true', ('true', 'false'))), - (EnumOption ('WITH_CHECK', 'Check test system', 'false', ('true', 'false'))), - (EnumOption ('WITH_TEST', 'Network test system', 'false', ('true', 'false'))), - (EnumOption ('WITH_EXAMPLES', 'Examples', 'true', ('true', 'false'))), - (EnumOption ('WITH_NCURSES', 'NCURSES examples', 'false', ('true', 'false'))), - (EnumOption ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', ('true', 'false'))), - (EnumOption ('WITH_PLUS', 'libpgmplus GPL library', 'false', ('true', 'false'))), -) - -#----------------------------------------------------------------------------- -# Dependencies - -env = Environment(); - -def CheckPKGConfig(context, version): - context.Message( 'Checking for pkg-config... ' ) - ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] - context.Result( ret ) - return ret - -def CheckPKG(context, name): - context.Message( 'Checking for %s... ' % name ) - ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] - context.Result( ret ) - return ret - -conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, - 'CheckPKG' : CheckPKG }) - -if not conf.CheckPKGConfig('0.15.0'): - print 'pkg-config >= 0.15.0 not found.' - Exit(1) - -if not conf.CheckPKG('glib-2.0 >= 2.10'): - print 'glib-2.0 >= 2.10 not found.' - Exit(1) - -if not conf.CheckPKG('gthread-2.0'): - print 'gthread-2.0 not found.' - Exit(1) - -env = conf.Finish(); - -#----------------------------------------------------------------------------- -# Platform specifics - -env = Environment(ENV = os.environ, - CCFLAGS = [ '-pipe', - '-Wall', - '-Wextra', - '-Wfloat-equal', - '-Wshadow', - '-Wunsafe-loop-optimizations', - '-Wpointer-arith', - '-Wbad-function-cast', - '-Wcast-qual', - '-Wcast-align', - '-Wwrite-strings', - '-Waggregate-return', - '-Wstrict-prototypes', - '-Wold-style-definition', - '-Wmissing-prototypes', - '-Wmissing-declarations', - '-Wmissing-noreturn', - '-Wmissing-format-attribute', - '-Wredundant-decls', - '-Wnested-externs', - '-Winline', - '-pedantic', - '-std=gnu99', - '--param', 'max-inline-insns-single=600', - '-D_REENTRANT', - '-D_GNU_SOURCE', - '-D__need_IOV_MAX', - '-DCONFIG_16BIT_CHECKSUM', - '-DCONFIG_HAVE_PROC', - '-DCONFIG_HAVE_BACKTRACE', - '-DCONFIG_HAVE_PSELECT', - '-DCONFIG_HAVE_POLL', - '-DCONFIG_HAVE_EPOLL', - '-DCONFIG_HAVE_CLOCK_GETTIME', - '-DCONFIG_HAVE_CLOCK_NANOSLEEP', - '-DCONFIG_HAVE_NANOSLEEP', - '-DCONFIG_HAVE_USLEEP', - '-DCONFIG_HAVE_RTC', - '-DCONFIG_HAVE_TSC', - '-DCONFIG_HAVE_IFR_NETMASK', - '-DCONFIG_HAVE_GETIFADDRS', - '-DCONFIG_HAVE_GETHOSTBYNAME2', - '-DCONFIG_HAVE_GETPROTOBYNAME_R', - '-DCONFIG_HAVE_MCAST_JOIN', - '-DCONFIG_HAVE_IP_MREQN', - '-DCONFIG_HAVE_DSO_VISIBILITY', - '-DCONFIG_BIND_INADDR_ANY', - '-DCONFIG_GALOIS_MUL_LUT', - ], - LINKFLAGS = [ '-pipe', - '-lm' - ] -) -opt.Update (env) - -# Branch prediction -if env['BRANCH'] == 'profile': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-fprofile-arcs') -elif env['BRANCH'] == 'seed': - env.Append(CCFLAGS = '-fbranch-probabilities') - -# Coverage analysis -if env['COVERAGE'] == 'full': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(CCFLAGS = '-ftest-coverage') - env.Append(LINKFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-lgcov') - -# Define separate build environments -release = env.Clone(BUILD = 'release') -release.Append(CCFLAGS = '-O2') - -debug = env.Clone(BUILD = 'debug') -debug.Append(CCFLAGS = '-ggdb', LINKFLAGS = '-gdb') - -profile = env.Clone(BUILD = 'profile') -profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = '-pg') - -thirtytwo = release.Clone(BUILD = 'thirtytwo') -thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') - -# choose and environment to build -if env['BUILD'] == 'release': - Export({'env':release}) -elif env['BUILD'] == 'profile': - Export({'env':profile}) -elif env['BUILD'] == 'thirtytwo': - Export({'env':thirtytwo}) -else: - Export({'env':debug}) - -#----------------------------------------------------------------------------- -# Re-analyse dependencies - -Import('env') - -# vanilla environment -env.ParseConfig('pkg-config --cflags --libs glib-2.0 gthread-2.0'); - -# instrumentation -if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); - -# managed environment for libpgmsnmp, libpgmhttp -env['SNMP_FLAGS'] = { - 'CCFLAGS' : [], - 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ], -}; - -def CheckSNMP(context): - context.Message('Checking Net-SNMP...'); - lastLIBS = context.env['LIBS']; - lastCCFLAGS= context.env['CCFLAGS']; - context.env.MergeFlags(env['SNMP_FLAGS']); - result = context.TryLink(""" -int main(int argc, char**argv) -{ - init_agent("PGM"); - return 0; -} -""", '.c'); - context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); - context.Result(not result); - return result; - -def CheckCheck(context): - context.Message('Checking Check unit test framework...'); - result = context.TryAction('pkg-config --cflags --libs check')[0]; - context.Result(result); - return result; - -def CheckEventFD(context): - context.Message('Checking eventfd...'); - result = context.TryLink(""" -#include -int main(int argc, char**argv) -{ - eventfd(0,0); - return 0; -} -""", '.c') - context.Result(result); - return result; - -tests = { - 'CheckCheck': CheckCheck, - 'CheckEventFD': CheckEventFD -} -if env['WITH_SNMP'] == 'true': - tests['CheckSNMP'] = CheckSNMP; -conf = Configure(env, custom_tests = tests); - -if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): - print 'Enabling extra Red Hat dependencies for Net-SNMP.'; - conf.env['SNMP_FLAGS']['LIBS'].append(['librpm', 'libsensors', 'libdl', 'libwrap']); - lastLIBS = conf.env['LIBS']; - conf.env.ParseConfig('perl -MExtUtils::Embed -e ldopts'); - conf.env['SNMP_FLAGS']['LIBS'].append(conf.env['LIBS']); - conf.env.Replace(LIBS = lastLIBS); - if not conf.CheckSNMP(): - print 'Net-SNMP libraries not compatible.'; - Exit(1); - -if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): - print 'Enabling Check unit tests.'; - conf.env['CHECK'] = 'true'; -else: - print 'Disabling Check unit tests.'; - conf.env['CHECK'] = 'false'; - -if conf.CheckEventFD(): - print 'Enabling kernel eventfd notification mechanism.'; - conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); - -env = conf.Finish(); - -# DSO visibility flags -if '-DCONFIG_HAVE_DSO_VISIBILITY' in env['CCFLAGS']: - env.Append(CCFLAGS = '-DPGM_GNUC_INTERNAL=G_GNUC_INTERNAL') -else: - env.Append(CCFLAGS = '-DPGM_GNUC_INTERNAL=') - -# add builder to create PIC static libraries for including in shared libraries -action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; -if env.Detect('ranlib'): - ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); - action_list.append(ranlib_action); -pic_lib = Builder( action = action_list, - emitter = '$LIBEMITTER', - prefix = '$LIBPREFIX', - suffix = '$LIBSUFFIX', - src_suffix = '$OBJSUFFIX', - src_builder = 'SharedObject') -env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); - - -#----------------------------------------------------------------------------- - -ref_node = 'ref/' + env['BUILD'] + '/'; -BuildDir(ref_node, '.', duplicate=0) - -env.Append(CPPPATH = os.getcwd() + '/include'); -env.Append(LIBPATH = os.getcwd() + '/' + ref_node); - -SConscript(ref_node + 'SConscript.libpgm'); -SConscript(ref_node + 'SConscript.libpgmex'); -if env['WITH_HTTP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmhttp'); -if env['WITH_SNMP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmsnmp'); -if env['WITH_TEST'] == 'true': - SConscript(ref_node + 'test/SConscript'); -if env['WITH_EXAMPLES'] == 'true': - SConscript(ref_node + 'examples/SConscript'); - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.FreeBSD80 b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.FreeBSD80 deleted file mode 100644 index 4d2b9a5..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.FreeBSD80 +++ /dev/null @@ -1,337 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script - -import platform -import os -import time -import sys - -EnsureSConsVersion( 1, 0 ) -SConsignFile('scons.signatures' + '-' + platform.system() + '-' + platform.machine()); - -vars = Variables() -vars.AddVariables ( - EnumVariable ('BUILD', 'build environment', 'debug', - allowed_values=('release', 'debug', 'profile')), - EnumVariable ('BRANCH', 'branch prediction', 'none', - allowed_values=('none', 'profile', 'seed')), - EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', - allowed_values=('true', 'false')), - EnumVariable ('COVERAGE', 'test coverage', 'none', - allowed_values=('none', 'full')), - EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_CHECK', 'Check test system', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_TEST', 'Network test system', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_CC', 'C++ examples', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', - allowed_values=('true', 'false')), -) - -#----------------------------------------------------------------------------- -# Dependencies - -env = Environment(); - -def CheckPKGConfig(context, version): - context.Message( 'Checking for pkg-config... ' ) - ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] - context.Result( ret ) - return ret - -def CheckPKG(context, name): - context.Message( 'Checking for %s... ' % name ) - ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] - context.Result( ret ) - return ret - -conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, - 'CheckPKG' : CheckPKG }) - -if not conf.CheckPKGConfig('0.15.0'): - print 'pkg-config >= 0.15.0 not found.' -# Exit(1) - -if not conf.CheckPKG('glib-2.0 >= 2.10'): - print 'glib-2.0 >= 2.10 not found.' -# Exit(1) - -if not conf.CheckPKG('gthread-2.0'): - print 'gthread-2.0 not found.' -# Exit(1) - -env = conf.Finish(); - -#----------------------------------------------------------------------------- -# Platform specifics - -env = Environment( - variables = vars, - ENV = os.environ, - CCFLAGS = [ '-pipe', - '-Wall', - '-Wextra', - '-Wfloat-equal', - '-Wshadow', - '-Wunsafe-loop-optimizations', - '-Wpointer-arith', - '-Wbad-function-cast', - '-Wcast-qual', - '-Wcast-align', - '-Wwrite-strings', - '-Waggregate-return', - '-Wstrict-prototypes', - '-Wold-style-definition', - '-Wmissing-prototypes', - '-Wmissing-declarations', - '-Wmissing-noreturn', - '-Wmissing-format-attribute', - '-Wredundant-decls', - '-Wnested-externs', - '-Winline', - '-pedantic', -# C99 - '-std=gnu99', -# re-entrant libc - '-D_REENTRANT', -# '-DCONFIG_HAVE_GETPROTOBYNAME_R', -# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', -# variadic macros - '-DCONFIG_HAVE_ISO_VARARGS', -# '-DCONFIG_HAVE_GNUC_VARARGS', -# stack memory api header -# '-DCONFIG_HAVE_ALLOCA_H', -# optimium checksum implementation -# '-DCONFIG_8BIT_CHECKSUM', - '-DCONFIG_16BIT_CHECKSUM', -# '-DCONFIG_32BIT_CHECKSUM', -# '-DCONFIG_64BIT_CHECKSUM', -# '-DCONFIG_VECTOR_CHECKSUM', -# useful /proc system -# '-DCONFIG_HAVE_PROC', -# example: crash handling -# '-DCONFIG_HAVE_BACKTRACE', -# timing - '-DCONFIG_HAVE_PSELECT', -# '-DCONFIG_HAVE_RTC', - '-DCONFIG_HAVE_TSC', -# '-DCONFIG_HAVE_HPET', -# event handling - '-DCONFIG_HAVE_POLL', -# '-DCONFIG_HAVE_EPOLL', -# interface enumeration - '-DCONFIG_HAVE_GETIFADDRS', - '-DCONFIG_HAVE_IFR_NETMASK', -# win32 cmsg -# '-DCONFIG_HAVE_WSACMSGHDR', -# multicast -# '-DCONFIG_HAVE_MCAST_JOIN', - '-DCONFIG_HAVE_IP_MREQN', -# sprintf - '-DCONFIG_HAVE_SPRINTF_GROUPING', - '-DCONFIG_HAVE_VASPRINTF', -# symbol linking scope - '-DCONFIG_HAVE_DSO_VISIBILITY', -# socket binding - '-DCONFIG_BIND_INADDR_ANY', -# IP header order as per IP(4) on FreeBSD - '-DCONFIG_HOST_ORDER_IP_LEN', - '-DCONFIG_HOST_ORDER_IP_OFF', -# optimum galois field multiplication - '-DCONFIG_GALOIS_MUL_LUT', -# Wine limited API support -# '-DCONFIG_TARGET_WINE', -# GNU getopt - '-DCONFIG_HAVE_GETOPT' - ], - LINKFLAGS = [ '-pipe' - ], - LIBS = [ -# histogram math - 'm', -# clock_gettime() - 'rt', -# ftime() - 'compat', -# POSIX threads - 'pthread' - ] -) - -# Branch prediction -if env['BRANCH'] == 'profile': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-fprofile-arcs') -elif env['BRANCH'] == 'seed': - env.Append(CCFLAGS = '-fbranch-probabilities') - -# Coverage analysis -if env['COVERAGE'] == 'full': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(CCFLAGS = '-ftest-coverage') - env.Append(LINKFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-lgcov') - -# Define separate build environments -release = env.Clone(BUILD = 'release') -release.Append(CCFLAGS = '-O2') - -debug = env.Clone(BUILD = 'debug') -debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = '-gdb') - -profile = env.Clone(BUILD = 'profile') -profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = '-pg') - -thirtytwo = release.Clone(BUILD = 'thirtytwo') -thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') - -# choose and environment to build -if env['BUILD'] == 'release': - Export({'env':release}) -elif env['BUILD'] == 'profile': - Export({'env':profile}) -elif env['BUILD'] == 'thirtytwo': - Export({'env':thirtytwo}) -else: - Export({'env':debug}) - -#----------------------------------------------------------------------------- -# Re-analyse dependencies - -Import('env') - -# vanilla environment -if env['WITH_GLIB'] == 'true': - env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); -else: - env['GLIB_FLAGS'] = ''; - -# l10n -if env['WITH_GETTEXT'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); - -# instrumentation -if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); - -# managed environment for libpgmsnmp, libpgmhttp -env['SNMP_FLAGS'] = { - 'CCFLAGS' : [], - 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ], -}; - -def CheckSNMP(context): - context.Message('Checking Net-SNMP...'); - lastLIBS = context.env['LIBS']; - lastCCFLAGS= context.env['CCFLAGS']; - context.env.MergeFlags(env['SNMP_FLAGS']); - result = context.TryLink(""" -int main(int argc, char**argv) -{ - init_agent("PGM"); - return 0; -} -""", '.c'); - context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); - context.Result(not result); - return result; - -def CheckCheck(context): - context.Message('Checking Check unit test framework...'); - result = context.TryAction('pkg-config --cflags --libs check')[0]; - context.Result(result); - return result; - -def CheckEventFD(context): - context.Message('Checking eventfd...'); - result = context.TryLink(""" -#include -int main(int argc, char**argv) -{ - eventfd(0,0); - return 0; -} -""", '.c') - context.Result(result); - return result; - -tests = { - 'CheckCheck': CheckCheck, - 'CheckEventFD': CheckEventFD -} -if env['WITH_SNMP'] == 'true': - tests['CheckSNMP'] = CheckSNMP; -conf = Configure(env, custom_tests = tests); - -if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): - print 'Enabling extra Red Hat dependencies for Net-SNMP.'; - conf.env['SNMP_FLAGS']['LIBS'].append(['librpm', 'libsensors', 'libdl', 'libwrap']); - lastLIBS = conf.env['LIBS']; - conf.env.ParseConfig('perl -MExtUtils::Embed -e ldopts'); - conf.env['SNMP_FLAGS']['LIBS'].append(conf.env['LIBS']); - conf.env.Replace(LIBS = lastLIBS); - if not conf.CheckSNMP(): - print 'Net-SNMP libraries not compatible.'; - Exit(1); - -if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): - print 'Enabling Check unit tests.'; - conf.env['CHECK'] = 'true'; -else: - print 'Disabling Check unit tests.'; - conf.env['CHECK'] = 'false'; - -if conf.CheckEventFD(): - print 'Enabling kernel eventfd notification mechanism.'; - conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); - -env = conf.Finish(); - -# add builder to create PIC static libraries for including in shared libraries -action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; -if env.Detect('ranlib'): - ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); - action_list.append(ranlib_action); -pic_lib = Builder( action = action_list, - emitter = '$LIBEMITTER', - prefix = '$LIBPREFIX', - suffix = '$LIBSUFFIX', - src_suffix = '$OBJSUFFIX', - src_builder = 'SharedObject') -env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); - - -#----------------------------------------------------------------------------- - -ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '/'; -BuildDir(ref_node, '.', duplicate=0) - -env.Append(CPPPATH = os.getcwd() + '/include'); -env.Append(LIBPATH = os.getcwd() + '/' + ref_node); - -if env['WITH_GLIB'] == 'true': - SConscript(ref_node + 'SConscript.libpgmex'); -SConscript(ref_node + 'SConscript.libpgm'); -if env['WITH_HTTP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmhttp'); -if env['WITH_SNMP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmsnmp'); -if env['WITH_TEST'] == 'true': - SConscript(ref_node + 'test/SConscript'); -if env['WITH_EXAMPLES'] == 'true': - SConscript(ref_node + 'examples/SConscript'); - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.OpenSolaris b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.OpenSolaris deleted file mode 100644 index c9833ed..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.OpenSolaris +++ /dev/null @@ -1,310 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script - -import platform -import os -import time -import sys - -EnsureSConsVersion( 1, 0 ) -SConsignFile('scons.signatures'+ '-OpenSolaris-' + platform.machine() + '-gcc'); - -vars = Variables() -vars.AddVariables ( - EnumVariable ('BUILD', 'build environment', 'debug', - allowed_values=('release', 'debug', 'profile')), - EnumVariable ('BRANCH', 'branch prediction', 'none', - allowed_values=('none', 'profile', 'seed')), - EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_CHECK', 'Check test system', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_TEST', 'Network test system', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_CC', 'C++ examples', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', - allowed_values=('true', 'false')), -) - -#----------------------------------------------------------------------------- -# Dependencies - -env = Environment(); - -def CheckPKGConfig(context, version): - context.Message( 'Checking for pkg-config... ' ) - ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] - context.Result( ret ) - return ret - -def CheckPKG(context, name): - context.Message( 'Checking for %s... ' % name ) - ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] - context.Result( ret ) - return ret - -conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, - 'CheckPKG' : CheckPKG }) - -if not conf.CheckPKGConfig('0.15.0'): - print 'pkg-config >= 0.15.0 not found.' -# Exit(1) - -if not conf.CheckPKG('glib-2.0 >= 2.10'): - print 'glib-2.0 >= 2.10 not found.' -# Exit(1) - -if not conf.CheckPKG('gthread-2.0'): - print 'gthread-2.0 not found.' -# Exit(1) - -env = conf.Finish(); - -#----------------------------------------------------------------------------- -# Platform specifics - -env = Environment( - variables = vars, - ENV = os.environ, - CCFLAGS = [ '-pipe', - '-Wall', - '-Wextra', - '-Wfloat-equal', - '-Wshadow', -# '-Wunsafe-loop-optimizations', - '-Wpointer-arith', - '-Wbad-function-cast', - '-Wcast-qual', - '-Wcast-align', - '-Wwrite-strings', - '-Waggregate-return', - '-Wstrict-prototypes', - '-Wold-style-definition', - '-Wmissing-prototypes', - '-Wmissing-declarations', - '-Wmissing-noreturn', - '-Wmissing-format-attribute', - '-Wredundant-decls', - '-Wnested-externs', - '-Winline', - '-pedantic', -# C99 - '-std=gnu99', - '-D_XOPEN_SOURCE=600', - '-D__EXTENSIONS__', - '-DBSD_COMP', - '-D_BSD_SOURCE', -# re-entrant libc - '-D_REENTRANT', - '-DCONFIG_HAVE_GETPROTOBYNAME_R', -# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', -# variadic macros - '-DCONFIG_HAVE_ISO_VARARGS', -# '-DCONFIG_HAVE_GNUC_VARARGS', -# stack memory api header - '-DCONFIG_HAVE_ALLOCA_H', -# optimium checksum implementation -# '-DCONFIG_8BIT_CHECKSUM', - '-DCONFIG_16BIT_CHECKSUM', -# '-DCONFIG_32BIT_CHECKSUM', -# '-DCONFIG_64BIT_CHECKSUM', -# '-DCONFIG_VECTOR_CHECKSUM', -# useful /proc system -# '-DCONFIG_HAVE_PROC', -# example: crash handling -# '-DCONFIG_HAVE_BACKTRACE', -# timing - '-DCONFIG_HAVE_PSELECT', -# '-DCONFIG_HAVE_RTC', - '-DCONFIG_HAVE_TSC', -# '-DCONFIG_HAVE_HPET', -# event handling - '-DCONFIG_HAVE_POLL', -# '-DCONFIG_HAVE_EPOLL', -# interface enumeration -# '-DCONFIG_HAVE_GETIFADDRS', -# '-DCONFIG_HAVE_IFR_NETMASK', -# win32 cmsg -# '-DCONFIG_HAVE_WSACMSGHDR', -# multicast - '-DCONFIG_HAVE_MCAST_JOIN', -# '-DCONFIG_HAVE_IP_MREQN', -# sprintf - '-DCONFIG_HAVE_SPRINTF_GROUPING', -# '-DCONFIG_HAVE_VASPRINTF', -# symbol linking scope - '-DCONFIG_HAVE_DSO_VISIBILITY', -# socket binding - '-DCONFIG_BIND_INADDR_ANY', -# IP header order as per IP(4) on FreeBSD -# '-DCONFIG_HOST_ORDER_IP_LEN', -# '-DCONFIG_HOST_ORDER_IP_OFF', -# optimum galois field multiplication - '-DCONFIG_GALOIS_MUL_LUT', -# Wine limited API support -# '-DCONFIG_TARGET_WINE', -# GNU getopt - '-DCONFIG_HAVE_GETOPT' - ], - LINKFLAGS = [ '-pipe' - ], - LIBS = [ -# histogram math - 'm', -# clock_gettime() - 'rt', -# Solaris sockets - 'resolv', - 'socket', - 'nsl' - ] -) - -# Branch prediction -if env['BRANCH'] == 'profile': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-fprofile-arcs') -elif env['BRANCH'] == 'seed': - env.Append(CCFLAGS = '-fbranch-probabilities') - -# Define separate build environments -release = env.Clone(BUILD = 'release') -release.Append(CCFLAGS = ['-O2'], LINKFLAGS = []) - -debug = env.Clone(BUILD = 'debug') -debug.Append(CCFLAGS = ['-DPGM_DEBUG', '-ggdb'], LINKFLAGS = ['-gdb']) - -profile = env.Clone(BUILD = 'profile') -profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = ['-pg']) - -thirtytwo = release.Clone(BUILD = 'thirtytwo') -thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') - -# choose and environment to build -if env['BUILD'] == 'release': - Export({'env':release}) -elif env['BUILD'] == 'profile': - Export({'env':profile}) -elif env['BUILD'] == 'thirtytwo': - Export({'env':thirtytwo}) -else: - Export({'env':debug}) - -#----------------------------------------------------------------------------- -# Re-analyse dependencies - -Import('env') - -# vanilla environment -if env['WITH_GLIB'] == 'true': - env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); -else: - env['GLIB_FLAGS'] = ''; - -# l10n -if env['WITH_GETTEXT'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); - -# instrumentation -if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); - -# managed environment for libpgmsnmp, libpgmhttp -env['SNMP_FLAGS'] = { - 'CCFLAGS' : [], - 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp', - 'ssl', 'kstat', 'wrap', 'adm' ], - 'CPPPATH' : ['/opt/cws/include'], - 'LIBPATH' : ['/opt/csw/lib'], -}; - -def CheckSNMP(context): - context.Message('Checking Net-SNMP...'); - lastLIBS = context.env['LIBS']; - lastCCFLAGS= context.env['CCFLAGS']; - context.env.MergeFlags(env['SNMP_FLAGS']); - result = context.TryLink(""" -int main(int argc, char**argv) -{ - init_agent("PGM"); - return 0; -} -""", '.c'); - context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); - context.Result(not result); - return result; - -def CheckCheck(context): - context.Message('Checking Check unit test framework...'); - result = context.TryAction('pkg-config --cflags --libs check')[0]; - context.Result(result); - return result; - -tests = { - 'CheckCheck': CheckCheck -} -if env['WITH_SNMP'] == 'true': - tests['CheckSNMP'] = CheckSNMP; -conf = Configure(env, custom_tests = tests); - -if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): - print 'Net-SNMP libraries not compatible.'; - Exit(1); - -if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): - print 'Enabling Check unit tests.'; - conf.env['CHECK'] = 'true'; -else: - print 'Disabling Check unit tests.'; - conf.env['CHECK'] = 'false'; - -env = conf.Finish(); - -# add builder to create PIC static libraries for including in shared libraries -action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; -if env.Detect('ranlib'): - ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); - action_list.append(ranlib_action); -pic_lib = Builder( action = action_list, - emitter = '$LIBEMITTER', - prefix = '$LIBPREFIX', - suffix = '$LIBSUFFIX', - src_suffix = '$OBJSUFFIX', - src_builder = 'SharedObject') -env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); - - -#----------------------------------------------------------------------------- - -ref_node = 'ref/' + env['BUILD'] + '-OpenSolaris-' + platform.machine() + '-gcc/'; -BuildDir(ref_node, '.', duplicate=0) - -env.Append(CPPPATH = os.getcwd() + '/include'); -env.Append(LIBPATH = os.getcwd() + '/' + ref_node); - -if env['WITH_GLIB'] == 'true': - SConscript(ref_node + 'SConscript.libpgmex'); -SConscript(ref_node + 'SConscript.libpgm'); -if env['WITH_HTTP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmhttp'); -if env['WITH_SNMP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmsnmp'); -if env['WITH_TEST'] == 'true': - SConscript(ref_node + 'test/SConscript'); -if env['WITH_EXAMPLES'] == 'true': - SConscript(ref_node + 'examples/SConscript'); - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.RHEL4 b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.RHEL4 deleted file mode 100644 index 1637ea5..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.RHEL4 +++ /dev/null @@ -1,279 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script - -import os -import time -import sys - -EnsureSConsVersion( 0, 97 ) -SConsignFile('scons.signatures'); - -opt = Options(None, ARGUMENTS) -opt.AddOptions ( - (EnumOption ('BUILD', 'build environment', 'debug', ('release', 'debug', 'profile'))), - (EnumOption ('BRANCH', 'branch prediction', 'none', ('none', 'profile', 'seed'))), - (EnumOption ('COVERAGE', 'test coverage', 'none', ('none', 'full'))), - (EnumOption ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', ('true', 'false'))), - (EnumOption ('WITH_HTTP', 'HTTP administration', 'true', ('true', 'false'))), - (EnumOption ('WITH_SNMP', 'SNMP administration', 'true', ('true', 'false'))), - (EnumOption ('WITH_CHECK', 'Check test system', 'false', ('true', 'false'))), - (EnumOption ('WITH_TEST', 'Network test system', 'false', ('true', 'false'))), - (EnumOption ('WITH_EXAMPLES', 'Examples', 'true', ('true', 'false'))), - (EnumOption ('WITH_NCURSES', 'NCURSES examples', 'false', ('true', 'false'))), - (EnumOption ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', ('true', 'false'))), - (EnumOption ('WITH_PLUS', 'libpgmplus GPL library', 'false', ('true', 'false'))), -) - -#----------------------------------------------------------------------------- -# Dependencies - -env = Environment(); - -def CheckPKGConfig(context, version): - context.Message( 'Checking for pkg-config... ' ) - ret = context.TryAction('PKG_CONFIG_PATH=/usr/evolution28/lib/pkgconfig:/usr/lib/pkconfig pkg-config --atleast-pkgconfig-version=%s' % version)[0] - context.Result( ret ) - return ret - -def CheckPKG(context, name): - context.Message( 'Checking for %s... ' % name ) - ret = context.TryAction('PKG_CONFIG_PATH=/usr/evolution28/lib/pkgconfig:/usr/lib/pkconfig pkg-config --exists \'%s\'' % name)[0] - context.Result( ret ) - return ret - -conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, - 'CheckPKG' : CheckPKG }) - -if not conf.CheckPKGConfig('0.15.0'): - print 'pkg-config >= 0.15.0 not found.' - Exit(1) - -if not conf.CheckPKG('glib-2.0 >= 2.10'): - print 'glib-2.0 >= 2.10 not found.' - Exit(1) - -if not conf.CheckPKG('gthread-2.0'): - print 'gthread-2.0 not found.' - Exit(1) - -env = conf.Finish(); - -#----------------------------------------------------------------------------- -# Platform specifics - -env = Environment(ENV = os.environ, - CCFLAGS = [ '-pipe', - '-Wall', - '-Wextra', - '-Wfloat-equal', - '-Wshadow', - '-Wpointer-arith', - '-Wbad-function-cast', - '-Wcast-qual', - '-Wcast-align', - '-Wwrite-strings', - '-Waggregate-return', - '-Wstrict-prototypes', - '-Wold-style-definition', - '-Wmissing-prototypes', - '-Wmissing-declarations', - '-Wmissing-noreturn', - '-Wmissing-format-attribute', - '-Wredundant-decls', - '-Wnested-externs', - '-Winline', - '-pedantic', - '-std=gnu99', - '--param', 'max-inline-insns-single=600', - '-D_REENTRANT', - '-D_GNU_SOURCE', - '-D__need_IOV_MAX', - '-DCONFIG_16BIT_CHECKSUM', - '-DCONFIG_HAVE_PROC', - '-DCONFIG_HAVE_BACKTRACE', - '-DCONFIG_HAVE_PSELECT', - '-DCONFIG_HAVE_POLL', - '-DCONFIG_HAVE_EPOLL', - '-DCONFIG_HAVE_CLOCK_GETTIME', - '-DCONFIG_HAVE_CLOCK_NANOSLEEP', - '-DCONFIG_HAVE_NANOSLEEP', - '-DCONFIG_HAVE_USLEEP', - '-DCONFIG_HAVE_RTC', - '-DCONFIG_HAVE_TSC', - '-DCONFIG_HAVE_GETIFADDRS', - '-DCONFIG_HAVE_GETHOSTBYNAME2', - '-DCONFIG_HAVE_GETPROTOBYNAME_R', - '-DCONFIG_HAVE_MCAST_JOIN', - '-DCONFIG_HAVE_IP_MREQN', - '-DCONFIG_HAVE_DSO_VISIBILITY', - '-DCONFIG_BIND_INADDR_ANY', - '-DCONFIG_GALOIS_MUL_LUT', - ], - LINKFLAGS = [ '-pipe', - '-lm' - ] -) -opt.Update (env) - -# Branch prediction -if env['BRANCH'] == 'profile': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-fprofile-arcs') -elif env['BRANCH'] == 'seed': - env.Append(CCFLAGS = '-fbranch-probabilities') - -# Coverage analysis -if env['COVERAGE'] == 'full': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(CCFLAGS = '-ftest-coverage') - env.Append(LINKFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-lgcov') - -# Define separate build environments -release = env.Clone(BUILD = 'release') -release.Append(CCFLAGS = '-O2') - -debug = env.Clone(BUILD = 'debug') -debug.Append(CCFLAGS = '-ggdb', LINKFLAGS = '-gdb') - -profile = env.Clone(BUILD = 'profile') -profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = '-pg') - -thirtytwo = release.Clone(BUILD = 'thirtytwo') -thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') - -# choose and environment to build -if env['BUILD'] == 'release': - Export({'env':release}) -elif env['BUILD'] == 'profile': - Export({'env':profile}) -elif env['BUILD'] == 'thirtytwo': - Export({'env':thirtytwo}) -else: - Export({'env':debug}) - -#----------------------------------------------------------------------------- -# Re-analyse dependencies - -Import('env') - -# vanilla environment -env.ParseConfig('PKG_CONFIG_PATH=/usr/evolution28/lib/pkgconfig:/usr/lib/pkconfig pkg-config --cflags --libs glib-2.0 gthread-2.0'); - -# instrumentation -if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); - -# managed environment for libpgmsnmp, libpgmhttp -env['SNMP_FLAGS'] = { - 'CCFLAGS' : [], - 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ], -}; - -def CheckSNMP(context): - context.Message('Checking Net-SNMP...'); - lastLIBS = context.env['LIBS']; - lastCCFLAGS= context.env['CCFLAGS']; - context.env.MergeFlags(env['SNMP_FLAGS']); - result = context.TryLink(""" -int main(int argc, char**argv) -{ - init_agent("PGM"); - return 0; -} -""", '.c'); - context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); - context.Result(not result); - return result; - -def CheckCheck(context): - context.Message('Checking Check unit test framework...'); - result = context.TryAction('pkg-config --cflags --libs check')[0]; - context.Result(result); - return result; - -def CheckEventFD(context): - context.Message('Checking eventfd...'); - result = context.TryLink(""" -#include -int main(int argc, char**argv) -{ - eventfd(0,0); - return 0; -} -""", '.c') - context.Result(result); - return result; - -tests = { - 'CheckCheck': CheckCheck, - 'CheckEventFD': CheckEventFD -} -if env['WITH_SNMP'] == 'true': - tests['CheckSNMP'] = CheckSNMP; -conf = Configure(env, custom_tests = tests); - -if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): - print 'Enabling extra Red Hat dependencies for Net-SNMP.'; - conf.env['SNMP_FLAGS']['LIBS'].append(['librpm', 'libsensors', 'libdl', 'libwrap']); - lastLIBS = conf.env['LIBS']; - conf.env.ParseConfig('perl -MExtUtils::Embed -e ldopts'); - conf.env['SNMP_FLAGS']['LIBS'].append(conf.env['LIBS']); - conf.env.Replace(LIBS = lastLIBS); - if not conf.CheckSNMP(): - print 'Net-SNMP libraries not compatible.'; - Exit(1); - -if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): - print 'Enabling Check unit tests.'; - conf.env['CHECK'] = 'true'; -else: - print 'Disabling Check unit tests.'; - conf.env['CHECK'] = 'false'; - -if conf.CheckEventFD(): - print 'Enabling kernel eventfd notification mechanism.'; - conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); - -env = conf.Finish(); - -# DSO visibility flags -if '-DCONFIG_HAVE_DSO_VISIBILITY' in env['CCFLAGS']: - env.Append(CCFLAGS = '-DPGM_GNUC_INTERNAL=G_GNUC_INTERNAL') -else: - env.Append(CCFLAGS = '-DPGM_GNUC_INTERNAL=') - -# add builder to create PIC static libraries for including in shared libraries -action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; -if env.Detect('ranlib'): - ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); - action_list.append(ranlib_action); -pic_lib = Builder( action = action_list, - emitter = '$LIBEMITTER', - prefix = '$LIBPREFIX', - suffix = '$LIBSUFFIX', - src_suffix = '$OBJSUFFIX', - src_builder = 'SharedObject') -env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); - - -#----------------------------------------------------------------------------- - -ref_node = 'ref/' + env['BUILD'] + '/'; -BuildDir(ref_node, '.', duplicate=0) - -env.Append(CPPPATH = os.getcwd() + '/include'); -env.Append(LIBPATH = os.getcwd() + '/' + ref_node); - -SConscript(ref_node + 'SConscript.libpgm'); -SConscript(ref_node + 'SConscript.libpgmex'); -if env['WITH_HTTP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmhttp'); -if env['WITH_SNMP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmsnmp'); -if env['WITH_TEST'] == 'true': - SConscript(ref_node + 'test/SConscript'); -if env['WITH_EXAMPLES'] == 'true': - SConscript(ref_node + 'examples/SConscript'); - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.gcc64 b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.gcc64 deleted file mode 100644 index 953e120..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.gcc64 +++ /dev/null @@ -1,324 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script - -import platform -import os -import time -import sys - -EnsureSConsVersion( 1, 0 ) -SConsignFile('scons.signatures' + '-' + platform.system() + '-' + platform.machine() + '-gcc64'); - -vars = Variables() -vars.AddVariables ( - EnumVariable ('BUILD', 'build environment', 'debug', - allowed_values=('release', 'debug', 'profile', 'thirtytwo')), - EnumVariable ('BRANCH', 'branch prediction', 'none', - allowed_values=('none', 'profile', 'seed')), - EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_CHECK', 'Check test system', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_TEST', 'Network test system', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_CC', 'C++ examples', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_PLUS', 'libpgmplus GPL library', 'false', - allowed_values=('true', 'false')), -) - -#----------------------------------------------------------------------------- -# Dependencies - -def force_gcc(env): - env.PrependENVPath('PATH', '/usr/sfw/bin'); - env.PrependENVPath('PATH', '/opt/glib-gcc64/bin'); - env.PrependENVPath('PATH', '/usr/local/bin'); - env.Tool('gcc'); - env.Tool('g++'); - -env = Environment(); -force_gcc(env); - -def CheckPKGConfig(context, version): - context.Message( 'Checking for pkg-config... ' ) - ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] - context.Result( ret ) - return ret - -def CheckPKG(context, name): - context.Message( 'Checking for %s... ' % name ) - ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] - context.Result( ret ) - return ret - -conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, - 'CheckPKG' : CheckPKG }) - -if not conf.CheckPKGConfig('0.15.0'): - print 'pkg-config >= 0.15.0 not found.' -# Exit(1) - -if not conf.CheckPKG('glib-2.0 >= 2.10'): - print 'glib-2.0 >= 2.10 not found.' -# Exit(1) - -if not conf.CheckPKG('gthread-2.0'): - print 'gthread-2.0 not found.' -# Exit(1) - -env = conf.Finish(); - -#----------------------------------------------------------------------------- -# Platform specifics - -env = Environment( - variables = vars, - ENV = os.environ, - CCFLAGS = [ '-pipe', - '-Wall', - '-Wextra', - '-Wfloat-equal', - '-Wshadow', -# '-Wunsafe-loop-optimizations', - '-Wpointer-arith', - '-Wbad-function-cast', - '-Wcast-qual', - '-Wcast-align', - '-Wwrite-strings', - '-Waggregate-return', - '-Wstrict-prototypes', - '-Wold-style-definition', - '-Wmissing-prototypes', - '-Wmissing-declarations', - '-Wmissing-noreturn', - '-Wmissing-format-attribute', - '-Wredundant-decls', - '-Wnested-externs', - '-Winline', - '-pedantic', -# C99 - '-std=gnu99', - '-D_XOPEN_SOURCE=600', - '-D__EXTENSIONS__', - '-DBSD_COMP', - '-D_BSD_SOURCE', -# re-entrant libc - '-D_REENTRANT', - '-DCONFIG_HAVE_GETPROTOBYNAME_R', -# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', -# variadic macros - '-DCONFIG_HAVE_ISO_VARARGS', -# '-DCONFIG_HAVE_GNUC_VARARGS', -# stack memory api header - '-DCONFIG_HAVE_ALLOCA_H', -# optimium checksum implementation -# '-DCONFIG_8BIT_CHECKSUM', - '-DCONFIG_16BIT_CHECKSUM', -# '-DCONFIG_32BIT_CHECKSUM', -# '-DCONFIG_64BIT_CHECKSUM', -# '-DCONFIG_VECTOR_CHECKSUM', -# useful /proc system -# '-DCONFIG_HAVE_PROC', -# example: crash handling -# '-DCONFIG_HAVE_BACKTRACE', -# timing -# '-DCONFIG_HAVE_PSELECT', -# '-DCONFIG_HAVE_RTC', -# '-DCONFIG_HAVE_TSC', -# '-DCONFIG_HAVE_HPET', -# event handling - '-DCONFIG_HAVE_POLL', -# '-DCONFIG_HAVE_EPOLL', -# interface enumeration -# '-DCONFIG_HAVE_GETIFADDRS', -# '-DCONFIG_HAVE_IFR_NETMASK', -# win32 cmsg -# '-DCONFIG_HAVE_WSACMSGHDR', -# multicast - '-DCONFIG_HAVE_MCAST_JOIN', -# '-DCONFIG_HAVE_IP_MREQN', -# sprintf -# '-DCONFIG_HAVE_SPRINTF_GROUPING', -# '-DCONFIG_HAVE_VASPRINTF', -# symbol linking scope - '-DCONFIG_HAVE_DSO_VISIBILITY', -# socket binding - '-DCONFIG_BIND_INADDR_ANY', -# IP header order as per IP(4) on FreeBSD -# '-DCONFIG_HOST_ORDER_IP_LEN', -# '-DCONFIG_HOST_ORDER_IP_OFF', -# optimum galois field multiplication - '-DCONFIG_GALOIS_MUL_LUT', -# Wine limited API support -# '-DCONFIG_TARGET_WINE', -# GNU getopt - '-DCONFIG_HAVE_GETOPT' - ], - LINKFLAGS = [ '-pipe' - ], - LIBS = [ -# histogram math - 'm', -# clock_gettime() - 'rt', -# Solaris sockets - 'resolv', - 'socket', - 'nsl' - ], - PROTOBUF_CCFLAGS = '-I/opt/glib-gcc64/include', - PROTOBUF_LIBS = '/opt/glib-gcc64/lib/sparcv9/libprotobuf.a', - PROTOBUF_PROTOC = '/opt/glib-gcc64/bin/protoc' -) -force_gcc(env); - -# Branch prediction -if env['BRANCH'] == 'profile': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-fprofile-arcs') -elif env['BRANCH'] == 'seed': - env.Append(CCFLAGS = '-fbranch-probabilities') - -# Define separate build environments -release = env.Clone(BUILD = 'release') -release.Append(CCFLAGS = ['-O2','-m64'], LINKFLAGS = '-m64') - -debug = env.Clone(BUILD = 'debug') -debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb','-m64'], LINKFLAGS = ['-gdb','-m64']) - -profile = env.Clone(BUILD = 'profile') -profile.Append(CCFLAGS = ['-O2','-pg','-m64'], LINKFLAGS = ['-pg','-m64']) - -thirtytwo = env.Clone(BUILD = 'thirtytwo') -thirtytwo.Append(CCFLAGS = ['-O2','-m32'], LINKFLAGS = '-m32') - -# choose and environment to build -if env['BUILD'] == 'release': - Export({'env':release}) -elif env['BUILD'] == 'profile': - Export({'env':profile}) -elif env['BUILD'] == 'thirtytwo': - Export({'env':thirtytwo}) -else: - Export({'env':debug}) - -#----------------------------------------------------------------------------- -# Re-analyse dependencies - -Import('env') - -# vanilla environment -if env['WITH_GLIB'] == 'true': - env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); -else: - env['GLIB_FLAGS'] = ''; - -# l10n -if env['WITH_GETTEXT'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); - -# instrumentation -if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); - -# managed environment for libpgmsnmp, libpgmhttp -env['SNMP_FLAGS'] = { - 'CCFLAGS' : [], - 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp', - 'ssl', 'kstat', 'wrap', 'adm' ], - 'CPPPATH' : ['/opt/cws/include'], - 'LIBPATH' : ['/opt/csw/lib'], -}; - -def CheckSNMP(context): - context.Message('Checking Net-SNMP...'); - lastLIBS = context.env['LIBS']; - lastCCFLAGS= context.env['CCFLAGS']; - context.env.MergeFlags(env['SNMP_FLAGS']); - result = context.TryLink(""" -int main(int argc, char**argv) -{ - init_agent("PGM"); - return 0; -} -""", '.c'); - context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); - context.Result(not result); - return result; - -def CheckCheck(context): - context.Message('Checking Check unit test framework...'); - result = context.TryAction('pkg-config --cflags --libs check')[0]; - context.Result(result); - return result; - -tests = { - 'CheckCheck': CheckCheck -} -if env['WITH_SNMP'] == 'true': - tests['CheckSNMP'] = CheckSNMP; -conf = Configure(env, custom_tests = tests); - -if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): - print 'Net-SNMP libraries not compatible.'; - Exit(1); - -if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): - print 'Enabling Check unit tests.'; - conf.env['CHECK'] = 'true'; -else: - print 'Disabling Check unit tests.'; - conf.env['CHECK'] = 'false'; - -env = conf.Finish(); - -# add builder to create PIC static libraries for including in shared libraries -action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; -if env.Detect('ranlib'): - ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); - action_list.append(ranlib_action); -pic_lib = Builder( action = action_list, - emitter = '$LIBEMITTER', - prefix = '$LIBPREFIX', - suffix = '$LIBSUFFIX', - src_suffix = '$OBJSUFFIX', - src_builder = 'SharedObject') -env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); - - -#----------------------------------------------------------------------------- - -ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '-gcc64/'; -BuildDir(ref_node, '.', duplicate=0) - -env.Append(CPPPATH = os.getcwd() + '/include'); -env.Append(LIBPATH = os.getcwd() + '/' + ref_node); - -if env['WITH_GLIB'] == 'true': - SConscript(ref_node + 'SConscript.libpgmex'); -SConscript(ref_node + 'SConscript.libpgm'); -if env['WITH_HTTP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmhttp'); -if env['WITH_SNMP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmsnmp'); -if env['WITH_TEST'] == 'true': - SConscript(ref_node + 'test/SConscript'); -if env['WITH_EXAMPLES'] == 'true': - SConscript(ref_node + 'examples/SConscript'); - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.sungcc b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.sungcc deleted file mode 100644 index a5be81f..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.sungcc +++ /dev/null @@ -1,321 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script - -import platform -import os -import time -import sys - -EnsureSConsVersion( 1, 0 ) -SConsignFile('scons.signatures'+ '-' + platform.system() + '-' + platform.machine() + '-sungcc'); - -vars = Variables() -vars.AddVariables ( - EnumVariable ('BUILD', 'build environment', 'debug', - allowed_values=('release', 'debug', 'profile')), - EnumVariable ('BRANCH', 'branch prediction', 'none', - allowed_values=('none', 'profile', 'seed')), - EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_CHECK', 'Check test system', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_TEST', 'Network test system', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_CC', 'C++ examples', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', - allowed_values=('true', 'false')), -) - -#----------------------------------------------------------------------------- -# Dependencies - -def force_gcc(env): - env.PrependENVPath('PATH', '/usr/sfw/bin'); - env.PrependENVPath('PATH', '/opt/glib-gcc/bin'); - env.Tool('gcc'); - env.Tool('g++'); - -env = Environment(); -force_gcc(env); - -def CheckPKGConfig(context, version): - context.Message( 'Checking for pkg-config... ' ) - ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] - context.Result( ret ) - return ret - -def CheckPKG(context, name): - context.Message( 'Checking for %s... ' % name ) - ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] - context.Result( ret ) - return ret - -conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, - 'CheckPKG' : CheckPKG }) - -if not conf.CheckPKGConfig('0.15.0'): - print 'pkg-config >= 0.15.0 not found.' -# Exit(1) - -if not conf.CheckPKG('glib-2.0 >= 2.10'): - print 'glib-2.0 >= 2.10 not found.' -# Exit(1) - -if not conf.CheckPKG('gthread-2.0'): - print 'gthread-2.0 not found.' -# Exit(1) - -env = conf.Finish(); - -#----------------------------------------------------------------------------- -# Platform specifics - -env = Environment( - variables = vars, - ENV = os.environ, - CCFLAGS = [ '-pipe', - '-Wall', - '-Wextra', - '-Wfloat-equal', - '-Wshadow', -# '-Wunsafe-loop-optimizations', - '-Wpointer-arith', - '-Wbad-function-cast', - '-Wcast-qual', - '-Wcast-align', - '-Wwrite-strings', - '-Waggregate-return', - '-Wstrict-prototypes', - '-Wold-style-definition', - '-Wmissing-prototypes', - '-Wmissing-declarations', - '-Wmissing-noreturn', - '-Wmissing-format-attribute', - '-Wredundant-decls', - '-Wnested-externs', - '-Winline', - '-pedantic', -# C99 - '-std=gnu99', - '-D_XOPEN_SOURCE=600', - '-D__EXTENSIONS__', - '-DBSD_COMP', - '-D_BSD_SOURCE', -# re-entrant libc - '-D_REENTRANT', - '-DCONFIG_HAVE_GETPROTOBYNAME_R', -# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', -# variadic macros - '-DCONFIG_HAVE_ISO_VARARGS', -# '-DCONFIG_HAVE_GNUC_VARARGS', -# stack memory api header - '-DCONFIG_HAVE_ALLOCA_H', -# optimium checksum implementation -# '-DCONFIG_8BIT_CHECKSUM', - '-DCONFIG_16BIT_CHECKSUM', -# '-DCONFIG_32BIT_CHECKSUM', -# '-DCONFIG_64BIT_CHECKSUM', -# '-DCONFIG_VECTOR_CHECKSUM', -# useful /proc system -# '-DCONFIG_HAVE_PROC', -# example: crash handling -# '-DCONFIG_HAVE_BACKTRACE', -# timing -# '-DCONFIG_HAVE_PSELECT', -# '-DCONFIG_HAVE_RTC', -# '-DCONFIG_HAVE_TSC', -# '-DCONFIG_HAVE_HPET', -# event handling - '-DCONFIG_HAVE_POLL', -# '-DCONFIG_HAVE_EPOLL', -# interface enumeration -# '-DCONFIG_HAVE_GETIFADDRS', -# '-DCONFIG_HAVE_IFR_NETMASK', -# win32 cmsg -# '-DCONFIG_HAVE_WSACMSGHDR', -# multicast - '-DCONFIG_HAVE_MCAST_JOIN', -# '-DCONFIG_HAVE_IP_MREQN', -# sprintf -# '-DCONFIG_HAVE_SPRINTF_GROUPING', -# '-DCONFIG_HAVE_VASPRINTF', -# symbol linking scope - '-DCONFIG_HAVE_DSO_VISIBILITY', -# socket binding - '-DCONFIG_BIND_INADDR_ANY', -# IP header order as per IP(4) on FreeBSD -# '-DCONFIG_HOST_ORDER_IP_LEN', -# '-DCONFIG_HOST_ORDER_IP_OFF', -# optimum galois field multiplication - '-DCONFIG_GALOIS_MUL_LUT', -# Wine limited API support -# '-DCONFIG_TARGET_WINE', -# GNU getopt - '-DCONFIG_HAVE_GETOPT' - ], - LINKFLAGS = [ '-pipe' - ], - LIBS = [ -# histogram math - 'm', -# clock_gettime() - 'rt', -# Solaris sockets - 'resolv', - 'socket', - 'nsl' - ], - PROTOBUF_CCFLAGS = '-I/opt/glib-gcc/include', - PROTOBUF_LIBS = '/opt/glib-gcc/lib/libprotobuf.a', - PROTOBUF_PROTOC = '/opt/glib-gcc/bin/protoc' -) -force_gcc(env); - -# Branch prediction -if env['BRANCH'] == 'profile': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-fprofile-arcs') -elif env['BRANCH'] == 'seed': - env.Append(CCFLAGS = '-fbranch-probabilities') - -# Define separate build environments -release = env.Clone(BUILD = 'release') -release.Append(CCFLAGS = ['-O2'], LINKFLAGS = []) - -debug = env.Clone(BUILD = 'debug') -debug.Append(CCFLAGS = ['-DPGM_DEBUG', '-ggdb'], LINKFLAGS = ['-gdb']) - -profile = env.Clone(BUILD = 'profile') -profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = ['-pg']) - -thirtytwo = release.Clone(BUILD = 'thirtytwo') -thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') - -# choose and environment to build -if env['BUILD'] == 'release': - Export({'env':release}) -elif env['BUILD'] == 'profile': - Export({'env':profile}) -elif env['BUILD'] == 'thirtytwo': - Export({'env':thirtytwo}) -else: - Export({'env':debug}) - -#----------------------------------------------------------------------------- -# Re-analyse dependencies - -Import('env') - -# vanilla environment -if env['WITH_GLIB'] == 'true': - env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); -else: - env['GLIB_FLAGS'] = ''; - -# l10n -if env['WITH_GETTEXT'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); - -# instrumentation -if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); - -# managed environment for libpgmsnmp, libpgmhttp -env['SNMP_FLAGS'] = { - 'CCFLAGS' : [], - 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp', - 'ssl', 'kstat', 'wrap', 'adm' ], - 'CPPPATH' : ['/opt/cws/include'], - 'LIBPATH' : ['/opt/csw/lib'], -}; - -def CheckSNMP(context): - context.Message('Checking Net-SNMP...'); - lastLIBS = context.env['LIBS']; - lastCCFLAGS= context.env['CCFLAGS']; - context.env.MergeFlags(env['SNMP_FLAGS']); - result = context.TryLink(""" -int main(int argc, char**argv) -{ - init_agent("PGM"); - return 0; -} -""", '.c'); - context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); - context.Result(not result); - return result; - -def CheckCheck(context): - context.Message('Checking Check unit test framework...'); - result = context.TryAction('pkg-config --cflags --libs check')[0]; - context.Result(result); - return result; - -tests = { - 'CheckCheck': CheckCheck -} -if env['WITH_SNMP'] == 'true': - tests['CheckSNMP'] = CheckSNMP; -conf = Configure(env, custom_tests = tests); - -if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): - print 'Net-SNMP libraries not compatible.'; - Exit(1); - -if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): - print 'Enabling Check unit tests.'; - conf.env['CHECK'] = 'true'; -else: - print 'Disabling Check unit tests.'; - conf.env['CHECK'] = 'false'; - -env = conf.Finish(); - -# add builder to create PIC static libraries for including in shared libraries -action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; -if env.Detect('ranlib'): - ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); - action_list.append(ranlib_action); -pic_lib = Builder( action = action_list, - emitter = '$LIBEMITTER', - prefix = '$LIBPREFIX', - suffix = '$LIBSUFFIX', - src_suffix = '$OBJSUFFIX', - src_builder = 'SharedObject') -env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); - - -#----------------------------------------------------------------------------- - -ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '-sungcc/'; -BuildDir(ref_node, '.', duplicate=0) - -env.Append(CPPPATH = os.getcwd() + '/include'); -env.Append(LIBPATH = os.getcwd() + '/' + ref_node); - -if env['WITH_GLIB'] == 'true': - SConscript(ref_node + 'SConscript.libpgmex'); -SConscript(ref_node + 'SConscript.libpgm'); -if env['WITH_HTTP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmhttp'); -if env['WITH_SNMP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmsnmp'); -if env['WITH_TEST'] == 'true': - SConscript(ref_node + 'test/SConscript'); -if env['WITH_EXAMPLES'] == 'true': - SConscript(ref_node + 'examples/SConscript'); - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.sunstudio b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.sunstudio deleted file mode 100644 index 435d907..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.sunstudio +++ /dev/null @@ -1,306 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script - -import platform -import os -import time -import sys - -EnsureSConsVersion( 1, 0 ) -SConsignFile('scons.signatures' + '-' + platform.system() + '-' + platform.machine() + '-sunstudio'); - -vars = Variables() -vars.AddVariables ( - EnumVariable ('BUILD', 'build environment', 'debug', - allowed_values=('release', 'debug', 'profile')), - EnumVariable ('BRANCH', 'branch prediction', 'none', - allowed_values=('none', 'profile', 'seed')), - EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_CHECK', 'Check test system', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_TEST', 'Network test system', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_CC', 'C++ examples', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_PLUS', 'libpgmplus GPL library', 'false', - allowed_values=('true', 'false')), -) - -#----------------------------------------------------------------------------- -# Dependencies - -def force_sunstudio(env): - env.PrependENVPath('PATH', '/usr/ccs/bin'); - env.PrependENVPath('PATH', '/opt/glib-sunstudio/bin'); - env.PrependENVPath('PATH', '/opt/sunstudio12.1/bin'); - env.Tool('sunc++'); - env.Tool('suncc'); - env.Tool('sunlink'); - env.Tool('sunar'); - -env = Environment(); -force_sunstudio(env); - -def CheckPKGConfig(context, version): - context.Message( 'Checking for pkg-config... ' ) - ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] - context.Result( ret ) - return ret - -def CheckPKG(context, name): - context.Message( 'Checking for %s... ' % name ) - ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] - context.Result( ret ) - return ret - -conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, - 'CheckPKG' : CheckPKG }) - -if not conf.CheckPKGConfig('0.15.0'): - print 'pkg-config >= 0.15.0 not found.' - Exit(1) - -if not conf.CheckPKG('glib-2.0 >= 2.10'): - print 'glib-2.0 >= 2.10 not found.' - Exit(1) - -if not conf.CheckPKG('gthread-2.0'): - print 'gthread-2.0 not found.' - Exit(1) - -env = conf.Finish(); - -#----------------------------------------------------------------------------- -# Platform specifics - -env = Environment( - variables = vars, - ENV = os.environ, - CCFLAGS = [ '-v', -# C99 - '-xc99=all', - '-D_XOPEN_SOURCE=600', - '-D__EXTENSIONS__', - '-DBSD_COMP', - '-D_BSD_SOURCE', -# re-entrant libc - '-D_REENTRANT', - '-DCONFIG_HAVE_GETPROTOBYNAME_R', -# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', -# variadic macros - '-DCONFIG_HAVE_ISO_VARARGS', -# '-DCONFIG_HAVE_GNUC_VARARGS', -# stack memory api header - '-DCONFIG_HAVE_ALLOCA_H', -# optimium checksum implementation -# '-DCONFIG_8BIT_CHECKSUM', - '-DCONFIG_16BIT_CHECKSUM', -# '-DCONFIG_32BIT_CHECKSUM', -# '-DCONFIG_64BIT_CHECKSUM', -# '-DCONFIG_VECTOR_CHECKSUM', -# useful /proc system -# '-DCONFIG_HAVE_PROC', -# example: crash handling -# '-DCONFIG_HAVE_BACKTRACE', -# timing -# '-DCONFIG_HAVE_PSELECT', -# '-DCONFIG_HAVE_RTC', -# '-DCONFIG_HAVE_TSC', -# '-DCONFIG_HAVE_HPET', -# event handling - '-DCONFIG_HAVE_POLL', -# '-DCONFIG_HAVE_EPOLL', -# interface enumeration -# '-DCONFIG_HAVE_GETIFADDRS', -# '-DCONFIG_HAVE_IFR_NETMASK', -# win32 cmsg -# '-DCONFIG_HAVE_WSACMSGHDR', -# multicast - '-DCONFIG_HAVE_MCAST_JOIN', -# '-DCONFIG_HAVE_IP_MREQN', -# sprintf -# '-DCONFIG_HAVE_SPRINTF_GROUPING', -# '-DCONFIG_HAVE_VASPRINTF', -# symbol linking scope - '-DCONFIG_HAVE_DSO_VISIBILITY', -# socket binding - '-DCONFIG_BIND_INADDR_ANY', -# IP header order as per IP(4) on FreeBSD -# '-DCONFIG_HOST_ORDER_IP_LEN', -# '-DCONFIG_HOST_ORDER_IP_OFF', -# optimum galois field multiplication - '-DCONFIG_GALOIS_MUL_LUT', -# Wine limited API support -# '-DCONFIG_TARGET_WINE', -# GNU getopt - '-DCONFIG_HAVE_GETOPT' - ], - LINKFLAGS = [], - LIBS = [ -# histogram math - 'm', -# clock_gettime() - 'rt', -# Solaris sockets - 'resolv', - 'socket', - 'nsl' - ], - PROTOBUF_CCFLAGS = '-I/opt/glib-sunstudio/include', - PROTOBUF_LIBS = '/opt/glib-sunstudio/lib/sparcv9/libprotobuf.a', - PROTOBUF_PROTOC = '/opt/glib-sunstudio/bin/protoc' -) -force_sunstudio(env); - -# Branch prediction -if env['BRANCH'] == 'profile': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-fprofile-arcs') -elif env['BRANCH'] == 'seed': - env.Append(CCFLAGS = '-fbranch-probabilities') - -# Define separate build environments -release = env.Clone(BUILD = 'release') -release.Append(CCFLAGS = ['-xO2','-m64'], LINKFLAGS = '-m64') - -# outstanding defect with 12u1 cannot compile extended asm without optimization.:w -# -debug = env.Clone(BUILD = 'debug') -debug.Append(CCFLAGS = ['-DPGM_DEBUG','-xO1', '-g','-m64'], LINKFLAGS = ['-g','-m64']) - -profile = env.Clone(BUILD = 'profile') -profile.Append(CCFLAGS = ['-xO2','-pg','-m64'], LINKFLAGS = ['-pg','-m64']) - -thirtytwo = release.Clone(BUILD = 'thirtytwo') -thirtytwo.Append(CCFLAGS = ['-xO2','-m32'], LINKFLAGS = '-m32'); - -# choose and environment to build -if env['BUILD'] == 'release': - Export({'env':release}) -elif env['BUILD'] == 'profile': - Export({'env':profile}) -elif env['BUILD'] == 'thirtytwo': - Export({'env':thirtytwo}) -else: - Export({'env':debug}) - -#----------------------------------------------------------------------------- -# Re-analyse dependencies - -Import('env') - -# vanilla environment -if env['WITH_GLIB'] == 'true': - env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); -else: - env['GLIB_FLAGS'] = ''; - -# l10n -if env['WITH_GETTEXT'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); - -# instrumentation -if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); - -# managed environment for libpgmsnmp, libpgmhttp -env['SNMP_FLAGS'] = { - 'CCFLAGS' : [], - 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp', - 'ssl', 'kstat', 'wrap', 'adm' ], - 'CPPPATH' : ['/opt/cws/include'], - 'LIBPATH' : ['/opt/csw/lib'], -}; - -def CheckSNMP(context): - context.Message('Checking Net-SNMP...'); - lastLIBS = context.env['LIBS']; - lastCCFLAGS= context.env['CCFLAGS']; - context.env.MergeFlags(env['SNMP_FLAGS']); - result = context.TryLink(""" -int main(int argc, char**argv) -{ - init_agent("PGM"); - return 0; -} -""", '.c'); - context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); - context.Result(not result); - return result; - -def CheckCheck(context): - context.Message('Checking Check unit test framework...'); - result = context.TryAction('pkg-config --cflags --libs check')[0]; - context.Result(result); - return result; - -tests = { - 'CheckCheck': CheckCheck -} -if env['WITH_SNMP'] == 'true': - tests['CheckSNMP'] = CheckSNMP; -conf = Configure(env, custom_tests = tests); - -if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): - print 'Net-SNMP libraries not compatible.'; - Exit(1); - -if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): - print 'Enabling Check unit tests.'; - conf.env['CHECK'] = 'true'; -else: - print 'Disabling Check unit tests.'; - conf.env['CHECK'] = 'false'; - -env = conf.Finish(); - -# add builder to create PIC static libraries for including in shared libraries -action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; -if env.Detect('ranlib'): - ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); - action_list.append(ranlib_action); -pic_lib = Builder( action = action_list, - emitter = '$LIBEMITTER', - prefix = '$LIBPREFIX', - suffix = '$LIBSUFFIX', - src_suffix = '$OBJSUFFIX', - src_builder = 'SharedObject') -env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); - - -#----------------------------------------------------------------------------- - -ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '-sunstudio/'; -BuildDir(ref_node, '.', duplicate=0) - -env.Append(CPPPATH = os.getcwd() + '/include'); -env.Append(LIBPATH = os.getcwd() + '/' + ref_node); - -if env['WITH_GLIB'] == 'true': - SConscript(ref_node + 'SConscript.libpgmex'); -SConscript(ref_node + 'SConscript.libpgm'); -if env['WITH_HTTP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmhttp'); -if env['WITH_SNMP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmsnmp'); -if env['WITH_TEST'] == 'true': - SConscript(ref_node + 'test/SConscript'); -if env['WITH_EXAMPLES'] == 'true': - SConscript(ref_node + 'examples/SConscript'); - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.clang b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.clang deleted file mode 100644 index 7f0a54a..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.clang +++ /dev/null @@ -1,336 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script - -import platform -import os -import time -import sys - -EnsureSConsVersion( 1, 0 ) -SConsignFile('scons.signatures' + '-' + platform.system() + '-' + platform.machine() + '-clang'); - -vars = Variables() -vars.AddVariables ( - EnumVariable ('BUILD', 'build environment', 'debug', - allowed_values=('release', 'debug', 'profile')), - EnumVariable ('BRANCH', 'branch prediction', 'none', - allowed_values=('none', 'profile', 'seed')), - EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', - allowed_values=('true', 'false')), - EnumVariable ('COVERAGE', 'test coverage', 'none', - allowed_values=('none', 'full')), - EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_CHECK', 'Check test system', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_TEST', 'Network test system', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_CC', 'C++ examples', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', - allowed_values=('true', 'false')), -) - -#----------------------------------------------------------------------------- -# Dependencies - -def force_clang(env): - env['CC'] = 'clang'; - -env = Environment(); -force_clang(env); - -def CheckPKGConfig(context, version): - context.Message( 'Checking for pkg-config... ' ) - ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] - context.Result( ret ) - return ret - -def CheckPKG(context, name): - context.Message( 'Checking for %s... ' % name ) - ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] - context.Result( ret ) - return ret - -conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, - 'CheckPKG' : CheckPKG }) - -if not conf.CheckPKGConfig('0.15.0'): - print 'pkg-config >= 0.15.0 not found.' -# Exit(1) - -if not conf.CheckPKG('glib-2.0 >= 2.10'): - print 'glib-2.0 >= 2.10 not found.' -# Exit(1) - -if not conf.CheckPKG('gthread-2.0'): - print 'gthread-2.0 not found.' -# Exit(1) - -env = conf.Finish(); - -#----------------------------------------------------------------------------- -# Platform specifics - -env = Environment( - variables = vars, - ENV = os.environ, - CCFLAGS = [ '-pipe', - '-Wall', - '-Wextra', - '-Wfloat-equal', - '-Wshadow', -# '-Wunsafe-loop-optimizations', - '-Wpointer-arith', - '-Wbad-function-cast', - '-Wcast-qual', - '-Wcast-align', - '-Wwrite-strings', - '-Waggregate-return', - '-Wstrict-prototypes', - '-Wold-style-definition', - '-Wmissing-prototypes', - '-Wmissing-declarations', - '-Wmissing-noreturn', - '-Wmissing-format-attribute', - '-Wredundant-decls', - '-Wnested-externs', - '-Winline', -# '-pedantic', -# C99 - '-std=gnu99', -# re-entrant libc - '-D_REENTRANT', -# '-DCONFIG_HAVE_GETPROTOBYNAME_R', - '-DCONFIG_HAVE_GETPROTOBYNAME_R2', -# variadic macros - '-DCONFIG_HAVE_ISO_VARARGS', -# '-DCONFIG_HAVE_GNUC_VARARGS', -# optimium checksum implementation -# '-DCONFIG_8BIT_CHECKSUM', - '-DCONFIG_16BIT_CHECKSUM', -# '-DCONFIG_32BIT_CHECKSUM', -# '-DCONFIG_64BIT_CHECKSUM', -# '-DCONFIG_VECTOR_CHECKSUM', -# useful /proc system - '-DCONFIG_HAVE_PROC', -# example: crash handling - '-DCONFIG_HAVE_BACKTRACE', -# timing - '-DCONFIG_HAVE_PSELECT', - '-DCONFIG_HAVE_RTC', - '-DCONFIG_HAVE_TSC', - '-DCONFIG_HAVE_HPET', -# event handling - '-DCONFIG_HAVE_POLL', - '-DCONFIG_HAVE_EPOLL', -# interface enumeration - '-DCONFIG_HAVE_GETIFADDRS', - '-DCONFIG_HAVE_IFR_NETMASK', -# win32 cmsg -# '-DCONFIG_HAVE_WSACMSGHDR', -# multicast - '-DCONFIG_HAVE_MCAST_JOIN', - '-DCONFIG_HAVE_IP_MREQN', -# sprintf - '-DCONFIG_HAVE_SPRINTF_GROUPING', - '-DCONFIG_HAVE_VASPRINTF', -# symbol linking scope - '-DCONFIG_HAVE_DSO_VISIBILITY', -# socket binding - '-DCONFIG_BIND_INADDR_ANY', -# IP header order as per IP(4) on FreeBSD -# '-DCONFIG_HOST_ORDER_IP_LEN', -# '-DCONFIG_HOST_ORDER_IP_OFF', -# optimum galois field multiplication - '-DCONFIG_GALOIS_MUL_LUT', -# Wine limited API support -# '-DCONFIG_TARGET_WINE', -# GNU getopt - '-DCONFIG_HAVE_GETOPT' - ], - LINKFLAGS = [ '-pipe' - ], - LIBS = [ -# histogram math - 'm', -# clock_gettime() - 'rt' - ] -) -force_clang(env); - -# Branch prediction -if env['BRANCH'] == 'profile': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-fprofile-arcs') -elif env['BRANCH'] == 'seed': - env.Append(CCFLAGS = '-fbranch-probabilities') - -# Coverage analysis -if env['COVERAGE'] == 'full': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(CCFLAGS = '-ftest-coverage') - env.Append(LINKFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-lgcov') - -# Define separate build environments -release = env.Clone(BUILD = 'release') -release.Append(CCFLAGS = '-O2') - -debug = env.Clone(BUILD = 'debug') -debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = '-gdb') - -profile = env.Clone(BUILD = 'profile') -profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = '-pg') - -thirtytwo = release.Clone(BUILD = 'thirtytwo') -thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') - -# choose and environment to build -if env['BUILD'] == 'release': - Export({'env':release}) -elif env['BUILD'] == 'profile': - Export({'env':profile}) -elif env['BUILD'] == 'thirtytwo': - Export({'env':thirtytwo}) -else: - Export({'env':debug}) - -#----------------------------------------------------------------------------- -# Re-analyse dependencies - -Import('env') - -# vanilla environment -if env['WITH_GLIB'] == 'true': - env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); -else: - env['GLIB_FLAGS'] = ''; - -# l10n -if env['WITH_GETTEXT'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); - -# instrumentation -if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); - -# managed environment for libpgmsnmp, libpgmhttp -env['SNMP_FLAGS'] = { - 'CCFLAGS' : [], - 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ], -}; - -def CheckSNMP(context): - context.Message('Checking Net-SNMP...'); - lastLIBS = context.env['LIBS']; - lastCCFLAGS= context.env['CCFLAGS']; - context.env.MergeFlags(env['SNMP_FLAGS']); - result = context.TryLink(""" -int main(int argc, char**argv) -{ - init_agent("PGM"); - return 0; -} -""", '.c'); - context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); - context.Result(not result); - return result; - -def CheckCheck(context): - context.Message('Checking Check unit test framework...'); - result = context.TryAction('pkg-config --cflags --libs check')[0]; - context.Result(result); - return result; - -def CheckEventFD(context): - context.Message('Checking eventfd...'); - result = context.TryLink(""" -#include -int main(int argc, char**argv) -{ - eventfd(0,0); - return 0; -} -""", '.c') - context.Result(result); - return result; - -tests = { - 'CheckCheck': CheckCheck, - 'CheckEventFD': CheckEventFD -} -if env['WITH_SNMP'] == 'true': - tests['CheckSNMP'] = CheckSNMP; -conf = Configure(env, custom_tests = tests); - -if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): - print 'Enabling extra Red Hat dependencies for Net-SNMP.'; - conf.env['SNMP_FLAGS']['LIBS'].append(['librpm', 'libsensors', 'libdl', 'libwrap']); - lastLIBS = conf.env['LIBS']; - conf.env.ParseConfig('perl -MExtUtils::Embed -e ldopts'); - conf.env['SNMP_FLAGS']['LIBS'].append(conf.env['LIBS']); - conf.env.Replace(LIBS = lastLIBS); - if not conf.CheckSNMP(): - print 'Net-SNMP libraries not compatible.'; - Exit(1); - -if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): - print 'Enabling Check unit tests.'; - conf.env['CHECK'] = 'true'; -else: - print 'Disabling Check unit tests.'; - conf.env['CHECK'] = 'false'; - -if conf.CheckEventFD(): - print 'Enabling kernel eventfd notification mechanism.'; - conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); - -env = conf.Finish(); - -# add builder to create PIC static libraries for including in shared libraries -action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; -if env.Detect('ranlib'): - ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); - action_list.append(ranlib_action); -pic_lib = Builder( action = action_list, - emitter = '$LIBEMITTER', - prefix = '$LIBPREFIX', - suffix = '$LIBSUFFIX', - src_suffix = '$OBJSUFFIX', - src_builder = 'SharedObject') -env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); - - -#----------------------------------------------------------------------------- - -ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '-clang/'; -BuildDir(ref_node, '.', duplicate=0) - -env.Append(CPPPATH = os.getcwd() + '/include'); -env.Append(LIBPATH = os.getcwd() + '/' + ref_node); - -if env['WITH_GLIB'] == 'true': - SConscript(ref_node + 'SConscript.libpgmex'); -SConscript(ref_node + 'SConscript.libpgm'); -if env['WITH_HTTP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmhttp'); -if env['WITH_SNMP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmsnmp'); -if env['WITH_TEST'] == 'true': - SConscript(ref_node + 'test/SConscript'); -if env['WITH_EXAMPLES'] == 'true': - SConscript(ref_node + 'examples/SConscript'); - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.mingw b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.mingw deleted file mode 100644 index 5f54704..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.mingw +++ /dev/null @@ -1,330 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script - -import platform -import os -import time -import sys - -EnsureSConsVersion( 1, 0 ) -SConsignFile('scons.signatures' + '-Win32-' + platform.machine()); - -vars = Variables() -vars.AddVariables ( - EnumVariable ('BUILD', 'build environment', 'debug', - allowed_values=('release', 'debug', 'profile')), - EnumVariable ('BRANCH', 'branch prediction', 'none', - allowed_values=('none', 'profile', 'seed')), - EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', - allowed_values=('true', 'false')), - EnumVariable ('COVERAGE', 'test coverage', 'none', - allowed_values=('none', 'full')), - EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_CHECK', 'Check test system', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_TEST', 'Network test system', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_CC', 'C++ examples', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', - allowed_values=('true', 'false')), -) - -#----------------------------------------------------------------------------- -# Dependencies - -def force_mingw(env): - env.Tool('crossmingw', toolpath=['.']); - -env = Environment(); -force_mingw(env); - -def CheckPKGConfig(context, version): - context.Message( 'Checking for pkg-config... ' ) - ret = context.TryAction('PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --atleast-pkgconfig-version=%s' % version)[0] - context.Result( ret ) - return ret - -def CheckPKG(context, name): - context.Message( 'Checking for %s... ' % name ) - ret = context.TryAction('PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --exists \'%s\'' % name)[0] - context.Result( ret ) - return ret - -conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, - 'CheckPKG' : CheckPKG }) - -if not conf.CheckPKGConfig('0.15.0'): - print 'pkg-config >= 0.15.0 not found.' -# Exit(1) - -if not conf.CheckPKG('glib-2.0 >= 2.10'): - print 'glib-2.0 >= 2.10 not found.' -# Exit(1) - -if not conf.CheckPKG('gthread-2.0'): - print 'gthread-2.0 not found.' -# Exit(1) - -env = conf.Finish(); - -#----------------------------------------------------------------------------- -# Platform specifics - -env = Environment( - variables = vars, - ENV = os.environ, - CCFLAGS = [ '-pipe', - '-Wall', - '-Wextra', - '-Wfloat-equal', - '-Wshadow', - '-Wunsafe-loop-optimizations', - '-Wpointer-arith', - '-Wbad-function-cast', - '-Wcast-qual', - '-Wcast-align', - '-Wwrite-strings', - '-Waggregate-return', - '-Wstrict-prototypes', - '-Wold-style-definition', - '-Wmissing-prototypes', - '-Wmissing-declarations', - '-Wmissing-noreturn', - '-Wmissing-format-attribute', - '-Wredundant-decls', - '-Wnested-externs', - '-Winline', - '-pedantic', -# C99 - '-std=gnu99', - '-D_GNU_SOURCE', - '-D_WIN32_WINNT=0x0501', -# re-entrant libc - '-D_REENTRANT', -# '-DCONFIG_HAVE_GETPROTOBYNAME_R', -# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', -# variadic macros - '-DCONFIG_HAVE_ISO_VARARGS', -# '-DCONFIG_HAVE_GNUC_VARARGS', -# stack memory api header -# '-DCONFIG_HAVE_ALLOCA_H', -# optimium checksum implementation -# '-DCONFIG_8BIT_CHECKSUM', - '-DCONFIG_16BIT_CHECKSUM', -# '-DCONFIG_32BIT_CHECKSUM', -# '-DCONFIG_64BIT_CHECKSUM', -# '-DCONFIG_VECTOR_CHECKSUM', -# useful /proc system -# '-DCONFIG_HAVE_PROC', -# example: crash handling -# '-DCONFIG_HAVE_BACKTRACE', -# timing -# '-DCONFIG_HAVE_PSELECT', -# '-DCONFIG_HAVE_RTC', - '-DCONFIG_HAVE_TSC', -# '-DCONFIG_HAVE_HPET', -# event handling -# '-DCONFIG_HAVE_POLL', -# '-DCONFIG_HAVE_EPOLL', -# interface enumeration -# '-DCONFIG_HAVE_GETIFADDRS', -# '-DCONFIG_HAVE_IFR_NETMASK', -# win32 cmsg - '-DCONFIG_HAVE_WSACMSGHDR', -# multicast -# '-DCONFIG_HAVE_MCAST_JOIN', -# '-DCONFIG_HAVE_IP_MREQN', -# sprintf -# '-DCONFIG_HAVE_SPRINTF_GROUPING', -# '-DCONFIG_HAVE_VASPRINTF', -# symbol linking scope - '-DCONFIG_HAVE_DSO_VISIBILITY', -# socket binding - '-DCONFIG_BIND_INADDR_ANY', -# IP header order as per IP(4) on FreeBSD -# '-DCONFIG_HOST_ORDER_IP_LEN', -# '-DCONFIG_HOST_ORDER_IP_OFF', -# optimum galois field multiplication - '-DCONFIG_GALOIS_MUL_LUT', -# Wine limited API support -# '-DCONFIG_TARGET_WINE', -# GNU getopt -# '-DCONFIG_HAVE_GETOPT' - ], - LINKFLAGS = [ '-pipe' - ], - LIBS = [ - 'iphlpapi.lib', - 'ws2_32.lib' - ] -) -force_mingw(env); - -# Branch prediction -if env['BRANCH'] == 'profile': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-fprofile-arcs') -elif env['BRANCH'] == 'seed': - env.Append(CCFLAGS = '-fbranch-probabilities') - -# Coverage analysis -if env['COVERAGE'] == 'full': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(CCFLAGS = '-ftest-coverage') - env.Append(LINKFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-lgcov') - -# Define separate build environments -release = env.Clone(BUILD = 'release') -release.Append(CCFLAGS = '-O2') - -debug = env.Clone(BUILD = 'debug') -debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = ['-gdb']) - -profile = env.Clone(BUILD = 'profile') -profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = '-pg') - -thirtytwo = release.Clone(BUILD = 'thirtytwo') -thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') - -# choose and environment to build -if env['BUILD'] == 'release': - Export({'env':release}) -elif env['BUILD'] == 'profile': - Export({'env':profile}) -elif env['BUILD'] == 'thirtytwo': - Export({'env':thirtytwo}) -else: - Export({'env':debug}) - -#----------------------------------------------------------------------------- -# Re-analyse dependencies - -Import('env') - -# vanilla environment -if env['WITH_GLIB'] == 'true': - env['GLIB_FLAGS'] = env.ParseFlags('!PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --cflags --libs glib-2.0 gthread-2.0'); - env.MergeFlags('-Iwin/include -Lwin/lib'); -else: - env['GLIB_FLAGS'] = ''; - -# l10n -if env['WITH_GETTEXT'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); - -# instrumentation -if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); - -# managed environment for libpgmsnmp, libpgmhttp -if env['WITH_SNMP'] == 'true': - env['SNMP_FLAGS'] = env.ParseFlags('-Iwin/include -Lwin/lib -lnetsnmpagent -lnetsnmphelpers -lnetsnmp'); - -def CheckSNMP(context): - context.Message('Checking Net-SNMP...'); - lastLIBS = context.env['LIBS']; - lastCCFLAGS= context.env['CCFLAGS']; - context.env.MergeFlags(env['SNMP_FLAGS']); - result = context.TryLink(""" -int main(int argc, char**argv) -{ - init_agent("PGM"); - return 0; -} -""", '.c'); - context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); - context.Result(not result); - return result; - -def CheckCheck(context): - context.Message('Checking Check unit test framework...'); - result = context.TryAction('PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --cflags --libs check')[0]; - context.Result(result); - return result; - -def CheckEventFD(context): - context.Message('Checking eventfd...'); - result = context.TryLink(""" -#include -int main(int argc, char**argv) -{ - eventfd(0,0); - return 0; -} -""", '.c') - context.Result(result); - return result; - -tests = { - 'CheckCheck': CheckCheck, - 'CheckEventFD': CheckEventFD -} -if env['WITH_SNMP'] == 'true': - tests['CheckSNMP'] = CheckSNMP; -conf = Configure(env, custom_tests = tests); - -if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): - print 'Net-SNMP libraries not compatible.'; - Exit(1); - -if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): - print 'Enabling Check unit tests.'; - conf.env['CHECK'] = 'true'; -else: - print 'Disabling Check unit tests.'; - conf.env['CHECK'] = 'false'; - -if conf.CheckEventFD(): - print 'Enabling kernel eventfd notification mechanism.'; - conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); - -env = conf.Finish(); - -# add builder to create PIC static libraries for including in shared libraries -action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; -if env.Detect('ranlib'): - ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); - action_list.append(ranlib_action); -pic_lib = Builder( action = action_list, - emitter = '$LIBEMITTER', - prefix = '$LIBPREFIX', - suffix = '$LIBSUFFIX', - src_suffix = '$OBJSUFFIX', - src_builder = 'SharedObject') -env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); - - -#----------------------------------------------------------------------------- - -ref_node = 'ref/' + env['BUILD'] + '-Win32-' + platform.machine() + '/'; -BuildDir(ref_node, '.', duplicate=0) - -env.Append(CPPPATH = os.getcwd() + '/include'); -env.Append(LIBPATH = os.getcwd() + '/' + ref_node); - -if env['WITH_GLIB'] == 'true': - SConscript(ref_node + 'SConscript.libpgmex'); -SConscript(ref_node + 'SConscript.libpgm'); -if env['WITH_HTTP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmhttp'); -if env['WITH_SNMP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmsnmp'); -if env['WITH_TEST'] == 'true': - SConscript(ref_node + 'test/SConscript'); -if env['WITH_EXAMPLES'] == 'true': - SConscript(ref_node + 'examples/SConscript'); - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.mingw-wine b/3rdparty/openpgm-svn-r1085/pgm/SConstruct.mingw-wine deleted file mode 100644 index 6af1e0a..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/SConstruct.mingw-wine +++ /dev/null @@ -1,339 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script - -import platform -import os -import time -import sys - -EnsureSConsVersion( 1, 0 ) -SConsignFile('scons.signatures' + '-Wine-' + platform.machine()); - -vars = Variables() -vars.AddVariables ( - EnumVariable ('BUILD', 'build environment', 'debug', - allowed_values=('release', 'debug', 'profile')), - EnumVariable ('BRANCH', 'branch prediction', 'none', - allowed_values=('none', 'profile', 'seed')), - EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', - allowed_values=('true', 'false')), - EnumVariable ('COVERAGE', 'test coverage', 'none', - allowed_values=('none', 'full')), - EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_CHECK', 'Check test system', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_TEST', 'Network test system', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_CC', 'C++ examples', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', - allowed_values=('true', 'false')), - EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', - allowed_values=('true', 'false')), - EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', - allowed_values=('true', 'false')), -) - -#----------------------------------------------------------------------------- -# Dependencies - -def force_mingw(env): - env.Tool('crossmingw', toolpath=['.']); - -env = Environment(); -force_mingw(env); - -def CheckPKGConfig(context, version): - context.Message( 'Checking for pkg-config... ' ) - ret = context.TryAction('PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --atleast-pkgconfig-version=%s' % version)[0] - context.Result( ret ) - return ret - -def CheckPKG(context, name): - context.Message( 'Checking for %s... ' % name ) - ret = context.TryAction('PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --exists \'%s\'' % name)[0] - context.Result( ret ) - return ret - -conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, - 'CheckPKG' : CheckPKG }) - -if not conf.CheckPKGConfig('0.15.0'): - print 'pkg-config >= 0.15.0 not found.' -# Exit(1) - -if not conf.CheckPKG('glib-2.0 >= 2.10'): - print 'glib-2.0 >= 2.10 not found.' -# Exit(1) - -if not conf.CheckPKG('gthread-2.0'): - print 'gthread-2.0 not found.' -# Exit(1) - -env = conf.Finish(); - -#----------------------------------------------------------------------------- -# Platform specifics - -env = Environment( - variables = vars, - ENV = os.environ, - CCFLAGS = [ '-pipe', - '-Wall', - '-Wextra', - '-Wfloat-equal', - '-Wshadow', - '-Wunsafe-loop-optimizations', - '-Wpointer-arith', - '-Wbad-function-cast', - '-Wcast-qual', - '-Wcast-align', - '-Wwrite-strings', - '-Waggregate-return', - '-Wstrict-prototypes', - '-Wold-style-definition', - '-Wmissing-prototypes', - '-Wmissing-declarations', - '-Wmissing-noreturn', - '-Wmissing-format-attribute', - '-Wredundant-decls', - '-Wnested-externs', - '-Winline', - '-pedantic', -# C99 - '-std=gnu99', - '-D_GNU_SOURCE', - '-D_WIN32_WINNT=0x0501', -# re-entrant libc - '-D_REENTRANT', -# '-DCONFIG_HAVE_GETPROTOBYNAME_R', -# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', -# variadic macros - '-DCONFIG_HAVE_ISO_VARARGS', -# '-DCONFIG_HAVE_GNUC_VARARGS', -# stack memory api header -# '-DCONFIG_HAVE_ALLOCA_H', -# optimium checksum implementation -# '-DCONFIG_8BIT_CHECKSUM', - '-DCONFIG_16BIT_CHECKSUM', -# '-DCONFIG_32BIT_CHECKSUM', -# '-DCONFIG_64BIT_CHECKSUM', -# '-DCONFIG_VECTOR_CHECKSUM', -# useful /proc system -# '-DCONFIG_HAVE_PROC', -# example: crash handling -# '-DCONFIG_HAVE_BACKTRACE', -# timing -# '-DCONFIG_HAVE_PSELECT', -# '-DCONFIG_HAVE_RTC', - '-DCONFIG_HAVE_TSC', -# '-DCONFIG_HAVE_HPET', -# event handling -# '-DCONFIG_HAVE_POLL', -# '-DCONFIG_HAVE_EPOLL', -# interface enumeration -# '-DCONFIG_HAVE_GETIFADDRS', -# '-DCONFIG_HAVE_IFR_NETMASK', -# win32 cmsg -# '-DCONFIG_HAVE_WSACMSGHDR', -# multicast -# '-DCONFIG_HAVE_MCAST_JOIN', -# '-DCONFIG_HAVE_IP_MREQN', -# sprintf -# '-DCONFIG_HAVE_SPRINTF_GROUPING', -# '-DCONFIG_HAVE_VASPRINTF', -# symbol linking scope - '-DCONFIG_HAVE_DSO_VISIBILITY', -# socket binding - '-DCONFIG_BIND_INADDR_ANY', -# IP header order as per IP(4) on FreeBSD -# '-DCONFIG_HOST_ORDER_IP_LEN', -# '-DCONFIG_HOST_ORDER_IP_OFF', -# optimum galois field multiplication - '-DCONFIG_GALOIS_MUL_LUT', -# Wine limited API support - '-DCONFIG_TARGET_WINE', -# GNU getopt -# '-DCONFIG_HAVE_GETOPT' - ], - LINKFLAGS = [ '-pipe' - ], - LIBS = [ - 'iphlpapi.lib', - 'ws2_32.lib' - ] -) -force_mingw(env); - -# Branch prediction -if env['BRANCH'] == 'profile': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-fprofile-arcs') -elif env['BRANCH'] == 'seed': - env.Append(CCFLAGS = '-fbranch-probabilities') - -# Coverage analysis -if env['COVERAGE'] == 'full': - env.Append(CCFLAGS = '-fprofile-arcs') - env.Append(CCFLAGS = '-ftest-coverage') - env.Append(LINKFLAGS = '-fprofile-arcs') - env.Append(LINKFLAGS = '-lgcov') - -# Define separate build environments -release = env.Clone(BUILD = 'release') -release.Append(CCFLAGS = '-O2') - -debug = env.Clone(BUILD = 'debug') -debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = ['-gdb']) - -profile = env.Clone(BUILD = 'profile') -profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = '-pg') - -thirtytwo = release.Clone(BUILD = 'thirtytwo') -thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') - -# choose and environment to build -if env['BUILD'] == 'release': - Export({'env':release}) -elif env['BUILD'] == 'profile': - Export({'env':profile}) -elif env['BUILD'] == 'thirtytwo': - Export({'env':thirtytwo}) -else: - Export({'env':debug}) - -#----------------------------------------------------------------------------- -# Re-analyse dependencies - -Import('env') - -# vanilla environment -if env['WITH_GLIB'] == 'true': - env['GLIB_FLAGS'] = env.ParseFlags('!PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --cflags --libs glib-2.0 gthread-2.0'); - env.MergeFlags('-Iwin/include -Lwin/lib'); -else: - env['GLIB_FLAGS'] = ''; - -# l10n -if env['WITH_GETTEXT'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); - -# instrumentation -if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': - env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); - -# managed environment for libpgmsnmp, libpgmhttp -env['SNMP_FLAGS'] = { - 'CCFLAGS' : [], - 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ], -}; - -def CheckSNMP(context): - context.Message('Checking Net-SNMP...'); - lastLIBS = context.env['LIBS']; - lastCCFLAGS= context.env['CCFLAGS']; - context.env.MergeFlags(env['SNMP_FLAGS']); - result = context.TryLink(""" -int main(int argc, char**argv) -{ - init_agent("PGM"); - return 0; -} -""", '.c'); - context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); - context.Result(not result); - return result; - -def CheckCheck(context): - context.Message('Checking Check unit test framework...'); - result = context.TryAction('PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --cflags --libs check')[0]; - context.Result(result); - return result; - -def CheckEventFD(context): - context.Message('Checking eventfd...'); - result = context.TryLink(""" -#include -int main(int argc, char**argv) -{ - eventfd(0,0); - return 0; -} -""", '.c') - context.Result(result); - return result; - -tests = { - 'CheckCheck': CheckCheck, - 'CheckEventFD': CheckEventFD -} -if env['WITH_SNMP'] == 'true': - tests['CheckSNMP'] = CheckSNMP; -conf = Configure(env, custom_tests = tests); - -if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): - print 'Enabling extra Red Hat dependencies for Net-SNMP.'; - conf.env['SNMP_FLAGS']['LIBS'].append(['librpm', 'libsensors', 'libdl', 'libwrap']); - lastLIBS = conf.env['LIBS']; - conf.env.ParseConfig('perl -MExtUtils::Embed -e ldopts'); - conf.env['SNMP_FLAGS']['LIBS'].append(conf.env['LIBS']); - conf.env.Replace(LIBS = lastLIBS); - if not conf.CheckSNMP(): - print 'Net-SNMP libraries not compatible.'; - Exit(1); - -if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): - print 'Enabling Check unit tests.'; - conf.env['CHECK'] = 'true'; -else: - print 'Disabling Check unit tests.'; - conf.env['CHECK'] = 'false'; - -if conf.CheckEventFD(): - print 'Enabling kernel eventfd notification mechanism.'; - conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); - -env = conf.Finish(); - -# add builder to create PIC static libraries for including in shared libraries -action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; -if env.Detect('ranlib'): - ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); - action_list.append(ranlib_action); -pic_lib = Builder( action = action_list, - emitter = '$LIBEMITTER', - prefix = '$LIBPREFIX', - suffix = '$LIBSUFFIX', - src_suffix = '$OBJSUFFIX', - src_builder = 'SharedObject') -env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); - - -#----------------------------------------------------------------------------- - -ref_node = 'ref/' + env['BUILD'] + '-Wine-' + platform.machine() + '/'; -BuildDir(ref_node, '.', duplicate=0) - -env.Append(CPPPATH = os.getcwd() + '/include'); -env.Append(LIBPATH = os.getcwd() + '/' + ref_node); - -if env['WITH_GLIB'] == 'true': - SConscript(ref_node + 'SConscript.libpgmex'); -SConscript(ref_node + 'SConscript.libpgm'); -if env['WITH_HTTP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmhttp'); -if env['WITH_SNMP'] == 'true': - SConscript(ref_node + 'SConscript.libpgmsnmp'); -if env['WITH_TEST'] == 'true': - SConscript(ref_node + 'test/SConscript'); -if env['WITH_EXAMPLES'] == 'true': - SConscript(ref_node + 'examples/SConscript'); - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/atomic_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/atomic_unittest.c deleted file mode 100644 index a301c28..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/atomic_unittest.c +++ /dev/null @@ -1,166 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for atomic operations. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include - - -/* mock state */ - - -/* mock functions for external references */ - -#define PGM_COMPILATION -#include "pgm/atomic.h" - - -/* target: - * uint32_t - * pgm_atomic_exchange_and_add32 ( - * volatile uint32_t* atomic, - * const uint32_t val - * ) - */ - -START_TEST (test_int32_exchange_and_add_pass_001) -{ - volatile uint32_t atomic = 0; - fail_unless (0 == pgm_atomic_exchange_and_add32 (&atomic, 5)); - fail_unless (5 == atomic); - fail_unless (5 == pgm_atomic_exchange_and_add32 (&atomic, (uint32_t)-10)); - fail_unless ((uint32_t)-5 == atomic); -} -END_TEST - -/* target: - * void - * pgm_atomic_add32 ( - * volatile uint32_t* atomic, - * const uint32_t val - * ) - */ - -START_TEST (test_int32_add_pass_001) -{ - volatile uint32_t atomic = (uint32_t)-5; - pgm_atomic_add32 (&atomic, 20); - fail_unless (15 == atomic); - pgm_atomic_add32 (&atomic, (uint32_t)-35); - fail_unless ((uint32_t)-20 == atomic); -} -END_TEST - -/* ensure wrap around when casting uint32 */ -START_TEST (test_int32_add_pass_002) -{ - volatile uint32_t atomic = 0; - pgm_atomic_add32 (&atomic, UINT32_MAX/2); - fail_unless ((UINT32_MAX/2) == atomic); - pgm_atomic_add32 (&atomic, UINT32_MAX - (UINT32_MAX/2)); - fail_unless (UINT32_MAX == atomic); - pgm_atomic_add32 (&atomic, 1); - fail_unless (0 == atomic); -} -END_TEST - -/* target: - * uint32_t - * pgm_atomic_read32 ( - * volatile uint32_t* atomic - * ) - */ - -START_TEST (test_int32_get_pass_001) -{ - volatile uint32_t atomic = (uint32_t)-20; - fail_unless ((uint32_t)-20 == pgm_atomic_read32 (&atomic)); -} -END_TEST - -/* target: - * void - * pgm_atomic_int32_set ( - * volatile int32_t* atomic, - * const int32_t val - * ) - */ - -START_TEST (test_int32_set_pass_001) -{ - volatile uint32_t atomic = (uint32_t)-20; - pgm_atomic_write32 (&atomic, 5); - fail_unless (5 == atomic); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_exchange_and_add = tcase_create ("exchange-and-add"); - suite_add_tcase (s, tc_exchange_and_add); - tcase_add_test (tc_exchange_and_add, test_int32_exchange_and_add_pass_001); - - TCase* tc_add = tcase_create ("add"); - suite_add_tcase (s, tc_add); - tcase_add_test (tc_add, test_int32_add_pass_001); - tcase_add_test (tc_add, test_int32_add_pass_002); - - TCase* tc_get = tcase_create ("get"); - suite_add_tcase (s, tc_get); - tcase_add_test (tc_get, test_int32_get_pass_001); - - TCase* tc_set = tcase_create ("set"); - suite_add_tcase (s, tc_set); - tcase_add_test (tc_set, test_int32_set_pass_001); - - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/backtrace.c b/3rdparty/openpgm-svn-r1085/pgm/backtrace.c deleted file mode 100644 index 2e9943d..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/backtrace.c +++ /dev/null @@ -1,69 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Dump back trace to stderr and try gdb. - * - * Copyright (c) 2006-2007 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifdef CONFIG_HAVE_BACKTRACE -# include -# include -# include -# include -# include -#endif -#include -#include - - -void -on_sigsegv ( - G_GNUC_UNUSED int signum - ) -{ -#ifdef CONFIG_HAVE_BACKTRACE - void* array[256]; - char** names; - char cmd[1024]; - int i, size; - gchar *out, *err; - gint exit_status; - - fprintf (stderr, "\n======= Backtrace: =========\n"); - - size = backtrace (array, G_N_ELEMENTS(array)); - names = backtrace_symbols (array, size); - - for (i = 0; i < size; i++) - fprintf (stderr, "%s\n", names[i]); - - free (names); - fflush (stderr); - - sprintf (cmd, "gdb --ex 'attach %ld' --ex 'info threads' --ex 'thread apply all bt' --batch", (long)getpid ()); - if ( g_spawn_command_line_sync (cmd, &out, &err, &exit_status, NULL) ) - { - fprintf (stderr, "======= GDB Backtrace: =========\n"); - fprintf (stderr, "%s\n", out); - } -#endif /* CONFIG_HAVE_BACKTRACE */ - - abort (); -} - -/* eof */ - diff --git a/3rdparty/openpgm-svn-r1085/pgm/checksum.c b/3rdparty/openpgm-svn-r1085/pgm/checksum.c deleted file mode 100644 index 959aceb..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/checksum.c +++ /dev/null @@ -1,941 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * PGM checksum routines - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - - -/* locals */ - -static inline uint16_t do_csum (const void*, uint16_t, uint32_t) PGM_GNUC_PURE; -static uint16_t do_csum_8bit (const void*, uint16_t, uint32_t) PGM_GNUC_PURE; -static uint16_t do_csum_16bit (const void*, uint16_t, uint32_t) PGM_GNUC_PURE; -static uint16_t do_csum_32bit (const void*, uint16_t, uint32_t) PGM_GNUC_PURE; -static uint16_t do_csum_64bit (const void*, uint16_t, uint32_t) PGM_GNUC_PURE; -#if defined(__amd64) || defined(__x86_64__) -static uint16_t do_csum_vector (const void*, uint16_t, uint32_t) PGM_GNUC_PURE; -#endif - - -/* endian independent checksum routine - */ - -static -uint16_t -do_csum_8bit ( - const void* addr, - uint16_t len, - uint32_t csum - ) -{ - uint_fast32_t acc; - uint16_t src; - const uint8_t* buf; - - acc = csum; - buf = (const uint8_t*)addr; - while (len > 1) { -/* first byte as most significant */ - src = (*buf++) << 8; -/* second byte as least significant */ - src |= (*buf++); - acc += src; - len -= 2; - } -/* trailing odd byte */ - if (len > 0) { - src = (*buf) << 8; - acc += src; - } - acc = (acc >> 16) + (acc & 0xffff); - acc += (acc >> 16); - return htons ((uint16_t)acc); -} - -static -uint16_t -do_csumcpy_8bit ( - const void* restrict srcaddr, - void* restrict dstaddr, - uint16_t len, - uint32_t csum - ) -{ - uint_fast32_t acc; - const uint8_t*restrict srcbuf; - uint8_t*restrict dstbuf; - uint_fast16_t val16; - - acc = csum; - srcbuf = (const uint8_t*restrict)srcaddr; - dstbuf = (uint8_t*restrict)dstaddr; - while (len > 1) { -/* first byte as most significant */ - val16 = (*dstbuf++ = *srcbuf++) << 8; -/* second byte as least significant */ - val16 |= (*dstbuf++ = *srcbuf++); - acc += val16; - len -= 2; - } -/* trailing odd byte */ - if (len > 0) { - val16 = (*dstbuf = *srcbuf) << 8; - acc += val16; - } - acc = (acc >> 16) + (acc & 0xffff); - acc += (acc >> 16); - return htons ((uint16_t)acc); -} - -static -uint16_t -do_csum_16bit ( - const void* addr, - uint16_t len, - uint32_t csum - ) -{ - uint_fast32_t acc; - const uint8_t* buf; - uint16_t remainder; - uint_fast16_t count8; - bool is_odd; - - acc = csum; - buf = (const uint8_t*)addr; - remainder = 0; - - if (PGM_UNLIKELY(len == 0)) - return acc; - is_odd = ((uintptr_t)buf & 1); -/* align first byte */ - if (PGM_UNLIKELY(is_odd)) { - ((uint8_t*)&remainder)[1] = *buf++; - len--; - } -/* 8-byte unrolls */ - count8 = len >> 3; - while (count8--) { - acc += ((const uint16_t*)buf)[ 0 ]; - acc += ((const uint16_t*)buf)[ 1 ]; - acc += ((const uint16_t*)buf)[ 2 ]; - acc += ((const uint16_t*)buf)[ 3 ]; - buf = &buf[ 8 ]; - } - len %= 8; -/* final 7 bytes */ - while (len > 1) { - acc += ((const uint16_t*)buf)[ 0 ]; - buf = &buf[ 2 ]; - len -= 2; - } -/* trailing odd byte */ - if (len > 0) { - ((uint8_t*)&remainder)[0] = *buf; - } - acc += remainder; - acc = (acc >> 16) + (acc & 0xffff); - acc += (acc >> 16); - if (PGM_UNLIKELY(is_odd)) - acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); - return acc; -} - -static -uint16_t -do_csumcpy_16bit ( - const void* restrict srcaddr, - void* restrict dstaddr, - uint16_t len, - uint32_t csum - ) -{ - uint_fast32_t acc; - const uint8_t*restrict srcbuf; - uint8_t*restrict dstbuf; - uint16_t remainder; - uint_fast16_t count8; - bool is_odd; - - acc = csum; - srcbuf = (const uint8_t*restrict)srcaddr; - dstbuf = (uint8_t*restrict)dstaddr; - remainder = 0; - - if (PGM_UNLIKELY(len == 0)) - return acc; - is_odd = ((uintptr_t)srcbuf & 1); -/* align first byte */ - if (PGM_UNLIKELY(is_odd)) { - ((uint8_t*restrict)&remainder)[1] = *dstbuf++ = *srcbuf++; - len--; - } -/* 8-byte unrolls, anything larger than 16-byte or less than 8 loses performance */ - count8 = len >> 3; - while (count8--) { - acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; - acc += ((uint16_t*restrict)dstbuf)[ 1 ] = ((const uint16_t*restrict)srcbuf)[ 1 ]; - acc += ((uint16_t*restrict)dstbuf)[ 2 ] = ((const uint16_t*restrict)srcbuf)[ 2 ]; - acc += ((uint16_t*restrict)dstbuf)[ 3 ] = ((const uint16_t*restrict)srcbuf)[ 3 ]; - srcbuf = &srcbuf[ 8 ]; - dstbuf = &dstbuf[ 8 ]; - } - len %= 8; -/* final 7 bytes */ - while (len > 1) { - acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; - srcbuf = &srcbuf[ 2 ]; - dstbuf = &dstbuf[ 2 ]; - len -= 2; - } -/* trailing odd byte */ - if (len > 0) { - ((uint8_t*restrict)&remainder)[0] = *dstbuf = *srcbuf; - } - acc += remainder; - acc = (acc >> 16) + (acc & 0xffff); - acc += (acc >> 16); - if (PGM_UNLIKELY(is_odd)) - acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); - return acc; -} - -static -uint16_t -do_csum_32bit ( - const void* addr, - uint16_t len, - uint32_t csum - ) -{ - uint_fast32_t acc; - const uint8_t* buf; - uint16_t remainder; - uint_fast16_t count; - bool is_odd; - - acc = csum; - buf = (const uint8_t*)addr; - remainder = 0; - - if (PGM_UNLIKELY(len == 0)) - return acc; - is_odd = ((uintptr_t)buf & 1); -/* align first byte */ - if (PGM_UNLIKELY(is_odd)) { - ((uint8_t*)&remainder)[1] = *buf++; - len--; - } -/* 16-bit words */ - count = len >> 1; - if (count) - { - if ((uintptr_t)buf & 2) { - acc += ((const uint16_t*)buf)[ 0 ]; - buf = &buf[ 2 ]; - count--; - len -= 2; - } -/* 32-bit words */ - count >>= 1; - if (count) - { - uint32_t carry = 0; - while (count) { - acc += carry; - acc += ((const uint32_t*)buf)[ 0 ]; - carry = ((const uint32_t*)buf)[ 0 ] > acc; - buf = &buf[ 4 ]; - count--; - } - acc += carry; - acc = (acc >> 16) + (acc & 0xffff); - } - if (len & 2) { - acc += ((const uint16_t*)buf)[ 0 ]; - buf = &buf[ 2 ]; - } - } -/* trailing odd byte */ - if (len & 1) { - ((uint8_t*)&remainder)[0] = *buf; - } - acc += remainder; - acc = (acc >> 16) + (acc & 0xffff); - acc += (acc >> 16); - if (PGM_UNLIKELY(is_odd)) - acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); - return acc; -} - -static -uint16_t -do_csumcpy_32bit ( - const void* restrict srcaddr, - void* restrict dstaddr, - uint16_t len, - uint32_t csum - ) -{ - uint_fast32_t acc; - const uint8_t*restrict srcbuf; - uint8_t*restrict dstbuf; - uint16_t remainder; - uint_fast16_t count; - bool is_odd; - - acc = csum; - srcbuf = (const uint8_t*restrict)srcaddr; - dstbuf = (uint8_t*restrict)dstaddr; - remainder = 0; - - if (PGM_UNLIKELY(len == 0)) - return acc; - is_odd = ((uintptr_t)srcbuf & 1); -/* align first byte */ - if (PGM_UNLIKELY(is_odd)) { - ((uint8_t*restrict)&remainder)[1] = *dstbuf++ = *srcbuf++; - len--; - } -/* 16-bit words */ - count = len >> 1; - if (count) - { - if ((uintptr_t)srcbuf & 2) { - acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; - srcbuf = &srcbuf[ 2 ]; - dstbuf = &dstbuf[ 2 ]; - count--; - len -= 2; - } -/* 32-bit words */ - count >>= 1; - if (count) - { - uint32_t carry = 0; - while (count) { - acc += carry; - acc += ((uint32_t*restrict)dstbuf)[ 0 ] = ((const uint32_t*restrict)srcbuf)[ 0 ]; - carry = ((const uint32_t*restrict)dstbuf)[ 0 ] > acc; - srcbuf = &srcbuf[ 4 ]; - dstbuf = &dstbuf[ 4 ]; - count--; - } - acc += carry; - acc = (acc >> 16) + (acc & 0xffff); - } - if (len & 2) { - acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; - srcbuf = &srcbuf[ 2 ]; - dstbuf = &dstbuf[ 2 ]; - } - } -/* trailing odd byte */ - if (len & 1) { - ((uint8_t*restrict)&remainder)[0] = *dstbuf = *srcbuf; - } - acc += remainder; - acc = (acc >> 16) + (acc & 0xffff); - acc += (acc >> 16); - if (PGM_UNLIKELY(is_odd)) - acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); - return acc; -} - -/* best if architecture has native 64-bit words - */ - -static -uint16_t -do_csum_64bit ( - const void* addr, - uint16_t len, - uint32_t csum - ) -{ - uint_fast64_t acc; - const uint8_t* buf; - uint16_t remainder; - uint_fast16_t count; - bool is_odd; - - acc = csum; - buf = (const uint8_t*)addr; - remainder = 0; - - if (PGM_UNLIKELY(len == 0)) - return acc; - is_odd = ((uintptr_t)buf & 1); -/* align first byte */ - if (PGM_UNLIKELY(is_odd)) { - ((uint8_t*)&remainder)[1] = *buf++; - len--; - } -/* 16-bit words */ - count = len >> 1; - if (count) - { - if ((uintptr_t)buf & 2) { - acc += ((const uint16_t*)buf)[ 0 ]; - buf = &buf[ 2 ]; - count--; - len -= 2; - } -/* 32-bit words */ - count >>= 1; - if (count) - { - if ((uintptr_t)buf & 4) { - acc += ((const uint32_t*)buf)[ 0 ]; - buf = &buf[ 4 ]; - count--; - len -= 4; - } -/* 64-bit words */ - count >>= 1; - if (count) - { - uint_fast64_t carry = 0; - while (count) { - acc += carry; - acc += ((const uint64_t*)buf)[ 0 ]; - carry = ((const uint64_t*)buf)[ 0 ] > acc; - buf = &buf[ 8 ]; - count--; - } - acc += carry; - acc = (acc >> 32) + (acc & 0xffffffff); - } - if (len & 4) { - acc += ((const uint32_t*)buf)[ 0 ]; - buf = &buf[ 4 ]; - } - } - if (len & 2) { - acc += ((const uint16_t*)buf)[ 0 ]; - buf = &buf[ 2 ]; - } - } -/* trailing odd byte */ - if (len & 1) { - ((uint8_t*)&remainder)[0] = *buf; - } - acc += remainder; - acc = (acc >> 32) + (acc & 0xffffffff); - acc = (acc >> 16) + (acc & 0xffff); - acc = (acc >> 16) + (acc & 0xffff); - acc += (acc >> 16); - if (PGM_UNLIKELY(is_odd)) - acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); - return acc; -} - -static -uint16_t -do_csumcpy_64bit ( - const void* restrict srcaddr, - void* restrict dstaddr, - uint16_t len, - uint32_t csum - ) -{ - uint_fast64_t acc; - const uint8_t* restrict srcbuf; - uint8_t* restrict dstbuf; - uint16_t remainder; - uint_fast16_t count; - bool is_odd; - - acc = csum; - srcbuf = (const uint8_t*restrict)srcaddr; - dstbuf = (uint8_t*restrict)dstaddr; - remainder = 0; - - if (PGM_UNLIKELY(len == 0)) - return acc; - is_odd = ((uintptr_t)srcbuf & 1); -/* align first byte */ - if (PGM_UNLIKELY(is_odd)) { - ((uint8_t*restrict)&remainder)[1] = *dstbuf++ = *srcbuf++; - len--; - } -/* 16-bit words */ - count = len >> 1; - if (count) - { - if ((uintptr_t)srcbuf & 2) { - acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; - srcbuf = &srcbuf[ 2 ]; - dstbuf = &dstbuf[ 2 ]; - count--; - len -= 2; - } -/* 32-bit words */ - count >>= 1; - if (count) - { - if ((uintptr_t)srcbuf & 4) { - acc += ((uint32_t*restrict)dstbuf)[ 0 ] = ((const uint32_t*restrict)srcbuf)[ 0 ]; - srcbuf = &srcbuf[ 4 ]; - dstbuf = &dstbuf[ 4 ]; - count--; - len -= 4; - } -/* 64-bit words */ - count >>= 1; - if (count) - { -/* 64-byte blocks */ - uint_fast64_t carry = 0; - uint_fast16_t count64 = count >> 3; - if (count64) - { - carry = 0; - while (count64) { - acc += carry; - acc += ((uint64_t*restrict)dstbuf)[ 0 ] = ((const uint64_t*restrict)srcbuf)[ 0 ]; - carry = ((const uint64_t*restrict)dstbuf)[ 0 ] > acc; - acc += carry; - acc += ((uint64_t*restrict)dstbuf)[ 1 ] = ((const uint64_t*restrict)srcbuf)[ 1 ]; - carry = ((const uint64_t*restrict)dstbuf)[ 1 ] > acc; - acc += carry; - acc += ((uint64_t*restrict)dstbuf)[ 2 ] = ((const uint64_t*restrict)srcbuf)[ 2 ]; - carry = ((const uint64_t*restrict)dstbuf)[ 2 ] > acc; - acc += carry; - acc += ((uint64_t*restrict)dstbuf)[ 3 ] = ((const uint64_t*restrict)srcbuf)[ 3 ]; - carry = ((const uint64_t*restrict)dstbuf)[ 3 ] > acc; - acc += carry; - acc += ((uint64_t*restrict)dstbuf)[ 4 ] = ((const uint64_t*restrict)srcbuf)[ 4 ]; - carry = ((const uint64_t*restrict)dstbuf)[ 4 ] > acc; - acc += carry; - acc += ((uint64_t*restrict)dstbuf)[ 5 ] = ((const uint64_t*restrict)srcbuf)[ 5 ]; - carry = ((const uint64_t*restrict)dstbuf)[ 5 ] > acc; - acc += carry; - acc += ((uint64_t*restrict)dstbuf)[ 6 ] = ((const uint64_t*restrict)srcbuf)[ 6 ]; - carry = ((const uint64_t*restrict)dstbuf)[ 6 ] > acc; - acc += carry; - acc += ((uint64_t*restrict)dstbuf)[ 7 ] = ((const uint64_t*restrict)srcbuf)[ 7 ]; - carry = ((const uint64_t*restrict)dstbuf)[ 7 ] > acc; - srcbuf = &srcbuf[ 64 ]; - dstbuf = &dstbuf[ 64 ]; - count64--; - } - acc += carry; - acc = (acc >> 32) + (acc & 0xffffffff); - count %= 8; - } - -/* last 56 bytes */ - carry = 0; - while (count) { - acc += carry; - acc += ((uint64_t*restrict)dstbuf)[ 0 ] = ((const uint64_t*restrict)srcbuf)[ 0 ]; - carry = ((const uint64_t*restrict)dstbuf)[ 0 ] > acc; - srcbuf = &srcbuf[ 8 ]; - dstbuf = &dstbuf[ 8 ]; - count--; - } - acc += carry; - acc = (acc >> 32) + (acc & 0xffffffff); - } - if (len & 4) { - acc += ((uint32_t*restrict)dstbuf)[ 0 ] = ((const uint32_t*restrict)srcbuf)[ 0 ]; - srcbuf = &srcbuf[ 4 ]; - dstbuf = &dstbuf[ 4 ]; - } - } - if (len & 2) { - acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; - srcbuf = &srcbuf[ 2 ]; - dstbuf = &dstbuf[ 2 ]; - } - } -/* trailing odd byte */ - if (len & 1) { - ((uint8_t*restrict)&remainder)[0] = *dstbuf = *srcbuf; - } - acc += remainder; - acc = (acc >> 32) + (acc & 0xffffffff); - acc = (acc >> 16) + (acc & 0xffff); - acc = (acc >> 16) + (acc & 0xffff); - acc += (acc >> 16); - if (PGM_UNLIKELY(is_odd)) - acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); - return acc; -} - -#if defined(__amd64) || defined(__x86_64__) -/* simd instructions unique to AMD/Intel 64-bit, so always little endian. - */ - -static -uint16_t -do_csum_vector ( - const void* addr, - uint16_t len, - uint32_t csum - ) -{ - uint64_t acc; /* fixed size for asm */ - const uint8_t* buf; - uint16_t remainder; /* fixed size for endian swap */ - uint_fast16_t count; - bool is_odd; - - acc = csum; - buf = (const uint8_t*)addr; - remainder = 0; - - if (PGM_UNLIKELY(len == 0)) - return acc; -/* align first byte */ - is_odd = ((uintptr_t)buf & 1); - if (PGM_UNLIKELY(is_odd)) { - ((uint8_t*)&remainder)[1] = *buf++; - len--; - } -/* 16-bit words */ - count = len >> 1; - if (count) - { - if ((uintptr_t)buf & 2) { - acc += ((const uint16_t*)buf)[ 0 ]; - buf = &buf[ 2 ]; - count--; - len -= 2; - } -/* 32-bit words */ - count >>= 1; - if (count) - { - if ((uintptr_t)buf & 4) { - acc += ((const uint32_t*)buf)[ 0 ]; - buf = &buf[ 4 ]; - count--; - len -= 4; - } -/* 64-bit words */ - count >>= 1; - if (count) - { - uint64_t carry = 0; - while (count) { - asm volatile ( "addq %1, %0\n\t" - "adcq %2, %0" - : "=r" (acc) - : "m" (*(const uint64_t*)buf), "r" (carry), "0" (acc) - : "cc" ); - buf = &buf[ 8 ]; - count--; - } - acc += carry; - acc = (acc >> 32) + (acc & 0xffffffff); - } - if (len & 4) { - acc += ((const uint32_t*)buf)[ 0 ]; - buf = &buf[ 4 ]; - } - } - if (len & 2) { - acc += ((const uint16_t*)buf)[ 0 ]; - buf = &buf[ 2 ]; - } - } -/* trailing odd byte */ - if (len & 1) { - ((uint8_t*)&remainder)[0] = *buf; - } - acc += remainder; - acc = (acc >> 32) + (acc & 0xffffffff); - acc = (acc >> 16) + (acc & 0xffff); - acc = (acc >> 16) + (acc & 0xffff); - acc += (acc >> 16); - if (PGM_UNLIKELY(is_odd)) - acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); - return acc; -} - -static -uint16_t -do_csumcpy_vector ( - const void* restrict srcaddr, - void* restrict dstaddr, - uint16_t len, - uint32_t csum - ) -{ - uint64_t acc; /* fixed size for asm */ - const uint8_t*restrict srcbuf; - uint8_t*restrict dstbuf; - uint16_t remainder; /* fixed size for endian swap */ - uint_fast16_t count; - bool is_odd; - - acc = csum; - srcbuf = (const uint8_t*restrict)srcaddr; - dstbuf = (uint8_t*restrict)dstaddr; - remainder = 0; - - if (PGM_UNLIKELY(len == 0)) - return acc; -/* fill cache line with source buffer, invalidate destination buffer, - * perversly for testing high temporal locality is better than no locality, - * whilst in production no locality may be preferred depending on skb re-use. - */ - pgm_prefetch (srcbuf); - pgm_prefetchw (dstbuf); -/* align first byte */ - is_odd = ((uintptr_t)srcbuf & 1); - if (PGM_UNLIKELY(is_odd)) { - ((uint8_t*restrict)&remainder)[1] = *dstbuf++ = *srcbuf++; - len--; - } -/* 16-bit words */ - count = len >> 1; - if (count) - { - if ((uintptr_t)srcbuf & 2) { - acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; - srcbuf = &srcbuf[ 2 ]; - dstbuf = &dstbuf[ 2 ]; - count--; - len -= 2; - } -/* 32-bit words */ - count >>= 1; - if (count) - { - if ((uintptr_t)srcbuf & 4) { - acc += ((uint32_t*restrict)dstbuf)[ 0 ] = ((const uint32_t*restrict)srcbuf)[ 0 ]; - srcbuf = &srcbuf[ 4 ]; - dstbuf = &dstbuf[ 4 ]; - count--; - len -= 4; - } -/* 64-bit words */ - count >>= 1; - if (count) - { -/* 64-byte blocks */ - uint64_t carry = 0; - uint_fast16_t count64 = count >> 3; - - while (count64) - { - pgm_prefetch (&srcbuf[ 64 ]); - pgm_prefetchw (&dstbuf[ 64 ]); - asm volatile ( "movq 0*8(%1), %%r8\n\t" /* load */ - "movq 1*8(%1), %%r9\n\t" - "movq 2*8(%1), %%r10\n\t" - "movq 3*8(%1), %%r11\n\t" - "movq 4*8(%1), %%r12\n\t" - "movq 5*8(%1), %%r13\n\t" - "movq 6*8(%1), %%r14\n\t" - "movq 7*8(%1), %%r15\n\t" - "adcq %%r8, %0\n\t" /* checksum */ - "adcq %%r9, %0\n\t" - "adcq %%r10, %0\n\t" - "adcq %%r11, %0\n\t" - "adcq %%r12, %0\n\t" - "adcq %%r13, %0\n\t" - "adcq %%r14, %0\n\t" - "adcq %%r15, %0\n\t" - "adcq %3, %0\n\t" - "movq %%r8, 0*8(%2)\n\t" /* save */ - "movq %%r9, 1*8(%2)\n\t" - "movq %%r10, 2*8(%2)\n\t" - "movq %%r11, 3*8(%2)\n\t" - "movq %%r12, 4*8(%2)\n\t" - "movq %%r13, 5*8(%2)\n\t" - "movq %%r14, 6*8(%2)\n\t" - "movq %%r15, 7*8(%2)" - : "=r" (acc) - : "r" (srcbuf), "r" (dstbuf), "r" (carry), "0" (acc) - : "cc", "memory", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" ); - srcbuf = &srcbuf[ 64 ]; - dstbuf = &dstbuf[ 64 ]; - count64--; - } - count %= 8; -/* last 56 bytes */ - while (count) { - asm volatile ( "addq %1, %0\n\t" - "adcq %2, %0" - : "=r" (acc) - : "m" (*(const uint64_t*restrict)srcbuf), "r" (carry), "0" (acc) - : "cc" ); - srcbuf = &srcbuf[ 8 ]; - count--; - } - acc += carry; - acc = (acc >> 32) + (acc & 0xffffffff); - } - if (len & 4) { - acc += ((uint32_t*restrict)dstbuf)[ 0 ] = ((const uint32_t*restrict)srcbuf)[ 0 ]; - srcbuf = &srcbuf[ 4 ]; - dstbuf = &dstbuf[ 4 ]; - } - } - if (len & 2) { - acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; - srcbuf = &srcbuf[ 2 ]; - dstbuf = &dstbuf[ 2 ]; - } - } -/* trailing odd byte */ - if (len & 1) { - ((uint8_t*restrict)&remainder)[0] = *dstbuf = *srcbuf; - } - acc += remainder; - acc = (acc >> 32) + (acc & 0xffffffff); - acc = (acc >> 16) + (acc & 0xffff); - acc = (acc >> 16) + (acc & 0xffff); - acc += (acc >> 16); - if (PGM_UNLIKELY(is_odd)) - acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); - return acc; -} - -#endif - -static inline -uint16_t -do_csum ( - const void* addr, - uint16_t len, - uint32_t csum - ) -{ -#if defined(CONFIG_8BIT_CHECKSUM) - return do_csum_8bit (addr, len, csum); -#elif defined(CONFIG_16BIT_CHECKSUM) - return do_csum_16bit (addr, len, csum); -#elif defined(CONFIG_32BIT_CHECKSUM) - return do_csum_32bit (addr, len, csum); -#elif defined(CONFIG_64BIT_CHECKSUM) - return do_csum_64bit (addr, len, csum); -#elif defined(CONFIG_VECTOR_CHECKSUM) - return do_csum_vector (addr, len, csum); -#else -# error "checksum routine undefined" -#endif -} - -/* Calculate an IP header style checksum - */ - -uint16_t -pgm_inet_checksum ( - const void* addr, - uint16_t len, - uint16_t csum - ) -{ -/* pre-conditions */ - pgm_assert (NULL != addr); - - return ~do_csum (addr, len, csum); -} - -/* Calculate a partial (unfolded) checksum - */ - -uint32_t -pgm_compat_csum_partial ( - const void* addr, - uint16_t len, - uint32_t csum - ) -{ -/* pre-conditions */ - pgm_assert (NULL != addr); - - csum = (csum >> 16) + (csum & 0xffff); - csum += do_csum (addr, len, 0); - csum = (csum >> 16) + (csum & 0xffff); - - return csum; -} - -/* Calculate & copy a partial PGM checksum - */ - -uint32_t -pgm_compat_csum_partial_copy ( - const void* restrict src, - void* restrict dst, - uint16_t len, - uint32_t csum - ) -{ -/* pre-conditions */ - pgm_assert (NULL != src); - pgm_assert (NULL != dst); - -#if defined(CONFIG_8BIT_CHECKSUM) - return do_csumcpy_8bit (src, dst, len, csum); -#elif defined(CONFIG_16BIT_CHECKSUM) - return do_csumcpy_16bit (src, dst, len, csum); -#elif defined(CONFIG_32BIT_CHECKSUM) - return do_csumcpy_32bit (src, dst, len, csum); -#elif defined(CONFIG_64BIT_CHECKSUM) - return do_csumcpy_64bit (src, dst, len, csum); -#elif defined(CONFIG_VECTOR_CHECKSUM) - return do_csumcpy_vector (src, dst, len, csum); -#else - memcpy (dst, src, len); - return pgm_csum_partial (dst, len, csum); -#endif -} - -/* Fold 32 bit checksum accumulator into 16 bit final value. - */ - -uint16_t -pgm_csum_fold ( - uint32_t csum - ) -{ - csum = (csum >> 16) + (csum & 0xffff); - csum += (csum >> 16); - -/* handle special case of no checksum */ - return csum == 0xffff ? csum : ~csum; -} - -/* Add together two unfolded checksum accumulators - */ - -uint32_t -pgm_csum_block_add ( - uint32_t csum, - uint32_t csum2, - const uint16_t offset - ) -{ - if (offset & 1) /* byte magic on odd offset */ - csum2 = ((csum2 & 0xff00ff) << 8) + - ((csum2 >> 8) & 0xff00ff); - - csum += csum2; - return csum + (csum < csum2); -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/checksum_perftest.c b/3rdparty/openpgm-svn-r1085/pgm/checksum_perftest.c deleted file mode 100644 index 678a066..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/checksum_perftest.c +++ /dev/null @@ -1,807 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * performance tests for PGM checksum routines - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define __STDC_FORMAT_MACROS -#include -#include -#include -#include -#include -#include - - -/* mock state */ - -static unsigned perf_testsize = 0; -static unsigned perf_answer = 0; - - -static -void -mock_setup_100b (void) -{ - perf_testsize = 100; - perf_answer = 0x6ea8; -} - -static -void -mock_setup_200b (void) -{ - perf_testsize = 200; - perf_answer = 0x86e1; -} - -static -void -mock_setup_1500b (void) -{ - perf_testsize = 1500; - perf_answer = 0xec69; -} - -static -void -mock_setup_9kb (void) -{ - perf_testsize = 9000; - perf_answer = 0x576e; -} - -static -void -mock_setup_64kb (void) -{ - perf_testsize = 65535; - perf_answer = 0x3fc0; -} - -/* mock functions for external references */ - -size_t -pgm_transport_pkt_offset2 ( - const bool can_fragment, - const bool use_pgmcc - ) -{ - return 0; -} - -#define CHECKSUM_DEBUG -#include "checksum.c" - - -static -void -mock_setup (void) -{ - g_assert (pgm_time_init (NULL)); -} - -static -void -mock_teardown (void) -{ - g_assert (pgm_time_shutdown ()); -} - - -/* target: - * guint16 - * pgm_inet_checksum ( - * const void* src, - * guint len, - * int csum - * ) - */ - -START_TEST (test_8bit) -{ - const unsigned iterations = 1000; - char* source = alloca (perf_testsize); - for (unsigned i = 0, j = 0; i < perf_testsize; i++) { - j = j * 1103515245 + 12345; - source[i] = j; - } - const guint16 answer = perf_answer; /* network order */ - - guint16 csum; - pgm_time_t start, check; - - start = pgm_time_update_now(); - for (unsigned i = iterations; i; i--) { - csum = ~do_csum_8bit (source, perf_testsize, 0); -/* function calculates answer in host order */ - csum = g_htons (csum); - fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); - } - - check = pgm_time_update_now(); - g_message ("8-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", - perf_testsize, - (guint64)(check - start), - (guint64)((check - start) / iterations)); -} -END_TEST - -/* checksum + memcpy */ -START_TEST (test_8bit_memcpy) -{ - const unsigned iterations = 1000; - char* source = alloca (perf_testsize); - char* target = alloca (perf_testsize); - for (unsigned i = 0, j = 0; i < perf_testsize; i++) { - j = j * 1103515245 + 12345; - source[i] = j; - } - const guint16 answer = perf_answer; /* network order */ - - guint16 csum; - pgm_time_t start, check; - - start = pgm_time_update_now(); - for (unsigned i = iterations; i; i--) { - memcpy (target, source, perf_testsize); - csum = ~do_csum_8bit (target, perf_testsize, 0); -/* function calculates answer in host order */ - csum = g_htons (csum); - fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); - } - - check = pgm_time_update_now(); - g_message ("8-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", - perf_testsize, - (guint64)(check - start), - (guint64)((check - start) / iterations)); -} -END_TEST - -/* checksum & copy */ -START_TEST (test_8bit_csumcpy) -{ - const unsigned iterations = 1000; - char* source = alloca (perf_testsize); - char* target = alloca (perf_testsize); - for (unsigned i = 0, j = 0; i < perf_testsize; i++) { - j = j * 1103515245 + 12345; - source[i] = j; - } - const guint16 answer = perf_answer; /* network order */ - - guint16 csum; - pgm_time_t start, check; - - start = pgm_time_update_now(); - for (unsigned i = iterations; i; i--) { - csum = ~do_csumcpy_8bit (source, target, perf_testsize, 0); -/* function calculates answer in host order */ - csum = g_htons (csum); - fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); - } - - check = pgm_time_update_now(); - g_message ("8-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", - perf_testsize, - (guint64)(check - start), - (guint64)((check - start) / iterations)); -} -END_TEST - -START_TEST (test_16bit) -{ - const unsigned iterations = 1000; - char* source = alloca (perf_testsize); - for (unsigned i = 0, j = 0; i < perf_testsize; i++) { - j = j * 1103515245 + 12345; - source[i] = j; - } - const guint16 answer = perf_answer; /* network order */ - - guint16 csum; - pgm_time_t start, check; - - start = pgm_time_update_now(); - for (unsigned i = iterations; i; i--) { - csum = ~do_csum_16bit (source, perf_testsize, 0); -/* function calculates answer in host order */ - csum = g_htons (csum); - fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); - } - - check = pgm_time_update_now(); - g_message ("16-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", - perf_testsize, - (guint64)(check - start), - (guint64)((check - start) / iterations)); -} -END_TEST - -START_TEST (test_16bit_memcpy) -{ - const unsigned iterations = 1000; - char* source = alloca (perf_testsize); - char* target = alloca (perf_testsize); - for (unsigned i = 0, j = 0; i < perf_testsize; i++) { - j = j * 1103515245 + 12345; - source[i] = j; - } - const guint16 answer = perf_answer; /* network order */ - - guint16 csum; - pgm_time_t start, check; - - start = pgm_time_update_now(); - for (unsigned i = iterations; i; i--) { - memcpy (target, source, perf_testsize); - csum = ~do_csum_16bit (target, perf_testsize, 0); -/* function calculates answer in host order */ - csum = g_htons (csum); - fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); - } - - check = pgm_time_update_now(); - g_message ("16-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", - perf_testsize, - (guint64)(check - start), - (guint64)((check - start) / iterations)); -} -END_TEST - -START_TEST (test_16bit_csumcpy) -{ - const unsigned iterations = 1000; - char* source = alloca (perf_testsize); - char* target = alloca (perf_testsize); - for (unsigned i = 0, j = 0; i < perf_testsize; i++) { - j = j * 1103515245 + 12345; - source[i] = j; - } - const guint16 answer = perf_answer; /* network order */ - - guint16 csum; - pgm_time_t start, check; - - start = pgm_time_update_now(); - for (unsigned i = iterations; i; i--) { - csum = ~do_csumcpy_16bit (source, target, perf_testsize, 0); -/* function calculates answer in host order */ - csum = g_htons (csum); - fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); - } - - check = pgm_time_update_now(); - g_message ("16-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", - perf_testsize, - (guint64)(check - start), - (guint64)((check - start) / iterations)); -} -END_TEST - -START_TEST (test_32bit) -{ - const unsigned iterations = 1000; - char* source = alloca (perf_testsize); - for (unsigned i = 0, j = 0; i < perf_testsize; i++) { - j = j * 1103515245 + 12345; - source[i] = j; - } - const guint16 answer = perf_answer; /* network order */ - - guint16 csum; - pgm_time_t start, check; - - start = pgm_time_update_now(); - for (unsigned i = iterations; i; i--) { - csum = ~do_csum_32bit (source, perf_testsize, 0); -/* function calculates answer in host order */ - csum = g_htons (csum); - fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); - } - - check = pgm_time_update_now(); - g_message ("32-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", - perf_testsize, - (guint64)(check - start), - (guint64)((check - start) / iterations)); -} -END_TEST - -START_TEST (test_32bit_memcpy) -{ - const unsigned iterations = 1000; - char* source = alloca (perf_testsize); - char* target = alloca (perf_testsize); - for (unsigned i = 0, j = 0; i < perf_testsize; i++) { - j = j * 1103515245 + 12345; - source[i] = j; - } - const guint16 answer = perf_answer; /* network order */ - - guint16 csum; - pgm_time_t start, check; - - start = pgm_time_update_now(); - for (unsigned i = iterations; i; i--) { - memcpy (target, source, perf_testsize); - csum = ~do_csum_32bit (target, perf_testsize, 0); -/* function calculates answer in host order */ - csum = g_htons (csum); - fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); - } - - check = pgm_time_update_now(); - g_message ("32-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", - perf_testsize, - (guint64)(check - start), - (guint64)((check - start) / iterations)); -} -END_TEST - -START_TEST (test_32bit_csumcpy) -{ - const unsigned iterations = 1000; - char* source = alloca (perf_testsize); - char* target = alloca (perf_testsize); - for (unsigned i = 0, j = 0; i < perf_testsize; i++) { - j = j * 1103515245 + 12345; - source[i] = j; - } - const guint16 answer = perf_answer; /* network order */ - - guint16 csum; - pgm_time_t start, check; - - start = pgm_time_update_now(); - for (unsigned i = iterations; i; i--) { - csum = ~do_csumcpy_32bit (source, target, perf_testsize, 0); -/* function calculates answer in host order */ - csum = g_htons (csum); - fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); - } - - check = pgm_time_update_now(); - g_message ("32-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", - perf_testsize, - (guint64)(check - start), - (guint64)((check - start) / iterations)); -} -END_TEST - -START_TEST (test_64bit) -{ - const unsigned iterations = 1000; - char* source = alloca (perf_testsize); - for (unsigned i = 0, j = 0; i < perf_testsize; i++) { - j = j * 1103515245 + 12345; - source[i] = j; - } - const guint16 answer = perf_answer; /* network order */ - - guint16 csum; - pgm_time_t start, check; - - start = pgm_time_update_now(); - for (unsigned i = iterations; i; i--) { - csum = ~do_csum_64bit (source, perf_testsize, 0); -/* function calculates answer in host order */ - csum = g_htons (csum); - fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); - } - - check = pgm_time_update_now(); - g_message ("64-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", - perf_testsize, - (guint64)(check - start), - (guint64)((check - start) / iterations)); -} -END_TEST - -START_TEST (test_64bit_memcpy) -{ - const unsigned iterations = 1000; - char* source = alloca (perf_testsize); - char* target = alloca (perf_testsize); - for (unsigned i = 0, j = 0; i < perf_testsize; i++) { - j = j * 1103515245 + 12345; - source[i] = j; - } - const guint16 answer = perf_answer; /* network order */ - - guint16 csum; - pgm_time_t start, check; - - start = pgm_time_update_now(); - for (unsigned i = iterations; i; i--) { - memcpy (target, source, perf_testsize); - csum = ~do_csum_64bit (target, perf_testsize, 0); -/* function calculates answer in host order */ - csum = g_htons (csum); - fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); - } - - check = pgm_time_update_now(); - g_message ("64-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", - perf_testsize, - (guint64)(check - start), - (guint64)((check - start) / iterations)); -} -END_TEST - -START_TEST (test_64bit_csumcpy) -{ - const unsigned iterations = 1000; - char* source = alloca (perf_testsize); - char* target = alloca (perf_testsize); - for (unsigned i = 0, j = 0; i < perf_testsize; i++) { - j = j * 1103515245 + 12345; - source[i] = j; - } - const guint16 answer = perf_answer; /* network order */ - - guint16 csum; - pgm_time_t start, check; - - start = pgm_time_update_now(); - for (unsigned i = iterations; i; i--) { - csum = ~do_csumcpy_64bit (source, target, perf_testsize, 0); -/* function calculates answer in host order */ - csum = g_htons (csum); - fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); - } - - check = pgm_time_update_now(); - g_message ("64-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", - perf_testsize, - (guint64)(check - start), - (guint64)((check - start) / iterations)); -} -END_TEST - -#if defined(__amd64) || defined(__x86_64__) -START_TEST (test_vector) -{ - const unsigned iterations = 1000; - char* source = alloca (perf_testsize); - for (unsigned i = 0, j = 0; i < perf_testsize; i++) { - j = j * 1103515245 + 12345; - source[i] = j; - } - const guint16 answer = perf_answer; /* network order */ - - guint16 csum; - pgm_time_t start, check; - - start = pgm_time_update_now(); - for (unsigned i = iterations; i; i--) { - csum = ~do_csum_vector (source, perf_testsize, 0); -/* function calculates answer in host order */ - csum = g_htons (csum); - fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); - } - - check = pgm_time_update_now(); - g_message ("vector/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", - perf_testsize, - (guint64)(check - start), - (guint64)((check - start) / iterations)); -} -END_TEST - -START_TEST (test_vector_memcpy) -{ - const unsigned iterations = 1000; - char* source = alloca (perf_testsize); - char* target = alloca (perf_testsize); - for (unsigned i = 0, j = 0; i < perf_testsize; i++) { - j = j * 1103515245 + 12345; - source[i] = j; - } - const guint16 answer = perf_answer; /* network order */ - - guint16 csum; - pgm_time_t start, check; - - start = pgm_time_update_now(); - for (unsigned i = iterations; i; i--) { - memcpy (target, source, perf_testsize); - csum = ~do_csum_vector (target, perf_testsize, 0); -/* function calculates answer in host order */ - csum = g_htons (csum); - fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); - } - - check = pgm_time_update_now(); - g_message ("vector/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", - perf_testsize, - (guint64)(check - start), - (guint64)((check - start) / iterations)); -} -END_TEST - -START_TEST (test_vector_csumcpy) -{ - const unsigned iterations = 1000; - char* source = alloca (perf_testsize); - char* target = alloca (perf_testsize); - for (unsigned i = 0, j = 0; i < perf_testsize; i++) { - j = j * 1103515245 + 12345; - source[i] = j; - } - const guint16 answer = perf_answer; /* network order */ - - guint16 csum; - pgm_time_t start, check; - - start = pgm_time_update_now(); - for (unsigned i = iterations; i; i--) { - csum = ~do_csumcpy_vector (source, target, perf_testsize, 0); -/* function calculates answer in host order */ - csum = g_htons (csum); - fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); - } - - check = pgm_time_update_now(); - g_message ("vector/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", - perf_testsize, - (guint64)(check - start), - (guint64)((check - start) / iterations)); -} -END_TEST -#endif /* defined(__amd64) || defined(__x86_64__) */ - - - -static -Suite* -make_csum_performance_suite (void) -{ - Suite* s; - - s = suite_create ("Raw checksum performance"); - - TCase* tc_100b = tcase_create ("100b"); - suite_add_tcase (s, tc_100b); - tcase_add_checked_fixture (tc_100b, mock_setup, mock_teardown); - tcase_add_checked_fixture (tc_100b, mock_setup_100b, NULL); - tcase_add_test (tc_100b, test_8bit); - tcase_add_test (tc_100b, test_16bit); - tcase_add_test (tc_100b, test_32bit); - tcase_add_test (tc_100b, test_64bit); -#if defined(__amd64) || defined(__x86_64__) - tcase_add_test (tc_100b, test_vector); -#endif - - TCase* tc_200b = tcase_create ("200b"); - suite_add_tcase (s, tc_200b); - tcase_add_checked_fixture (tc_200b, mock_setup, mock_teardown); - tcase_add_checked_fixture (tc_200b, mock_setup_200b, NULL); - tcase_add_test (tc_200b, test_8bit); - tcase_add_test (tc_200b, test_16bit); - tcase_add_test (tc_200b, test_32bit); - tcase_add_test (tc_200b, test_64bit); -#if defined(__amd64) || defined(__x86_64__) - tcase_add_test (tc_200b, test_vector); -#endif - - TCase* tc_1500b = tcase_create ("1500b"); - suite_add_tcase (s, tc_1500b); - tcase_add_checked_fixture (tc_1500b, mock_setup, mock_teardown); - tcase_add_checked_fixture (tc_1500b, mock_setup_1500b, NULL); - tcase_add_test (tc_1500b, test_8bit); - tcase_add_test (tc_1500b, test_16bit); - tcase_add_test (tc_1500b, test_32bit); - tcase_add_test (tc_1500b, test_64bit); -#if defined(__amd64) || defined(__x86_64__) - tcase_add_test (tc_1500b, test_vector); -#endif - - TCase* tc_9kb = tcase_create ("9KB"); - suite_add_tcase (s, tc_9kb); - tcase_add_checked_fixture (tc_9kb, mock_setup, mock_teardown); - tcase_add_checked_fixture (tc_9kb, mock_setup_9kb, NULL); - tcase_add_test (tc_9kb, test_8bit); - tcase_add_test (tc_9kb, test_16bit); - tcase_add_test (tc_9kb, test_32bit); - tcase_add_test (tc_9kb, test_64bit); -#if defined(__amd64) || defined(__x86_64__) - tcase_add_test (tc_9kb, test_vector); -#endif - - TCase* tc_64kb = tcase_create ("64KB"); - suite_add_tcase (s, tc_64kb); - tcase_add_checked_fixture (tc_64kb, mock_setup, mock_teardown); - tcase_add_checked_fixture (tc_64kb, mock_setup_64kb, NULL); - tcase_add_test (tc_64kb, test_8bit); - tcase_add_test (tc_64kb, test_16bit); - tcase_add_test (tc_64kb, test_32bit); - tcase_add_test (tc_64kb, test_64bit); -#if defined(__amd64) || defined(__x86_64__) - tcase_add_test (tc_64kb, test_vector); -#endif - - return s; -} - -static -Suite* -make_csum_memcpy_performance_suite (void) -{ - Suite* s; - - s = suite_create ("Checksum and memcpy performance"); - - TCase* tc_100b = tcase_create ("100b"); - suite_add_tcase (s, tc_100b); - tcase_add_checked_fixture (tc_100b, mock_setup, mock_teardown); - tcase_add_checked_fixture (tc_100b, mock_setup_100b, NULL); - tcase_add_test (tc_100b, test_8bit_memcpy); - tcase_add_test (tc_100b, test_16bit_memcpy); - tcase_add_test (tc_100b, test_32bit_memcpy); - tcase_add_test (tc_100b, test_64bit_memcpy); -#if defined(__amd64) || defined(__x86_64__) - tcase_add_test (tc_100b, test_vector_memcpy); -#endif - - TCase* tc_200b = tcase_create ("200b"); - suite_add_tcase (s, tc_200b); - tcase_add_checked_fixture (tc_200b, mock_setup, mock_teardown); - tcase_add_checked_fixture (tc_200b, mock_setup_200b, NULL); - tcase_add_test (tc_200b, test_8bit_memcpy); - tcase_add_test (tc_200b, test_16bit_memcpy); - tcase_add_test (tc_200b, test_32bit_memcpy); - tcase_add_test (tc_200b, test_64bit_memcpy); -#if defined(__amd64) || defined(__x86_64__) - tcase_add_test (tc_200b, test_vector_memcpy); -#endif - - TCase* tc_1500b = tcase_create ("1500b"); - suite_add_tcase (s, tc_1500b); - tcase_add_checked_fixture (tc_1500b, mock_setup, mock_teardown); - tcase_add_checked_fixture (tc_1500b, mock_setup_1500b, NULL); - tcase_add_test (tc_1500b, test_8bit_memcpy); - tcase_add_test (tc_1500b, test_16bit_memcpy); - tcase_add_test (tc_1500b, test_32bit_memcpy); - tcase_add_test (tc_1500b, test_64bit_memcpy); -#if defined(__amd64) || defined(__x86_64__) - tcase_add_test (tc_1500b, test_vector_memcpy); -#endif - - TCase* tc_9kb = tcase_create ("9KB"); - suite_add_tcase (s, tc_9kb); - tcase_add_checked_fixture (tc_9kb, mock_setup, mock_teardown); - tcase_add_checked_fixture (tc_9kb, mock_setup_9kb, NULL); - tcase_add_test (tc_9kb, test_8bit_memcpy); - tcase_add_test (tc_9kb, test_16bit_memcpy); - tcase_add_test (tc_9kb, test_32bit_memcpy); - tcase_add_test (tc_9kb, test_64bit_memcpy); -#if defined(__amd64) || defined(__x86_64__) - tcase_add_test (tc_9kb, test_vector_memcpy); -#endif - - TCase* tc_64kb = tcase_create ("64KB"); - suite_add_tcase (s, tc_64kb); - tcase_add_checked_fixture (tc_64kb, mock_setup, mock_teardown); - tcase_add_checked_fixture (tc_64kb, mock_setup_64kb, NULL); - tcase_add_test (tc_64kb, test_8bit_memcpy); - tcase_add_test (tc_64kb, test_16bit_memcpy); - tcase_add_test (tc_64kb, test_32bit_memcpy); - tcase_add_test (tc_64kb, test_64bit_memcpy); -#if defined(__amd64) || defined(__x86_64__) - tcase_add_test (tc_64kb, test_vector_memcpy); -#endif - - return s; -} - -static -Suite* -make_csumcpy_performance_suite (void) -{ - Suite* s; - - s = suite_create ("Checksum copy performance"); - - TCase* tc_100b = tcase_create ("100b"); - suite_add_tcase (s, tc_100b); - tcase_add_checked_fixture (tc_100b, mock_setup, mock_teardown); - tcase_add_checked_fixture (tc_100b, mock_setup_100b, NULL); - tcase_add_test (tc_100b, test_8bit_csumcpy); - tcase_add_test (tc_100b, test_16bit_csumcpy); - tcase_add_test (tc_100b, test_32bit_csumcpy); - tcase_add_test (tc_100b, test_64bit_csumcpy); -#if defined(__amd64) || defined(__x86_64__) - tcase_add_test (tc_100b, test_vector_csumcpy); -#endif - - TCase* tc_200b = tcase_create ("200b"); - suite_add_tcase (s, tc_200b); - tcase_add_checked_fixture (tc_200b, mock_setup, mock_teardown); - tcase_add_checked_fixture (tc_200b, mock_setup_200b, NULL); - tcase_add_test (tc_200b, test_8bit_csumcpy); - tcase_add_test (tc_200b, test_16bit_csumcpy); - tcase_add_test (tc_200b, test_32bit_csumcpy); - tcase_add_test (tc_200b, test_64bit_csumcpy); -#if defined(__amd64) || defined(__x86_64__) - tcase_add_test (tc_200b, test_vector_csumcpy); -#endif - - TCase* tc_1500b = tcase_create ("1500b"); - suite_add_tcase (s, tc_1500b); - tcase_add_checked_fixture (tc_1500b, mock_setup, mock_teardown); - tcase_add_checked_fixture (tc_1500b, mock_setup_1500b, NULL); - tcase_add_test (tc_1500b, test_8bit_csumcpy); - tcase_add_test (tc_1500b, test_16bit_csumcpy); - tcase_add_test (tc_1500b, test_32bit_csumcpy); - tcase_add_test (tc_1500b, test_64bit_csumcpy); -#if defined(__amd64) || defined(__x86_64__) - tcase_add_test (tc_1500b, test_vector_csumcpy); -#endif - - TCase* tc_9kb = tcase_create ("9KB"); - suite_add_tcase (s, tc_9kb); - tcase_add_checked_fixture (tc_9kb, mock_setup, mock_teardown); - tcase_add_checked_fixture (tc_9kb, mock_setup_9kb, NULL); - tcase_add_test (tc_9kb, test_8bit_csumcpy); - tcase_add_test (tc_9kb, test_16bit_csumcpy); - tcase_add_test (tc_9kb, test_32bit_csumcpy); - tcase_add_test (tc_9kb, test_64bit_csumcpy); -#if defined(__amd64) || defined(__x86_64__) - tcase_add_test (tc_9kb, test_vector_csumcpy); -#endif - - TCase* tc_64kb = tcase_create ("64KB"); - suite_add_tcase (s, tc_64kb); - tcase_add_checked_fixture (tc_64kb, mock_setup, mock_teardown); - tcase_add_checked_fixture (tc_64kb, mock_setup_64kb, NULL); - tcase_add_test (tc_64kb, test_8bit_csumcpy); - tcase_add_test (tc_64kb, test_16bit_csumcpy); - tcase_add_test (tc_64kb, test_32bit_csumcpy); - tcase_add_test (tc_64kb, test_64bit_csumcpy); -#if defined(__amd64) || defined(__x86_64__) - tcase_add_test (tc_64kb, test_vector_csumcpy); -#endif - - return s; -} - - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_csum_performance_suite ()); - srunner_add_suite (sr, make_csum_memcpy_performance_suite ()); - srunner_add_suite (sr, make_csumcpy_performance_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/checksum_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/checksum_unittest.c deleted file mode 100644 index a25d36a..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/checksum_unittest.c +++ /dev/null @@ -1,278 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for PGM checksum routines - * - * Copyright (c) 2009-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include - - -/* mock state */ - - -/* mock functions for external references */ - -size_t -pgm_transport_pkt_offset2 ( - const bool can_fragment, - const bool use_pgmcc - ) -{ - return 0; -} - - -#define CHECKSUM_DEBUG -#include "checksum.c" - - -/* target: - * uint16_t - * pgm_inet_checksum ( - * const void* src, - * uint16_t len, - * uint16_t csum - * ) - */ - -START_TEST (test_inet_pass_001) -{ - const char source[] = "i am not a string"; - const guint16 answer = 0x1fda; /* network order */ - - guint16 csum = pgm_inet_checksum (source, sizeof(source), 0); -/* function calculates answer in host order */ - csum = g_htons (csum); - g_message ("IP checksum of \"%s\" (%d) is %u (%u)", - source, sizeof(source), csum, answer); - - fail_unless (answer == csum, "checksum mismatch"); -} -END_TEST - -START_TEST (test_inet_pass_002) -{ - char* source = alloca (65535); - for (unsigned i = 0, j = 0; i < 65535; i++) { - j = j * 1103515245 + 12345; - source[i] = j; - } - const guint16 answer = 0x3fc0; /* network order */ - - guint16 csum = pgm_inet_checksum (source, 65535, 0); -/* function calculates answer in host order */ - csum = g_htons (csum); - g_message ("IP checksum of 64KB is %u (%u)", - csum, answer); - - fail_unless (answer == csum, "checksum mismatch"); -} -END_TEST - -START_TEST (test_inet_fail_001) -{ - pgm_inet_checksum (NULL, 0, 0); - fail ("reached"); -} -END_TEST - -/* target: - * guint16 - * pgm_csum_fold ( - * guint32 csum - * ) - */ - -START_TEST (test_fold_pass_001) -{ - const guint32 csum = 0xdd250300; /* network order */ - const guint16 answer = 0x1fda; - - guint16 folded_csum = pgm_csum_fold (g_ntohl (csum)); - folded_csum = g_htons (folded_csum); - g_message ("32-bit checksum %u folds into %u (%u)", csum, folded_csum, answer); - - fail_unless (answer == folded_csum, "folded checksum mismatch"); -} -END_TEST - - -/* target: - * guint32 - * pgm_csum_partial ( - * const void* src, - * guint len, - * guint32 sum - * ) - */ - -START_TEST (test_partial_pass_001) -{ - const char source[] = "i am not a string"; -#if __BYTE_ORDER == __BIG_ENDIAN - const guint32 answer = 0x0000e025; /* network order */ -#elif __BYTE_ORDER == __LITTLE_ENDIAN - const guint32 answer = 0xe0250000; /* network order */ -#else -# error "__BYTE_ORDER not supported." -#endif - - guint32 csum = pgm_csum_partial (source, sizeof(source), 0); - csum = g_htonl (csum); - g_message ("Partial checksum of \"%s\" is %u (%u)", source, csum, answer); - - fail_unless (answer == csum, "checksum mismatch"); -} -END_TEST - -START_TEST (test_partial_fail_001) -{ - pgm_csum_partial (NULL, 0, 0); - fail ("reached"); -} -END_TEST - -/* target: - * guint32 - * pgm_csum_partial_copy ( - * const void* src, - * void* dst, - * guint len, - * guint32 sum - * ) - */ - -START_TEST (test_partial_copy_pass_001) -{ - const char source[] = "i am not a string"; -#if __BYTE_ORDER == __BIG_ENDIAN - const guint32 answer = 0x0000e025; /* network order */ -#elif __BYTE_ORDER == __LITTLE_ENDIAN - const guint32 answer = 0xe0250000; /* network order */ -#else -# error "__BYTE_ORDER not supported." -#endif - - char dest[1024]; - guint32 csum_source = pgm_csum_partial_copy (source, dest, sizeof(source), 0); - csum_source = g_htonl (csum_source); - guint32 csum_dest = pgm_csum_partial (dest, sizeof(source), 0); - csum_dest = g_htonl (csum_dest); - g_message ("Partial copy checksum of \"%s\" is %u, partial checksum is %u (%u)", - source, csum_source, csum_dest, answer); - fail_unless (answer == csum_source, "checksum mismatch in partial-copy"); - fail_unless (answer == csum_dest, "checksum mismatch in partial"); -} -END_TEST - -START_TEST (test_partial_copy_fail_001) -{ - pgm_csum_partial_copy (NULL, NULL, 0, 0); - fail ("reached"); -} -END_TEST - -/* target: - * guint32 - * pgm_csum_block_add ( - * guint32 csum, - * guint32 csum2, - * guint offset - * ) - */ - -START_TEST (test_block_add_pass_001) -{ - const char source[] = "i am not a string"; - const guint16 answer = 0x1fda; /* network order */ - - guint32 csum_a = pgm_csum_partial (source, sizeof(source) / 2, 0); - guint32 csum_b = pgm_csum_partial (source + (sizeof(source) / 2), sizeof(source) - (sizeof(source) / 2), 0); - guint32 csum = pgm_csum_block_add (csum_a, csum_b, sizeof(source) / 2); - guint16 fold = pgm_csum_fold (csum); -/* convert to display in network order */ - csum_a = g_htonl (csum_a); - csum_b = g_htonl (csum_b); - csum = g_htonl (csum); - fold = g_htons (fold); - g_message ("Checksum A:%u + B:%u = %u -> %u (%u)", - csum_a, csum_b, csum, fold, answer); - fail_unless (answer == fold, "checksum mismatch"); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_inet = tcase_create ("inet"); - suite_add_tcase (s, tc_inet); - tcase_add_test (tc_inet, test_inet_pass_001); - tcase_add_test (tc_inet, test_inet_pass_002); - tcase_add_test_raise_signal (tc_inet, test_inet_fail_001, SIGABRT); - - TCase* tc_fold = tcase_create ("fold"); - suite_add_tcase (s, tc_fold); - tcase_add_test (tc_fold, test_fold_pass_001); - - TCase* tc_block_add = tcase_create ("block-add"); - suite_add_tcase (s, tc_block_add); - tcase_add_test (tc_block_add, test_block_add_pass_001); - - TCase* tc_partial = tcase_create ("partial"); - suite_add_tcase (s, tc_partial); - tcase_add_test (tc_partial, test_partial_pass_001); - tcase_add_test_raise_signal (tc_partial, test_partial_fail_001, SIGABRT); - - TCase* tc_partial_copy = tcase_create ("partial-copy"); - suite_add_tcase (s, tc_partial_copy); - tcase_add_test (tc_partial_copy, test_partial_copy_pass_001); - tcase_add_test_raise_signal (tc_partial_copy, test_partial_copy_fail_001, SIGABRT); - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/crossmingw.py b/3rdparty/openpgm-svn-r1085/pgm/crossmingw.py deleted file mode 100644 index 2981506..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/crossmingw.py +++ /dev/null @@ -1,144 +0,0 @@ -import os -import os.path -import string - -import SCons.Action -import SCons.Builder -import SCons.Tool -import SCons.Util - -# This is what we search for to find mingw: -prefixes = SCons.Util.Split(""" - mingw32- - i386-mingw32msvc- - i486-mingw32msvc- - i586-mingw32msvc- - i686-mingw32msvc- -""") - -def find(env): - for prefix in prefixes: - # First search in the SCons path and then the OS path: - if env.WhereIs(prefix + 'gcc') or SCons.Util.WhereIs(prefix + 'gcc'): - return prefix - - return '' - -def shlib_generator(target, source, env, for_signature): - cmd = SCons.Util.CLVar(['$SHLINK', '$SHLINKFLAGS']) - - dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') - if dll: cmd.extend(['-o', dll]) - - cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS']) - - implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') - if implib: cmd.append('-Wl,--out-implib,'+implib.get_string(for_signature)) - - def_target = env.FindIxes(target, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX') - if def_target: cmd.append('-Wl,--output-def,'+def_target.get_string(for_signature)) - - return [cmd] - -def shlib_emitter(target, source, env): - dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') - no_import_lib = env.get('no_import_lib', 0) - - if not dll: - raise SCons.Errors.UserError, "A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX") - - if not no_import_lib and \ - not env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX'): - - # Append an import library to the list of targets. - target.append(env.ReplaceIxes(dll, - 'SHLIBPREFIX', 'SHLIBSUFFIX', - 'LIBPREFIX', 'LIBSUFFIX')) - - # Append a def file target if there isn't already a def file target - # or a def file source. There is no option to disable def file - # target emitting, because I can't figure out why someone would ever - # want to turn it off. - def_source = env.FindIxes(source, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX') - def_target = env.FindIxes(target, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX') - if not def_source and not def_target: - target.append(env.ReplaceIxes(dll, - 'SHLIBPREFIX', 'SHLIBSUFFIX', - 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX')) - - return (target, source) - -# TODO: Backported to old scons version -#shlib_action = SCons.Action.CommandGenerator(shlib_generator) -shlib_action = SCons.Action.Action(shlib_generator,generator=1) - -res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR') - -res_builder = SCons.Builder.Builder(action=res_action, suffix='.o', - source_scanner=SCons.Tool.SourceFileScanner) -SCons.Tool.SourceFileScanner.add_scanner('.rc', SCons.Defaults.CScan) - -def generate(env): - mingw_prefix = find(env) - - if mingw_prefix: - dir = os.path.dirname(env.WhereIs(mingw_prefix + 'gcc') or SCons.Util.WhereIs(mingw_prefix + 'gcc')) - - # The mingw bin directory must be added to the path: - path = env['ENV'].get('PATH', []) - if not path: - path = [] - if SCons.Util.is_String(path): - path = string.split(path, os.pathsep) - - env['ENV']['PATH'] = string.join([dir] + path, os.pathsep) - - # Most of mingw is the same as gcc and friends... - gnu_tools = ['gcc', 'g++', 'gnulink', 'ar', 'gas'] - for tool in gnu_tools: - SCons.Tool.Tool(tool)(env) - - #... but a few things differ: - env['CC'] = mingw_prefix + 'gcc' - env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') - env['CXX'] = mingw_prefix + 'g++' - env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') - env['SHLINKCOM'] = shlib_action - env.Append(SHLIBEMITTER = [shlib_emitter]) - # This line isn't required and breaks C++ linking - #env['LINK'] = mingw_prefix + 'g++' - env['AS'] = mingw_prefix + 'as' - env['AR'] = mingw_prefix + 'ar' - env['RANLIB'] = mingw_prefix + 'ranlib' - env['WIN32DEFPREFIX'] = '' - env['WIN32DEFSUFFIX'] = '.def' - env['SHOBJSUFFIX'] = '.o' - env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 - - env['RC'] = mingw_prefix + 'windres' - env['RCFLAGS'] = SCons.Util.CLVar('') - env['RCINCFLAGS'] = '$( ${_concat(RCINCPREFIX, CPPPATH, RCINCSUFFIX, __env__, RDirs, TARGET)} $)' - env['RCINCPREFIX'] = '--include-dir ' - env['RCINCSUFFIX'] = '' - env['RCCOM'] = '$RC $RCINCFLAGS $RCINCPREFIX $SOURCE.dir $RCFLAGS -i $SOURCE -o $TARGET' - env['BUILDERS']['RES'] = res_builder - - # Some setting from the platform also have to be overridden: - env['OBJPREFIX'] = '' - env['OBJSUFFIX'] = '.o' - env['LIBPREFIX'] = 'lib' - env['LIBSUFFIX'] = '.a' - env['SHOBJPREFIX'] = '$OBJPREFIX' - env['SHOBJSUFFIX'] = '$OBJSUFFIX' - env['PROGPREFIX'] = '' - env['PROGSUFFIX'] = '.exe' - env['LIBPREFIX'] = '' - env['LIBSUFFIX'] = '.lib' - env['SHLIBPREFIX'] = '' - env['SHLIBSUFFIX'] = '.dll' - env['LIBPREFIXES'] = [ '$LIBPREFIX' ] - env['LIBSUFFIXES'] = [ '$LIBSUFFIX' ] - -def exists(env): - return find(env) diff --git a/3rdparty/openpgm-svn-r1085/pgm/crossmingw64.py b/3rdparty/openpgm-svn-r1085/pgm/crossmingw64.py deleted file mode 100644 index 111e0ed..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/crossmingw64.py +++ /dev/null @@ -1,140 +0,0 @@ -import os -import os.path -import string - -import SCons.Action -import SCons.Builder -import SCons.Tool -import SCons.Util - -# This is what we search for to find mingw: -prefixes = SCons.Util.Split(""" - x86_64-w64-mingw32- -""") - -def find(env): - for prefix in prefixes: - # First search in the SCons path and then the OS path: - if env.WhereIs(prefix + 'gcc') or SCons.Util.WhereIs(prefix + 'gcc'): - return prefix - - return '' - -def shlib_generator(target, source, env, for_signature): - cmd = SCons.Util.CLVar(['$SHLINK', '$SHLINKFLAGS']) - - dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') - if dll: cmd.extend(['-o', dll]) - - cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS']) - - implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') - if implib: cmd.append('-Wl,--out-implib,'+implib.get_string(for_signature)) - - def_target = env.FindIxes(target, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX') - if def_target: cmd.append('-Wl,--output-def,'+def_target.get_string(for_signature)) - - return [cmd] - -def shlib_emitter(target, source, env): - dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') - no_import_lib = env.get('no_import_lib', 0) - - if not dll: - raise SCons.Errors.UserError, "A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX") - - if not no_import_lib and \ - not env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX'): - - # Append an import library to the list of targets. - target.append(env.ReplaceIxes(dll, - 'SHLIBPREFIX', 'SHLIBSUFFIX', - 'LIBPREFIX', 'LIBSUFFIX')) - - # Append a def file target if there isn't already a def file target - # or a def file source. There is no option to disable def file - # target emitting, because I can't figure out why someone would ever - # want to turn it off. - def_source = env.FindIxes(source, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX') - def_target = env.FindIxes(target, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX') - if not def_source and not def_target: - target.append(env.ReplaceIxes(dll, - 'SHLIBPREFIX', 'SHLIBSUFFIX', - 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX')) - - return (target, source) - -# TODO: Backported to old scons version -#shlib_action = SCons.Action.CommandGenerator(shlib_generator) -shlib_action = SCons.Action.Action(shlib_generator,generator=1) - -res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR') - -res_builder = SCons.Builder.Builder(action=res_action, suffix='.o', - source_scanner=SCons.Tool.SourceFileScanner) -SCons.Tool.SourceFileScanner.add_scanner('.rc', SCons.Defaults.CScan) - -def generate(env): - mingw_prefix = find(env) - - if mingw_prefix: - dir = os.path.dirname(env.WhereIs(mingw_prefix + 'gcc') or SCons.Util.WhereIs(mingw_prefix + 'gcc')) - - # The mingw bin directory must be added to the path: - path = env['ENV'].get('PATH', []) - if not path: - path = [] - if SCons.Util.is_String(path): - path = string.split(path, os.pathsep) - - env['ENV']['PATH'] = string.join([dir] + path, os.pathsep) - - # Most of mingw is the same as gcc and friends... - gnu_tools = ['gcc', 'g++', 'gnulink', 'ar', 'gas'] - for tool in gnu_tools: - SCons.Tool.Tool(tool)(env) - - #... but a few things differ: - env['CC'] = mingw_prefix + 'gcc' - env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') - env['CXX'] = mingw_prefix + 'g++' - env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') - env['SHLINKCOM'] = shlib_action - env.Append(SHLIBEMITTER = [shlib_emitter]) - # This line isn't required and breaks C++ linking - #env['LINK'] = mingw_prefix + 'g++' - env['AS'] = mingw_prefix + 'as' - env['AR'] = mingw_prefix + 'ar' - env['RANLIB'] = mingw_prefix + 'ranlib' - env['WIN32DEFPREFIX'] = '' - env['WIN32DEFSUFFIX'] = '.def' - env['SHOBJSUFFIX'] = '.o' - env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 - - env['RC'] = mingw_prefix + 'windres' - env['RCFLAGS'] = SCons.Util.CLVar('') - env['RCINCFLAGS'] = '$( ${_concat(RCINCPREFIX, CPPPATH, RCINCSUFFIX, __env__, RDirs, TARGET)} $)' - env['RCINCPREFIX'] = '--include-dir ' - env['RCINCSUFFIX'] = '' - env['RCCOM'] = '$RC $RCINCFLAGS $RCINCPREFIX $SOURCE.dir $RCFLAGS -i $SOURCE -o $TARGET' - env['BUILDERS']['RES'] = res_builder - - # Some setting from the platform also have to be overridden: - env['OBJPREFIX'] = '' - env['OBJSUFFIX'] = '.o' - env['LIBPREFIX'] = 'lib' - env['LIBSUFFIX'] = '.a' - env['SHOBJPREFIX'] = '$OBJPREFIX' - env['SHOBJSUFFIX'] = '$OBJSUFFIX' - env['PROGPREFIX'] = '' - env['PROGSUFFIX'] = '.exe' - env['LIBPREFIX'] = '' - env['LIBSUFFIX'] = '.lib' - env['SHLIBPREFIX'] = '' - env['SHLIBSUFFIX'] = '.dll' - env['LIBPREFIXES'] = [ '$LIBPREFIX' ] - env['LIBSUFFIXES'] = [ '$LIBSUFFIX' ] - -def exists(env): - return find(env) diff --git a/3rdparty/openpgm-svn-r1085/pgm/engine.c b/3rdparty/openpgm-svn-r1085/pgm/engine.c deleted file mode 100644 index 994bca2..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/engine.c +++ /dev/null @@ -1,277 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * PGM engine. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _WIN32 -# include -#endif -#include -#include -#include -#include -#include -#include -#include - - -//#define ENGINE_DEBUG - - -/* globals */ -int pgm_ipproto_pgm PGM_GNUC_READ_MOSTLY = IPPROTO_PGM; - -#ifdef _WIN32 -LPFN_WSARECVMSG pgm_WSARecvMsg PGM_GNUC_READ_MOSTLY = NULL; -#endif - -#ifdef PGM_DEBUG -unsigned pgm_loss_rate PGM_GNUC_READ_MOSTLY = 0; -#endif - -/* locals */ -static bool pgm_is_supported = FALSE; -static volatile uint32_t pgm_ref_count = 0; - -#ifdef _WIN32 -# ifndef WSAID_WSARECVMSG -/* http://cvs.winehq.org/cvsweb/wine/include/mswsock.h */ -# define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}} -# endif -#endif - - -/* startup PGM engine, mainly finding PGM protocol definition, if any from NSS - * - * returns TRUE on success, returns FALSE if an error occurred, implying some form of - * system re-configuration is required to resolve before trying again. - * - * NB: Valgrind loves generating errors in getprotobyname(). - */ -bool -pgm_init ( - pgm_error_t** error - ) -{ - if (pgm_atomic_exchange_and_add32 (&pgm_ref_count, 1) > 0) - return TRUE; - -/* initialise dependent modules */ - pgm_messages_init(); - - pgm_minor ("OpenPGM %d.%d.%d%s%s%s %s %s %s %s", - pgm_major_version, pgm_minor_version, pgm_micro_version, - pgm_build_revision ? " (" : "", pgm_build_revision ? pgm_build_revision : "", pgm_build_revision ? ")" : "", - pgm_build_date, pgm_build_time, pgm_build_system, pgm_build_machine); - - pgm_thread_init(); - pgm_mem_init(); - pgm_rand_init(); - -#ifdef _WIN32 - WORD wVersionRequested = MAKEWORD (2, 2); - WSADATA wsaData; - if (WSAStartup (wVersionRequested, &wsaData) != 0) - { - const int save_errno = WSAGetLastError (); - pgm_set_error (error, - PGM_ERROR_DOMAIN_ENGINE, - pgm_error_from_wsa_errno (save_errno), - _("WSAStartup failure: %s"), - pgm_wsastrerror (save_errno)); - goto err_shutdown; - } - - if (LOBYTE (wsaData.wVersion) != 2 || HIBYTE (wsaData.wVersion) != 2) - { - WSACleanup(); - pgm_set_error (error, - PGM_ERROR_DOMAIN_ENGINE, - PGM_ERROR_FAILED, - _("WSAStartup failed to provide requested version 2.2.")); - goto err_shutdown; - } - -# ifndef CONFIG_TARGET_WINE -/* find WSARecvMsg API */ - if (NULL == pgm_WSARecvMsg) { - GUID WSARecvMsg_GUID = WSAID_WSARECVMSG; - DWORD cbBytesReturned; - const SOCKET sock = socket (AF_INET, SOCK_DGRAM, 0); - if (SOCKET_ERROR == sock) { - WSACleanup(); - pgm_set_error (error, - PGM_ERROR_DOMAIN_ENGINE, - PGM_ERROR_FAILED, - _("Cannot open socket.")); - goto err_shutdown; - } - if (SOCKET_ERROR == WSAIoctl (sock, - SIO_GET_EXTENSION_FUNCTION_POINTER, - &WSARecvMsg_GUID, sizeof(WSARecvMsg_GUID), - &pgm_WSARecvMsg, sizeof(pgm_WSARecvMsg), - &cbBytesReturned, - NULL, - NULL)) - { - closesocket (sock); - WSACleanup(); - pgm_set_error (error, - PGM_ERROR_DOMAIN_ENGINE, - PGM_ERROR_FAILED, - _("WSARecvMsg function not found.")); - goto err_shutdown; - } - pgm_debug ("Retrieved address of WSARecvMsg."); - closesocket (sock); - } -# endif -#endif /* _WIN32 */ - -/* find PGM protocol id overriding default value, use first value from NIS */ -#ifdef CONFIG_HAVE_GETPROTOBYNAME_R - char b[1024]; - struct protoent protobuf; - const struct protoent* proto = getprotobyname_r ("pgm", &protobuf, b, sizeof(b)); - if (NULL != proto) { - if (proto->p_proto != pgm_ipproto_pgm) { - pgm_minor (_("Setting PGM protocol number to %i from /etc/protocols."), - proto->p_proto); - pgm_ipproto_pgm = proto->p_proto; - } - } -#elif defined(CONFIG_HAVE_GETPROTOBYNAME_R2) - char b[1024]; - struct protoent protobuf, *proto; - const int e = getprotobyname_r ("pgm", &protobuf, b, sizeof(b), &proto); - if (e != -1 && proto != NULL) { - if (proto->p_proto != pgm_ipproto_pgm) { - pgm_minor (_("Setting PGM protocol number to %i from /etc/protocols."), - proto->p_proto); - pgm_ipproto_pgm = proto->p_proto; - } - } -#else - const struct protoent *proto = getprotobyname ("pgm"); - if (proto != NULL) { - if (proto->p_proto != pgm_ipproto_pgm) { -#ifndef _WIN32 - pgm_minor (_("Setting PGM protocol number to %i from /etc/protocols."), - proto->p_proto); -#else - pgm_minor (_("Setting PGM protocol number to %i from %%SYSTEMROOT%%\\system32\\drivers\\etc\\protocols."), - proto->p_proto); -#endif - pgm_ipproto_pgm = proto->p_proto; - } - } -#endif - -/* ensure timing enabled */ - pgm_error_t* sub_error = NULL; - if (!pgm_time_init (&sub_error)) { - if (sub_error) - pgm_propagate_error (error, sub_error); -#ifdef _WIN32 - WSACleanup(); -#endif - goto err_shutdown; - } - -/* receiver simulated loss rate */ -#ifdef PGM_DEBUG - const char *loss_rate = getenv ("PGM_LOSS_RATE"); - if (NULL != loss_rate) { - int value = atoi (loss_rate); - if (value > 0 && value <= 100) { - pgm_loss_rate = value; - pgm_minor (_("Setting PGM packet loss rate to %i%%."), pgm_loss_rate); - } - } -#endif - -/* create global sock list lock */ - pgm_rwlock_init (&pgm_sock_list_lock); - - pgm_is_supported = TRUE; - return TRUE; - -err_shutdown: - pgm_rand_shutdown(); - pgm_mem_shutdown(); - pgm_thread_shutdown(); - pgm_messages_shutdown(); - pgm_atomic_dec32 (&pgm_ref_count); - return FALSE; -} - -/* returns TRUE if PGM engine has been initialized - */ - -bool -pgm_supported (void) -{ - return ( pgm_is_supported == TRUE ); -} - -/* returns TRUE on success, returns FALSE if an error occurred. - */ - -bool -pgm_shutdown (void) -{ - pgm_return_val_if_fail (pgm_atomic_read32 (&pgm_ref_count) > 0, FALSE); - - if (pgm_atomic_exchange_and_add32 (&pgm_ref_count, (uint32_t)-1) != 1) - return TRUE; - - pgm_is_supported = FALSE; - -/* destroy all open socks */ - while (pgm_sock_list) { - pgm_close ((pgm_sock_t*)pgm_sock_list->data, FALSE); - } - - pgm_time_shutdown(); - -#ifdef _WIN32 - WSACleanup(); -#endif - - pgm_rand_shutdown(); - pgm_mem_shutdown(); - pgm_thread_shutdown(); - pgm_messages_shutdown(); - return TRUE; -} - -/* helper to drop out of setuid 0 after creating PGM sockets - */ -void -pgm_drop_superuser (void) -{ -#ifndef _WIN32 - if (0 == getuid()) { - setuid((gid_t)65534); - setgid((uid_t)65534); - } -#endif -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/engine_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/engine_unittest.c deleted file mode 100644 index f434e35..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/engine_unittest.c +++ /dev/null @@ -1,232 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for PGM engine. - * - * Copyright (c) 2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include - - -/* mock state */ - -struct pgm_rwlock_t; -struct pgm_slist_t; - -static gint mock_time_init = 0; -static struct pgm_rwlock_t mock_pgm_sock_list_lock; -static struct pgm_slist_t* mock_pgm_sock_list = NULL; - -#define pgm_time_init mock_pgm_time_init -#define pgm_time_shutdown mock_pgm_time_shutdown -#define pgm_close mock_pgm_close -#define pgm_sock_list_lock mock_pgm_sock_list_lock -#define pgm_sock_list mock_pgm_sock_list - -#define ENGINE_DEBUG -#include "engine.c" - - -static -void -mock_setup (void) -{ - mock_time_init = FALSE; -} - -static -void -mock_teardown (void) -{ -// null -} - - -/* mock functions for external references */ - -size_t -pgm_pkt_offset ( - const bool can_fragment, - const sa_family_t pgmcc_family /* 0 = disable */ - ) -{ - return 0; -} - -PGM_GNUC_INTERNAL -bool -mock_pgm_time_init ( - pgm_error_t** error - ) -{ - mock_time_init++; - return TRUE; -} - -PGM_GNUC_INTERNAL -bool -mock_pgm_time_shutdown (void) -{ - if (!mock_time_init) - return FALSE; - mock_time_init--; - return TRUE; -} - -bool -mock_pgm_close ( - pgm_sock_t* sock, - bool flush - ) -{ - return TRUE; -} - - -/* target: - * bool - * pgm_init (pgm_error_t** error) - */ - -/* reference counting on init */ -START_TEST (test_init_pass_001) -{ - fail_unless (TRUE == pgm_init (NULL), "init failed"); - fail_unless (TRUE == pgm_init (NULL), "init failed"); -} -END_TEST - -/* timing module already init */ -START_TEST (test_init_pass_003) -{ - pgm_error_t* err = NULL; - fail_unless (TRUE == pgm_time_init (&err), "time-init failed: %s", (err && err->message) ? err->message : "(null)"); - fail_unless (TRUE == pgm_init (&err), "init failed: %s", (err && err->message) ? err->message : "(null)"); - fail_unless (TRUE == pgm_init (&err), "init failed: %s", (err && err->message) ? err->message : "(null)"); -} -END_TEST - -/* target: - * bool - * pgm_shutdown (void) - */ - -START_TEST (test_shutdown_pass_001) -{ - fail_unless (TRUE == pgm_init (NULL), "init failed"); - fail_unless (TRUE == pgm_shutdown (), "shutdown failed"); -} -END_TEST - -/* no init */ -START_TEST (test_shutdown_pass_002) -{ - fail_unless (FALSE == pgm_shutdown (), "shutdown failed"); -} -END_TEST - -/* double call */ -START_TEST (test_shutdown_pass_003) -{ - fail_unless (TRUE == pgm_init (NULL), "init failed"); - fail_unless (TRUE == pgm_shutdown (), "shutdown failed"); - fail_unless (FALSE == pgm_shutdown (), "shutdown failed"); -} -END_TEST - -/* check reference counting */ -START_TEST (test_shutdown_pass_004) -{ - fail_unless (TRUE == pgm_init (NULL), "init failed"); - fail_unless (TRUE == pgm_init (NULL), "init failed"); - fail_unless (TRUE == pgm_shutdown (), "shutdown failed"); - fail_unless (TRUE == pgm_shutdown (), "shutdown failed"); - fail_unless (FALSE == pgm_shutdown (), "shutdown failed"); -} -END_TEST - -/* target: - * bool - * pgm_supported (void) - */ - -START_TEST (test_supported_pass_001) -{ - fail_unless (FALSE == pgm_supported(), "supported failed"); - fail_unless (TRUE == pgm_init (NULL), "init failed"); - fail_unless (TRUE == pgm_supported(), "supported failed"); - fail_unless (TRUE == pgm_shutdown (), "shutdown failed"); - fail_unless (FALSE == pgm_supported(), "supported failed"); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_init = tcase_create ("init"); - tcase_add_checked_fixture (tc_init, mock_setup, mock_teardown); - suite_add_tcase (s, tc_init); - tcase_add_test (tc_init, test_init_pass_001); - tcase_add_test (tc_init, test_init_pass_003); - - TCase* tc_shutdown = tcase_create ("shutdown"); - tcase_add_checked_fixture (tc_shutdown, mock_setup, mock_teardown); - suite_add_tcase (s, tc_shutdown); - tcase_add_test (tc_shutdown, test_shutdown_pass_001); - tcase_add_test (tc_shutdown, test_shutdown_pass_002); - tcase_add_test (tc_shutdown, test_shutdown_pass_003); - tcase_add_test (tc_shutdown, test_shutdown_pass_004); - - TCase* tc_supported = tcase_create ("supported"); - tcase_add_checked_fixture (tc_supported, mock_setup, mock_teardown); - suite_add_tcase (s, tc_supported); - tcase_add_test (tc_supported, test_supported_pass_001); - - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/error.c b/3rdparty/openpgm-svn-r1085/pgm/error.c deleted file mode 100644 index 3f3fe30..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/error.c +++ /dev/null @@ -1,518 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable error reporting. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _WIN32 -# include -#endif -#include -#include - - -//#define ERROR_DEBUG - - -#define ERROR_OVERWRITTEN_WARNING "pgm_error_t set over the top of a previous pgm_error_t or uninitialized memory.\n" \ - "This indicates a bug. You must ensure an error is NULL before it's set.\n" \ - "The overwriting error message was: %s" - -static pgm_error_t* pgm_error_new_valist (const int, const int, const char*, va_list) PGM_GNUC_PRINTF(3, 0); -static void pgm_error_add_prefix (char**restrict, const char*restrict, va_list) PGM_GNUC_PRINTF(2, 0); - - -static -pgm_error_t* -pgm_error_new_valist ( - const int error_domain, - const int error_code, - const char* format, - va_list args - ) -{ - pgm_error_t *error = pgm_new (pgm_error_t, 1); - error->domain = error_domain; - error->code = error_code; - error->message = pgm_strdup_vprintf (format, args); - return error; -} - -void -pgm_error_free ( - pgm_error_t* error - ) -{ - pgm_return_if_fail (error != NULL); - pgm_free (error->message); - pgm_free (error); -} - -void -pgm_set_error ( - pgm_error_t** restrict err, - const int error_domain, - const int error_code, - const char* restrict format, - ... - ) -{ - pgm_error_t *new; - va_list args; - - if (NULL == err) - return; - - va_start (args, format); - new = pgm_error_new_valist (error_domain, error_code, format, args); - va_end (args); - - if (NULL == *err) - *err = new; - else - pgm_warn (_(ERROR_OVERWRITTEN_WARNING), new->message); -} - -void -pgm_propagate_error ( - pgm_error_t** restrict dest, - pgm_error_t* restrict src - ) -{ - pgm_return_if_fail (src != NULL); - - if (NULL == dest) { - if (src) - pgm_error_free (src); - return; - } else { - if (NULL != *dest) { - pgm_warn (_(ERROR_OVERWRITTEN_WARNING), src->message); - } else { - *dest = src; - } - } -} - -void -pgm_clear_error ( - pgm_error_t** err - ) -{ - if (err && *err) { - pgm_error_free (*err); - *err = NULL; - } -} - -static -void -pgm_error_add_prefix ( - char** restrict string, - const char* restrict format, - va_list ap - ) -{ - char* prefix = pgm_strdup_vprintf (format, ap); - char* oldstring = *string; - *string = pgm_strconcat (prefix, oldstring, NULL); - pgm_free (oldstring); - pgm_free (prefix); -} - -void -pgm_prefix_error ( - pgm_error_t** restrict err, - const char* restrict format, - ... - ) -{ - if (err && *err) { - va_list ap; - va_start (ap, format); - pgm_error_add_prefix (&(*err)->message, format, ap); - va_end (ap); - } -} - -/* error from libc. - */ - -int -pgm_error_from_errno ( - const int from_errno - ) -{ - switch (from_errno) { -#ifdef EAFNOSUPPORT - case EAFNOSUPPORT: - return PGM_ERROR_AFNOSUPPORT; - break; -#endif - -#ifdef EAGAIN - case EAGAIN: - return PGM_ERROR_AGAIN; - break; -#endif - -#ifdef EBADF - case EBADF: - return PGM_ERROR_BADF; - break; -#endif - -#ifdef ECONNRESET - case ECONNRESET: - return PGM_ERROR_CONNRESET; - break; -#endif - -#ifdef EFAULT - case EFAULT: - return PGM_ERROR_FAULT; - break; -#endif - -#ifdef EINTR - case EINTR: - return PGM_ERROR_INTR; - break; -#endif - -#ifdef EINVAL - case EINVAL: - return PGM_ERROR_INVAL; - break; -#endif - -#ifdef EMFILE - case EMFILE: - return PGM_ERROR_MFILE; - break; -#endif - -#ifdef ENFILE - case ENFILE: - return PGM_ERROR_NFILE; - break; -#endif - -#ifdef ENODEV - case ENODEV: - return PGM_ERROR_NODEV; - break; -#endif - -#ifdef ENOENT - case ENOENT: - return PGM_ERROR_NOENT; - break; -#endif - -#ifdef ENOMEM - case ENOMEM: - return PGM_ERROR_NOMEM; - break; -#endif - -#ifdef ENONET - case ENONET: - return PGM_ERROR_NONET; - break; -#endif - -#ifdef ENOPROTOOPT - case ENOPROTOOPT: - return PGM_ERROR_NOPROTOOPT; - break; -#endif - -#ifdef ENOTUNIQ - case ENOTUNIQ: - return PGM_ERROR_NOTUNIQ; - break; -#endif - -#ifdef ENXIO - case ENXIO: - return PGM_ERROR_NXIO; - break; -#endif - -#ifdef EPERM - case EPERM: - return PGM_ERROR_PERM; - break; -#endif - -#ifdef EPROTO - case EPROTO: - return PGM_ERROR_PROTO; - break; -#endif - -#ifdef ERANGE - case ERANGE: - return PGM_ERROR_RANGE; - break; -#endif - -#ifdef EXDEV - case EXDEV: - return PGM_ERROR_XDEV; - break; -#endif - - default : - return PGM_ERROR_FAILED; - break; - } -} - -/* h_errno from gethostbyname. - */ - -int -pgm_error_from_h_errno ( - const int from_h_errno - ) -{ - switch (from_h_errno) { -#ifdef HOST_NOT_FOUND - case HOST_NOT_FOUND: - return PGM_ERROR_NONAME; - break; -#endif - -#ifdef TRY_AGAIN - case TRY_AGAIN: - return PGM_ERROR_AGAIN; - break; -#endif - -#ifdef NO_RECOVERY - case NO_RECOVERY: - return PGM_ERROR_FAIL; - break; -#endif - -#ifdef NO_DATA - case NO_DATA: - return PGM_ERROR_NODATA; - break; -#endif - - default: - return PGM_ERROR_FAILED; - break; - } -} - -/* errno must be preserved before calling to catch correct error - * status with EAI_SYSTEM. - */ - -int -pgm_error_from_eai_errno ( - const int from_eai_errno, -#ifdef EAI_SYSTEM - const int from_errno -#else - PGM_GNUC_UNUSED const int from_errno -#endif - ) -{ - switch (from_eai_errno) { -#ifdef EAI_ADDRFAMILY - case EAI_ADDRFAMILY: - return PGM_ERROR_ADDRFAMILY; - break; -#endif - -#ifdef EAI_AGAIN - case EAI_AGAIN: - return PGM_ERROR_AGAIN; - break; -#endif - -#ifdef EAI_BADFLAGS - case EAI_BADFLAGS: - return PGM_ERROR_INVAL; - break; -#endif - -#ifdef EAI_FAIL - case EAI_FAIL: - return PGM_ERROR_FAIL; - break; -#endif - -#ifdef EAI_FAMILY - case EAI_FAMILY: - return PGM_ERROR_AFNOSUPPORT; - break; -#endif - -#ifdef EAI_MEMORY - case EAI_MEMORY: - return PGM_ERROR_NOMEM; - break; -#endif - -#ifdef EAI_NODATA - case EAI_NODATA: - return PGM_ERROR_NODATA; - break; -#endif - -#if defined(EAI_NONAME) && EAI_NONAME != EAI_NODATA - case EAI_NONAME: - return PGM_ERROR_NONAME; - break; -#endif - -#ifdef EAI_SERVICE - case EAI_SERVICE: - return PGM_ERROR_SERVICE; - break; -#endif - -#ifdef EAI_SOCKTYPE - case EAI_SOCKTYPE: - return PGM_ERROR_SOCKTNOSUPPORT; - break; -#endif - -#ifdef EAI_SYSTEM - case EAI_SYSTEM: - return pgm_error_from_errno (from_errno); - break; -#endif - - default : - return PGM_ERROR_FAILED; - break; - } -} - -/* from WSAGetLastError() - */ - -int -pgm_error_from_wsa_errno ( - const int from_wsa_errno - ) -{ - switch (from_wsa_errno) { -#ifdef WSAEINVAL - case WSAEINVAL: - return PGM_ERROR_INVAL; - break; -#endif -#ifdef WSAEMFILE - case WSAEMFILE: - return PGM_ERROR_MFILE; - break; -#endif -#ifdef WSA_NOT_ENOUGH_MEMORY - case WSA_NOT_ENOUGH_MEMORY: - return PGM_ERROR_NOMEM; - break; -#endif -#ifdef WSAENOPROTOOPT - case WSAENOPROTOOPT: - return PGM_ERROR_NOPROTOOPT; - break; -#endif -#ifdef WSAECONNRESET - case WSAECONNRESET: - return PGM_ERROR_CONNRESET; - break; -#endif - - default : - return PGM_ERROR_FAILED; - break; - } -} - -/* from Last-Error codes, i.e. Windows non-WinSock and non-DOS errors. - */ - -int -pgm_error_from_win_errno ( - const int from_win_errno - ) -{ - switch (from_win_errno) { -#ifdef ERROR_ADDRESS_NOT_ASSOCIATED - case ERROR_ADDRESS_NOT_ASSOCIATED: - return PGM_ERROR_NODATA; - break; -#endif - -#ifdef ERROR_BUFFER_OVERFLOW - case ERROR_BUFFER_OVERFLOW: - return PGM_ERROR_NOBUFS; - break; -#endif - -#ifdef ERROR_INVALID_DATA - case ERROR_INVALID_DATA: - return PGM_ERROR_BADE; - break; -#endif - -#ifdef ERROR_INSUFFICIENT_BUFFER - case ERROR_INSUFFICIENT_BUFFER: - return PGM_ERROR_NOMEM; - break; -#endif - -#ifdef ERROR_INVALID_PARAMETER - case ERROR_INVALID_PARAMETER: - return PGM_ERROR_INVAL; - break; -#endif - -#ifdef ERROR_NOT_ENOUGH_MEMORY - case ERROR_NOT_ENOUGH_MEMORY: - return PGM_ERROR_NOMEM; - break; -#endif - -#ifdef ERROR_NO_DATA - case ERROR_NO_DATA: - return PGM_ERROR_NODATA; - break; -#endif - -#ifdef ERROR_NOT_SUPPORTED - case ERROR_NOT_SUPPORTED: - return PGM_ERROR_NOSYS; - break; -#endif - - default : - return PGM_ERROR_FAILED; - break; - } -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/error_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/error_unittest.c deleted file mode 100644 index 035c0f3..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/error_unittest.c +++ /dev/null @@ -1,292 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for error reporting. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include -#include - - -/* mock state */ - - - -/* mock functions for external references */ - -size_t -pgm_transport_pkt_offset2 ( - const bool can_fragment, - const bool use_pgmcc - ) -{ - return 0; -} - - -#define ERROR_DEBUG -#include "error.c" - - -/* target: - * void - * pgm_set_error ( - * pgm_error_t** err, - * int err_domain, - * int err_code, - * const char* format, - * ... - * ) - */ - -START_TEST (test_set_error_pass_001) -{ - pgm_error_t* err = NULL; - const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; - const gint err_code = 100; - pgm_set_error (&err, err_domain, err_code, "an error occurred."); - fail_unless (NULL != err); -} -END_TEST - -START_TEST (test_set_error_pass_002) -{ - pgm_error_t* err = NULL; - const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; - const gint err_code = 100; - pgm_set_error (&err, err_domain, err_code, "an error occurred: value=%d.", 123); - fail_unless (NULL != err); -} -END_TEST - -/* ignore NULL error */ -START_TEST (test_set_error_pass_003) -{ - pgm_error_t* err = NULL; - const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; - const gint err_code = 100; - pgm_set_error (NULL, err_domain, err_code, "an error occurred."); -} -END_TEST - -/* overwritten error */ -START_TEST (test_set_error_pass_004) -{ - pgm_error_t* err = NULL; - const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; - const gint err_code = 100; - pgm_set_error (&err, err_domain, err_code, "an error occurred."); - fail_unless (NULL != err); - pgm_set_error (&err, err_domain, err_code, "another error occurred."); -} -END_TEST - -/* target: - * void - * pgm_prefix_error ( - * pgm_error_t** err, - * const char* format, - * ... - * ) - */ - -START_TEST (test_prefix_error_pass_001) -{ - pgm_error_t* err = NULL; - const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; - const gint err_code = 100; - pgm_set_error (&err, err_domain, err_code, "an error occurred."); - fail_unless (NULL != err); - pgm_prefix_error (&err, "i am a prefix:"); - pgm_prefix_error (&err, "i am another prefix, value=%d:", 123); -} -END_TEST - -/* ignore null original error */ -START_TEST (test_prefix_error_pass_002) -{ - pgm_error_t* err = NULL; - pgm_prefix_error (&err, "i am a prefix:"); -} -END_TEST - -/* target: - * void - * pgm_propagate_error ( - * pgm_error_t** dest, - * pgm_error_t* src, - * ) - */ - -START_TEST (test_propagate_error_pass_001) -{ - pgm_error_t* dest = NULL; - pgm_error_t* err = NULL; - const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; - const gint err_code = 100; - pgm_set_error (&err, err_domain, err_code, "an error occurred."); - fail_unless (NULL != err); - pgm_propagate_error (&dest, err); - fail_unless (NULL != dest); -} -END_TEST - -/* ignore NULL destination */ -START_TEST (test_propagate_error_pass_002) -{ - pgm_error_t* err = NULL; - const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; - const gint err_code = 100; - pgm_set_error (&err, err_domain, err_code, "an error occurred."); - fail_unless (NULL != err); - pgm_propagate_error (NULL, err); -} -END_TEST - -/* src error SHOULD be valid */ -START_TEST (test_propagate_error_pass_003) -{ - pgm_error_t* dest = NULL; - pgm_error_t* err = NULL; - pgm_propagate_error (&dest, err); -} -END_TEST - -/* target: - * void - * pgm_clear_error ( - * pgm_error_t** err - * ) - */ - -START_TEST (test_clear_error_pass_001) -{ - pgm_error_t* err = NULL; - const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; - const gint err_code = 100; - pgm_set_error (&err, err_domain, err_code, "an error occurred."); - fail_unless (NULL != err); - pgm_clear_error (&err); - fail_unless (NULL == err); -} -END_TEST - -START_TEST (test_clear_error_pass_002) -{ - pgm_error_t* err = NULL; - pgm_clear_error (&err); - fail_unless (NULL == err); -} -END_TEST - -START_TEST (test_clear_error_pass_003) -{ - pgm_clear_error (NULL); -} -END_TEST - -/* target: - * void - * pgm_error_free ( - * pgm_error_t* err - * ) - */ - -START_TEST (test_error_free_pass_001) -{ - pgm_error_t* err = NULL; - const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; - const gint err_code = 100; - pgm_set_error (&err, err_domain, err_code, "an error occurred."); - fail_unless (NULL != err); - pgm_error_free (err); -} -END_TEST - -START_TEST (test_error_free_pass_002) -{ - pgm_error_free (NULL); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_set_error = tcase_create ("set-error"); - suite_add_tcase (s, tc_set_error); - tcase_add_test (tc_set_error, test_set_error_pass_001); - tcase_add_test (tc_set_error, test_set_error_pass_002); - tcase_add_test (tc_set_error, test_set_error_pass_003); - tcase_add_test (tc_set_error, test_set_error_pass_004); - - TCase* tc_prefix_error = tcase_create ("prefix-error"); - suite_add_tcase (s, tc_prefix_error); - tcase_add_test (tc_prefix_error, test_prefix_error_pass_001); - tcase_add_test (tc_prefix_error, test_prefix_error_pass_002); - - TCase* tc_propagate_error = tcase_create ("propagate-error"); - suite_add_tcase (s, tc_propagate_error); - tcase_add_test (tc_propagate_error, test_propagate_error_pass_001); - tcase_add_test (tc_propagate_error, test_propagate_error_pass_002); - tcase_add_test (tc_propagate_error, test_propagate_error_pass_003); - - TCase* tc_clear_error = tcase_create ("clear-error"); - suite_add_tcase (s, tc_clear_error); - tcase_add_test (tc_clear_error, test_clear_error_pass_001); - tcase_add_test (tc_clear_error, test_clear_error_pass_002); - tcase_add_test (tc_clear_error, test_clear_error_pass_003); - - TCase* tc_error_free = tcase_create ("error-free"); - suite_add_tcase (s, tc_error_free); - tcase_add_test (tc_error_free, test_error_free_pass_001); - tcase_add_test (tc_error_free, test_error_free_pass_002); - - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/SConscript b/3rdparty/openpgm-svn-r1085/pgm/examples/SConscript deleted file mode 100644 index 46ebce3..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/SConscript +++ /dev/null @@ -1,88 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script -# $Id$ - -import os; - -Import('env'); -e = env.Clone(); -e.Prepend(LIBS = ['libpgm']); -p = e.Clone(); -if '-DCONFIG_HAVE_GETOPT' in env['CCFLAGS']: - getopt = [] -else: - getopt = ['getopt.c'] - -if e['WITH_GLIB'] == 'true': - e.Prepend(LIBS = ['libpgmex']); - e.MergeFlags(env['GLIB_FLAGS']); - e2 = e.Clone(); - if e2['WITH_SNMP'] == 'true': - e2.Append(CCFLAGS = ['-DCONFIG_WITH_SNMP']); - e2.Prepend(LIBS = ['libpgmsnmp']); - e2.MergeFlags(e['SNMP_FLAGS']); - if e2['WITH_HTTP'] == 'true': - e2.Append(CCFLAGS = ['-DCONFIG_WITH_HTTP']); - e2.Prepend(LIBS = ['libpgmhttp']); - -# core preferred examples - e.Program(['pgmdump.c']) - e2.Program(['pgmsend.c']) - e2.Program(['pgmrecv.c']) - -# sync examples - e.Program(['blocksyncrecv.c']) - e.Program(['snonblocksyncrecv.c']) - if '-DCONFIG_HAVE_POLL' in e['CCFLAGS']: - e.Program(['pnonblocksyncrecv.c']) - -# epoll based examples - if '-DCONFIG_HAVE_EPOLL' in e['CCFLAGS']: - e.Program(['enonblocksyncrecv.c']) - e.Program(['enonblocksyncrecvmsg.c']) - e.Program(['enonblocksyncrecvmsgv.c']) - -# ncurses examples - if e['WITH_NCURSES'] == 'true': - en = e.Clone() - en.Append(LIBS = ['panel', 'ncurses']); - en.Program(['pgmtop.c']) - -# Google Protocol Buffer example - if e['WITH_PROTOBUF'] == 'true': - ep = e2.Clone(); - newCCFLAGS = []; - for flag in ep['CCFLAGS']: - if ("-W" != flag[:2]) and ("-std=gnu99" != flag[:10]) and ("-pedantic" != flag[:9]) and ("-D_XOPEN_SOURCE=600" != flag[:19]) and ("-xc99=all" != flag[:9]): - newCCFLAGS.append(flag); - if ("-D_XOPEN_SOURCE=600" == flag[:19]): - newCCFLAGS.append("-D_XOPEN_SOURCE=500"); - ep['CCFLAGS'] = newCCFLAGS; - ep.Append(CPPPATH = '.'); - ep.Append(CCFLAGS = ep['PROTOBUF_CCFLAGS']); - ep.Depends('pgmping.cc', ['ping.pb.cc', 'ping.pb.h']); - protobuf = Builder(action = 'cd ${SOURCE.dir} && %s ${SOURCE.file} --cpp_out=../${TARGET.dir}' % ep['PROTOBUF_PROTOC']) - ep.Append(BUILDERS = {'Protobuf' : protobuf}) - ep.Protobuf('ping.pb.cc', 'ping.proto') - ep.Program(['pgmping.cc', 'ping.pb.cc', ep['PROTOBUF_LIBS']]) - -# Vanilla example -p.Program(['purinsend.c'] + getopt) -p.Program(['purinrecv.c'] + getopt) -p.Program(['daytime.c'] + getopt) -p.Program(['shortcakerecv.c', 'async.c'] + getopt) - -# Vanilla C++ example -if e['WITH_CC'] == 'true': - pcc = p.Clone(); - newCCFLAGS = []; - for flag in pcc['CCFLAGS']: - if ("-W" != flag[:2]) and ("-std=gnu99" != flag[:10]) and ("-pedantic" != flag[:9]) and ("-D_XOPEN_SOURCE=600" != flag[:19]) and ("-xc99=all" != flag[:9]): - newCCFLAGS.append(flag); - if ("-D_XOPEN_SOURCE=600" == flag[:19]): - newCCFLAGS.append("-D_XOPEN_SOURCE=500"); - pcc['CCFLAGS'] = newCCFLAGS; - pcc.Program('purinsendcc', ['purinsendcc.cc'] + p.Object(getopt)) - pcc.Program('purinrecvcc', ['purinrecvcc.cc'] + p.Object(getopt)) - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/SConscript89 b/3rdparty/openpgm-svn-r1085/pgm/examples/SConscript89 deleted file mode 100644 index 5595d3d..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/SConscript89 +++ /dev/null @@ -1,41 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script -# $Id$ - -import os; - -Import('env'); -e = env.Clone(); -e.Prepend(LIBS = ['libpgm89']); -p = e.Clone(); -if '-DCONFIG_HAVE_GETOPT' in env['CCFLAGS']: - getopt = [] -else: - getopt = ['getopt.c'] - -c89source = Builder(action = 'perl -p -e "s/%z(u|d)/%l\\1/g" $SOURCE > $TARGET', - suffix = '.c89.c', - src_suffix = '.c', - single_source = 1); -p.Append(BUILDERS = {'C89Source' : c89source}) - -for c99file in ['purinsend.c', 'purinrecv.c']: - p.C89Source(c99file); - -p.Program('purinsend', ['purinsend.c89.c'] + getopt) -p.Program('purinrecv', ['purinrecv.c89.c'] + getopt) - -# Vanilla C++ example -if e['WITH_CC'] == 'true': - pcc = p.Clone(); - newCCFLAGS = []; - for flag in pcc['CCFLAGS']: - if ("-W" != flag[:2]) and ("-std=gnu99" != flag[:10]) and ("-pedantic" != flag[:9]) and ("-D_XOPEN_SOURCE=600" != flag[:19]) and ("-xc99=all" != flag[:9]): - newCCFLAGS.append(flag); - if ("-D_XOPEN_SOURCE=600" == flag[:19]): - newCCFLAGS.append("-D_XOPEN_SOURCE=500"); - pcc['CCFLAGS'] = newCCFLAGS; - pcc.Program('purinsendcc', ['purinsendcc.cc'] + p.Object(getopt)) - pcc.Program('purinrecvcc', ['purinrecvcc.cc'] + p.Object(getopt)) - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/async.c b/3rdparty/openpgm-svn-r1085/pgm/examples/async.c deleted file mode 100644 index 042bf8e..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/async.c +++ /dev/null @@ -1,441 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Asynchronous queue for receiving packets in a separate managed thread. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#ifndef _WIN32 -# include -# include -# include -#else -# include -#endif -#include - -#include "async.h" - - -/* locals */ - -struct async_event_t { - struct async_event_t *next, *prev; - size_t len; - struct pgm_sockaddr_t addr; -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) - char data[]; -#elif defined(__cplusplus) - char data[1]; -#else - char data[0]; -#endif -}; - - -static void on_data (async_t*const restrict, const void*restrict, const size_t, const struct pgm_sockaddr_t*restrict, const socklen_t); - - -/* queued data is stored as async_event_t objects - */ - -static inline -struct async_event_t* -async_event_alloc ( - size_t len - ) -{ - struct async_event_t* event; - event = (struct async_event_t*)calloc (1, len + sizeof(struct async_event_t)); - event->len = len; - return event; -} - -static inline -void -async_event_unref ( - struct async_event_t* const event - ) -{ - free (event); -} - -/* async_t implements a queue - */ - -static inline -void -async_push_event ( - async_t* restrict async, - struct async_event_t* restrict event - ) -{ - event->next = async->head; - if (async->head) - async->head->prev = event; - else - async->tail = event; - async->head = event; - async->length++; -} - -static inline -struct async_event_t* -async_pop_event ( - async_t* async - ) -{ - if (async->tail) - { - struct async_event_t *event = async->tail; - - async->tail = event->prev; - if (async->tail) - { - async->tail->next = NULL; - event->prev = NULL; - } - else - async->head = NULL; - async->length--; - - return event; - } - - return NULL; -} - -/* asynchronous receiver thread, sits in a loop processing incoming packets - */ - -static -#ifndef _WIN32 -void* -#else -unsigned -__stdcall -#endif -receiver_routine ( - void* arg - ) -{ - assert (NULL != arg); - async_t* async = (async_t*)arg; - assert (NULL != async->sock); -#ifndef _WIN32 - int fds; - fd_set readfds; -#else - int n_handles = 3, recv_sock, pending_sock; - HANDLE waitHandles[ 3 ]; - DWORD dwTimeout, dwEvents; - WSAEVENT recvEvent, pendingEvent; - socklen_t socklen = sizeof(int); - - recvEvent = WSACreateEvent (); - pgm_getsockopt (async->sock, PGM_RECV_SOCK, &recv_sock, &socklen); - WSAEventSelect (recv_sock, recvEvent, FD_READ); - pendingEvent = WSACreateEvent (); - pgm_getsockopt (async->sock, PGM_PENDING_SOCK, &pending_sock, &socklen); - WSAEventSelect (pending_sock, pendingEvent, FD_READ); - - waitHandles[0] = async->destroy_event; - waitHandles[1] = recvEvent; - waitHandles[2] = pendingEvent; -#endif /* !_WIN32 */ - -/* dispatch loop */ - do { - struct timeval tv; - char buffer[4096]; - size_t len; - struct pgm_sockaddr_t from; - socklen_t fromlen = sizeof (from); - const int status = pgm_recvfrom (async->sock, - buffer, - sizeof(buffer), - 0, - &len, - &from, - &fromlen, - NULL); - switch (status) { - case PGM_IO_STATUS_NORMAL: - on_data (async, buffer, len, &from, fromlen); - break; - case PGM_IO_STATUS_TIMER_PENDING: - { - socklen_t optlen = sizeof (tv); - pgm_getsockopt (async->sock, PGM_TIME_REMAIN, &tv, &optlen); - } - goto block; - case PGM_IO_STATUS_RATE_LIMITED: - { - socklen_t optlen = sizeof (tv); - pgm_getsockopt (async->sock, PGM_RATE_REMAIN, &tv, &optlen); - } - case PGM_IO_STATUS_WOULD_BLOCK: -/* select for next event */ -block: -#ifndef _WIN32 - fds = async->destroy_pipe[0] + 1; - FD_ZERO(&readfds); - FD_SET(async->destroy_pipe[0], &readfds); - pgm_select_info (async->sock, &readfds, NULL, &fds); - fds = select (fds, &readfds, NULL, NULL, PGM_IO_STATUS_WOULD_BLOCK == status ? NULL : &tv); -#else - dwTimeout = PGM_IO_STATUS_WOULD_BLOCK == status ? INFINITE : (DWORD)((tv.tv_sec * 1000) + (tv. -tv_usec / 1000)); - dwEvents = WaitForMultipleObjects (n_handles, waitHandles, FALSE, dwTimeout); - switch (dwEvents) { - case WAIT_OBJECT_0+1: WSAResetEvent (recvEvent); break; - case WAIT_OBJECT_0+2: WSAResetEvent (pendingEvent); break; - default: break; - } -#endif /* !_WIN32 */ - break; - - default: - if (PGM_IO_STATUS_ERROR == status) - break; - } - } while (!async->is_destroyed); - -/* cleanup */ -#ifndef _WIN32 - return NULL; -#else - WSACloseEvent (recvEvent); - WSACloseEvent (pendingEvent); - _endthread(); - return 0; -#endif /* !_WIN32 */ -} - -/* enqueue a new data event. - */ - -static -void -on_data ( - async_t*const restrict async, - const void* restrict data, - const size_t len, - const struct pgm_sockaddr_t* restrict from, - const socklen_t fromlen - ) -{ - struct async_event_t* event = async_event_alloc (len); - memcpy (&event->addr, from, fromlen); - memcpy (&event->data, data, len); -#ifndef _WIN32 - pthread_mutex_lock (&async->pthread_mutex); - async_push_event (async, event); - if (1 == async->length) { - const char one = '1'; - const size_t writelen = write (async->notify_pipe[1], &one, sizeof(one)); - assert (sizeof(one) == writelen); - } - pthread_mutex_unlock (&async->pthread_mutex); -#else - WaitForSingleObject (async->win32_mutex, INFINITE); - async_push_event (async, event); - if (1 == async->length) { - SetEvent (async->notify_event); - } - ReleaseMutex (async->win32_mutex); -#endif /* _WIN32 */ -} - -/* create asynchronous thread handler from bound PGM sock. - * - * on success, 0 is returned. on error, -1 is returned, and errno set appropriately. - */ - -int -async_create ( - async_t** restrict async, - pgm_sock_t* const restrict sock - ) -{ - async_t* new_async; - - if (NULL == async || NULL == sock) { - errno = EINVAL; - return -1; - } - - new_async = (async_t*)calloc (1, sizeof(async_t)); - new_async->sock = sock; -#ifndef _WIN32 - int e; - e = pthread_mutex_init (&new_async->pthread_mutex, NULL); - if (0 != e) goto err_destroy; - e = pipe (new_async->notify_pipe); - const int flags = fcntl (new_async->notify_pipe[0], F_GETFL); - fcntl (new_async->notify_pipe[0], F_SETFL, flags | O_NONBLOCK); - if (0 != e) goto err_destroy; - e = pipe (new_async->destroy_pipe); - if (0 != e) goto err_destroy; - const int status = pthread_create (&new_async->thread, NULL, &receiver_routine, new_async); - if (0 != status) goto err_destroy; -#else - new_async->win32_mutex = CreateMutex (NULL, FALSE, NULL); - new_async->notify_event = CreateEvent (NULL, TRUE, FALSE, TEXT("AsyncNotify")); - new_async->destroy_event = CreateEvent (NULL, TRUE, FALSE, TEXT("AsyncDestroy")); - new_async->thread = (HANDLE)_beginthreadex (NULL, 0, &receiver_routine, new_async, 0, NULL); - if (0 == new_async->thread) goto err_destroy; -#endif /* _WIN32 */ - -/* return new object */ - *async = new_async; - return 0; - -err_destroy: -#ifndef _WIN32 - close (new_async->destroy_pipe[0]); - close (new_async->destroy_pipe[1]); - close (new_async->notify_pipe[0]); - close (new_async->notify_pipe[1]); - pthread_mutex_destroy (&new_async->pthread_mutex); -#else - CloseHandle (new_async->destroy_event); - CloseHandle (new_async->notify_event); - CloseHandle (new_async->win32_mutex); -#endif /* _WIN32 */ - if (new_async) - free (new_async); - return -1; -} - -/* Destroy asynchronous receiver, there must be no active queue consumer. - * - * on success, 0 is returned, on error -1 is returned and errno set appropriately. - */ - -int -async_destroy ( - async_t* const async - ) -{ - if (NULL == async || async->is_destroyed) { - errno = EINVAL; - return -1; - } - - async->is_destroyed = TRUE; -#ifndef _WIN32 - const char one = '1'; - const size_t writelen = write (async->destroy_pipe[1], &one, sizeof(one)); - assert (sizeof(one) == writelen); - pthread_join (async->thread, NULL); - close (async->destroy_pipe[0]); - close (async->destroy_pipe[1]); - close (async->notify_pipe[0]); - close (async->notify_pipe[1]); - pthread_mutex_destroy (&async->pthread_mutex); -#else - SetEvent (async->destroy_event); - WaitForSingleObject (async->thread, INFINITE); - CloseHandle (async->thread); - CloseHandle (async->destroy_event); - CloseHandle (async->notify_event); - CloseHandle (async->win32_mutex); -#endif /* !_WIN32 */ - while (async->head) { - struct async_event_t *next = async->head->next; - async_event_unref (async->head); - async->head = next; - async->length--; - } - free (async); - return 0; -} - -/* synchronous reading from the queue. - * - * returns GIOStatus with success, error, again, or eof. - */ - -ssize_t -async_recvfrom ( - async_t* const restrict async, - void* restrict buf, - size_t len, - struct pgm_sockaddr_t* restrict from, - socklen_t* restrict fromlen - ) -{ - struct async_event_t* event; - - if (NULL == async || NULL == buf || async->is_destroyed) { - errno = EINVAL; - return -1; - } - -#ifndef _WIN32 - pthread_mutex_lock (&async->pthread_mutex); - if (0 == async->length) { -/* flush event pipe */ - char tmp; - while (sizeof(tmp) == read (async->notify_pipe[0], &tmp, sizeof(tmp))); - pthread_mutex_unlock (&async->pthread_mutex); - errno = EAGAIN; - return -1; - } - event = async_pop_event (async); - pthread_mutex_unlock (&async->pthread_mutex); -#else - WaitForSingleObject (async->win32_mutex, INFINITE); - if (0 == async->length) { -/* clear event */ - ResetEvent (async->notify_event); - ReleaseMutex (async->win32_mutex); - errno = EAGAIN; - return -1; - } - event = async_pop_event (async); - ReleaseMutex (async->win32_mutex); -#endif /* _WIN32 */ - assert (NULL != event); - -/* pass data back to callee */ - const size_t event_len = MIN(event->len, len); - if (NULL != from && sizeof(struct pgm_sockaddr_t) == *fromlen) { - memcpy (from, &event->addr, *fromlen); - } - memcpy (buf, event->data, event_len); - async_event_unref (event); - return event_len; -} - -ssize_t -async_recv ( - async_t* const restrict async, - void* restrict buf, - size_t len - ) -{ - return async_recvfrom (async, buf, len, NULL, NULL); -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/async.h b/3rdparty/openpgm-svn-r1085/pgm/examples/async.h deleted file mode 100644 index 788a777..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/async.h +++ /dev/null @@ -1,82 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * Asynchronous receive thread helper - * - * Copyright (c) 2006-2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_ASYNC_H__ -#define __PGM_ASYNC_H__ - -struct async_event_t; - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct async_t { - pgm_sock_t* sock; -#ifndef _WIN32 - pthread_t thread; - int notify_pipe[2]; - int destroy_pipe[2]; - pthread_mutex_t pthread_mutex; -#else - HANDLE thread; - HANDLE notify_event; - HANDLE destroy_event; - HANDLE win32_mutex; -#endif - struct async_event_t *head, *tail; - unsigned length; - bool is_destroyed; -}; -typedef struct async_t async_t; - -int async_create (async_t** restrict, pgm_sock_t*const restrict); -int async_destroy (async_t* const); -ssize_t async_recv (async_t*const restrict, void* restrict, size_t); -ssize_t async_recvfrom (async_t*const restrict, void*restrict, size_t, struct pgm_sockaddr_t*restrict, socklen_t*restrict); - -#ifndef _WIN32 -static inline int async_get_fd (async_t* async) -{ - if (NULL == async) { - errno = EINVAL; - return -1; - } - return async->notify_pipe[0]; -} -#else -static inline HANDLE async_get_event (async_t* async) -{ - if (NULL == async) { - errno = EINVAL; - return NULL; - } - return async->notify_event; -} -#endif /* _WIN32 */ - -#ifdef __cplusplus -} -#endif - -#endif /* __PGM_ASYNC_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/blocksyncrecv.c b/3rdparty/openpgm-svn-r1085/pgm/examples/blocksyncrecv.c deleted file mode 100644 index ec43d17..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/blocksyncrecv.c +++ /dev/null @@ -1,350 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Simple PGM receiver: blocking synchronous receiver - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#ifndef G_OS_WIN32 -# include -#else -# include "getopt.h" -#endif -#include - -/* example dependencies */ -#include -#include - - -/* typedefs */ - -/* globals */ - -static int g_port = 0; -static const char* g_network = ""; -static gboolean g_multicast_loop = FALSE; -static int g_udp_encap_port = 0; - -static int g_max_tpdu = 1500; -static int g_sqns = 100; - -static pgm_sock_t* g_sock = NULL; -static gboolean g_quit = FALSE; - -#ifdef G_OS_UNIX -static void on_signal (int); -#else -static BOOL on_console_ctrl (DWORD); -#endif -static gboolean on_startup (void); -static int on_data (gconstpointer, size_t, struct pgm_sockaddr_t*); - - -G_GNUC_NORETURN static -void -usage ( - const char* bin - ) -{ - fprintf (stderr, "Usage: %s [options]\n", bin); - fprintf (stderr, " -n : Multicast group or unicast IP address\n"); - fprintf (stderr, " -s : IP port\n"); - fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); - fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); - exit (1); -} - -int -main ( - int argc, - char* argv[] - ) -{ - pgm_error_t* pgm_err = NULL; - - setlocale (LC_ALL, ""); - - log_init (); - g_message ("blocksyncrecv"); - - if (!pgm_init (&pgm_err)) { - g_error ("Unable to start PGM engine: %s", pgm_err->message); - pgm_error_free (pgm_err); - return EXIT_FAILURE; - } - -/* parse program arguments */ - const char* binary_name = strrchr (argv[0], '/'); - int c; - while ((c = getopt (argc, argv, "s:n:p:lh")) != -1) - { - switch (c) { - case 'n': g_network = optarg; break; - case 's': g_port = atoi (optarg); break; - case 'p': g_udp_encap_port = atoi (optarg); break; - case 'l': g_multicast_loop = TRUE; break; - - case 'h': - case '?': usage (binary_name); - } - } - -/* setup signal handlers */ - signal(SIGSEGV, on_sigsegv); -#ifdef SIGHUP - signal(SIGHUP, SIG_IGN); -#endif -#ifdef G_OS_UNIX - signal(SIGINT, on_signal); - signal(SIGTERM, on_signal); -#else - SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); -#endif - - on_startup(); - -/* dispatch loop */ - g_message ("entering PGM message loop ... "); - do { - char buffer[4096]; - size_t len; - struct pgm_sockaddr_t from; - socklen_t fromlen = sizeof(from); - const int status = pgm_recvfrom (g_sock, - buffer, - sizeof(buffer), - 0, - &len, - &from, - &fromlen, - &pgm_err); - if (PGM_IO_STATUS_NORMAL == status) - on_data (buffer, len, &from); - else { - if (pgm_err) { - g_warning ("%s", pgm_err->message); - pgm_error_free (pgm_err); - pgm_err = NULL; - } - if (PGM_IO_STATUS_ERROR == status) - break; - } - } while (!g_quit); - - g_message ("message loop terminated, cleaning up."); - -/* cleanup */ - if (g_sock) { - g_message ("closing PGM socket."); - pgm_close (g_sock, TRUE); - g_sock = NULL; - } - - g_message ("PGM engine shutdown."); - pgm_shutdown (); - g_message ("finished."); - return EXIT_SUCCESS; -} - -#ifdef G_OS_UNIX -static -void -on_signal ( - int signum - ) -{ - g_message ("on_signal (signum:%d)", signum); - g_quit = TRUE; -} -#else -static -BOOL -on_console_ctrl ( - DWORD dwCtrlType - ) -{ - g_message ("on_console_ctrl (dwCtrlType:%lu)", (unsigned long)dwCtrlType); - g_quit = TRUE; - return TRUE; -} -#endif /* !G_OS_UNIX */ - -static -gboolean -on_startup (void) -{ - struct pgm_addrinfo_t* res = NULL; - pgm_error_t* pgm_err = NULL; - sa_family_t sa_family = AF_UNSPEC; - - g_message ("startup."); - -/* parse network parameter into transport address structure */ - if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { - g_error ("parsing network parameter: %s", pgm_err->message); - goto err_abort; - } - - sa_family = res->ai_send_addrs[0].gsr_group.ss_family; - - if (g_udp_encap_port) { - g_message ("create PGM/UDP socket."); - if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { - g_error ("socket: %s", pgm_err->message); - goto err_abort; - } - pgm_setsockopt (g_sock, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); - pgm_setsockopt (g_sock, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); - } else { - g_message ("create PGM/IP socket."); - if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { - g_error ("socket: %s", pgm_err->message); - goto err_abort; - } - } - -/* Use RFC 2113 tagging for PGM Router Assist */ - const int no_router_assist = 0; - pgm_setsockopt (g_sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); - - pgm_drop_superuser(); - -/* set PGM parameters */ - const int recv_only = 1, - passive = 0, - peer_expiry = pgm_secs (300), - spmr_expiry = pgm_msecs (250), - nak_bo_ivl = pgm_msecs (50), - nak_rpt_ivl = pgm_secs (2), - nak_rdata_ivl = pgm_secs (2), - nak_data_retries = 50, - nak_ncf_retries = 50; - - pgm_setsockopt (g_sock, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); - pgm_setsockopt (g_sock, PGM_PASSIVE, &passive, sizeof(passive)); - pgm_setsockopt (g_sock, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); - pgm_setsockopt (g_sock, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); - pgm_setsockopt (g_sock, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); - pgm_setsockopt (g_sock, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); - pgm_setsockopt (g_sock, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); - pgm_setsockopt (g_sock, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); - -/* create global session identifier */ - struct pgm_sockaddr_t addr; - memset (&addr, 0, sizeof(addr)); - addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; - addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; - if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { - g_error ("creating GSI: %s", pgm_err->message); - goto err_abort; - } - -/* assign socket to specified address */ - struct pgm_interface_req_t if_req; - memset (&if_req, 0, sizeof(if_req)); - if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; - if_req.ir_scope_id = 0; - if (AF_INET6 == sa_family) { - struct sockaddr_in6 sa6; - memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); - if_req.ir_scope_id = sa6.sin6_scope_id; - } - if (!pgm_bind3 (g_sock, - &addr, sizeof(addr), - &if_req, sizeof(if_req), /* tx interface */ - &if_req, sizeof(if_req), /* rx interface */ - &pgm_err)) - { - g_error ("binding PGM socket: %s", pgm_err->message); - goto err_abort; - } - -/* join IP multicast groups */ - for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) - pgm_setsockopt (g_sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); - pgm_setsockopt (g_sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); - pgm_freeaddrinfo (res); - -/* set IP parameters */ - const int blocking = 0, - multicast_loop = g_multicast_loop ? 1 : 0, - multicast_hops = 16, - dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ - - pgm_setsockopt (g_sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); - pgm_setsockopt (g_sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); - if (AF_INET6 != sa_family) - pgm_setsockopt (g_sock, PGM_TOS, &dscp, sizeof(dscp)); - pgm_setsockopt (g_sock, PGM_NOBLOCK, &blocking, sizeof(blocking)); - - if (!pgm_connect (g_sock, &pgm_err)) { - g_error ("connecting PGM socket: %s", pgm_err->message); - goto err_abort; - } - - g_message ("startup complete."); - return TRUE; - -err_abort: - if (NULL != g_sock) { - pgm_close (g_sock, FALSE); - g_sock = NULL; - } - if (NULL != res) { - pgm_freeaddrinfo (res); - res = NULL; - } - if (NULL != pgm_err) { - pgm_error_free (pgm_err); - pgm_err = NULL; - } - return FALSE; -} - -static -int -on_data ( - gconstpointer data, - size_t len, - struct pgm_sockaddr_t* from - ) -{ -/* protect against non-null terminated strings */ - char buf[1024], tsi[PGM_TSISTRLEN]; - const size_t buflen = MIN( sizeof(buf) - 1, len ); - strncpy (buf, data, buflen); - buf[buflen] = '\0'; - pgm_tsi_print_r (&from->sa_addr, tsi, sizeof(tsi)); - - g_message ("\"%s\" (%u bytes from %s)", - buf, - (unsigned)len, - tsi); - - return 0; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/daytime.c b/3rdparty/openpgm-svn-r1085/pgm/examples/daytime.c deleted file mode 100644 index dda619b..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/daytime.c +++ /dev/null @@ -1,546 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Daytime broadcast service. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#ifndef _WIN32 -# include -# include -# include -#else -# include -# include -# include "getopt.h" -# define snprintf _snprintf -#endif -#include - - -/* globals */ -#define TIME_FORMAT "%a, %d %b %Y %H:%M:%S %z" - -static int port = 0; -static const char* network = ""; -static bool use_multicast_loop = FALSE; -static int udp_encap_port = 0; - -static int max_tpdu = 1500; -static int max_rte = 400*1000; /* very conservative rate, 2.5mb/s */ -static int sqns = 100; - -static bool use_pgmcc = FALSE; -static bool use_fec = FALSE; -static bool use_ondemand_parity = FALSE; -static int proactive_packets = 0; -static int rs_k = 8; -static int rs_n = 255; - -static pgm_sock_t* sock = NULL; -static bool is_terminated = FALSE; - -#ifndef _WIN32 -static pthread_t nak_thread; -static int terminate_pipe[2]; -static void on_signal (int); -static void* nak_routine (void*); -#else -static HANDLE nak_thread; -static HANDLE terminate_event; -static BOOL on_console_ctrl (DWORD); -static unsigned __stdcall nak_routine (void*); -#endif -#ifndef _MSC_VER -static void usage (const char*) __attribute__((__noreturn__)); -#else -static void usage (const char*); -#endif - -static bool on_startup (void); -static bool create_sock (void); -static bool create_nak_thread (void); - - -static void -usage ( - const char* bin - ) -{ - fprintf (stderr, "Usage: %s [options]\n", bin); - fprintf (stderr, " -n : Multicast group or unicast IP address\n"); - fprintf (stderr, " -s : IP port\n"); - fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); - fprintf (stderr, " -r : Regulate to rate bytes per second\n"); - fprintf (stderr, " -c : Enable PGMCC\n"); - fprintf (stderr, " -f : Enable FEC: proactive, ondemand, or both\n"); - fprintf (stderr, " -N : Reed-Solomon block size (255)\n"); - fprintf (stderr, " -K : Reed-Solomon group size (8)\n"); - fprintf (stderr, " -P : Number of pro-active parity packets (h)\n"); - fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); - fprintf (stderr, " -i : List available interfaces\n"); - exit (EXIT_SUCCESS); -} - -int -main ( - int argc, - char *argv[] - ) -{ - pgm_error_t* pgm_err = NULL; - - setlocale (LC_ALL, ""); - - puts ("PGM daytime service"); - - if (!pgm_init (&pgm_err)) { - fprintf (stderr, "Unable to start PGM engine: %s\n", pgm_err->message); - pgm_error_free (pgm_err); - return EXIT_FAILURE; - } - -/* parse program arguments */ - const char* binary_name = strrchr (argv[0], '/'); - int c; - while ((c = getopt (argc, argv, "s:n:p:r:cf:N:K:P:lih")) != -1) - { - switch (c) { - case 'n': network = optarg; break; - case 's': port = atoi (optarg); break; - case 'p': udp_encap_port = atoi (optarg); break; - case 'r': max_rte = atoi (optarg); break; - case 'c': use_pgmcc = TRUE; break; - case 'f': - use_fec = TRUE; - switch (optarg[0]) { - case 'p': - case 'P': - proactive_packets = 1; - break; - case 'b': - case 'B': - proactive_packets = 1; - case 'o': - case 'O': - use_ondemand_parity = TRUE; - break; - } - break; - case 'N': rs_n = atoi (optarg); break; - case 'K': rs_k = atoi (optarg); break; - case 'P': proactive_packets = atoi (optarg); break; - - case 'l': use_multicast_loop = TRUE; break; - - case 'i': - pgm_if_print_all(); - return EXIT_SUCCESS; - - case 'h': - case '?': - usage (binary_name); - } - } - - if (use_fec && ( !rs_n || !rs_k )) { - fprintf (stderr, "Invalid Reed-Solomon parameters RS(%d,%d).\n", rs_n, rs_k); - usage (binary_name); - } - -/* setup signal handlers */ -#ifdef SIGHUP - signal (SIGHUP, SIG_IGN); -#endif -#ifndef _WIN32 - int e = pipe (terminate_pipe); - assert (0 == e); - const int flags = fcntl (terminate_pipe[0], F_GETFL); - fcntl (terminate_pipe[0], F_SETFL, flags | O_NONBLOCK); - signal (SIGINT, on_signal); - signal (SIGTERM, on_signal); -#else - terminate_event = CreateEvent (NULL, TRUE, FALSE, TEXT("TerminateEvent")); - SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); -#endif /* !_WIN32 */ - - if (!on_startup()) { - fprintf (stderr, "Startup failed\n"); - return EXIT_FAILURE; - } - -/* service loop */ - do { - time_t now; - time (&now); - const struct tm* time_ptr = localtime(&now); -#ifndef _WIN32 - char s[1024]; - const size_t slen = strftime (s, sizeof(s), TIME_FORMAT, time_ptr); - const int status = pgm_send (sock, s, slen + 1, NULL); -#else - char s[1024]; - const size_t slen = strftime (s, sizeof(s), TIME_FORMAT, time_ptr); - wchar_t ws[1024]; - size_t wslen = MultiByteToWideChar (CP_ACP, 0, s, slen, ws, 1024); - char us[1024]; - size_t uslen = WideCharToMultiByte (CP_UTF8, 0, ws, wslen + 1, us, sizeof(us), NULL, NULL); - const int status = pgm_send (sock, us, uslen + 1, NULL); -#endif - if (PGM_IO_STATUS_NORMAL != status) { - fprintf (stderr, "pgm_send() failed.\n"); - } -#ifndef _WIN32 - sleep (1); -#else - Sleep (1 * 1000); -#endif - } while (!is_terminated); - -/* cleanup */ - puts ("Waiting for NAK thread."); -#ifndef _WIN32 - pthread_join (nak_thread, NULL); - close (terminate_pipe[0]); - close (terminate_pipe[1]); -#else - WaitForSingleObject (nak_thread, INFINITE); - CloseHandle (nak_thread); - CloseHandle (terminate_event); -#endif /* !_WIN32 */ - - if (sock) { - puts ("Closing PGM sock."); - pgm_close (sock, TRUE); - sock = NULL; - } - - puts ("PGM engine shutdown."); - pgm_shutdown(); - puts ("finished."); - return EXIT_SUCCESS; -} - -#ifndef _WIN32 -static -void -on_signal ( - int signum - ) -{ - printf ("on_signal (signum:%d)\n", signum); - is_terminated = TRUE; - const char one = '1'; - const size_t writelen = write (terminate_pipe[1], &one, sizeof(one)); - assert (sizeof(one) == writelen); -} -#else -static -BOOL -on_console_ctrl ( - DWORD dwCtrlType - ) -{ - printf ("on_console_ctrl (dwCtrlType:%lu)\n", (unsigned long)dwCtrlType); - is_terminated = TRUE; - SetEvent (terminate_event); - return TRUE; -} -#endif /* !_WIN32 */ - -static -bool -on_startup (void) -{ - bool status = (create_sock() && create_nak_thread()); - if (status) - puts ("Startup complete."); - return status; -} - -static -bool -create_sock (void) -{ - struct pgm_addrinfo_t* res = NULL; - pgm_error_t* pgm_err = NULL; - sa_family_t sa_family = AF_UNSPEC; - -/* parse network parameter into sock address structure */ - if (!pgm_getaddrinfo (network, NULL, &res, &pgm_err)) { - fprintf (stderr, "Parsing network parameter: %s\n", pgm_err->message); - goto err_abort; - } - - sa_family = res->ai_send_addrs[0].gsr_group.ss_family; - - puts ("Create PGM socket."); - if (udp_encap_port) { - if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { - fprintf (stderr, "Creating PGM/UDP socket: %s\n", pgm_err->message); - goto err_abort; - } - pgm_setsockopt (sock, PGM_UDP_ENCAP_UCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); - pgm_setsockopt (sock, PGM_UDP_ENCAP_MCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); - } else { - if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { - fprintf (stderr, "Creating PGM/IP socket: %s\n", pgm_err->message); - goto err_abort; - } - } - -/* Use RFC 2113 tagging for PGM Router Assist */ - const int no_router_assist = 0; - pgm_setsockopt (sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); - - pgm_drop_superuser(); - -/* set PGM parameters */ - const int send_only = 1, - ambient_spm = pgm_secs (30), - heartbeat_spm[] = { pgm_msecs (100), - pgm_msecs (100), - pgm_msecs (100), - pgm_msecs (100), - pgm_msecs (1300), - pgm_secs (7), - pgm_secs (16), - pgm_secs (25), - pgm_secs (30) }; - - pgm_setsockopt (sock, PGM_SEND_ONLY, &send_only, sizeof(send_only)); - pgm_setsockopt (sock, PGM_MTU, &max_tpdu, sizeof(max_tpdu)); - pgm_setsockopt (sock, PGM_TXW_SQNS, &sqns, sizeof(sqns)); - pgm_setsockopt (sock, PGM_TXW_MAX_RTE, &max_rte, sizeof(max_rte)); - pgm_setsockopt (sock, PGM_AMBIENT_SPM, &ambient_spm, sizeof(ambient_spm)); - pgm_setsockopt (sock, PGM_HEARTBEAT_SPM, &heartbeat_spm, sizeof(heartbeat_spm)); - -#ifdef I_UNDERSTAND_PGMCC_AND_FEC_ARE_NOT_SUPPORTED - if (use_pgmcc) { - struct pgm_pgmccinfo_t pgmccinfo; - pgmccinfo.ack_bo_ivl = pgm_msecs (50); - pgmccinfo.ack_c = 75; - pgmccinfo.ack_c_p = 500; - pgm_setsockopt (sock, PGM_USE_PGMCC, &pgmccinfo, sizeof(pgmccinfo)); - } - if (use_fec) { - struct pgm_fecinfo_t fecinfo; - fecinfo.block_size = rs_n; - fecinfo.proactive_packets = proactive_packets; - fecinfo.group_size = rs_k; - fecinfo.ondemand_parity_enabled = use_ondemand_parity; - fecinfo.var_pktlen_enabled = TRUE; - pgm_setsockopt (sock, PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); - } -#endif - -/* create global session identifier */ - struct pgm_sockaddr_t addr; - memset (&addr, 0, sizeof(addr)); - addr.sa_port = port ? port : DEFAULT_DATA_DESTINATION_PORT; - addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; - if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { - fprintf (stderr, "Creating GSI: %s\n", pgm_err->message); - goto err_abort; - } - -/* assign socket to specified address */ - struct pgm_interface_req_t if_req; - memset (&if_req, 0, sizeof(if_req)); - if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; - if_req.ir_scope_id = 0; - if (AF_INET6 == sa_family) { - struct sockaddr_in6 sa6; - memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); - if_req.ir_scope_id = sa6.sin6_scope_id; - } - if (!pgm_bind3 (sock, - &addr, sizeof(addr), - &if_req, sizeof(if_req), /* tx interface */ - &if_req, sizeof(if_req), /* rx interface */ - &pgm_err)) - { - fprintf (stderr, "Binding PGM socket: %s\n", pgm_err->message); - goto err_abort; - } - -/* join IP multicast groups */ - for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) - pgm_setsockopt (sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); - pgm_setsockopt (sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); - pgm_freeaddrinfo (res); - -/* set IP parameters */ - const int nonblocking = 1, - multicast_loop = use_multicast_loop ? 1 : 0, - multicast_hops = 16, - dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ - - pgm_setsockopt (sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); - pgm_setsockopt (sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); - if (AF_INET6 != sa_family) - pgm_setsockopt (sock, PGM_TOS, &dscp, sizeof(dscp)); - pgm_setsockopt (sock, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); - - if (!pgm_connect (sock, &pgm_err)) { - fprintf (stderr, "Connecting PGM socket: %s\n", pgm_err->message); - goto err_abort; - } - - return TRUE; - -err_abort: - if (NULL != sock) { - pgm_close (sock, FALSE); - sock = NULL; - } - if (NULL != res) { - pgm_freeaddrinfo (res); - res = NULL; - } - if (NULL != pgm_err) { - pgm_error_free (pgm_err); - pgm_err = NULL; - } - return FALSE; -} - -static -bool -create_nak_thread (void) -{ -#ifndef _WIN32 - const int status = pthread_create (&nak_thread, NULL, &nak_routine, sock); - if (0 != status) { - fprintf (stderr, "Creating new thread: %s\n", strerror (status)); - return FALSE; - } -#else - nak_thread = (HANDLE)_beginthreadex (NULL, 0, &nak_routine, sock, 0, NULL); - const int save_errno = errno; - if (0 == nak_thread) { - fprintf (stderr, "Creating new thread: %s\n", strerror (save_errno)); - return FALSE; - } -#endif /* _WIN32 */ - return TRUE; -} - -static -#ifndef _WIN32 -void* -#else -unsigned -__stdcall -#endif -nak_routine ( - void* arg - ) -{ -/* dispatch loop */ - pgm_sock_t* nak_sock = (pgm_sock_t*)arg; -#ifndef _WIN32 - int fds; - fd_set readfds; -#else - int n_handles = 4, recv_sock, repair_sock, pending_sock; - HANDLE waitHandles[ 4 ]; - DWORD dwTimeout, dwEvents; - WSAEVENT recvEvent, repairEvent, pendingEvent; - socklen_t socklen = sizeof(int); - - recvEvent = WSACreateEvent (); - pgm_getsockopt (nak_sock, PGM_RECV_SOCK, &recv_sock, &socklen); - WSAEventSelect (recv_sock, recvEvent, FD_READ); - repairEvent = WSACreateEvent (); - pgm_getsockopt (nak_sock, PGM_REPAIR_SOCK, &repair_sock, &socklen); - WSAEventSelect (repair_sock, repairEvent, FD_READ); - pendingEvent = WSACreateEvent (); - pgm_getsockopt (nak_sock, PGM_PENDING_SOCK, &pending_sock, &socklen); - WSAEventSelect (pending_sock, pendingEvent, FD_READ); - - waitHandles[0] = terminate_event; - waitHandles[1] = recvEvent; - waitHandles[2] = repairEvent; - waitHandles[3] = pendingEvent; -#endif /* !_WIN32 */ - do { - struct timeval tv; - char buf[4064]; - pgm_error_t* pgm_err = NULL; - const int status = pgm_recv (nak_sock, buf, sizeof(buf), 0, NULL, &pgm_err); - switch (status) { - case PGM_IO_STATUS_TIMER_PENDING: - { - socklen_t optlen = sizeof (tv); - pgm_getsockopt (sock, PGM_TIME_REMAIN, &tv, &optlen); - } - goto block; - case PGM_IO_STATUS_RATE_LIMITED: - { - socklen_t optlen = sizeof (tv); - pgm_getsockopt (sock, PGM_RATE_REMAIN, &tv, &optlen); - } - case PGM_IO_STATUS_WOULD_BLOCK: -block: -#ifndef _WIN32 - fds = terminate_pipe[0] + 1; - FD_ZERO(&readfds); - FD_SET(terminate_pipe[0], &readfds); - pgm_select_info (nak_sock, &readfds, NULL, &fds); - fds = select (fds, &readfds, NULL, NULL, PGM_IO_STATUS_WOULD_BLOCK == status ? NULL : &tv); -#else - dwTimeout = PGM_IO_STATUS_WOULD_BLOCK == status ? INFINITE : (DWORD)((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); - dwEvents = WaitForMultipleObjects (n_handles, waitHandles, FALSE, dwTimeout); - switch (dwEvents) { - case WAIT_OBJECT_0+1: WSAResetEvent (recvEvent); break; - case WAIT_OBJECT_0+2: WSAResetEvent (repairEvent); break; - case WAIT_OBJECT_0+3: WSAResetEvent (pendingEvent); break; - default: break; - } -#endif /* !_WIN32 */ - break; - - default: - if (pgm_err) { - fprintf (stderr, "%s\n", pgm_err->message ? pgm_err->message : "(null)"); - pgm_error_free (pgm_err); - pgm_err = NULL; - } - if (PGM_IO_STATUS_ERROR == status) - break; - } - } while (!is_terminated); -#ifndef _WIN32 - return NULL; -#else - WSACloseEvent (recvEvent); - WSACloseEvent (repairEvent); - WSACloseEvent (pendingEvent); - _endthread(); - return 0; -#endif -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecv.c b/3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecv.c deleted file mode 100644 index 27625ce..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecv.c +++ /dev/null @@ -1,382 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Simple PGM receiver: epoll based non-blocking synchronous receiver. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef G_OS_UNIX -# include -# include -# include -# include -#endif -#include - -/* example dependencies */ -#include -#include - - -/* typedefs */ - -/* globals */ - -static int g_port = 0; -static const char* g_network = ""; -static gboolean g_multicast_loop = FALSE; -static int g_udp_encap_port = 0; - -static int g_max_tpdu = 1500; -static int g_sqns = 100; - -static pgm_sock_t* g_sock = NULL; -static gboolean g_quit = FALSE; - -static void on_signal (int); -static gboolean on_startup (void); - -static int on_data (gconstpointer, size_t, struct pgm_sockaddr_t*); - - -G_GNUC_NORETURN static -void -usage ( - const char* bin - ) -{ - fprintf (stderr, "Usage: %s [options]\n", bin); - fprintf (stderr, " -n : Multicast group or unicast IP address\n"); - fprintf (stderr, " -s : IP port\n"); - fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); - fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); - exit (1); -} - -int -main ( - int argc, - char* argv[] - ) -{ - pgm_error_t* pgm_err = NULL; - - setlocale (LC_ALL, ""); - - log_init (); - g_message ("enonblocksyncrecv"); - - if (!pgm_init (&pgm_err)) { - g_error ("Unable to start PGM engine: %s", pgm_err->message); - pgm_error_free (pgm_err); - return EXIT_FAILURE; - } - -/* parse program arguments */ - const char* binary_name = strrchr (argv[0], '/'); - int c; - while ((c = getopt (argc, argv, "s:n:p:lh")) != -1) - { - switch (c) { - case 'n': g_network = optarg; break; - case 's': g_port = atoi (optarg); break; - case 'p': g_udp_encap_port = atoi (optarg); break; - case 'l': g_multicast_loop = TRUE; break; - - case 'h': - case '?': usage (binary_name); - } - } - - -/* setup signal handlers */ - signal (SIGSEGV, on_sigsegv); - signal (SIGINT, on_signal); - signal (SIGTERM, on_signal); -#ifdef SIGHUP - signal (SIGHUP, SIG_IGN); -#endif - - if (!on_startup ()) { - g_error ("startup failed"); - return EXIT_FAILURE; - } - -/* epoll file descriptor */ - int efd = epoll_create (IP_MAX_MEMBERSHIPS); - if (efd < 0) { - g_error ("epoll_create failed errno %i: \"%s\"", errno, strerror(errno)); - return EXIT_FAILURE; - } - - int retval = pgm_epoll_ctl (g_sock, efd, EPOLL_CTL_ADD, EPOLLIN); - if (retval < 0) { - g_error ("pgm_epoll_ctl failed."); - return EXIT_FAILURE; - } - - struct epoll_event events[1]; /* wait for maximum 1 event */ - -/* dispatch loop */ - g_message ("entering PGM message loop ... "); - do { - struct timeval tv; - int timeout; - char buffer[4096]; - size_t len; - struct pgm_sockaddr_t from; - socklen_t fromlen = sizeof(from); - const int status = pgm_recvfrom (g_sock, - buffer, - sizeof(buffer), - 0, - &len, - &from, - &fromlen, - &pgm_err); - switch (status) { - case PGM_IO_STATUS_NORMAL: - on_data (buffer, len, &from); - break; - - case PGM_IO_STATUS_TIMER_PENDING: - { - socklen_t optlen = sizeof (tv); - pgm_getsockopt (g_sock, PGM_TIME_REMAIN, &tv, &optlen); - } - goto block; - case PGM_IO_STATUS_RATE_LIMITED: - { - socklen_t optlen = sizeof (tv); - pgm_getsockopt (g_sock, PGM_RATE_REMAIN, &tv, &optlen); - } -/* fall through */ - case PGM_IO_STATUS_WOULD_BLOCK: -/* poll for next event */ -block: - timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); - epoll_wait (efd, events, G_N_ELEMENTS(events), timeout /* ms */); - break; - - default: - if (pgm_err) { - g_warning ("%s", pgm_err->message); - pgm_error_free (pgm_err); - pgm_err = NULL; - } - if (PGM_IO_STATUS_ERROR == status) - break; - } - } while (!g_quit); - - g_message ("message loop terminated, cleaning up."); - -/* cleanup */ - close (efd); - if (g_sock) { - g_message ("closing PGM socket."); - pgm_close (g_sock, TRUE); - g_sock = NULL; - } - - g_message ("PGM engine shutdown."); - pgm_shutdown (); - g_message ("finished."); - return EXIT_SUCCESS; -} - -static -void -on_signal ( - int signum - ) -{ - g_message ("on_signal (signum:%d)", signum); - g_quit = TRUE; -} - -static -gboolean -on_startup (void) -{ - struct pgm_addrinfo_t* res = NULL; - pgm_error_t* pgm_err = NULL; - sa_family_t sa_family = AF_UNSPEC; - - g_message ("startup."); - -/* parse network parameter into transport address structure */ - if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { - g_error ("parsing network parameter: %s", pgm_err->message); - goto err_abort; - } - - sa_family = res->ai_send_addrs[0].gsr_group.ss_family; - - if (g_udp_encap_port) { - g_message ("create PGM/UDP socket."); - if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { - g_error ("socket: %s", pgm_err->message); - goto err_abort; - } - pgm_setsockopt (g_sock, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); - pgm_setsockopt (g_sock, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); - } else { - g_message ("create PGM/IP socket."); - if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { - g_error ("socket: %s", pgm_err->message); - goto err_abort; - } - } - -/* Use RFC 2113 tagging for PGM Router Assist */ - const int no_router_assist = 0; - pgm_setsockopt (g_sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); - - pgm_drop_superuser(); - -/* set PGM parameters */ - const int recv_only = 1, - passive = 0, - peer_expiry = pgm_secs (300), - spmr_expiry = pgm_msecs (250), - nak_bo_ivl = pgm_msecs (50), - nak_rpt_ivl = pgm_secs (2), - nak_rdata_ivl = pgm_secs (2), - nak_data_retries = 50, - nak_ncf_retries = 50; - - pgm_setsockopt (g_sock, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); - pgm_setsockopt (g_sock, PGM_PASSIVE, &passive, sizeof(passive)); - pgm_setsockopt (g_sock, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); - pgm_setsockopt (g_sock, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); - pgm_setsockopt (g_sock, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); - pgm_setsockopt (g_sock, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); - pgm_setsockopt (g_sock, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); - pgm_setsockopt (g_sock, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); - -/* create global session identifier */ - struct pgm_sockaddr_t addr; - memset (&addr, 0, sizeof(addr)); - addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; - addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; - if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { - g_error ("creating GSI: %s", pgm_err->message); - goto err_abort; - } - -/* assign socket to specified address */ - struct pgm_interface_req_t if_req; - memset (&if_req, 0, sizeof(if_req)); - if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; - if_req.ir_scope_id = 0; - if (AF_INET6 == sa_family) { - struct sockaddr_in6 sa6; - memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); - if_req.ir_scope_id = sa6.sin6_scope_id; - } - if (!pgm_bind3 (g_sock, - &addr, sizeof(addr), - &if_req, sizeof(if_req), /* tx interface */ - &if_req, sizeof(if_req), /* rx interface */ - &pgm_err)) - { - g_error ("binding PGM socket: %s", pgm_err->message); - goto err_abort; - } - -/* join IP multicast groups */ - for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) - pgm_setsockopt (g_sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); - pgm_setsockopt (g_sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); - pgm_freeaddrinfo (res); - -/* set IP parameters */ - const int nonblocking = 1, - multicast_loop = g_multicast_loop ? 1 : 0, - multicast_hops = 16, - dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ - - pgm_setsockopt (g_sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); - pgm_setsockopt (g_sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); - if (AF_INET6 != sa_family) - pgm_setsockopt (g_sock, PGM_TOS, &dscp, sizeof(dscp)); - pgm_setsockopt (g_sock, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); - - if (!pgm_connect (g_sock, &pgm_err)) { - g_error ("connecting PGM socket: %s", pgm_err->message); - goto err_abort; - } - - g_message ("startup complete."); - return TRUE; - -err_abort: - if (NULL != g_sock) { - pgm_close (g_sock, FALSE); - g_sock = NULL; - } - if (NULL != res) { - pgm_freeaddrinfo (res); - res = NULL; - } - if (NULL != pgm_err) { - pgm_error_free (pgm_err); - pgm_err = NULL; - } - return FALSE; -} - -static -int -on_data ( - gconstpointer data, - size_t len, - struct pgm_sockaddr_t* from - ) -{ -/* protect against non-null terminated strings */ - char buf[1024], tsi[PGM_TSISTRLEN]; - const size_t buflen = MIN(sizeof(buf) - 1, len); - strncpy (buf, (const char*)data, buflen); - buf[buflen] = '\0'; - pgm_tsi_print_r (&from->sa_addr, tsi, sizeof(tsi)); - - g_message ("\"%s\" (%u bytes from %s)", - buf, - (unsigned)len, - tsi); - - return 0; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecvmsg.c b/3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecvmsg.c deleted file mode 100644 index 9a0e9c6..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecvmsg.c +++ /dev/null @@ -1,382 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Simple PGM receiver: blocking synchronous receiver with scatter/gather io - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef G_OS_UNIX -# include -# include -# include -# include -#endif -#include - -/* example dependencies */ -#include -#include - - -/* typedefs */ - -/* globals */ - -static int g_port = 0; -static const char* g_network = ""; -static gboolean g_multicast_loop = FALSE; -static int g_udp_encap_port = 0; - -static int g_max_tpdu = 1500; -static int g_sqns = 100; - -static pgm_sock_t* g_sock = NULL; -static gboolean g_quit = FALSE; - -static void on_signal (int); -static gboolean on_startup (void); - -static int on_datav (struct pgm_msgv_t*, size_t); - - -G_GNUC_NORETURN static -void -usage ( - const char* bin - ) -{ - fprintf (stderr, "Usage: %s [options]\n", bin); - fprintf (stderr, " -n : Multicast group or unicast IP address\n"); - fprintf (stderr, " -s : IP port\n"); - fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); - fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); - exit (1); -} - -int -main ( - int argc, - char* argv[] - ) -{ - pgm_error_t* pgm_err = NULL; - - setlocale (LC_ALL, ""); - - log_init (); - g_message ("enonblocksyncrecvmsg"); - - if (!pgm_init (&pgm_err)) { - g_error ("Unable to start PGM engine: %s", pgm_err->message); - pgm_error_free (pgm_err); - return EXIT_FAILURE; - } - -/* parse program arguments */ - const char* binary_name = strrchr (argv[0], '/'); - int c; - while ((c = getopt (argc, argv, "s:n:p:lh")) != -1) - { - switch (c) { - case 'n': g_network = optarg; break; - case 's': g_port = atoi (optarg); break; - case 'p': g_udp_encap_port = atoi (optarg); break; - case 'l': g_multicast_loop = TRUE; break; - - case 'h': - case '?': usage (binary_name); - } - } - -/* setup signal handlers */ - signal (SIGSEGV, on_sigsegv); - signal (SIGINT, on_signal); - signal (SIGTERM, on_signal); -#ifdef SIGHUP - signal (SIGHUP, SIG_IGN); -#endif - - if (!on_startup ()) { - g_error ("startup failed"); - return EXIT_FAILURE; - } - -/* epoll file descriptor */ - int efd = epoll_create (IP_MAX_MEMBERSHIPS); - if (efd < 0) { - g_error ("epoll_create failed errno %i: \"%s\"", errno, strerror(errno)); - return EXIT_FAILURE; - } - - int retval = pgm_epoll_ctl (g_sock, efd, EPOLL_CTL_ADD, EPOLLIN); - if (retval < 0) { - g_error ("pgm_epoll_ctl failed."); - return EXIT_FAILURE; - } - -/* incoming message buffer */ - struct pgm_msgv_t msgv; - struct epoll_event events[1]; /* wait for maximum 1 event */ - -/* dispatch loop */ - g_message ("entering PGM message loop ... "); - do { - struct timeval tv; - int timeout; - size_t len; - const int status = pgm_recvmsg (g_sock, - &msgv, - 0, - &len, - &pgm_err); - switch (status) { - case PGM_IO_STATUS_NORMAL: - on_datav (&msgv, len); - break; - - case PGM_IO_STATUS_TIMER_PENDING: - { - socklen_t optlen = sizeof (tv); - pgm_getsockopt (g_sock, PGM_TIME_REMAIN, &tv, &optlen); - } - goto block; - case PGM_IO_STATUS_RATE_LIMITED: - { - socklen_t optlen = sizeof (tv); - pgm_getsockopt (g_sock, PGM_RATE_REMAIN, &tv, &optlen); - } -/* fall through */ - case PGM_IO_STATUS_WOULD_BLOCK: -/* poll for next event */ -block: - timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); - epoll_wait (efd, events, G_N_ELEMENTS(events), timeout /* ms */); - break; - - default: - if (pgm_err) { - g_warning ("%s", pgm_err->message); - pgm_error_free (pgm_err); - pgm_err = NULL; - } - if (PGM_IO_STATUS_ERROR == status) - break; - } - } while (!g_quit); - - g_message ("message loop terminated, cleaning up."); - -/* cleanup */ - close (efd); - if (g_sock) { - g_message ("closing PGM socket."); - pgm_close (g_sock, TRUE); - g_sock = NULL; - } - - g_message ("PGM engine shutdown."); - pgm_shutdown (); - g_message ("finished."); - return EXIT_SUCCESS; -} - -static -void -on_signal ( - int signum - ) -{ - g_message ("on_signal (signum:%d)", signum); - g_quit = TRUE; -} - -static -gboolean -on_startup (void) -{ - struct pgm_addrinfo_t* res = NULL; - pgm_error_t* pgm_err = NULL; - sa_family_t sa_family = AF_UNSPEC; - - g_message ("startup."); - -/* parse network parameter into transport address structure */ - if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { - g_error ("parsing network parameter: %s", pgm_err->message); - goto err_abort; - } - - sa_family = res->ai_send_addrs[0].gsr_group.ss_family; - - if (g_udp_encap_port) { - g_message ("create PGM/UDP socket."); - if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { - g_error ("socket: %s", pgm_err->message); - goto err_abort; - } - pgm_setsockopt (g_sock, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); - pgm_setsockopt (g_sock, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); - } else { - g_message ("create PGM/IP socket."); - if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { - g_error ("socket: %s", pgm_err->message); - goto err_abort; - } - } - -/* Use RFC 2113 tagging for PGM Router Assist */ - const int no_router_assist = 0; - pgm_setsockopt (g_sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); - - pgm_drop_superuser(); - -/* set PGM parameters */ - const int recv_only = 1, - passive = 0, - peer_expiry = pgm_secs (300), - spmr_expiry = pgm_msecs (250), - nak_bo_ivl = pgm_msecs (50), - nak_rpt_ivl = pgm_secs (2), - nak_rdata_ivl = pgm_secs (2), - nak_data_retries = 50, - nak_ncf_retries = 50; - - pgm_setsockopt (g_sock, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); - pgm_setsockopt (g_sock, PGM_PASSIVE, &passive, sizeof(passive)); - pgm_setsockopt (g_sock, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); - pgm_setsockopt (g_sock, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); - pgm_setsockopt (g_sock, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); - pgm_setsockopt (g_sock, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); - pgm_setsockopt (g_sock, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); - pgm_setsockopt (g_sock, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); - -/* create global session identifier */ - struct pgm_sockaddr_t addr; - memset (&addr, 0, sizeof(addr)); - addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; - addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; - if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { - g_error ("creating GSI: %s", pgm_err->message); - goto err_abort; - } - -/* assign socket to specified address */ - struct pgm_interface_req_t if_req; - memset (&if_req, 0, sizeof(if_req)); - if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; - if_req.ir_scope_id = 0; - if (AF_INET6 == sa_family) { - struct sockaddr_in6 sa6; - memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); - if_req.ir_scope_id = sa6.sin6_scope_id; - } - if (!pgm_bind3 (g_sock, - &addr, sizeof(addr), - &if_req, sizeof(if_req), /* tx interface */ - &if_req, sizeof(if_req), /* rx interface */ - &pgm_err)) - { - g_error ("binding PGM socket: %s", pgm_err->message); - goto err_abort; - } - -/* join IP multicast groups */ - for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) - pgm_setsockopt (g_sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); - pgm_setsockopt (g_sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); - pgm_freeaddrinfo (res); - -/* set IP parameters */ - const int nonblocking = 1, - multicast_loop = g_multicast_loop ? 1 : 0, - multicast_hops = 16, - dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ - - pgm_setsockopt (g_sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); - pgm_setsockopt (g_sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); - if (AF_INET6 != sa_family) - pgm_setsockopt (g_sock, PGM_TOS, &dscp, sizeof(dscp)); - pgm_setsockopt (g_sock, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); - - if (!pgm_connect (g_sock, &pgm_err)) { - g_error ("connecting PGM socket: %s", pgm_err->message); - goto err_abort; - } - - g_message ("startup complete."); - return TRUE; - -err_abort: - if (NULL != g_sock) { - pgm_close (g_sock, FALSE); - g_sock = NULL; - } - if (NULL != res) { - pgm_freeaddrinfo (res); - res = NULL; - } - if (NULL != pgm_err) { - pgm_error_free (pgm_err); - pgm_err = NULL; - } - return FALSE; -} - -static -int -on_datav ( - struct pgm_msgv_t* datav, /* one msgv object */ - size_t len - ) -{ - char tsi[PGM_TSISTRLEN]; - pgm_tsi_print_r (&datav->msgv_skb[0]->tsi, tsi, sizeof(tsi)); - g_message ("(%u bytes from %s)", (unsigned)len, tsi); - -/* protect against non-null terminated strings */ - const struct pgm_sk_buff_t* skb = datav->msgv_skb[0]; - int i = 0; - while (len) - { - char buf[1024]; - const size_t buflen = MIN( sizeof(buf) - 1, skb->len ); - strncpy (buf, (const char*)skb->data, buflen); - buf[buflen] = '\0'; - g_message ("\t%i: %s (%" G_GUINT16_FORMAT " bytes)", ++i, buf, skb->len); - len -= skb->len; - skb++; - } - - return 0; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecvmsgv.c b/3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecvmsgv.c deleted file mode 100644 index 0a04056..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecvmsgv.c +++ /dev/null @@ -1,397 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Simple PGM receiver: epoll based non-blocking synchronous receiver with scatter/gather io - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef G_OS_UNIX -# include -# include -# include -# include -# include -#endif -#include - -/* example dependencies */ -#include -#include - - -/* typedefs */ - -/* globals */ - -static int g_port = 0; -static const char* g_network = ""; -static gboolean g_multicast_loop = FALSE; -static int g_udp_encap_port = 0; - -static int g_max_tpdu = 1500; -static int g_sqns = 100; - -static pgm_sock_t* g_sock = NULL; -static gboolean g_quit = FALSE; - -static void on_signal (int); -static gboolean on_startup (void); - -static int on_msgv (struct pgm_msgv_t*, size_t); - - -G_GNUC_NORETURN static -void -usage ( - const char* bin - ) -{ - fprintf (stderr, "Usage: %s [options]\n", bin); - fprintf (stderr, " -n : Multicast group or unicast IP address\n"); - fprintf (stderr, " -s : IP port\n"); - fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); - fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); - exit (1); -} - -int -main ( - int argc, - char* argv[] - ) -{ - pgm_error_t* pgm_err = NULL; - - setlocale (LC_ALL, ""); - - log_init (); - g_message ("enonblocksyncrecvmsgv"); - - if (!pgm_init (&pgm_err)) { - g_error ("Unable to start PGM engine: %s", pgm_err->message); - pgm_error_free (pgm_err); - return EXIT_FAILURE; - } - -/* parse program arguments */ - const char* binary_name = strrchr (argv[0], '/'); - int c; - while ((c = getopt (argc, argv, "s:n:p:lh")) != -1) - { - switch (c) { - case 'n': g_network = optarg; break; - case 's': g_port = atoi (optarg); break; - case 'p': g_udp_encap_port = atoi (optarg); break; - case 'l': g_multicast_loop = TRUE; break; - - case 'h': - case '?': usage (binary_name); - } - } - -/* setup signal handlers */ - signal (SIGSEGV, on_sigsegv); - signal (SIGINT, on_signal); - signal (SIGTERM, on_signal); -#ifdef SIGHUP - signal (SIGHUP, SIG_IGN); -#endif - - if (!on_startup ()) { - g_error ("startup failed"); - return EXIT_FAILURE; - } - -/* incoming message buffer, iov_len must be less than SC_IOV_MAX */ - const long iov_len = 8; - const long ev_len = 1; - g_message ("Using iov_len %li ev_len %li", iov_len, ev_len); - - struct pgm_msgv_t msgv[iov_len]; - struct epoll_event events[ev_len]; /* wait for maximum 1 event */ - -/* epoll file descriptor */ - const int efd = epoll_create (IP_MAX_MEMBERSHIPS); - if (efd < 0) { - g_error ("epoll_create failed errno %i: \"%s\"", errno, strerror(errno)); - return EXIT_FAILURE; - } - - const int retval = pgm_epoll_ctl (g_sock, efd, EPOLL_CTL_ADD, EPOLLIN); - if (retval < 0) { - g_error ("pgm_epoll_ctl failed."); - return EXIT_FAILURE; - } - -/* dispatch loop */ - g_message ("entering PGM message loop ... "); - do { - struct timeval tv; - int timeout; - size_t len; - const int status = pgm_recvmsgv (g_sock, - msgv, - iov_len, - 0, - &len, - &pgm_err); - switch (status) { - case PGM_IO_STATUS_NORMAL: - on_msgv (msgv, len); - break; - - case PGM_IO_STATUS_TIMER_PENDING: - { - socklen_t optlen = sizeof (tv); - pgm_getsockopt (g_sock, PGM_TIME_REMAIN, &tv, &optlen); - } - goto block; - case PGM_IO_STATUS_RATE_LIMITED: - { - socklen_t optlen = sizeof (tv); - pgm_getsockopt (g_sock, PGM_RATE_REMAIN, &tv, &optlen); - } -/* fall through */ - case PGM_IO_STATUS_WOULD_BLOCK: -/* poll for next event */ -block: - timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); - epoll_wait (efd, events, G_N_ELEMENTS(events), timeout /* ms */); - break; - - default: - if (pgm_err) { - g_warning ("%s", pgm_err->message); - pgm_error_free (pgm_err); - pgm_err = NULL; - } - if (PGM_IO_STATUS_ERROR == status) - break; - } - } while (!g_quit); - - g_message ("message loop terminated, cleaning up."); - -/* cleanup */ - close (efd); - if (g_sock) { - g_message ("closing PGM socket."); - pgm_close (g_sock, TRUE); - g_sock = NULL; - } - - g_message ("PGM engine shutdown."); - pgm_shutdown (); - g_message ("finished."); - return EXIT_SUCCESS; -} - -static -void -on_signal ( - int signum - ) -{ - g_message ("on_signal (signum:%d)", signum); - g_quit = TRUE; -} - -static -gboolean -on_startup (void) -{ - struct pgm_addrinfo_t* res = NULL; - pgm_error_t* pgm_err = NULL; - sa_family_t sa_family = AF_UNSPEC; - - g_message ("startup."); - -/* parse network parameter into transport address structure */ - if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { - g_error ("parsing network parameter: %s", pgm_err->message); - goto err_abort; - } - - sa_family = res->ai_send_addrs[0].gsr_group.ss_family; - - if (g_udp_encap_port) { - g_message ("create PGM/UDP socket."); - if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { - g_error ("socket: %s", pgm_err->message); - goto err_abort; - } - pgm_setsockopt (g_sock, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); - pgm_setsockopt (g_sock, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); - } else { - g_message ("create PGM/IP socket."); - if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { - g_error ("socket: %s", pgm_err->message); - goto err_abort; - } - } - -/* Use RFC 2113 tagging for PGM Router Assist */ - const int no_router_assist = 0; - pgm_setsockopt (g_sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); - - pgm_drop_superuser(); - -/* set PGM parameters */ - const int recv_only = 1, - passive = 0, - peer_expiry = pgm_secs (300), - spmr_expiry = pgm_msecs (250), - nak_bo_ivl = pgm_msecs (50), - nak_rpt_ivl = pgm_secs (2), - nak_rdata_ivl = pgm_secs (2), - nak_data_retries = 50, - nak_ncf_retries = 50; - - pgm_setsockopt (g_sock, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); - pgm_setsockopt (g_sock, PGM_PASSIVE, &passive, sizeof(passive)); - pgm_setsockopt (g_sock, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); - pgm_setsockopt (g_sock, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); - pgm_setsockopt (g_sock, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); - pgm_setsockopt (g_sock, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); - pgm_setsockopt (g_sock, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); - pgm_setsockopt (g_sock, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); - -/* create global session identifier */ - struct pgm_sockaddr_t addr; - memset (&addr, 0, sizeof(addr)); - addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; - addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; - if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { - g_error ("creating GSI: %s", pgm_err->message); - goto err_abort; - } - -/* assign socket to specified address */ - struct pgm_interface_req_t if_req; - memset (&if_req, 0, sizeof(if_req)); - if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; - if_req.ir_scope_id = 0; - if (AF_INET6 == sa_family) { - struct sockaddr_in6 sa6; - memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); - if_req.ir_scope_id = sa6.sin6_scope_id; - } - if (!pgm_bind3 (g_sock, - &addr, sizeof(addr), - &if_req, sizeof(if_req), /* tx interface */ - &if_req, sizeof(if_req), /* rx interface */ - &pgm_err)) - { - g_error ("binding PGM socket: %s", pgm_err->message); - goto err_abort; - } - -/* join IP multicast groups */ - for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) - pgm_setsockopt (g_sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); - pgm_setsockopt (g_sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); - pgm_freeaddrinfo (res); - -/* set IP parameters */ - const int nonblocking = 1, - multicast_loop = g_multicast_loop ? 1 : 0, - multicast_hops = 16, - dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ - - pgm_setsockopt (g_sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); - pgm_setsockopt (g_sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); - if (AF_INET6 != sa_family) - pgm_setsockopt (g_sock, PGM_TOS, &dscp, sizeof(dscp)); - pgm_setsockopt (g_sock, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); - - if (!pgm_connect (g_sock, &pgm_err)) { - g_error ("connecting PGM socket: %s", pgm_err->message); - goto err_abort; - } - - g_message ("startup complete."); - return TRUE; - -err_abort: - if (NULL != g_sock) { - pgm_close (g_sock, FALSE); - g_sock = NULL; - } - if (NULL != res) { - pgm_freeaddrinfo (res); - res = NULL; - } - if (NULL != pgm_err) { - pgm_error_free (pgm_err); - pgm_err = NULL; - } - return FALSE; -} - -static -int -on_msgv ( - struct pgm_msgv_t* msgv, /* an array of msgv's */ - size_t len /* total size of all msgv's */ - ) -{ - g_message ("(%u bytes)", - (unsigned)len); - - guint i = 0; - -/* for each apdu display each fragment */ - do { - const struct pgm_sk_buff_t* pskb = msgv[i].msgv_skb[0]; - gsize apdu_len = 0; - for (unsigned j = 0; j < msgv[i].msgv_len; j++) - apdu_len += msgv[i].msgv_skb[j]->len; -/* truncate to first fragment to make GLib printing happy */ - char buf[1024], tsi[PGM_TSISTRLEN]; - const size_t buflen = MIN( sizeof(buf) - 1, pskb->len ); - strncpy (buf, (const char*)pskb->data, buflen); - buf[buflen] = '\0'; - pgm_tsi_print_r (&pskb->tsi, tsi, sizeof(tsi)); - if (msgv[i].msgv_len > 1) { - g_message ("\t%u: \"%s\" ... (%" G_GSIZE_FORMAT " bytes from %s)", i, buf, apdu_len, tsi); - } else { - g_message ("\t%u: \"%s\" (%" G_GSIZE_FORMAT " bytes from %s)", i, buf, apdu_len, tsi); - } - i++; - len -= apdu_len; - } while (len); - - return 0; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/getopt.c b/3rdparty/openpgm-svn-r1085/pgm/examples/getopt.c deleted file mode 100644 index 8c655b6..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/getopt.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 1987, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -#include -#include "getopt.h" - -int opterr = 1, /* if error message should be printed */ - optind = 1, /* index into parent argv vector */ - optopt, /* character checked for validity */ - optreset; /* reset getopt */ -char *optarg; /* argument associated with option */ - -#define BADCH (int)'?' -#define BADARG (int)':' -#define EMSG "" - -/* - * getopt -- - * Parse argc/argv argument vector. - */ -int -getopt(int nargc, char* const* nargv, const char* ostr) -{ - static char *place = EMSG; /* option letter processing */ - char *oli; /* option letter list index */ - - if (optreset || !*place) { /* update scanning pointer */ - optreset = 0; - if (optind >= nargc || *(place = nargv[optind]) != '-') { - place = EMSG; - return (-1); - } - if (place[1] && *++place == '-') { /* found "--" */ - ++optind; - place = EMSG; - return (-1); - } - } /* option letter okay? */ - if ((optopt = (int)*place++) == (int)':' || - !(oli = strchr(ostr, optopt))) { - /* - * if the user didn't specify '-' as an option, - * assume it means -1. - */ - if (optopt == (int)'-') - return (-1); - if (!*place) - ++optind; - if (opterr && *ostr != ':' && optopt != BADCH) - (void)fprintf(stderr, "%s: illegal option -- %c\n", - "progname", optopt); - return (BADCH); - } - if (*++oli != ':') { /* don't need argument */ - optarg = NULL; - if (!*place) - ++optind; - } - else { /* need an argument */ - if (*place) /* no white space */ - optarg = place; - else if (nargc <= ++optind) { /* no arg */ - place = EMSG; - if (*ostr == ':') - return (BADARG); - if (opterr) - (void)fprintf(stderr, - "%s: option requires an argument -- %c\n", - "progname", optopt); - return (BADCH); - } - else /* white space */ - optarg = nargv[optind]; - place = EMSG; - ++optind; - } - return (optopt); /* dump back option letter */ -} - diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/getopt.h b/3rdparty/openpgm-svn-r1085/pgm/examples/getopt.h deleted file mode 100644 index f04387b..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/getopt.h +++ /dev/null @@ -1,62 +0,0 @@ -/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */ -/* $FreeBSD: src/include/getopt.h,v 1.1 2002/09/29 04:14:30 eric Exp $ */ - -/*- - * Copyright (c) 2000 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Dieter Baron and Thomas Klausner. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _GETOPT_H_ -#define _GETOPT_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -/* These are global getopt variables */ -extern int opterr, /* if error message should be printed */ - optind, /* index into parent argv vector */ - optopt, /* character checked for validity */ - optreset; /* reset getopt */ -extern char* optarg; /* argument associated with option */ - -/* Original getopt */ -int getopt (int, char*const*, const char*); - -#ifdef __cplusplus -} -#endif - -#endif /* !_GETOPT_H_ */ - diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/pgmdump.c b/3rdparty/openpgm-svn-r1085/pgm/examples/pgmdump.c deleted file mode 100644 index b91b804..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/pgmdump.c +++ /dev/null @@ -1,279 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Dump PGM packets to the console. - * - * Copyright (c) 2006-2007 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef G_OS_UNIX -# include -# include -# include -# include -#endif - -#include - -/* PGM internals */ -#include - -/* example dependencies */ -#include -#include - - -/* globals */ - -static const char* g_network = "239.192.0.1"; - -static GIOChannel* g_io_channel = NULL; -static GMainLoop* g_loop = NULL; - - -static void on_signal (int); -static gboolean on_startup (gpointer); -static gboolean on_mark (gpointer); - -static gboolean on_io_data (GIOChannel*, GIOCondition, gpointer); - - -int -main ( - G_GNUC_UNUSED int argc, - G_GNUC_UNUSED char *argv[] - ) -{ - GError* err = NULL; - - setlocale (LC_ALL, ""); - - log_init (); - g_message ("pgmdump"); - -/* setup signal handlers */ - signal (SIGSEGV, on_sigsegv); - signal (SIGINT, on_signal); - signal (SIGTERM, on_signal); -#ifdef SIGHUP - signal (SIGHUP, SIG_IGN); -#endif - -/* delayed startup */ - g_message ("scheduling startup."); - g_timeout_add (0, (GSourceFunc)on_startup, NULL); - -/* dispatch loop */ - g_loop = g_main_loop_new (NULL, FALSE); - - g_message ("entering main event loop ... "); - g_main_loop_run (g_loop); - - g_message ("event loop terminated, cleaning up."); - -/* cleanup */ - g_main_loop_unref (g_loop); - g_loop = NULL; - - if (g_io_channel) { - g_message ("closing socket."); - g_io_channel_shutdown (g_io_channel, FALSE, &err); - g_io_channel = NULL; - } - - g_message ("finished."); - return EXIT_SUCCESS; -} - -static void -on_signal ( - G_GNUC_UNUSED int signum - ) -{ - puts ("on_signal"); - - g_main_loop_quit(g_loop); -} - -static -gboolean -on_startup ( - G_GNUC_UNUSED gpointer data - ) -{ - int e; - - g_message ("startup."); - -/* find PGM protocol id */ -// TODO: fix valgrind errors - int ipproto_pgm = IPPROTO_PGM; -#if HAVE_GETPROTOBYNAME_R - char b[1024]; - struct protoent protobuf, *proto; - e = getprotobyname_r ("pgm", &protobuf, b, sizeof(b), &proto); - if (e != -1 && proto != NULL) { - if (proto->p_proto != ipproto_pgm) { - g_message ("Setting PGM protocol number to %i from /etc/protocols.\n"); - ipproto_pgm = proto->p_proto; - } - } -#else - struct protoent *proto = getprotobyname ("pgm"); - if (proto != NULL) { - if (proto->p_proto != ipproto_pgm) { - g_message ("Setting PGM protocol number to %i from /etc/protocols.\n", proto -->p_proto); - ipproto_pgm = proto->p_proto; - } - } -#endif - -/* open socket for snooping */ - g_message ("opening raw socket."); - int sock = socket (PF_INET, SOCK_RAW, ipproto_pgm); - if (sock < 0) { - perror("on_startup() failed"); -#ifdef G_OS_UNIX - if (EPERM == errno && 0 != getuid()) { - g_message ("PGM protocol requires this program to run as superuser."); - } -#endif - g_main_loop_quit (g_loop); - return FALSE; - } - -#ifdef G_OS_UNIX -/* drop out of setuid 0 */ - if (0 == getuid ()) { - g_message ("dropping superuser privileges."); - setuid ((gid_t)65534); - setgid ((uid_t)65534); - } -#endif - - char _t = 1; - e = setsockopt (sock, IPPROTO_IP, IP_HDRINCL, &_t, sizeof(_t)); - if (e < 0) { - perror ("on_startup() failed"); - close (sock); - g_main_loop_quit (g_loop); - return FALSE; - } - -/* buffers */ - int buffer_size = 0; - socklen_t len = 0; - e = getsockopt (sock, SOL_SOCKET, SO_RCVBUF, (char*)&buffer_size, &len); - if (e == 0) { - g_message ("receive buffer set at %i bytes.\n", buffer_size); - } - e = getsockopt (sock, SOL_SOCKET, SO_SNDBUF, (char*)&buffer_size, &len); - if (e == 0) { - g_message ("send buffer set at %i bytes.\n", buffer_size); - } - -/* bind */ - struct sockaddr_in addr; - memset (&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_ANY); - - e = bind (sock, (struct sockaddr*)&addr, sizeof(addr)); - if (e < 0) { - perror ("on_startup() failed"); - close (sock); - g_main_loop_quit (g_loop); - return FALSE; - } - -/* multicast */ - struct ip_mreq mreq; - memset (&mreq, 0, sizeof(mreq)); - mreq.imr_interface.s_addr = htonl (INADDR_ANY); - g_message ("listening on interface %s.\n", inet_ntoa (mreq.imr_interface)); - mreq.imr_multiaddr.s_addr = inet_addr (g_network); - g_message ("subscription on multicast address %s.\n", inet_ntoa (mreq.imr_multiaddr)); - e = setsockopt (sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)); - if (e < 0) { - perror ("on_startup() failed"); - close (sock); - g_main_loop_quit (g_loop); - return FALSE; - } - -/* multicast loopback */ -/* multicast ttl */ - -/* add socket to event manager */ - g_io_channel = g_io_channel_unix_new (sock); - g_message ("socket opened with encoding %s.\n", g_io_channel_get_encoding (g_io_channel)); - - /* guint event = */ g_io_add_watch (g_io_channel, G_IO_IN | G_IO_PRI, on_io_data, NULL); - -/* period timer to indicate some form of life */ -// TODO: Gnome 2.14: replace with g_timeout_add_seconds() - g_timeout_add (10 * 1000, (GSourceFunc)on_mark, NULL); - - g_message ("startup complete."); - return FALSE; -} - -static gboolean -on_mark ( - G_GNUC_UNUSED gpointer data - ) -{ - g_message ("-- MARK --"); - return TRUE; -} - -static gboolean -on_io_data ( - GIOChannel* source, - G_GNUC_UNUSED GIOCondition condition, - G_GNUC_UNUSED gpointer data - ) -{ - char buffer[4096]; - - int fd = g_io_channel_unix_get_fd (source); - struct sockaddr_in addr; - socklen_t addr_len = sizeof(addr); - int len = recvfrom (fd, buffer, sizeof(buffer), MSG_DONTWAIT, (struct sockaddr*)&addr, &addr_len); - - g_message ("%i bytes received from %s.\n", len, inet_ntoa (addr.sin_addr)); - - if (!pgm_print_packet (buffer, len)) { - g_message ("invalid packet :("); - } - - fflush (stdout); - - return TRUE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/pgmping.cc b/3rdparty/openpgm-svn-r1085/pgm/examples/pgmping.cc deleted file mode 100644 index 38ac560..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/pgmping.cc +++ /dev/null @@ -1,1059 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Simple send/reply ping tool using the PGM transport. - * - * With no arguments, one message is sent per second. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* c99 compatibility for c++ */ -#define __STDC_LIMIT_MACROS - -/* Must be first for Sun */ -#include "ping.pb.h" - -/* c99 compatibility for c++ */ -#define __STDC_FORMAT_MACROS -#define restrict - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#ifdef CONFIG_HAVE_EPOLL -# include -#endif -#include -#ifndef _WIN32 -# include -# include -# include -# include -# include -#endif -#include -#include -#ifdef CONFIG_WITH_HTTP -# include -#endif -#ifdef CONFIG_WITH_SNMP -# include -#endif - -/* PGM internal time keeper */ -typedef pgm_time_t (*pgm_time_update_func)(void); -extern pgm_time_update_func pgm_time_update_now; -extern "C" { - size_t pgm_pkt_offset (bool, sa_family_t); -} - -/* example dependencies */ -#include -#include -#include - - -using namespace std; - - -/* globals */ - -static int g_port = 0; -static const char* g_network = ""; -static int g_udp_encap_port = 0; - -static int g_odata_rate = 0; -static int g_odata_interval = 0; -static guint32 g_payload = 0; -static int g_max_tpdu = 1500; -static int g_max_rte = 16*1000*1000; -static int g_sqns = 200; - -static gboolean g_use_pgmcc = FALSE; -static sa_family_t g_pgmcc_family = 0; /* 0 = disabled */ - -static gboolean g_use_fec = FALSE; -static int g_rs_k = 8; -static int g_rs_n = 255; - -static enum { - PGMPING_MODE_SOURCE, - PGMPING_MODE_RECEIVER, - PGMPING_MODE_INITIATOR, - PGMPING_MODE_REFLECTOR -} g_mode = PGMPING_MODE_INITIATOR; - -static pgm_sock_t* g_sock = NULL; - -/* stats */ -static guint64 g_msg_sent = 0; -static guint64 g_msg_received = 0; -static pgm_time_t g_interval_start = 0; -static pgm_time_t g_latency_current = 0; -static guint64 g_latency_seqno = 0; -static guint64 g_last_seqno = 0; -static double g_latency_total = 0.0; -static double g_latency_square_total = 0.0; -static guint64 g_latency_count = 0; -static double g_latency_max = 0.0; -#ifdef INFINITY -static double g_latency_min = INFINITY; -#else -static double g_latency_min = INT64_MAX; -#endif -static double g_latency_running_average = 0.0; -static guint64 g_out_total = 0; -static guint64 g_in_total = 0; - -static GMainLoop* g_loop = NULL; -static GThread* g_sender_thread = NULL; -static GThread* g_receiver_thread = NULL; -static gboolean g_quit; -#ifdef G_OS_UNIX -static int g_quit_pipe[2]; -static void on_signal (int, gpointer); -#else -static HANDLE g_quit_event; -static BOOL on_console_ctrl (DWORD); -#endif - -static gboolean on_startup (gpointer); -static gboolean on_shutdown (gpointer); -static gboolean on_mark (gpointer); - -static void send_odata (void); -static int on_msgv (struct pgm_msgv_t*, size_t); - -static gpointer sender_thread (gpointer); -static gpointer receiver_thread (gpointer); - - -G_GNUC_NORETURN static void -usage (const char* bin) -{ - fprintf (stderr, "Usage: %s [options]\n", bin); - fprintf (stderr, " -n : Multicast group or unicast IP address\n"); - fprintf (stderr, " -s : IP port\n"); - fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); - fprintf (stderr, " -d : Terminate transport after duration.\n"); - fprintf (stderr, " -m : Number of message to send per second\n"); - fprintf (stderr, " -o : Send-only mode (default send & receive mode)\n"); - fprintf (stderr, " -l : Listen-only mode\n"); - fprintf (stderr, " -e : Relect mode\n"); - fprintf (stderr, " -r : Regulate to rate bytes per second\n"); - fprintf (stderr, " -c : Enable PGMCC\n"); - fprintf (stderr, " -f : Enable FEC with either proactive or ondemand parity\n"); - fprintf (stderr, " -K : Configure Reed-Solomon code (n, k)\n"); - fprintf (stderr, " -N \n"); - fprintf (stderr, " -H : Enable HTTP administrative interface\n"); - fprintf (stderr, " -S : Enable SNMP interface\n"); - exit (1); -} - -int -main ( - int argc, - char *argv[] - ) -{ - GError* err = NULL; - pgm_error_t* pgm_err = NULL; - gboolean enable_http = FALSE; - gboolean enable_snmpx = FALSE; - int timeout = 0; - - GOOGLE_PROTOBUF_VERIFY_VERSION; - - setlocale (LC_ALL, ""); - setenv ("PGM_TIMER", "GTOD", 1); - setenv ("PGM_SLEEP", "USLEEP", 1); - - log_init (); - g_message ("pgmping"); - - g_thread_init (NULL); - - if (!pgm_init (&pgm_err)) { - g_error ("Unable to start PGM engine: %s", pgm_err->message); - pgm_error_free (pgm_err); - return EXIT_FAILURE; - } - -/* parse program arguments */ - const char* binary_name = g_get_prgname(); - int c; - while ((c = getopt (argc, argv, "s:n:p:m:old:r:cfeK:N:HSh")) != -1) - { - switch (c) { - case 'n': g_network = optarg; break; - case 's': g_port = atoi (optarg); break; - case 'p': g_udp_encap_port = atoi (optarg); break; - case 'r': g_max_rte = atoi (optarg); break; - - case 'c': g_use_pgmcc = TRUE; break; - - case 'f': g_use_fec = TRUE; break; - case 'K': g_rs_k = atoi (optarg); break; - case 'N': g_rs_n = atoi (optarg); break; - - case 'H': enable_http = TRUE; break; - case 'S': enable_snmpx = TRUE; break; - - case 'm': g_odata_rate = atoi (optarg); - g_odata_interval = (1000 * 1000) / g_odata_rate; break; - case 'd': timeout = 1000 * atoi (optarg); break; - - case 'o': g_mode = PGMPING_MODE_SOURCE; break; - case 'l': g_mode = PGMPING_MODE_RECEIVER; break; - case 'e': g_mode = PGMPING_MODE_REFLECTOR; break; - - case 'h': - case '?': usage (binary_name); - } - } - - if (g_use_fec && ( !g_rs_k || !g_rs_n )) { - g_error ("Invalid Reed-Solomon parameters."); - usage (binary_name); - } - -#ifdef CONFIG_WITH_HTTP - if (enable_http) { - if (!pgm_http_init (PGM_HTTP_DEFAULT_SERVER_PORT, &pgm_err)) { - g_error ("Unable to start HTTP interface: %s", pgm_err->message); - pgm_error_free (pgm_err); - pgm_shutdown (); - return EXIT_FAILURE; - } - } -#endif -#ifdef CONFIG_WITH_SNMP - if (enable_snmpx) { - if (!pgm_snmp_init (&pgm_err)) { - g_error ("Unable to start SNMP interface: %s", pgm_err->message); - pgm_error_free (pgm_err); -#ifdef CONFIG_WITH_HTTP - if (enable_http) - pgm_http_shutdown (); -#endif - pgm_shutdown (); - return EXIT_FAILURE; - } - } -#endif - - g_loop = g_main_loop_new (NULL, FALSE); - - g_quit = FALSE; - -/* setup signal handlers */ - signal (SIGSEGV, on_sigsegv); -#ifdef SIGHUP - signal (SIGHUP, SIG_IGN); -#endif -#ifdef G_OS_UNIX - pipe (g_quit_pipe); - pgm_signal_install (SIGINT, on_signal, g_loop); - pgm_signal_install (SIGTERM, on_signal, g_loop); -#else - g_quit_event = CreateEvent (NULL, TRUE, FALSE, TEXT("QuitEvent")); - SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); -#endif /* !G_OS_UNIX */ - -/* delayed startup */ - g_message ("scheduling startup."); - g_timeout_add (0, (GSourceFunc)on_startup, g_loop); - - if (timeout) { - g_message ("scheduling shutdown."); - g_timeout_add (timeout, (GSourceFunc)on_shutdown, g_loop); - } - -/* dispatch loop */ - g_message ("entering main event loop ... "); - g_main_loop_run (g_loop); - - g_message ("event loop terminated, cleaning up."); - -/* cleanup */ - g_quit = TRUE; -#ifdef G_OS_UNIX - const char one = '1'; - write (g_quit_pipe[1], &one, sizeof(one)); - if (PGMPING_MODE_SOURCE == g_mode || PGMPING_MODE_INITIATOR == g_mode) - g_thread_join (g_sender_thread); - g_thread_join (g_receiver_thread); - close (g_quit_pipe[0]); - close (g_quit_pipe[1]); -#else - SetEvent (g_quit_event); - if (PGMPING_MODE_SOURCE == g_mode || PGMPING_MODE_INITIATOR == g_mode) - g_thread_join (g_sender_thread); - g_thread_join (g_receiver_thread); - CloseHandle (g_quit_event); -#endif - - g_main_loop_unref (g_loop); - g_loop = NULL; - - if (g_sock) { - g_message ("closing PGM socket."); - pgm_close (g_sock, TRUE); - g_sock = NULL; - } - -#ifdef CONFIG_WITH_HTTP - if (enable_http) - pgm_http_shutdown(); -#endif -#ifdef CONFIG_WITH_SNMP - if (enable_snmpx) - pgm_snmp_shutdown(); -#endif - - google::protobuf::ShutdownProtobufLibrary(); - - g_message ("PGM engine shutdown."); - pgm_shutdown (); - g_message ("finished."); - return EXIT_SUCCESS; -} - -#ifdef G_OS_UNIX -static -void -on_signal ( - int signum, - gpointer user_data - ) -{ - GMainLoop* loop = (GMainLoop*)user_data; - g_message ("on_signal (signum:%d user-data:%p)", - signum, user_data); - g_main_loop_quit (loop); -} -#else -static -BOOL -on_console_ctrl ( - DWORD dwCtrlType - ) -{ - g_message ("on_console_ctrl (dwCtrlType:%lu)", (unsigned long)dwCtrlType); - g_main_loop_quit (g_loop); - return TRUE; -} -#endif /* !G_OS_UNIX */ - -static -gboolean -on_shutdown ( - gpointer user_data - ) -{ - GMainLoop* loop = (GMainLoop*)user_data; - g_message ("on_shutdown (user-data:%p)", user_data); - g_main_loop_quit (loop); - return FALSE; -} - -static -gboolean -on_startup ( - gpointer user_data - ) -{ - GMainLoop* loop = (GMainLoop*)user_data; - struct pgm_addrinfo_t* res = NULL; - GError* err = NULL; - pgm_error_t* pgm_err = NULL; - sa_family_t sa_family = AF_UNSPEC; - - g_message ("startup."); - -/* parse network parameter into transport address structure */ - if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { - g_error ("parsing network parameter: %s", pgm_err->message); - goto err_abort; - } - - sa_family = res->ai_send_addrs[0].gsr_group.ss_family; - if (g_use_pgmcc) - g_pgmcc_family = sa_family; - - if (g_udp_encap_port) { - g_message ("create PGM/UDP socket."); - if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { - g_error ("socket: %s", pgm_err->message); - goto err_abort; - } - pgm_setsockopt (g_sock, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); - pgm_setsockopt (g_sock, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); - } else { - g_message ("create PGM/IP socket."); - if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { - g_error ("socket: %s", pgm_err->message); - goto err_abort; - } - } - -/* Use RFC 2113 tagging for PGM Router Assist */ - { - const int no_router_assist = 0; - pgm_setsockopt (g_sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); - } - - pgm_drop_superuser(); - -/* set PGM parameters */ - if (PGMPING_MODE_SOURCE == g_mode || - PGMPING_MODE_INITIATOR == g_mode || - PGMPING_MODE_REFLECTOR == g_mode) - { - const int send_only = PGMPING_MODE_SOURCE == g_mode ? 1 : 0, - ambient_spm = pgm_secs (30), - heartbeat_spm[] = { pgm_msecs (100), - pgm_msecs (100), - pgm_msecs (100), - pgm_msecs (100), - pgm_msecs (1300), - pgm_secs (7), - pgm_secs (16), - pgm_secs (25), - pgm_secs (30) }; - - pgm_setsockopt (g_sock, PGM_SEND_ONLY, &send_only, sizeof(send_only)); - pgm_setsockopt (g_sock, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); - pgm_setsockopt (g_sock, PGM_TXW_SQNS, &g_sqns, sizeof(g_sqns)); - pgm_setsockopt (g_sock, PGM_TXW_MAX_RTE, &g_max_rte, sizeof(g_max_rte)); - pgm_setsockopt (g_sock, PGM_AMBIENT_SPM, &ambient_spm, sizeof(ambient_spm)); - pgm_setsockopt (g_sock, PGM_HEARTBEAT_SPM, &heartbeat_spm, sizeof(heartbeat_spm)); - } - if (PGMPING_MODE_RECEIVER == g_mode || - PGMPING_MODE_INITIATOR == g_mode || - PGMPING_MODE_REFLECTOR == g_mode) - { - const int recv_only = PGMPING_MODE_RECEIVER == g_mode ? 1 : 0, - passive = 0, - peer_expiry = pgm_secs (300), - spmr_expiry = pgm_msecs (250), - nak_bo_ivl = pgm_msecs (50), - nak_rpt_ivl = pgm_msecs (200), //pgm_secs (2), - nak_rdata_ivl = pgm_msecs (200), //pgm_secs (2), - nak_data_retries = 50, - nak_ncf_retries = 50; - - pgm_setsockopt (g_sock, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); - pgm_setsockopt (g_sock, PGM_PASSIVE, &passive, sizeof(passive)); - pgm_setsockopt (g_sock, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); - pgm_setsockopt (g_sock, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); - pgm_setsockopt (g_sock, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); - pgm_setsockopt (g_sock, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); - pgm_setsockopt (g_sock, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); - pgm_setsockopt (g_sock, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); - } - -#ifdef I_UNDERSTAND_PGMCC_AND_FEC_ARE_NOT_SUPPORTED -/* PGMCC congestion control */ - if (g_use_pgmcc) { - struct pgm_pgmccinfo_t pgmccinfo; - pgmccinfo.ack_bo_ivl = pgm_msecs (50); - pgmccinfo.ack_c = 75; - pgmccinfo.ack_c_p = 500; - pgm_setsockopt (g_sock, PGM_USE_PGMCC, &pgmccinfo, sizeof(pgmccinfo)); - } - -/* Reed Solomon forward error correction */ - if (g_use_fec) { - struct pgm_fecinfo_t fecinfo; - fecinfo.block_size = g_rs_n; - fecinfo.proactive_packets = 0; - fecinfo.group_size = g_rs_k; - fecinfo.ondemand_parity_enabled = TRUE; - fecinfo.var_pktlen_enabled = TRUE; - pgm_setsockopt (g_sock, PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); - } -#endif - -/* create global session identifier */ - struct pgm_sockaddr_t addr; - memset (&addr, 0, sizeof(addr)); - addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; - addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; - if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { - g_error ("creating GSI: %s", pgm_err->message); - goto err_abort; - } - -/* assign socket to specified address */ - struct pgm_interface_req_t if_req; - memset (&if_req, 0, sizeof(if_req)); - if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; - if_req.ir_scope_id = 0; - if (AF_INET6 == sa_family) { - struct sockaddr_in6 sa6; - memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); - if_req.ir_scope_id = sa6.sin6_scope_id; - } - if (!pgm_bind3 (g_sock, - &addr, sizeof(addr), - &if_req, sizeof(if_req), /* tx interface */ - &if_req, sizeof(if_req), /* rx interface */ - &pgm_err)) - { - g_error ("binding PGM socket: %s", pgm_err->message); - goto err_abort; - } - -/* join IP multicast groups */ - for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) - pgm_setsockopt (g_sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); - pgm_setsockopt (g_sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); - pgm_freeaddrinfo (res); - -/* set IP parameters */ - { - const int nonblocking = 1, - multicast_loop = 0, - multicast_hops = 16, - dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ - - pgm_setsockopt (g_sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); - pgm_setsockopt (g_sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); - if (AF_INET6 != sa_family) - pgm_setsockopt (g_sock, PGM_TOS, &dscp, sizeof(dscp)); - pgm_setsockopt (g_sock, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); - } - - if (!pgm_connect (g_sock, &pgm_err)) { - g_error ("connecting PGM socket: %s", pgm_err->message); - goto err_abort; - } - -/* period timer to indicate some form of life */ -// TODO: Gnome 2.14: replace with g_timeout_add_seconds() - g_timeout_add (2 * 1000, (GSourceFunc)on_mark, NULL); - - if (PGMPING_MODE_SOURCE == g_mode || PGMPING_MODE_INITIATOR == g_mode) - { - g_sender_thread = g_thread_create_full (sender_thread, - g_sock, - 0, - TRUE, - TRUE, - G_THREAD_PRIORITY_NORMAL, - &err); - if (!g_sender_thread) { - g_critical ("g_thread_create_full failed errno %i: \"%s\"", err->code, err->message); - goto err_abort; - } - } - - { - g_receiver_thread = g_thread_create_full (receiver_thread, - g_sock, - 0, - TRUE, - TRUE, - G_THREAD_PRIORITY_HIGH, - &err); - if (!g_receiver_thread) { - g_critical ("g_thread_create_full failed errno %i: \"%s\"", err->code, err->message); - goto err_abort; - } - } - - g_message ("startup complete."); - return FALSE; - -err_abort: - if (NULL != g_sock) { - pgm_close (g_sock, FALSE); - g_sock = NULL; - } - if (NULL != res) { - pgm_freeaddrinfo (res); - res = NULL; - } - if (NULL != pgm_err) { - pgm_error_free (pgm_err); - pgm_err = NULL; - } - return FALSE; -} - -static -gpointer -sender_thread ( - gpointer user_data - ) -{ - pgm_sock_t* tx_sock = (pgm_sock_t*)user_data; - example::Ping ping; - string subject("PING.PGM.TEST."); - char hostname[NI_MAXHOST + 1]; - const long payload_len = 1000; - char payload[payload_len]; - gpointer buffer = NULL; - guint64 latency, now, last; - -#ifdef CONFIG_HAVE_EPOLL - const long ev_len = 1; - struct epoll_event events[ev_len]; - - int efd_again = epoll_create (IP_MAX_MEMBERSHIPS); - if (efd_again < 0) { - g_error ("epoll_create failed errno %i: \"%s\"", errno, strerror(errno)); - g_main_loop_quit (g_loop); - return NULL; - } -/* Add write event to epoll domain in order to re-enable as required by return - * value. We use one-shot flag to disable ASAP, as we don't want such events - * until triggered. - */ - if (pgm_epoll_ctl (tx_sock, efd_again, EPOLL_CTL_ADD, EPOLLOUT | EPOLLONESHOT) < 0) { - g_error ("pgm_epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); - g_main_loop_quit (g_loop); - return NULL; - } - struct epoll_event event; - memset (&event, 0, sizeof(event)); - event.events = EPOLLIN; - event.data.fd = g_quit_pipe[0]; - if (epoll_ctl (efd_again, EPOLL_CTL_ADD, g_quit_pipe[0], &event) < 0) { - g_error ("epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); - g_main_loop_quit (g_loop); - return NULL; - } -#elif defined(CONFIG_HAVE_POLL) - int n_fds = 2; - struct pollfd fds[ 2 + 1 ]; -#endif /* !CONFIG_HAVE_EPOLL */ - - gethostname (hostname, sizeof(hostname)); - subject.append(hostname); - memset (payload, 0, sizeof(payload)); - - ping.mutable_subscription_header()->set_subject (subject); - ping.mutable_market_data_header()->set_msg_type (example::MarketDataHeader::MSG_VERIFY); - ping.mutable_market_data_header()->set_rec_type (example::MarketDataHeader::PING); - ping.mutable_market_data_header()->set_rec_status (example::MarketDataHeader::STATUS_OK); - ping.set_time (last); - - last = now = pgm_time_update_now(); - do { - if (g_msg_sent && g_latency_seqno + 1 == g_msg_sent) - latency = g_latency_current; - else - latency = g_odata_interval; - - ping.set_seqno (g_msg_sent); - ping.set_latency (latency); - ping.set_payload (payload, sizeof(payload)); - - const size_t header_size = pgm_pkt_offset (FALSE, g_pgmcc_family); - const size_t apdu_size = ping.ByteSize(); - struct pgm_sk_buff_t* skb = pgm_alloc_skb (g_max_tpdu); - pgm_skb_reserve (skb, header_size); - pgm_skb_put (skb, apdu_size); - -/* wait on packet rate limit */ - if ((last + g_odata_interval) > now) { -#ifndef _WIN32 - const unsigned int usec = g_odata_interval - (now - last); - usleep (usec); -#else - const DWORD msec = usecs_to_msecs (g_odata_interval - (now - last)); - Sleep (msec); -#endif - now = pgm_time_update_now(); - } - last += g_odata_interval; - ping.set_time (now); - ping.SerializeToArray (skb->data, skb->len); - - struct timeval tv; - int timeout; - size_t bytes_written; - int status; -again: - status = pgm_send_skbv (tx_sock, &skb, 1, TRUE, &bytes_written); - switch (status) { -/* rate control */ - case PGM_IO_STATUS_RATE_LIMITED: - { - socklen_t optlen = sizeof (tv); - pgm_getsockopt (tx_sock, PGM_RATE_REMAIN, &tv, &optlen); - timeout = (tv.tv_sec * 1000) + ((tv.tv_usec + 500) / 1000); -/* busy wait under 2ms */ - if (timeout < 2) timeout = 0; -#ifdef CONFIG_HAVE_EPOLL - const int ready = epoll_wait (efd_again, events, G_N_ELEMENTS(events), timeout /* ms */); -#elif defined(CONFIG_HAVE_POLL) - memset (fds, 0, sizeof(fds)); - fds[0].fd = g_quit_pipe[0]; - fds[0].events = POLLIN; - pgm_poll_info (tx_sock, &fds[1], &n_fds, POLLIN); - poll (fds, 1 + n_fds, timeout /* ms */); -#endif /* !CONFIG_HAVE_EPOLL */ - if (G_UNLIKELY(g_quit)) - break; - goto again; - } -/* congestion control */ - case PGM_IO_STATUS_CONGESTION: -/* kernel feedback */ - case PGM_IO_STATUS_WOULD_BLOCK: - { -#ifdef CONFIG_HAVE_EPOLL -/* re-enable write event for one-shot */ - if (pgm_epoll_ctl (tx_sock, efd_again, EPOLL_CTL_MOD, EPOLLOUT | EPOLLONESHOT) < 0) - { - g_error ("pgm_epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); - g_main_loop_quit (g_loop); - return NULL; - } - const int ready = epoll_wait (efd_again, events, G_N_ELEMENTS(events), -1 /* ms */); - if (G_UNLIKELY(g_quit)) - break; -#elif defined(CONFIG_HAVE_POLL) - memset (fds, 0, sizeof(fds)); - fds[0].fd = g_quit_pipe[0]; - fds[0].events = POLLIN; - pgm_poll_info (g_sock, &fds[1], &n_fds, POLLOUT); - poll (fds, 1 + n_fds, -1 /* ms */); -#endif /* !CONFIG_HAVE_EPOLL */ - goto again; - } -/* successful delivery */ - case PGM_IO_STATUS_NORMAL: -// g_message ("sent payload: %s", ping.DebugString().c_str()); -// g_message ("sent %u bytes", (unsigned)bytes_written); - break; - default: - g_warning ("pgm_send_skbv failed, status:%i", status); - g_main_loop_quit (g_loop); - return NULL; - } - g_out_total += bytes_written; - g_msg_sent++; - } while (!g_quit); - -#ifdef CONFIG_HAVE_EPOLL - close (efd_again); -#endif - return NULL; -} - -static -gpointer -receiver_thread ( - gpointer data - ) -{ - pgm_sock_t* rx_sock = (pgm_sock_t*)data; - const long iov_len = 20; - struct pgm_msgv_t msgv[iov_len]; - pgm_time_t lost_tstamp = 0; - pgm_tsi_t lost_tsi; - guint32 lost_count = 0; - -#ifdef CONFIG_HAVE_EPOLL - const long ev_len = 1; - struct epoll_event events[ev_len]; - - int efd = epoll_create (IP_MAX_MEMBERSHIPS); - if (efd < 0) { - g_error ("epoll_create failed errno %i: \"%s\"", errno, strerror(errno)); - g_main_loop_quit (g_loop); - return NULL; - } - if (pgm_epoll_ctl (rx_sock, efd, EPOLL_CTL_ADD, EPOLLIN) < 0) { - g_error ("pgm_epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); - g_main_loop_quit (g_loop); - return NULL; - } - struct epoll_event event; - memset (&event, 0, sizeof(event)); - event.events = EPOLLIN; - if (epoll_ctl (efd, EPOLL_CTL_ADD, g_quit_pipe[0], &event) < 0) { - g_error ("epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); - g_main_loop_quit (g_loop); - return NULL; - } -#elif defined(CONFIG_HAVE_POLL) - int n_fds = 3; - struct pollfd fds[ 3 + 1 ]; -#endif /* !CONFIG_HAVE_EPOLL */ - - memset (&lost_tsi, 0, sizeof(lost_tsi)); - - do { - struct timeval tv; - int timeout; - size_t len; - pgm_error_t* pgm_err = NULL; - const int status = pgm_recvmsgv (rx_sock, - msgv, - G_N_ELEMENTS(msgv), - MSG_ERRQUEUE, - &len, - &pgm_err); - if (lost_count) { - pgm_time_t elapsed = pgm_time_update_now() - lost_tstamp; - if (elapsed >= pgm_secs(1)) { - g_warning ("pgm data lost %" G_GUINT32_FORMAT " packets detected from %s", - lost_count, pgm_tsi_print (&lost_tsi)); - lost_count = 0; - } - } - - switch (status) { - case PGM_IO_STATUS_NORMAL: -// g_message ("recv %u bytes", (unsigned)len); - on_msgv (msgv, len); - break; - case PGM_IO_STATUS_TIMER_PENDING: - { - socklen_t optlen = sizeof (tv); - pgm_getsockopt (g_sock, PGM_TIME_REMAIN, &tv, &optlen); - } - goto block; - case PGM_IO_STATUS_RATE_LIMITED: - { - socklen_t optlen = sizeof (tv); - pgm_getsockopt (g_sock, PGM_RATE_REMAIN, &tv, &optlen); - } -/* fall through */ - case PGM_IO_STATUS_WOULD_BLOCK: -block: - timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); -/* busy wait under 2ms */ - if (timeout > 0 && timeout < 2) timeout = 0; -#ifdef CONFIG_HAVE_EPOLL - epoll_wait (efd, events, G_N_ELEMENTS(events), timeout /* ms */); -#elif defined(CONFIG_HAVE_POLL) - memset (fds, 0, sizeof(fds)); - fds[0].fd = g_quit_pipe[0]; - fds[0].events = POLLIN; - pgm_transport_poll_info (g_transport, &fds[1], &n_fds, POLLIN); - poll (fds, 1 + n_fds, timeout /* ms */); -#endif /* !CONFIG_HAVE_EPOLL */ - break; - case PGM_IO_STATUS_RESET: - { - struct pgm_sk_buff_t* skb = msgv[0].msgv_skb[0]; - lost_tstamp = skb->tstamp; - if (pgm_tsi_equal (&skb->tsi, &lost_tsi)) - lost_count += skb->sequence; - else { - lost_count = skb->sequence; - memcpy (&lost_tsi, &skb->tsi, sizeof(pgm_tsi_t)); - } - pgm_free_skb (skb); - break; - } - default: - if (pgm_err) { - g_warning ("%s", pgm_err->message); - pgm_error_free (pgm_err); - pgm_err = NULL; - } - break; - } - } while (!g_quit); - -#ifdef CONFIG_HAVE_EPOLL - close (efd); -#endif - return NULL; -} - -static -int -on_msgv ( - struct pgm_msgv_t* msgv, /* an array of msgvs */ - size_t len - ) -{ - example::Ping ping; - guint i = 0; - static pgm_time_t last_time = pgm_time_update_now(); - - while (len) - { - const struct pgm_sk_buff_t* pskb = msgv[i].msgv_skb[0]; - gsize apdu_len = 0; - for (unsigned j = 0; j < msgv[i].msgv_len; j++) - apdu_len += msgv[i].msgv_skb[j]->len; - - if (PGMPING_MODE_REFLECTOR == g_mode) - { - int status; -again: - status = pgm_send (g_sock, pskb->data, pskb->len, NULL); - switch (status) { - case PGM_IO_STATUS_RATE_LIMITED: - case PGM_IO_STATUS_CONGESTION: - case PGM_IO_STATUS_WOULD_BLOCK: -/* busy wait always as reflector */ - goto again; - - case PGM_IO_STATUS_NORMAL: - break; - - default: - g_warning ("pgm_send_skbv failed"); - g_main_loop_quit (g_loop); - return 0; - } - goto next_msg; - } - -/* only parse first fragment of each apdu */ - if (!ping.ParseFromArray (pskb->data, pskb->len)) - goto next_msg; -// g_message ("payload: %s", ping.DebugString().c_str()); - - { - const pgm_time_t send_time = ping.time(); - const pgm_time_t recv_time = pskb->tstamp; - const guint64 seqno = ping.seqno(); - const guint64 latency = ping.latency(); - - if (seqno < g_latency_seqno) { - g_message ("seqno replay?"); - goto next_msg; - } - - g_in_total += pskb->len; - g_msg_received++; - -/* handle ping */ - const pgm_time_t now = pgm_time_update_now(); - if (send_time > now) - g_warning ("send time %" PGM_TIME_FORMAT " newer than now %" PGM_TIME_FORMAT, - send_time, now); - if (recv_time > now) - g_warning ("recv time %" PGM_TIME_FORMAT " newer than now %" PGM_TIME_FORMAT, - recv_time, now); - if (send_time >= recv_time){ - g_message ("timer mismatch, send time = recv time + %.3f ms (last time + %.3f ms)", - pgm_to_msecsf(send_time - recv_time), - pgm_to_msecsf(last_time - send_time)); - goto next_msg; - } - g_latency_current = pgm_to_secs(recv_time - send_time); - g_latency_seqno = seqno; - - const double elapsed = pgm_to_usecsf (recv_time - send_time); - g_latency_total += elapsed; - g_latency_square_total += elapsed * elapsed; - - if (elapsed > g_latency_max) - g_latency_max = elapsed; - if (elapsed < g_latency_min) - g_latency_min = elapsed; - - g_latency_running_average += elapsed; - g_latency_count++; - last_time = recv_time; - } - -/* move onto next apdu */ -next_msg: - i++; - len -= apdu_len; - } - - return 0; -} - -/* idle log notification - */ - -static -gboolean -on_mark ( - G_GNUC_UNUSED gpointer data - ) -{ - const pgm_time_t now = pgm_time_update_now (); - const double interval = pgm_to_secsf(now - g_interval_start); - g_interval_start = now; - -/* receiving a ping */ - if (g_latency_count) - { - const double average = g_latency_total / g_latency_count; - const double variance = g_latency_square_total / g_latency_count - - average * average; - const double standard_deviation = sqrt (variance); - - if (g_latency_count < 10) - { - if (average < 1000.0) - g_message ("seqno=%" G_GUINT64_FORMAT " time=%.01f us", - g_latency_seqno, average); - else - g_message ("seqno=%" G_GUINT64_FORMAT " time=%.01f ms", - g_latency_seqno, average / 1000); - } - else - { - double seq_rate = (g_latency_seqno - g_last_seqno) / interval; - double out_rate = g_out_total * 8.0 / 1000000.0 / interval; - double in_rate = g_in_total * 8.0 / 1000000.0 / interval; - if (g_latency_min < 1000.0) - g_message ("s=%.01f avg=%.01f min=%.01f max=%.01f stddev=%0.1f us o=%.2f i=%.2f mbit", - seq_rate, average, g_latency_min, g_latency_max, standard_deviation, out_rate, in_rate); - else - g_message ("s=%.01f avg=%.01f min=%.01f max=%.01f stddev=%0.1f ms o=%.2f i=%.2f mbit", - seq_rate, average / 1000, g_latency_min / 1000, g_latency_max / 1000, standard_deviation / 1000, out_rate, in_rate); - } - -/* reset interval counters */ - g_latency_total = 0.0; - g_latency_square_total = 0.0; - g_latency_count = 0; - g_last_seqno = g_latency_seqno; -#ifdef INFINITY - g_latency_min = INFINITY; -#else - g_latency_min = INT64_MAX; -#endif - g_latency_max = 0.0; - g_out_total = 0; - g_in_total = 0; - } - - return TRUE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/pgmrecv.c b/3rdparty/openpgm-svn-r1085/pgm/examples/pgmrecv.c deleted file mode 100644 index 035ccc4..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/pgmrecv.c +++ /dev/null @@ -1,649 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Simple receiver using the PGM transport, based on enonblocksyncrecvmsgv :/ - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_HAVE_EPOLL -# include -#endif -#include -#include -#ifdef G_OS_UNIX -# include -# include -# include -# include -# include -# include -# include -#else -# include "getopt.h" -#endif -#include -#ifdef CONFIG_WITH_HTTP -# include -#endif -#ifdef CONFIG_WITH_SNMP -# include -#endif - -/* example dependencies */ -#include -#include -#include - - -/* globals */ - -static int g_port = 0; -static const char* g_network = ""; -static const char* g_source = ""; -static gboolean g_multicast_loop = FALSE; -static int g_udp_encap_port = 0; - -static int g_max_tpdu = 1500; -static int g_sqns = 100; - -static pgm_sock_t* g_sock = NULL; -static GThread* g_thread = NULL; -static GMainLoop* g_loop = NULL; -static gboolean g_quit; -#ifdef G_OS_UNIX -static int g_quit_pipe[2]; -static void on_signal (int, gpointer); -#else -static HANDLE g_quit_event; -static BOOL on_console_ctrl (DWORD); -#endif - -static gboolean on_startup (gpointer); -static gboolean on_mark (gpointer); - -static gpointer receiver_thread (gpointer); -static int on_msgv (struct pgm_msgv_t*, size_t); - - -G_GNUC_NORETURN static void -usage ( - const char* bin - ) -{ - fprintf (stderr, "Usage: %s [options]\n", bin); - fprintf (stderr, " -n : Multicast group or unicast IP address\n"); - fprintf (stderr, " -a : Source unicast IP address\n"); - fprintf (stderr, " -s : IP port\n"); - fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); - fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); -#ifdef CONFIG_WITH_HTTP - fprintf (stderr, " -H : Enable HTTP administrative interface\n"); -#endif -#ifdef CONFIG_WITH_SNMP - fprintf (stderr, " -S : Enable SNMP interface\n"); -#endif - fprintf (stderr, " -i : List available interfaces\n"); - exit (1); -} - -int -main ( - int argc, - char* argv[] - ) -{ - int e; - pgm_error_t* pgm_err = NULL; -#ifdef CONFIG_WITH_HTTP - gboolean enable_http = FALSE; -#endif -#ifdef CONFIG_WITH_SNMP - gboolean enable_snmpx = FALSE; -#endif - - setlocale (LC_ALL, ""); - -/* pre-initialise PGM messages module to add hook for GLib logging */ - pgm_messages_init(); - log_init (); - g_message ("pgmrecv"); - - if (!pgm_init (&pgm_err)) { - g_error ("Unable to start PGM engine: %s", (pgm_err && pgm_err->message) ? pgm_err->message : "(null)"); - pgm_error_free (pgm_err); - pgm_messages_shutdown(); - return EXIT_FAILURE; - } - - g_thread_init (NULL); - -/* parse program arguments */ - const char* binary_name = strrchr (argv[0], '/'); - int c; - while ((c = getopt (argc, argv, "a:s:n:p:lih" -#ifdef CONFIG_WITH_HTTP - "H" -#endif -#ifdef CONFIG_WITH_SNMP - "S" -#endif - )) != -1) - { - switch (c) { - case 'n': g_network = optarg; break; - case 'a': g_source = optarg; break; - case 's': g_port = atoi (optarg); break; - case 'p': g_udp_encap_port = atoi (optarg); break; - - case 'l': g_multicast_loop = TRUE; break; -#ifdef CONFIG_WITH_HTTP - case 'H': enable_http = TRUE; break; -#endif -#ifdef CONFIG_WITH_SNMP - case 'S': enable_snmpx = TRUE; break; -#endif - - case 'i': - pgm_if_print_all(); - pgm_messages_shutdown(); - return EXIT_SUCCESS; - - case 'h': - case '?': - pgm_messages_shutdown(); - usage (binary_name); - } - } - -#ifdef CONFIG_WITH_HTTP - if (enable_http) { - if (!pgm_http_init (PGM_HTTP_DEFAULT_SERVER_PORT, &pgm_err)) { - g_error ("Unable to start HTTP interface: %s", pgm_err->message); - pgm_error_free (pgm_err); - pgm_shutdown(); - pgm_messages_shutdown(); - return EXIT_FAILURE; - } - } -#endif -#ifdef CONFIG_WITH_SNMP - if (enable_snmpx) { - if (!pgm_snmp_init (&pgm_err)) { - g_error ("Unable to start SNMP interface: %s", pgm_err->message); - pgm_error_free (pgm_err); -#ifdef CONFIG_WITH_HTTP - if (enable_http) - pgm_http_shutdown (); -#endif - pgm_shutdown (); - pgm_messages_shutdown(); - return EXIT_FAILURE; - } - } -#endif - - g_loop = g_main_loop_new (NULL, FALSE); - - g_quit = FALSE; - -/* setup signal handlers */ - signal (SIGSEGV, on_sigsegv); -#ifdef SIGHUP - signal (SIGHUP, SIG_IGN); -#endif -#ifdef G_OS_UNIX - e = pipe (g_quit_pipe); - g_assert (0 == e); - pgm_signal_install (SIGINT, on_signal, g_loop); - pgm_signal_install (SIGTERM, on_signal, g_loop); -#else - g_quit_event = CreateEvent (NULL, TRUE, FALSE, TEXT("QuitEvent")); - SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); -#endif - -/* delayed startup */ - g_message ("scheduling startup."); - g_timeout_add (0, (GSourceFunc)on_startup, NULL); - -/* dispatch loop */ - g_message ("entering main event loop ... "); - g_main_loop_run (g_loop); - - g_message ("event loop terminated, cleaning up."); - -/* cleanup */ - g_quit = TRUE; -#ifdef G_OS_UNIX - const char one = '1'; - const size_t writelen = write (g_quit_pipe[1], &one, sizeof(one)); - g_assert (sizeof(one) == writelen); - g_thread_join (g_thread); - close (g_quit_pipe[0]); - close (g_quit_pipe[1]); -#else - SetEvent (g_quit_event); - g_thread_join (g_thread); - CloseHandle (g_quit_event); -#endif - - g_main_loop_unref (g_loop); - g_loop = NULL; - - if (g_sock) { - g_message ("closing PGM socket."); - - pgm_close (g_sock, TRUE); - g_sock = NULL; - } - -#ifdef CONFIG_WITH_HTTP - if (enable_http) - pgm_http_shutdown(); -#endif -#ifdef CONFIG_WITH_SNMP - if (enable_snmpx) - pgm_snmp_shutdown(); -#endif - - g_message ("PGM engine shutdown."); - pgm_shutdown(); - g_message ("finished."); - pgm_messages_shutdown(); - return EXIT_SUCCESS; -} - -#ifdef G_OS_UNIX -static -void -on_signal ( - int signum, - gpointer user_data - ) -{ - GMainLoop* loop = (GMainLoop*)user_data; - g_message ("on_signal (signum:%d user_data:%p)", - signum, user_data); - g_main_loop_quit (loop); -} -#else -static -BOOL -on_console_ctrl ( - DWORD dwCtrlType - ) -{ - g_message ("on_console_ctrl (dwCtrlType:%lu)", (unsigned long)dwCtrlType); - g_main_loop_quit (g_loop); - return TRUE; -} -#endif /* !G_OS_UNIX */ - -static -gboolean -on_startup ( - G_GNUC_UNUSED gpointer data - ) -{ - struct pgm_addrinfo_t* res = NULL; - pgm_error_t* pgm_err = NULL; - sa_family_t sa_family = AF_UNSPEC; - - g_message ("startup."); - -/* parse network parameter into transport address structure */ - if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { - g_error ("parsing network parameter: %s", pgm_err->message); - goto err_abort; - } - - sa_family = res->ai_send_addrs[0].gsr_group.ss_family; - - if (g_udp_encap_port) { - g_message ("create PGM/UDP socket."); - if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { - g_error ("socket: %s", pgm_err->message); - goto err_abort; - } - pgm_setsockopt (g_sock, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); - pgm_setsockopt (g_sock, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); - } else { - g_message ("create PGM/IP socket."); - if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { - g_error ("socket: %s", pgm_err->message); - goto err_abort; - } - } - -/* Use RFC 2113 tagging for PGM Router Assist */ - const int no_router_assist = 0; - pgm_setsockopt (g_sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); - - pgm_drop_superuser(); - -/* set PGM parameters */ - const int recv_only = 1, - passive = 0, - peer_expiry = pgm_secs (300), - spmr_expiry = pgm_msecs (250), - nak_bo_ivl = pgm_msecs (50), - nak_rpt_ivl = pgm_secs (2), - nak_rdata_ivl = pgm_secs (2), - nak_data_retries = 50, - nak_ncf_retries = 50; - - pgm_setsockopt (g_sock, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); - pgm_setsockopt (g_sock, PGM_PASSIVE, &passive, sizeof(passive)); - pgm_setsockopt (g_sock, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); - pgm_setsockopt (g_sock, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); - pgm_setsockopt (g_sock, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); - pgm_setsockopt (g_sock, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); - pgm_setsockopt (g_sock, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); - pgm_setsockopt (g_sock, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); - -/* create global session identifier */ - struct pgm_sockaddr_t addr; - memset (&addr, 0, sizeof(addr)); - addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; - addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; - if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { - g_error ("creating GSI: %s", pgm_err->message); - goto err_abort; - } - -/* assign socket to specified address */ - struct pgm_interface_req_t if_req; - memset (&if_req, 0, sizeof(if_req)); - if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; - if_req.ir_scope_id = 0; - if (AF_INET6 == sa_family) { - struct sockaddr_in6 sa6; - memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); - if_req.ir_scope_id = sa6.sin6_scope_id; - } - if (!pgm_bind3 (g_sock, - &addr, sizeof(addr), - &if_req, sizeof(if_req), /* tx interface */ - &if_req, sizeof(if_req), /* rx interface */ - &pgm_err)) - { - g_error ("binding PGM socket: %s", pgm_err->message); - goto err_abort; - } - -/* join IP multicast groups */ - for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) - pgm_setsockopt (g_sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); - pgm_setsockopt (g_sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); - pgm_freeaddrinfo (res); - -/* set IP parameters */ - const int nonblocking = 1, - multicast_loop = g_multicast_loop ? 1 : 0, - multicast_hops = 16, - dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ - - pgm_setsockopt (g_sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); - pgm_setsockopt (g_sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); - if (AF_INET6 != sa_family) - pgm_setsockopt (g_sock, PGM_TOS, &dscp, sizeof(dscp)); - pgm_setsockopt (g_sock, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); - - if (!pgm_connect (g_sock, &pgm_err)) { - g_error ("connecting PGM socket: %s", pgm_err->message); - goto err_abort; - } - -/* create receiver thread */ - GError* glib_err = NULL; - g_thread = g_thread_create_full (receiver_thread, - g_sock, - 0, - TRUE, - TRUE, - G_THREAD_PRIORITY_HIGH, - &glib_err); - if (!g_thread) { - g_error ("g_thread_create_full failed errno %i: \"%s\"", glib_err->code, glib_err->message); - g_error_free (glib_err); - goto err_abort; - } - -/* period timer to indicate some form of life */ -// TODO: Gnome 2.14: replace with g_timeout_add_seconds() - g_timeout_add(10 * 1000, (GSourceFunc)on_mark, NULL); - - g_message ("startup complete."); - return FALSE; - -err_abort: - if (NULL != g_sock) { - pgm_close (g_sock, FALSE); - g_sock = NULL; - } - if (NULL != res) { - pgm_freeaddrinfo (res); - res = NULL; - } - if (NULL != pgm_err) { - pgm_error_free (pgm_err); - pgm_err = NULL; - } - g_main_loop_quit (g_loop); - return FALSE; -} - -/* idle log notification - */ - -static -gboolean -on_mark ( - G_GNUC_UNUSED gpointer data - ) -{ - g_message ("-- MARK --"); - return TRUE; -} - -static -gpointer -receiver_thread ( - gpointer data - ) -{ - pgm_sock_t* rx_sock = (pgm_sock_t*)data; - const long iov_len = 20; - const long ev_len = 1; - struct pgm_msgv_t msgv[iov_len]; - -#ifdef CONFIG_HAVE_EPOLL - struct epoll_event events[ev_len]; /* wait for maximum 1 event */ - int timeout; - const int efd = epoll_create (IP_MAX_MEMBERSHIPS); - if (efd < 0) { - g_error ("epoll_create failed errno %i: \"%s\"", errno, strerror(errno)); - g_main_loop_quit (g_loop); - return NULL; - } - - if (pgm_epoll_ctl (rx_sock, efd, EPOLL_CTL_ADD, EPOLLIN) < 0) - { - g_error ("pgm_epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); - g_main_loop_quit (g_loop); - return NULL; - } - struct epoll_event event; - event.events = EPOLLIN; - event.data.fd = g_quit_pipe[0]; - if (epoll_ctl (efd, EPOLL_CTL_ADD, g_quit_pipe[0], &event) < 0) - { - g_error ("epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); - g_main_loop_quit (g_loop); - return NULL; - } -#elif defined(CONFIG_HAVE_POLL) - int timeout; - int n_fds = 2; - struct pollfd fds[ 1 + n_fds ]; -#elif defined(G_OS_UNIX) /* HAVE_SELECT */ - int n_fds; - fd_set readfds; -#else /* G_OS_WIN32 */ - int n_handles = 3; -# if (__STDC_VERSION__ >= 199901L) - HANDLE waitHandles[n_handles]; -# else - HANDLE* waitHandles = (HANDLE*)g_malloc (n_handles * sizeof(HANDLE));; -# endif - DWORD timeout, dwEvents; - WSAEVENT recvEvent, pendingEvent; - - recvEvent = WSACreateEvent (); - WSAEventSelect (pgm_transport_get_recv_fd (g_transport), recvEvent, FD_READ); - pendingEvent = WSACreateEvent (); - WSAEventSelect (pgm_transport_get_pending_fd (g_transport), pendingEvent, FD_READ); - - waitHandles[0] = g_quit_event; - waitHandles[1] = recvEvent; - waitHandles[2] = pendingEvent; -#endif /* !CONFIG_HAVE_EPOLL */ - - do { - struct timeval tv; - size_t len; - pgm_error_t* pgm_err = NULL; - const int status = pgm_recvmsgv (rx_sock, - msgv, - G_N_ELEMENTS(msgv), - 0, - &len, - &pgm_err); - switch (status) { - case PGM_IO_STATUS_NORMAL: - on_msgv (msgv, len); - break; - case PGM_IO_STATUS_TIMER_PENDING: - { - socklen_t optlen = sizeof (tv); - pgm_getsockopt (rx_sock, PGM_TIME_REMAIN, &tv, &optlen); - } - goto block; - case PGM_IO_STATUS_RATE_LIMITED: - { - socklen_t optlen = sizeof (tv); - pgm_getsockopt (rx_sock, PGM_RATE_REMAIN, &tv, &optlen); - } -/* fall through */ - case PGM_IO_STATUS_WOULD_BLOCK: -block: -#ifdef CONFIG_HAVE_EPOLL - timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); - epoll_wait (efd, events, G_N_ELEMENTS(events), timeout /* ms */); -#elif defined(CONFIG_HAVE_POLL) - timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); - memset (fds, 0, sizeof(fds)); - fds[0].fd = g_quit_pipe[0]; - fds[0].events = POLLIN; - pgm_poll_info (rx_sock, &fds[1], &n_fds, POLLIN); - poll (fds, 1 + n_fds, timeout /* ms */); -#elif defined(G_OS_UNIX) /* HAVE_SELECT */ - FD_ZERO(&readfds); - FD_SET(g_quit_pipe[0], &readfds); - n_fds = g_quit_pipe[0] + 1; - pgm_select_info (rx_sock, &readfds, NULL, &n_fds); - select (n_fds, &readfds, NULL, NULL, PGM_IO_STATUS_RATE_LIMITED == status ? &tv : NULL); -#else /* G_OS_WIN32 */ - timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? INFINITE : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); - dwEvents = WaitForMultipleObjects (n_handles, waitHandles, FALSE, timeout); - switch (dwEvents) { - case WAIT_OBJECT_0+1: WSAResetEvent (recvEvent); break; - case WAIT_OBJECT_0+2: WSAResetEvent (pendingEvent); break; - default: break; - } -#endif /* !CONFIG_HAVE_EPOLL */ - break; - - default: - if (pgm_err) { - g_warning ("%s", pgm_err->message); - pgm_error_free (pgm_err); - pgm_err = NULL; - } - if (PGM_IO_STATUS_ERROR == status) - break; - } - } while (!g_quit); - -#ifdef CONFIG_HAVE_EPOLL - close (efd); -#elif defined(G_OS_WIN32) - WSACloseEvent (recvEvent); - WSACloseEvent (pendingEvent); -# if (__STDC_VERSION__ < 199901L) - g_free (waitHandles); -# endif -#endif - return NULL; -} - -static -int -on_msgv ( - struct pgm_msgv_t* msgv, /* an array of msgvs */ - size_t len - ) -{ - g_message ("(%u bytes)", - (unsigned)len); - - guint i = 0; -/* for each apdu */ - do { - const struct pgm_sk_buff_t* pskb = msgv[i].msgv_skb[0]; - gsize apdu_len = 0; - for (unsigned j = 0; j < msgv[i].msgv_len; j++) - apdu_len += msgv[i].msgv_skb[j]->len; -/* truncate to first fragment to make GLib printing happy */ - char buf[2048], tsi[PGM_TSISTRLEN]; - const gsize buflen = MIN(sizeof(buf) - 1, pskb->len); - strncpy (buf, (const char*)pskb->data, buflen); - buf[buflen] = '\0'; - pgm_tsi_print_r (&pskb->tsi, tsi, sizeof(tsi)); - if (msgv[i].msgv_len > 1) - g_message ("\t%u: \"%s\" ... (%" G_GSIZE_FORMAT " bytes from %s)", - i, buf, apdu_len, tsi); - else - g_message ("\t%u: \"%s\" (%" G_GSIZE_FORMAT " bytes from %s)", - i, buf, apdu_len, tsi); - i++; - len -= apdu_len; - } while (len); - - return 0; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/pgmsend.c b/3rdparty/openpgm-svn-r1085/pgm/examples/pgmsend.c deleted file mode 100644 index d8d3539..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/pgmsend.c +++ /dev/null @@ -1,305 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Simple sender using the PGM transport. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef G_OS_UNIX -# include -# include -# include -# include -# include -# include -#else -# include "getopt.h" -#endif -#include - -/* example dependencies */ -#include -#include - - -/* typedefs */ - -/* globals */ - -static int g_port = 0; -static const char* g_network = ""; -static gboolean g_multicast_loop = FALSE; -static int g_udp_encap_port = 0; - -static int g_max_tpdu = 1500; -static int g_max_rte = 400*1000; -static int g_sqns = 100; - -static gboolean g_fec = FALSE; -static int g_k = 8; -static int g_n = 255; - -static pgm_sock_t* g_sock = NULL; - -static gboolean create_pgm_socket (void); - - -G_GNUC_NORETURN static -void -usage (const char* bin) -{ - fprintf (stderr, "Usage: %s [options] message\n", bin); - fprintf (stderr, " -n : Multicast group or unicast IP address\n"); - fprintf (stderr, " -s : IP port\n"); - fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); - fprintf (stderr, " -r : Regulate to rate bytes per second\n"); - fprintf (stderr, " -f : Enable FEC with either proactive or ondemand parity\n"); - fprintf (stderr, " -K : Configure Reed-Solomon code (n, k)\n"); - fprintf (stderr, " -N \n"); - fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); - fprintf (stderr, " -i : List available interfaces\n"); - exit (1); -} - -int -main ( - int argc, - char *argv[] - ) -{ - pgm_error_t* pgm_err = NULL; - - setlocale (LC_ALL, ""); - -/* pre-initialise PGM messages module to add hook for GLib logging */ - pgm_messages_init(); - log_init(); - if (!pgm_init (&pgm_err)) { - g_error ("Unable to start PGM engine: %s", pgm_err->message); - pgm_error_free (pgm_err); - pgm_messages_shutdown(); - return EXIT_FAILURE; - } - -/* parse program arguments */ - const char* binary_name = strrchr (argv[0], '/'); - int c; - while ((c = getopt (argc, argv, "s:n:p:r:f:K:N:lih")) != -1) - { - switch (c) { - case 'n': g_network = optarg; break; - case 's': g_port = atoi (optarg); break; - case 'p': g_udp_encap_port = atoi (optarg); break; - case 'r': g_max_rte = atoi (optarg); break; - - case 'f': g_fec = TRUE; break; - case 'K': g_k = atoi (optarg); break; - case 'N': g_n = atoi (optarg); break; - - case 'l': g_multicast_loop = TRUE; break; - - case 'i': - pgm_if_print_all(); - pgm_messages_shutdown(); - return EXIT_SUCCESS; - - case 'h': - case '?': - pgm_messages_shutdown(); - usage (binary_name); - } - } - - if (g_fec && ( !g_k || !g_n )) { - pgm_messages_shutdown(); - g_error ("Invalid Reed-Solomon parameters RS(%d, %d).", g_n, g_k); - usage (binary_name); - } - -/* setup signal handlers */ - signal (SIGSEGV, on_sigsegv); -#ifdef SIGHUP - signal (SIGHUP, SIG_IGN); -#endif - - if (create_pgm_socket()) - { - while (optind < argc) { - const int status = pgm_send (g_sock, argv[optind], strlen(argv[optind]) + 1, NULL); - if (PGM_IO_STATUS_NORMAL != status) { - g_warning ("pgm_send failed."); - } - optind++; - } - } - -/* cleanup */ - if (g_sock) { - pgm_close (g_sock, TRUE); - g_sock = NULL; - } - pgm_shutdown(); - pgm_messages_shutdown(); - return EXIT_SUCCESS; -} - -static -gboolean -create_pgm_socket (void) -{ - struct pgm_addrinfo_t* res = NULL; - pgm_error_t* pgm_err = NULL; - sa_family_t sa_family = AF_UNSPEC; - -/* parse network parameter into PGM socket address structure */ - if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { - g_error ("Parsing network parameter: %s", pgm_err->message); - goto err_abort; - } - - sa_family = res->ai_send_addrs[0].gsr_group.ss_family; - - if (g_udp_encap_port) { - if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { - g_error ("Creating PGM/UDP socket: %s", pgm_err->message); - goto err_abort; - } - pgm_setsockopt (g_sock, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); - pgm_setsockopt (g_sock, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); - } else { - if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { - g_error ("Creating PGM/IP socket: %s", pgm_err->message); - goto err_abort; - } - } - -/* Use RFC 2113 tagging for PGM Router Assist */ - const int no_router_assist = 0; - pgm_setsockopt (g_sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); - - pgm_drop_superuser(); - -/* set PGM parameters */ - const int send_only = 1, - ambient_spm = pgm_secs (30), - heartbeat_spm[] = { pgm_msecs (100), - pgm_msecs (100), - pgm_msecs (100), - pgm_msecs (100), - pgm_msecs (1300), - pgm_secs (7), - pgm_secs (16), - pgm_secs (25), - pgm_secs (30) }; - - pgm_setsockopt (g_sock, PGM_SEND_ONLY, &send_only, sizeof(send_only)); - pgm_setsockopt (g_sock, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); - pgm_setsockopt (g_sock, PGM_TXW_SQNS, &g_sqns, sizeof(g_sqns)); - pgm_setsockopt (g_sock, PGM_TXW_MAX_RTE, &g_max_rte, sizeof(g_max_rte)); - pgm_setsockopt (g_sock, PGM_AMBIENT_SPM, &ambient_spm, sizeof(ambient_spm)); - pgm_setsockopt (g_sock, PGM_HEARTBEAT_SPM, &heartbeat_spm, sizeof(heartbeat_spm)); - if (g_fec) { - struct pgm_fecinfo_t fecinfo; - fecinfo.block_size = g_n; - fecinfo.proactive_packets = 0; - fecinfo.group_size = g_k; - fecinfo.ondemand_parity_enabled = TRUE; - fecinfo.var_pktlen_enabled = TRUE; - pgm_setsockopt (g_sock, PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); - } - -/* create global session identifier */ - struct pgm_sockaddr_t addr; - memset (&addr, 0, sizeof(addr)); - addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; - addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; - if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { - g_error ("Creating GSI: %s", pgm_err->message); - goto err_abort; - } - -/* assign socket to specified address */ - struct pgm_interface_req_t if_req; - memset (&if_req, 0, sizeof(if_req)); - if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; - if_req.ir_scope_id = 0; - if (AF_INET6 == sa_family) { - struct sockaddr_in6 sa6; - memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); - if_req.ir_scope_id = sa6.sin6_scope_id; - } - if (!pgm_bind3 (g_sock, - &addr, sizeof(addr), - &if_req, sizeof(if_req), /* tx interface */ - &if_req, sizeof(if_req), /* rx interface */ - &pgm_err)) - { - g_error ("Binding PGM socket: %s", pgm_err->message); - goto err_abort; - } - -/* join IP multicast groups */ - for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) - pgm_setsockopt (g_sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); - pgm_setsockopt (g_sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); - pgm_freeaddrinfo (res); - -/* set IP parameters */ - const int blocking = 0, - multicast_loop = g_multicast_loop ? 1 : 0, - multicast_hops = 16, - dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ - - pgm_setsockopt (g_sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); - pgm_setsockopt (g_sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); - if (AF_INET6 != sa_family) - pgm_setsockopt (g_sock, PGM_TOS, &dscp, sizeof(dscp)); - pgm_setsockopt (g_sock, PGM_NOBLOCK, &blocking, sizeof(blocking)); - - if (!pgm_connect (g_sock, &pgm_err)) { - g_error ("Connecting PGM socket: %s", pgm_err->message); - goto err_abort; - } - - return TRUE; - -err_abort: - if (NULL != g_sock) { - pgm_close (g_sock, FALSE); - g_sock = NULL; - } - if (NULL != res) { - pgm_freeaddrinfo (res); - res = NULL; - } - if (NULL != pgm_err) { - pgm_error_free (pgm_err); - pgm_err = NULL; - } - return FALSE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/pgmtop.c b/3rdparty/openpgm-svn-r1085/pgm/examples/pgmtop.c deleted file mode 100644 index 9b18310..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/pgmtop.c +++ /dev/null @@ -1,1031 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * PGM packet monitor. - * - * Copyright (c) 2006-2007 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -/* PGM internals */ -#include - -/* example dependencies */ -#include -#include - - -struct ncurses_window; - -typedef void (*paint_func)(struct ncurses_window*); -typedef void (*resize_func)(struct ncurses_window*, int, int); - -struct ncurses_window { - WINDOW* window; - PANEL* panel; - char* title; - paint_func paint; - resize_func resize; -}; - -struct pgm_stat { - gulong count, snap_count; - gulong bytes, snap_bytes; - gulong tsdu; - - gulong duplicate; - gulong invalid; - - struct timeval last; - struct timeval last_valid; - struct timeval last_invalid; -}; - -struct pgm_netstat { - struct in_addr addr; - gulong corrupt; -}; - -struct pgm_hoststat { - pgm_tsi_t tsi; - - struct in_addr last_addr; - struct in_addr nla; - - gulong txw_secs; - gulong txw_trail; - gulong txw_lead; - gulong txw_sqns; - - gulong rxw_trail; - gulong rxw_lead; - - gulong rxw_trail_init; - gboolean window_defined; - gboolean rxw_constrained; - - gulong spm_sqn; - - struct pgm_stat spm, - poll, - polr, - odata, - rdata, - nak, - nnak, - ncf, - spmr, - - general; - - struct timeval session_start; -}; - - -/* globals */ - -static int g_port = 7500; -static const char* g_network = "239.192.0.1"; -static struct in_addr g_filter = { 0 }; - -static GIOChannel* g_io_channel = NULL; -static GIOChannel* g_stdin_channel = NULL; - -static GMainLoop* g_loop = NULL; - -static guint g_status_height = 6; -static guint g_info_width = 10; -static time_t start_time; - -static struct ncurses_window *g_peer, *g_info, *g_status, *g_active; -static GList* g_window_list = NULL; -static guint g_paint_interval = ( 1 * 1000 ) / 15; -static guint g_snap_interval = 10 * 1000; -static struct timeval g_last_snap, g_now; - -static GList* g_status_list = NULL; - -static guint32 g_packets = 0; -static GHashTable *g_hosts = NULL; -static GHashTable *g_nets = NULL; - -static void init_ncurses (void); -static void paint_ncurses (void); -static void resize_ncurses (int, int); - -static void paint_peer (struct ncurses_window*); -static gboolean tsi_row (gpointer, gpointer, gpointer); - -static void paint_info (struct ncurses_window*); -static void paint_status (struct ncurses_window*); -static void resize_peer (struct ncurses_window*, int, int); -static void resize_info (struct ncurses_window*, int, int); -static void resize_status (struct ncurses_window*, int, int); - -static void write_status (const gchar*, ...) G_GNUC_PRINTF (1, 2); -static void write_statusv (const gchar*, va_list); - -static void on_signal (int, gpointer); -static void on_winch (int); -static gboolean on_startup (gpointer); -static gboolean on_snap (gpointer); -static gboolean on_paint (gpointer); - -static gboolean on_io_data (GIOChannel*, GIOCondition, gpointer); -static gboolean on_io_error (GIOChannel*, GIOCondition, gpointer); - -static gboolean on_stdin_data (GIOChannel*, GIOCondition, gpointer); - -int -main ( - G_GNUC_UNUSED int argc, - G_GNUC_UNUSED char *argv[] - ) -{ - GError* err = NULL; - pgm_error_t* pgm_err = NULL; - - setlocale (LC_ALL, ""); - - log_init (); - g_message ("pgmtop"); - - if (!pgm_init (&pgm_err)) { - g_error ("Unable to start PGM engine: %s", pgm_err->message); - pgm_error_free (pgm_err); - return EXIT_FAILURE; - } - - g_loop = g_main_loop_new (NULL, FALSE); - -/* setup signal handlers */ - signal (SIGSEGV, on_sigsegv); - signal (SIGHUP, SIG_IGN); - pgm_signal_install (SIGINT, on_signal, g_loop); - pgm_signal_install (SIGTERM, on_signal, g_loop); - -/* delayed startup */ - g_message ("scheduling startup.n"); - g_timeout_add(0, (GSourceFunc)on_startup, g_loop); - -/* dispatch loop */ - g_message ("entering main event loop ..."); - g_main_loop_run (g_loop); - - endwin(); - g_message ("event loop terminated, cleaning up."); - -/* cleanup */ - g_main_loop_unref (g_loop); - g_loop = NULL; - if (g_io_channel) { - g_message ("closing socket."); - g_io_channel_shutdown (g_io_channel, FALSE, &err); - g_io_channel = NULL; - } - - if (g_stdin_channel) { - g_message ("unbinding stdin."); - g_io_channel_unref (g_stdin_channel); - g_stdin_channel = NULL; - } - - g_message ("PGM engine shutdown."); - pgm_shutdown (); - g_message ("finished."); - return EXIT_SUCCESS; -} - -static struct ncurses_window* -create_window ( - char* name, - paint_func paint, - resize_func resize - ) -{ - struct ncurses_window* nw = g_malloc0 (sizeof(struct ncurses_window)); - nw->window = newwin (0, 0, 0, 0); - nw->panel = new_panel (nw->window); - nw->title = name; - - nw->paint = paint; - nw->resize = resize; - - g_window_list = g_list_append (g_window_list, nw); - return nw; -} - -/* +-Peer list --------------++-Info-+ - * | || | - * | < peer window > || < info window > - * | || | - * +-------------------------++------+ - * +-Status--------------------------+ - * | < status window > | - * +---------------------------------+ - */ - -static void -init_ncurses (void) -{ -/* setup ncurses terminal display */ - initscr(); /* init ncurses library */ - -// signal_install (SIGWINCH, on_winch); - - noecho(); /* hide entered keys */ - cbreak(); - -/* setup ncurses windows */ - g_peer = create_window ("Peers", paint_peer, resize_peer); - - g_info = create_window ("Info", paint_info, resize_info); - start_time = time (0); - - g_status = create_window ("Status", paint_status, resize_status); - scrollok (g_status->window, 1); - - g_active = g_peer; - top_panel (g_active->panel); - - paint_ncurses(); -} - -static void -resize_ncurses ( - int hsize, - int vsize - ) -{ - GList* nw_list = g_window_list; - while (nw_list) - { - struct ncurses_window* nw = (struct ncurses_window*)nw_list->data; - - nw->resize (nw, hsize, vsize); - - nw_list = nw_list->next; - } -} - -static void -paint_ncurses (void) -{ - static int hsize = 0, vsize = 0; - - if (hsize != COLS || vsize != LINES) - { - hsize = COLS; vsize = LINES; - resize_ncurses(hsize, vsize); - } - - GList* nw_list = g_window_list; - while (nw_list) - { - struct ncurses_window* nw = (struct ncurses_window*)nw_list->data; - werase (nw->window); - - box (nw->window, ACS_VLINE, ACS_HLINE); - mvwaddstr (nw->window, 0, 2, nw->title); - - nw->paint (nw); - - nw_list = nw_list->next; - } - -/* have cursor stay at top left of active window */ - wmove (g_active->window, 0, 0); - - update_panels(); /* update virtual screen */ - doupdate(); /* update real screen */ -} - -/* peer window */ - -static void -paint_peer ( - struct ncurses_window* nw - ) -{ - -/* 1 2 3 4 5 6 7 8 - * 012345678901234567890123456789012345678901234567890123456789012345678901234567890 - * TSI Packets Bytes Packet/s Bit/s Data Inv Dupe - * 100.200.300.400.500.600.70000 1,000K 1,000MB 1,000 1,000 100% 100% 100% - */ - mvwaddstr (nw->window, 1, 1, "TSI"); - mvwaddstr (nw->window, 1, 32, "Packets"); - mvwaddstr (nw->window, 1, 40, "Bytes"); - mvwaddstr (nw->window, 1, 48, "Packet/s"); - mvwaddstr (nw->window, 1, 58, "Bit/s"); - mvwaddstr (nw->window, 1, 68, "Data"); - mvwaddstr (nw->window, 1, 73, "Inv"); - mvwaddstr (nw->window, 1, 78, "Dupe"); - - if (g_hosts) - { - int row = 2; - gettimeofday(&g_now, NULL); - g_hash_table_foreach (g_hosts, (GHFunc)tsi_row, &row); - } -} - -static char* -print_si ( - float* v - ) -{ - static char prefix[5] = ""; - - if (*v > 100 * 1000 * 1000) { - strcpy (prefix, "G"); - *v /= 1000.0 * 1000.0 * 1000.0; - } else if (*v > 100 * 1000) { - strcpy (prefix, "M"); - *v /= 1000.0 * 1000.0; - } else if (*v > 100) { - strcpy (prefix, "K"); - *v /= 1000.0; - } else { - *prefix = 0; - } - - return prefix; -} - -static gboolean -tsi_row ( - G_GNUC_UNUSED gpointer key, - gpointer value, - gpointer user_data - ) -{ - struct pgm_hoststat* hoststat = value; - int* row = user_data; - - float secs = (g_now.tv_sec - g_last_snap.tv_sec) + - ( (g_now.tv_usec - g_last_snap.tv_usec) / 1000.0 / 1000.0 ); - -/* TSI */ - char* tsi_string = pgm_tsi_print (&hoststat->tsi); - mvwaddstr (g_peer->window, *row, 1, tsi_string); - -/* Packets */ - char buffer[100]; - float v = hoststat->general.count; - char* prefix = print_si (&v); - snprintf (buffer, sizeof(buffer), "%lu%s", (gulong)v, prefix); - mvwaddstr (g_peer->window, *row, 32, buffer); - -/* Bytes */ - v = hoststat->general.bytes; - prefix = print_si (&v); - snprintf (buffer, sizeof(buffer), "%lu%s", (gulong)v, prefix); - mvwaddstr (g_peer->window, *row, 40, buffer); - -/* Packet/s */ - v = ( hoststat->general.count - hoststat->general.snap_count ) / secs; - prefix = print_si (&v); - snprintf (buffer, sizeof(buffer), "%.1f%s", v, prefix); - mvwaddstr (g_peer->window, *row, 48, buffer); - -/* Bit/s */ - float bitrate = ((float)( hoststat->general.bytes - hoststat->general.snap_bytes ) * 8.0 / secs); - char* bitprefix = print_si (&bitrate); - snprintf (buffer, sizeof(buffer), "%.1f%s", bitrate, bitprefix); - mvwaddstr (g_peer->window, *row, 58, buffer); - -/* % Data */ - snprintf (buffer, sizeof(buffer), "%d%%", (int)((100.0 * hoststat->odata.tsdu) / hoststat->general.bytes)); - mvwaddstr (g_peer->window, *row, 68, buffer); - -/* % Invalid */ - snprintf (buffer, sizeof(buffer), "%d%%", (int)(hoststat->general.invalid ? (100.0 * hoststat->general.invalid) / hoststat->general.count : 0.0)); - mvwaddstr (g_peer->window, *row, 73, buffer); - -/* % Duplicate */ - snprintf (buffer, sizeof(buffer), "%d%%", (int)(hoststat->general.duplicate ? (100.0 * hoststat->general.duplicate) / hoststat->general.count : 0.0)); - mvwaddstr (g_peer->window, *row, 78, buffer); - - *row = *row + 1; - - return FALSE; -} - -static void -resize_peer ( - struct ncurses_window* nw, - int hsize, /* COLS */ - int vsize /* LINES */ - ) -{ - wresize (nw->window, vsize - g_status_height, hsize - g_info_width); - replace_panel (nw->panel, nw->window); - move_panel (nw->panel, 0, 0); -} - -/* info window */ - -static void -paint_info ( - struct ncurses_window* nw - ) -{ - char buffer[20]; - - mvwaddstr (nw->window, 1, 2, "Peers"); - snprintf (buffer, sizeof(buffer), "%d", g_hosts ? g_hash_table_size (g_hosts) : 0); - mvwaddstr (nw->window, 2, 2, buffer); - - mvwaddstr (nw->window, 3, 2, "Packets"); - snprintf (buffer, sizeof(buffer), "%d", g_packets); - mvwaddstr (nw->window, 4, 2, buffer); - - mvwaddstr (nw->window, LINES - g_status_height - 2, 2, "Elapsed"); - time_t elapsed = time(0) - start_time; - snprintf (buffer, sizeof(buffer), "%02d:%02d:%02d", - (int) (elapsed / 60) / 60, (int) (elapsed / 60) % 60, - (int) elapsed % 60); - mvwaddstr (nw->window, LINES - g_status_height - 1, 1, buffer); -} - -static void -resize_info ( - struct ncurses_window* nw, - int hsize, /* COLS */ - int vsize /* LINES */ - ) -{ - wresize (nw->window, vsize - g_status_height, g_info_width); - replace_panel (nw->panel, nw->window); - move_panel (nw->panel, 0, hsize - g_info_width); -} - -/* status window */ - -static void -paint_status ( - G_GNUC_UNUSED struct ncurses_window* nw - ) -{ - if (!g_status_list) return; - - guint len = g_list_length (g_status_list); - while (len > g_status_height) { - g_free (g_status_list->data); - g_status_list = g_list_delete_link (g_status_list, g_status_list); - len--; - } - guint y = 1; - GList* list = g_status_list; - while (list) { - mvwaddstr (g_status->window, y++, 3, (char*)list->data); - list = list->next; - } -} - -static void -resize_status ( - struct ncurses_window* nw, - int hsize, /* COLS */ - int vsize /* LINES */ - ) -{ - wresize (nw->window, g_status_height, hsize); - replace_panel (nw->panel, nw->window); - move_panel (nw->panel, vsize - g_status_height, 0); -} - -static void -write_status ( - const gchar* format, - ... - ) -{ - va_list args; - - va_start (args, format); - write_statusv (format, args); - va_end (args); -} - -static void -write_statusv ( - const gchar* format, - va_list args1 - ) -{ - char buffer[1024]; - vsnprintf (buffer, sizeof(buffer), format, args1); - - g_status_list = g_list_append (g_status_list, g_memdup (buffer, strlen(buffer)+1)); -} - -static -void -on_signal ( - int signum, - gpointer user_data - ) -{ - GMainLoop* loop = (GMainLoop*)user_data; - puts ("on_signal"); - g_main_loop_quit (loop); -} - -/* terminal resize signal - */ - -static void -on_winch ( - G_GNUC_UNUSED int signum - ) -{ - paint_ncurses (); -} - -static gboolean -on_startup ( - gpointer user_data - ) -{ - GMainLoop* loop = (GMainLoop*)user_data; - int e; - - puts ("startup."); - -/* find PGM protocol id */ -// TODO: fix valgrind errors - int ipproto_pgm = IPPROTO_PGM; -#if HAVE_GETPROTOBYNAME_R - char b[1024]; - struct protoent protobuf, *proto; - e = getprotobyname_r ("pgm", &protobuf, b, sizeof(b), &proto); - if (e != -1 && proto != NULL) { - if (proto->p_proto != ipproto_pgm) { - print f("Setting PGM protocol number to %i from /etc/protocols.\n", proto->p_proto); - ipproto_pgm = proto->p_proto; - } - } -#else - struct protoent *proto = getprotobyname ("pgm"); - if (proto != NULL) { - if (proto->p_proto != ipproto_pgm) { - printf("Setting PGM protocol number to %i from /etc/protocols.\n", proto->p_proto); - ipproto_pgm = proto->p_proto; - } - } -#endif - -/* open socket for snooping */ - puts ("opening raw socket."); - int sock = socket (PF_INET, SOCK_RAW, ipproto_pgm); - if (sock < 0) { - int _e = errno; - puts ("on_startup() failed"); - - if (_e == EPERM && 0 != getuid()) { - puts ("PGM protocol requires this program to run as superuser."); - } - g_main_loop_quit (loop); - return FALSE; - } - -/* drop out of setuid 0 */ - if (0 == getuid ()) { - puts ("dropping superuser privileges."); - setuid ((gid_t)65534); - setgid ((uid_t)65534); - } - - char _t = 1; - e = setsockopt (sock, IPPROTO_IP, IP_HDRINCL, &_t, sizeof(_t)); - if (e < 0) { - printw ("on_startup() failed\n"); - close (sock); - g_main_loop_quit (loop); - return FALSE; - } - -/* buffers */ - int buffer_size = 0; - socklen_t len = 0; - e = getsockopt (sock, SOL_SOCKET, SO_RCVBUF, &buffer_size, &len); - if (e == 0) { - printf ("receive buffer set at %i bytes.\n", buffer_size); - } - e = getsockopt (sock, SOL_SOCKET, SO_SNDBUF, &buffer_size, &len); - if (e == 0) { - printf ("send buffer set at %i bytes.\n", buffer_size); - } - -/* bind */ - struct sockaddr_in addr; - memset (&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_ANY); - - e = bind (sock, (struct sockaddr*)&addr, sizeof(addr)); - if (e < 0) { - printw ("on_startup() failed\n"); - close (sock); - g_main_loop_quit (loop); - return FALSE; - } - -/* multicast */ - struct ip_mreq mreq; - memset (&mreq, 0, sizeof(mreq)); - mreq.imr_interface.s_addr = htonl(INADDR_ANY); - printf ("listening on interface %s.\n", inet_ntoa(mreq.imr_interface)); - mreq.imr_multiaddr.s_addr = inet_addr(g_network); - printf ("subscription on multicast address %s.\n", inet_ntoa(mreq.imr_multiaddr)); - e = setsockopt (sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); - if (e < 0) { - printw ("on_startup() failed\n"); - close (sock); - g_main_loop_quit (loop); - return FALSE; - } - -/* multicast loopback */ -/* multicast ttl */ - -/* add socket to event manager */ - g_io_channel = g_io_channel_unix_new (sock); - printf ("socket opened with encoding %s.\n", g_io_channel_get_encoding(g_io_channel)); - - /* guint event = */ g_io_add_watch (g_io_channel, G_IO_IN | G_IO_PRI, on_io_data, NULL); - /* guint event = */ g_io_add_watch (g_io_channel, G_IO_ERR | G_IO_HUP | G_IO_NVAL, on_io_error, NULL); - -/* add stdin to event manager */ - g_stdin_channel = g_io_channel_unix_new (fileno(stdin)); - printf ("binding stdin with encoding %s.\n", g_io_channel_get_encoding(g_stdin_channel)); - - g_io_add_watch (g_stdin_channel, G_IO_IN | G_IO_PRI, on_stdin_data, NULL); - -/* periodic timer to snapshot statistics */ - g_timeout_add (g_snap_interval, (GSourceFunc)on_snap, NULL); - -/* period timer to update screen */ - g_timeout_add (g_paint_interval, (GSourceFunc)on_paint, NULL); - - puts ("READY"); - - init_ncurses(); - return FALSE; -} - -static gboolean -on_paint ( - G_GNUC_UNUSED gpointer data - ) -{ - paint_ncurses(); - - return TRUE; -} - -static guint -tsi_hash ( - gconstpointer v - ) -{ - return g_str_hash(pgm_tsi_print(v)); -} - -static gint -tsi_equal ( - gconstpointer v, - gconstpointer v2 - ) -{ - return memcmp (v, v2, (6 * sizeof(guint8)) + sizeof(guint16)) == 0; -} - -static gboolean -on_io_data ( - GIOChannel* source, - G_GNUC_UNUSED GIOCondition condition, - G_GNUC_UNUSED gpointer user_data - ) -{ - struct timeval now; - struct pgm_sk_buff_t* skb = pgm_alloc_skb (4096); - struct sockaddr_storage src, dst; - struct sockaddr_in* sin = (struct sockaddr_in*)&src; - socklen_t src_addr_len = sizeof(src); - int fd = g_io_channel_unix_get_fd(source); - - skb->len = recvfrom(fd, skb->head, 4096, MSG_DONTWAIT, (struct sockaddr*)&src, &src_addr_len); - - gettimeofday (&now, NULL); - g_packets++; - - GError* err = NULL; - gboolean is_valid = pgm_parse_raw (skb, (struct sockaddr*)&dst, &err); - if (!is_valid && err && PGM_PACKET_ERROR_CKSUM == err->code) - { -/* corrupt packet */ - if (!g_nets) { - g_nets = g_hash_table_new (g_int_hash, g_int_equal); - } - - struct pgm_netstat* netstat = g_hash_table_lookup (g_nets, &sin->sin_addr); - if (netstat == NULL) { - write_status ("new host publishing corrupt data, local nla %s", inet_ntoa(sin->sin_addr)); - netstat = g_malloc0(sizeof(struct pgm_netstat)); - netstat->addr = sin->sin_addr; - g_hash_table_insert (g_nets, (gpointer)&netstat->addr, (gpointer)netstat); - } - - netstat->corrupt++; - pgm_free_skb (skb); - return TRUE; - } - else if (!is_valid) - { -/* general error */ - pgm_free_skb (skb); - return TRUE; - } - -/* search for existing session */ - if (!g_hosts) { - g_hosts = g_hash_table_new (tsi_hash, tsi_equal); - } - - struct pgm_hoststat* hoststat = g_hash_table_lookup (g_hosts, &skb->tsi); - if (hoststat == NULL) { - write_status ("new tsi %s with local nla %s", pgm_tsi_print (&skb->tsi), inet_ntoa(sin->sin_addr)); - - hoststat = g_malloc0(sizeof(struct pgm_hoststat)); - memcpy (&hoststat->tsi, &skb->tsi, sizeof(pgm_tsi_t)); - hoststat->session_start = now; - - g_hash_table_insert (g_hosts, (gpointer)&hoststat->tsi, (gpointer)hoststat); - } - -/* increment statistics */ - memcpy (&hoststat->last_addr, &sin->sin_addr, sizeof(sin->sin_addr)); - hoststat->general.count++; - hoststat->general.bytes += skb->len; - hoststat->general.last = now; - - skb->data = (guint8*)skb->data + sizeof(struct pgm_header); - skb->len -= sizeof(struct pgm_header); - -/* repurpose is_valid for PGM subtype */ - is_valid = FALSE; - switch (skb->pgm_header->pgm_type) { - case PGM_SPM: - hoststat->spm.count++; - hoststat->spm.bytes += skb->len; - hoststat->spm.last = now; - - is_valid = pgm_verify_spm (skb); - if (!is_valid) { - hoststat->spm.invalid++; - hoststat->spm.last_invalid = now; - } else { - const struct pgm_spm* spm = (struct pgm_spm*)skb->data; - - hoststat->nla.s_addr = spm->spm_nla.s_addr; - if (pgm_uint32_lte (g_ntohl( spm->spm_sqn ), hoststat->spm_sqn)) { - hoststat->general.duplicate++; - break; - } - hoststat->spm_sqn = g_ntohl( spm->spm_sqn ); - hoststat->txw_trail = g_ntohl( spm->spm_trail ); - hoststat->txw_lead = g_ntohl( spm->spm_lead ); - hoststat->rxw_trail = hoststat->txw_trail; - hoststat->window_defined = TRUE; - } - break; - - case PGM_ODATA: - hoststat->odata.count++; - hoststat->odata.bytes += skb->len; - hoststat->odata.last = now; - - const struct pgm_data* data = (struct pgm_data*)skb->data; - - if (!hoststat->window_defined) { - hoststat->rxw_lead = g_ntohl (data->data_sqn) - 1; - hoststat->rxw_trail = hoststat->rxw_trail_init = hoststat->rxw_lead + 1; - hoststat->rxw_constrained = TRUE; - hoststat->window_defined = TRUE; - } else { - if (! pgm_uint32_gte( g_ntohl (data->data_sqn) , hoststat->rxw_trail ) ) - { - hoststat->odata.invalid++; - hoststat->odata.last_invalid = now; - break; - } - hoststat->rxw_trail = g_ntohl (data->data_trail); - } - - if (hoststat->rxw_constrained && hoststat->txw_trail > hoststat->rxw_trail_init) { - hoststat->rxw_constrained = FALSE; - } - - if ( pgm_uint32_lte ( g_ntohl (data->data_sqn), hoststat->rxw_lead ) ) { - hoststat->general.duplicate++; - break; - } else { - hoststat->rxw_lead = g_ntohl (data->data_sqn); - - hoststat->odata.tsdu += g_ntohs (skb->pgm_header->pgm_tsdu_length); - } - break; - - case PGM_RDATA: - hoststat->rdata.count++; - hoststat->rdata.bytes += skb->len; - hoststat->rdata.last = now; - break; - - case PGM_POLL: - hoststat->poll.count++; - hoststat->poll.bytes += skb->len; - hoststat->poll.last = now; - break; - - case PGM_POLR: - hoststat->polr.count++; - hoststat->polr.bytes += skb->len; - hoststat->polr.last = now; - break; - - case PGM_NAK: - hoststat->nak.count++; - hoststat->nak.bytes += skb->len; - hoststat->nak.last = now; - - is_valid = pgm_verify_nak (skb); - if (!is_valid) { - hoststat->nak.invalid++; - hoststat->nak.last_invalid = now; - } - break; - - case PGM_NNAK: - hoststat->nnak.count++; - hoststat->nnak.bytes += skb->len; - hoststat->nnak.last = now; - break; - - case PGM_NCF: - hoststat->ncf.count++; - hoststat->ncf.bytes += skb->len; - hoststat->ncf.last = now; - break; - - case PGM_SPMR: - hoststat->spmr.count++; - hoststat->spmr.bytes += skb->len; - hoststat->spmr.last = now; - - is_valid = pgm_verify_spmr (skb); - if (!is_valid) { - hoststat->spmr.invalid++; - hoststat->spmr.last_invalid = now; - } - break; - - default: - break; - } - - if (!is_valid) { - hoststat->general.invalid++; - hoststat->general.last_invalid = now; - } else { - hoststat->general.last_valid = now; - } - - pgm_free_skb (skb); - return TRUE; -} - -static gboolean -on_io_error ( - GIOChannel* source, - G_GNUC_UNUSED GIOCondition condition, - G_GNUC_UNUSED gpointer data - ) -{ - puts ("on_error."); - - GError *err; - g_io_channel_shutdown (source, FALSE, &err); - -/* remove event */ - return FALSE; -} - -/* process input commands from stdin/fd - */ - -static gboolean -on_stdin_data ( - G_GNUC_UNUSED GIOChannel* source, - G_GNUC_UNUSED GIOCondition condition, - G_GNUC_UNUSED gpointer data - ) -{ - int ch = wgetch (g_active->window); - if (ch == ERR) { - goto out; - } - -/* force redraw */ - if (ch == 12) { - clearok (curscr, TRUE); - paint_ncurses (); - goto out; - } - - if (ch == 'q') { - g_main_loop_quit(g_loop); - } - -out: - return TRUE; -} - -static gboolean -snap_stat ( - G_GNUC_UNUSED gpointer key, - gpointer value, - G_GNUC_UNUSED gpointer user_data - ) -{ - struct pgm_hoststat* hoststat = value; - -#define SNAP_STAT(name) \ - { \ - hoststat->name.snap_count = hoststat->name.count; \ - hoststat->name.snap_bytes = hoststat->name.bytes; \ - } - - SNAP_STAT(spm); - SNAP_STAT(poll); - SNAP_STAT(polr); - SNAP_STAT(odata); - SNAP_STAT(rdata); - SNAP_STAT(nak); - SNAP_STAT(nnak); - SNAP_STAT(ncf); - SNAP_STAT(spmr); - - SNAP_STAT(general); - - return FALSE; -} - -static gboolean -on_snap ( - gpointer data - ) -{ - if (!g_hosts) return TRUE; - - gettimeofday (&g_last_snap, NULL); - g_hash_table_foreach (g_hosts, (GHFunc)snap_stat, NULL); - - return TRUE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/ping.proto b/3rdparty/openpgm-svn-r1085/pgm/examples/ping.proto deleted file mode 100644 index 8c6dfd1..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/ping.proto +++ /dev/null @@ -1,47 +0,0 @@ -package example; - -message SubscriptionHeader { - required string subject = 1; -} - -message MarketDataHeader { - enum MsgType { - MSG_VERIFY = 0; - MSG_UPDATE = 1; - MSG_CORRECT = 2; - MSG_CLOSING = 3; - MSG_DROP = 4; - MSG_AGGREGATE = 5; - MSG_STATUS = 6; - MSG_CANCEL = 7; - MSG_INITIAL = 8; - } - required MsgType msg_type = 1; - enum RecType { - PING = 1; - } - required RecType rec_type = 2; - enum RecStatus { - STATUS_OK = 0; - STATUS_BAD_NAME = 1; - STATUS_BAD_LINE = 2; - STATUS_CACHE_FULL = 3; - STATUS_PERMISSION_DENIED = 4; - STATUS_PREEMPTED = 5; - STATUS_BAD_ACCESS = 6; - STATUS_TEMP_UNAVAIL = 7; - STATUS_REASSIGN = 8; - STATUS_NOSUBSCRIBERS = 9; - STATUS_EXPIRED = 10; - } - required RecStatus rec_status = 3; -} - -message Ping { - required SubscriptionHeader subscription_header = 1; - required MarketDataHeader market_data_header = 2; - required fixed64 time = 3; - required fixed64 seqno = 4; - required fixed64 latency = 5; - required bytes payload = 6; -} diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/pnonblocksyncrecv.c b/3rdparty/openpgm-svn-r1085/pgm/examples/pnonblocksyncrecv.c deleted file mode 100644 index f66454f..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/pnonblocksyncrecv.c +++ /dev/null @@ -1,385 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Simple PGM receiver: poll based non-blocking synchronous receiver. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef G_OS_UNIX -# include -# include -# include -# include -#endif -#include - -/* example dependencies */ -#include -#include - - -/* typedefs */ - -/* globals */ - -static int g_port = 0; -static const char* g_network = ""; -static gboolean g_multicast_loop = FALSE; -static int g_udp_encap_port = 0; - -static int g_max_tpdu = 1500; -static int g_sqns = 100; - -static pgm_sock_t* g_sock = NULL; -static gboolean g_quit; -static int g_quit_pipe[2]; - -static void on_signal (int); -static gboolean on_startup (void); - -static int on_data (gconstpointer, size_t, struct pgm_sockaddr_t*); - - -G_GNUC_NORETURN static -void -usage ( - const char* bin - ) -{ - fprintf (stderr, "Usage: %s [options]\n", bin); - fprintf (stderr, " -n : Multicast group or unicast IP address\n"); - fprintf (stderr, " -s : IP port\n"); - fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); - fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); - exit (1); -} - -int -main ( - int argc, - char* argv[] - ) -{ - int e; - pgm_error_t* pgm_err = NULL; - - setlocale (LC_ALL, ""); - - log_init (); - g_message ("pnonblocksyncrecv"); - - if (!pgm_init (&pgm_err)) { - g_error ("Unable to start PGM engine: %s", pgm_err->message); - pgm_error_free (pgm_err); - return EXIT_FAILURE; - } - -/* parse program arguments */ - const char* binary_name = strrchr (argv[0], '/'); - int c; - while ((c = getopt (argc, argv, "s:n:p:lh")) != -1) - { - switch (c) { - case 'n': g_network = optarg; break; - case 's': g_port = atoi (optarg); break; - case 'p': g_udp_encap_port = atoi (optarg); break; - case 'l': g_multicast_loop = TRUE; break; - - case 'h': - case '?': usage (binary_name); - } - } - - g_quit = FALSE; -#ifdef G_OS_UNIX - e = pipe (g_quit_pipe); -#else - e = _pipe (g_quit_pipe, 4096, _O_BINARY | _O_NOINHERIT); -#endif - g_assert (0 == e); - -/* setup signal handlers */ - signal (SIGSEGV, on_sigsegv); - signal (SIGINT, on_signal); - signal (SIGTERM, on_signal); -#ifdef SIGHUP - signal (SIGHUP, SIG_IGN); -#endif - - if (!on_startup()) { - g_error ("startup failed"); - exit(1); - } - -/* dispatch loop */ - g_message ("entering PGM message loop ... "); - do { - struct timeval tv; - int timeout; - int n_fds = 2; - struct pollfd fds[ 1 + n_fds ]; - char buffer[4096]; - size_t len; - struct pgm_sockaddr_t from; - socklen_t fromlen = sizeof(from); - const int status = pgm_recvfrom (g_sock, - buffer, - sizeof(buffer), - 0, - &len, - &from, - &fromlen, - &pgm_err); - switch (status) { - case PGM_IO_STATUS_NORMAL: - on_data (buffer, len, &from); - break; - case PGM_IO_STATUS_TIMER_PENDING: - { - socklen_t optlen = sizeof (tv); - pgm_getsockopt (g_sock, PGM_TIME_REMAIN, &tv, &optlen); - } - goto block; - case PGM_IO_STATUS_RATE_LIMITED: - { - socklen_t optlen = sizeof (tv); - pgm_getsockopt (g_sock, PGM_RATE_REMAIN, &tv, &optlen); - } -/* fall through */ - case PGM_IO_STATUS_WOULD_BLOCK: -/* poll for next event */ -block: - timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); - memset (fds, 0, sizeof(fds)); - fds[0].fd = g_quit_pipe[0]; - fds[0].events = POLLIN; - pgm_poll_info (g_sock, &fds[1], &n_fds, POLLIN); - poll (fds, 1 + n_fds, timeout /* ms */); - break; - default: - if (pgm_err) { - g_warning ("%s", pgm_err->message); - pgm_error_free (pgm_err); - pgm_err = NULL; - } - if (PGM_IO_STATUS_ERROR == status) - break; - } - } while (!g_quit); - - g_message ("message loop terminated, cleaning up."); - -/* cleanup */ - close (g_quit_pipe[0]); - close (g_quit_pipe[1]); - - if (g_sock) { - g_message ("closing PGM socket."); - pgm_close (g_sock, TRUE); - g_sock = NULL; - } - - g_message ("PGM engine shutdown."); - pgm_shutdown (); - g_message ("finished."); - return EXIT_SUCCESS; -} - -static -void -on_signal ( - int signum - ) -{ - g_message ("on_signal (signum:%d)", signum); - g_quit = TRUE; - const char one = '1'; - const size_t writelen = write (g_quit_pipe[1], &one, sizeof(one)); - g_assert (sizeof(one) == writelen); -} - -static -gboolean -on_startup (void) -{ - struct pgm_addrinfo_t* res = NULL; - pgm_error_t* pgm_err = NULL; - sa_family_t sa_family = AF_UNSPEC; - - g_message ("startup."); - -/* parse network parameter into transport address structure */ - if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { - g_error ("parsing network parameter: %s", pgm_err->message); - goto err_abort; - } - - sa_family = res->ai_send_addrs[0].gsr_group.ss_family; - - if (g_udp_encap_port) { - g_message ("create PGM/UDP socket."); - if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { - g_error ("socket: %s", pgm_err->message); - goto err_abort; - } - pgm_setsockopt (g_sock, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); - pgm_setsockopt (g_sock, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); - } else { - g_message ("create PGM/IP socket."); - if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { - g_error ("socket: %s", pgm_err->message); - goto err_abort; - } - } - -/* Use RFC 2113 tagging for PGM Router Assist */ - const int no_router_assist = 0; - pgm_setsockopt (g_sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); - - pgm_drop_superuser(); - -/* set PGM parameters */ - const int recv_only = 1, - passive = 0, - peer_expiry = pgm_secs (300), - spmr_expiry = pgm_msecs (250), - nak_bo_ivl = pgm_msecs (50), - nak_rpt_ivl = pgm_secs (2), - nak_rdata_ivl = pgm_secs (2), - nak_data_retries = 50, - nak_ncf_retries = 50; - - pgm_setsockopt (g_sock, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); - pgm_setsockopt (g_sock, PGM_PASSIVE, &passive, sizeof(passive)); - pgm_setsockopt (g_sock, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); - pgm_setsockopt (g_sock, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); - pgm_setsockopt (g_sock, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); - pgm_setsockopt (g_sock, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); - pgm_setsockopt (g_sock, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); - pgm_setsockopt (g_sock, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); - -/* create global session identifier */ - struct pgm_sockaddr_t addr; - memset (&addr, 0, sizeof(addr)); - addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; - addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; - if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { - g_error ("creating GSI: %s", pgm_err->message); - goto err_abort; - } - -/* assign socket to specified address */ - struct pgm_interface_req_t if_req; - memset (&if_req, 0, sizeof(if_req)); - if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; - if_req.ir_scope_id = 0; - if (AF_INET6 == sa_family) { - struct sockaddr_in6 sa6; - memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); - if_req.ir_scope_id = sa6.sin6_scope_id; - } - if (!pgm_bind3 (g_sock, - &addr, sizeof(addr), - &if_req, sizeof(if_req), /* tx interface */ - &if_req, sizeof(if_req), /* rx interface */ - &pgm_err)) - { - g_error ("binding PGM socket: %s", pgm_err->message); - goto err_abort; - } - -/* join IP multicast groups */ - for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) - pgm_setsockopt (g_sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); - pgm_setsockopt (g_sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); - pgm_freeaddrinfo (res); - -/* set IP parameters */ - const int nonblocking = 1, - multicast_loop = g_multicast_loop ? 1 : 0, - multicast_hops = 16, - dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ - - pgm_setsockopt (g_sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); - pgm_setsockopt (g_sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); - if (AF_INET6 != sa_family) - pgm_setsockopt (g_sock, PGM_TOS, &dscp, sizeof(dscp)); - pgm_setsockopt (g_sock, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); - - if (!pgm_connect (g_sock, &pgm_err)) { - g_error ("connecting PGM socket: %s", pgm_err->message); - goto err_abort; - } - - g_message ("startup complete."); - return TRUE; - -err_abort: - if (NULL != g_sock) { - pgm_close (g_sock, FALSE); - g_sock = NULL; - } - if (NULL != res) { - pgm_freeaddrinfo (res); - res = NULL; - } - if (NULL != pgm_err) { - pgm_error_free (pgm_err); - pgm_err = NULL; - } - return FALSE; -} - -static -int -on_data ( - gconstpointer data, - size_t len, - struct pgm_sockaddr_t* from - ) -{ -/* protect against non-null terminated strings */ - char buf[1024], tsi[PGM_TSISTRLEN]; - const size_t buflen = MIN(sizeof(buf) - 1, len); - strncpy (buf, (const char*)data, buflen); - buf[buflen] = '\0'; - pgm_tsi_print_r (&from->sa_addr, tsi, sizeof(tsi)); - - g_message ("\"%s\" (%u bytes from %s)", - buf, - (unsigned)len, - tsi); - - return 0; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/purinrecv.c b/3rdparty/openpgm-svn-r1085/pgm/examples/purinrecv.c deleted file mode 100644 index c5e4bd3..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/purinrecv.c +++ /dev/null @@ -1,474 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * プリン PGM receiver - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#ifndef _WIN32 -# include -#else -# include "getopt.h" -# define snprintf _snprintf -#endif -#include - - -/* globals */ - -static int port = 0; -static const char* network = ""; -static bool use_multicast_loop = FALSE; -static int udp_encap_port = 0; - -static int max_tpdu = 1500; -static int sqns = 100; - -static bool use_pgmcc = FALSE; -static bool use_fec = FALSE; -static int rs_k = 8; -static int rs_n = 255; - -static pgm_sock_t* sock = NULL; -static bool is_terminated = FALSE; - -#ifndef _WIN32 -static int terminate_pipe[2]; -static void on_signal (int); -#else -static HANDLE terminate_event; -static BOOL on_console_ctrl (DWORD); -#endif -#ifndef _MSC_VER -static void usage (const char*) __attribute__((__noreturn__)); -#else -static void usage (const char*); -#endif - -static bool on_startup (void); -static int on_data (const void*restrict, const size_t, const struct pgm_sockaddr_t*restrict); - - -static void -usage ( - const char* bin - ) -{ - fprintf (stderr, "Usage: %s [options]\n", bin); - fprintf (stderr, " -n : Multicast group or unicast IP address\n"); - fprintf (stderr, " -s : IP port\n"); - fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); - fprintf (stderr, " -c : Enable PGMCC\n"); - fprintf (stderr, " -f : Enable FEC with either proactive or ondemand parity\n"); - fprintf (stderr, " -K : Configure Reed-Solomon code (n, k)\n"); - fprintf (stderr, " -N \n"); - fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); - fprintf (stderr, " -i : List available interfaces\n"); - exit (EXIT_SUCCESS); -} - -int -main ( - int argc, - char* argv[] - ) -{ - pgm_error_t* pgm_err = NULL; - - setlocale (LC_ALL, ""); - -#if !defined(_WIN32) || defined(CONFIG_TARGET_WINE) - puts ("プリン プリン"); -#else - _putws (L"プリン プリン"); -#endif - - if (!pgm_init (&pgm_err)) { - fprintf (stderr, "Unable to start PGM engine: %s\n", pgm_err->message); - pgm_error_free (pgm_err); - return EXIT_FAILURE; - } - -/* parse program arguments */ - const char* binary_name = strrchr (argv[0], '/'); - int c; - while ((c = getopt (argc, argv, "s:n:p:cf:K:N:lih")) != -1) - { - switch (c) { - case 'n': network = optarg; break; - case 's': port = atoi (optarg); break; - case 'p': udp_encap_port = atoi (optarg); break; - case 'c': use_pgmcc = TRUE; break; - case 'f': use_fec = TRUE; break; - case 'K': rs_k = atoi (optarg); break; - case 'N': rs_n = atoi (optarg); break; - case 'l': use_multicast_loop = TRUE; break; - - case 'i': - pgm_if_print_all(); - return EXIT_SUCCESS; - - case 'h': - case '?': usage (binary_name); - } - } - - if (use_fec && ( !rs_n || !rs_k )) { - fprintf (stderr, "Invalid Reed-Solomon parameters RS(%d,%d).\n", rs_n, rs_k); - usage (binary_name); - } - -/* setup signal handlers */ -#ifdef SIGHUP - signal (SIGHUP, SIG_IGN); -#endif -#ifndef _WIN32 - int e = pipe (terminate_pipe); - assert (0 == e); - signal (SIGINT, on_signal); - signal (SIGTERM, on_signal); -#else - terminate_event = CreateEvent (NULL, TRUE, FALSE, TEXT("TerminateEvent")); - SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); -#endif /* !_WIN32 */ - - if (!on_startup()) { - fprintf (stderr, "Startup failed\n"); - return EXIT_FAILURE; - } - -/* dispatch loop */ -#ifndef _WIN32 - int fds; - fd_set readfds; -#else - int n_handles = 3, recv_sock, pending_sock; - HANDLE waitHandles[ 3 ]; - DWORD dwTimeout, dwEvents; - WSAEVENT recvEvent, pendingEvent; - socklen_t socklen = sizeof(int); - - recvEvent = WSACreateEvent (); - pgm_getsockopt (sock, PGM_RECV_SOCK, &recv_sock, &socklen); - WSAEventSelect (recv_sock, recvEvent, FD_READ); - pendingEvent = WSACreateEvent (); - pgm_getsockopt (sock, PGM_PENDING_SOCK, &pending_sock, &socklen); - WSAEventSelect (pending_sock, pendingEvent, FD_READ); - - waitHandles[0] = terminate_event; - waitHandles[1] = recvEvent; - waitHandles[2] = pendingEvent; -#endif /* !_WIN32 */ - puts ("Entering PGM message loop ... "); - do { - struct timeval tv; - char buffer[4096]; - size_t len; - struct pgm_sockaddr_t from; - socklen_t fromlen = sizeof (from); - const int status = pgm_recvfrom (sock, - buffer, - sizeof(buffer), - 0, - &len, - &from, - &fromlen, - &pgm_err); - switch (status) { - case PGM_IO_STATUS_NORMAL: - on_data (buffer, len, &from); - break; - case PGM_IO_STATUS_TIMER_PENDING: - { - socklen_t optlen = sizeof (tv); - pgm_getsockopt (sock, PGM_TIME_REMAIN, &tv, &optlen); - } - goto block; - case PGM_IO_STATUS_RATE_LIMITED: - { - socklen_t optlen = sizeof (tv); - pgm_getsockopt (sock, PGM_RATE_REMAIN, &tv, &optlen); - } - case PGM_IO_STATUS_WOULD_BLOCK: -/* select for next event */ -block: -#ifndef _WIN32 - fds = terminate_pipe[0] + 1; - FD_ZERO(&readfds); - FD_SET(terminate_pipe[0], &readfds); - pgm_select_info (sock, &readfds, NULL, &fds); - fds = select (fds, &readfds, NULL, NULL, PGM_IO_STATUS_WOULD_BLOCK == status ? NULL : &tv); -#else - dwTimeout = PGM_IO_STATUS_WOULD_BLOCK == status ? INFINITE : (DWORD)((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); - dwEvents = WaitForMultipleObjects (n_handles, waitHandles, FALSE, dwTimeout); - switch (dwEvents) { - case WAIT_OBJECT_0+1: WSAResetEvent (recvEvent); break; - case WAIT_OBJECT_0+2: WSAResetEvent (pendingEvent); break; - default: break; - } -#endif /* !_WIN32 */ - break; - - default: - if (pgm_err) { - fprintf (stderr, "%s\n", pgm_err->message); - pgm_error_free (pgm_err); - pgm_err = NULL; - } - if (PGM_IO_STATUS_ERROR == status) - break; - } - } while (!is_terminated); - - puts ("Message loop terminated, cleaning up."); - -/* cleanup */ -#ifndef _WIN32 - close (terminate_pipe[0]); - close (terminate_pipe[1]); -#else - WSACloseEvent (recvEvent); - WSACloseEvent (pendingEvent); - CloseHandle (terminate_event); -#endif /* !_WIN32 */ - - if (sock) { - puts ("Destroying PGM socket."); - pgm_close (sock, TRUE); - sock = NULL; - } - - puts ("PGM engine shutdown."); - pgm_shutdown (); - puts ("finished."); - return EXIT_SUCCESS; -} - -#ifndef _WIN32 -static -void -on_signal ( - int signum - ) -{ - printf ("on_signal (signum:%d)\n", signum); - is_terminated = TRUE; - const char one = '1'; - const size_t writelen = write (terminate_pipe[1], &one, sizeof(one)); - assert (sizeof(one) == writelen); -} -#else -static -BOOL -on_console_ctrl ( - DWORD dwCtrlType - ) -{ - printf ("on_console_ctrl (dwCtrlType:%lu)\n", (unsigned long)dwCtrlType); - is_terminated = TRUE; - SetEvent (terminate_event); - return TRUE; -} -#endif /* !_WIN32 */ - -static -bool -on_startup (void) -{ - struct pgm_addrinfo_t* res = NULL; - pgm_error_t* pgm_err = NULL; - sa_family_t sa_family = AF_UNSPEC; - -/* parse network parameter into PGM socket address structure */ - if (!pgm_getaddrinfo (network, NULL, &res, &pgm_err)) { - fprintf (stderr, "Parsing network parameter: %s\n", pgm_err->message); - goto err_abort; - } - - sa_family = res->ai_send_addrs[0].gsr_group.ss_family; - - if (udp_encap_port) { - puts ("Create PGM/UDP socket."); - if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { - fprintf (stderr, "Creating PGM/UDP socket: %s\n", pgm_err->message); - goto err_abort; - } - pgm_setsockopt (sock, PGM_UDP_ENCAP_UCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); - pgm_setsockopt (sock, PGM_UDP_ENCAP_MCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); - } else { - puts ("Create PGM/IP socket."); - if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { - fprintf (stderr, "Creating PGM/IP socket: %s\n", pgm_err->message); - goto err_abort; - } - } - -/* Use RFC 2113 tagging for PGM Router Assist */ - const int no_router_assist = 0; - pgm_setsockopt (sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); - - pgm_drop_superuser(); - -/* set PGM parameters */ - const int recv_only = 1, - passive = 0, - peer_expiry = pgm_secs (300), - spmr_expiry = pgm_msecs (250), - nak_bo_ivl = pgm_msecs (50), - nak_rpt_ivl = pgm_secs (2), - nak_rdata_ivl = pgm_secs (2), - nak_data_retries = 50, - nak_ncf_retries = 50; - - pgm_setsockopt (sock, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); - pgm_setsockopt (sock, PGM_PASSIVE, &passive, sizeof(passive)); - pgm_setsockopt (sock, PGM_MTU, &max_tpdu, sizeof(max_tpdu)); - pgm_setsockopt (sock, PGM_RXW_SQNS, &sqns, sizeof(sqns)); - pgm_setsockopt (sock, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); - pgm_setsockopt (sock, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); - pgm_setsockopt (sock, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); - pgm_setsockopt (sock, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); - pgm_setsockopt (sock, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); - pgm_setsockopt (sock, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); - pgm_setsockopt (sock, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); - -#ifdef I_UNDERSTAND_PGMCC_AND_FEC_ARE_NOT_SUPPORTED - if (use_pgmcc) { - struct pgm_pgmccinfo_t pgmccinfo; - pgmccinfo.ack_bo_ivl = pgm_msecs (50); - pgmccinfo.ack_c = 75; - pgmccinfo.ack_c_p = 500; - pgm_setsockopt (sock, PGM_USE_PGMCC, &pgmccinfo, sizeof(pgmccinfo)); - } - if (use_fec) { - struct pgm_fecinfo_t fecinfo; - fecinfo.block_size = rs_n; - fecinfo.proactive_packets = 0; - fecinfo.group_size = rs_k; - fecinfo.ondemand_parity_enabled = TRUE; - fecinfo.var_pktlen_enabled = FALSE; - pgm_setsockopt (sock, PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); - } -#endif - -/* create global session identifier */ - struct pgm_sockaddr_t addr; - memset (&addr, 0, sizeof(addr)); - addr.sa_port = port ? port : DEFAULT_DATA_DESTINATION_PORT; - addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; - if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { - fprintf (stderr, "Creating GSI: %s\n", pgm_err->message); - goto err_abort; - } - -/* assign socket to specified address */ - struct pgm_interface_req_t if_req; - memset (&if_req, 0, sizeof(if_req)); - if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; - if_req.ir_scope_id = 0; - if (AF_INET6 == sa_family) { - struct sockaddr_in6 sa6; - memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); - if_req.ir_scope_id = sa6.sin6_scope_id; - } - if (!pgm_bind3 (sock, - &addr, sizeof(addr), - &if_req, sizeof(if_req), /* tx interface */ - &if_req, sizeof(if_req), /* rx interface */ - &pgm_err)) - { - fprintf (stderr, "Binding PGM socket: %s\n", pgm_err->message); - goto err_abort; - } - -/* join IP multicast groups */ - for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) - pgm_setsockopt (sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); - pgm_setsockopt (sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); - pgm_freeaddrinfo (res); - -/* set IP parameters */ - const int nonblocking = 1, - multicast_loop = use_multicast_loop ? 1 : 0, - multicast_hops = 16, - dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ - - pgm_setsockopt (sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); - pgm_setsockopt (sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); - if (AF_INET6 != sa_family) - pgm_setsockopt (sock, PGM_TOS, &dscp, sizeof(dscp)); - pgm_setsockopt (sock, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); - - if (!pgm_connect (sock, &pgm_err)) { - fprintf (stderr, "Connecting PGM socket: %s\n", pgm_err->message); - goto err_abort; - } - - puts ("Startup complete."); - return TRUE; - -err_abort: - if (NULL != sock) { - pgm_close (sock, FALSE); - sock = NULL; - } - if (NULL != res) { - pgm_freeaddrinfo (res); - res = NULL; - } - if (NULL != pgm_err) { - pgm_error_free (pgm_err); - pgm_err = NULL; - } - if (NULL != sock) { - pgm_close (sock, FALSE); - sock = NULL; - } - return FALSE; -} - -static -int -on_data ( - const void* restrict data, - const size_t len, - const struct pgm_sockaddr_t* restrict from - ) -{ -/* protect against non-null terminated strings */ - char buf[1024], tsi[PGM_TSISTRLEN]; - const size_t buflen = MIN(sizeof(buf) - 1, len); - strncpy (buf, (const char*)data, buflen); - buf[buflen] = '\0'; - pgm_tsi_print_r (&from->sa_addr, tsi, sizeof(tsi)); -#ifndef _MSC_VER - printf ("\"%s\" (%zu bytes from %s)\n", - buf, len, tsi); -#else -/* Microsoft CRT will crash on %zu */ - printf ("\"%s\" (%u bytes from %s)\n", - buf, (unsigned)len, tsi); -#endif - return 0; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/purinrecvcc.cc b/3rdparty/openpgm-svn-r1085/pgm/examples/purinrecvcc.cc deleted file mode 100644 index 72c1d23..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/purinrecvcc.cc +++ /dev/null @@ -1,434 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * プリン PGM receiver - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#ifndef _WIN32 -# include -# include -# include -#else -# include "getopt.h" -#endif -#include - - -/* globals */ - -static int port = 0; -static const char* network = ""; -static bool use_multicast_loop = FALSE; -static int udp_encap_port = 0; - -static int max_tpdu = 1500; -static int sqns = 100; - -static bool use_fec = FALSE; -static int rs_k = 8; -static int rs_n = 255; - -static ip::pgm::endpoint* endpoint = NULL; -static ip::pgm::socket* sock = NULL; -static bool is_terminated = FALSE; - -#ifndef _WIN32 -static int terminate_pipe[2]; -static void on_signal (int); -#else -static HANDLE terminate_event; -static BOOL on_console_ctrl (DWORD); -#endif -#ifndef _MSC_VER -static void usage (const char*) __attribute__((__noreturn__)); -#else -static void usage (const char*); -#endif - -static bool on_startup (void); -static int on_data (const void*, size_t, const ip::pgm::endpoint&); - - -static void -usage ( - const char* bin - ) -{ - std::cerr << "Usage: " << bin << " [options]" << std::endl; - std::cerr << " -n : Multicast group or unicast IP address" << std::endl; - std::cerr << " -s : IP port" << std::endl; - std::cerr << " -p : Encapsulate PGM in UDP on IP port" << std::endl; - std::cerr << " -f : Enable FEC with either proactive or ondemand parity" << std::endl; - std::cerr << " -K : Configure Reed-Solomon code (n, k)" << std::endl; - std::cerr << " -N " << std::endl; - std::cerr << " -l : Enable multicast loopback and address sharing" << std::endl; - std::cerr << " -i : List available interfaces" << std::endl; - exit (EXIT_SUCCESS); -} - -int -main ( - int argc, - char* argv[] - ) -{ - cpgm::pgm_error_t* pgm_err = NULL; - - setlocale (LC_ALL, ""); - -#if !defined(_WIN32) || defined(CONFIG_TARGET_WINE) - std::cout << "プリン プリン" << std::endl; -#else - std::wcout << L"プリン プリン" << std::endl; -#endif - - if (!cpgm::pgm_init (&pgm_err)) { - std::cerr << "Unable to start PGM engine: " << pgm_err->message << std::endl; - cpgm::pgm_error_free (pgm_err); - return EXIT_FAILURE; - } - -/* parse program arguments */ - const char* binary_name = std::strrchr (argv[0], '/'); - int c; - while ((c = getopt (argc, argv, "s:n:p:f:K:N:lih")) != -1) - { - switch (c) { - case 'n': network = optarg; break; - case 's': port = atoi (optarg); break; - case 'p': udp_encap_port = atoi (optarg); break; - case 'f': use_fec = TRUE; break; - case 'K': rs_k = atoi (optarg); break; - case 'N': rs_n = atoi (optarg); break; - case 'l': use_multicast_loop = TRUE; break; - - case 'i': - cpgm::pgm_if_print_all(); - return EXIT_SUCCESS; - - case 'h': - case '?': usage (binary_name); - } - } - - if (use_fec && ( !rs_n || !rs_k )) { - std::cerr << "Invalid Reed-Solomon parameters RS(" << rs_n << "," << rs_k << ")." << std::endl; - usage (binary_name); - } - -/* setup signal handlers */ -#ifdef SIGHUP - signal (SIGHUP, SIG_IGN); -#endif -#ifndef _WIN32 - int e = pipe (terminate_pipe); - assert (0 == e); - signal (SIGINT, on_signal); - signal (SIGTERM, on_signal); -#else - terminate_event = CreateEvent (NULL, TRUE, FALSE, TEXT("TerminateEvent")); - SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); -#endif /* !_WIN32 */ - - if (!on_startup()) { - std::cerr << "Startup failed" << std::endl; - return EXIT_FAILURE; - } - -/* dispatch loop */ -#ifndef _WIN32 - int fds; - fd_set readfds; -#else - int n_handles = 3, recv_sock, pending_sock; - HANDLE waitHandles[ 3 ]; - DWORD dwTimeout, dwEvents; - WSAEVENT recvEvent, pendingEvent; - socklen_t socklen = sizeof(int); - - recvEvent = WSACreateEvent (); - sock->get_option (cpgm::PGM_RECV_SOCK, &recv_sock, &socklen); - WSAEventSelect (recv_sock, recvEvent, FD_READ); - pendingEvent = WSACreateEvent (); - sock->get_option (cpgm::PGM_PENDING_SOCK, &pending_sock, &socklen); - WSAEventSelect (pending_sock, pendingEvent, FD_READ); - - waitHandles[0] = terminate_event; - waitHandles[1] = recvEvent; - waitHandles[2] = pendingEvent; -#endif /* !_WIN32 */ - std::cout << "Entering PGM message loop ... " << std::endl; - do { - socklen_t optlen; - struct timeval tv; - char buffer[4096]; - size_t len; - ip::pgm::endpoint from; - const int status = sock->receive_from (buffer, - sizeof(buffer), - 0, - &len, - &from, - &pgm_err); - switch (status) { - case cpgm::PGM_IO_STATUS_NORMAL: - on_data (buffer, len, from); - break; - case cpgm::PGM_IO_STATUS_TIMER_PENDING: - optlen = sizeof (tv); - sock->get_option (cpgm::PGM_TIME_REMAIN, &tv, &optlen); - goto block; - case cpgm::PGM_IO_STATUS_RATE_LIMITED: - optlen = sizeof (tv); - sock->get_option (cpgm::PGM_RATE_REMAIN, &tv, &optlen); - case cpgm::PGM_IO_STATUS_WOULD_BLOCK: -/* select for next event */ -block: -#ifndef _WIN32 - fds = terminate_pipe[0] + 1; - FD_ZERO(&readfds); - FD_SET(terminate_pipe[0], &readfds); - pgm_select_info (sock->native(), &readfds, NULL, &fds); - fds = select (fds, &readfds, NULL, NULL, cpgm::PGM_IO_STATUS_WOULD_BLOCK == status ? NULL : &tv); -#else - dwTimeout = cpgm::PGM_IO_STATUS_WOULD_BLOCK == status ? INFINITE : (DWORD)((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); - dwEvents = WaitForMultipleObjects (n_handles, waitHandles, FALSE, dwTimeout); - switch (dwEvents) { - case WAIT_OBJECT_0+1: WSAResetEvent (recvEvent); break; - case WAIT_OBJECT_0+2: WSAResetEvent (pendingEvent); break; - default: break; - } -#endif /* !_WIN32 */ - break; - - default: - if (pgm_err) { - std::cerr << pgm_err->message << std::endl; - cpgm::pgm_error_free (pgm_err); - pgm_err = NULL; - } - if (cpgm::PGM_IO_STATUS_ERROR == status) - break; - } - } while (!is_terminated); - - std::cout << "Message loop terminated, cleaning up." << std::endl; - -/* cleanup */ -#ifndef _WIN32 - close (terminate_pipe[0]); - close (terminate_pipe[1]); -#else - WSACloseEvent (recvEvent); - WSACloseEvent (pendingEvent); - CloseHandle (terminate_event); -#endif /* !_WIN32 */ - - if (sock) { - std::cout << "Closing PGM socket." << std::endl; - sock->close (TRUE); - sock = NULL; - } - - std::cout << "PGM engine shutdown." << std::endl; - cpgm::pgm_shutdown (); - std::cout << "finished." << std::endl; - return EXIT_SUCCESS; -} - -#ifndef _WIN32 -static -void -on_signal ( - int signum - ) -{ - std::cout << "on_signal (signum:" << signum << ")" << std::endl; - is_terminated = TRUE; - const char one = '1'; - const size_t writelen = write (terminate_pipe[1], &one, sizeof(one)); - assert (sizeof(one) == writelen); -} -#else -static -BOOL -on_console_ctrl ( - DWORD dwCtrlType - ) -{ - std::cout << "on_console_ctrl (dwCtrlType:" << dwCtrlType << ")" << std::endl; - is_terminated = TRUE; - SetEvent (terminate_event); - return TRUE; -} -#endif /* !_WIN32 */ - -static -bool -on_startup (void) -{ - struct cpgm::pgm_addrinfo_t* res = NULL; - cpgm::pgm_error_t* pgm_err = NULL; - sa_family_t sa_family = AF_UNSPEC; - -/* parse network parameter into PGM socket address structure */ - if (!pgm_getaddrinfo (network, NULL, &res, &pgm_err)) { - std::cerr << "Parsing network parameter: " << pgm_err->message << std::endl; - goto err_abort; - } - - sa_family = res->ai_send_addrs[0].gsr_group.ss_family; - - sock = new ip::pgm::socket(); - - if (udp_encap_port) { - std::cout << "Create PGM/UDP socket." << std::endl; - if (!sock->open (sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { - std::cerr << "Creating PGM/UDP socket: " << pgm_err->message << std::endl; - goto err_abort; - } - sock->set_option (cpgm::PGM_UDP_ENCAP_UCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); - sock->set_option (cpgm::PGM_UDP_ENCAP_MCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); - } else { - std::cout << "Create PGM/IP socket." << std::endl; - if (!sock->open (sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { - std::cerr << "Creating PGM/IP socket: " << pgm_err->message << std::endl; - goto err_abort; - } - } - - { -/* Use RFC 2113 tagging for PGM Router Assist */ - const int no_router_assist = 0; - sock->set_option (cpgm::PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); - } - - cpgm::pgm_drop_superuser(); - - { -/* set PGM parameters */ - const int recv_only = 1, - passive = 0, - peer_expiry = pgm_secs (300), - spmr_expiry = pgm_msecs (250), - nak_bo_ivl = pgm_msecs (50), - nak_rpt_ivl = pgm_secs (2), - nak_rdata_ivl = pgm_secs (2), - nak_data_retries = 50, - nak_ncf_retries = 50; - - sock->set_option (cpgm::PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); - sock->set_option (cpgm::PGM_PASSIVE, &passive, sizeof(passive)); - sock->set_option (cpgm::PGM_MTU, &max_tpdu, sizeof(max_tpdu)); - sock->set_option (cpgm::PGM_RXW_SQNS, &sqns, sizeof(sqns)); - sock->set_option (cpgm::PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); - sock->set_option (cpgm::PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); - sock->set_option (cpgm::PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); - sock->set_option (cpgm::PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); - sock->set_option (cpgm::PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); - sock->set_option (cpgm::PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); - sock->set_option (cpgm::PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); - } - if (use_fec) { - struct cpgm::pgm_fecinfo_t fecinfo; - fecinfo.block_size = rs_n; - fecinfo.proactive_packets = 0; - fecinfo.group_size = rs_k; - fecinfo.ondemand_parity_enabled = TRUE; - fecinfo.var_pktlen_enabled = FALSE; - sock->set_option (cpgm::PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); - } - -/* create global session identifier */ - endpoint = new ip::pgm::endpoint (DEFAULT_DATA_DESTINATION_PORT); - -/* assign socket to specified address */ - if (!sock->bind (*endpoint, &pgm_err)) { - std::cerr << "Binding PGM socket: " << pgm_err->message << std::endl; - goto err_abort; - } - -/* join IP multicast groups */ - for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) - sock->set_option (cpgm::PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); - sock->set_option (cpgm::PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); - pgm_freeaddrinfo (res); - - { -/* set IP parameters */ - const int nonblocking = 1, - multicast_loop = use_multicast_loop ? 1 : 0, - multicast_hops = 16, - dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ - - sock->set_option (cpgm::PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); - sock->set_option (cpgm::PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); - sock->set_option (cpgm::PGM_TOS, &dscp, sizeof(dscp)); - sock->set_option (cpgm::PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); - } - - if (!sock->connect (&pgm_err)) { - std::cerr << "Connecting PGM socket: " << pgm_err->message << std::endl; - goto err_abort; - } - - std::cout << "Startup complete." << std::endl; - return TRUE; - -err_abort: - if (NULL != sock) { - sock->close (FALSE); - sock = NULL; - } - if (NULL != res) { - cpgm::pgm_freeaddrinfo (res); - res = NULL; - } - if (NULL != pgm_err) { - cpgm::pgm_error_free (pgm_err); - pgm_err = NULL; - } - return FALSE; -} - -static -int -on_data ( - const void* data, - size_t len, - const ip::pgm::endpoint& from - ) -{ -/* protect against non-null terminated strings */ - char buf[1024], tsi[PGM_TSISTRLEN]; - const size_t buflen = MIN(sizeof(buf) - 1, len); - strncpy (buf, (char*)data, buflen); - buf[buflen] = '\0'; - cpgm::pgm_tsi_print_r (from.address(), tsi, sizeof(tsi)); - std::cout << "\"" << buf << "\" (" << len << " bytes from " << tsi << ")" << std::endl; - return 0; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/purinsend.c b/3rdparty/openpgm-svn-r1085/pgm/examples/purinsend.c deleted file mode 100644 index 811c14c..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/purinsend.c +++ /dev/null @@ -1,280 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * プリン PGM sender - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#ifndef _WIN32 -# include -#else -# include "getopt.h" -# define snprintf _snprintf -#endif -#include - - -/* globals */ - -static int port = 0; -static const char* network = ""; -static bool use_multicast_loop = FALSE; -static int udp_encap_port = 0; - -static int max_tpdu = 1500; -static int max_rte = 400*1000; /* very conservative rate, 2.5mb/s */ -static int sqns = 100; - -static bool use_fec = FALSE; -static int rs_k = 8; -static int rs_n = 255; - -static pgm_sock_t* sock = NULL; - -#ifndef _MSC_VER -static void usage (const char*) __attribute__((__noreturn__)); -#else -static void usage (const char*); -#endif -static bool create_sock (void); - - -static void -usage ( - const char* bin - ) -{ - fprintf (stderr, "Usage: %s [options] message\n", bin); - fprintf (stderr, " -n : Multicast group or unicast IP address\n"); - fprintf (stderr, " -s : IP port\n"); - fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); - fprintf (stderr, " -r : Regulate to rate bytes per second\n"); - fprintf (stderr, " -f : Enable FEC with either proactive or ondemand parity\n"); - fprintf (stderr, " -K : Configure Reed-Solomon code (n, k)\n"); - fprintf (stderr, " -N \n"); - fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); - fprintf (stderr, " -i : List available interfaces\n"); - exit (EXIT_SUCCESS); -} - -int -main ( - int argc, - char *argv[] - ) -{ - pgm_error_t* pgm_err = NULL; - - setlocale (LC_ALL, ""); - - if (!pgm_init (&pgm_err)) { - fprintf (stderr, "Unable to start PGM engine: %s\n", pgm_err->message); - pgm_error_free (pgm_err); - return EXIT_FAILURE; - } - -/* parse program arguments */ - const char* binary_name = strrchr (argv[0], '/'); - int c; - while ((c = getopt (argc, argv, "s:n:p:r:f:K:N:lih")) != -1) - { - switch (c) { - case 'n': network = optarg; break; - case 's': port = atoi (optarg); break; - case 'p': udp_encap_port = atoi (optarg); break; - case 'r': max_rte = atoi (optarg); break; - case 'f': use_fec = TRUE; break; - case 'K': rs_k = atoi (optarg); break; - case 'N': rs_n = atoi (optarg); break; - - case 'l': use_multicast_loop = TRUE; break; - - case 'i': - pgm_if_print_all(); - return EXIT_SUCCESS; - - case 'h': - case '?': - usage (binary_name); - } - } - - if (use_fec && ( !rs_n || !rs_k )) { - fprintf (stderr, "Invalid Reed-Solomon parameters RS(%d,%d).\n", rs_n, rs_k); - usage (binary_name); - } - - if (create_sock()) - { - while (optind < argc) { - const int status = pgm_send (sock, argv[optind], strlen (argv[optind]) + 1, NULL); - if (PGM_IO_STATUS_NORMAL != status) { - fprintf (stderr, "pgm_send() failed.\n"); - } - optind++; - } - } - -/* cleanup */ - if (sock) { - pgm_close (sock, TRUE); - sock = NULL; - } - pgm_shutdown(); - return EXIT_SUCCESS; -} - -static -bool -create_sock (void) -{ - struct pgm_addrinfo_t* res = NULL; - pgm_error_t* pgm_err = NULL; - sa_family_t sa_family = AF_UNSPEC; - -/* parse network parameter into PGM socket address structure */ - if (!pgm_getaddrinfo (network, NULL, &res, &pgm_err)) { - fprintf (stderr, "Parsing network parameter: %s\n", pgm_err->message); - goto err_abort; - } - - sa_family = res->ai_send_addrs[0].gsr_group.ss_family; - - if (udp_encap_port) { - if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { - fprintf (stderr, "Creating PGM/UDP socket: %s\n", pgm_err->message); - goto err_abort; - } - pgm_setsockopt (sock, PGM_UDP_ENCAP_UCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); - pgm_setsockopt (sock, PGM_UDP_ENCAP_MCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); - } else { - if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { - fprintf (stderr, "Creating PGM/IP socket: %s\n", pgm_err->message); - goto err_abort; - } - } - -/* Use RFC 2113 tagging for PGM Router Assist */ - const int no_router_assist = 0; - pgm_setsockopt (sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); - - pgm_drop_superuser(); - -/* set PGM parameters */ - const int send_only = 1, - ambient_spm = pgm_secs (30), - heartbeat_spm[] = { pgm_msecs (100), - pgm_msecs (100), - pgm_msecs (100), - pgm_msecs (100), - pgm_msecs (1300), - pgm_secs (7), - pgm_secs (16), - pgm_secs (25), - pgm_secs (30) }; - - pgm_setsockopt (sock, PGM_SEND_ONLY, &send_only, sizeof(send_only)); - pgm_setsockopt (sock, PGM_MTU, &max_tpdu, sizeof(max_tpdu)); - pgm_setsockopt (sock, PGM_TXW_SQNS, &sqns, sizeof(sqns)); - pgm_setsockopt (sock, PGM_TXW_MAX_RTE, &max_rte, sizeof(max_rte)); - pgm_setsockopt (sock, PGM_AMBIENT_SPM, &ambient_spm, sizeof(ambient_spm)); - pgm_setsockopt (sock, PGM_HEARTBEAT_SPM, &heartbeat_spm, sizeof(heartbeat_spm)); - if (use_fec) { - struct pgm_fecinfo_t fecinfo; - fecinfo.block_size = rs_n; - fecinfo.proactive_packets = 0; - fecinfo.group_size = rs_k; - fecinfo.ondemand_parity_enabled = TRUE; - fecinfo.var_pktlen_enabled = TRUE; - pgm_setsockopt (sock, PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); - } - -/* create global session identifier */ - struct pgm_sockaddr_t addr; - memset (&addr, 0, sizeof(addr)); - addr.sa_port = port ? port : DEFAULT_DATA_DESTINATION_PORT; - addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; - if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { - fprintf (stderr, "Creating GSI: %s\n", pgm_err->message); - goto err_abort; - } - -/* assign socket to specified address */ - struct pgm_interface_req_t if_req; - memset (&if_req, 0, sizeof(if_req)); - if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; - if_req.ir_scope_id = 0; - if (AF_INET6 == sa_family) { - struct sockaddr_in6 sa6; - memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); - if_req.ir_scope_id = sa6.sin6_scope_id; - } - if (!pgm_bind3 (sock, - &addr, sizeof(addr), - &if_req, sizeof(if_req), /* tx interface */ - &if_req, sizeof(if_req), /* rx interface */ - &pgm_err)) - { - fprintf (stderr, "Binding PGM socket: %s\n", pgm_err->message); - goto err_abort; - } - -/* join IP multicast groups */ - for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) - pgm_setsockopt (sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); - pgm_setsockopt (sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); - pgm_freeaddrinfo (res); - -/* set IP parameters */ - const int blocking = 0, - multicast_loop = use_multicast_loop ? 1 : 0, - multicast_hops = 16, - dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ - - pgm_setsockopt (sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); - pgm_setsockopt (sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); - if (AF_INET6 != sa_family) - pgm_setsockopt (sock, PGM_TOS, &dscp, sizeof(dscp)); - pgm_setsockopt (sock, PGM_NOBLOCK, &blocking, sizeof(blocking)); - - if (!pgm_connect (sock, &pgm_err)) { - fprintf (stderr, "Connecting PGM socket: %s\n", pgm_err->message); - goto err_abort; - } - - return TRUE; - -err_abort: - if (NULL != sock) { - pgm_close (sock, FALSE); - sock = NULL; - } - if (NULL != res) { - pgm_freeaddrinfo (res); - res = NULL; - } - if (NULL != pgm_err) { - pgm_error_free (pgm_err); - pgm_err = NULL; - } - return FALSE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/purinsendcc.cc b/3rdparty/openpgm-svn-r1085/pgm/examples/purinsendcc.cc deleted file mode 100644 index fe3c430..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/purinsendcc.cc +++ /dev/null @@ -1,269 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * プリン PGM sender - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#ifndef _WIN32 -# include -# include -#else -# include "getopt.h" -#endif -#include - - -/* globals */ - -static int port = 0; -static const char* network = ""; -static bool use_multicast_loop = FALSE; -static int udp_encap_port = 0; - -static int max_tpdu = 1500; -static int max_rte = 400*1000; /* very conservative rate, 2.5mb/s */ -static int sqns = 100; - -static bool use_fec = FALSE; -static int rs_k = 8; -static int rs_n = 255; - -static ip::pgm::endpoint* endpoint = NULL; -static ip::pgm::socket* sock = NULL; - -#ifndef _MSC_VER -static void usage (const char*) __attribute__((__noreturn__)); -#else -static void usage (const char*); -#endif -static bool create_sock (void); - - -static void -usage ( - const char* bin - ) -{ - std::cerr << "Usage: " << bin << " [options] message" << std::endl; - std::cerr << " -n : Multicast group or unicast IP address" << std::endl; - std::cerr << " -s : IP port" << std::endl; - std::cerr << " -p : Encapsulate PGM in UDP on IP port" << std::endl; - std::cerr << " -r : Regulate to rate bytes per second" << std::endl; - std::cerr << " -f : Enable FEC with either proactive or ondemand parity" << std::endl; - std::cerr << " -K : Configure Reed-Solomon code (n, k)" << std::endl; - std::cerr << " -N " << std::endl; - std::cerr << " -l : Enable multicast loopback and address sharing" << std::endl; - std::cerr << " -i : List available interfaces" << std::endl; - exit (EXIT_SUCCESS); -} - -int -main ( - int argc, - char *argv[] - ) -{ - cpgm::pgm_error_t* pgm_err = NULL; - - setlocale (LC_ALL, ""); - - if (!cpgm::pgm_init (&pgm_err)) { - std::cerr << "Unable to start PGM engine: " << pgm_err->message << std::endl; - cpgm::pgm_error_free (pgm_err); - return EXIT_FAILURE; - } - -/* parse program arguments */ - const char* binary_name = strrchr (argv[0], '/'); - int c; - while ((c = getopt (argc, argv, "s:n:p:r:f:K:N:lih")) != -1) - { - switch (c) { - case 'n': network = optarg; break; - case 's': port = atoi (optarg); break; - case 'p': udp_encap_port = atoi (optarg); break; - case 'r': max_rte = atoi (optarg); break; - case 'f': use_fec = TRUE; break; - case 'K': rs_k = atoi (optarg); break; - case 'N': rs_n = atoi (optarg); break; - - case 'l': use_multicast_loop = TRUE; break; - - case 'i': - cpgm::pgm_if_print_all(); - return EXIT_SUCCESS; - - case 'h': - case '?': - usage (binary_name); - } - } - - if (use_fec && ( !rs_n || !rs_k )) { - std::cerr << "Invalid Reed-Solomon parameters RS(" << rs_n << "," << rs_k << ")." << std::endl; - usage (binary_name); - } - - if (create_sock()) - { - while (optind < argc) { - const int status = sock->send (argv[optind], strlen (argv[optind]) + 1, NULL); - if (cpgm::PGM_IO_STATUS_NORMAL != status) { - std::cerr << "pgm_send() failed.." << std::endl; - } - optind++; - } - } - -/* cleanup */ - if (sock) { - sock->close (TRUE); - sock = NULL; - } - cpgm::pgm_shutdown(); - return EXIT_SUCCESS; -} - -static -bool -create_sock (void) -{ - struct cpgm::pgm_addrinfo_t* res = NULL; - cpgm::pgm_error_t* pgm_err = NULL; - sa_family_t sa_family = AF_UNSPEC; - -/* parse network parameter into PGM socket address structure */ - if (!cpgm::pgm_getaddrinfo (network, NULL, &res, &pgm_err)) { - std::cerr << "Parsing network parameter: " << pgm_err->message << std::endl; - goto err_abort; - } - - sa_family = res->ai_send_addrs[0].gsr_group.ss_family; - - sock = new ip::pgm::socket(); - - if (udp_encap_port) { - if (!sock->open (sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { - std::cerr << "Creating PGM/UDP socket: " << pgm_err->message << std::endl; - goto err_abort; - } - sock->set_option (cpgm::PGM_UDP_ENCAP_UCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); - sock->set_option (cpgm::PGM_UDP_ENCAP_MCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); - } else { - if (!sock->open (sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { - std::cerr << "Creating PGM/IP socket: " << pgm_err->message << std::endl; - goto err_abort; - } - } - - { -/* Use RFC 2113 tagging for PGM Router Assist */ - const int no_router_assist = 0; - sock->set_option (cpgm::PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); - } - - cpgm::pgm_drop_superuser(); - - { -/* set PGM parameters */ - const int send_only = 1, - ambient_spm = pgm_secs (30), - heartbeat_spm[] = { pgm_msecs (100), - pgm_msecs (100), - pgm_msecs (100), - pgm_msecs (100), - pgm_msecs (1300), - pgm_secs (7), - pgm_secs (16), - pgm_secs (25), - pgm_secs (30) }; - - sock->set_option (cpgm::PGM_SEND_ONLY, &send_only, sizeof(send_only)); - sock->set_option (cpgm::PGM_MTU, &max_tpdu, sizeof(max_tpdu)); - sock->set_option (cpgm::PGM_TXW_SQNS, &sqns, sizeof(sqns)); - sock->set_option (cpgm::PGM_TXW_MAX_RTE, &max_rte, sizeof(max_rte)); - sock->set_option (cpgm::PGM_AMBIENT_SPM, &ambient_spm, sizeof(ambient_spm)); - sock->set_option (cpgm::PGM_HEARTBEAT_SPM, &heartbeat_spm, sizeof(heartbeat_spm)); - } - if (use_fec) { - struct cpgm::pgm_fecinfo_t fecinfo; - fecinfo.block_size = rs_n; - fecinfo.proactive_packets = 0; - fecinfo.group_size = rs_k; - fecinfo.ondemand_parity_enabled = TRUE; - fecinfo.var_pktlen_enabled = TRUE; - sock->set_option (cpgm::PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); - } - -/* create global session identifier */ - endpoint = new ip::pgm::endpoint (DEFAULT_DATA_DESTINATION_PORT); - -/* assign socket to specified address */ - if (!sock->bind (*endpoint, &pgm_err)) { - std::cerr << "Binding PGM socket: " << pgm_err->message << std::endl; - goto err_abort; - } - -/* join IP multicast groups */ - for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) - sock->set_option (cpgm::PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); - sock->set_option (cpgm::PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); - cpgm::pgm_freeaddrinfo (res); - - { -/* set IP parameters */ - const int blocking = 0, - multicast_loop = use_multicast_loop ? 1 : 0, - multicast_hops = 16, - dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ - - sock->set_option (cpgm::PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); - sock->set_option (cpgm::PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); - sock->set_option (cpgm::PGM_TOS, &dscp, sizeof(dscp)); - sock->set_option (cpgm::PGM_NOBLOCK, &blocking, sizeof(blocking)); - } - - if (!sock->connect (&pgm_err)) { - fprintf (stderr, "Connecting PGM socket: %s\n", pgm_err->message); - goto err_abort; - } - - return TRUE; - -err_abort: - if (NULL != sock) { - sock->close (FALSE); - sock = NULL; - } - if (NULL != res) { - cpgm::pgm_freeaddrinfo (res); - res = NULL; - } - if (NULL != pgm_err) { - cpgm::pgm_error_free (pgm_err); - pgm_err = NULL; - } - return FALSE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/shortcakerecv.c b/3rdparty/openpgm-svn-r1085/pgm/examples/shortcakerecv.c deleted file mode 100644 index 87ce8dc..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/shortcakerecv.c +++ /dev/null @@ -1,430 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * ショートケーキ PGM receiver - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#ifndef _WIN32 -# include -#else -# include "getopt.h" -# define snprintf _snprintf -#endif -#include - -#include "async.h" - - -/* globals */ - -static int port = 0; -static const char* network = ""; -static bool use_multicast_loop = FALSE; -static int udp_encap_port = 0; - -static int max_tpdu = 1500; -static int sqns = 100; - -static bool use_fec = FALSE; -static int rs_k = 8; -static int rs_n = 255; - -static pgm_sock_t* sock = NULL; -static async_t* async = NULL; -static bool is_terminated = FALSE; - -#ifndef _WIN32 -static int terminate_pipe[2]; -static void on_signal (int); -#else -static HANDLE terminate_event; -static BOOL on_console_ctrl (DWORD); -#endif -#ifndef _MSC_VER -static void usage (const char*) __attribute__((__noreturn__)); -#else -static void usage (const char*); -#endif - -static bool on_startup (void); -static int on_data (const void*restrict, const size_t, const struct pgm_sockaddr_t*restrict); - - -static void -usage ( - const char* bin - ) -{ - fprintf (stderr, "Usage: %s [options]\n", bin); - fprintf (stderr, " -n : Multicast group or unicast IP address\n"); - fprintf (stderr, " -s : IP port\n"); - fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); - fprintf (stderr, " -f : Enable FEC with either proactive or ondemand parity\n"); - fprintf (stderr, " -K : Configure Reed-Solomon code (n, k)\n"); - fprintf (stderr, " -N \n"); - fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); - fprintf (stderr, " -i : List available interfaces\n"); - exit (EXIT_SUCCESS); -} - -int -main ( - int argc, - char* argv[] - ) -{ - pgm_error_t* pgm_err = NULL; - - setlocale (LC_ALL, ""); - -#if !defined(_WIN32) || defined(CONFIG_TARGET_WINE) - puts ("いちごのショートケーキ"); -#else - _putws (L"いちごのショートケーキ"); -#endif - - if (!pgm_init (&pgm_err)) { - fprintf (stderr, "Unable to start PGM engine: %s\n", pgm_err->message); - pgm_error_free (pgm_err); - return EXIT_FAILURE; - } - -/* parse program arguments */ - const char* binary_name = strrchr (argv[0], '/'); - int c; - while ((c = getopt (argc, argv, "s:n:p:f:K:N:lih")) != -1) - { - switch (c) { - case 'n': network = optarg; break; - case 's': port = atoi (optarg); break; - case 'p': udp_encap_port = atoi (optarg); break; - case 'f': use_fec = TRUE; break; - case 'K': rs_k = atoi (optarg); break; - case 'N': rs_n = atoi (optarg); break; - case 'l': use_multicast_loop = TRUE; break; - - case 'i': - pgm_if_print_all(); - return EXIT_SUCCESS; - - case 'h': - case '?': usage (binary_name); - } - } - - if (use_fec && ( !rs_n || !rs_k )) { - fprintf (stderr, "Invalid Reed-Solomon parameters RS(%d,%d).\n", rs_n, rs_k); - usage (binary_name); - } - -/* setup signal handlers */ -#ifdef SIGHUP - signal (SIGHUP, SIG_IGN); -#endif -#ifndef _WIN32 - int e = pipe (terminate_pipe); - assert (0 == e); - signal (SIGINT, on_signal); - signal (SIGTERM, on_signal); -#else - terminate_event = CreateEvent (NULL, TRUE, FALSE, TEXT("TerminateEvent")); - SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); -#endif /* !_WIN32 */ - - if (!on_startup()) { - fprintf (stderr, "Startup failed\n"); - return EXIT_FAILURE; - } - -/* dispatch loop */ -#ifndef _WIN32 - int fds, read_fd = async_get_fd (async); - fd_set readfds; -#else - int n_handles = 2; - HANDLE waitHandles[ 2 ]; - DWORD dwEvents; - WSAEVENT recvEvent; - - recvEvent = async_get_event (async); - - waitHandles[0] = terminate_event; - waitHandles[1] = recvEvent; -#endif /* !_WIN32 */ - puts ("Entering PGM message loop ... "); - do { - char buffer[4096]; - struct pgm_sockaddr_t from; - socklen_t fromlen = sizeof (from); - const ssize_t len = async_recvfrom (async, - buffer, - sizeof(buffer), - &from, - &fromlen); - if (len >= 0) { - on_data (buffer, len, &from); - } else { -#ifndef _WIN32 - fds = MAX(terminate_pipe[0], read_fd) + 1; - FD_ZERO(&readfds); - FD_SET(terminate_pipe[0], &readfds); - FD_SET(read_fd, &readfds); - fds = select (fds, &readfds, NULL, NULL, NULL); -#else - dwEvents = WaitForMultipleObjects (n_handles, waitHandles, FALSE, INFINITE); - switch (dwEvents) { - case WAIT_OBJECT_0+1: ResetEvent (recvEvent); break; - default: break; - } -#endif /* _WIN32 */ - } - } while (!is_terminated); - - puts ("Message loop terminated, cleaning up."); - -/* cleanup */ -#ifndef _WIN32 - close (terminate_pipe[0]); - close (terminate_pipe[1]); -#else - CloseHandle (terminate_event); -#endif /* !_WIN32 */ - - if (async) { - puts ("Destroying asynchronous queue."); - async_destroy (async); - async = NULL; - } - - if (sock) { - puts ("Closing PGM socket."); - pgm_close (sock, TRUE); - sock = NULL; - } - - puts ("PGM engine shutdown."); - pgm_shutdown (); - puts ("finished."); - return EXIT_SUCCESS; -} - -#ifndef _WIN32 -static -void -on_signal ( - int signum - ) -{ - printf ("on_signal (signum:%d)\n", signum); - is_terminated = TRUE; - const char one = '1'; - const size_t writelen = write (terminate_pipe[1], &one, sizeof(one)); - assert (sizeof(one) == writelen); -} -#else -static -BOOL -on_console_ctrl ( - DWORD dwCtrlType - ) -{ - printf ("on_console_ctrl (dwCtrlType:%lu)\n", (unsigned long)dwCtrlType); - is_terminated = TRUE; - SetEvent (terminate_event); - return TRUE; -} -#endif /* !_WIN32 */ - -static -bool -on_startup (void) -{ - struct pgm_addrinfo_t* res = NULL; - pgm_error_t* pgm_err = NULL; - sa_family_t sa_family = AF_UNSPEC; - -/* parse network parameter into PGM socket address structure */ - if (!pgm_getaddrinfo (network, NULL, &res, &pgm_err)) { - fprintf (stderr, "Parsing network parameter: %s\n", pgm_err->message); - goto err_abort; - } - - sa_family = res->ai_send_addrs[0].gsr_group.ss_family; - - puts ("Create PGM socket."); - if (udp_encap_port) { - if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { - fprintf (stderr, "Creating PGM/UDP socket: %s\n", pgm_err->message); - goto err_abort; - } - pgm_setsockopt (sock, PGM_UDP_ENCAP_UCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); - pgm_setsockopt (sock, PGM_UDP_ENCAP_MCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); - } else { - if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { - fprintf (stderr, "Creating PGM/IP socket: %s\n", pgm_err->message); - goto err_abort; - } - } - -/* Use RFC 2113 tagging for PGM Router Assist */ - const int no_router_assist = 0; - pgm_setsockopt (sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); - - pgm_drop_superuser(); - -/* set PGM parameters */ - const int recv_only = 1, - passive = 0, - peer_expiry = pgm_secs (300), - spmr_expiry = pgm_msecs (250), - nak_bo_ivl = pgm_msecs (50), - nak_rpt_ivl = pgm_secs (2), - nak_rdata_ivl = pgm_secs (2), - nak_data_retries = 50, - nak_ncf_retries = 50; - - pgm_setsockopt (sock, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); - pgm_setsockopt (sock, PGM_PASSIVE, &passive, sizeof(passive)); - pgm_setsockopt (sock, PGM_MTU, &max_tpdu, sizeof(max_tpdu)); - pgm_setsockopt (sock, PGM_RXW_SQNS, &sqns, sizeof(sqns)); - pgm_setsockopt (sock, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); - pgm_setsockopt (sock, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); - pgm_setsockopt (sock, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); - pgm_setsockopt (sock, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); - pgm_setsockopt (sock, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); - pgm_setsockopt (sock, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); - pgm_setsockopt (sock, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); - if (use_fec) { - struct pgm_fecinfo_t fecinfo; - fecinfo.block_size = rs_n; - fecinfo.proactive_packets = 0; - fecinfo.group_size = rs_k; - fecinfo.ondemand_parity_enabled = TRUE; - fecinfo.var_pktlen_enabled = TRUE; - pgm_setsockopt (sock, PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); - } - -/* create global session identifier */ - struct pgm_sockaddr_t addr; - memset (&addr, 0, sizeof(addr)); - addr.sa_port = port ? port : DEFAULT_DATA_DESTINATION_PORT; - addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; - if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { - fprintf (stderr, "Creating GSI: %s\n", pgm_err->message); - goto err_abort; - } - -/* assign socket to specified address */ - struct pgm_interface_req_t if_req; - memset (&if_req, 0, sizeof(if_req)); - if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; - if_req.ir_scope_id = 0; - if (AF_INET6 == sa_family) { - struct sockaddr_in6 sa6; - memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); - if_req.ir_scope_id = sa6.sin6_scope_id; - } - if (!pgm_bind3 (sock, - &addr, sizeof(addr), - &if_req, sizeof(if_req), /* tx interface */ - &if_req, sizeof(if_req), /* rx interface */ - &pgm_err)) - { - fprintf (stderr, "Binding PGM socket: %s\n", pgm_err->message); - goto err_abort; - } - -/* join IP multicast groups */ - for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) - pgm_setsockopt (sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); - pgm_setsockopt (sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); - pgm_freeaddrinfo (res); - -/* set IP parameters */ - const int nonblocking = 1, - multicast_loop = use_multicast_loop ? 1 : 0, - multicast_hops = 16, - dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ - - pgm_setsockopt (sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); - pgm_setsockopt (sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); - if (AF_INET6 != sa_family) - pgm_setsockopt (sock, PGM_TOS, &dscp, sizeof(dscp)); - pgm_setsockopt (sock, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); - - if (!pgm_connect (sock, &pgm_err)) { - fprintf (stderr, "Connecting PGM socket: %s\n", pgm_err->message); - goto err_abort; - } - -/* wrap bound socket in asynchronous queue */ - if (0 != async_create (&async, sock)) { - fprintf (stderr, "Creating asynchronous queue failed: %s\n", strerror(errno)); - goto err_abort; - } - - puts ("Startup complete."); - return TRUE; - -err_abort: - if (NULL != sock) { - pgm_close (sock, FALSE); - sock = NULL; - } - if (NULL != res) { - pgm_freeaddrinfo (res); - res = NULL; - } - if (NULL != pgm_err) { - pgm_error_free (pgm_err); - pgm_err = NULL; - } - return FALSE; -} - -static -int -on_data ( - const void* restrict data, - const size_t len, - const struct pgm_sockaddr_t* restrict from - ) -{ -/* protect against non-null terminated strings */ - char buf[1024], tsi[PGM_TSISTRLEN]; - const size_t buflen = MIN(sizeof(buf) - 1, len); - strncpy (buf, (const char*)data, buflen); - buf[buflen] = '\0'; - pgm_tsi_print_r (&from->sa_addr, tsi, sizeof(tsi)); -#ifndef _MSC_VER - printf ("\"%s\" (%zu bytes from %s)\n", - buf, len, tsi); -#else -/* Microsoft CRT will crash on %zu */ - printf ("\"%s\" (%u bytes from %s)\n", - buf, (unsigned)len, tsi); -#endif - return 0; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/examples/snonblocksyncrecv.c b/3rdparty/openpgm-svn-r1085/pgm/examples/snonblocksyncrecv.c deleted file mode 100644 index 434f72d..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/examples/snonblocksyncrecv.c +++ /dev/null @@ -1,435 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Simple PGM receiver: select based non-blocking synchronous receiver. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef G_OS_UNIX -# include -# include -# include -# include -#endif -#include - -/* example dependencies */ -#include -#include - - -/* typedefs */ - -/* globals */ - -static int g_port = 0; -static const char* g_network = ""; -static gboolean g_multicast_loop = FALSE; -static int g_udp_encap_port = 0; - -static int g_max_tpdu = 1500; -static int g_sqns = 100; - -static pgm_sock_t* g_sock = NULL; -static gboolean g_quit; - -#ifdef G_OS_UNIX -static int g_quit_pipe[2]; -static void on_signal (int); -#else -static HANDLE g_quit_event; -static BOOL on_console_ctrl (DWORD); -#endif - -static gboolean on_startup (void); - -static int on_data (gconstpointer, size_t, struct pgm_sockaddr_t*); - - -G_GNUC_NORETURN static -void -usage ( - const char* bin - ) -{ - fprintf (stderr, "Usage: %s [options]\n", bin); - fprintf (stderr, " -n : Multicast group or unicast IP address\n"); - fprintf (stderr, " -s : IP port\n"); - fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); - fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); - exit (1); -} - -int -main ( - int argc, - char* argv[] - ) -{ - int e; - pgm_error_t* pgm_err = NULL; - - setlocale (LC_ALL, ""); - - log_init (); - g_message ("snonblocksyncrecv"); - - if (!pgm_init (&pgm_err)) { - g_error ("Unable to start PGM engine: %s", pgm_err->message); - pgm_error_free (pgm_err); - return EXIT_FAILURE; - } - -/* parse program arguments */ - const char* binary_name = strrchr (argv[0], '/'); - int c; - while ((c = getopt (argc, argv, "s:n:p:lh")) != -1) - { - switch (c) { - case 'n': g_network = optarg; break; - case 's': g_port = atoi (optarg); break; - case 'p': g_udp_encap_port = atoi (optarg); break; - case 'l': g_multicast_loop = TRUE; break; - - case 'h': - case '?': usage (binary_name); - } - } - - g_quit = FALSE; - -/* setup signal handlers */ - signal (SIGSEGV, on_sigsegv); -#ifdef SIGHUP - signal (SIGHUP, SIG_IGN); -#endif -#ifdef G_OS_UNIX - e = pipe (g_quit_pipe); - g_assert (0 == e); - signal (SIGINT, on_signal); - signal (SIGTERM, on_signal); -#else - g_quit_event = CreateEvent (NULL, TRUE, FALSE, TEXT("QuitEvent")); - SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); -#endif /* !G_OS_UNIX */ - - if (!on_startup()) { - g_error ("startup failed"); - exit(1); - } - -/* dispatch loop */ -#ifdef G_OS_UNIX - int fds; - fd_set readfds; -#else - int n_handles = 3; - HANDLE waitHandles[ n_handles ]; - DWORD dwTimeout, dwEvents; - WSAEVENT recvEvent, pendingEvent; - - recvEvent = WSACreateEvent (); - WSAEventSelect (pgm_transport_get_recv_fd (g_transport), recvEvent, FD_READ); - pendingEvent = WSACreateEvent (); - WSAEventSelect (pgm_transport_get_pending_fd (g_transport), pendingEvent, FD_READ); - - waitHandles[0] = g_quit_event; - waitHandles[1] = recvEvent; - waitHandles[2] = pendingEvent; -#endif /* !G_OS_UNIX */ - g_message ("entering PGM message loop ... "); - do { - struct timeval tv; - char buffer[4096]; - size_t len; - struct pgm_sockaddr_t from; - socklen_t fromlen = sizeof(from); - const int status = pgm_recvfrom (g_sock, - buffer, - sizeof(buffer), - 0, - &len, - &from, - &fromlen, - &pgm_err); - switch (status) { - case PGM_IO_STATUS_NORMAL: - on_data (buffer, len, &from); - break; - case PGM_IO_STATUS_TIMER_PENDING: - { - socklen_t optlen = sizeof (tv); - pgm_getsockopt (g_sock, PGM_TIME_REMAIN, &tv, &optlen); - } - goto block; - case PGM_IO_STATUS_RATE_LIMITED: - { - socklen_t optlen = sizeof (tv); - pgm_getsockopt (g_sock, PGM_RATE_REMAIN, &tv, &optlen); - } -/* fall through */ - case PGM_IO_STATUS_WOULD_BLOCK: -/* select for next event */ -block: -#ifdef G_OS_UNIX - fds = g_quit_pipe[0] + 1; - FD_ZERO(&readfds); - FD_SET(g_quit_pipe[0], &readfds); - pgm_select_info (g_sock, &readfds, NULL, &fds); - fds = select (fds, &readfds, NULL, NULL, PGM_IO_STATUS_WOULD_BLOCK == status ? NULL : &tv); -#else - dwTimeout = PGM_IO_STATUS_WOULD_BLOCK == status ? INFINITE : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); - dwEvents = WaitForMultipleObjects (n_handles, waitHandles, FALSE, dwTimeout); - switch (dwEvents) { - case WAIT_OBJECT_0+1: WSAResetEvent (recvEvent); break; - case WAIT_OBJECT_0+2: WSAResetEvent (pendingEvent); break; - default: break; - } -#endif /* !G_OS_UNIX */ - break; - - default: - if (pgm_err) { - g_warning ("%s", pgm_err->message); - pgm_error_free (pgm_err); - pgm_err = NULL; - } - if (PGM_IO_STATUS_ERROR == status) - break; - } - } while (!g_quit); - - g_message ("message loop terminated, cleaning up."); - -/* cleanup */ -#ifdef G_OS_UNIX - close (g_quit_pipe[0]); - close (g_quit_pipe[1]); -#else - WSACloseEvent (recvEvent); - WSACloseEvent (pendingEvent); - CloseHandle (g_quit_event); -#endif /* !G_OS_UNIX */ - - if (g_sock) { - g_message ("closing PGM socket."); - pgm_close (g_sock, TRUE); - g_sock = NULL; - } - - g_message ("PGM engine shutdown."); - pgm_shutdown (); - g_message ("finished."); - return EXIT_SUCCESS; -} - -#ifdef G_OS_UNIX -static -void -on_signal ( - int signum - ) -{ - g_message ("on_signal (signum:%d)", signum); - g_quit = TRUE; - const char one = '1'; - const size_t writelen = write (g_quit_pipe[1], &one, sizeof(one)); - g_assert (sizeof(one) == writelen); -} -#else -static -BOOL -on_console_ctrl ( - DWORD dwCtrlType - ) -{ - g_message ("on_console_ctrl (dwCtrlType:%lu)", (unsigned long)dwCtrlType); - SetEvent (g_quit_event); - return TRUE; -} -#endif /* !G_OS_UNIX */ - -static -gboolean -on_startup (void) -{ - struct pgm_addrinfo_t* res = NULL; - pgm_error_t* pgm_err = NULL; - sa_family_t sa_family = AF_UNSPEC; - - g_message ("startup."); - -/* parse network parameter into transport address structure */ - if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { - g_error ("parsing network parameter: %s", pgm_err->message); - goto err_abort; - } - - sa_family = res->ai_send_addrs[0].gsr_group.ss_family; - - if (g_udp_encap_port) { - g_message ("create PGM/UDP socket."); - if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { - g_error ("socket: %s", pgm_err->message); - goto err_abort; - } - pgm_setsockopt (g_sock, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); - pgm_setsockopt (g_sock, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); - } else { - g_message ("create PGM/IP socket."); - if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { - g_error ("socket: %s", pgm_err->message); - goto err_abort; - } - } - -/* Use RFC 2113 tagging for PGM Router Assist */ - const int no_router_assist = 0; - pgm_setsockopt (g_sock, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); - - pgm_drop_superuser(); - -/* set PGM parameters */ - const int recv_only = 1, - passive = 0, - peer_expiry = pgm_secs (300), - spmr_expiry = pgm_msecs (250), - nak_bo_ivl = pgm_msecs (50), - nak_rpt_ivl = pgm_secs (2), - nak_rdata_ivl = pgm_secs (2), - nak_data_retries = 50, - nak_ncf_retries = 50; - - pgm_setsockopt (g_sock, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); - pgm_setsockopt (g_sock, PGM_PASSIVE, &passive, sizeof(passive)); - pgm_setsockopt (g_sock, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); - pgm_setsockopt (g_sock, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); - pgm_setsockopt (g_sock, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); - pgm_setsockopt (g_sock, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); - pgm_setsockopt (g_sock, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); - pgm_setsockopt (g_sock, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); - pgm_setsockopt (g_sock, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); - -/* create global session identifier */ - struct pgm_sockaddr_t addr; - memset (&addr, 0, sizeof(addr)); - addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; - addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; - if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { - g_error ("creating GSI: %s", pgm_err->message); - goto err_abort; - } - -/* assign socket to specified address */ - struct pgm_interface_req_t if_req; - memset (&if_req, 0, sizeof(if_req)); - if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; - if_req.ir_scope_id = 0; - if (AF_INET6 == sa_family) { - struct sockaddr_in6 sa6; - memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); - if_req.ir_scope_id = sa6.sin6_scope_id; - } - if (!pgm_bind3 (g_sock, - &addr, sizeof(addr), - &if_req, sizeof(if_req), /* tx interface */ - &if_req, sizeof(if_req), /* rx interface */ - &pgm_err)) - { - g_error ("binding PGM socket: %s", pgm_err->message); - goto err_abort; - } - -/* join IP multicast groups */ - for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) - pgm_setsockopt (g_sock, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); - pgm_setsockopt (g_sock, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); - pgm_freeaddrinfo (res); - -/* set IP parameters */ - const int nonblocking = 1, - multicast_loop = g_multicast_loop ? 1 : 0, - multicast_hops = 16, - dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ - - pgm_setsockopt (g_sock, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); - pgm_setsockopt (g_sock, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); - if (AF_INET6 != sa_family) - pgm_setsockopt (g_sock, PGM_TOS, &dscp, sizeof(dscp)); - pgm_setsockopt (g_sock, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); - - if (!pgm_connect (g_sock, &pgm_err)) { - g_error ("connecting PGM socket: %s", pgm_err->message); - goto err_abort; - } - - g_message ("startup complete."); - return TRUE; - -err_abort: - if (NULL != g_sock) { - pgm_close (g_sock, FALSE); - g_sock = NULL; - } - if (NULL != res) { - pgm_freeaddrinfo (res); - res = NULL; - } - if (NULL != pgm_err) { - pgm_error_free (pgm_err); - pgm_err = NULL; - } - return FALSE; -} - -static -int -on_data ( - gconstpointer data, - size_t len, - struct pgm_sockaddr_t* from - ) -{ -/* protect against non-null terminated strings */ - char buf[1024], tsi[PGM_TSISTRLEN]; - const size_t buflen = MIN(sizeof(buf) - 1, len); - strncpy (buf, (const char*)data, buflen); - buf[buflen] = '\0'; - pgm_tsi_print_r (&from->sa_addr, tsi, sizeof(tsi)); - - g_message ("\"%s\" (%u bytes from %s)", - buf, - (unsigned)len, - tsi); - - return 0; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/fec-block.txt b/3rdparty/openpgm-svn-r1085/pgm/fec-block.txt deleted file mode 100644 index 8278fb6..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/fec-block.txt +++ /dev/null @@ -1,66 +0,0 @@ -from rfc5052 ------------- - - -step one: - - Input: - - B -- Maximum Source Block Length, i.e., the maximum number of source - symbols per source block - - L -- Transfer Length in octets - - E -- Encoding Symbol Length in octets - - Output: - - T -- the number of source symbols in the object. - - N -- the number of source blocks into which the object shall be - partitioned. - - Algorithm: - - 1. The number of source symbols in the transport object is computed - as T = ceil[L/E]. - - 2. The transport object shall be partitioned into N = ceil[T/B] - source blocks. - - -B = maximum TPDU - IP header ( - UDP header ) - PGM header -L = APDU length (pgm_transport_send length parameter). -E = 1 (fixed). - -T = ceil( L / E ) = ceil( L / 1 ) = L -N = ceil( T / B ) = ceil( L / B ) - -step two: - - Input: - - T -- the number of source symbols in the object. - - N -- the number of source blocks into which the object is - partitioned. - - Output: - - I -- the number of larger source blocks. - - A_large -- the length of each of the larger source blocks in - symbols. - - A_small -- the length of each of the smaller source blocks in - symbols. - - Algorithm: - - 1. A_large = ceil[T/N] - - 2. A_small = floor[T/N] - - 3. I = T - A_small * N - - diff --git a/3rdparty/openpgm-svn-r1085/pgm/fec.txt b/3rdparty/openpgm-svn-r1085/pgm/fec.txt deleted file mode 100644 index b83590d..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/fec.txt +++ /dev/null @@ -1,77 +0,0 @@ -pkt:k=1 - -non-parity - -rs eqn: - -n = 255 -k = 2t -255 = 2k -k = 128 -=> 2t = 128 - --------------------------------------------------------------------------------- - -1456, 1442 - -pkt:k=2 ( 2 data packets ) - -1 packet loss: - -pkt:h=1 -pkt:n=3 - -rs eqn: - -n = 255 -# reed-solomon codes = tsdu / n -k = 2 4 6 ... 254 -=> 2t = 1 2 3 127 (k/2) - -255 = k + (k/2) -510 = 2k + k -510 = 3k -170 = k -=> 2t = 85 - -2 packet loss: - -pkt:h=2 pkts -pkt:n=4 pkts - --------------------------------------------------------------------------------- - -pkt:k=4 ( 4 data packets ) - -1 packet loss: - -255 = k + (k/4) -k = 51 -2t = 13 (rounded up) - --------------------------------------------------------------------------------- - -pkt:k=8 ( 8 data packets ) - -1 packet loss: - -255 = k + (k/8) -k = 29 -2t = 4 (rounded up) - --------------------------------------------------------------------------------- - -pkt:k=16 ( 16 data packets ) - -1 packet loss: - -255 = k + (k/16) -k = 15 -2t = 1 (invalid?) - -2 packet loss: - -255 = k + (2k/16) -k = 29 -2t = 4 - diff --git a/3rdparty/openpgm-svn-r1085/pgm/galois_generator.pl b/3rdparty/openpgm-svn-r1085/pgm/galois_generator.pl deleted file mode 100755 index b3f531d..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/galois_generator.pl +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/perl -# -# Galois field table generator. -# -# Copyright (c) 2006-2010 Miru Limited. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -use strict; - -my $GF_ELEMENT_BYTES = 1; -my $GF_ELEMENT_BITS = 8 * $GF_ELEMENT_BYTES; -my $GF_NO_ELEMENTS = 1 << $GF_ELEMENT_BITS; -my $GF_MAX = $GF_NO_ELEMENTS - 1; - -my $GF_GENERATOR = 0x11d; - -my @gflog; -my @gfantilog; - -my $j = 1; - -for (my $i = 0; $i < $GF_MAX; $i++) -{ - $gflog[ $j ] = $i; - $gfantilog[ $i ] = $j; - - $j <<= 1; - if ($j & $GF_NO_ELEMENTS) { - $j ^= $GF_GENERATOR; - } -} - -$gflog[ 0 ] = $GF_MAX; -$gfantilog[ $GF_MAX ] = 0; - -print< - - -/* globals */ - -const pgm_gf8_t pgm_gflog[PGM_GF_NO_ELEMENTS] = -{ -MOO - -# print out y = log₂(x) table -for (my $i = 0; $i < $GF_NO_ELEMENTS; $i++) -{ - print "\t" if ($i % 8 == 0); - print sprintf("0x%2.2x", $gflog[ $i ]); - print ',' unless ($i == $GF_MAX); - print ( (($i % 8) == 7) ? "\n" : ' ' ); -} - -print<= $GF_MAX) ? $gfantilog[ $sum - $GF_MAX ] : $gfantilog[ $sum ]; -} - -# print out multiplication table z = x • y -for (my $i = 0; $i < $GF_NO_ELEMENTS; $i++) -{ - for (my $j = 0; $j < $GF_NO_ELEMENTS; $j++) - { - print "\t" if ($j % 8 == 0); - print sprintf("0x%2.2x", gfmul( $i, $j )); - print ',' unless ($i == $GF_MAX && $j == $GF_MAX); - print ( (($j % 8) == 7) ? "\n" : ' ' ); - } -} - -print<) { - chomp; - if (/^(Function|File) '(.+)'/) { - $type = $1; - $target = $2; - } elsif (/^Lines executed:(\d+\.\d+)% of (\d+)/) { -# print "$type,$target,$1,$2\n"; - if ($type cmp 'File') { - $files{$target} = $1; - } else { - $functions{$target} = $1; - } - } -} - -#@sorted = sort { $files{$a} <=> $files{$b} } keys %files; -#foreach $name (@sorted) -#{ -# print "$name:$files{$name}\n"; -#} -@sorted = sort { $functions{$a} <=> $functions{$b} } keys %functions; -$total = 0; -$count = 0; -foreach $name (@sorted) -{ - next if $name =~ m#^/#; - next if $name =~ m#.+\.h$#; - next if $name =~ m#_unittest\.c$#; - print sprintf("%20s: %3.1f%%\n", $name, $functions{$name}); - $total += $functions{$name}; - $count++; -} -$total /= $count; -print "\n TOTAL: ~" . int($total) . "%\n\n"; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/gcov-seed.sh b/3rdparty/openpgm-svn-r1085/pgm/gcov-seed.sh deleted file mode 100755 index 853fbe6..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/gcov-seed.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -./ref/debug/async_unittest -./ref/debug/checksum_unittest -./ref/debug/getifaddrs_unittest -./ref/debug/getnodeaddr_unittest -./ref/debug/gsi_unittest -./ref/debug/http_unittest -./ref/debug/if_unittest -./ref/debug/indextoaddr_unittest -./ref/debug/inet_network_unittest -./ref/debug/md5_unittest -./ref/debug/net_unittest -./ref/debug/packet_unittest -./ref/debug/pgmMIB_unittest -./ref/debug/pgm_unittest -./ref/debug/rate_control_unittest -./ref/debug/receiver_unittest -./ref/debug/recv_unittest -./ref/debug/reed_solomon_unittest -./ref/debug/rxwi_unittest -./ref/debug/signal_unittest -./ref/debug/snmp_unittest -./ref/debug/source_unittest -./ref/debug/timer_unittest -./ref/debug/time_unittest -user=`id -nu` -group=`id -ng` -sudo execcap 'cap_net_raw=ep' /sbin/sucap $user $group ./ref/debug/transport_unittest -sudo find ref/debug/ -user 0 -exec chown $user:$group {} \; -./ref/debug/tsi_unittest -./ref/debug/txwi_unittest - diff --git a/3rdparty/openpgm-svn-r1085/pgm/gcov.sh b/3rdparty/openpgm-svn-r1085/pgm/gcov.sh deleted file mode 100755 index ca17d62..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/gcov.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash - -gcov -fno ref/debug async_unittest.c -gcov -fno ref/debug checksum_unittest.c -gcov -fno ref/debug getifaddrs_unittest.c -gcov -fno ref/debug getnodeaddr_unittest.c -gcov -fno ref/debug gsi_unittest.c -gcov -fno ref/debug http_unittest.c -gcov -fno ref/debug if_unittest.c -gcov -fno ref/debug indextoaddr_unittest.c -gcov -fno ref/debug inet_network_unittest.c -gcov -fno ref/debug md5_unittest.c -gcov -fno ref/debug net_unittest.c -gcov -fno ref/debug packet_unittest.c -gcov -fno ref/debug pgmMIB_unittest.c -gcov -fno ref/debug pgm_unittest.c -gcov -fno ref/debug rate_control_unittest.c -gcov -fno ref/debug receiver_unittest.c -gcov -fno ref/debug recv_unittest.c -gcov -fno ref/debug reed_solomon_unittest.c -gcov -fno ref/debug rxwi_unittest.c -gcov -fno ref/debug signal_unittest.c -gcov -fno ref/debug snmp_unittest.c -gcov -fno ref/debug source_unittest.c -gcov -fno ref/debug timer_unittest.c -gcov -fno ref/debug time_unittest.c -gcov -fno ref/debug tsi_unittest.c -gcov -fno ref/debug txwi_unittest.c - diff --git a/3rdparty/openpgm-svn-r1085/pgm/getifaddrs.c b/3rdparty/openpgm-svn-r1085/pgm/getifaddrs.c deleted file mode 100644 index 9db1d86..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/getifaddrs.c +++ /dev/null @@ -1,840 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable getifaddrs implementation. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#ifdef CONFIG_HAVE_GETIFADDRS -# include -# include -#endif -#if defined( sun ) -# include -#endif -#if defined( _WIN32 ) -# include -# include -#endif -#include -#include - - -//#define GETIFADDRS_DEBUG - -/* locals */ -struct _pgm_ifaddrs_t -{ - struct pgm_ifaddrs_t _ifa; - char _name[IF_NAMESIZE]; - struct sockaddr_storage _addr; - struct sockaddr_storage _netmask; -}; - -/* Number of attempts to try enumerating the interface list */ -#define MAX_TRIES 3 -#define DEFAULT_BUFFER_SIZE 4096 - -/* returns TRUE on success setting ifap to a linked list of system interfaces, - * returns FALSE on failure and sets error appropriately. - */ - -#ifdef SIOCGLIFCONF -static -bool -_pgm_getlifaddrs ( - struct pgm_ifaddrs_t** restrict ifap, - pgm_error_t** restrict error - ) -{ - const int sock = socket (AF_INET, SOCK_DGRAM, 0); - if (-1 == sock) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - pgm_error_from_errno (errno), - _("Opening IPv4 datagram socket: %s"), - strerror (errno)); - return FALSE; - } - -/* process IPv6 interfaces */ - const int sock6 = socket (AF_INET6, SOCK_DGRAM, 0); - if (-1 == sock6) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - pgm_error_from_errno (errno), - _("Opening IPv6 datagram socket: %s"), - strerror (errno)); - close (sock); - return FALSE; - } - - struct lifnum lifn; -again: - lifn.lifn_family = AF_INET; - lifn.lifn_flags = 0; - if (-1 == ioctl (sock, SIOCGLIFNUM, &lifn)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - pgm_error_from_errno (errno), - _("SIOCGLIFNUM failed on IPv4 socket: %s"), - strerror (errno)); - close (sock); - close (sock6); - return FALSE; - } - unsigned if_count = lifn.lifn_count; - pgm_debug ("ioctl(AF_INET, SIOCGLIFNUM) = %d", lifn.lifn_count); - -/* nb: Sun and Apple code likes to pad the interface count here in case interfaces - * are coming up between calls, - */ - lifn.lifn_count += 4; - -/* process all interfaces with family-agnostic ioctl, unfortunately it still - * must be called on each family socket despite what if_tcp(7P) says. - */ - struct lifconf lifc, lifc6; - lifc.lifc_family = AF_INET; - lifc.lifc_flags = 0; - lifc.lifc_len = lifn.lifn_count * sizeof(struct lifreq); - lifc.lifc_buf = alloca (lifc.lifc_len); - if (-1 == ioctl (sock, SIOCGLIFCONF, &lifc)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - pgm_error_from_errno (errno), - _("SIOCGLIFCONF failed on IPv4 socket: %s"), - strerror (errno)); - close (sock); - close (sock6); - return FALSE; - } - pgm_debug ("ioctl(AF_INET, SIOCGLIFCONF) = %d (%d)", lifc.lifc_len, (int)(lifc.lifc_len / sizeof(struct lifreq))); - -/* repeat everything for IPv6 */ - lifn.lifn_family = AF_INET6; - lifn.lifn_flags = 0; - if (-1 == ioctl (sock6, SIOCGLIFNUM, &lifn)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - pgm_error_from_errno (errno), - _("SIOCGLIFNUM failed on IPv6 socket: %s"), - strerror (errno)); - close (sock); - close (sock6); - return FALSE; - } - if_count += lifn.lifn_count; - pgm_debug ("ioctl(AF_INET6, SIOCGLIFNUM) = %d", lifn.lifn_count); - - lifn.lifn_count += 4; - - lifc6.lifc_family = AF_INET6; - lifc6.lifc_flags = 0; - lifc6.lifc_len = lifn.lifn_count * sizeof(struct lifreq); - lifc6.lifc_buf = alloca (lifc6.lifc_len); - if (-1 == ioctl (sock6, SIOCGLIFCONF, &lifc6)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - pgm_error_from_errno (errno), - _("SIOCGLIFCONF failed on IPv6 socket: %s"), - strerror (errno)); - close (sock); - close (sock6); - return FALSE; - } - pgm_debug ("ioctl(AF_INET6, SIOCGLIFCONF) = %d (%d)", lifc6.lifc_len, (int)(lifc6.lifc_len / sizeof(struct lifreq))); - - unsigned nlifr = (lifc.lifc_len + lifc6.lifc_len) / sizeof(struct lifreq); - if (nlifr > if_count) - goto again; - -/* alloc a contiguous block for entire list */ - struct _pgm_ifaddrs_t* ifa = calloc (nlifr, sizeof (struct _pgm_ifaddrs_t)); - pgm_assert (NULL != ifa); - - struct _pgm_ifaddrs_t* ift = ifa; - struct lifreq* lifr = lifc.lifc_req; - struct lifreq* lifr_end = (struct lifreq *)&lifc.lifc_buf[lifc.lifc_len]; - - pgm_assert (IF_NAMESIZE >= LIFNAMSIZ); - - while (lifr < lifr_end) - { -/* name */ - pgm_debug ("AF_INET/name: %s", lifr->lifr_name ? lifr->lifr_name : "(null)"); - ift->_ifa.ifa_name = ift->_name; - strncpy (ift->_ifa.ifa_name, lifr->lifr_name, LIFNAMSIZ); - ift->_ifa.ifa_name[LIFNAMSIZ - 1] = 0; - -/* flags */ - if (-1 != ioctl (sock, SIOCGLIFFLAGS, lifr)) { - ift->_ifa.ifa_flags = lifr->lifr_flags; - } else { - pgm_warn (_("SIOCGLIFFLAGS failed on interface %s%s%s"), - lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); - } - -/* address */ - if (-1 != ioctl (sock, SIOCGLIFADDR, lifr)) { - ift->_ifa.ifa_addr = (void*)&ift->_addr; - memcpy (ift->_ifa.ifa_addr, &lifr->lifr_addr, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_addr)); - } else { - pgm_warn (_("SIOCGLIFADDR failed on interface %s%s%s"), - lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); - } - -/* netmask */ - if (-1 != ioctl (sock, SIOCGLIFNETMASK, lifr)) { - ift->_ifa.ifa_netmask = (void*)&ift->_netmask; -# ifdef CONFIG_HAVE_IFR_NETMASK - memcpy (ift->_ifa.ifa_netmask, &lifr->lifr_netmask, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_netmask)); -# else - memcpy (ift->_ifa.ifa_netmask, &lifr->lifr_addr, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_addr)); -# endif - } else { - pgm_warn (_("SIOCGLIFNETMASK failed on interface %s%s%s"), - lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); - } - - ++lifr; - if (lifr < lifr_end) { - ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); - ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); - } - } - -/* repeat everything for IPv6 */ - lifr = lifc6.lifc_req; - lifr_end = (struct lifreq *)&lifc6.lifc_buf[lifc6.lifc_len]; - - while (lifr < lifr_end) - { - if (ift != ifa) { - ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); - ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); - } - -/* name */ - pgm_debug ("AF_INET6/name: %s", lifr->lifr_name ? lifr->lifr_name : "(null)"); - ift->_ifa.ifa_name = ift->_name; - strncpy (ift->_ifa.ifa_name, lifr->lifr_name, sizeof(lifr->lifr_name)); - ift->_ifa.ifa_name[sizeof(lifr->lifr_name) - 1] = 0; - -/* flags */ - if (-1 != ioctl (sock6, SIOCGLIFFLAGS, lifr)) { - ift->_ifa.ifa_flags = lifr->lifr_flags; - } else { - pgm_warn (_("SIOCGLIFFLAGS failed on interface %s%s%s"), - lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); - } - -/* address */ - if (-1 != ioctl (sock6, SIOCGLIFADDR, lifr)) { - ift->_ifa.ifa_addr = (void*)&ift->_addr; - memcpy (ift->_ifa.ifa_addr, &lifr->lifr_addr, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_addr)); - } else { - pgm_warn (_("SIOCGLIFADDR failed on interface %s%s%s"), - lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); - } - -/* netmask */ - if (ioctl (sock6, SIOCGLIFNETMASK, lifr) != -1) { - ift->_ifa.ifa_netmask = (void*)&ift->_netmask; -# ifdef CONFIG_HAVE_IFR_NETMASK - memcpy (ift->_ifa.ifa_netmask, &lifr->lifr_netmask, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_netmask)); -# else - memcpy (ift->_ifa.ifa_netmask, &lifr->lifr_addr, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_addr)); -# endif - } else { - pgm_warn (_("SIOCGLIFNETMASK failed on interface %s%s%s"), - lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); - } - - ++lifr; - } - - if (-1 == close (sock6)) - pgm_warn (_("Closing IPv6 socket failed: %s"), strerror(errno)); - if (-1 == close (sock)) - pgm_warn (_("Closing IPv4 socket failed: %s"), strerror(errno)); - - *ifap = (struct pgm_ifaddrs_t*)ifa; - return TRUE; -} -#endif /* SIOCGLIFCONF */ - -#ifdef SIOCGIFCONF -static -bool -_pgm_getifaddrs ( - struct pgm_ifaddrs_t** restrict ifap, - pgm_error_t** restrict error - ) -{ - const int sock = socket (AF_INET, SOCK_DGRAM, 0); - if (-1 == sock) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - pgm_error_from_errno (errno), - _("Opening IPv4 datagram socket: %s"), - strerror (errno)); - return FALSE; - } - -/* process IPv4 interfaces */ - char buf[ DEFAULT_BUFFER_SIZE ]; - struct ifconf ifc; - ifc.ifc_buf = buf; - ifc.ifc_len = sizeof(buf); - if (-1 == ioctl (sock, SIOCGIFCONF, &ifc)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - pgm_error_from_errno (errno), - _("SIOCGIFCONF failed on IPv4 socket: %s"), - strerror (errno)); - close (sock); - return FALSE; - } - int if_count = ifc.ifc_len / sizeof(struct ifreq); - -# ifdef CONFIG_HAVE_IPV6_SIOCGIFADDR -/* process IPv6 interfaces */ - const int sock6 = socket (AF_INET6, SOCK_DGRAM, 0); - if (-1 == sock6) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - pgm_error_from_errno (errno), - _("Opening IPv6 datagram socket: %s"), - strerror (errno)); - close (sock); - return FALSE; - } - - char buf6[1024]; - struct ifconf ifc6; - ifc6.ifc_buf = buf6; - ifc6.ifc_len = sizeof(buf6); - if (-1 == ioctl (sock6, SIOCGIFCONF, &ifc6)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - pgm_error_from_errno (errno), - _("SIOCGIFCONF failed on IPv6 socket: %s"), - strerror (errno)); - close (sock); - close (sock6); - return FALSE; - } - if_count += ifc6.ifc_len / sizeof(struct ifreq); -# endif /* CONFIG_HAVE_IPV6_SIOCGIFADDR */ - -/* alloc a contiguous block for entire list */ - struct _pgm_ifaddrs_t* ifa = pgm_new0 (struct _pgm_ifaddrs_t, if_count); - struct _pgm_ifaddrs_t* ift = ifa; - struct ifreq *ifr = ifc.ifc_req; - struct ifreq *ifr_end = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len]; - - pgm_assert (IF_NAMESIZE >= sizeof(ifr->ifr_name)); - - while (ifr < ifr_end) - { -/* name */ - pgm_debug ("AF_INET/name:%s", ifr->ifr_name ? ifr->ifr_name : "(null)"); - ift->_ifa.ifa_name = ift->_name; - strncpy (ift->_ifa.ifa_name, ifr->ifr_name, sizeof(ifr->ifr_name)); - ift->_ifa.ifa_name[sizeof(ifr->ifr_name) - 1] = 0; - -/* flags */ - if (-1 != ioctl (sock, SIOCGIFFLAGS, ifr)) { - ift->_ifa.ifa_flags = ifr->ifr_flags; - } else { - pgm_warn (_("SIOCGIFFLAGS failed on interface %s%s%s"), - ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); - } - -/* address */ - if (-1 != ioctl (sock, SIOCGIFADDR, ifr)) { - ift->_ifa.ifa_addr = (void*)&ift->_addr; - memcpy (ift->_ifa.ifa_addr, &ifr->ifr_addr, pgm_sockaddr_len(&ifr->ifr_addr)); - } else { - pgm_warn (_("SIOCGIFADDR failed on interface %s%s%s"), - ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); - } - -/* netmask */ - if (-1 != ioctl (sock, SIOCGIFNETMASK, ifr)) { - ift->_ifa.ifa_netmask = (void*)&ift->_netmask; -# ifdef CONFIG_HAVE_IFR_NETMASK - memcpy (ift->_ifa.ifa_netmask, &ifr->ifr_netmask, pgm_sockaddr_len(&ifr->ifr_netmask)); -# else - memcpy (ift->_ifa.ifa_netmask, &ifr->ifr_addr, pgm_sockaddr_len(&ifr->ifr_addr)); -# endif - } else { - pgm_warn (_("SIOCGIFNETMASK failed on interface %s%s%s"), - ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); - } - - ++ifr; - if (ifr < ifr_end) { - ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); - ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); - } - } - -# ifdef CONFIG_HAVE_IPV6_SIOCGIFADDR -/* repeat everything for IPv6 */ - ifr = ifc6.ifc_req; - ifr_end = (struct ifreq *)&ifc6.ifc_buf[ifc6.ifc_len]; - - while (ifr < ifr_end) - { - if (ift != ifa) { - ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); - ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); - } - -/* name */ - pgm_debug ("AF_INET6/name:%s", ifr->ifr_name ? ifr->ifr_name : "(null)"); - ift->_ifa.ifa_name = ift->_name; - strncpy (ift->_ifa.ifa_name, ifr->ifr_name, sizeof(ifr->ifr_name)); - ift->_ifa.ifa_name[sizeof(ifr->ifr_name) - 1] = 0; - -/* flags */ - if (-1 != ioctl (sock6, SIOCGIFFLAGS, ifr)) { - ift->_ifa.ifa_flags = ifr->ifr_flags; - } else { - pgm_warn (_("SIOCGIFFLAGS failed on interface %s%s%s"), - ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); - } - -/* address, note this does not work on Linux as struct ifreq is too small for an IPv6 address */ - if (-1 != ioctl (sock6, SIOCGIFADDR, ifr)) { - ift->_ifa.ifa_addr = (void*)&ift->_addr; - memcpy (ift->_ifa.ifa_addr, &ifr->ifr_addr, pgm_sockaddr_len(&ifr->ifr_addr)); - } else { - pgm_warn (_("SIOCGIFADDR failed on interface %s%s%s"), - ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); - } - -/* netmask */ - if (-1 != ioctl (sock6, SIOCGIFNETMASK, ifr)) { - ift->_ifa.ifa_netmask = (void*)&ift->_netmask; -# ifdef CONFIG_HAVE_IFR_NETMASK - memcpy (ift->_ifa.ifa_netmask, &ifr->ifr_netmask, pgm_sockaddr_len(&ifr->ifr_netmask)); -# else - memcpy (ift->_ifa.ifa_netmask, &ifr->ifr_addr, pgm_sockaddr_len(&ifr->ifr_addr)); -# endif - } else { - pgm_warn (_("SIOCGIFNETMASK failed on interface %s%s%s"), - ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); - } - - ++ifr; - } - - if (-1 == close (sock6)) - pgm_warn (_("Closing IPv6 socket failed: %s"), strerror(errno)); -# endif /* CONFIG_HAVE_IPV6_SIOCGIFADDR */ - - if (-1 == close (sock)) - pgm_warn (_("Closing IPv4 socket failed: %s"), strerror(errno)); - - *ifap = (struct pgm_ifaddrs_t*)ifa; - return TRUE; -} -#endif /* SIOCLIFCONF */ - -#if defined(_WIN32) -static inline -void* -_pgm_heap_alloc ( - const size_t n_bytes - ) -{ -# ifdef CONFIG_USE_HEAPALLOC -/* Does not appear very safe with re-entrant calls on XP */ - return HeapAlloc (GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, n_bytes); -# else - return pgm_malloc (n_bytes); -# endif -} - -static inline -void -_pgm_heap_free ( - void* mem - ) -{ -# ifdef CONFIG_USE_HEAPALLOC - HeapFree (GetProcessHeap(), 0, mem); -# else - pgm_free (mem); -# endif -} - -/* NB: IP_ADAPTER_INFO size varies size due to sizeof (time_t), the API assumes - * 4-byte datatype whilst compiler uses an 8-byte datatype. Size can be forced - * with -D_USE_32BIT_TIME_T with side effects to everything else. - */ - -static -bool -_pgm_getadaptersinfo ( - struct pgm_ifaddrs_t** restrict ifap, - pgm_error_t** restrict error - ) -{ - DWORD dwRet; - ULONG ulOutBufLen = DEFAULT_BUFFER_SIZE; - PIP_ADAPTER_INFO pAdapterInfo = NULL; - PIP_ADAPTER_INFO pAdapter = NULL; - -/* loop to handle interfaces coming online causing a buffer overflow - * between first call to list buffer length and second call to enumerate. - */ - for (unsigned i = MAX_TRIES; i; i--) - { - pgm_debug ("IP_ADAPTER_INFO buffer length %lu bytes.", ulOutBufLen); - pAdapterInfo = (IP_ADAPTER_INFO*)_pgm_heap_alloc (ulOutBufLen); - dwRet = GetAdaptersInfo (pAdapterInfo, &ulOutBufLen); - if (ERROR_BUFFER_OVERFLOW == dwRet) { - _pgm_heap_free (pAdapterInfo); - pAdapterInfo = NULL; - } else { - break; - } - } - - switch (dwRet) { - case ERROR_SUCCESS: /* NO_ERROR */ - break; - case ERROR_BUFFER_OVERFLOW: - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_NOBUFS, - _("GetAdaptersInfo repeatedly failed with ERROR_BUFFER_OVERFLOW.")); - if (pAdapterInfo) - _pgm_heap_free (pAdapterInfo); - return FALSE; - default: - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - pgm_error_from_win_errno (dwRet), - _("GetAdaptersInfo failed: %s"), - pgm_adapter_strerror (dwRet)); - if (pAdapterInfo) - _pgm_heap_free (pAdapterInfo); - return FALSE; - } - -/* count valid adapters */ - int n = 0, k = 0; - for (pAdapter = pAdapterInfo; - pAdapter; - pAdapter = pAdapter->Next) - { - for (IP_ADDR_STRING *pIPAddr = &pAdapter->IpAddressList; - pIPAddr; - pIPAddr = pIPAddr->Next) - { -/* skip null adapters */ - if (strlen (pIPAddr->IpAddress.String) == 0) - continue; - ++n; - } - } - - pgm_debug ("GetAdaptersInfo() discovered %d interfaces.", n); - -/* contiguous block for adapter list */ - struct _pgm_ifaddrs_t* ifa = pgm_new0 (struct _pgm_ifaddrs_t, n); - struct _pgm_ifaddrs_t* ift = ifa; - -/* now populate list */ - for (pAdapter = pAdapterInfo; - pAdapter; - pAdapter = pAdapter->Next) - { - for (IP_ADDR_STRING *pIPAddr = &pAdapter->IpAddressList; - pIPAddr; - pIPAddr = pIPAddr->Next) - { -/* skip null adapters */ - if (strlen (pIPAddr->IpAddress.String) == 0) - continue; - -/* address */ - ift->_ifa.ifa_addr = (void*)&ift->_addr; - pgm_assert (pgm_sockaddr_pton (pIPAddr->IpAddress.String, ift->_ifa.ifa_addr)); - -/* name */ - pgm_debug ("name:%s IPv4 index:%lu", - pAdapter->AdapterName, pAdapter->Index); - ift->_ifa.ifa_name = ift->_name; - strncpy (ift->_ifa.ifa_name, pAdapter->AdapterName, IF_NAMESIZE); - ift->_ifa.ifa_name[IF_NAMESIZE - 1] = 0; - -/* flags: assume up, broadcast and multicast */ - ift->_ifa.ifa_flags = IFF_UP | IFF_BROADCAST | IFF_MULTICAST; - if (pAdapter->Type == MIB_IF_TYPE_LOOPBACK) - ift->_ifa.ifa_flags |= IFF_LOOPBACK; - -/* netmask */ - ift->_ifa.ifa_netmask = (void*)&ift->_netmask; - pgm_assert (pgm_sockaddr_pton (pIPAddr->IpMask.String, ift->_ifa.ifa_netmask)); - -/* next */ - if (k++ < (n - 1)) { - ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); - ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); - } - } - } - - if (pAdapterInfo) - free (pAdapterInfo); - *ifap = (struct pgm_ifaddrs_t*)ifa; - return TRUE; -} - -static -bool -_pgm_getadaptersaddresses ( - struct pgm_ifaddrs_t** restrict ifap, - pgm_error_t** restrict error - ) -{ - DWORD dwSize = DEFAULT_BUFFER_SIZE, dwRet; - IP_ADAPTER_ADDRESSES *pAdapterAddresses = NULL, *adapter; - -/* loop to handle interfaces coming online causing a buffer overflow - * between first call to list buffer length and second call to enumerate. - */ - for (unsigned i = MAX_TRIES; i; i--) - { - pgm_debug ("IP_ADAPTER_ADDRESSES buffer length %lu bytes.", dwSize); - pAdapterAddresses = (IP_ADAPTER_ADDRESSES*)_pgm_heap_alloc (dwSize); - dwRet = GetAdaptersAddresses (AF_UNSPEC, - GAA_FLAG_INCLUDE_PREFIX | - GAA_FLAG_SKIP_ANYCAST | - GAA_FLAG_SKIP_DNS_SERVER | - GAA_FLAG_SKIP_FRIENDLY_NAME | - GAA_FLAG_SKIP_MULTICAST, - NULL, - pAdapterAddresses, - &dwSize); - if (ERROR_BUFFER_OVERFLOW == dwRet) { - _pgm_heap_free (pAdapterAddresses); - pAdapterAddresses = NULL; - } else { - break; - } - } - - switch (dwRet) { - case ERROR_SUCCESS: - break; - case ERROR_BUFFER_OVERFLOW: - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_NOBUFS, - _("GetAdaptersAddresses repeatedly failed with ERROR_BUFFER_OVERFLOW.")); - if (pAdapterAddresses) - free (pAdapterAddresses); - return FALSE; - default: - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - pgm_error_from_win_errno (dwRet), - _("GetAdaptersAddresses failed: %s"), - pgm_adapter_strerror (dwRet)); - if (pAdapterAddresses) - free (pAdapterAddresses); - return FALSE; - } - -/* count valid adapters */ - int n = 0, k = 0; - for (adapter = pAdapterAddresses; - adapter; - adapter = adapter->Next) - { - for (IP_ADAPTER_UNICAST_ADDRESS *unicast = adapter->FirstUnicastAddress; - unicast; - unicast = unicast->Next) - { -/* ensure IP adapter */ - if (AF_INET != unicast->Address.lpSockaddr->sa_family && - AF_INET6 != unicast->Address.lpSockaddr->sa_family) - { - continue; - } - - ++n; - } - } - -/* contiguous block for adapter list */ - struct _pgm_ifaddrs_t* ifa = pgm_new0 (struct _pgm_ifaddrs_t, n); - struct _pgm_ifaddrs_t* ift = ifa; - -/* now populate list */ - for (adapter = pAdapterAddresses; - adapter; - adapter = adapter->Next) - { - int unicastIndex = 0; - for (IP_ADAPTER_UNICAST_ADDRESS *unicast = adapter->FirstUnicastAddress; - unicast; - unicast = unicast->Next, ++unicastIndex) - { -/* ensure IP adapter */ - if (AF_INET != unicast->Address.lpSockaddr->sa_family && - AF_INET6 != unicast->Address.lpSockaddr->sa_family) - { - continue; - } - -/* address */ - ift->_ifa.ifa_addr = (void*)&ift->_addr; - memcpy (ift->_ifa.ifa_addr, unicast->Address.lpSockaddr, unicast->Address.iSockaddrLength); - -/* name */ - pgm_debug ("name:%s IPv4 index:%lu IPv6 index:%lu", - adapter->AdapterName, adapter->IfIndex, adapter->Ipv6IfIndex); - ift->_ifa.ifa_name = ift->_name; - strncpy (ift->_ifa.ifa_name, adapter->AdapterName, IF_NAMESIZE); - ift->_ifa.ifa_name[IF_NAMESIZE - 1] = 0; - -/* flags */ - ift->_ifa.ifa_flags = 0; - if (IfOperStatusUp == adapter->OperStatus) - ift->_ifa.ifa_flags |= IFF_UP; - if (IF_TYPE_SOFTWARE_LOOPBACK == adapter->IfType) - ift->_ifa.ifa_flags |= IFF_LOOPBACK; - if (!(adapter->Flags & IP_ADAPTER_NO_MULTICAST)) - ift->_ifa.ifa_flags |= IFF_MULTICAST; - -/* netmask */ - ift->_ifa.ifa_netmask = (void*)&ift->_netmask; - -/* pre-Vista must hunt for matching prefix in linked list, otherwise use OnLinkPrefixLength */ - int prefixIndex = 0; - ULONG prefixLength = 0; - for (IP_ADAPTER_PREFIX *prefix = adapter->FirstPrefix; - prefix; - prefix = prefix->Next, ++prefixIndex) - { - if (prefixIndex == unicastIndex) { - prefixLength = prefix->PrefixLength; - break; - } - } - -/* map prefix to netmask */ - ift->_ifa.ifa_netmask->sa_family = unicast->Address.lpSockaddr->sa_family; - switch (unicast->Address.lpSockaddr->sa_family) { - case AF_INET: - if (0 == prefixLength) { - pgm_warn (_("IPv4 adapter %s prefix length is 0, overriding to 32."), adapter->AdapterName); - prefixLength = 32; - } - ((struct sockaddr_in*)ift->_ifa.ifa_netmask)->sin_addr.s_addr = htonl( 0xffffffffU << ( 32 - prefixLength ) ); - break; - - case AF_INET6: - if (0 == prefixLength) { - pgm_warn (_("IPv6 adapter %s prefix length is 0, overriding to 128."), adapter->AdapterName); - prefixLength = 128; - } - for (ULONG i = prefixLength, j = 0; i > 0; i -= 8, ++j) - { - ((struct sockaddr_in6*)ift->_ifa.ifa_netmask)->sin6_addr.s6_addr[ j ] = i >= 8 ? 0xff : (ULONG)(( 0xffU << ( 8 - i ) ) & 0xffU ); - } - break; - } - -/* next */ - if (k++ < (n - 1)) { - ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); - ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); - } - } - } - - if (pAdapterAddresses) - free (pAdapterAddresses); - *ifap = (struct pgm_ifaddrs_t*)ifa; - return TRUE; -} -#endif /* _WIN32 */ - -/* returns TRUE on success setting ifap to a linked list of system interfaces, - * returns FALSE on failure and sets error appropriately. - */ - -bool -pgm_getifaddrs ( - struct pgm_ifaddrs_t** restrict ifap, - pgm_error_t** restrict error - ) -{ - pgm_assert (NULL != ifap); - - pgm_debug ("pgm_getifaddrs (ifap:%p error:%p)", - (void*)ifap, (void*)error); - -#ifdef CONFIG_HAVE_GETIFADDRS - const int e = getifaddrs ((struct ifaddrs**)ifap); - if (-1 == e) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - pgm_error_from_errno (errno), - _("getifaddrs failed: %s"), - strerror (errno)); - return FALSE; - } - return TRUE; -#elif defined(CONFIG_TARGET_WINE) - return _pgm_getadaptersinfo (ifap, error); -#elif defined(_WIN32) - return _pgm_getadaptersaddresses (ifap, error); -#elif defined(SIOCGLIFCONF) - return _pgm_getlifaddrs (ifap, error); -#elif defined(SIOCGIFCONF) - return _pgm_getifaddrs (ifap, error); -#else -# error "Unsupported interface enumeration on this platform." -#endif /* !CONFIG_HAVE_GETIFADDRS */ -} - -void -pgm_freeifaddrs ( - struct pgm_ifaddrs_t* ifa - ) -{ - pgm_return_if_fail (NULL != ifa); - -#ifdef CONFIG_HAVE_GETIFADDRS - freeifaddrs ((struct ifaddrs*)ifa); -#else - pgm_free (ifa); -#endif -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/getifaddrs_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/getifaddrs_unittest.c deleted file mode 100644 index 05063fe..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/getifaddrs_unittest.c +++ /dev/null @@ -1,262 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for portable getifaddrs implementation. - * - * Copyright (c) 2009-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* IFF_UP */ -#define _BSD_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -/* mock state */ - -/* mock functions for external references */ - -size_t -pgm_pkt_offset ( - const bool can_fragment, - const sa_family_t pgmcc_family /* 0 = disable */ - ) -{ - return 0; -} - - -#define GETIFADDRS_DEBUG -#include "getifaddrs.c" - - -char* -ifflags_string ( - unsigned int flags - ) -{ - static char s[1024]; - - s[0] = '\0'; - if (flags & IFF_UP) - strcat (s, "IFF_UP"); -#define IFF(flag) \ - do { \ - if (flags & flag) { \ - strcat (s, s[0] ? ("|" #flag) : (#flag)); \ - } \ - } while (0) -#ifdef IFF_BROADCAST - IFF(IFF_BROADCAST); -#endif -#ifdef IFF_DEBUG - IFF(IFF_DEBUG); -#endif -#ifdef IFF_LOOPBACK - IFF(IFF_LOOPBACK); -#endif -#ifdef IFF_POINTOPOINT - IFF(IFF_POINTOPOINT); -#endif -#ifdef IFF_RUNNING - IFF(IFF_RUNNING); -#endif -#ifdef IFF_NOARP - IFF(IFF_NOARP); -#endif -#ifdef IFF_PROMISC - IFF(IFF_PROMISC); -#endif -#ifdef IFF_NOTRAILERS - IFF(IFF_NOTRAILERS); -#endif -#ifdef IFF_ALLMULTI - IFF(IFF_ALLMULTI); -#endif -#ifdef IFF_MASTER - IFF(IFF_MASTER); -#endif -#ifdef IFF_SLAVE - IFF(IFF_SLAVE); -#endif -#ifdef IFF_MULTICAST - IFF(IFF_MULTICAST); -#endif -#ifdef IFF_PORTSEL - IFF(IFF_PORTSEL); -#endif -#ifdef IFF_AUTOMEDIA - IFF(IFF_AUTOMEDIA); -#endif -#ifdef IFF_DYNAMIC - IFF(IFF_DYNAMIC); -#endif -#ifdef IFF_LOWER_UP - IFF(IFF_LOWER_UP); -#endif -#ifdef IFF_DORMANT - IFF(IFF_DORMANT); -#endif -#ifdef IFF_ECHO - IFF(IFF_ECHO); -#endif - if (!s[0]) { - if (flags) - sprintf (s, "0x%x", flags); - else - strcpy (s, "(null)"); - } - return s; -} - -/* target: - * bool - * pgm_getifaddrs ( - * struct pgm_ifaddrs_t**restrict ifap, - * pgm_error_t**restrict error - * ) - */ - -START_TEST (test_getifaddrs_pass_001) -{ - char saddr[INET6_ADDRSTRLEN], snetmask[INET6_ADDRSTRLEN]; - struct pgm_ifaddrs_t *ifap = NULL, *ifa; - pgm_error_t* err = NULL; - fail_unless (TRUE == pgm_getifaddrs (&ifap, &err), "getifaddrs failed"); - for (ifa = ifap; ifa; ifa = ifa->ifa_next) - { - fail_unless (NULL != ifa, "invalid address"); - if (ifa->ifa_addr) { - if (AF_INET == ifa->ifa_addr->sa_family || - AF_INET6 == ifa->ifa_addr->sa_family) - pgm_sockaddr_ntop (ifa->ifa_addr, saddr, sizeof(saddr)); -#ifdef AF_PACKET - else if (AF_PACKET == ifa->ifa_addr->sa_family) - strcpy (saddr, "(AF_PACKET)"); -#endif - else - sprintf (saddr, "(AF = %d)", ifa->ifa_addr->sa_family); - } else - strcpy (saddr, "(null)"); - if (ifa->ifa_netmask) { - if (AF_INET == ifa->ifa_addr->sa_family || - AF_INET6 == ifa->ifa_addr->sa_family) - pgm_sockaddr_ntop (ifa->ifa_netmask, snetmask, sizeof(snetmask)); -#ifdef AF_PACKET - else if (AF_PACKET == ifa->ifa_addr->sa_family) - strcpy (snetmask, "(AF_PACKET)"); -#endif - else - sprintf (snetmask, "(AF = %d)", ifa->ifa_addr->sa_family); - } else - strcpy (snetmask, "(null)"); - g_message ("ifa = {" - "ifa_next = %p, " - "ifa_name = %s%s%s, " - "ifa_flags = %s, " - "ifa_addr = %s, " - "ifa_netmask = %s" - "}", - ifa->ifa_next, - ifa->ifa_name ? "\"" : "", ifa->ifa_name ? ifa->ifa_name : "(null)", ifa->ifa_name ? "\"" : "", - ifflags_string (ifa->ifa_flags), - saddr, - snetmask); - } -} -END_TEST - -START_TEST (test_getifaddrs_fail_001) -{ - fail_unless (FALSE == pgm_getifaddrs (NULL, NULL), "getifaddrs failed"); - g_message ("errno:%d", errno); -} -END_TEST - -/* target: - * void - * pgm_freeifaddrs ( - * struct pgm_ifaddrs* ifa - * ) - */ - -START_TEST (test_freeifaddrs_pass_001) -{ - struct pgm_ifaddrs_t* ifap = NULL; - pgm_error_t* err = NULL; - fail_unless (TRUE == pgm_getifaddrs (&ifap, &err), "getifaddrs failed"); - pgm_freeifaddrs (ifap); -} -END_TEST - -/* silent failure */ -START_TEST (test_freeifaddrs_pass_002) -{ - pgm_freeifaddrs (NULL); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_getifaddrs = tcase_create ("getifaddrs"); - suite_add_tcase (s, tc_getifaddrs); - tcase_add_test (tc_getifaddrs, test_getifaddrs_pass_001); - tcase_add_test_raise_signal (tc_getifaddrs, test_getifaddrs_fail_001, SIGABRT); - - TCase* tc_freeifaddrs = tcase_create ("freeifaddrs"); - suite_add_tcase (s, tc_freeifaddrs); - tcase_add_test (tc_freeifaddrs, test_freeifaddrs_pass_001); - tcase_add_test (tc_freeifaddrs, test_freeifaddrs_pass_002); - - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/getnodeaddr.c b/3rdparty/openpgm-svn-r1085/pgm/getnodeaddr.c deleted file mode 100644 index 0765c9f..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/getnodeaddr.c +++ /dev/null @@ -1,196 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable function to return the nodes IP address. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#ifndef _WIN32 -# include -#endif -#include -#include - - -//#define GETNODEADDR_DEBUG - - -/* globals */ - -static const char* pgm_family_string (const sa_family_t); - - -/* return node primary address on multi-address family interfaces. - * - * returns TRUE on success, returns FALSE on failure. - */ - -bool -pgm_if_getnodeaddr ( - const sa_family_t family, /* requested address family, AF_INET, AF_INET6, or AF_UNSPEC */ - struct sockaddr* restrict addr, - const socklen_t cnt, /* size of address pointed to by addr */ - pgm_error_t** restrict error - ) -{ - pgm_return_val_if_fail (AF_INET == family || AF_INET6 == family || AF_UNSPEC == family, FALSE); - pgm_return_val_if_fail (NULL != addr, FALSE); - if (AF_INET == family || AF_UNSPEC == family) - pgm_return_val_if_fail (cnt >= sizeof(struct sockaddr_in), FALSE); - else - pgm_return_val_if_fail (cnt >= sizeof(struct sockaddr_in6), FALSE); - - pgm_debug ("pgm_if_getnodeaddr (family:%s addr:%p cnt:%d error:%p)", - pgm_family_string (family), (const void*)addr, cnt, (const void*)error); - - char hostname[NI_MAXHOST + 1]; - struct hostent* he; - - if (0 != gethostname (hostname, sizeof(hostname))) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - pgm_error_from_errno (errno), - _("Resolving hostname: %s"), -#ifndef _WIN32 - strerror (errno) -#else - pgm_wsastrerror (WSAGetLastError()) -#endif - ); - return FALSE; - } - - addr->sa_family = family; - struct addrinfo hints = { - .ai_family = family, - .ai_socktype = SOCK_STREAM, /* not really */ - .ai_protocol = IPPROTO_TCP, /* not really */ - .ai_flags = AI_ADDRCONFIG, - }, *res; - - int e = getaddrinfo (hostname, NULL, &hints, &res); - if (0 == e) { - const socklen_t addrlen = res->ai_addrlen; - memcpy (addr, res->ai_addr, addrlen); - freeaddrinfo (res); - return TRUE; - } else if (EAI_NONAME != e) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - pgm_error_from_eai_errno (e, errno), - _("Resolving hostname address: %s"), - gai_strerror (e)); - return FALSE; - } else if (AF_UNSPEC == family) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_NONAME, - _("Resolving hostname address family.")); - return FALSE; - } - -/* Common case a dual stack host has incorrect IPv6 configuration, i.e. - * hostname is only IPv4 and despite one or more IPv6 addresses. Workaround - * for this case is to resolve the IPv4 hostname, find the matching interface - * and from that interface find an active IPv6 address taking global scope as - * preference over link scoped addresses. - */ - he = gethostbyname (hostname); - if (NULL == he) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - pgm_error_from_h_errno (h_errno), -#ifndef _WIN32 - _("Resolving IPv4 hostname address: %s"), - hstrerror (h_errno) -#else - _("Resolving IPv4 hostname address: %s"), - pgm_wsastrerror (WSAGetLastError()) -#endif - ); - return FALSE; - } - - struct pgm_ifaddrs_t *ifap, *ifa, *ifa6; - if (!pgm_getifaddrs (&ifap, error)) { - pgm_prefix_error (error, - _("Enumerating network interfaces: ")); - return FALSE; - } - -/* hunt for IPv4 interface */ - for (ifa = ifap; ifa; ifa = ifa->ifa_next) - { - if (NULL == ifa->ifa_addr || - AF_INET != ifa->ifa_addr->sa_family) - continue; - if (((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr == ((struct in_addr*)(he->h_addr_list[0]))->s_addr) - { - goto ipv4_found; - } - } - pgm_freeifaddrs (ifap); - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_NONET, - _("Discovering primary IPv4 network interface.")); - return FALSE; -ipv4_found: - -/* hunt for IPv6 interface */ - for (ifa6 = ifap; ifa6; ifa6 = ifa6->ifa_next) - { - if (AF_INET6 != ifa6->ifa_addr->sa_family) - continue; - if (0 == strcmp (ifa->ifa_name, ifa6->ifa_name)) - { - goto ipv6_found; - } - } - pgm_freeifaddrs (ifap); - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_NONET, - _("Discovering primary IPv6 network interface.")); - return FALSE; -ipv6_found: - - memcpy (addr, ifa6->ifa_addr, pgm_sockaddr_len (ifa6->ifa_addr)); - pgm_freeifaddrs (ifap); - return TRUE; -} - -static -const char* -pgm_family_string ( - const sa_family_t family - ) -{ - const char* c; - - switch (family) { - case AF_UNSPEC: c = "AF_UNSPEC"; break; - case AF_INET: c = "AF_INET"; break; - case AF_INET6: c = "AF_INET6"; break; - default: c = "(unknown)"; break; - } - - return c; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/getnodeaddr_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/getnodeaddr_unittest.c deleted file mode 100644 index 6792d2a..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/getnodeaddr_unittest.c +++ /dev/null @@ -1,573 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for portable function to return the nodes IP address. - * - * Copyright (c) 2009-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* IFF_UP */ -#define _BSD_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - - -/* mock state */ - -struct addrinfo; - -struct mock_host_t { - struct sockaddr_storage address; - char* canonical_hostname; - char* alias; -}; - -struct mock_interface_t { - unsigned int index; - char* name; - unsigned int flags; - struct sockaddr_storage addr; - struct sockaddr_storage netmask; -}; - -static GList *mock_hosts = NULL, *mock_interfaces = NULL; - -#define MOCK_HOSTNAME "kiku" -static char* mock_kiku = MOCK_HOSTNAME; -static char* mock_localhost = "localhost"; -static char* mock_invalid = "invalid.invalid"; /* RFC 2606 */ -static char* mock_toolong = "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij12345"; /* 65 */ -static char* mock_hostname = NULL; - -struct pgm_ifaddrs_t; -struct pgm_error_t; - -static bool mock_pgm_getifaddrs (struct pgm_ifaddrs_t**, struct pgm_error_t**); -static void mock_pgm_freeifaddrs (struct pgm_ifaddrs_t*); -static int mock_getaddrinfo (const char*, const char*, const struct addrinfo*, struct addrinfo**); -static void mock_freeaddrinfo (struct addrinfo*); -static int mock_gethostname (char*, size_t); -static struct hostent* mock_gethostbyname (const char*); - - -#define pgm_getifaddrs mock_pgm_getifaddrs -#define pgm_freeifaddrs mock_pgm_freeifaddrs -#define getaddrinfo mock_getaddrinfo -#define freeaddrinfo mock_freeaddrinfo -#define gethostname mock_gethostname -#define gethostbyname mock_gethostbyname - - -#define GETNODEADDR_DEBUG -#include "getnodeaddr.c" - - -static -gpointer -create_host ( - const char* address, - const char* canonical_hostname, - const char* alias - ) -{ - struct mock_host_t* new_host; - - g_assert (address); - g_assert (canonical_hostname); - - new_host = g_slice_alloc0 (sizeof(struct mock_host_t)); - g_assert (pgm_sockaddr_pton (address, (struct sockaddr*)&new_host->address)); - new_host->canonical_hostname = g_strdup (canonical_hostname); - new_host->alias = alias ? g_strdup (alias) : NULL; - - return new_host; -} - -static -gpointer -create_interface ( - const unsigned index, - const char* name, - const char* flags - ) -{ - struct mock_interface_t* new_interface; - - g_assert (name); - g_assert (flags); - - new_interface = g_slice_alloc0 (sizeof(struct mock_interface_t)); - new_interface->index = index; - new_interface->name = g_strdup (name); - - struct sockaddr_in* sin = (gpointer)&new_interface->addr; - struct sockaddr_in6* sin6 = (gpointer)&new_interface->addr; - - gchar** tokens = g_strsplit (flags, ",", 0); - for (guint i = 0; tokens[i]; i++) - { - if (strcmp (tokens[i], "up") == 0) - new_interface->flags |= IFF_UP; - else if (strcmp (tokens[i], "down") == 0) - new_interface->flags |= 0; - else if (strcmp (tokens[i], "loop") == 0) - new_interface->flags |= IFF_LOOPBACK; - else if (strcmp (tokens[i], "broadcast") == 0) - new_interface->flags |= IFF_BROADCAST; - else if (strcmp (tokens[i], "multicast") == 0) - new_interface->flags |= IFF_MULTICAST; - else if (strncmp (tokens[i], "ip=", strlen("ip=")) == 0) { - const char* addr = tokens[i] + strlen("ip="); - g_assert (pgm_sockaddr_pton (addr, (struct sockaddr*)&new_interface->addr)); - } - else if (strncmp (tokens[i], "netmask=", strlen("netmask=")) == 0) { - const char* addr = tokens[i] + strlen("netmask="); - g_assert (pgm_sockaddr_pton (addr, (struct sockaddr*)&new_interface->netmask)); - } - else if (strncmp (tokens[i], "scope=", strlen("scope=")) == 0) { - const char* scope = tokens[i] + strlen("scope="); - g_assert (AF_INET6 == ((struct sockaddr*)&new_interface->addr)->sa_family); - ((struct sockaddr_in6*)&new_interface->addr)->sin6_scope_id = atoi (scope); - } - else - g_error ("parsing failed for flag %s%s%s", - tokens[i] ? "\"" : "", tokens[i] ? tokens[i] : "(null)", tokens[i] ? "\"" : ""); - } - - g_strfreev (tokens); - return new_interface; -} - -#define APPEND_HOST2(a,b,c) \ - do { \ - gpointer data = create_host ((a), (b), (c)); \ - g_assert (data); \ - mock_hosts = g_list_append (mock_hosts, data); \ - g_assert (mock_hosts); g_assert (mock_hosts->data); \ - } while (0) -#define APPEND_HOST(a,b) APPEND_HOST2((a),(b),NULL) -#define APPEND_INTERFACE(a,b,c) \ - do { \ - gpointer data = create_interface ((a), (b), (c)); \ - g_assert (data); \ - mock_interfaces = g_list_append (mock_interfaces, data); \ - g_assert (mock_interfaces); g_assert (mock_interfaces->data); \ - } while (0) -static -void -mock_setup_net (void) -{ - mock_hostname = mock_kiku; - - APPEND_HOST ( "127.0.0.1", "localhost"); - APPEND_HOST2( "10.6.28.33", "kiku.hk.miru.hk", "kiku"); - APPEND_HOST2( "2002:dce8:d28e::33", "ip6-kiku", "kiku"); - APPEND_HOST2( "::1", "ip6-localhost", "ip6-loopback"); - - APPEND_INTERFACE( 1, "lo", "up,loop"); - APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast"); - APPEND_INTERFACE( 3, "eth1", "down,broadcast,multicast"); - APPEND_INTERFACE( 1, "lo", "up,loop,ip=127.0.0.1,netmask=255.0.0.0"); - APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=10.6.28.33,netmask=255.255.255.0"); - APPEND_INTERFACE( 1, "lo", "up,loop,ip=::1,netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,scope=0"); - APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=2002:dce8:d28e::33,netmask=ffff:ffff:ffff:ffff::0,scope=0"); - APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=fe80::214:5eff:febd:6dda,netmask=ffff:ffff:ffff:ffff::0,scope=2"); -} - -/* with broken IPv6 hostname setup */ -static -void -mock_setup_net2 (void) -{ - mock_hostname = mock_kiku; - - APPEND_HOST ( "127.0.0.1", "localhost"); - APPEND_HOST2( "10.6.28.33", "kiku.hk.miru.hk", "kiku"); - APPEND_HOST( "2002:dce8:d28e::33", "ip6-kiku"); - APPEND_HOST2( "::1", "ip6-localhost", "ip6-loopback"); - - APPEND_INTERFACE( 1, "lo", "up,loop"); - APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast"); - APPEND_INTERFACE( 3, "eth1", "down,broadcast,multicast"); - APPEND_INTERFACE( 1, "lo", "up,loop,ip=127.0.0.1,netmask=255.0.0.0"); - APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=10.6.28.33,netmask=255.255.255.0"); - APPEND_INTERFACE( 1, "lo", "up,loop,ip=::1,netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,scope=0"); - APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=2002:dce8:d28e::33,netmask=ffff:ffff:ffff:ffff::0,scope=0"); - APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=fe80::214:5eff:febd:6dda,netmask=ffff:ffff:ffff:ffff::0,scope=2"); -} - -static -void -mock_teardown_net (void) -{ - GList* list; - - list = mock_hosts; - while (list) { - struct mock_host_t* host = list->data; - g_free (host->canonical_hostname); - if (host->alias) - g_free (host->alias); - g_slice_free1 (sizeof(struct mock_host_t), host); - list = list->next; - } - g_list_free (mock_hosts); - - list = mock_interfaces; - while (list) { - struct mock_interface_t* interface = list->data; - g_free (interface->name); - g_slice_free1 (sizeof(struct mock_interface_t), interface); - list = list->next; - } - g_list_free (mock_interfaces); -} - - -/* mock functions for external references */ - -size_t -pgm_pkt_offset ( - const bool can_fragment, - const sa_family_t pgmcc_family /* 0 = disable */ - ) -{ - return 0; -} - -static -bool -mock_pgm_getifaddrs ( - struct pgm_ifaddrs_t** ifap, - pgm_error_t** err - ) -{ - if (NULL == ifap) { - return FALSE; - } - - g_debug ("mock_getifaddrs (ifap:%p err:%p)", (gpointer)ifap, (gpointer)err); - - GList* list = mock_interfaces; - int n = g_list_length (list); - struct pgm_ifaddrs_t* ifa = malloc (n * sizeof(struct pgm_ifaddrs_t)); - memset (ifa, 0, n * sizeof(struct pgm_ifaddrs_t)); - struct pgm_ifaddrs_t* ift = ifa; - while (list) { - struct mock_interface_t* interface = list->data; - ift->ifa_addr = (gpointer)&interface->addr; - ift->ifa_name = interface->name; - ift->ifa_flags = interface->flags; - ift->ifa_netmask = (gpointer)&interface->netmask; - list = list->next; - if (list) { - ift->ifa_next = ift + 1; - ift = ift->ifa_next; - } - } - - *ifap = ifa; - return TRUE; -} - -static -void -mock_pgm_freeifaddrs ( - struct pgm_ifaddrs_t* ifa - ) -{ - g_debug ("mock_freeifaddrs (ifa:%p)", (gpointer)ifa); - free (ifa); -} - -static -struct hostent* -mock_gethostbyname ( - const char* name - ) -{ - static struct hostent he; - static char* aliases[2]; - static char* addr_list[2]; - -/* pre-conditions */ - g_assert (NULL != name); - - g_debug ("mock_gethostbyname (name:%s%s%s)", - name ? "\"" : "", name ? name : "(null)", name ? "\"" : ""); - - GList* list = mock_hosts; - while (list) { - struct mock_host_t* host = list->data; - const int host_family = ((struct sockaddr*)&host->address)->sa_family; - if (((strcmp (host->canonical_hostname, name) == 0) || - (host->alias && strcmp (host->alias, name) == 0))) - { - he.h_name = host->canonical_hostname; - aliases[0] = host->alias; - aliases[1] = NULL; - he.h_aliases = aliases; - he.h_addrtype = host_family; - switch (host->address.ss_family){ - case AF_INET: - he.h_length = sizeof (struct in_addr); - addr_list[0] = (char*)&host->address + G_STRUCT_OFFSET(struct sockaddr_in, sin_addr); - break; - case AF_INET6: - he.h_length = sizeof (struct in6_addr); - addr_list[0] = (char*)&host->address + G_STRUCT_OFFSET(struct sockaddr_in6, sin6_addr); - break; - default: - g_assert_not_reached(); - } - addr_list[1] = NULL; - he.h_addr_list = addr_list; - return &he; - } - list = list->next; - } - h_errno = HOST_NOT_FOUND; - return NULL; -} - -static -int -mock_getaddrinfo ( - const char* node, - const char* service, - const struct addrinfo* hints, - struct addrinfo** res - ) -{ - const int ai_flags = hints ? hints->ai_flags : (AI_V4MAPPED | AI_ADDRCONFIG); - const int ai_family = hints ? hints->ai_family : AF_UNSPEC; - GList* list; - struct sockaddr_storage addr; - - if (NULL == node && NULL == service) - return EAI_NONAME; - -/* pre-conditions */ - g_assert (NULL != node); - g_assert (NULL == service); - g_assert (!(ai_flags & AI_CANONNAME)); - g_assert (!(ai_flags & AI_NUMERICSERV)); - g_assert (!(ai_flags & AI_V4MAPPED)); - - g_debug ("mock_getaddrinfo (node:\"%s\" service:%s hints:%p res:%p)", - node ? node : "(null)", - service ? service : "(null)", - (gpointer)hints, - (gpointer)res); - - gboolean has_ip4_config; - gboolean has_ip6_config; - - if (hints && hints->ai_flags & AI_ADDRCONFIG) - { - has_ip4_config = has_ip6_config = FALSE; - list = mock_interfaces; - while (list) { - const struct mock_interface_t* interface = list->data; - if (AF_INET == ((struct sockaddr*)&interface->addr)->sa_family) - has_ip4_config = TRUE; - else if (AF_INET6 == ((struct sockaddr*)&interface->addr)->sa_family) - has_ip6_config = TRUE; - if (has_ip4_config && has_ip6_config) - break; - list = list->next; - } - } else { - has_ip4_config = has_ip6_config = TRUE; - } - - if (ai_flags & AI_NUMERICHOST) { - pgm_sockaddr_pton (node, (struct sockaddr*)&addr); - } - list = mock_hosts; - while (list) { - struct mock_host_t* host = list->data; - const int host_family = ((struct sockaddr*)&host->address)->sa_family; - if (((strcmp (host->canonical_hostname, node) == 0) || - (host->alias && strcmp (host->alias, node) == 0) || - (ai_flags & AI_NUMERICHOST && - 0 == pgm_sockaddr_cmp ((struct sockaddr*)&addr, (struct sockaddr*)&host->address))) - && - (host_family == ai_family || AF_UNSPEC == ai_family) && - ((AF_INET == host_family && has_ip4_config) || (AF_INET6 == host_family && has_ip6_config))) - { - struct addrinfo* ai = malloc (sizeof(struct addrinfo)); - memset (ai, 0, sizeof(struct addrinfo)); - ai->ai_family = host_family; - ai->ai_addrlen = AF_INET == host_family ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); - ai->ai_addr = (gpointer)&host->address; - *res = ai; - return 0; - } - list = list->next; - } - return EAI_NONAME; -} - -static -void -mock_freeaddrinfo ( - struct addrinfo* res - ) -{ - g_assert (NULL != res); - g_debug ("mock_freeaddrinfo (res:%p)", (gpointer)res); - free (res); -} - -static -int -mock_gethostname ( - char* name, - size_t len - ) -{ - g_debug ("mock_gethostname (name:%p len:%d)", - (gpointer)name, len); - - if (NULL == name) { - errno = EFAULT; - return -1; - } - if (len < 0) { - errno = EINVAL; - return -1; - } - if (len < (1 + strlen (mock_hostname))) { - errno = ENAMETOOLONG; - return -1; - } -/* force an error */ - if (mock_hostname == mock_toolong) { - errno = ENAMETOOLONG; - return -1; - } - strncpy (name, mock_hostname, len); - if (len > 0) - name[len - 1] = '\0'; - return 0; -} - - -/* target: - * bool - * pgm_if_getnodeaddr ( - * const sa_family_t family, - * struct sockaddr* addr, - * const socklen_t cnt, - * pgm_error_t** error - * ) - */ - -START_TEST (test_getnodeaddr_pass_001) -{ - struct sockaddr_storage addr; - char saddr[INET6_ADDRSTRLEN]; - pgm_error_t* err = NULL; - gboolean success = pgm_if_getnodeaddr (AF_UNSPEC, (struct sockaddr*)&addr, sizeof(addr), &err); - if (!success && err) { - g_error ("Resolving node address with AF_UNSPEC: %s", (err && err->message) ? err->message : "(null)"); - } - fail_unless (TRUE == success, "getnodeaddr failed"); - fail_unless (NULL == err, "error raised"); - pgm_sockaddr_ntop ((struct sockaddr*)&addr, saddr, sizeof(saddr)); - g_message ("AF_UNSPEC:%s", saddr ? saddr : "(null)"); - fail_unless (TRUE == pgm_if_getnodeaddr (AF_INET, (struct sockaddr*)&addr, sizeof(addr), &err), "getnodeaddr failed"); - fail_unless (NULL == err, "error raised"); - pgm_sockaddr_ntop ((struct sockaddr*)&addr, saddr, sizeof(saddr)); - g_message ("AF_INET:%s", saddr ? saddr : "(null)"); - fail_unless (TRUE == pgm_if_getnodeaddr (AF_INET6, (struct sockaddr*)&addr, sizeof(addr), &err), "getnodeaddr failed"); - fail_unless (NULL == err, "error raised"); - pgm_sockaddr_ntop ((struct sockaddr*)&addr, saddr, sizeof(saddr)); - g_message ("AF_INET6:%s", saddr ? saddr : "(null)"); -} -END_TEST - -START_TEST (test_getnodeaddr_fail_001) -{ - pgm_error_t* err = NULL; - fail_unless (FALSE == pgm_if_getnodeaddr (AF_UNSPEC, NULL, 0, &err), "getnodeaddr failed"); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - -{ - mock_setup_net(); - - struct sockaddr_storage addr; - char saddr[INET6_ADDRSTRLEN]; - pgm_error_t* err = NULL; - gboolean success = pgm_if_getnodeaddr (AF_UNSPEC, (struct sockaddr*)&addr, sizeof(addr), &err); - if (!success && err) { - g_error ("Resolving node address with AF_UNSPEC: %s", (err && err->message) ? err->message : "(null)"); - } -} - s = suite_create (__FILE__); - - TCase* tc_getnodeaddr = tcase_create ("getnodeaddr"); - suite_add_tcase (s, tc_getnodeaddr); - tcase_add_checked_fixture (tc_getnodeaddr, mock_setup_net, mock_teardown_net); - tcase_add_test (tc_getnodeaddr, test_getnodeaddr_pass_001); - tcase_add_test (tc_getnodeaddr, test_getnodeaddr_fail_001); - - TCase* tc_getnodeaddr2 = tcase_create ("getnodeaddr/2"); - suite_add_tcase (s, tc_getnodeaddr2); - tcase_add_checked_fixture (tc_getnodeaddr2, mock_setup_net2, mock_teardown_net); - tcase_add_test (tc_getnodeaddr2, test_getnodeaddr_pass_001); - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/gsi.c b/3rdparty/openpgm-svn-r1085/pgm/gsi.c deleted file mode 100644 index e7ebec4..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/gsi.c +++ /dev/null @@ -1,227 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * global session ID helper functions. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#ifndef _WIN32 -# include -#endif -#include -#include - - -//#define GSI_DEBUG - - -/* create a GSI based on md5 of a user provided data block. - * - * returns TRUE on success, returns FALSE on error and sets error appropriately, - */ - -bool -pgm_gsi_create_from_data ( - pgm_gsi_t* restrict gsi, - const uint8_t* restrict data, - const size_t length - ) -{ - pgm_return_val_if_fail (NULL != gsi, FALSE); - pgm_return_val_if_fail (NULL != data, FALSE); - pgm_return_val_if_fail (length > 1, FALSE); - -#ifdef CONFIG_HAVE_GLIB_CHECKSUM - GChecksum* checksum = g_checksum_new (G_CHECKSUM_MD5); - pgm_return_val_if_fail (NULL != checksum, FALSE); - g_checksum_update (checksum, data, length); - memcpy (gsi, g_checksum_get_string (checksum) + 10, 6); - g_checksum_free (checksum); -#else - struct pgm_md5_t ctx; - char resblock[16]; - pgm_md5_init_ctx (&ctx); - pgm_md5_process_bytes (&ctx, data, length); - pgm_md5_finish_ctx (&ctx, resblock); - memcpy (gsi, resblock + 10, 6); -#endif - return TRUE; -} - -bool -pgm_gsi_create_from_string ( - pgm_gsi_t* restrict gsi, - const char* restrict str, - ssize_t length /* -1 for NULL terminated */ - ) -{ - pgm_return_val_if_fail (NULL != gsi, FALSE); - pgm_return_val_if_fail (NULL != str, FALSE); - - if (length < 0) - length = strlen (str); - - return pgm_gsi_create_from_data (gsi, (const uint8_t*)str, length); -} - -/* create a global session ID as recommended by the PGM draft specification using - * low order 48 bits of md5 of the hostname. - * - * returns TRUE on success, returns FALSE on error and sets error appropriately, - */ - -bool -pgm_gsi_create_from_hostname ( - pgm_gsi_t* restrict gsi, - pgm_error_t** restrict error - ) -{ - pgm_return_val_if_fail (NULL != gsi, FALSE); - - char hostname[NI_MAXHOST]; - int retval = gethostname (hostname, sizeof(hostname)); - if (0 != retval) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - pgm_error_from_errno (errno), - _("Resolving hostname: %s"), -#ifndef _WIN32 - strerror (errno) -#else - pgm_wsastrerror (WSAGetLastError()) -#endif - ); - return FALSE; - } - - return pgm_gsi_create_from_string (gsi, hostname, -1); -} - -/* create a global session ID based on the IP address. - * - * returns TRUE on succcess, returns FALSE on error and sets error. - */ - -bool -pgm_gsi_create_from_addr ( - pgm_gsi_t* restrict gsi, - pgm_error_t** restrict error - ) -{ - char hostname[NI_MAXHOST]; - struct addrinfo hints, *res = NULL; - - pgm_return_val_if_fail (NULL != gsi, FALSE); - - int retval = gethostname (hostname, sizeof(hostname)); - if (0 != retval) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - pgm_error_from_errno (errno), - _("Resolving hostname: %s"), -#ifndef _WIN32 - strerror (errno) -#else - pgm_wsastrerror (WSAGetLastError()) -#endif - ); - return FALSE; - } - memset (&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; - hints.ai_flags = AI_ADDRCONFIG; - retval = getaddrinfo (hostname, NULL, &hints, &res); - if (0 != retval) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - pgm_error_from_eai_errno (retval, errno), - _("Resolving hostname address: %s"), - gai_strerror (retval)); - return FALSE; - } - memcpy (gsi, &((struct sockaddr_in*)(res->ai_addr))->sin_addr, sizeof(struct in_addr)); - freeaddrinfo (res); - const uint16_t random_val = pgm_random_int_range (0, UINT16_MAX); - memcpy ((uint8_t*)gsi + sizeof(struct in_addr), &random_val, sizeof(random_val)); - return TRUE; -} - -/* re-entrant form of pgm_gsi_print() - * - * returns number of bytes written to buffer on success, returns -1 on - * invalid parameters. - */ - -int -pgm_gsi_print_r ( - const pgm_gsi_t* restrict gsi, - char* restrict buf, - const size_t bufsize - ) -{ - const uint8_t* restrict src = (const uint8_t* restrict)gsi; - - pgm_return_val_if_fail (NULL != gsi, -1); - pgm_return_val_if_fail (NULL != buf, -1); - pgm_return_val_if_fail (bufsize > 0, -1); - - return snprintf (buf, bufsize, "%i.%i.%i.%i.%i.%i", - src[0], src[1], src[2], src[3], src[4], src[5]); -} - -/* transform GSI to ASCII string form. - * - * on success, returns pointer to ASCII string. on error, returns NULL. - */ - -char* -pgm_gsi_print ( - const pgm_gsi_t* gsi - ) -{ - static char buf[PGM_GSISTRLEN]; - - pgm_return_val_if_fail (NULL != gsi, NULL); - - pgm_gsi_print_r (gsi, buf, sizeof(buf)); - return buf; -} - -/* compare two global session identifier GSI values and return TRUE if they are equal - */ - -bool -pgm_gsi_equal ( - const void* restrict p1, - const void* restrict p2 - ) -{ - const union { - pgm_gsi_t gsi; - uint16_t s[3]; - } *u1 = p1, *u2 = p2; - -/* pre-conditions */ - pgm_assert (NULL != p1); - pgm_assert (NULL != p2); - - return (u1->s[0] == u2->s[0] && u1->s[1] == u2->s[1] && u1->s[2] == u2->s[2]); -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/gsi_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/gsi_unittest.c deleted file mode 100644 index dc4c244..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/gsi_unittest.c +++ /dev/null @@ -1,350 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for global session ID helper functions. - * - * Copyright (c) 2009-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include -#include -#include -#include - - -/* mock state */ - -static char* mock_localhost = "localhost"; -static char* mock_invalid = "invalid.invalid"; /* RFC 2606 */ -static char* mock_toolong = "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij12345"; /* 65 */ -static char* mock_hostname = NULL; - - -static -void -mock_setup_invalid (void) -{ - mock_hostname = mock_invalid; -} - -static -void -mock_setup_toolong (void) -{ - mock_hostname = mock_toolong; -} - -static -void -mock_setup_localhost (void) -{ - mock_hostname = mock_localhost; -} - -static -void -mock_teardown (void) -{ -// null -} - - -/* mock functions for external references */ - -size_t -pgm_transport_pkt_offset2 ( - const bool can_fragment, - const bool use_pgmcc - ) -{ - return 0; -} - -int -mock_gethostname ( - char* name, - size_t len - ) -{ - if (mock_hostname == mock_toolong) { - errno = EINVAL; - return -1; - } - strncpy (name, mock_hostname, len); - if (len > 0) - name[len - 1] = '\0'; - return 0; -} - - -#define gethostname mock_gethostname - -#define GSI_DEBUG -#include "gsi.c" - - -/* target: - * bool - * pgm_gsi_create_from_hostname ( - * pgm_gsi_t* gsi, - * pgm_error_t** err - * ) - */ - -START_TEST (test_create_from_hostname_pass_001) -{ - pgm_gsi_t gsi; - pgm_error_t* err = NULL; - fail_unless (pgm_gsi_create_from_hostname (&gsi, &err), "create_from_hostname failed"); - fail_if (err, "error raised"); - fail_unless (pgm_gsi_create_from_hostname (&gsi, NULL), "create_from_hostname failed"); -} -END_TEST - -START_TEST (test_create_from_hostname_pass_002) -{ - pgm_error_t* err = NULL; - fail_if (pgm_gsi_create_from_hostname (NULL, &err), "create_from_hostname failed"); - fail_if (err, "error raised"); - fail_if (pgm_gsi_create_from_hostname (NULL, NULL), "create_from_hostname failed"); -} -END_TEST - -/* hostname too long */ -START_TEST (test_create_from_hostname_pass_003) -{ - pgm_gsi_t gsi; - pgm_error_t* err = NULL; - fail_if (pgm_gsi_create_from_hostname (&gsi, &err), "create_from_hostname failed"); - fail_if (NULL == err, "error not raised"); - fail_if (NULL == err->message, "no error message"); - g_debug ("pgm_error_t: %s", err->message); - fail_if (pgm_gsi_create_from_hostname (&gsi, NULL), "create_from_hostname failed"); -} -END_TEST - -/* target: - * bool - * pgm_gsi_create_from_addr ( - * pgm_gsi_t* gsi, - * pgm_error_t** err - * ) - */ - -START_TEST (test_create_from_addr_pass_001) -{ - pgm_gsi_t gsi; - pgm_error_t* err = NULL; - fail_unless (pgm_gsi_create_from_addr (&gsi, &err), "create_from_addr failed"); - fail_if (err, "error raised"); - fail_unless (pgm_gsi_create_from_addr (&gsi, NULL), "create_from_addr failed"); -} -END_TEST - -START_TEST (test_create_from_addr_pass_002) -{ - pgm_error_t* err = NULL; - fail_if (pgm_gsi_create_from_addr (NULL, &err), "create_from_addr failed"); - fail_if (pgm_gsi_create_from_addr (NULL, NULL), "create_from_addr failed"); -} -END_TEST - -/* invalid hostname */ -START_TEST (test_create_from_addr_pass_003) -{ - pgm_gsi_t gsi; - pgm_error_t* err = NULL; - fail_if (pgm_gsi_create_from_addr (&gsi, &err), "create_from_addr failed"); - fail_if (NULL == err, "error not raised"); - fail_if (NULL == err->message, "no error message"); - g_debug ("pgm_error_t: %s", err->message); - fail_if (pgm_gsi_create_from_addr (&gsi, NULL), "create_from_addr failed"); -} -END_TEST - -/* target: - * char* - * pgm_gsi_print ( - * const pgm_gsi_t* gsi - * ) - */ - -START_TEST (test_print_pass_001) -{ - pgm_gsi_t gsi; - fail_unless (pgm_gsi_create_from_hostname (&gsi, NULL), "create_from_hostname failed"); - fail_if (NULL == pgm_gsi_print (&gsi), "print failed"); -} -END_TEST - -START_TEST (test_print_pass_002) -{ - fail_unless (NULL == pgm_gsi_print (NULL), "print failed"); -} -END_TEST - -/* target: - * int - * pgm_gsi_print_r ( - * const pgm_gsi_t* gsi, - * char* buf, - * size_t bufsize - * ) - */ - -START_TEST (test_print_r_pass_001) -{ - pgm_gsi_t gsi; - char buf[PGM_GSISTRLEN]; - fail_unless (pgm_gsi_create_from_hostname (&gsi, NULL), "create_from_hostname failed"); - fail_unless (pgm_gsi_print_r (&gsi, buf, sizeof(buf)) > 0, "print_r failed"); -} -END_TEST - -START_TEST (test_print_r_pass_002) -{ - pgm_gsi_t gsi; - char buf[PGM_GSISTRLEN]; - fail_unless (pgm_gsi_create_from_hostname (&gsi, NULL), "create_from_hostname failed"); - fail_unless (pgm_gsi_print_r (NULL, buf, sizeof(buf)) == -1, "print_r failed"); - fail_unless (pgm_gsi_print_r (&gsi, NULL, sizeof(buf)) == -1, "print_r failed"); - fail_unless (pgm_gsi_print_r (&gsi, buf, 0) == -1, "print_r failed"); -} -END_TEST - -/* target: - * bool - * pgm_gsi_equal ( - * const void* gsi1, - * const void* gsi2 - * ) - */ - -START_TEST (test_equal_pass_001) -{ - pgm_gsi_t gsi1, gsi2; - fail_unless (pgm_gsi_create_from_hostname (&gsi1, NULL), "create_from_hostname failed"); - fail_unless (pgm_gsi_create_from_hostname (&gsi2, NULL), "create_from_hostname failed"); - fail_unless (pgm_gsi_equal (&gsi1, &gsi2), "equal failed"); -} -END_TEST - -START_TEST (test_equal_pass_002) -{ - pgm_gsi_t gsi1, gsi2; - fail_unless (pgm_gsi_create_from_hostname (&gsi1, NULL), "create_from_hostname failed"); - fail_unless (pgm_gsi_create_from_addr (&gsi2, NULL), "create_from_addr failed"); - fail_if (pgm_gsi_equal (&gsi1, &gsi2), "equal failed"); -} -END_TEST - -START_TEST (test_equal_fail_001) -{ - pgm_gsi_t gsi; - fail_unless (pgm_gsi_create_from_hostname (&gsi, NULL), "create_from_hostname failed"); - gboolean retval = pgm_gsi_equal (NULL, &gsi); - fail ("reached"); -} -END_TEST - -START_TEST (test_equal_fail_002) -{ - pgm_gsi_t gsi; - fail_unless (pgm_gsi_create_from_hostname (&gsi, NULL), "create_from_hostname failed"); - gboolean retval = pgm_gsi_equal (&gsi, NULL); - fail ("reached"); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_create_from_hostname = tcase_create ("create-from-hostname"); - suite_add_tcase (s, tc_create_from_hostname); - tcase_add_checked_fixture (tc_create_from_hostname, mock_setup_localhost, mock_teardown); - tcase_add_test (tc_create_from_hostname, test_create_from_hostname_pass_001); - tcase_add_test (tc_create_from_hostname, test_create_from_hostname_pass_002); - - TCase* tc_create_from_hostname2 = tcase_create ("create-from-hostname/2"); - suite_add_tcase (s, tc_create_from_hostname2); - tcase_add_checked_fixture (tc_create_from_hostname2, mock_setup_toolong, mock_teardown); - tcase_add_test (tc_create_from_hostname2, test_create_from_hostname_pass_003); - - TCase* tc_create_from_addr = tcase_create ("create-from-addr"); - suite_add_tcase (s, tc_create_from_addr); - tcase_add_checked_fixture (tc_create_from_addr, mock_setup_localhost, mock_teardown); - tcase_add_test (tc_create_from_addr, test_create_from_addr_pass_001); - tcase_add_test (tc_create_from_addr, test_create_from_addr_pass_002); - - TCase* tc_create_from_addr2 = tcase_create ("create-from-addr/2"); - suite_add_tcase (s, tc_create_from_addr2); - tcase_add_checked_fixture (tc_create_from_addr2, mock_setup_invalid, mock_teardown); - tcase_add_test (tc_create_from_addr2, test_create_from_addr_pass_003); - - TCase* tc_print = tcase_create ("print"); - suite_add_tcase (s, tc_print); - tcase_add_checked_fixture (tc_print, mock_setup_localhost, mock_teardown); - tcase_add_test (tc_print, test_print_pass_001); - tcase_add_test (tc_print, test_print_pass_002); - - TCase* tc_print_r = tcase_create ("print-r"); - suite_add_tcase (s, tc_print_r); - tcase_add_checked_fixture (tc_print_r, mock_setup_localhost, mock_teardown); - tcase_add_test (tc_print_r, test_print_r_pass_001); - tcase_add_test (tc_print_r, test_print_r_pass_002); - - TCase* tc_equal = tcase_create ("equal"); - suite_add_tcase (s, tc_equal); - tcase_add_checked_fixture (tc_equal, mock_setup_localhost, mock_teardown); - tcase_add_test (tc_equal, test_equal_pass_001); - tcase_add_test (tc_equal, test_equal_pass_002); - tcase_add_test_raise_signal (tc_equal, test_equal_fail_001, SIGABRT); - tcase_add_test_raise_signal (tc_equal, test_equal_fail_002, SIGABRT); - - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/hashtable.c b/3rdparty/openpgm-svn-r1085/pgm/hashtable.c deleted file mode 100644 index da842aa..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/hashtable.c +++ /dev/null @@ -1,327 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable hashtable. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - - -//#define HASHTABLE_DEBUG - -#define HASHTABLE_MIN_SIZE 11 -#define HASHTABLE_MAX_SIZE 13845163 - -struct pgm_hashnode_t -{ - const void* key; - void* value; - struct pgm_hashnode_t* next; - uint_fast32_t key_hash; -}; - -typedef struct pgm_hashnode_t pgm_hashnode_t; - -struct pgm_hashtable_t -{ - unsigned size; - unsigned nnodes; - pgm_hashnode_t** nodes; - pgm_hashfunc_t hash_func; - pgm_equalfunc_t key_equal_func; -}; - -#define PGM_HASHTABLE_RESIZE(hash_ttable) \ - do { \ - if ( (hash_table->size >= 3 * hash_table->nnodes && hash_table->size > HASHTABLE_MIN_SIZE) || \ - (3 * hash_table->size <= hash_table->nnodes && hash_table->size < HASHTABLE_MAX_SIZE) ) \ - { \ - pgm_hashtable_resize (hash_table); \ - } \ - } while (0) - -static void pgm_hashtable_resize (pgm_hashtable_t*); -static pgm_hashnode_t** pgm_hashtable_lookup_node (const pgm_hashtable_t*restrict, const void*restrict, pgm_hash_t*restrict) PGM_GNUC_PURE; -static pgm_hashnode_t* pgm_hash_node_new (const void*restrict, void*restrict, const pgm_hash_t); -static void pgm_hash_node_destroy (pgm_hashnode_t*); -static void pgm_hash_nodes_destroy (pgm_hashnode_t*); - - -pgm_hashtable_t* -pgm_hashtable_new ( - pgm_hashfunc_t hash_func, - pgm_equalfunc_t key_equal_func - ) -{ - pgm_return_val_if_fail (NULL != hash_func, NULL); - pgm_return_val_if_fail (NULL != key_equal_func, NULL); - - pgm_hashtable_t *hash_table; - - hash_table = pgm_new (pgm_hashtable_t, 1); - hash_table->size = HASHTABLE_MIN_SIZE; - hash_table->nnodes = 0; - hash_table->hash_func = hash_func; - hash_table->key_equal_func = key_equal_func; - hash_table->nodes = pgm_new0 (pgm_hashnode_t*, hash_table->size); - - return hash_table; -} - -void -pgm_hashtable_unref ( - pgm_hashtable_t* hash_table - ) -{ - pgm_return_if_fail (hash_table != NULL); - - for (unsigned i = 0; i < hash_table->size; i++) - pgm_hash_nodes_destroy (hash_table->nodes[i]); - pgm_free (hash_table->nodes); - pgm_free (hash_table); -} - -void -pgm_hashtable_destroy ( - pgm_hashtable_t* hash_table - ) -{ - pgm_return_if_fail (hash_table != NULL); - - pgm_hashtable_remove_all (hash_table); - pgm_hashtable_unref (hash_table); -} - -static inline -pgm_hashnode_t** -pgm_hashtable_lookup_node ( - const pgm_hashtable_t* restrict hash_table, - const void* restrict key, - pgm_hash_t* restrict hash_return /* non-NULL to return hash value */ - ) -{ - const pgm_hash_t hash_value = (*hash_table->hash_func) (key); - pgm_hashnode_t** node = &hash_table->nodes[hash_value % hash_table->size]; - - if (hash_return) - *hash_return = hash_value; - - while (*node && (((*node)->key_hash != hash_value) || - !(*hash_table->key_equal_func) ((*node)->key, key))) - { - node = &(*node)->next; - } - - return node; -} - -void* -pgm_hashtable_lookup ( - const pgm_hashtable_t* restrict hash_table, - const void* restrict key - ) -{ - pgm_return_val_if_fail (hash_table != NULL, NULL); - - const pgm_hashnode_t* node = *pgm_hashtable_lookup_node (hash_table, key, NULL); - return node ? node->value : NULL; -} - -void* -pgm_hashtable_lookup_extended ( - const pgm_hashtable_t* restrict hash_table, - const void* restrict key, - void* restrict hash_return - ) -{ - pgm_return_val_if_fail (hash_table != NULL, NULL); - - const pgm_hashnode_t* node = *pgm_hashtable_lookup_node (hash_table, key, hash_return); - return node ? node->value : NULL; -} - -void -pgm_hashtable_insert ( - pgm_hashtable_t* restrict hash_table, - const void* restrict key, - void* restrict value - ) -{ - pgm_hashnode_t **node; - pgm_hash_t key_hash; - - pgm_return_if_fail (hash_table != NULL); - - node = pgm_hashtable_lookup_node (hash_table, key, &key_hash); - pgm_return_if_fail (NULL == *node); - - *node = pgm_hash_node_new (key, value, key_hash); - hash_table->nnodes++; - PGM_HASHTABLE_RESIZE (hash_table); -} - -bool -pgm_hashtable_remove ( - pgm_hashtable_t* restrict hash_table, - const void* restrict key - ) -{ - pgm_hashnode_t **node, *dest; - - pgm_return_val_if_fail (hash_table != NULL, FALSE); - - node = pgm_hashtable_lookup_node (hash_table, key, NULL); - if (*node) - { - dest = *node; - (*node) = dest->next; - pgm_hash_node_destroy (dest); - hash_table->nnodes--; - PGM_HASHTABLE_RESIZE (hash_table); - return TRUE; - } - return FALSE; -} - -void -pgm_hashtable_remove_all ( - pgm_hashtable_t* hash_table - ) -{ - pgm_return_if_fail (hash_table != NULL); - - for (unsigned i = 0; i < hash_table->size; i++) - { - pgm_hash_nodes_destroy (hash_table->nodes[i]); - hash_table->nodes[i] = NULL; - } - hash_table->nnodes = 0; - PGM_HASHTABLE_RESIZE (hash_table); -} - -static -void -pgm_hashtable_resize ( - pgm_hashtable_t* hash_table - ) -{ - const unsigned new_size = CLAMP (pgm_spaced_primes_closest (hash_table->nnodes), - HASHTABLE_MIN_SIZE, HASHTABLE_MAX_SIZE); - pgm_hashnode_t** new_nodes = pgm_new0 (pgm_hashnode_t*, new_size); - - for (unsigned i = 0; i < hash_table->size; i++) - for (pgm_hashnode_t *node = hash_table->nodes[i], *next; node; node = next) - { - next = node->next; - const pgm_hash_t hash_val = node->key_hash % new_size; - node->next = new_nodes[hash_val]; - new_nodes[hash_val] = node; - } - - pgm_free (hash_table->nodes); - hash_table->nodes = new_nodes; - hash_table->size = new_size; -} - -static -pgm_hashnode_t* -pgm_hash_node_new ( - const void* restrict key, - void* restrict value, - const pgm_hash_t key_hash - ) -{ - pgm_hashnode_t *hash_node = pgm_new (pgm_hashnode_t, 1); - hash_node->key = key; - hash_node->value = value; - hash_node->key_hash = key_hash; - hash_node->next = NULL; - return hash_node; -} - -static -void -pgm_hash_node_destroy ( - pgm_hashnode_t* hash_node - ) -{ - pgm_free (hash_node); -} - -static -void -pgm_hash_nodes_destroy ( - pgm_hashnode_t* hash_node - ) -{ - while (hash_node) { - pgm_hashnode_t *next = hash_node->next; - pgm_free (hash_node); - hash_node = next; - } -} - -/* common hash value compare and hash key generation functions */ - -bool -pgm_str_equal ( - const void* restrict p1, - const void* restrict p2 - ) -{ - const char *restrict s1 = p1, *restrict s2 = p2; - return (strcmp (s1, s2) == 0); -} - -/* 31 bit hash function */ - -pgm_hash_t -pgm_str_hash ( - const void* p - ) -{ - const char* s = p; - pgm_hash_t hash_val = *s; - - if (PGM_LIKELY (hash_val)) - for (s++; *s; s++) - hash_val = (hash_val << 5) - hash_val + *s; - return hash_val; -} - -bool -pgm_int_equal ( - const void* restrict p1, - const void* restrict p2 - ) -{ - const int i1 = *(const int*restrict)p1, i2 = *(const int*restrict)p2; - return (i1 == i2); -} - -pgm_hash_t -pgm_int_hash ( - const void* p - ) -{ - const int i = *(const int*)p; - return (pgm_hash_t)i; -} - - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/histogram.c b/3rdparty/openpgm-svn-r1085/pgm/histogram.c deleted file mode 100644 index 3e5ad66..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/histogram.c +++ /dev/null @@ -1,414 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Histograms. - * - * Copyright (c) 2009-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include - - -//#define HISTOGRAM_DEBUG - - -pgm_slist_t* pgm_histograms = NULL; - - -static void sample_set_accumulate (pgm_sample_set_t*, pgm_sample_t, pgm_count_t, unsigned); -static pgm_count_t sample_set_total_count (const pgm_sample_set_t*) PGM_GNUC_PURE; - -static void set_bucket_range (pgm_histogram_t*, unsigned, pgm_sample_t); -static void initialize_bucket_range (pgm_histogram_t*); -static unsigned bucket_index (const pgm_histogram_t*, const pgm_sample_t); -static void accumulate (pgm_histogram_t*, pgm_sample_t, pgm_count_t, unsigned); -static double get_peak_bucket_size (const pgm_histogram_t*restrict, const pgm_sample_set_t*restrict); -static double get_bucket_size (const pgm_histogram_t*, const pgm_count_t, const unsigned); - -static void pgm_histogram_write_html_graph (pgm_histogram_t*restrict, pgm_string_t*restrict); -static void write_ascii (pgm_histogram_t*restrict, const char*restrict, pgm_string_t*restrict); -static void write_ascii_header (pgm_histogram_t*restrict, pgm_sample_set_t*restrict, pgm_count_t, pgm_string_t*restrict); -static void write_ascii_bucket_graph (double, double, pgm_string_t*); -static void write_ascii_bucket_context (int64_t, pgm_count_t, int64_t, unsigned, pgm_string_t*); -static void write_ascii_bucket_value (pgm_count_t, double, pgm_string_t*); -static pgm_string_t* get_ascii_bucket_range (pgm_histogram_t*, unsigned); - - -void -pgm_histogram_add ( - pgm_histogram_t* histogram, - int value - ) -{ - if (value > INT_MAX) - value = INT_MAX - 1; - if (value < 0) - value = 0; - const unsigned i = bucket_index (histogram, value); - pgm_assert (value >= histogram->ranges[ i ]); - pgm_assert (value < histogram->ranges[ i + 1 ]); - accumulate (histogram, value, 1, i); -} - -void -pgm_histogram_write_html_graph_all ( - pgm_string_t* string - ) -{ - if (!pgm_histograms) - return; - pgm_slist_t* snapshot = pgm_histograms; - while (snapshot) { - pgm_histogram_t* histogram = snapshot->data; - pgm_histogram_write_html_graph (histogram, string); - snapshot = snapshot->next; - } -} - -static -void -pgm_histogram_write_html_graph ( - pgm_histogram_t* histogram, - pgm_string_t* string - ) -{ - pgm_string_append (string, "
");
-	write_ascii (histogram, "
", string); - pgm_string_append (string, "
"); -} - -static -void -sample_set_accumulate ( - pgm_sample_set_t* sample_set, - pgm_sample_t value, - pgm_count_t count, - unsigned i - ) -{ - pgm_assert (1 == count || -1 == count); - sample_set->counts[ i ] += count; - sample_set->sum += count * value; - sample_set->square_sum += (count * value) * (int64_t)value; - pgm_assert (sample_set->counts[ i ] >= 0); - pgm_assert (sample_set->sum >= 0); - pgm_assert (sample_set->square_sum >= 0); -} - -static -pgm_count_t -sample_set_total_count ( - const pgm_sample_set_t* sample_set - ) -{ - pgm_count_t total = 0; - for (unsigned i = 0; i < sample_set->counts_len; i++) - total += sample_set->counts[ i ]; - return total; -} - -void -pgm_histogram_init ( - pgm_histogram_t* histogram - ) -{ - if (histogram->declared_min <= 0) - histogram->declared_min = 1; - pgm_assert (histogram->declared_min > 0); - histogram->declared_max = INT_MAX - 1; - pgm_assert (histogram->declared_min <= histogram->declared_max); - pgm_assert (1 < histogram->bucket_count); - set_bucket_range (histogram, histogram->bucket_count, INT_MAX); - initialize_bucket_range (histogram); - -/* register with global list */ - histogram->histograms_link.data = histogram; - histogram->histograms_link.next = pgm_histograms; - pgm_histograms = &histogram->histograms_link; - histogram->is_registered = TRUE; -} - -static -void -set_bucket_range ( - pgm_histogram_t* histogram, - unsigned i, - pgm_sample_t value - ) -{ - histogram->ranges[ i ] = value; -} - -static -void -initialize_bucket_range ( - pgm_histogram_t* histogram - ) -{ - const double log_max = log(histogram->declared_max); - double log_ratio; - double log_next; - unsigned i = 1; - pgm_sample_t current = histogram->declared_min; - - set_bucket_range (histogram, i, current); - while (histogram->bucket_count > ++i) { - double log_current = log(current); - log_ratio = (log_max - log_current) / (histogram->bucket_count - i); - log_next = log_current + log_ratio; - int next = floor(exp(log_next) + 0.5); - if (next > current) - current = next; - else - current++; - set_bucket_range (histogram, i, current); - } - pgm_assert (histogram->bucket_count == i); -} - -static -unsigned -bucket_index ( - const pgm_histogram_t* histogram, - const pgm_sample_t value - ) -{ - pgm_assert (histogram->ranges[0] <= value); - pgm_assert (histogram->ranges[ histogram->bucket_count ] > value); - unsigned under = 0; - unsigned over = histogram->bucket_count; - unsigned mid; - - do { - pgm_assert (over >= under); - mid = ((unsigned)under + (unsigned)over) >> 1; - if (mid == under) - break; - if (histogram->ranges[ mid ] <= value) - under = mid; - else - over = mid; - } while (TRUE); - pgm_assert (histogram->ranges[ mid ] <= value && - histogram->ranges[ mid + 1] > value); - return mid; -} - -static -void -accumulate ( - pgm_histogram_t* histogram, - pgm_sample_t value, - pgm_count_t count, - unsigned i - ) -{ - sample_set_accumulate (&histogram->sample, value, count, i); -} - -static -void -write_ascii ( - pgm_histogram_t* restrict histogram, - const char* restrict newline, - pgm_string_t* restrict output - ) -{ - pgm_count_t snapshot_counts[ histogram->sample.counts_len ]; - pgm_sample_set_t snapshot = { - .counts = snapshot_counts, - .counts_len = histogram->sample.counts_len, - .sum = histogram->sample.sum, - .square_sum = histogram->sample.square_sum - }; - memcpy (snapshot_counts, histogram->sample.counts, sizeof(pgm_count_t) * histogram->sample.counts_len); - - pgm_count_t sample_count = sample_set_total_count (&snapshot); - write_ascii_header (histogram, &snapshot, sample_count, output); - pgm_string_append (output, newline); - - double max_size = get_peak_bucket_size (histogram, &snapshot); - unsigned largest_non_empty_bucket = histogram->bucket_count - 1; - while (0 == snapshot.counts[ largest_non_empty_bucket ]) - { - if (0 == largest_non_empty_bucket) - break; - largest_non_empty_bucket--; - } - - int print_width = 1; - for (unsigned i = 0; i < histogram->bucket_count; ++i) - { - if (snapshot.counts[ i ]) { - pgm_string_t* bucket_range = get_ascii_bucket_range (histogram, i); - const int width = bucket_range->len + 1; - pgm_string_free (bucket_range, TRUE); - if (width > print_width) - print_width = width; - } - } - - int64_t remaining = sample_count; - int64_t past = 0; - for (unsigned i = 0; i < histogram->bucket_count; ++i) - { - pgm_count_t current = snapshot.counts[ i ]; - remaining -= current; - pgm_string_t* bucket_range = get_ascii_bucket_range (histogram, i); - pgm_string_append_printf (output, "%*s ", print_width, bucket_range->str); - pgm_string_free (bucket_range, TRUE); - if (0 == current && - i < histogram->bucket_count - 1 && - 0 == snapshot.counts[ i + 1 ]) - { - while (i < histogram->bucket_count - 1 && - 0 == snapshot.counts[ i + 1 ]) - { - i++; - } - pgm_string_append (output, "... "); - pgm_string_append (output, newline); - continue; - } - - const double current_size = get_bucket_size (histogram, current, i); - write_ascii_bucket_graph (current_size, max_size, output); - write_ascii_bucket_context (past, current, remaining, i, output); - pgm_string_append (output, newline); - past += current; - } -} - -static -void -write_ascii_header ( - pgm_histogram_t* restrict histogram, - pgm_sample_set_t* restrict sample_set, - pgm_count_t sample_count, - pgm_string_t* restrict output - ) -{ - pgm_string_append_printf (output, - "Histogram: %s recorded %d samples", - histogram->histogram_name ? histogram->histogram_name : "(null)", - sample_count); - if (sample_count > 0) { - const double average = sample_set->sum / sample_count; - const double variance = sample_set->square_sum / sample_count - - average * average; - const double standard_deviation = sqrt (variance); - pgm_string_append_printf (output, - ", average = %.1f, standard deviation = %.1f", - average, standard_deviation); - } -} - -static -void -write_ascii_bucket_graph ( - double current_size, - double max_size, - pgm_string_t* output - ) -{ - static const int k_line_length = 72; - int x_count = (k_line_length * (current_size / max_size) + 0.5); - int x_remainder = k_line_length - x_count; - while (0 < x_count--) - pgm_string_append_c (output, '-'); - pgm_string_append_c (output, 'O'); - while (0 < x_remainder--) - pgm_string_append_c (output, ' '); -} - -static -void -write_ascii_bucket_context ( - int64_t past, - pgm_count_t current, - int64_t remaining, - unsigned i, - pgm_string_t* output - ) -{ - const double scaled_sum = (past + current + remaining) / 100.0; - write_ascii_bucket_value (current, scaled_sum, output); - if (0 < i) { - const double percentage = past / scaled_sum; - pgm_string_append_printf (output, " {%3.1f%%}", percentage); - } -} - -static -void -write_ascii_bucket_value ( - pgm_count_t current, - double scaled_sum, - pgm_string_t* output - ) -{ - pgm_string_append_printf (output, " (%d = %3.1f%%)", current, current/scaled_sum); -} - -static -double -get_peak_bucket_size ( - const pgm_histogram_t* restrict histogram, - const pgm_sample_set_t* restrict sample_set - ) -{ - double max_size = 0; - for (unsigned i = 0; i < histogram->bucket_count; i++) { - const double current_size = get_bucket_size (histogram, sample_set->counts[ i ], i); - if (current_size > max_size) - max_size = current_size; - } - return max_size; -} - -static -double -get_bucket_size ( - const pgm_histogram_t* histogram, - const pgm_count_t current, - const unsigned i - ) -{ - pgm_assert (histogram->ranges[ i + 1 ] > histogram->ranges[ i ]); - static const double kTransitionWidth = 5; - double denominator = histogram->ranges[ i + 1 ] - histogram->ranges[ i ]; - if (denominator > kTransitionWidth) - denominator = kTransitionWidth; - return current / denominator; -} - -static -pgm_string_t* -get_ascii_bucket_range ( - pgm_histogram_t* histogram, - unsigned i - ) -{ - pgm_string_t* result = pgm_string_new (NULL); - pgm_string_printf (result, "%d", histogram->ranges[ i ]); - return result; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/htdocs/404.html b/3rdparty/openpgm-svn-r1085/pgm/htdocs/404.html deleted file mode 100644 index 538c90a..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/htdocs/404.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - OpenPGM - Page Not Found - - - -

Lah, page not found.

-

Return to main page

- - diff --git a/3rdparty/openpgm-svn-r1085/pgm/htdocs/base.css b/3rdparty/openpgm-svn-r1085/pgm/htdocs/base.css deleted file mode 100644 index 5aba236..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/htdocs/base.css +++ /dev/null @@ -1,136 +0,0 @@ -html { - background-color: white; - font-family: Verdana; - font-size: 12px; - color: black; -} - -a, a:link, a:visited { - color: #0033cc; - text-decoration: none; -} - -#header { - text-align: right; -} - -#header #hostname { - font-weight: bold; -} - -#header a { - color: black; -} - -#header a:hover { - text-decoration: underline; -} - -#footer { - clear: both; - margin-top: 3.5em; - margin-bottom: 1em; - padding-top: 20px; - text-align: center; -} - -#navigation a { - color: black; -} - -#navigation .tab { - -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; - -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; - padding: 4px 1em 2px; - margin-right: 8px; - float: left; - font-weight: bold; -} - -#navigation #tabtop,#tabline { - background-color: #fb879c; -} - -#navigation #tabbottom { - background-color: #fbc1a9; -} - -#navigation #tabline { - clear: left; - -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; - height: 4px; -} - -#content { - margin-top: 6px; - padding: 3px; -} - -#content a:hover { - background: #ffffaa; -} - -.heading { - -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; - -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; - background-color: #fb879c; - padding: 6px; - margin-bottom: 3px; -} - -table { - border-collapse: separate; -} - -th { - text-align: left; -} - -#information { - float: right; -} - -.rounded { - background-color: #fbc1a9; - -moz-border-radius: 4px; -webkit-border-radius: 4px; - padding: 5px; - margin-top: 6px; - margin-bottom: 6px; - width: 25em; -} - -.break { - border-top: 3px solid white; - margin-top: 1em; - padding-top: 6px; -} - -.bubbly { - background-color: #fb879c; - -moz-border-radius: 4px; -webkit-border-radius: 4px; - padding: 4px; -} - -.bubbly table { - width: 100%; -} - -.bubbly th,.bubbly td { - border-bottom: 1px solid #bbbbbb; -} - -.bubbly th { - background-color: #fbc1a9; - border-left: 1px solid #bbbbbb; - padding: 2px 1px 2px 2px; -} - -.bubbly td { - background-color: white; - padding: 4px; -} - -.bubbly .empty { - padding: 3em; - text-align: center; -} diff --git a/3rdparty/openpgm-svn-r1085/pgm/htdocs/convert_to_macro.pl b/3rdparty/openpgm-svn-r1085/pgm/htdocs/convert_to_macro.pl deleted file mode 100755 index bea44af..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/htdocs/convert_to_macro.pl +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/perl - -use strict; -use File::Basename; - -die "usage: $0 [text file]\n" unless ($ARGV[0]); -open(MOO, $ARGV[0]) or die "cannot open $ARGV[0]: $!"; -my $all = do { local $/; }; -close(MOO); -$all =~ s/"/\\"/g; -$all =~ s/\n/\\n/mg; -$all =~ s/\r/\\r/mg; - -my $var = uc (basename($ARGV[0])); -$var =~ s/\s+/_/g; -$var =~ s/\./_/g; - -print< - - diff --git a/3rdparty/openpgm-svn-r1085/pgm/http.c b/3rdparty/openpgm-svn-r1085/pgm/http.c deleted file mode 100644 index 82e934c..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/http.c +++ /dev/null @@ -1,1718 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * HTTP administrative interface - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#ifndef _WIN32 -# include -#else -# include -# include -# include -#endif -#include -#include -#include -#include -#include -#include -#include - -#include "pgm/http.h" -#include "htdocs/404.html.h" -#include "htdocs/base.css.h" -#include "htdocs/robots.txt.h" -#include "htdocs/xhtml10_strict.doctype.h" - - -/* OpenSolaris */ -#ifndef LOGIN_NAME_MAX -# ifdef _WIN32 -# define LOGIN_NAME_MAX (UNLEN + 1) -# else -# define LOGIN_NAME_MAX 256 -# endif -#endif - -#ifdef _WIN32 -# define getpid _getpid -# define read _read -# define write _write -# define SHUT_WR SD_SEND -#endif - -#ifdef CONFIG_HAVE_SPRINTF_GROUPING -# define GROUP_FORMAT "'" -#else -# define GROUP_FORMAT "" -#endif - -#define HTTP_BACKLOG 10 /* connections */ -#define HTTP_TIMEOUT 60 /* seconds */ - - -/* locals */ - -struct http_connection_t { - pgm_list_t link_; - int sock; - enum { - HTTP_STATE_READ, - HTTP_STATE_WRITE, - HTTP_STATE_FINWAIT - } state; - - char* buf; - size_t buflen; - size_t bufoff; - unsigned status_code; - const char* status_text; - const char* content_type; -}; - -enum { - HTTP_MEMORY_STATIC, - HTTP_MEMORY_TAKE -}; - -static char http_hostname[NI_MAXHOST + 1]; -static char http_address[INET6_ADDRSTRLEN]; -static char http_username[LOGIN_NAME_MAX + 1]; -static int http_pid; - -#ifndef _WIN32 -static int http_sock = -1; -static pthread_t http_thread; -static void* http_routine (void*); -#else -static int http_sock = INVALID_SOCKET; -static HANDLE http_thread; -static unsigned __stdcall http_routine (void*); -#endif -static int http_max_sock = -1; -static fd_set http_readfds, http_writefds, http_exceptfds; -static pgm_list_t* http_socks = NULL; -static pgm_notify_t http_notify = PGM_NOTIFY_INIT; -static volatile uint32_t http_ref_count = 0; - - -static int http_tsi_response (struct http_connection_t*, pgm_tsi_t*); -static void http_each_receiver (pgm_peer_t*, pgm_string_t*); -static int http_receiver_response (struct http_connection_t*, pgm_peer_t*); - -static void default_callback (struct http_connection_t*, const char*); -static void robots_callback (struct http_connection_t*, const char*); -static void css_callback (struct http_connection_t*, const char*); -static void index_callback (struct http_connection_t*, const char*); -static void interfaces_callback (struct http_connection_t*, const char*); -static void transports_callback (struct http_connection_t*, const char*); -static void histograms_callback (struct http_connection_t*, const char*); - -static struct { - const char* path; - void (*callback) (struct http_connection_t*, const char*); -} http_directory[] = { - { "/robots.txt", robots_callback }, - { "/base.css", css_callback }, - { "/", index_callback }, - { "/interfaces", interfaces_callback }, - { "/transports", transports_callback } -#ifdef CONFIG_HISTOGRAMS - ,{ "/histograms", histograms_callback } -#endif -}; - - -static -int -http_sock_rcvtimeo ( - int sock, - int seconds - ) -{ -#if defined( sun ) - return 0; -#elif !defined( _WIN32 ) - const struct timeval timeout = { .tv_sec = seconds, .tv_usec = 0 }; - return setsockopt (sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)); -#else - const int optval = seconds * 1000; - return setsockopt (sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&optval, sizeof(optval)); -#endif -} - -static -int -http_sock_sndtimeo ( - int sock, - int seconds - ) -{ -#if defined( sun ) - return 0; -#elif !defined( _WIN32 ) - const struct timeval timeout = { .tv_sec = seconds, .tv_usec = 0 }; - return setsockopt (sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout)); -#else - const int optval = seconds * 1000; - return setsockopt (sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&optval, sizeof(optval)); -#endif -} - -bool -pgm_http_init ( - uint16_t http_port, - pgm_error_t** error - ) -{ - int e; - - if (pgm_atomic_exchange_and_add32 (&http_ref_count, 1) > 0) - return TRUE; - -/* resolve and store relatively constant runtime information */ - if (0 != gethostname (http_hostname, sizeof(http_hostname))) { - const int save_errno = errno; - pgm_set_error (error, - PGM_ERROR_DOMAIN_HTTP, - pgm_error_from_errno (save_errno), - _("Resolving hostname: %s"), - strerror (save_errno)); - goto err_cleanup; - } - struct addrinfo hints = { - .ai_family = AF_UNSPEC, - .ai_socktype = SOCK_STREAM, - .ai_protocol = IPPROTO_TCP, - .ai_flags = AI_ADDRCONFIG - }, *res = NULL; - e = getaddrinfo (http_hostname, NULL, &hints, &res); - if (0 != e) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_HTTP, - pgm_error_from_eai_errno (e, errno), - _("Resolving hostname address: %s"), - gai_strerror (e)); - goto err_cleanup; - } - e = getnameinfo (res->ai_addr, res->ai_addrlen, - http_address, sizeof(http_address), - NULL, 0, - NI_NUMERICHOST); - if (0 != e) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_HTTP, - pgm_error_from_eai_errno (e, errno), - _("Resolving numeric hostname: %s"), - gai_strerror (e)); - goto err_cleanup; - } - freeaddrinfo (res); -#ifndef _WIN32 - e = getlogin_r (http_username, sizeof(http_username)); - if (0 != e) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_HTTP, - pgm_error_from_errno (errno), - _("Retrieving user name: %s"), - strerror (errno)); - goto err_cleanup; - } -#else - wchar_t wusername[UNLEN + 1]; - DWORD nSize = PGM_N_ELEMENTS( wusername ); - if (!GetUserNameW (wusername, &nSize)) { - const DWORD save_errno = GetLastError(); - char winstr[1024]; - pgm_set_error (error, - PGM_ERROR_DOMAIN_HTTP, - pgm_error_from_win_errno (save_errno), - _("Retrieving user name: %s"), - pgm_win_strerror (winstr, sizeof(winstr), save_errno)); - goto err_cleanup; - } - WideCharToMultiByte (CP_UTF8, 0, wusername, nSize + 1, http_username, sizeof(http_username), NULL, NULL); -#endif /* _WIN32 */ - http_pid = getpid(); - -/* create HTTP listen socket */ - if ((http_sock = socket (AF_INET, SOCK_STREAM, 0)) < 0) { -#ifndef _WIN32 - pgm_set_error (error, - PGM_ERROR_DOMAIN_HTTP, - pgm_error_from_errno (errno), - _("Creating HTTP socket: %s"), - strerror (errno)); -#else - const int save_errno = WSAGetLastError(); - pgm_set_error (error, - PGM_ERROR_DOMAIN_HTTP, - pgm_error_from_wsa_errno (save_errno), - _("Creating HTTP socket: %s"), - pgm_wsastrerror (save_errno)); -#endif - goto err_cleanup; - } - const int v = 1; - if (0 != setsockopt (http_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&v, sizeof(v))) { -#ifndef _WIN32 - pgm_set_error (error, - PGM_ERROR_DOMAIN_HTTP, - pgm_error_from_errno (errno), - _("Enabling reuse of socket local address: %s"), - strerror (errno)); -#else - const int save_errno = WSAGetLastError(); - pgm_set_error (error, - PGM_ERROR_DOMAIN_HTTP, - pgm_error_from_wsa_errno (save_errno), - _("Enabling reuse of socket local address: %s"), - pgm_wsastrerror (save_errno)); -#endif - goto err_cleanup; - } - if (0 != http_sock_rcvtimeo (http_sock, HTTP_TIMEOUT) || - 0 != http_sock_sndtimeo (http_sock, HTTP_TIMEOUT)) { -#ifndef _WIN32 - pgm_set_error (error, - PGM_ERROR_DOMAIN_HTTP, - pgm_error_from_errno (errno), - _("Setting socket timeout: %s"), - strerror (errno)); -#else - const int save_errno = WSAGetLastError(); - pgm_set_error (error, - PGM_ERROR_DOMAIN_HTTP, - pgm_error_from_wsa_errno (save_errno), - _("Setting socket timeout: %s"), - pgm_wsastrerror (save_errno)); -#endif - goto err_cleanup; - } - struct sockaddr_in http_addr; - memset (&http_addr, 0, sizeof(http_addr)); - http_addr.sin_family = AF_INET; - http_addr.sin_addr.s_addr = INADDR_ANY; - http_addr.sin_port = htons (http_port); - if (0 != bind (http_sock, (struct sockaddr*)&http_addr, sizeof(http_addr))) { - char addr[INET6_ADDRSTRLEN]; - pgm_sockaddr_ntop ((struct sockaddr*)&http_addr, addr, sizeof(addr)); -#ifndef _WIN32 - pgm_set_error (error, - PGM_ERROR_DOMAIN_HTTP, - pgm_error_from_errno (errno), - _("Binding HTTP socket to address %s: %s"), - addr, - strerror (errno)); -#else - const int save_errno = WSAGetLastError(); - pgm_set_error (error, - PGM_ERROR_DOMAIN_HTTP, - pgm_error_from_wsa_errno (save_errno), - _("Binding HTTP socket to address %s: %s"), - addr, - pgm_wsastrerror (save_errno)); -#endif - goto err_cleanup; - } - if (listen (http_sock, HTTP_BACKLOG) < 0) { -#ifndef _WIN32 - pgm_set_error (error, - PGM_ERROR_DOMAIN_HTTP, - pgm_error_from_errno (errno), - _("Listening to HTTP socket: %s"), - strerror (errno)); -#else - const int save_errno = WSAGetLastError(); - pgm_set_error (error, - PGM_ERROR_DOMAIN_HTTP, - pgm_error_from_wsa_errno (save_errno), - _("Listening to HTTP socket: %s"), - pgm_wsastrerror (save_errno)); -#endif - goto err_cleanup; - } - -/* non-blocking notification of new connections */ - pgm_sockaddr_nonblocking (http_sock, TRUE); - -/* create notification channel */ - if (0 != pgm_notify_init (&http_notify)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_HTTP, - pgm_error_from_errno (errno), - _("Creating HTTP notification channel: %s"), - strerror (errno)); - goto err_cleanup; - } - -/* spawn thread to handle HTTP requests */ -#ifndef _WIN32 - const int status = pthread_create (&http_thread, NULL, &http_routine, NULL); - if (0 != status) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_HTTP, - pgm_error_from_errno (errno), - _("Creating HTTP thread: %s"), - strerror (errno)); - goto err_cleanup; - } -#else - http_thread = (HANDLE)_beginthreadex (NULL, 0, &http_routine, NULL, 0, NULL); - const int save_errno = errno; - if (0 == http_thread) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_HTTP, - pgm_error_from_errno (save_errno), - _("Creating HTTP thread: %s"), - strerror (save_errno)); - goto err_cleanup; - } -#endif /* _WIN32 */ - pgm_minor (_("Web interface: http://%s:%i"), - http_hostname, - http_port); - return TRUE; - -err_cleanup: -#ifndef _WIN32 - if (-1 != http_sock) { - close (http_sock); - http_sock = -1; - } -#else - if (INVALID_SOCKET != http_sock) { - closesocket (http_sock); - http_sock = INVALID_SOCKET; - } -#endif /* _WIN32 */ - if (pgm_notify_is_valid (&http_notify)) { - pgm_notify_destroy (&http_notify); - } - pgm_atomic_dec32 (&http_ref_count); - return FALSE; -} - -/* notify HTTP thread to shutdown, wait for shutdown and cleanup. - */ - -bool -pgm_http_shutdown (void) -{ - pgm_return_val_if_fail (pgm_atomic_read32 (&http_ref_count) > 0, FALSE); - - if (pgm_atomic_exchange_and_add32 (&http_ref_count, (uint32_t)-1) != 1) - return TRUE; - - pgm_notify_send (&http_notify); -#ifndef _WIN32 - pthread_join (http_thread, NULL); -#else - CloseHandle (http_thread); -#endif -#ifndef _WIN32 - if (-1 != http_sock) { - close (http_sock); - http_sock = -1; - } -#else - if (INVALID_SOCKET != http_sock) { - closesocket (http_sock); - http_sock = INVALID_SOCKET; - } -#endif /* _WIN32 */ - pgm_notify_destroy (&http_notify); - return TRUE; -} - -/* accept a new incoming HTTP connection. - */ - -static -void -http_accept ( - int listen_sock - ) -{ -/* new connection */ - struct sockaddr_storage addr; - socklen_t addrlen = sizeof(addr); - int new_sock = accept (listen_sock, (struct sockaddr*)&addr, &addrlen); - if (-1 == new_sock) { - if (EAGAIN == errno) - return; -#ifndef _WIN32 - pgm_warn (_("HTTP accept: %s"), strerror (errno)); -#else - const int save_errno = WSAGetLastError(); - pgm_warn (_("HTTP accept: %s"), pgm_wsastrerror (save_errno)); -#endif - return; - } - -#ifndef _WIN32 -/* out of bounds file descriptor for select() */ - if (new_sock >= FD_SETSIZE) { - close (new_sock); - pgm_warn (_("Rejected new HTTP client socket due to out of bounds file descriptor.")); - return; - } -#endif - - pgm_sockaddr_nonblocking (new_sock, TRUE); - - struct http_connection_t* connection = pgm_new0 (struct http_connection_t, 1); - connection->sock = new_sock; - connection->state = HTTP_STATE_READ; - http_socks = pgm_list_prepend_link (http_socks, &connection->link_); - FD_SET( new_sock, &http_readfds ); - FD_SET( new_sock, &http_exceptfds ); - if (new_sock > http_max_sock) - http_max_sock = new_sock; -} - -static -void -http_close ( - struct http_connection_t* connection - ) -{ -#ifndef _WIN32 - if (0 != close (connection->sock)) { - pgm_warn (_("Close HTTP client socket: %s"), strerror (errno)); - } -#else - if (0 != closesocket (connection->sock)) { - const int save_errno = WSAGetLastError(); - pgm_warn (_("Close HTTP client socket: %s"), pgm_wsastrerror (save_errno)); - } -#endif - switch (connection->state) { - case HTTP_STATE_READ: - case HTTP_STATE_FINWAIT: - FD_CLR( connection->sock, &http_readfds ); - break; - case HTTP_STATE_WRITE: - FD_CLR( connection->sock, &http_writefds ); - break; - } - FD_CLR( connection->sock, &http_exceptfds ); - http_socks = pgm_list_remove_link (http_socks, &connection->link_); - if (connection->buflen > 0) { - pgm_free (connection->buf); - connection->buf = NULL; - connection->buflen = 0; - } -/* find new highest fd */ - if (connection->sock == http_max_sock) - { - http_max_sock = -1; - for (pgm_list_t* list = http_socks; list; list = list->next) - { - struct http_connection_t* c = (void*)list; - if (c->sock > http_max_sock) - http_max_sock = c->sock; - } - } - pgm_free (connection); -} - -/* non-blocking read an incoming HTTP request - */ - -static -void -http_read ( - struct http_connection_t* connection - ) -{ - for (;;) - { -/* grow buffer as needed */ - if (connection->bufoff + 1024 > connection->buflen) { - connection->buf = pgm_realloc (connection->buf, connection->buflen + 1024); - connection->buflen += 1024; - } - const ssize_t bytes_read = recv (connection->sock, &connection->buf[ connection->bufoff ], connection->buflen - connection->bufoff, 0); - if (bytes_read < 0) { - if (EINTR == errno || EAGAIN == errno) - return; -#ifndef _WIN32 - pgm_warn (_("HTTP client read: %s"), strerror (errno)); -#else - const int save_errno = WSAGetLastError(); - pgm_warn (_("HTTP client read: %s"), pgm_wsastrerror (save_errno)); -#endif - http_close (connection); - return; - } - -/* complete */ - if (strstr (connection->buf, "\r\n\r\n")) - break; - } - -/* process request, e.g. GET /index.html HTTP/1.1\r\n - */ - connection->buf[ connection->buflen - 1 ] = '\0'; - if (0 != memcmp (connection->buf, "GET ", strlen("GET "))) { -/* 501 (not implemented) */ - http_close (connection); - return; - } - - char* request_uri = connection->buf + strlen("GET "); - char* p = request_uri; - do { - if (*p == '?' || *p == ' ') { - *p = '\0'; - break; - } - } while (*(++p)); - - connection->status_code = 200; /* OK */ - connection->status_text = "OK"; - connection->content_type = "text/html"; - connection->bufoff = 0; - for (unsigned i = 0; i < PGM_N_ELEMENTS(http_directory); i++) - { - if (0 == strcmp (request_uri, http_directory[i].path)) - { - http_directory[i].callback (connection, request_uri); - goto complete; - } - } - default_callback (connection, request_uri); - -complete: - connection->state = HTTP_STATE_WRITE; - FD_CLR( connection->sock, &http_readfds ); - FD_SET( connection->sock, &http_writefds ); -} - -/* non-blocking write a HTTP response - */ - -static -void -http_write ( - struct http_connection_t* connection - ) -{ - do { - const ssize_t bytes_written = send (connection->sock, &connection->buf[ connection->bufoff ], connection->buflen - connection->bufoff, 0); - if (bytes_written < 0) { - if (EINTR == errno || EAGAIN == errno) - return; -#ifndef _WIN32 - pgm_warn (_("HTTP client write: %s"), strerror (errno)); -#else - const int save_errno = WSAGetLastError(); - pgm_warn (_("HTTP client write: %s"), pgm_wsastrerror (save_errno)); -#endif - http_close (connection); - return; - } - connection->bufoff += bytes_written; - } while (connection->bufoff < connection->buflen); - - if (0 == shutdown (connection->sock, SHUT_WR)) { - http_close (connection); - } else { - pgm_debug ("HTTP socket entering finwait state."); - connection->state = HTTP_STATE_FINWAIT; - FD_CLR( connection->sock, &http_writefds ); - FD_SET( connection->sock, &http_readfds ); - } -} - -/* read and discard pending data waiting for FIN - */ - -static -void -http_finwait ( - struct http_connection_t* connection - ) -{ - char buf[1024]; - const ssize_t bytes_read = read (connection->sock, buf, sizeof(buf)); - if (bytes_read < 0 && (EINTR == errno || EAGAIN == errno)) - return; - http_close (connection); -} - -static -void -http_process ( - struct http_connection_t* connection - ) -{ - switch (connection->state) { - case HTTP_STATE_READ: http_read (connection); break; - case HTTP_STATE_WRITE: http_write (connection); break; - case HTTP_STATE_FINWAIT: http_finwait (connection); break; - } -} - -static -void -http_set_status ( - struct http_connection_t* connection, - int status_code, - const char* status_text - ) -{ - connection->status_code = status_code; - connection->status_text = status_text; -} - -static -void -http_set_content_type ( - struct http_connection_t* connection, - const char* content_type - ) -{ - connection->content_type = content_type; -} - -/* finalise response buffer with headers and content */ - -static -void -http_set_static_response ( - struct http_connection_t* connection, - const char* content, - size_t content_length - ) -{ - pgm_string_t* response = pgm_string_new (NULL); - pgm_string_printf (response, "HTTP/1.0 %d %s\r\n" - "Server: OpenPGM HTTP Server %u.%u.%u\r\n" - "Last-Modified: Fri, 1 Jan 2010, 00:00:01 GMT\r\n" - "Content-Length: %d\r\n" - "Content-Type: %s\r\n" - "Connection: close\r\n" - "\r\n", - connection->status_code, - connection->status_text, - pgm_major_version, pgm_minor_version, pgm_micro_version, - content_length, - connection->content_type - ); - pgm_string_append (response, content); - if (connection->buflen) - pgm_free (connection->buf); - connection->buflen = response->len; - connection->buf = pgm_string_free (response, FALSE); -} - -static -void -http_set_response ( - struct http_connection_t* connection, - char* content, - size_t content_length - ) -{ - pgm_string_t* response = pgm_string_new (NULL); - pgm_string_printf (response, "HTTP/1.0 %d %s\r\n" - "Server: OpenPGM HTTP Server %u.%u.%u\r\n" - "Content-Length: %d\r\n" - "Content-Type: %s\r\n" - "Connection: close\r\n" - "\r\n", - connection->status_code, - connection->status_text, - pgm_major_version, pgm_minor_version, pgm_micro_version, - content_length, - connection->content_type - ); - pgm_string_append (response, content); - pgm_free (content); - if (connection->buflen) - pgm_free (connection->buf); - connection->buflen = response->len; - connection->buf = pgm_string_free (response, FALSE); -} - -/* Thread routine for processing HTTP requests - */ - -static -#ifndef _WIN32 -void* -#else -unsigned -__stdcall -#endif -http_routine ( - PGM_GNUC_UNUSED void* arg - ) -{ - const int notify_fd = pgm_notify_get_fd (&http_notify); - const int max_fd = MAX( notify_fd, http_sock ); - - FD_ZERO( &http_readfds ); - FD_ZERO( &http_writefds ); - FD_ZERO( &http_exceptfds ); - FD_SET( notify_fd, &http_readfds ); - FD_SET( http_sock, &http_readfds ); - - for (;;) - { - int fds = MAX( http_max_sock, max_fd ) + 1; - fd_set readfds = http_readfds, writefds = http_writefds, exceptfds = http_exceptfds; - - fds = select (fds, &readfds, &writefds, &exceptfds, NULL); -/* signal interrupt */ - if (PGM_UNLIKELY(fds < 0 && EINTR == errno)) - continue; -/* terminate */ - if (PGM_UNLIKELY(FD_ISSET( notify_fd, &readfds ))) - break; -/* new connection */ - if (FD_ISSET( http_sock, &readfds )) { - http_accept (http_sock); - continue; - } -/* existing connection */ - for (pgm_list_t* list = http_socks; list;) - { - struct http_connection_t* c = (void*)list; - list = list->next; - if ((FD_ISSET( c->sock, &readfds ) && HTTP_STATE_READ == c->state) || - (FD_ISSET( c->sock, &writefds ) && HTTP_STATE_WRITE == c->state) || - (FD_ISSET( c->sock, &exceptfds ))) - { - http_process (c); - } - } - } - -/* cleanup */ -#ifndef _WIN32 - return NULL; -#else - _endthread(); - return 0; -#endif /* WIN32 */ -} - -/* add xhtml doctype and head, populate with runtime values - */ - -typedef enum { - HTTP_TAB_GENERAL_INFORMATION, - HTTP_TAB_INTERFACES, - HTTP_TAB_TRANSPORTS, - HTTP_TAB_HISTOGRAMS -} http_tab_e; - -static -pgm_string_t* -http_create_response ( - const char* subtitle, - http_tab_e tab - ) -{ - pgm_assert (NULL != subtitle); - pgm_assert (tab == HTTP_TAB_GENERAL_INFORMATION || - tab == HTTP_TAB_INTERFACES || - tab == HTTP_TAB_TRANSPORTS || - tab == HTTP_TAB_HISTOGRAMS); - -/* surprising deficiency of GLib is no support of display locale time */ - char timestamp[100]; - time_t now; - time (&now); - const struct tm* time_ptr = localtime (&now); -#ifndef _WIN32 - strftime (timestamp, sizeof(timestamp), "%c", time_ptr); -#else - wchar_t wtimestamp[100]; - const size_t slen = strftime (timestamp, sizeof(timestamp), "%c", time_ptr); - const size_t wslen = MultiByteToWideChar (CP_ACP, 0, timestamp, slen, wtimestamp, 100); - WideCharToMultiByte (CP_UTF8, 0, wtimestamp, wslen + 1, timestamp, sizeof(timestamp), NULL, NULL); -#endif - - pgm_string_t* response = pgm_string_new (WWW_XHTML10_STRICT_DOCTYPE); - pgm_string_append_printf (response, "\n" - "%s - %s" - "" - "\n" - "" - "
" - "%s" - " | OpenPGM %u.%u.%u" - " | %s" - "
" - "
" - "General Information" - "Interfaces" - "Transports" -#ifdef CONFIG_HISTOGRAMS - "Histograms" -#endif - "
" - "
" - "
", - http_hostname, - subtitle, - http_hostname, - pgm_major_version, pgm_minor_version, pgm_micro_version, - timestamp, - tab == HTTP_TAB_GENERAL_INFORMATION ? "top" : "bottom", - tab == HTTP_TAB_INTERFACES ? "top" : "bottom", - tab == HTTP_TAB_TRANSPORTS ? "top" : "bottom" -#ifdef CONFIG_HISTOGRAMS - ,tab == HTTP_TAB_HISTOGRAMS ? "top" : "bottom" -#endif - ); - - return response; -} - -static -void -http_finalize_response ( - struct http_connection_t* connection, - pgm_string_t* response - ) -{ - pgm_string_append (response, "
" - "
" - "©2010 Miru" - "
" - "\n" - ""); - - char* buf = pgm_string_free (response, FALSE); - http_set_response (connection, buf, strlen (buf)); -} - -static -void -robots_callback ( - struct http_connection_t* connection, - PGM_GNUC_UNUSED const char* path - ) -{ - http_set_content_type (connection, "text/plain"); - http_set_static_response (connection, WWW_ROBOTS_TXT, strlen(WWW_ROBOTS_TXT)); -} - -static -void -css_callback ( - struct http_connection_t* connection, - PGM_GNUC_UNUSED const char* path - ) -{ - http_set_content_type (connection, "text/css"); - http_set_static_response (connection, WWW_BASE_CSS, strlen(WWW_BASE_CSS)); -} - -static -void -index_callback ( - struct http_connection_t* connection, - const char* path - ) -{ - if (strlen (path) > 1) { - default_callback (connection, path); - return; - } - pgm_rwlock_reader_lock (&pgm_sock_list_lock); - const unsigned transport_count = pgm_slist_length (pgm_sock_list); - pgm_rwlock_reader_unlock (&pgm_sock_list_lock); - - pgm_string_t* response = http_create_response ("OpenPGM", HTTP_TAB_GENERAL_INFORMATION); - pgm_string_append_printf (response, "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "
host name:%s
user name:%s
IP address:%s
transports:%i
process ID:%i
\n", - http_hostname, - http_username, - http_address, - transport_count, - http_pid); - http_finalize_response (connection, response); -} - -static -void -interfaces_callback ( - struct http_connection_t* connection, - PGM_GNUC_UNUSED const char* path - ) -{ - pgm_string_t* response = http_create_response ("Interfaces", HTTP_TAB_INTERFACES); - pgm_string_append (response, "
");
-	struct pgm_ifaddrs_t *ifap, *ifa;
-	pgm_error_t* err = NULL;
-	if (!pgm_getifaddrs (&ifap, &err)) {
-		pgm_string_append_printf (response, "pgm_getifaddrs(): %s", (err && err->message) ? err->message : "(null)");
-		http_finalize_response (connection, response);
-		return;
-	}
-	for (ifa = ifap; ifa; ifa = ifa->ifa_next)
-	{
-		int i = NULL == ifa->ifa_addr ? 0 : pgm_if_nametoindex (ifa->ifa_addr->sa_family, ifa->ifa_name);
-		char rname[IF_NAMESIZE * 2 + 3];
-		char b[IF_NAMESIZE * 2 + 3];
-
-		pgm_if_indextoname (i, rname);
-		sprintf (b, "%s (%s)", ifa->ifa_name, rname);
-
-		 if (NULL == ifa->ifa_addr ||
-		      (ifa->ifa_addr->sa_family != AF_INET &&
-		       ifa->ifa_addr->sa_family != AF_INET6) )
-		{
-			pgm_string_append_printf (response,
-				"#%d name %-15.15s ---- %-46.46s scope 0 status %s loop %s b/c %s m/c %s
\n", - i, - b, - "", - ifa->ifa_flags & IFF_UP ? "UP " : "DOWN", - ifa->ifa_flags & IFF_LOOPBACK ? "YES" : "NO ", - ifa->ifa_flags & IFF_BROADCAST ? "YES" : "NO ", - ifa->ifa_flags & IFF_MULTICAST ? "YES" : "NO " - ); - continue; - } - - char s[INET6_ADDRSTRLEN]; - getnameinfo (ifa->ifa_addr, pgm_sockaddr_len(ifa->ifa_addr), - s, sizeof(s), - NULL, 0, - NI_NUMERICHOST); - pgm_string_append_printf (response, - "#%d name %-15.15s IPv%i %-46.46s scope %u status %s loop %s b/c %s m/c %s
\n", - i, - b, - ifa->ifa_addr->sa_family == AF_INET ? 4 : 6, - s, - (unsigned)pgm_sockaddr_scope_id(ifa->ifa_addr), - ifa->ifa_flags & IFF_UP ? "UP " : "DOWN", - ifa->ifa_flags & IFF_LOOPBACK ? "YES" : "NO ", - ifa->ifa_flags & IFF_BROADCAST ? "YES" : "NO ", - ifa->ifa_flags & IFF_MULTICAST ? "YES" : "NO " - ); - } - pgm_freeifaddrs (ifap); - pgm_string_append (response, "
\n"); - http_finalize_response (connection, response); -} - -static -void -transports_callback ( - struct http_connection_t* connection, - PGM_GNUC_UNUSED const char* path - ) -{ - pgm_string_t* response = http_create_response ("Transports", HTTP_TAB_TRANSPORTS); - pgm_string_append (response, "
" - "\n" - "" - "" - "" - "" - "" - "" - ); - - if (pgm_sock_list) - { - pgm_rwlock_reader_lock (&pgm_sock_list_lock); - - pgm_slist_t* list = pgm_sock_list; - while (list) - { - pgm_slist_t* next = list->next; - pgm_sock_t* sock = list->data; - - char group_address[INET6_ADDRSTRLEN]; - getnameinfo ((struct sockaddr*)&sock->send_gsr.gsr_group, pgm_sockaddr_len ((struct sockaddr*)&sock->send_gsr.gsr_group), - group_address, sizeof(group_address), - NULL, 0, - NI_NUMERICHOST); - char gsi[ PGM_GSISTRLEN ]; - pgm_gsi_print_r (&sock->tsi.gsi, gsi, sizeof(gsi)); - const uint16_t sport = ntohs (sock->tsi.sport); - const uint16_t dport = ntohs (sock->dport); - pgm_string_append_printf (response, "" - "" - "" - "" - "" - "", - group_address, - dport, - gsi, sport, - gsi, - gsi, sport, - sport); - list = next; - } - pgm_rwlock_reader_unlock (&pgm_sock_list_lock); - } - else - { -/* no transports */ - pgm_string_append (response, "" - "" - "" - ); - } - - pgm_string_append (response, "
Group addressDest portSource GSISource port
%s%i%s%u
This transport has no peers.
\n" - "
"); - http_finalize_response (connection, response); -} - -static -void -histograms_callback ( - struct http_connection_t* connection, - PGM_GNUC_UNUSED const char* path - ) -{ - pgm_string_t* response = http_create_response ("Histograms", HTTP_TAB_HISTOGRAMS); - pgm_histogram_write_html_graph_all (response); - http_finalize_response (connection, response); -} - -static -void -default_callback ( - struct http_connection_t* connection, - const char* path - ) -{ - pgm_tsi_t tsi; - const int count = sscanf (path, "/%hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hu", - (unsigned char*)&tsi.gsi.identifier[0], - (unsigned char*)&tsi.gsi.identifier[1], - (unsigned char*)&tsi.gsi.identifier[2], - (unsigned char*)&tsi.gsi.identifier[3], - (unsigned char*)&tsi.gsi.identifier[4], - (unsigned char*)&tsi.gsi.identifier[5], - &tsi.sport); - tsi.sport = htons (tsi.sport); - if (count == 7) - { - int retval = http_tsi_response (connection, &tsi); - if (!retval) return; - } - - http_set_status (connection, 404, "Not Found"); - http_set_static_response (connection, WWW_404_HTML, strlen(WWW_404_HTML)); -} - -static -int -http_tsi_response ( - struct http_connection_t* connection, - pgm_tsi_t* tsi - ) -{ -/* first verify this is a valid TSI */ - pgm_rwlock_reader_lock (&pgm_sock_list_lock); - - pgm_sock_t* sock = NULL; - pgm_slist_t* list = pgm_sock_list; - while (list) - { - pgm_sock_t* list_sock = (pgm_sock_t*)list->data; - pgm_slist_t* next = list->next; - -/* check source */ - if (pgm_tsi_equal (tsi, &list_sock->tsi)) - { - sock = list_sock; - break; - } - -/* check receivers */ - pgm_rwlock_reader_lock (&list_sock->peers_lock); - pgm_peer_t* receiver = pgm_hashtable_lookup (list_sock->peers_hashtable, tsi); - if (receiver) { - int retval = http_receiver_response (connection, receiver); - pgm_rwlock_reader_unlock (&list_sock->peers_lock); - pgm_rwlock_reader_unlock (&pgm_sock_list_lock); - return retval; - } - pgm_rwlock_reader_unlock (&list_sock->peers_lock); - - list = next; - } - - if (!sock) { - pgm_rwlock_reader_unlock (&pgm_sock_list_lock); - return -1; - } - -/* transport now contains valid matching TSI */ - char gsi[ PGM_GSISTRLEN ]; - pgm_gsi_print_r (&sock->tsi.gsi, gsi, sizeof(gsi)); - - char title[ sizeof("Transport .00000") + PGM_GSISTRLEN ]; - sprintf (title, "Transport %s.%hu", - gsi, - ntohs (sock->tsi.sport)); - - char source_address[INET6_ADDRSTRLEN]; - getnameinfo ((struct sockaddr*)&sock->send_gsr.gsr_source, pgm_sockaddr_len ((struct sockaddr*)&sock->send_gsr.gsr_source), - source_address, sizeof(source_address), - NULL, 0, - NI_NUMERICHOST); - - char group_address[INET6_ADDRSTRLEN]; - getnameinfo ((struct sockaddr*)&sock->send_gsr.gsr_group, pgm_sockaddr_len ((struct sockaddr*)&sock->send_gsr.gsr_group), - group_address, sizeof(group_address), - NULL, 0, - NI_NUMERICHOST); - - const uint16_t dport = ntohs (sock->dport); - const uint16_t sport = ntohs (sock->tsi.sport); - - const pgm_time_t ihb_min = sock->spm_heartbeat_len ? sock->spm_heartbeat_interval[ 1 ] : 0; - const pgm_time_t ihb_max = sock->spm_heartbeat_len ? sock->spm_heartbeat_interval[ sock->spm_heartbeat_len - 1 ] : 0; - - char spm_path[INET6_ADDRSTRLEN]; - getnameinfo ((struct sockaddr*)&sock->recv_gsr[0].gsr_source, pgm_sockaddr_len ((struct sockaddr*)&sock->recv_gsr[0].gsr_source), - spm_path, sizeof(spm_path), - NULL, 0, - NI_NUMERICHOST); - - pgm_string_t* response = http_create_response (title, HTTP_TAB_TRANSPORTS); - pgm_string_append_printf (response, "
" - "Transport: " - "%s.%hu" - "
", - gsi, sport); - -/* peers */ - - pgm_string_append (response, "
" - "\n" - "" - "" - "" - "" - "" - "" - "" - "" - ); - - if (sock->peers_list) - { - pgm_rwlock_reader_lock (&sock->peers_lock); - pgm_list_t* peers_list = sock->peers_list; - while (peers_list) { - pgm_list_t* next = peers_list->next; - http_each_receiver (peers_list->data, response); - peers_list = next; - } - pgm_rwlock_reader_unlock (&sock->peers_lock); - } - else - { -/* no peers */ - - pgm_string_append (response, "" - "" - "" - ); - - } - - pgm_string_append (response, "
Group addressDest portSource addressLast hopSource GSISource port
This transport has no peers.
\n" - "
"); - -/* source and configuration information */ - - pgm_string_append_printf (response, "
" - "\n" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "", - source_address, - group_address, - dport, - gsi, - sport); - -/* continue with source information */ - - pgm_string_append_printf (response, "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "
Source address%s
Group address%s
Dest port%u
Source GSI%s
Source port%u
Ttl%u
Adv Mode%s
Late joindisable(2)
TXW_MAX_RTE%" GROUP_FORMAT "zd
TXW_SECS%" GROUP_FORMAT "u
TXW_ADV_SECS0
Ambient SPM interval%" GROUP_FORMAT PGM_TIME_FORMAT " ms
IHB_MIN%" GROUP_FORMAT PGM_TIME_FORMAT " ms
IHB_MAX%" GROUP_FORMAT PGM_TIME_FORMAT " ms
NAK_BO_IVL%" GROUP_FORMAT PGM_TIME_FORMAT " ms
FECdisabled(1)
Source Path Address%s
\n" - "
", - sock->hops, - 0 == sock->adv_mode ? "time(0)" : "data(1)", - sock->txw_max_rte, - sock->txw_secs, - pgm_to_msecs(sock->spm_ambient_interval), - ihb_min, - ihb_max, - pgm_to_msecs(sock->nak_bo_ivl), - spm_path); - -/* performance information */ - - const pgm_txw_t* window = sock->window; - pgm_string_append_printf (response, "\n

Performance information

" - "\n" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "
Data bytes sent%" GROUP_FORMAT PRIu32 "
Data packets sent%" GROUP_FORMAT PRIu32 "
Bytes buffered%" GROUP_FORMAT PRIu32 "
Packets buffered%" GROUP_FORMAT PRIu32 "
Bytes sent%" GROUP_FORMAT PRIu32 "
Raw NAKs received%" GROUP_FORMAT PRIu32 "
Checksum errors%" GROUP_FORMAT PRIu32 "
Malformed NAKs%" GROUP_FORMAT PRIu32 "
Packets discarded%" GROUP_FORMAT PRIu32 "
Bytes retransmitted%" GROUP_FORMAT PRIu32 "
Packets retransmitted%" GROUP_FORMAT PRIu32 "
NAKs received%" GROUP_FORMAT PRIu32 "
NAKs ignored%" GROUP_FORMAT PRIu32 "
Transmission rate%" GROUP_FORMAT PRIu32 " bps
NNAK packets received%" GROUP_FORMAT PRIu32 "
NNAKs received%" GROUP_FORMAT PRIu32 "
Malformed NNAKs%" GROUP_FORMAT PRIu32 "
\n", - sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT], - sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT], - window ? pgm_txw_size (window) : 0, /* minus IP & any UDP header */ - window ? pgm_txw_length (window) : 0, - sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], - sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED], - sock->cumulative_stats[PGM_PC_SOURCE_CKSUM_ERRORS], - sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS], - sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED], - sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_BYTES_RETRANSMITTED], - sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED], - sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED], - sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_IGNORED], - sock->cumulative_stats[PGM_PC_SOURCE_TRANSMISSION_CURRENT_RATE], - sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAK_PACKETS_RECEIVED], - sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAKS_RECEIVED], - sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]); - - pgm_rwlock_reader_unlock (&pgm_sock_list_lock); - http_finalize_response (connection, response); - return 0; -} - -static -void -http_each_receiver ( - pgm_peer_t* peer, - pgm_string_t* response - ) -{ - char group_address[INET6_ADDRSTRLEN]; - getnameinfo ((struct sockaddr*)&peer->group_nla, pgm_sockaddr_len ((struct sockaddr*)&peer->group_nla), - group_address, sizeof(group_address), - NULL, 0, - NI_NUMERICHOST); - - char source_address[INET6_ADDRSTRLEN]; - getnameinfo ((struct sockaddr*)&peer->nla, pgm_sockaddr_len ((struct sockaddr*)&peer->nla), - source_address, sizeof(source_address), - NULL, 0, - NI_NUMERICHOST); - - char last_hop[INET6_ADDRSTRLEN]; - getnameinfo ((struct sockaddr*)&peer->local_nla, pgm_sockaddr_len ((struct sockaddr*)&peer->local_nla), - last_hop, sizeof(last_hop), - NULL, 0, - NI_NUMERICHOST); - - char gsi[ PGM_GSISTRLEN + sizeof(".00000") ]; - pgm_gsi_print_r (&peer->tsi.gsi, gsi, sizeof(gsi)); - - const int sport = ntohs (peer->tsi.sport); - const int dport = ntohs (peer->sock->dport); /* by definition must be the same */ - pgm_string_append_printf (response, "" - "%s" - "%u" - "%s" - "%s" - "%s" - "%u" - "", - group_address, - dport, - source_address, - last_hop, - gsi, sport, gsi, - gsi, sport, sport - ); -} - -static -int -http_time_summary ( - const time_t* activity_time, - char* sz - ) -{ - time_t now_time = time (NULL); - - if (*activity_time > now_time) { - return sprintf (sz, "clock skew"); - } - - struct tm* activity_tm = localtime (activity_time); - - now_time -= *activity_time; - - if (now_time < (24 * 60 * 60)) - { - char hourmin[6]; - strftime (hourmin, sizeof(hourmin), "%H:%M", activity_tm); - - if (now_time < 60) { - return sprintf (sz, "%s (%li second%s ago)", - hourmin, now_time, now_time > 1 ? "s" : ""); - } - now_time /= 60; - if (now_time < 60) { - return sprintf (sz, "%s (%li minute%s ago)", - hourmin, now_time, now_time > 1 ? "s" : ""); - } - now_time /= 60; - return sprintf (sz, "%s (%li hour%s ago)", - hourmin, now_time, now_time > 1 ? "s" : ""); - } - else - { - char daymonth[32]; -#ifndef _WIN32 - strftime (daymonth, sizeof(daymonth), "%d %b", activity_tm); -#else - wchar_t wdaymonth[32]; - const size_t slen = strftime (daymonth, sizeof(daymonth), "%d %b", &activity_tm); - const size_t wslen = MultiByteToWideChar (CP_ACP, 0, daymonth, slen, wdaymonth, 32); - WideCharToMultiByte (CP_UTF8, 0, wdaymonth, wslen + 1, daymonth, sizeof(daymonth), NULL, NULL); -#endif - now_time /= 24; - if (now_time < 14) { - return sprintf (sz, "%s (%li day%s ago)", - daymonth, now_time, now_time > 1 ? "s" : ""); - } else { - return sprintf (sz, "%s", daymonth); - } - } -} - -static -int -http_receiver_response ( - struct http_connection_t* connection, - pgm_peer_t* peer - ) -{ - char gsi[ PGM_GSISTRLEN ]; - pgm_gsi_print_r (&peer->tsi.gsi, gsi, sizeof(gsi)); - char title[ sizeof("Peer .00000") + PGM_GSISTRLEN ]; - sprintf (title, "Peer %s.%u", - gsi, - ntohs (peer->tsi.sport)); - - char group_address[INET6_ADDRSTRLEN]; - getnameinfo ((struct sockaddr*)&peer->group_nla, pgm_sockaddr_len ((struct sockaddr*)&peer->group_nla), - group_address, sizeof(group_address), - NULL, 0, - NI_NUMERICHOST); - - char source_address[INET6_ADDRSTRLEN]; - getnameinfo ((struct sockaddr*)&peer->nla, pgm_sockaddr_len ((struct sockaddr*)&peer->nla), - source_address, sizeof(source_address), - NULL, 0, - NI_NUMERICHOST); - - char last_hop[INET6_ADDRSTRLEN]; - getnameinfo ((struct sockaddr*)&peer->local_nla, pgm_sockaddr_len ((struct sockaddr*)&peer->local_nla), - last_hop, sizeof(last_hop), - NULL, 0, - NI_NUMERICHOST); - - const uint16_t sport = ntohs (peer->tsi.sport); - const uint16_t dport = ntohs (peer->sock->dport); /* by definition must be the same */ - const pgm_rxw_t* window = peer->window; - const uint32_t outstanding_naks = window->nak_backoff_queue.length + - window->wait_ncf_queue.length + - window->wait_data_queue.length; - - time_t last_activity_time; - pgm_time_since_epoch (&peer->last_packet, &last_activity_time); - - char last_activity[100]; - http_time_summary (&last_activity_time, last_activity); - - pgm_string_t* response = http_create_response (title, HTTP_TAB_TRANSPORTS); - pgm_string_append_printf (response, "
" - "Peer: " - "%s.%u" - "
", - gsi, sport); - - -/* peer information */ - pgm_string_append_printf (response, "
" - "\n" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "", - group_address, - dport, - source_address, - last_hop, - gsi, - sport); - - pgm_string_append_printf (response, "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "
Group address%s
Dest port%u
Source address%s
Last hop%s
Source GSI%s
Source port%u
NAK_BO_IVL%" GROUP_FORMAT PGM_TIME_FORMAT " ms
NAK_RPT_IVL%" GROUP_FORMAT PGM_TIME_FORMAT " ms
NAK_NCF_RETRIES%" GROUP_FORMAT "u
NAK_RDATA_IVL%" GROUP_FORMAT PGM_TIME_FORMAT " ms
NAK_DATA_RETRIES%" GROUP_FORMAT "u
Send NAKsenabled(1)
Late joindisabled(2)
NAK TTL%u
Delivery orderordered(2)
Multicast NAKsdisabled(2)
\n" - "
", - pgm_to_msecs(peer->sock->nak_bo_ivl), - pgm_to_msecs(peer->sock->nak_rpt_ivl), - peer->sock->nak_ncf_retries, - pgm_to_msecs(peer->sock->nak_rdata_ivl), - peer->sock->nak_data_retries, - peer->sock->hops); - - pgm_string_append_printf (response, "\n

Performance information

" - "\n" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" /* detected missed packets */ - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "
Data bytes received%" GROUP_FORMAT PRIu32 "
Data packets received%" GROUP_FORMAT PRIu32 "
NAK failures%" GROUP_FORMAT PRIu32 "
Bytes received%" GROUP_FORMAT PRIu32 "
Checksum errors%" GROUP_FORMAT PRIu32 "
Malformed SPMs%" GROUP_FORMAT PRIu32 "
Malformed ODATA%" GROUP_FORMAT PRIu32 "
Malformed RDATA%" GROUP_FORMAT PRIu32 "
Malformed NCFs%" GROUP_FORMAT PRIu32 "
Packets discarded%" GROUP_FORMAT PRIu32 "
Losses%" GROUP_FORMAT PRIu32 "
Bytes delivered to app%" GROUP_FORMAT PRIu32 "
Packets delivered to app%" GROUP_FORMAT PRIu32 "
Duplicate SPMs%" GROUP_FORMAT PRIu32 "
Duplicate ODATA/RDATA%" GROUP_FORMAT PRIu32 "
NAK packets sent%" GROUP_FORMAT PRIu32 "
NAKs sent%" GROUP_FORMAT PRIu32 "
NAKs retransmitted%" GROUP_FORMAT PRIu32 "
NAKs failed%" GROUP_FORMAT PRIu32 "
NAKs failed due to RXW advance%" GROUP_FORMAT PRIu32 "
NAKs failed due to NCF retries%" GROUP_FORMAT PRIu32 "
NAKs failed due to DATA retries%" GROUP_FORMAT PRIu32 "
NAK failures delivered to app%" GROUP_FORMAT PRIu32 "
NAKs suppressed%" GROUP_FORMAT PRIu32 "
Malformed NAKs%" GROUP_FORMAT PRIu32 "
Outstanding NAKs%" GROUP_FORMAT PRIu32 "
Last activity%s
NAK repair min time%" GROUP_FORMAT PRIu32 " μs
NAK repair mean time%" GROUP_FORMAT PRIu32 " μs
NAK repair max time%" GROUP_FORMAT PRIu32 " μs
NAK fail min time%" GROUP_FORMAT PRIu32 " μs
NAK fail mean time%" GROUP_FORMAT PRIu32 " μs
NAK fail max time%" GROUP_FORMAT PRIu32 " μs
NAK min retransmit count%" GROUP_FORMAT PRIu32 "
NAK mean retransmit count%" GROUP_FORMAT PRIu32 "
NAK max retransmit count%" GROUP_FORMAT PRIu32 "
\n", - peer->cumulative_stats[PGM_PC_RECEIVER_DATA_BYTES_RECEIVED], - peer->cumulative_stats[PGM_PC_RECEIVER_DATA_MSGS_RECEIVED], - peer->cumulative_stats[PGM_PC_RECEIVER_NAK_FAILURES], - peer->cumulative_stats[PGM_PC_RECEIVER_BYTES_RECEIVED], - peer->sock->cumulative_stats[PGM_PC_SOURCE_CKSUM_ERRORS], - peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_SPMS], - peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_ODATA], - peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_RDATA], - peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_NCFS], - peer->cumulative_stats[PGM_PC_RECEIVER_PACKETS_DISCARDED], - window->cumulative_losses, - window->bytes_delivered, - window->msgs_delivered, - peer->cumulative_stats[PGM_PC_RECEIVER_DUP_SPMS], - peer->cumulative_stats[PGM_PC_RECEIVER_DUP_DATAS], - peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAK_PACKETS_SENT], - peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SENT], - peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_RETRANSMITTED], - peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_FAILED], - peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_RXW_ADVANCED], - peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_NCF_RETRIES_EXCEEDED], - peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_DATA_RETRIES_EXCEEDED], - peer->cumulative_stats[PGM_PC_RECEIVER_NAK_FAILURES_DELIVERED], - peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED], - peer->cumulative_stats[PGM_PC_RECEIVER_NAK_ERRORS], - outstanding_naks, - last_activity, - window->min_fill_time, - peer->cumulative_stats[PGM_PC_RECEIVER_NAK_SVC_TIME_MEAN], - window->max_fill_time, - peer->min_fail_time, - peer->cumulative_stats[PGM_PC_RECEIVER_NAK_FAIL_TIME_MEAN], - peer->max_fail_time, - window->min_nak_transmit_count, - peer->cumulative_stats[PGM_PC_RECEIVER_TRANSMIT_MEAN], - window->max_nak_transmit_count); - http_finalize_response (connection, response); - return 0; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/http_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/http_unittest.c deleted file mode 100644 index 32ba11b..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/http_unittest.c +++ /dev/null @@ -1,186 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for the HTTP administration interface. - * - * Copyright (c) 2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include - -#include "pgm/transport.h" - - -/* mock state */ -static const guint mock_pgm_major_version = 0; -static const guint mock_pgm_minor_version = 0; -static const guint mock_pgm_micro_version = 0; -static GStaticRWLock mock_pgm_transport_list_lock = G_STATIC_RW_LOCK_INIT; -static GSList* mock_pgm_transport_list = NULL; - -static -gboolean -mock_pgm_tsi_equal ( - gconstpointer v1, - gconstpointer v2 - ) -{ - return memcmp (v1, v2, sizeof(struct pgm_tsi_t)) == 0; -} - -static -void -mock_pgm_time_since_epoch ( - pgm_time_t* pgm_time_t_time, - time_t* time_t_time - ) -{ - *time_t_time = pgm_to_secs (*pgm_time_t_time + 0); -} - -static -void -mock_pgm_histogram_write_html_graph_all - ( - GString* string - ) -{ -} - - -/* mock functions for external references */ - -#define pgm_major_version mock_pgm_major_version -#define pgm_minor_version mock_pgm_minor_version -#define pgm_micro_version mock_pgm_micro_version -#define pgm_transport_list_lock mock_pgm_transport_list_lock -#define pgm_transport_list mock_pgm_transport_list -#define pgm_tsi_equal mock_pgm_tsi_equal -#define pgm_time_since_epoch mock_pgm_time_since_epoch -#define pgm_histogram_write_html_graph_all mock_pgm_histogram_write_html_graph_all - -#define HTTP_DEBUG -#include "http.c" - - -/* target: - * gboolean - * pgm_http_init ( - * guint16* http_port, - * GError** error - * ) - */ - -START_TEST (test_init_pass_001) -{ - GError* err = NULL; - fail_unless (TRUE == pgm_http_init (8080, &err)); - fail_unless (NULL == err); -} -END_TEST - -/* duplicate servers */ -START_TEST (test_init_fail_001) -{ - GError* err = NULL; - fail_unless (TRUE == pgm_http_init (8080, &err)); - fail_unless (FALSE == pgm_http_init (8080, &err)); -} -END_TEST - -/* target: - * gboolean - * pgm_http_shutdown (void) - */ - -START_TEST (test_shutdown_pass_001) -{ - GError* err = NULL; - fail_unless (TRUE == pgm_http_init (8080, &err)); - fail_unless (NULL == err); - fail_unless (TRUE == pgm_http_shutdown ()); -} -END_TEST - -/* repeatability - */ -START_TEST (test_shutdown_pass_002) -{ - GError* err = NULL; - fail_unless (TRUE == pgm_http_init (8080, &err)); - fail_unless (NULL == err); - fail_unless (TRUE == pgm_http_shutdown ()); - fail_unless (TRUE == pgm_http_init (8080, &err)); - fail_unless (NULL == err); - fail_unless (TRUE == pgm_http_shutdown ()); -} -END_TEST - -/* no running server */ -START_TEST (test_shutdown_fail_001) -{ - fail_unless (FALSE == pgm_http_shutdown ()); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_init = tcase_create ("init"); - suite_add_tcase (s, tc_init); - tcase_add_test (tc_init, test_init_pass_001); - tcase_add_test (tc_init, test_init_fail_001); - - TCase* tc_shutdown = tcase_create ("shutdown"); - suite_add_tcase (s, tc_shutdown); - tcase_add_test (tc_shutdown, test_shutdown_pass_001); - tcase_add_test (tc_shutdown, test_shutdown_pass_002); - tcase_add_test (tc_shutdown, test_shutdown_fail_001); - - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/if.c b/3rdparty/openpgm-svn-r1085/pgm/if.c deleted file mode 100644 index f034758..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/if.c +++ /dev/null @@ -1,1595 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * network interface handling. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define _GNU_SOURCE -#include -#include -#include -#ifndef _WIN32 -# include -# include -# include /* _GNU_SOURCE for EAI_NODATA */ -#endif -#include -#include -#include - - -//#define IF_DEBUG - -/* temporary structure to contain interface name whilst address family - * has not been resolved. - */ -struct interface_req { - char ir_name[IF_NAMESIZE]; - unsigned int ir_flags; /* from SIOCGIFFLAGS */ - unsigned int ir_interface; /* interface index */ - struct sockaddr_storage ir_addr; /* interface address */ -}; - - -/* locals */ - -#ifndef _WIN32 -# define IF_DEFAULT_GROUP ((in_addr_t)0xefc00001) /* 239.192.0.1 */ -#else -# define IF_DEFAULT_GROUP ((u_long)0xefc00001) -#endif - -/* ff08::1 */ -#define IF6_DEFAULT_INIT { { { 0xff,8,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } } -const struct in6_addr if6_default_group_addr = IF6_DEFAULT_INIT; - - -static inline bool is_in_net (const struct in_addr*restrict, const struct in_addr*restrict, const struct in_addr*restrict) PGM_GNUC_WARN_UNUSED_RESULT; -static inline bool is_in_net6 (const struct in6_addr*restrict, const struct in6_addr*restrict, const struct in6_addr*restrict) PGM_GNUC_WARN_UNUSED_RESULT; -static inline bool is_network_char (const int, const char) PGM_GNUC_CONST; -static const char* pgm_family_string (const int) PGM_GNUC_CONST; - - -/* recommended address space for multicast: - * rfc4607, rfc3180, rfc2365 - * - * avoid 5 high-order bit overlap. - * - * loopback: ffx1::/16 - * segment: ffx2::/16 - * glop: 238/8 - * mysterious admin: 239/8, ffx6::/16 - * site: 239.252-255/16, ffx5::/16 - * org: 239.192/14, ffx8::/16 - * - * internets: 224.0.1.0-238.255.255.255, ffxe::/16 - */ - - -/* dump all interfaces to console. - * - * note that interface indexes are only in regard to the link layer and hence - * no 1-1 mapping between adapter name to index back to address. - */ - -void -pgm_if_print_all (void) -{ - struct pgm_ifaddrs_t *ifap, *ifa; - - if (!pgm_getifaddrs (&ifap, NULL)) - return; - - for (ifa = ifap; ifa; ifa = ifa->ifa_next) - { - const unsigned int i = NULL == ifa->ifa_addr ? 0 : pgm_if_nametoindex (ifa->ifa_addr->sa_family, ifa->ifa_name); - char rname[IF_NAMESIZE * 2 + 3]; - char b[IF_NAMESIZE * 2 + 3]; - - pgm_if_indextoname (i, rname); - sprintf (b, "%s (%s)", - ifa->ifa_name ? ifa->ifa_name : "(null)", rname); - - if (NULL == ifa->ifa_addr || - (ifa->ifa_addr->sa_family != AF_INET && - ifa->ifa_addr->sa_family != AF_INET6) ) - { - pgm_info (_("#%d name %-15.15s ---- %-46.46s scope 0 status %s loop %s b/c %s m/c %s"), - i, - b, - "", - ifa->ifa_flags & IFF_UP ? "UP " : "DOWN", - ifa->ifa_flags & IFF_LOOPBACK ? "YES" : "NO ", - ifa->ifa_flags & IFF_BROADCAST ? "YES" : "NO ", - ifa->ifa_flags & IFF_MULTICAST ? "YES" : "NO " - ); - continue; - } - - char s[INET6_ADDRSTRLEN]; - getnameinfo (ifa->ifa_addr, pgm_sockaddr_len(ifa->ifa_addr), - s, sizeof(s), - NULL, 0, - NI_NUMERICHOST); - pgm_info (_("#%d name %-15.15s IPv%i %-46.46s scope %u status %s loop %s b/c %s m/c %s"), - i, - b, - ifa->ifa_addr->sa_family == AF_INET ? 4 : 6, - s, - (unsigned)pgm_sockaddr_scope_id(ifa->ifa_addr), - ifa->ifa_flags & IFF_UP ? "UP " : "DOWN", - ifa->ifa_flags & IFF_LOOPBACK ? "YES" : "NO ", - ifa->ifa_flags & IFF_BROADCAST ? "YES" : "NO ", - ifa->ifa_flags & IFF_MULTICAST ? "YES" : "NO " - ); - } - - pgm_freeifaddrs (ifap); -} - -static inline -bool -is_in_net ( - const struct in_addr* restrict addr, /* host byte order */ - const struct in_addr* restrict netaddr, - const struct in_addr* restrict netmask - ) -{ - pgm_assert (NULL != addr); - pgm_assert (NULL != netaddr); - pgm_assert (NULL != netmask); - -#ifdef IF_DEBUG - const struct in_addr taddr = { .s_addr = htonl (addr->s_addr) }; - const struct in_addr tnetaddr = { .s_addr = htonl (netaddr->s_addr) }; - const struct in_addr tnetmask = { .s_addr = htonl (netmask->s_addr) }; - char saddr[INET_ADDRSTRLEN], snetaddr[INET_ADDRSTRLEN], snetmask[INET_ADDRSTRLEN]; - pgm_debug ("is_in_net (addr:%s netaddr:%s netmask:%s)", - pgm_inet_ntop (AF_INET, &taddr, saddr, sizeof(saddr)), - pgm_inet_ntop (AF_INET, &tnetaddr, snetaddr, sizeof(snetaddr)), - pgm_inet_ntop (AF_INET, &tnetmask, snetmask, sizeof(snetmask))); -#endif - - if ((addr->s_addr & netmask->s_addr) == (netaddr->s_addr & netmask->s_addr)) - return TRUE; - return FALSE; -} - -static -bool -is_in_net6 ( - const struct in6_addr* restrict addr, - const struct in6_addr* restrict netaddr, - const struct in6_addr* restrict netmask - ) -{ - pgm_assert (NULL != addr); - pgm_assert (NULL != netaddr); - pgm_assert (NULL != netmask); - -#ifdef IF_DEBUG - char saddr[INET6_ADDRSTRLEN], snetaddr[INET6_ADDRSTRLEN], snetmask[INET6_ADDRSTRLEN]; - pgm_debug ("is_in_net6 (addr:%s netaddr:%s netmask:%s)", - pgm_inet_ntop (AF_INET6, addr, saddr, sizeof(saddr)), - pgm_inet_ntop (AF_INET6, netaddr, snetaddr, sizeof(snetaddr)), - pgm_inet_ntop (AF_INET6, netmask, snetmask, sizeof(snetmask))); -#endif - - for (unsigned i = 0; i < 16; i++) - if ((addr->s6_addr[i] & netmask->s6_addr[i]) != (netaddr->s6_addr[i] & netmask->s6_addr[i])) - return FALSE; - return TRUE; -} - -/* parse interface entity into an interface-request structure. - * - * e.g. eth0 - * 1.2.3.4 - * 1.2 - * abcd:: - * [abcd::] - * - * - * - * special addresses should be ignored: - * - * local physical link: 169.254.0.0/16, fe80::/64 - * broadcast: 255.255.255.255 - * multicast: 224.0.0.0/4 (224.0.0.0 to 239.255.255.255), ff00::/8 - * - * We could use if_nametoindex() but we might as well check that the interface is - * actually UP and capable of multicast traffic. - * - * returns TRUE on success, FALSE on error and sets error appropriately. - */ - -static -bool -parse_interface ( - int family, /* AF_UNSPEC | AF_INET | AF_INET6 */ - const char* restrict ifname, /* NULL terminated */ - struct interface_req* restrict ir, /* location to write interface details to */ - pgm_error_t** restrict error - ) -{ - bool check_inet_network = FALSE, check_inet6_network = FALSE; - bool check_addr = FALSE; - bool check_ifname = FALSE; - char literal[1024]; - struct in_addr in_addr; - struct in6_addr in6_addr; - struct pgm_ifaddrs_t *ifap, *ifa; - struct sockaddr_storage addr; - unsigned interface_matches = 0; - -/* pre-conditions */ - pgm_assert (AF_INET == family || AF_INET6 == family || AF_UNSPEC == family); - pgm_assert (NULL != ifname); - pgm_assert (NULL != ir); - - pgm_debug ("parse_interface (family:%s ifname:%s%s%s ir:%p error:%p)", - pgm_family_string (family), - ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : "", - (const void*)ir, - (const void*)error); - -/* strip any square brackets for IPv6 early evaluation */ - if (AF_INET != family && - '[' == ifname[0]) - { - const size_t ifnamelen = strlen(ifname); - if (']' == ifname[ ifnamelen - 1 ]) { - strncpy (literal, ifname + 1, ifnamelen - 2); - literal[ ifnamelen - 2 ] = 0; - family = AF_INET6; /* force IPv6 evaluation */ - check_inet6_network = TRUE; /* may be a network IP or CIDR block */ - check_addr = TRUE; /* cannot be not a name */ - ifname = literal; - } - } - -/* network address: in_addr in host byte order */ - if (AF_INET6 != family && 0 == pgm_inet_network (ifname, &in_addr)) - { -#ifdef IF_DEBUG - struct in_addr t = { .s_addr = htonl (in_addr.s_addr) }; - pgm_debug ("IPv4 network address: %s", inet_ntoa (t)); -#endif - if (IN_MULTICAST(in_addr.s_addr)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_XDEV, - _("Expecting network interface address, found IPv4 multicast network %s%s%s"), - ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); - return FALSE; - } - struct sockaddr_in s4; - memset (&s4, 0, sizeof(s4)); - s4.sin_family = AF_INET; - s4.sin_addr.s_addr = htonl (in_addr.s_addr); - memcpy (&addr, &s4, sizeof(s4)); - - check_inet_network = TRUE; - check_addr = TRUE; - } - if (AF_INET != family && 0 == pgm_inet6_network (ifname, &in6_addr)) - { - if (IN6_IS_ADDR_MULTICAST(&in6_addr)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_XDEV, - _("Expecting network interface address, found IPv6 multicast network %s%s%s"), - ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); - return FALSE; - } - struct sockaddr_in6 s6; - memset (&s6, 0, sizeof(s6)); - s6.sin6_family = AF_INET6; - s6.sin6_addr = in6_addr; - memcpy (&addr, &s6, sizeof(s6)); - - check_inet6_network = TRUE; - check_addr = TRUE; - } - -/* numeric host with scope id */ - if (!check_addr) - { - struct addrinfo hints = { - .ai_family = family, - .ai_socktype = SOCK_STREAM, /* not really, SOCK_RAW */ - .ai_protocol = IPPROTO_TCP, /* not really, IPPROTO_PGM */ - .ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST /* AI_V4MAPPED is unhelpful */ - }, *res; - const int eai = getaddrinfo (ifname, NULL, &hints, &res); - switch (eai) { - case 0: - if (AF_INET == res->ai_family && - IN_MULTICAST(ntohl (((struct sockaddr_in*)(res->ai_addr))->sin_addr.s_addr))) - { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_XDEV, - _("Expecting interface address, found IPv4 multicast address %s%s%s"), - ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); - freeaddrinfo (res); - return FALSE; - } - else if (AF_INET6 == res->ai_family && - IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)res->ai_addr)->sin6_addr)) - { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_XDEV, - _("Expecting interface address, found IPv6 multicast address %s%s%s"), - ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); - freeaddrinfo (res); - return FALSE; - } - - memcpy (&addr, res->ai_addr, pgm_sockaddr_len (res->ai_addr)); - freeaddrinfo (res); - check_addr = TRUE; - break; - -#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME - case EAI_NODATA: -#endif - case EAI_NONAME: - break; - - default: - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - pgm_error_from_eai_errno (eai, errno), - _("Numeric host resolution: %s"), - gai_strerror (eai)); - return FALSE; - } - } - -#ifndef _WIN32 -/* network name into network address, can be expensive with NSS network lookup - * - * Only Class A, B or C networks are supported, partitioned networks - * (i.e. network/26 or network/28) are not supported by this facility. - */ - if (!(check_inet_network || check_inet6_network)) - { - const struct netent* ne = getnetbyname (ifname); -/* ne::n_net in host byte order */ - - if (ne) { - switch (ne->n_addrtype) { - case AF_INET: - if (AF_INET6 == family) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_NODEV, - _("IP address family conflict when resolving network name %s%s%s, found AF_INET when AF_INET6 expected."), - ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); - return FALSE; - } -/* ne->n_net in network order */ - in_addr.s_addr = ne->n_net; - if (IN_MULTICAST(in_addr.s_addr)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_XDEV, - _("Network name %s%s%s resolves to IPv4 mulicast address."), - ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); - return FALSE; - } - check_inet_network = TRUE; - break; - case AF_INET6: -#ifndef CONFIG_HAVE_IP6_NETWORKS - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_NODEV, - _("Not configured for IPv6 network name support, %s%s%s is an IPv6 network name."), - ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); - return FALSE; -#else - if (AF_INET == family) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_NODEV, - _("IP address family conflict when resolving network name %s%s%s, found AF_INET6 when AF_INET expected."), - ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); - return FALSE; - } - if (IN6_IS_ADDR_MULTICAST(&ne->n_net)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_XDEV, - _("Network name resolves to IPv6 mulicast address %s%s%s"), - ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); - return FALSE; - } - in6_addr = *(const struct in6_addr*)&ne->n_net; - check_inet6_network = TRUE; - break; -#endif - default: - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_NODEV, - _("Network name resolves to non-internet protocol address family %s%s%s"), - ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); - return FALSE; - } - } - } -#endif /* _WIN32 */ - -/* hostname lookup with potential DNS delay or error */ - if (!check_addr) - { - struct addrinfo hints = { - .ai_family = family, - .ai_socktype = SOCK_STREAM, /* not really, SOCK_RAW */ - .ai_protocol = IPPROTO_TCP, /* not really, IPPROTO_PGM */ - .ai_flags = AI_ADDRCONFIG, /* AI_V4MAPPED is unhelpful */ - }, *res; - - const int eai = getaddrinfo (ifname, NULL, &hints, &res); - switch (eai) { - case 0: - if (AF_INET == res->ai_family && - IN_MULTICAST(ntohl (((struct sockaddr_in*)(res->ai_addr))->sin_addr.s_addr))) - { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_XDEV, - _("Expecting interface address, found IPv4 multicast name %s%s%s"), - ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); - freeaddrinfo (res); - return FALSE; - } - else if (AF_INET6 == res->ai_family && - IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)res->ai_addr)->sin6_addr)) - { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_XDEV, - _("Expecting interface address, found IPv6 multicast name %s%s%s"), - ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); - freeaddrinfo (res); - return FALSE; - } - memcpy (&addr, res->ai_addr, pgm_sockaddr_len (res->ai_addr)); - freeaddrinfo (res); - check_addr = TRUE; - break; - -#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME - case EAI_NODATA: -#endif - case EAI_NONAME: - check_ifname = TRUE; - break; - - default: - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - pgm_error_from_eai_errno (eai, errno), - _("Internet host resolution: %s(%d)"), - gai_strerror (eai), eai); - return FALSE; - } - } - -/* iterate through interface list and match device name, ip or net address */ - if (!pgm_getifaddrs (&ifap, error)) { - pgm_prefix_error (error, - _("Enumerating network interfaces: ")); - return FALSE; - } - - for (ifa = ifap; ifa; ifa = ifa->ifa_next) - { - if (NULL == ifa->ifa_addr) - continue; - - switch (ifa->ifa_addr->sa_family) { -/* ignore raw entries on Linux */ -#ifdef AF_PACKET - case AF_PACKET: - continue; -#endif - case AF_INET: - if (AF_INET6 == family) - continue; - break; - case AF_INET6: - if (AF_INET == family) - continue; - break; - default: - continue; - } - - const unsigned ifindex = pgm_if_nametoindex (ifa->ifa_addr->sa_family, ifa->ifa_name); - pgm_assert (0 != ifindex); - -/* check numeric host */ - if (check_addr && - (0 == pgm_sockaddr_cmp (ifa->ifa_addr, (const struct sockaddr*)&addr))) - { - strcpy (ir->ir_name, ifa->ifa_name); - ir->ir_flags = ifa->ifa_flags; - if (ir->ir_flags & IFF_LOOPBACK) - pgm_warn (_("Interface %s reports as a loopback device."), ir->ir_name); - if (!(ir->ir_flags & IFF_MULTICAST)) - pgm_warn (_("Interface %s reports as a non-multicast capable device."), ir->ir_name); - ir->ir_interface = ifindex; - memcpy (&ir->ir_addr, ifa->ifa_addr, pgm_sockaddr_len (ifa->ifa_addr)); - pgm_freeifaddrs (ifap); - return TRUE; - } - -/* check network address */ - if (check_inet_network && - AF_INET == ifa->ifa_addr->sa_family) - { - const struct in_addr ifaddr = { .s_addr = ntohl (((struct sockaddr_in*)ifa->ifa_addr)->sin_addr.s_addr) }; - const struct in_addr netmask = { .s_addr = ntohl (((struct sockaddr_in*)ifa->ifa_netmask)->sin_addr.s_addr) }; - if (is_in_net (&ifaddr, &in_addr, &netmask)) { - strcpy (ir->ir_name, ifa->ifa_name); - ir->ir_flags = ifa->ifa_flags; - if (ir->ir_flags & IFF_LOOPBACK) { - pgm_warn (_("Skipping matching loopback network device %s."), ir->ir_name); - goto skip_inet_network; - } - if (!(ir->ir_flags & IFF_MULTICAST)) { - pgm_warn (_("Skipping matching non-multicast capable network device %s."), ir->ir_name); - goto skip_inet_network; - } - ir->ir_interface = ifindex; - memcpy (&ir->ir_addr, ifa->ifa_addr, pgm_sockaddr_len (ifa->ifa_addr)); - pgm_freeifaddrs (ifap); - return TRUE; - } - } - if (check_inet6_network && - AF_INET6 == ifa->ifa_addr->sa_family) - { - const struct in6_addr ifaddr = ((struct sockaddr_in6*)ifa->ifa_addr)->sin6_addr; - const struct in6_addr netmask = ((struct sockaddr_in6*)ifa->ifa_netmask)->sin6_addr; - if (is_in_net6 (&ifaddr, &in6_addr, &netmask)) { - strcpy (ir->ir_name, ifa->ifa_name); - ir->ir_flags = ifa->ifa_flags; - if (ir->ir_flags & IFF_LOOPBACK) { - pgm_warn (_("Skipping matching loopback network device %s."), ir->ir_name); - goto skip_inet_network; - } - if (!(ir->ir_flags & IFF_MULTICAST)) { - pgm_warn (_("Skipping matching non-multicast capable network device %s."), ir->ir_name); - goto skip_inet_network; - } - ir->ir_interface = ifindex; - memcpy (&ir->ir_addr, ifa->ifa_addr, pgm_sockaddr_len (ifa->ifa_addr)); - pgm_freeifaddrs (ifap); - return TRUE; - } - } -skip_inet_network: - -/* check interface name */ - if (check_ifname) - { - if (0 != strcmp (ifname, ifa->ifa_name)) - continue; - - ir->ir_flags = ifa->ifa_flags; -/* skip loopback and non-multicast capable devices */ - if ((ir->ir_flags & IFF_LOOPBACK) || !(ir->ir_flags & IFF_MULTICAST)) - continue; - -/* check for multiple interfaces */ - if (interface_matches++) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_NOTUNIQ, - _("Network interface name not unique %s%s%s"), - ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); - pgm_freeifaddrs (ifap); - return FALSE; - } - - ir->ir_interface = ifindex; - strcpy (ir->ir_name, ifa->ifa_name); - memcpy (&ir->ir_addr, ifa->ifa_addr, pgm_sockaddr_len (ifa->ifa_addr)); - continue; - } - - } - - if (0 == interface_matches) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_NODEV, - _("No matching non-loopback and multicast capable network interface %s%s%s"), - ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); - pgm_freeifaddrs (ifap); - return FALSE; - } - - pgm_freeifaddrs (ifap); - return TRUE; -} - -/* parse one multicast address, conflict resolution of multiple address families of DNS multicast names is - * deferred to libc. - * - * Zone indices are ignored as interface specification is already available. - * - * reserved addresses may flag warnings: - * - * 224.0.0.0/24 for local network control - * 224.0.1/24 for internetwork control - * 169.254.255.255, ff02::1 all local nodes on segment - * ff02::2 all routers - * ff05::1 all nodes - * ff0x::fb multicast DNS - * ff0x::108 NIS - * ff05::1:3 DHCP - * - * returns TRUE on success, FALSE on error and sets error appropriately. - */ - -static -bool -parse_group ( - const int family, /* AF_UNSPEC | AF_INET | AF_INET6 */ - const char* restrict group, /* NULL terminated */ - struct sockaddr* restrict addr, /* pointer to sockaddr_storage for writing */ - pgm_error_t** restrict error - ) -{ -/* pre-conditions */ - pgm_assert (AF_INET == family || AF_INET6 == family || AF_UNSPEC == family); - pgm_assert (NULL != group); - pgm_assert (NULL != addr); - - pgm_debug ("parse_group (family:%s group:%s%s%s addr:%p error:%p)", - pgm_family_string (family), - group ? "\"" : "", group ? group : "(null)", group ? "\"" : "", - (const void*)addr, - (const void*)error); - -/* strip any square brackets for early IPv6 literal evaluation */ - if (AF_INET != family && - '[' == group[0]) - { - const size_t grouplen = strlen(group); - if (']' == group[ grouplen - 1 ]) { - char literal[1024]; - strncpy (literal, group + 1, grouplen - 2); - literal[ grouplen - 2 ] = 0; - if (pgm_inet_pton (AF_INET6, literal, &((struct sockaddr_in6*)addr)->sin6_addr) && - IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)addr)->sin6_addr)) - { - addr->sa_family = AF_INET6; - ((struct sockaddr_in6*)addr)->sin6_port = 0; - ((struct sockaddr_in6*)addr)->sin6_flowinfo = 0; - ((struct sockaddr_in6*)addr)->sin6_scope_id = 0; - return TRUE; - } - } - } - -/* IPv4 address */ - if (AF_INET6 != family && - pgm_inet_pton (AF_INET, group, &((struct sockaddr_in*)addr)->sin_addr) && - IN_MULTICAST(ntohl (((struct sockaddr_in*)addr)->sin_addr.s_addr))) - { - addr->sa_family = AF_INET; - return TRUE; - } - if (AF_INET != family && - pgm_inet_pton (AF_INET6, group, &((struct sockaddr_in6*)addr)->sin6_addr) && - IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)addr)->sin6_addr)) - { - addr->sa_family = AF_INET6; - ((struct sockaddr_in6*)addr)->sin6_port = 0; - ((struct sockaddr_in6*)addr)->sin6_flowinfo = 0; - ((struct sockaddr_in6*)addr)->sin6_scope_id = 0; - return TRUE; - } - -#ifndef _WIN32 -/* NSS network */ - const struct netent* ne = getnetbyname (group); -/* ne::n_net in host byte order */ - if (ne) { - switch (ne->n_addrtype) { - case AF_INET: - if (AF_INET6 == family) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_NODEV, - _("IP address family conflict when resolving network name %s%s%s, found IPv4 when IPv6 expected."), - group ? "\"" : "", group ? group : "(null)", group ? "\"" : ""); - return FALSE; - } - if (IN_MULTICAST(ne->n_net)) { - addr->sa_family = AF_INET; - ((struct sockaddr_in*)addr)->sin_addr.s_addr = htonl (ne->n_net); - return TRUE; - } - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_NODEV, - _("IP address class conflict when resolving network name %s%s%s, expected IPv4 multicast."), - group ? "\"" : "", group ? group : "(null)", group ? "\"" : ""); - return FALSE; - case AF_INET6: -#ifndef CONFIG_HAVE_IP6_NETWORKS - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_NODEV, - _("Not configured for IPv6 network name support, %s%s%s is an IPv6 network name."), - group ? "\"" : "", group ? group : "(null)", group ? "\"" : ""); - return FALSE; -#else - if (AF_INET == family) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_NODEV, - _("IP address family conflict when resolving network name %s%s%s, found IPv6 when IPv4 expected."), - group ? "\"" : "", group ? group : "(null)", group ? "\"" : ""); - return FALSE; - } - if (IN6_IS_ADDR_MULTICAST(&ne->n_net)) { - addr->sa_family = AF_INET6; - ((struct sockaddr_in6*)addr)->sin6_addr = *(const struct in6_addr*)ne->n_net; - ((struct sockaddr_in6*)&addr)->sin6_port = 0; - ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; - ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; - return TRUE; - } - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_NODEV, - _("IP address class conflict when resolving network name %s%s%s, expected IPv6 multicast."), - group ? "\"" : "", group ? group : "(null)", group ? "\"" : ""); - return FALSE; -#endif /* CONFIG_HAVE_IP6_NETWORKS */ - default: - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_NODEV, - _("Network name resolves to non-internet protocol address family %s%s%s"), - group ? "\"" : "", group ? group : "(null)", group ? "\"" : ""); - return FALSE; - } - } -#endif /* _WIN32 */ - -/* lookup group through name service */ - struct addrinfo hints = { - .ai_family = family, - .ai_socktype = SOCK_STREAM, /* not really, SOCK_RAW */ - .ai_protocol = IPPROTO_TCP, /* not really, IPPROTO_PGM */ - .ai_flags = AI_ADDRCONFIG, /* AI_V4MAPPED is unhelpful */ - }, *res; - - const int eai = getaddrinfo (group, NULL, &hints, &res); - if (0 != eai) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - pgm_error_from_eai_errno (eai, errno), - _("Resolving receive group: %s"), - gai_strerror (eai)); - return FALSE; - } - - if ((AF_INET6 != family && IN_MULTICAST(ntohl (((struct sockaddr_in*)res->ai_addr)->sin_addr.s_addr))) || - (AF_INET != family && IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)res->ai_addr)->sin6_addr))) - { - memcpy (addr, res->ai_addr, res->ai_addrlen); - freeaddrinfo (res); - return TRUE; - } - - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_INVAL, - _("Unresolvable receive group %s%s%s"), - group ? "\"" : "", group ? group : "(null)", group ? "\"" : ""); - freeaddrinfo (res); - return FALSE; -} - -/* parse an interface entity from a network parameter. - * - * family can be unspecified - AF_UNSPEC, can return interfaces with the unspecified - * address family - * - * examples: "eth0" - * "hme0,hme1" - * "qe0,qe1,qe2" - * "qe0,qe2,qe2" => valid even though duplicate interface name - * - * returns TRUE on success with device_list containing double linked list of devices as - * sockaddr/idx pairs. returns FALSE on error, including multiple matching adapters. - * - * memory ownership of linked list is passed to caller and must be freed with pgm_free - * and the pgm_list_free* api. - */ - -static -bool -parse_interface_entity ( - int family, /* AF_UNSPEC | AF_INET | AF_INET6 */ - const char* restrict entity, /* NULL terminated */ - pgm_list_t** restrict interface_list, /* */ - pgm_error_t** restrict error - ) -{ - struct interface_req* ir; - pgm_list_t* source_list = NULL; - -/* pre-conditions */ - pgm_assert (AF_INET == family || AF_INET6 == family || AF_UNSPEC == family); - pgm_assert (NULL != interface_list); - pgm_assert (NULL == *interface_list); - pgm_assert (NULL != error); - - pgm_debug ("parse_interface_entity (family:%s entity:%s%s%s interface_list:%p error:%p)", - pgm_family_string (family), - entity ? "\"":"", entity ? entity : "(null)", entity ? "\"":"", - (const void*)interface_list, - (const void*)error); - -/* the empty entity, returns in_addr_any for both receive and send interfaces */ - if (NULL == entity) - { - ir = pgm_new0 (struct interface_req, 1); - ir->ir_addr.ss_family = family; - *interface_list = pgm_list_append (*interface_list, ir); - return TRUE; - } - -/* check interface name length limit */ - char** tokens = pgm_strsplit (entity, ",", 10); - int j = 0; - while (tokens && tokens[j]) - { - pgm_error_t* sub_error = NULL; - ir = pgm_new (struct interface_req, 1); - if (!parse_interface (family, tokens[j], ir, &sub_error)) - { -/* mark multiple interfaces for later decision based on group families */ - if (sub_error && PGM_ERROR_NOTUNIQ == sub_error->code) - { - ir->ir_addr.ss_family = AF_UNSPEC; - pgm_error_free (sub_error); - } -/* bail out on first interface with an error */ - else - { - pgm_propagate_error (error, sub_error); - pgm_free (ir); - pgm_strfreev (tokens); - while (source_list) { - pgm_free (source_list->data); - source_list = pgm_list_delete_link (source_list, source_list); - } - return FALSE; - } - } - - source_list = pgm_list_append (source_list, ir); - ++j; - } - - pgm_strfreev (tokens); - *interface_list = source_list; - return TRUE; -} - -/* parse a receive multicast group entity. can contain more than one multicast group to - * support asymmetric fan-out. - * - * if group is ambiguous, i.e. empty or a name mapping then the address family of the matching - * interface is queried. if the interface is also ambiguous, i.e. empty interface and receive group - * then the hostname will be used to determine the default node address family. if the hosts - * node name resolves both IPv4 and IPv6 address families then the first matching value is taken. - * - * e.g. "239.192.0.1" - * "239.192.0.100,239.192.0.101" - * - * unspecified address family interfaces are forced to AF_INET or AF_INET6. - * - * returns TRUE on success, returns FALSE on error and sets error appropriately. - */ - -static -bool -parse_receive_entity ( - int family, /* AF_UNSPEC | AF_INET | AF_INET6 */ - const char* restrict entity, /* NULL terminated */ - pgm_list_t** restrict interface_list, /* */ - pgm_list_t** restrict recv_list, /* */ - pgm_error_t** restrict error - ) -{ -/* pre-conditions */ - pgm_assert (AF_INET == family || AF_INET6 == family || AF_UNSPEC == family); - pgm_assert (NULL != recv_list); - pgm_assert (NULL == *recv_list); - pgm_assert (NULL != error); - - pgm_debug ("parse_receive_entity (family:%s entity:%s%s%s interface_list:%p recv_list:%p error:%p)", - pgm_family_string (family), - entity ? "\"":"", entity ? entity : "(null)", entity ? "\"":"", - (const void*)interface_list, - (const void*)recv_list, - (const void*)error); - - struct group_source_req* recv_gsr; - struct interface_req* primary_interface = (struct interface_req*)pgm_memdup ((*interface_list)->data, sizeof(struct interface_req)); - -/* the empty entity */ - if (NULL == entity) - { -/* default receive object */ - recv_gsr = pgm_new0 (struct group_source_req, 1); - recv_gsr->gsr_interface = primary_interface->ir_interface; - recv_gsr->gsr_group.ss_family = family; - -/* track IPv6 scope from any resolved interface */ - unsigned scope_id = 0; - -/* if using unspec default group check the interface for address family - */ - if (AF_UNSPEC == recv_gsr->gsr_group.ss_family) - { - if (AF_UNSPEC == primary_interface->ir_addr.ss_family) - { - struct sockaddr_storage addr; - if (!pgm_if_getnodeaddr (AF_UNSPEC, (struct sockaddr*)&addr, sizeof(addr), error)) - { - pgm_prefix_error (error, - _("Node primary address family cannot be determined: ")); - pgm_free (recv_gsr); - pgm_free (primary_interface); - return FALSE; - } - recv_gsr->gsr_group.ss_family = addr.ss_family; - scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&addr); - -/* was an interface actually specified */ - if (primary_interface->ir_name[0] != '\0') - { - struct interface_req ir; - if (!parse_interface (recv_gsr->gsr_group.ss_family, primary_interface->ir_name, &ir, error)) - { - pgm_prefix_error (error, - _("Unique address cannot be determined for interface %s%s%s: "), - primary_interface->ir_name ? "\"" : "", primary_interface->ir_name ? primary_interface->ir_name : "(null)", primary_interface->ir_name ? "\"" : ""); - pgm_free (recv_gsr); - pgm_free (primary_interface); - return FALSE; - } - - recv_gsr->gsr_interface = ir.ir_interface; - memcpy (&primary_interface->ir_addr, &ir.ir_addr, pgm_sockaddr_len ((struct sockaddr*)&ir.ir_addr)); - scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&ir.ir_addr); - } - } - else - { -/* use interface address family for multicast group */ - recv_gsr->gsr_group.ss_family = primary_interface->ir_addr.ss_family; - scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&primary_interface->ir_addr); - } - } - - - pgm_assert (AF_UNSPEC != recv_gsr->gsr_group.ss_family); - if (AF_UNSPEC != primary_interface->ir_addr.ss_family) - { - pgm_assert (recv_gsr->gsr_group.ss_family == primary_interface->ir_addr.ss_family); - } - else - { -/* check if we can now resolve the interface by address family of the receive group */ - if (primary_interface->ir_name[0] != '\0') - { - struct interface_req ir; - if (!parse_interface (recv_gsr->gsr_group.ss_family, primary_interface->ir_name, &ir, error)) - { - pgm_prefix_error (error, - _("Unique address cannot be determined for interface %s%s%s: "), - primary_interface->ir_name ? "\"" : "", primary_interface->ir_name ? primary_interface->ir_name : "(null)", primary_interface->ir_name ? "\"" : ""); - pgm_free (recv_gsr); - pgm_free (primary_interface); - return FALSE; - } - - recv_gsr->gsr_interface = ir.ir_interface; - scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&ir.ir_addr); - } - } - -/* copy default PGM multicast group */ - switch (recv_gsr->gsr_group.ss_family) { - case AF_INET6: - memcpy (&((struct sockaddr_in6*)&recv_gsr->gsr_group)->sin6_addr, - &if6_default_group_addr, - sizeof(if6_default_group_addr)); - ((struct sockaddr_in6*)&recv_gsr->gsr_group)->sin6_scope_id = scope_id; - break; - - case AF_INET: - ((struct sockaddr_in*)&recv_gsr->gsr_group)->sin_addr.s_addr = htonl(IF_DEFAULT_GROUP); - break; - - default: - pgm_assert_not_reached(); - } - -/* ASM: source = group */ - memcpy (&recv_gsr->gsr_source, &recv_gsr->gsr_group, pgm_sockaddr_len ((struct sockaddr*)&recv_gsr->gsr_group)); - *recv_list = pgm_list_append (*recv_list, recv_gsr); - pgm_free (primary_interface); - return TRUE; - } - -/* parse one or more multicast receive groups. - */ - - int j = 0; - char** tokens = pgm_strsplit (entity, ",", 10); - while (tokens && tokens[j]) - { -/* default receive object */ - recv_gsr = pgm_new0 (struct group_source_req, 1); - recv_gsr->gsr_interface = primary_interface->ir_interface; - recv_gsr->gsr_group.ss_family = family; - - if (AF_UNSPEC == recv_gsr->gsr_group.ss_family) - { - if (AF_UNSPEC == primary_interface->ir_addr.ss_family) - { - pgm_debug ("Address family of receive group cannot be determined from interface."); - } - else - { - recv_gsr->gsr_group.ss_family = primary_interface->ir_addr.ss_family; - ((struct sockaddr_in6*)&recv_gsr->gsr_group)->sin6_scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&primary_interface->ir_addr); - } - } - - if (!parse_group (recv_gsr->gsr_group.ss_family, tokens[j], (struct sockaddr*)&recv_gsr->gsr_group, error)) - { - pgm_prefix_error (error, - _("Unresolvable receive entity %s%s%s: "), - tokens[j] ? "\"" : "", tokens[j] ? tokens[j] : "(null)", tokens[j] ? "\"" : ""); - pgm_free (recv_gsr); - pgm_strfreev (tokens); - pgm_free (primary_interface); - return FALSE; - } - -/* check if we can now resolve the source interface by address family of the receive group */ - if (AF_UNSPEC == primary_interface->ir_addr.ss_family) - { - if (primary_interface->ir_name[0] != '\0') - { - struct interface_req ir; - if (!parse_interface (recv_gsr->gsr_group.ss_family, primary_interface->ir_name, &ir, error)) - { - pgm_prefix_error (error, - _("Unique address cannot be determined for interface %s%s%s: "), - primary_interface->ir_name ? "\"" : "", primary_interface->ir_name ? primary_interface->ir_name : "(null)", primary_interface->ir_name ? "\"" : ""); - pgm_free (recv_gsr); - pgm_free (primary_interface); - return FALSE; - } - - recv_gsr->gsr_interface = ir.ir_interface; - ((struct sockaddr_in6*)&recv_gsr->gsr_group)->sin6_scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&ir.ir_addr); - } - } - else - { -/* keep interface scope */ - ((struct sockaddr_in6*)&recv_gsr->gsr_group)->sin6_scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&primary_interface->ir_addr); - } - -/* ASM: source = group */ - memcpy (&recv_gsr->gsr_source, &recv_gsr->gsr_group, pgm_sockaddr_len ((struct sockaddr*)&recv_gsr->gsr_group)); - *recv_list = pgm_list_append (*recv_list, recv_gsr); - ++j; - } - - pgm_strfreev (tokens); - pgm_free (primary_interface); - return TRUE; -} - -static -bool -parse_send_entity ( - int family, /* AF_UNSPEC | AF_INET | AF_INET6 */ - const char* restrict entity, /* null = empty entity */ - pgm_list_t** restrict interface_list, /* */ - pgm_list_t** restrict recv_list, /* */ - pgm_list_t** restrict send_list, /* */ - pgm_error_t** restrict error - ) -{ -/* pre-conditions */ - pgm_assert (AF_INET == family || AF_INET6 == family || AF_UNSPEC == family); - pgm_assert (NULL != recv_list); - pgm_assert (NULL != *recv_list); - pgm_assert (NULL != send_list); - pgm_assert (NULL == *send_list); - pgm_assert (NULL != error); - - pgm_debug ("parse_send_entity (family:%s entity:%s%s%s interface_list:%p recv_list:%p send_list:%p error:%p)", - pgm_family_string (family), - entity ? "\"":"", entity ? entity : "(null)", entity ? "\"":"", - (const void*)interface_list, - (const void*)recv_list, - (const void*)send_list, - (const void*)error); - - struct group_source_req* send_gsr; - const struct interface_req* primary_interface = (struct interface_req*)(*interface_list)->data; - - if (entity == NULL) - { - send_gsr = pgm_memdup ((*recv_list)->data, sizeof(struct group_source_req)); - *send_list = pgm_list_append (*send_list, send_gsr); - return TRUE; - } - -/* default send object */ - send_gsr = pgm_new0 (struct group_source_req, 1); - send_gsr->gsr_interface = primary_interface->ir_interface; - if (!parse_group (family, entity, (struct sockaddr*)&send_gsr->gsr_group, error)) - { - pgm_prefix_error (error, - _("Unresolvable send entity %s%s%s: "), - entity ? "\"":"", entity ? entity : "(null)", entity ? "\"":""); - pgm_free (send_gsr); - return FALSE; - } - -/* check if we can now resolve the source interface by address family of the send group */ - if (AF_UNSPEC == primary_interface->ir_addr.ss_family) - { - if (primary_interface->ir_name[0] != '\0') - { - struct interface_req ir; - if (!parse_interface (send_gsr->gsr_group.ss_family, primary_interface->ir_name, &ir, error)) - { - pgm_prefix_error (error, - _("Unique address cannot be determined for interface %s%s%s: "), - primary_interface->ir_name ? "\"":"", primary_interface->ir_name ? primary_interface->ir_name : "(null)", primary_interface->ir_name ? "\"":""); - pgm_free (send_gsr); - return FALSE; - } - - send_gsr->gsr_interface = ir.ir_interface; - ((struct sockaddr_in6*)&send_gsr->gsr_group)->sin6_scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&ir.ir_addr); - } - } - -/* ASM: source = group */ - memcpy (&send_gsr->gsr_source, &send_gsr->gsr_group, pgm_sockaddr_len ((struct sockaddr*)&send_gsr->gsr_group)); - *send_list = pgm_list_append (*send_list, send_gsr); - return TRUE; -} - -/* parse network parameter - * - * interface list; receive multicast group list; send multicast group - */ - -#define IS_HOSTNAME(x) ( /* RFC 952 */ \ - isalnum(x) || \ - ((x) == '-') || \ - ((x) == '.') \ - ) -#define IS_IP(x) ( \ - isdigit(x) || \ - ((x) == '.') || \ - ((x) == '/') \ - ) -#define IS_IP6(x) ( \ - isxdigit(x) || \ - ((x) == ':') || \ - ((x) == '/') || \ - ((x) == '.') || \ - ((x) == '[') || \ - ((x) == ']') \ - ) -/* e.g. fe80::1%eth0.620 vlan tag, - * fe80::1%eth0:0 IP alias - * fe80::1%qe0_0 Solaris link name - * - * The Linux kernel generally doesn't care too much, but everything else falls apart with - * random characters in interface names. Hyphen is a popular problematic character. - */ -#define IS_IP6_WITH_ZONE(x) ( \ - IS_IP6(x) || \ - ((x) == '%') || \ - isalpha(x) || \ - ((x) == '_') \ - ) -#define IS_NETPARAM(x) ( \ - ((x) == ',') || \ - ((x) == ';') \ - ) - -static inline -bool -is_network_char ( - const int family, - const char c - ) -{ - if (IS_HOSTNAME(c) || - (AF_INET == family && IS_IP(c)) || - ((AF_INET6 == family || AF_UNSPEC == family) && IS_IP6_WITH_ZONE(c)) || - IS_NETPARAM(c)) - return TRUE; - else - return FALSE; -} - -static -bool -network_parse ( - const char* restrict network, /* NULL terminated */ - int family, /* AF_UNSPEC | AF_INET | AF_INET6 */ - pgm_list_t** restrict recv_list, /* */ - pgm_list_t** restrict send_list, /* */ - pgm_error_t** restrict error - ) -{ - bool retval = FALSE; - const char *p = network; - const char *e = p + strlen(network); - enum { ENTITY_INTERFACE, ENTITY_RECEIVE, ENTITY_SEND, ENTITY_ERROR } ec = ENTITY_INTERFACE; - const char *b = p; /* begin of entity */ - pgm_list_t* source_list = NULL; - pgm_error_t* sub_error = NULL; - -/* pre-conditions */ - pgm_assert (NULL != network); - pgm_assert (AF_UNSPEC == family || AF_INET == family || AF_INET6 == family); - pgm_assert (NULL != recv_list); - pgm_assert (NULL != send_list); - - pgm_debug ("network_parse (network:%s%s%s family:%s recv_list:%p send_list:%p error:%p)", - network ? "\"" : "", network ? network : "(null)", network ? "\"" : "", - pgm_family_string (family), - (const void*)recv_list, - (const void*)send_list, - (const void*)error); - - while (p < e) - { - if (!is_network_char (family, *p)) - { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_INVAL, - _("'%c' is not a valid character."), - *p); - goto free_lists; - } - - if (*p == ';') /* end of entity */ - { - if (b == p) /* empty entity */ - { - switch (ec++) { - case ENTITY_INTERFACE: - retval = parse_interface_entity (family, NULL, &source_list, error); - break; - - case ENTITY_RECEIVE: - retval = parse_receive_entity (family, NULL, &source_list, recv_list, error); - break; - - case ENTITY_SEND: - retval = parse_send_entity (family, NULL, &source_list, recv_list, send_list, error); - break; - - default: - pgm_assert_not_reached(); - break; - } - - if (!retval) - goto free_lists; - - b = ++p; - continue; - } - -/* entity from b to p-1 */ - char entity[1024]; - strncpy (entity, b, sizeof(entity)); - entity[p - b] = 0; - - switch (ec++) { - case ENTITY_INTERFACE: - if (parse_interface_entity (family, entity, &source_list, &sub_error)) - break; - if (!(sub_error && PGM_ERROR_XDEV == sub_error->code)) - { -/* fall through on multicast */ - if (!(sub_error && PGM_ERROR_NOTUNIQ == sub_error->code)) - { - pgm_propagate_error (error, sub_error); - goto free_lists; - } - pgm_clear_error (&sub_error); -/* FIXME: too many interfaces */ - if (pgm_list_length (source_list) > 1) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_INVAL, - _("Send group list contains more than one entity.")); - goto free_lists; - } - break; - } - pgm_clear_error (&sub_error); - while (source_list) { - pgm_free (source_list->data); - source_list = pgm_list_delete_link (source_list, source_list); - } - if (!parse_interface_entity (family, NULL, &source_list, &sub_error) && - !(sub_error && PGM_ERROR_NOTUNIQ == sub_error->code)) - { - pgm_propagate_error (error, sub_error); - goto free_lists; - } - pgm_clear_error (&sub_error); - ec++; - - case ENTITY_RECEIVE: - if (!parse_receive_entity (family, entity, &source_list, recv_list, error)) - goto free_lists; - break; - - case ENTITY_SEND: - if (!parse_send_entity (family, entity, &source_list, recv_list, send_list, error)) - goto free_lists; - break; - - default: - pgm_assert_not_reached(); - break; - } - - b = ++p; - continue; - } - - p++; - } - - if (b < e) { - switch (ec++) { - case ENTITY_INTERFACE: - if (parse_interface_entity (family, b, &source_list, &sub_error)) - break; - if (!(sub_error && PGM_ERROR_XDEV == sub_error->code)) - { -/* fall through on multicast */ - if (!(sub_error && PGM_ERROR_NOTUNIQ == sub_error->code)) - { - pgm_propagate_error (error, sub_error); - goto free_lists; - } - pgm_clear_error (&sub_error); - -/* FIXME: too many interfaces */ - if (pgm_list_length (source_list) > 1) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_INVAL, - _("Send group list contains more than one entity.")); - goto free_lists; - } - break; - } - pgm_clear_error (&sub_error); - while (source_list) { - pgm_free (source_list->data); - source_list = pgm_list_delete_link (source_list, source_list); - } - if (!parse_interface_entity (family, NULL, &source_list, &sub_error) && - !(sub_error && PGM_ERROR_NOTUNIQ == sub_error->code)) - { - pgm_propagate_error (error, sub_error); - goto free_lists; - } - ec++; - - case ENTITY_RECEIVE: - if (!parse_receive_entity (family, b, &source_list, recv_list, error)) - goto free_lists; - break; - - case ENTITY_SEND: - if (!parse_send_entity (family, b, &source_list, recv_list, send_list, error)) - goto free_lists; - break; - - default: - pgm_assert_not_reached(); - break; - } - } - - while (ec <= ENTITY_SEND) - { - switch (ec++) { - case ENTITY_INTERFACE: - if (!parse_interface_entity (family, NULL, &source_list, error)) - goto free_lists; - break; - - case ENTITY_RECEIVE: - if (!parse_receive_entity (family, NULL, &source_list, recv_list, error)) - goto free_lists; - break; - - case ENTITY_SEND: - if (!parse_send_entity (family, NULL, &source_list, recv_list, send_list, error)) - goto free_lists; - break; - - default: - pgm_assert_not_reached(); - break; - } - } - - if (pgm_list_length (source_list) > 1) - goto free_lists; - -/* cleanup source interface list */ - while (source_list) { - pgm_free (source_list->data); - source_list = pgm_list_delete_link (source_list, source_list); - } - - return TRUE; - -free_lists: - while (source_list) { - pgm_free (source_list->data); - source_list = pgm_list_delete_link (source_list, source_list); - } - while (*recv_list) { - pgm_free ((*recv_list)->data); - *recv_list = pgm_list_delete_link (*recv_list, *recv_list); - } - while (*send_list) { - pgm_free ((*send_list)->data); - *send_list = pgm_list_delete_link (*send_list, *send_list); - } - return FALSE; -} - -/* create group_source_req as used by pgm_transport_create which specify port, address & interface. - * gsr_source is copied from gsr_group for ASM, caller needs to populate gsr_source for SSM. - * - * returns TRUE on success, returns FALSE on error and sets error appropriately. - */ - -bool -pgm_getaddrinfo ( - const char* restrict network, - const struct pgm_addrinfo_t* const restrict hints, - struct pgm_addrinfo_t** restrict res, - pgm_error_t** restrict error - ) -{ - struct pgm_addrinfo_t* ai; - const int family = hints ? hints->ai_family : AF_UNSPEC; - pgm_list_t* recv_list = NULL; /* */ - pgm_list_t* send_list = NULL; /* */ - - pgm_return_val_if_fail (NULL != network, FALSE); - pgm_return_val_if_fail (AF_UNSPEC == family || AF_INET == family || AF_INET6 == family, FALSE); - pgm_return_val_if_fail (NULL != res, FALSE); - - if (hints) { - pgm_debug ("pgm_getaddrinfo (network:%s%s%s hints: {family:%s} res:%p error:%p)", - network ? "\"" : "", network ? network : "(null)", network ? "\"" : "", - pgm_family_string (family), - (const void*)res, - (const void*)error); - } else { - pgm_debug ("pgm_getaddrinfo (network:%s%s%s hints:%p res:%p error:%p)", - network ? "\"" : "", network ? network : "(null)", network ? "\"" : "", - (const void*)hints, - (const void*)res, - (const void*)error); - } - - if (!network_parse (network, family, &recv_list, &send_list, error)) - return FALSE; - const size_t recv_list_len = pgm_list_length (recv_list); - const size_t send_list_len = pgm_list_length (send_list); - ai = pgm_malloc0 (sizeof(struct pgm_addrinfo_t) + - (recv_list_len + send_list_len) * sizeof(struct group_source_req)); - ai->ai_recv_addrs_len = recv_list_len; - ai->ai_recv_addrs = (void*)((char*)ai + sizeof(struct pgm_addrinfo_t)); - ai->ai_send_addrs_len = send_list_len; - ai->ai_send_addrs = (void*)((char*)ai->ai_recv_addrs + recv_list_len * sizeof(struct group_source_req)); - - size_t i = 0; - while (recv_list) { - memcpy (&ai->ai_recv_addrs[i++], recv_list->data, sizeof(struct group_source_req)); - pgm_free (recv_list->data); - recv_list = pgm_list_delete_link (recv_list, recv_list); - } - i = 0; - while (send_list) { - memcpy (&ai->ai_send_addrs[i++], send_list->data, sizeof(struct group_source_req)); - pgm_free (send_list->data); - send_list = pgm_list_delete_link (send_list, send_list); - } - *res = ai; - return TRUE; -} - -void -pgm_freeaddrinfo ( - struct pgm_addrinfo_t* res - ) -{ - pgm_free (res); -} - - -static -const char* -pgm_family_string ( - const int family - ) -{ - const char* c; - - switch (family) { - case AF_UNSPEC: c = "AF_UNSPEC"; break; - case AF_INET: c = "AF_INET"; break; - case AF_INET6: c = "AF_INET6"; break; - default: c = "(unknown)"; break; - } - - return c; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/if_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/if_unittest.c deleted file mode 100644 index dbaa684..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/if_unittest.c +++ /dev/null @@ -1,1495 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for network interface declaration parsing. - * - * CAUTION: Assumes host is IPv4 by default for AF_UNSPEC - * - * Copyright (c) 2009-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* IFF_UP */ -#define _BSD_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - - -/* mock state */ - -struct mock_host_t { - struct sockaddr_storage address; - char* canonical_hostname; - char* alias; -}; - -struct mock_network_t { - char* name; - struct sockaddr_storage number; - char** aliases; -}; - -struct mock_interface_t { - unsigned int index; - char* name; - unsigned int flags; - struct sockaddr_storage addr; - struct sockaddr_storage netmask; -}; - -static GList *mock_hosts = NULL, *mock_networks = NULL, *mock_interfaces = NULL; - -#define MOCK_HOSTNAME "kiku" -#define MOCK_HOSTNAME6 "ip6-kiku" /* ping6 doesn't work on fe80:: */ -#define MOCK_NETWORK "private" /* /etc/networks */ -#define MOCK_NETWORK6 "ip6-private" -#define MOCK_PGM_NETWORK "pgm-private" -#define MOCK_PGM_NETWORK6 "pgm-ip6-private" -#define MOCK_INTERFACE "eth0" -#define MOCK_INTERFACE_INDEX 2 -#define MOCK_ADDRESS "10.6.28.33" -#define MOCK_GROUP ((in_addr_t) 0xefc00001) /* 239.192.0.1 */ -#define MOCK_GROUP6_INIT { { { 0xff,8,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } } /* ff08::1 */ -static const struct in6_addr mock_group6_addr = MOCK_GROUP6_INIT; -#define MOCK_ADDRESS6 "2002:dce8:d28e::33" -#define MOCK_ADDRESS6_INIT { { { 0x20,2,0xdc,0xe8,0xd2,0x8e,0,0,0,0,0,0,0,0,0,0x33 } } } -static const struct in6_addr mock_address6_addr = MOCK_ADDRESS6_INIT; - -static int mock_family = 0; -static char* mock_kiku = MOCK_HOSTNAME; -static char* mock_localhost = "localhost"; -static char* mock_invalid = "invalid.invalid"; /* RFC 2606 */ -static char* mock_toolong = "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij12345"; /* 65 */ -static char* mock_hostname = NULL; - -struct pgm_ifaddrs_t; -struct pgm_error_t; - -bool mock_pgm_getifaddrs (struct pgm_ifaddrs_t**, struct pgm_error_t**); -void mock_pgm_freeifaddrs (struct pgm_ifaddrs_t*); -unsigned mock_pgm_if_nametoindex (const sa_family_t, const char*); -char* mock_if_indextoname (unsigned int, char*); -int mock_getnameinfo (const struct sockaddr*, socklen_t, char*, size_t, char*, size_t, int); -int mock_getaddrinfo (const char*, const char*, const struct addrinfo*, struct addrinfo**); -void mock_freeaddrinfo (struct addrinfo*); -int mock_gethostname (char*, size_t); -struct netent* mock_getnetbyname (const char*); -bool mock_pgm_if_getnodeaddr (const sa_family_t, struct sockaddr*, const socklen_t, struct pgm_error_t**); - -#define pgm_getifaddrs mock_pgm_getifaddrs -#define pgm_freeifaddrs mock_pgm_freeifaddrs -#define pgm_if_nametoindex mock_pgm_if_nametoindex -#define if_indextoname mock_if_indextoname -#define getnameinfo mock_getnameinfo -#define getaddrinfo mock_getaddrinfo -#define freeaddrinfo mock_freeaddrinfo -#define gethostname mock_gethostname -#define getnetbyname mock_getnetbyname -#define pgm_if_getnodeaddr mock_pgm_if_getnodeaddr - - -#define IF_DEBUG -#include "if.c" - - -static -gpointer -create_host ( - const char* address, - const char* canonical_hostname, - const char* alias - ) -{ - struct mock_host_t* new_host; - - g_assert (address); - g_assert (canonical_hostname); - - new_host = g_slice_alloc0 (sizeof(struct mock_host_t)); - g_assert (pgm_sockaddr_pton (address, (struct sockaddr*)&new_host->address)); - new_host->canonical_hostname = g_strdup (canonical_hostname); - new_host->alias = alias ? g_strdup (alias) : NULL; - - return new_host; -} - -static -gpointer -create_network ( - const char* name, - const char* number - ) -{ - struct mock_network_t* new_network; - - g_assert (name); - g_assert (number); - - new_network = g_slice_alloc0 (sizeof(struct mock_network_t)); - new_network->name = g_strdup (name); - g_assert (pgm_sockaddr_pton (number, (struct sockaddr*)&new_network->number)); - - return new_network; -} - -static -gpointer -create_interface ( - const unsigned index, - const char* name, - const char* flags - ) -{ - struct mock_interface_t* new_interface; - - g_assert (name); - g_assert (flags); - - new_interface = g_slice_alloc0 (sizeof(struct mock_interface_t)); - new_interface->index = index; - new_interface->name = g_strdup (name); - - struct sockaddr_in* sin = (gpointer)&new_interface->addr; - struct sockaddr_in6* sin6 = (gpointer)&new_interface->addr; - - gchar** tokens = g_strsplit (flags, ",", 0); - for (guint i = 0; tokens[i]; i++) - { - if (strcmp (tokens[i], "up") == 0) - new_interface->flags |= IFF_UP; - else if (strcmp (tokens[i], "down") == 0) - new_interface->flags |= 0; - else if (strcmp (tokens[i], "loop") == 0) - new_interface->flags |= IFF_LOOPBACK; - else if (strcmp (tokens[i], "broadcast") == 0) - new_interface->flags |= IFF_BROADCAST; - else if (strcmp (tokens[i], "multicast") == 0) - new_interface->flags |= IFF_MULTICAST; - else if (strncmp (tokens[i], "ip=", strlen("ip=")) == 0) { - const char* addr = tokens[i] + strlen("ip="); - g_assert (pgm_sockaddr_pton (addr, (struct sockaddr*)&new_interface->addr)); - } - else if (strncmp (tokens[i], "netmask=", strlen("netmask=")) == 0) { - const char* addr = tokens[i] + strlen("netmask="); - g_assert (pgm_sockaddr_pton (addr, (struct sockaddr*)&new_interface->netmask)); - } - else if (strncmp (tokens[i], "scope=", strlen("scope=")) == 0) { - const char* scope = tokens[i] + strlen("scope="); - g_assert (AF_INET6 == ((struct sockaddr*)&new_interface->addr)->sa_family); - ((struct sockaddr_in6*)&new_interface->addr)->sin6_scope_id = atoi (scope); - } - else - g_error ("parsing failed for flag %s%s%s", - tokens[i] ? "\"" : "", tokens[i] ? tokens[i] : "(null)", tokens[i] ? "\"" : ""); - } - - g_strfreev (tokens); - return new_interface; -} - -#define APPEND_HOST2(a,b,c) \ - do { \ - gpointer data = create_host ((a), (b), (c)); \ - g_assert (data); \ - mock_hosts = g_list_append (mock_hosts, data); \ - g_assert (mock_hosts); g_assert (mock_hosts->data); \ - } while (0) -#define APPEND_HOST(a,b) APPEND_HOST2((a),(b),NULL) -#define APPEND_NETWORK(a,b) \ - do { \ - gpointer data = create_network ((a), (b)); \ - g_assert (data); \ - mock_networks = g_list_append (mock_networks, data); \ - g_assert (mock_networks); g_assert (mock_networks->data); \ - } while (0) -#define APPEND_INTERFACE(a,b,c) \ - do { \ - gpointer data = create_interface ((a), (b), (c)); \ - g_assert (data); \ - mock_interfaces = g_list_append (mock_interfaces, data); \ - g_assert (mock_interfaces); g_assert (mock_interfaces->data); \ - } while (0) -static -void -mock_setup_net (void) -{ - mock_hostname = mock_kiku; - - APPEND_HOST ( "127.0.0.1", "localhost"); - APPEND_HOST2( "10.6.28.33", "kiku.hk.miru.hk", "kiku"); - APPEND_HOST2( "2002:dce8:d28e::33", "ip6-kiku", "kiku"); - APPEND_HOST2( "172.12.90.1", "mi-hee.ko.miru.hk", "mi-hee"); - APPEND_HOST2( "::1", "ip6-localhost", "ip6-loopback"); - APPEND_HOST ( "239.192.0.1", "PGM.MCAST.NET"); - APPEND_HOST ( "ff08::1", "IP6-PGM.MCAST.NET"); - - APPEND_NETWORK( "loopback", "127.0.0.0"); - APPEND_NETWORK( "private", "10.6.28.0"); - APPEND_NETWORK( "private2", "172.16.90.0"); - APPEND_NETWORK( "pgm-private", "239.192.0.1"); -#ifdef CONFIG_HAVE_IP6_NETWORKS - APPEND_NETWORK( "ip6-private", "2002:dce8:d28e:0:0:0"); - APPEND_NETWORK( "ip6-pgm-private","ff08::1"); -#endif - - APPEND_INTERFACE( 1, "lo", "up,loop"); - APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast"); - APPEND_INTERFACE( 3, "eth1", "down,broadcast,multicast"); - APPEND_INTERFACE( 1, "lo", "up,loop,ip=127.0.0.1,netmask=255.0.0.0"); - APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=10.6.28.33,netmask=255.255.255.0"); - APPEND_INTERFACE( 1, "lo", "up,loop,ip=::1,netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,scope=0"); - APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=2002:dce8:d28e::33,netmask=ffff:ffff:ffff:ffff::0,scope=0"); - APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=fe80::214:5eff:febd:6dda,netmask=ffff:ffff:ffff:ffff::0,scope=2"); -} - -static -void -mock_teardown_net (void) -{ - GList* list; - - list = mock_hosts; - while (list) { - struct mock_host_t* host = list->data; - g_free (host->canonical_hostname); - if (host->alias) - g_free (host->alias); - g_slice_free1 (sizeof(struct mock_host_t), host); - list = list->next; - } - g_list_free (mock_hosts); - - list = mock_networks; - while (list) { - struct mock_network_t* network = list->data; - g_free (network->name); - g_slice_free1 (sizeof(struct mock_network_t), network); - list = list->next; - } - g_list_free (mock_networks); - - list = mock_interfaces; - while (list) { - struct mock_interface_t* interface = list->data; - g_free (interface->name); - g_slice_free1 (sizeof(struct mock_interface_t), interface); - list = list->next; - } - g_list_free (mock_interfaces); -} - -/* mock functions for external references */ - -size_t -pgm_transport_pkt_offset2 ( - const bool can_fragment, - const bool use_pgmcc - ) -{ - return 0; -} - -bool -mock_pgm_getifaddrs ( - struct pgm_ifaddrs_t** ifap, - pgm_error_t** err - ) -{ - if (NULL == ifap) { - return -1; - } - - g_debug ("mock_getifaddrs (ifap:%p err:%p)", (gpointer)ifap, (gpointer)err); - - GList* list = mock_interfaces; - int n = g_list_length (list); - struct pgm_ifaddrs_t* ifa = calloc (n, sizeof(struct pgm_ifaddrs_t)); - struct pgm_ifaddrs_t* ift = ifa; - while (list) { - struct mock_interface_t* interface = list->data; - ift->ifa_addr = (gpointer)&interface->addr; - ift->ifa_name = interface->name; - ift->ifa_flags = interface->flags; - ift->ifa_netmask = (gpointer)&interface->netmask; - list = list->next; - if (list) { - ift->ifa_next = ift + 1; - ift = ift->ifa_next; - } - } - - *ifap = ifa; - return TRUE; -} - -void -mock_pgm_freeifaddrs ( - struct pgm_ifaddrs_t* ifa - ) -{ - free (ifa); -} - -unsigned -mock_pgm_if_nametoindex ( - const sa_family_t iffamily, - const char* ifname - ) -{ - GList* list = mock_interfaces; - while (list) { - const struct mock_interface_t* interface = list->data; - if (0 == strcmp (ifname, interface->name)) - return interface->index; - list = list->next; - } - return 0; -} - -char* -mock_if_indextoname ( - unsigned ifindex, - char* ifname - ) -{ - GList* list = mock_interfaces; - while (list) { - const struct mock_interface_t* interface = list->data; - if (interface->index == ifindex) { - strcpy (ifname, interface->name); - return ifname; - } - list = list->next; - } - errno = ENXIO; - return NULL; -} - -int -mock_getnameinfo ( - const struct sockaddr* sa, - socklen_t salen, - char* host, - size_t hostlen, - char* serv, - size_t servlen, - int flags - ) -{ - if ((0 == hostlen && 0 == servlen) || - (NULL == host && NULL == serv)) - return EAI_NONAME; - - if (flags & NI_NUMERICHOST && flags & NI_NAMEREQD) - return EAI_BADFLAGS; - -/* pre-conditions */ - g_assert (NULL != host); - g_assert (hostlen > 0); - g_assert (NULL == serv); - g_assert (0 == servlen); - - const int sa_family = sa->sa_family; - - if (AF_INET == sa_family) - g_assert (sizeof(struct sockaddr_in) == salen); - else { - g_assert (AF_INET6 == sa_family); - g_assert (sizeof(struct sockaddr_in6) == salen); - } - - if (!(flags & NI_NUMERICHOST)) - { - GList* list = mock_hosts; - while (list) { - const struct mock_host_t* _host = list->data; - const int host_family = ((struct sockaddr*)&_host->address)->sa_family; - const size_t host_len = AF_INET == host_family ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); - - if (host_family == sa_family && - host_len == salen && - 0 == memcmp (sa, &_host->address, salen)) - { - if (hostlen < (1 + strlen(_host->canonical_hostname))) - return EAI_OVERFLOW; - strncpy (host, _host->canonical_hostname, hostlen); - return 0; - } - list = list->next; - } - - if (flags & NI_NAMEREQD) - return EAI_NONAME; - } - - if (AF_INET == sa_family) - pgm_inet_ntop (sa_family, &((const struct sockaddr_in*)sa)->sin_addr, host, hostlen); - else { - const unsigned scope = ((const struct sockaddr_in6*)sa)->sin6_scope_id; - pgm_inet_ntop (sa_family, &((const struct sockaddr_in6*)sa)->sin6_addr, host, hostlen); - if (scope) { - char buffer[1+IF_NAMESIZE]; - strcat (host, "%"); - strcat (host, mock_if_indextoname (scope, buffer)); - } - } - return 0; -} - -int -mock_getaddrinfo ( - const char* node, - const char* service, - const struct addrinfo* hints, - struct addrinfo** res - ) -{ - const int ai_flags = hints ? hints->ai_flags : (AI_V4MAPPED | AI_ADDRCONFIG); - const int ai_family = hints ? hints->ai_family : AF_UNSPEC; - GList* list; - struct sockaddr_storage addr; - - if (NULL == node && NULL == service) - return EAI_NONAME; - -/* pre-conditions */ - g_assert (NULL != node); - g_assert (NULL == service); - g_assert (!(ai_flags & AI_CANONNAME)); - g_assert (!(ai_flags & AI_NUMERICSERV)); - g_assert (!(ai_flags & AI_V4MAPPED)); - - g_message ("mock_getaddrinfo (node:%s%s%s service:%s%s%s hints:%p res:%p)", - node ? "\"" : "", node ? node : "(null)", node ? "\"" : "", - service ? "\"" : "", service ? service : "(null)", service ? "\"" : "", - (gpointer)hints, - (gpointer)res); - - gboolean has_ip4_config; - gboolean has_ip6_config; - - if (hints && hints->ai_flags & AI_ADDRCONFIG) - { - has_ip4_config = has_ip6_config = FALSE; - list = mock_interfaces; - while (list) { - const struct mock_interface_t* interface = list->data; - if (AF_INET == ((struct sockaddr*)&interface->addr)->sa_family) - has_ip4_config = TRUE; - else if (AF_INET6 == ((struct sockaddr*)&interface->addr)->sa_family) - has_ip6_config = TRUE; - if (has_ip4_config && has_ip6_config) - break; - list = list->next; - } - } else { - has_ip4_config = has_ip6_config = TRUE; - } - - if (ai_flags & AI_NUMERICHOST) { - pgm_sockaddr_pton (node, (struct sockaddr*)&addr); - } - list = mock_hosts; - while (list) { - struct mock_host_t* host = list->data; - const int host_family = ((struct sockaddr*)&host->address)->sa_family; - if (((strcmp (host->canonical_hostname, node) == 0) || - (host->alias && strcmp (host->alias, node) == 0) || - (ai_flags & AI_NUMERICHOST && - 0 == pgm_sockaddr_cmp ((struct sockaddr*)&addr, (struct sockaddr*)&host->address))) - && - (host_family == ai_family || AF_UNSPEC == ai_family) && - ((AF_INET == host_family && has_ip4_config) || (AF_INET6 == host_family && has_ip6_config))) - { - struct addrinfo* ai = malloc (sizeof(struct addrinfo)); - memset (ai, 0, sizeof(struct addrinfo)); - ai->ai_family = host_family; - ai->ai_addrlen = AF_INET == host_family ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); - ai->ai_addr = (gpointer)&host->address; - *res = ai; - return 0; - } - list = list->next; - } - return EAI_NONAME; -} - -void -mock_freeaddrinfo ( - struct addrinfo* res - ) -{ - free (res); -} - -int -mock_gethostname ( - char* name, - size_t len - ) -{ - if (NULL == name) { - errno = EFAULT; - return -1; - } - if (len < 0) { - errno = EINVAL; - return -1; - } - if (len < (1 + strlen (mock_hostname))) { - errno = ENAMETOOLONG; - return -1; - } -/* force an error */ - if (mock_hostname == mock_toolong) { - errno = ENAMETOOLONG; - return -1; - } - strncpy (name, mock_hostname, len); - if (len > 0) - name[len - 1] = '\0'; - return 0; -} - -struct netent* -mock_getnetbyname ( - const char* name - ) -{ - static struct netent ne; - GList* list = mock_networks; - - if (NULL == name) - return NULL; - - while (list) { - const struct mock_network_t* network = list->data; - if (strcmp (network->name, name) == 0) { - ne.n_name = network->name; - ne.n_aliases = network->aliases; - ne.n_addrtype = AF_INET; - ne.n_net = g_ntohl (((struct sockaddr_in*)&network->number)->sin_addr.s_addr); - return ≠ - } - list = list->next; - } - return NULL; -} - -PGM_GNUC_INTERNAL -bool -mock_pgm_if_getnodeaddr ( - const sa_family_t family, - struct sockaddr* addr, - const socklen_t cnt, - pgm_error_t** error - ) -{ - switch (family) { - case AF_UNSPEC: - case AF_INET: - ((struct sockaddr*)addr)->sa_family = AF_INET; - ((struct sockaddr_in*)addr)->sin_addr.s_addr = inet_addr(MOCK_ADDRESS); - break; - case AF_INET6: - ((struct sockaddr*)addr)->sa_family = AF_INET6; - ((struct sockaddr_in6*)addr)->sin6_addr = mock_address6_addr; - break; - default: - g_assert_not_reached(); - } - return TRUE; -} - -/* following tests will use AF_UNSPEC address family */ - -static -void -mock_setup_unspec (void) -{ - mock_family = AF_UNSPEC; -} - -/* following tests will use AF_INET address family */ - -static -void -mock_setup_ip4 (void) -{ - mock_family = AF_INET; -} - -/* following tests will use AF_INET6 address family */ - -static -void -mock_setup_ip6 (void) -{ - mock_family = AF_INET6; -} - - -/* return 0 if gsr multicast group does not match the default PGM group for - * the address family, return -1 on no match. - */ - -static -gboolean -match_default_group ( - const int ai_family, - const struct group_source_req* gsr - ) -{ - const struct sockaddr_in sa_default = { - .sin_family = AF_INET, - .sin_addr.s_addr = g_htonl (MOCK_GROUP) - }; - const struct sockaddr_in6 sa6_default = { - .sin6_family = AF_INET6, - .sin6_addr = MOCK_GROUP6_INIT - }; - gboolean is_match = FALSE; - - switch (ai_family) { - case AF_UNSPEC: - case AF_INET: - is_match = (0 == pgm_sockaddr_cmp ((struct sockaddr*)&gsr->gsr_group, (const struct sockaddr*)&sa_default)); - if (!is_match) { - char addr1[INET6_ADDRSTRLEN], addr2[INET6_ADDRSTRLEN]; - pgm_sockaddr_ntop ((struct sockaddr*)&gsr->gsr_group, addr1, sizeof(addr1)); - pgm_sockaddr_ntop ((struct sockaddr*)&sa_default, addr2, sizeof(addr2)); - g_message ("FALSE == cmp(%s%s%s, default-group %s%s%s)", - addr1 ? "\"" : "", addr1 ? addr1 : "(null)", addr1 ? "\"" : "", - addr2 ? "\"" : "", addr2 ? addr2 : "(null)", addr2 ? "\"" : ""); - } - break; - case AF_INET6: - is_match = (0 == pgm_sockaddr_cmp ((struct sockaddr*)&gsr->gsr_group, (const struct sockaddr*)&sa6_default)); - if (!is_match) { - char addr1[INET6_ADDRSTRLEN], addr2[INET6_ADDRSTRLEN]; - pgm_sockaddr_ntop ((struct sockaddr*)&gsr->gsr_group, addr1, sizeof(addr1)); - pgm_sockaddr_ntop ((struct sockaddr*)&sa6_default, addr2, sizeof(addr2)); - g_message ("FALSE == cmp(%s%s%s, default-group %s%s%s)", - addr1 ? "\"" : "", addr1 ? addr1 : "(null)", addr1 ? "\"" : "", - addr2 ? "\"" : "", addr2 ? addr2 : "(null)", addr2 ? "\"" : ""); - } - default: - break; - } - return is_match; -} - -/* return 0 if gsr source inteface does not match the INADDR_ANY reserved - * address, return -1 on no match. - */ - -static -int -match_default_source ( - const int ai_family, - const struct group_source_req* gsr - ) -{ - if (0 != gsr->gsr_interface) - return FALSE; - -/* ASM: source == group */ - return (0 == pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_group, (const struct sockaddr*)&gsr->gsr_source)); -} - -/* return 0 if gsr source interface does not match the hosts default interface, - * return -1 on mismatch - */ - -static -int -match_default_interface ( - const int ai_family, - const struct group_source_req* gsr - ) -{ - if (MOCK_INTERFACE_INDEX != gsr->gsr_interface) - return FALSE; - -/* ASM: source == group */ - return (0 == pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_group, (const struct sockaddr*)&gsr->gsr_source)); -} - -/* target: - * bool - * pgm_getaddrinfo ( - * const char* s, - * const struct pgm_addrinfo_t* const hints, - * struct pgm_addrinfo_t** res, - * pgm_error_t** err - * ) - */ - -struct test_case_t { - const char* ip4; - const char* ip6; -}; - -#define IP4_AND_IP6(x) x, x - -static const struct test_case_t cases_001[] = { - { IP4_AND_IP6("") }, - { IP4_AND_IP6(";") }, - { IP4_AND_IP6(";;") }, - { "239.192.0.1", "ff08::1" }, - { "239.192.0.1", "[ff08::1]" }, - { ";239.192.0.1", ";ff08::1" }, - { ";239.192.0.1", ";[ff08::1]" }, - { ";239.192.0.1;239.192.0.1", ";ff08::1;ff08::1" }, - { ";239.192.0.1;239.192.0.1", ";[ff08::1];[ff08::1]" }, - { "PGM.MCAST.NET", "IP6-PGM.MCAST.NET" }, - { ";PGM.MCAST.NET", ";IP6-PGM.MCAST.NET" }, - { ";PGM.MCAST.NET;PGM.MCAST.NET", ";IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, - { ";239.192.0.1;PGM.MCAST.NET", ";ff08::1;IP6-PGM.MCAST.NET" }, - { ";239.192.0.1;PGM.MCAST.NET", ";[ff08::1];IP6-PGM.MCAST.NET" }, - { ";PGM.MCAST.NET;239.192.0.1", ";IP6-PGM.MCAST.NET;ff08::1" }, - { ";PGM.MCAST.NET;239.192.0.1", ";IP6-PGM.MCAST.NET;[ff08::1]" }, - { "pgm-private", /* ‡ */ "pgm-ip6-private" }, - { ";pgm-private", /* ‡ */ ";pgm-ip6-private" }, - { ";pgm-private;pgm-private", /* ‡ */ ";pgm-ip6-private;pgm-ip6-private" }, - { ";PGM.MCAST.NET;pgm-private", /* ‡ */ ";IP6-PGM.MCAST.NET;pgm-ip6-private" }, - { ";pgm-private;PGM.MCAST.NET", /* ‡ */ ";pgm-ip6-private;IP6-PGM.MCAST.NET" }, - { ";239.192.0.1;pgm-private", /* ‡ */ ";ff08::1;pgm-ip6-private" }, - { ";239.192.0.1;pgm-private", /* ‡ */ ";[ff08::1];pgm-ip6-private" }, - { ";pgm-private;239.192.0.1", /* ‡ */ ";pgm-ip6-private;ff08::1" }, - { ";pgm-private;239.192.0.1", /* ‡ */ ";pgm-ip6-private;[ff08::1]" }, -}; - -START_TEST (test_parse_transport_pass_001) -{ - fail_unless (mock_family == AF_UNSPEC || mock_family == AF_INET || mock_family == AF_INET6, "invalid mock address family"); - - const char* s = (mock_family == AF_INET6) ? cases_001[_i].ip6 : cases_001[_i].ip4; - struct pgm_addrinfo_t hints = { - .ai_family = mock_family - }, *res = NULL; - pgm_error_t* err = NULL; - - g_message ("%i: test_parse_transport_001(%s, %s%s%s)", - _i, - (mock_family == AF_INET6) ? "AF_INET6" : ( (mock_family == AF_INET) ? "AF_INET" : "AF_UNSPEC" ), - s ? "\"" : "", s ? s : "(null)", s ? "\"" : ""); - -/* ‡ Linux does not support IPv6 /etc/networks so IPv6 entries appear as 255.255.255.255 and - * pgm_if_parse_transport will fail. - */ -#ifndef CONFIG_HAVE_IP6_NETWORKS - if (NULL != strstr (s, MOCK_NETWORK6) || NULL != strstr (s, MOCK_PGM_NETWORK6)) - { - g_message ("IPv6 exception, /etc/networks not supported on this platform."); - return; - } -#endif - - gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); - if (!retval) { - g_message ("pgm_getaddrinfo: %s", - (err && err->message) ? err->message : "(null)"); - } - fail_unless (TRUE == retval, "pgm_getaddrinfo failed"); - fail_if (NULL == res, "no result"); - fail_unless (NULL == err, "error raised"); - - fail_unless (1 == res->ai_recv_addrs_len, "not exactly one receive address"); - fail_unless (match_default_group (mock_family, &res->ai_recv_addrs[0]), "receive address not match default group"); - fail_unless (match_default_source (mock_family, &res->ai_recv_addrs[0]), "receive address not match default source"); - fail_unless (1 == res->ai_send_addrs_len, "not exactly one send address"); - fail_unless (match_default_group (mock_family, &res->ai_send_addrs[0]), "send address not match default group"); - fail_unless (match_default_source (mock_family, &res->ai_send_addrs[0]), "send address not match default source"); -} -END_TEST - -/* interface name - * - * pre-condition: interface defined to match running host - * ipv4 and ipv6 hostnames are different, otherwise "" tests might go unexpected. - */ - -static const struct test_case_t cases_002[] = { - { MOCK_INTERFACE, /* † */ MOCK_INTERFACE }, - { MOCK_INTERFACE ";", /* † */ MOCK_INTERFACE ";" }, - { MOCK_INTERFACE ";;", /* † */ MOCK_INTERFACE ";;" }, - { MOCK_INTERFACE ";239.192.0.1", /* † */ MOCK_INTERFACE ";ff08::1" }, - { MOCK_INTERFACE ";239.192.0.1", /* † */ MOCK_INTERFACE ";[ff08::1]" }, - { MOCK_INTERFACE ";239.192.0.1;239.192.0.1", /* † */ MOCK_INTERFACE ";ff08::1;ff08::1" }, - { MOCK_INTERFACE ";239.192.0.1;239.192.0.1", /* † */ MOCK_INTERFACE ";[ff08::1];[ff08::1]" }, - { MOCK_INTERFACE ";PGM.MCAST.NET", /* † */ MOCK_INTERFACE ";IP6-PGM.MCAST.NET" }, - { MOCK_INTERFACE ";PGM.MCAST.NET;PGM.MCAST.NET",/* † */ MOCK_INTERFACE ";IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, - { MOCK_INTERFACE ";239.192.0.1;PGM.MCAST.NET", /* † */ MOCK_INTERFACE ";ff08::1;IP6-PGM.MCAST.NET" }, - { MOCK_INTERFACE ";239.192.0.1;PGM.MCAST.NET", /* † */ MOCK_INTERFACE ";[ff08::1];IP6-PGM.MCAST.NET" }, - { MOCK_INTERFACE ";PGM.MCAST.NET;239.192.0.1", /* † */ MOCK_INTERFACE ";IP6-PGM.MCAST.NET;ff08::1" }, - { MOCK_INTERFACE ";PGM.MCAST.NET;239.192.0.1", /* † */ MOCK_INTERFACE ";IP6-PGM.MCAST.NET;[ff08::1]" }, - { MOCK_INTERFACE ";pgm-private", /* ‡ */ MOCK_INTERFACE ";pgm-ip6-private" }, - { MOCK_INTERFACE ";pgm-private;pgm-private", /* ‡ */ MOCK_INTERFACE ";pgm-ip6-private;pgm-ip6-private" }, - { MOCK_ADDRESS, MOCK_ADDRESS6 }, - { MOCK_ADDRESS, "[" MOCK_ADDRESS6 "]" }, - { MOCK_ADDRESS ";", MOCK_ADDRESS6 ";" }, - { MOCK_ADDRESS ";", "[" MOCK_ADDRESS6 "];" }, - { MOCK_ADDRESS ";;", MOCK_ADDRESS6 ";;" }, - { MOCK_ADDRESS ";;", "[" MOCK_ADDRESS6 "];;" }, - { MOCK_ADDRESS ";239.192.0.1", MOCK_ADDRESS6 ";ff08::1" }, - { MOCK_ADDRESS ";239.192.0.1", "[" MOCK_ADDRESS6 "];[ff08::1]" }, - { MOCK_ADDRESS ";239.192.0.1;239.192.0.1", MOCK_ADDRESS6 ";ff08::1;ff08::1" }, - { MOCK_ADDRESS ";239.192.0.1;239.192.0.1", "[" MOCK_ADDRESS6 "];[ff08::1];[ff08::1]" }, - { MOCK_ADDRESS ";PGM.MCAST.NET", MOCK_ADDRESS6 ";IP6-PGM.MCAST.NET" }, - { MOCK_ADDRESS ";PGM.MCAST.NET", "[" MOCK_ADDRESS6 "];IP6-PGM.MCAST.NET" }, - { MOCK_ADDRESS ";PGM.MCAST.NET;PGM.MCAST.NET", MOCK_ADDRESS6 ";IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, - { MOCK_ADDRESS ";PGM.MCAST.NET;PGM.MCAST.NET", "[" MOCK_ADDRESS6 "];IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, - { MOCK_ADDRESS ";239.192.0.1;PGM.MCAST.NET", MOCK_ADDRESS6 ";ff08::1;IP6-PGM.MCAST.NET" }, - { MOCK_ADDRESS ";239.192.0.1;PGM.MCAST.NET", "[" MOCK_ADDRESS6 "];[ff08::1];IP6-PGM.MCAST.NET" }, - { MOCK_ADDRESS ";PGM.MCAST.NET;239.192.0.1", MOCK_ADDRESS6 ";IP6-PGM.MCAST.NET;ff08::1" }, - { MOCK_ADDRESS ";PGM.MCAST.NET;239.192.0.1", "[" MOCK_ADDRESS6 "];IP6-PGM.MCAST.NET;[ff08::1]" }, - { MOCK_ADDRESS ";pgm-private", MOCK_ADDRESS6 ";pgm-ip6-private" }, - { MOCK_ADDRESS ";pgm-private", "[" MOCK_ADDRESS6 "];pgm-ip6-private" }, - { MOCK_ADDRESS ";pgm-private;pgm-private", MOCK_ADDRESS6 ";pgm-ip6-private;pgm-ip6-private" }, - { MOCK_ADDRESS ";pgm-private;pgm-private", "[" MOCK_ADDRESS6 "];pgm-ip6-private;pgm-ip6-private" }, - { MOCK_NETWORK, /* ‡ */ MOCK_NETWORK6 }, - { MOCK_NETWORK ";", /* ‡ */ MOCK_NETWORK6 ";" }, - { MOCK_NETWORK ";;", /* ‡ */ MOCK_NETWORK6 ";;" }, - { MOCK_NETWORK ";239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";ff08::1" }, - { MOCK_NETWORK ";239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";[ff08::1]" }, - { MOCK_NETWORK ";239.192.0.1;239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";ff08::1;ff08::1" }, - { MOCK_NETWORK ";239.192.0.1;239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";[ff08::1];[ff08::1]" }, - { MOCK_NETWORK ";PGM.MCAST.NET", /* ‡ */ MOCK_NETWORK6 ";IP6-PGM.MCAST.NET" }, - { MOCK_NETWORK ";PGM.MCAST.NET;PGM.MCAST.NET", /* ‡ */ MOCK_NETWORK6 ";IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, - { MOCK_NETWORK ";239.192.0.1;PGM.MCAST.NET", /* ‡ */ MOCK_NETWORK6 ";ff08::1;IP6-PGM.MCAST.NET" }, - { MOCK_NETWORK ";239.192.0.1;PGM.MCAST.NET", /* ‡ */ MOCK_NETWORK6 ";[ff08::1];IP6-PGM.MCAST.NET" }, - { MOCK_NETWORK ";PGM.MCAST.NET;239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";IP6-PGM.MCAST.NET;ff08::1" }, - { MOCK_NETWORK ";PGM.MCAST.NET;239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";IP6-PGM.MCAST.NET;[ff08::1]" }, - { MOCK_NETWORK ";pgm-private", /* ‡ */ MOCK_NETWORK6 ";pgm-ip6-private" }, - { MOCK_NETWORK ";pgm-private;pgm-private", /* ‡ */ MOCK_NETWORK6 ";pgm-ip6-private;pgm-ip6-private" }, - { MOCK_HOSTNAME, MOCK_HOSTNAME6 }, - { MOCK_HOSTNAME ";", MOCK_HOSTNAME6 ";" }, - { MOCK_HOSTNAME ";;", MOCK_HOSTNAME6 ";;" }, - { MOCK_HOSTNAME ";239.192.0.1", MOCK_HOSTNAME6 ";ff08::1" }, - { MOCK_HOSTNAME ";239.192.0.1", MOCK_HOSTNAME6 ";[ff08::1]" }, - { MOCK_HOSTNAME ";239.192.0.1;239.192.0.1", MOCK_HOSTNAME6 ";ff08::1;ff08::1" }, - { MOCK_HOSTNAME ";239.192.0.1;239.192.0.1", MOCK_HOSTNAME6 ";[ff08::1];[ff08::1]" }, - { MOCK_HOSTNAME ";PGM.MCAST.NET", MOCK_HOSTNAME6 ";IP6-PGM.MCAST.NET" }, - { MOCK_HOSTNAME ";PGM.MCAST.NET;PGM.MCAST.NET", MOCK_HOSTNAME6 ";IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, - { MOCK_HOSTNAME ";239.192.0.1;PGM.MCAST.NET", MOCK_HOSTNAME6 ";ff08::1;IP6-PGM.MCAST.NET" }, - { MOCK_HOSTNAME ";239.192.0.1;PGM.MCAST.NET", MOCK_HOSTNAME6 ";[ff08::1];IP6-PGM.MCAST.NET" }, - { MOCK_HOSTNAME ";PGM.MCAST.NET;239.192.0.1", MOCK_HOSTNAME6 ";IP6-PGM.MCAST.NET;ff08::1" }, - { MOCK_HOSTNAME ";PGM.MCAST.NET;239.192.0.1", MOCK_HOSTNAME6 ";IP6-PGM.MCAST.NET;[ff08::1]" }, - { MOCK_HOSTNAME ";pgm-private", MOCK_HOSTNAME6 ";pgm-ip6-private" }, - { MOCK_HOSTNAME ";pgm-private;pgm-private", MOCK_HOSTNAME6 ";pgm-ip6-private;pgm-ip6-private" }, -}; - -START_TEST (test_parse_transport_pass_002) -{ - fail_unless (mock_family == AF_UNSPEC || mock_family == AF_INET || mock_family == AF_INET6, "invalid mock address family"); - - const char* s = (mock_family == AF_INET6) ? cases_002[_i].ip6 : cases_002[_i].ip4; - struct pgm_addrinfo_t hints = { - .ai_family = mock_family - }, *res = NULL; - pgm_error_t* err = NULL; - - g_message ("%i: test_parse_transport_002(%s, %s%s%s)", - _i, - (mock_family == AF_INET6) ? "AF_INET6" : ( (mock_family == AF_INET) ? "AF_INET" : "AF_UNSPEC" ), - s ? "\"" : "", s ? s : "(null)", s ? "\"" : ""); - -/* ‡ Linux does not support IPv6 /etc/networks so IPv6 entries appear as 255.255.255.255 and - * pgm_if_parse_transport will fail. - */ -#ifndef CONFIG_HAVE_IP6_NETWORKS - if (NULL != strstr (s, MOCK_NETWORK6) || NULL != strstr (s, MOCK_PGM_NETWORK6)) - { - g_message ("IPv6 exception, /etc/networks not supported on this platform."); - return; - } -#endif - -/* † Multiple scoped IPv6 interfaces match a simple interface name network parameter and so - * pgm-if_parse_transport will fail finding multiple matching interfaces - */ - if (AF_INET6 == mock_family && 0 == strncmp (s, MOCK_INTERFACE, strlen (MOCK_INTERFACE))) - { - g_message ("IPv6 exception, multiple scoped addresses on one interface"); - fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); - fail_unless (NULL == res, "unexpected result"); - fail_if (NULL == err, "error not raised"); - fail_unless (PGM_ERROR_NOTUNIQ == err->code, "interfaces not found unique"); - return; - } - - fail_unless (TRUE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); - fail_unless (1 == res->ai_recv_addrs_len, "not exactly one receive address"); - fail_unless (match_default_group (mock_family, &res->ai_recv_addrs[0]), "receive address not match default group"); - fail_unless (match_default_interface (mock_family, &res->ai_recv_addrs[0]), "receive address not match default interface"); - fail_unless (1 == res->ai_send_addrs_len, "not exactly one send address"); - fail_unless (match_default_group (mock_family, &res->ai_send_addrs[0]), "send address not match default group"); - fail_unless (match_default_interface (mock_family, &res->ai_send_addrs[0]), "send address not match default interface"); -} -END_TEST - -/* network to node address in bits, 8-32 - * - * e.g. 127.0.0.1/16 - */ - -static const struct test_case_t cases_003[] = { - { MOCK_ADDRESS "/24", MOCK_ADDRESS6 "/64" }, - { MOCK_ADDRESS "/24;", MOCK_ADDRESS6 "/64;" }, - { MOCK_ADDRESS "/24;;", MOCK_ADDRESS6 "/64;;" }, - { MOCK_ADDRESS "/24;239.192.0.1", MOCK_ADDRESS6 "/64;ff08::1" }, - { MOCK_ADDRESS "/24;239.192.0.1", MOCK_ADDRESS6 "/64;[ff08::1]" }, - { MOCK_ADDRESS "/24;239.192.0.1;239.192.0.1", MOCK_ADDRESS6 "/64;ff08::1;ff08::1" }, - { MOCK_ADDRESS "/24;239.192.0.1;239.192.0.1", MOCK_ADDRESS6 "/64;[ff08::1];[ff08::1]" }, - { MOCK_ADDRESS "/24;PGM.MCAST.NET", MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET" }, - { MOCK_ADDRESS "/24;PGM.MCAST.NET;PGM.MCAST.NET",MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, - { MOCK_ADDRESS "/24;239.192.0.1;PGM.MCAST.NET", MOCK_ADDRESS6 "/64;ff08::1;IP6-PGM.MCAST.NET" }, - { MOCK_ADDRESS "/24;239.192.0.1;PGM.MCAST.NET", MOCK_ADDRESS6 "/64;[ff08::1];IP6-PGM.MCAST.NET" }, - { MOCK_ADDRESS "/24;PGM.MCAST.NET;239.192.0.1", MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET;ff08::1" }, - { MOCK_ADDRESS "/24;PGM.MCAST.NET;239.192.0.1", MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET;[ff08::1]" }, - { MOCK_ADDRESS "/24;PGM.MCAST.NET", MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET" }, - { MOCK_ADDRESS "/24;PGM.MCAST.NET;PGM.MCAST.NET",MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, - { MOCK_ADDRESS "/24;pgm-private", /* ‡ */ MOCK_ADDRESS6 "/64;pgm-ip6-private" }, - { MOCK_ADDRESS "/24;pgm-private;pgm-private", /* ‡ */ MOCK_ADDRESS6 "/64;pgm-ip6-private;pgm-ip6-private" }, - { MOCK_ADDRESS "/24;239.192.0.1;pgm-private", /* ‡ */ MOCK_ADDRESS6 "/64;ff08::1;pgm-ip6-private" }, - { MOCK_ADDRESS "/24;239.192.0.1;pgm-private", /* ‡ */ MOCK_ADDRESS6 "/64;[ff08::1];pgm-ip6-private" }, - { MOCK_ADDRESS "/24;pgm-private;239.192.0.1", /* ‡ */ MOCK_ADDRESS6 "/64;pgm-ip6-private;ff08::1" }, - { MOCK_ADDRESS "/24;pgm-private;239.192.0.1", /* ‡ */ MOCK_ADDRESS6 "/64;pgm-ip6-private;[ff08::1]" }, - { MOCK_ADDRESS "/24;PGM.MCAST.NET;pgm-private", /* ‡ */ MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET;pgm-ip6-private" }, - { MOCK_ADDRESS "/24;pgm-private;PGM.MCAST.NET", /* ‡ */ MOCK_ADDRESS6 "/64;pgm-ip6-private;IP6-PGM.MCAST.NET" }, -}; - -START_TEST (test_parse_transport_pass_003) -{ - fail_unless (mock_family == AF_UNSPEC || mock_family == AF_INET || mock_family == AF_INET6, "invalid mock address family"); - - const char* s = (mock_family == AF_INET6) ? cases_003[_i].ip6 : cases_003[_i].ip4; - struct pgm_addrinfo_t hints = { - .ai_family = mock_family - }, *res = NULL; - pgm_error_t* err = NULL; - - g_message ("%i: test_parse_transport_003(%s, %s%s%s)", - _i, - (mock_family == AF_INET6) ? "AF_INET6" : ( (mock_family == AF_INET) ? "AF_INET" : "AF_UNSPEC" ), - s ? "\"" : "", s ? s : "(null)", s ? "\"" : ""); - -/* ‡ Linux does not support IPv6 /etc/networks so IPv6 entries appear as 255.255.255.255 and - * pgm_if_parse_transport will fail. - */ -#ifndef CONFIG_HAVE_IP6_NETWORKS - if (NULL != strstr (s, MOCK_NETWORK6) || NULL != strstr (s, MOCK_PGM_NETWORK6)) - { - g_message ("IPv6 exception, /etc/networks not supported on this platform."); - return; - } -#endif - - gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); - if (!retval) { - g_message ("pgm_getaddrinfo: %s", - (err && err->message) ? err->message : "(null)"); - } - fail_unless (TRUE == retval, "pgm_getaddrinfo failed"); - fail_unless (1 == res->ai_recv_addrs_len, "not exactly one receive address"); - fail_unless (match_default_group (mock_family, &res->ai_recv_addrs[0]), "receive address not match default group"); - fail_unless (match_default_interface (mock_family, &res->ai_recv_addrs[0]), "receive address not match default interface"); - fail_unless (1 == res->ai_send_addrs_len, "not exactly one send address"); - fail_unless (match_default_group (mock_family, &res->ai_send_addrs[0]), "send address not match default group"); - fail_unless (match_default_interface (mock_family, &res->ai_send_addrs[0]), "send address not match default interface"); -} -END_TEST - -/* asymmetric groups - */ - -START_TEST (test_parse_transport_pass_004) -{ - fail_unless (mock_family == AF_UNSPEC || mock_family == AF_INET || mock_family == AF_INET6, "invalid mock address family"); - - const char* s = (mock_family == AF_INET6) ? ";ff08::1;ff08::2" - /* AF_INET */: ";239.192.56.1;239.192.56.2"; - struct pgm_addrinfo_t hints = { - .ai_family = mock_family - }, *res = NULL; - pgm_error_t* err = NULL; - struct sockaddr_storage addr; - - fail_unless (TRUE == pgm_getaddrinfo (s, &hints, &res, &err), "get_transport_info failed"); - fail_unless (1 == res->ai_recv_addrs_len, "not exactly one receive address"); - fail_unless (1 == res->ai_send_addrs_len, "not exactly one send address"); - if (mock_family == AF_INET6) - { - inet_pton (AF_INET6, "ff08::1", &((struct sockaddr_in6*)&addr)->sin6_addr); - ((struct sockaddr*)&addr)->sa_family = mock_family; - ((struct sockaddr_in6*)&addr)->sin6_port = 0; - ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; - ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; - fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); - inet_pton (AF_INET6, "ff08::2", &((struct sockaddr_in6*)&addr)->sin6_addr); - ((struct sockaddr*)&addr)->sa_family = mock_family; - ((struct sockaddr_in6*)&addr)->sin6_port = 0; - ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; - ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; - fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_send_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); - } else { - inet_pton (AF_INET, "239.192.56.1", &((struct sockaddr_in*)&addr)->sin_addr); - ((struct sockaddr*)&addr)->sa_family = AF_INET; - fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); - inet_pton (AF_INET, "239.192.56.2", &((struct sockaddr_in*)&addr)->sin_addr); - ((struct sockaddr*)&addr)->sa_family = AF_INET; - fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_send_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); - } - fail_unless (match_default_source (mock_family, &res->ai_recv_addrs[0]), "source not match"); - fail_unless (match_default_source (mock_family, &res->ai_send_addrs[0]), "source not match"); -} -END_TEST - -/* multiple receive groups and asymmetric sending - */ - -START_TEST (test_parse_transport_pass_005) -{ - fail_unless (mock_family == AF_UNSPEC || mock_family == AF_INET || mock_family == AF_INET6, "invalid mock address family"); - - const char* s = (mock_family == AF_INET6) ? ";ff08::1,ff08::2;ff08::3" - /* AF_INET */: ";239.192.56.1,239.192.56.2;239.192.56.3"; - struct pgm_addrinfo_t hints = { - .ai_family = mock_family - }, *res = NULL; - pgm_error_t* err = NULL; - struct sockaddr_storage addr; - - fail_unless (TRUE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); - fail_unless (2 == res->ai_recv_addrs_len, "not exactly one receive address"); - fail_unless (1 == res->ai_send_addrs_len, "not exactly one send address"); - if (mock_family == AF_INET6) - { - inet_pton (AF_INET6, "ff08::1", &((struct sockaddr_in6*)&addr)->sin6_addr); - ((struct sockaddr*)&addr)->sa_family = mock_family; - ((struct sockaddr_in6*)&addr)->sin6_port = 0; - ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; - ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; - fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); - inet_pton (AF_INET6, "ff08::2", &((struct sockaddr_in6*)&addr)->sin6_addr); - ((struct sockaddr*)&addr)->sa_family = mock_family; - ((struct sockaddr_in6*)&addr)->sin6_port = 0; - ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; - ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; - fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[1].gsr_group, (struct sockaddr*)&addr), "group not match"); - inet_pton (AF_INET6, "ff08::3", &((struct sockaddr_in6*)&addr)->sin6_addr); - ((struct sockaddr*)&addr)->sa_family = mock_family; - ((struct sockaddr_in6*)&addr)->sin6_port = 0; - ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; - ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; - fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_send_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); - } else { - inet_pton (AF_INET, "239.192.56.1", &((struct sockaddr_in*)&addr)->sin_addr); - ((struct sockaddr*)&addr)->sa_family = AF_INET; - fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); - inet_pton (AF_INET, "239.192.56.2", &((struct sockaddr_in*)&addr)->sin_addr); - ((struct sockaddr*)&addr)->sa_family = AF_INET; - fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[1].gsr_group, (struct sockaddr*)&addr), "group not match"); - inet_pton (AF_INET, "239.192.56.3", &((struct sockaddr_in*)&addr)->sin_addr); - ((struct sockaddr*)&addr)->sa_family = AF_INET; - fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_send_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); - } - fail_unless (match_default_source (mock_family, &res->ai_recv_addrs[0]), "source not match"); - fail_unless (match_default_source (mock_family, &res->ai_send_addrs[0]), "source not match"); -} -END_TEST - - -/* too many interfaces - */ -START_TEST (test_parse_transport_fail_001) -{ - const char* s = "eth0,lo;;;"; - struct pgm_addrinfo_t hints = { - .ai_family = AF_UNSPEC - }, *res = NULL; - pgm_error_t* err = NULL; - - fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); - fail_unless (NULL == res, "unexpected result"); -} -END_TEST - -/* invalid characters, or simply just bogus - */ -START_TEST (test_parse_transport_fail_002) -{ - const char* s = "!@#$%^&*()"; - struct pgm_addrinfo_t hints = { - .ai_family = AF_UNSPEC - }, *res = NULL; - pgm_error_t* err = NULL; - - fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); - fail_unless (NULL == res, "unexpected result"); -} -END_TEST - -/* too many groups - */ -START_TEST (test_parse_transport_fail_003) -{ - const char* s = ";239.192.0.1,239.192.0.2,239.192.0.3,239.192.0.4,239.192.0.5,239.192.0.6,239.192.0.7,239.192.0.8,239.192.0.9,239.192.0.10,239.192.0.11,239.192.0.12,239.192.0.13,239.192.0.14,239.192.0.15,239.192.0.16,239.192.0.17,239.192.0.18,239.192.0.19,239.192.0.20;239.192.0.21"; - struct pgm_addrinfo_t hints = { - .ai_family = AF_UNSPEC - }, *res = NULL; - pgm_error_t* err = NULL; - - fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); - fail_unless (NULL == res, "unexpected result"); -} -END_TEST - -/* too many receiver groups in asymmetric pairing - */ -START_TEST (test_parse_transport_fail_004) -{ - const char* s = ";239.192.0.1,239.192.0.2,239.192.0.3,239.192.0.4,239.192.0.5,239.192.0.6,239.192.0.7,239.192.0.8,239.192.0.9,239.192.0.10,239.192.0.11,239.192.0.12,239.192.0.13,239.192.0.14,239.192.0.15,239.192.0.16,239.192.0.17,239.192.0.18,239.192.0.19,239.192.0.20,239.192.0.21;239.192.0.22"; - struct pgm_addrinfo_t hints = { - .ai_family = AF_UNSPEC - }, *res = NULL; - pgm_error_t* err = NULL; - - fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); - fail_unless (NULL == res, "unexpected result"); -} -END_TEST - -/* null string - */ -START_TEST (test_parse_transport_fail_005) -{ - const char* s = NULL; - struct pgm_addrinfo_t hints = { - .ai_family = AF_UNSPEC - }, *res = NULL; - pgm_error_t* err = NULL; - - fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); - fail_unless (NULL == res, "unexpected result"); -} -END_TEST - -/* invalid address family - */ -START_TEST (test_parse_transport_fail_006) -{ - const char* s = ";"; - struct pgm_addrinfo_t hints = { - .ai_family = AF_IPX - }, *res = NULL; - pgm_error_t* err = NULL; - - fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); - fail_unless (NULL == res, "unexpected result"); -} -END_TEST - -/* invalid transport info pointer - */ -START_TEST (test_parse_transport_fail_007) -{ - const char* s = ";"; - pgm_error_t* err = NULL; - - fail_unless (FALSE == pgm_getaddrinfo (s, NULL, NULL, &err), "pgm_getaddrinfo failed"); -} -END_TEST - -/* invalid interface - */ -START_TEST (test_parse_transport_fail_008) -{ - const char* s = "qe0;"; - struct pgm_addrinfo_t hints = { - .ai_family = AF_UNSPEC - }, *res = NULL; - pgm_error_t* err = NULL; - - gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); - if (!retval) { - g_message ("pgm_getaddrinfo: %s", err ? err->message : "(null)"); - } - fail_unless (FALSE == retval, "pgm_getaddrinfo failed"); - fail_unless (NULL == res, "unexpected result"); -} -END_TEST - -/* non-existing interface IP address - */ -START_TEST (test_parse_transport_fail_009) -{ - const char* s = "172.16.90.1;"; - struct pgm_addrinfo_t hints = { - .ai_family = AF_UNSPEC - }, *res = NULL; - pgm_error_t* err = NULL; - - gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); - if (!retval) { - g_message ("pgm_getaddrinfo: %s", - (err && err->message) ? err->message : "(null)"); - } - fail_unless (FALSE == retval, "pgm_getaddrinfo failed"); - fail_unless (NULL == res, "unexpected result"); -} -END_TEST - -/* non-existing network name address - */ -START_TEST (test_parse_transport_fail_010) -{ - const char* s = "private2;"; - struct pgm_addrinfo_t hints = { - .ai_family = AF_UNSPEC - }, *res = NULL; - pgm_error_t* err = NULL; - - gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); - if (!retval) { - g_message ("pgm_getaddrinfo: %s", - (err && err->message) ? err->message : "(null)"); - } - fail_unless (FALSE == retval, "pgm_getaddrinfo failed"); - fail_unless (NULL == res, "unexpected result"); -} -END_TEST - -/* non-existing host name interface - */ -START_TEST (test_parse_transport_fail_011) -{ - const char* s = "mi-hee.ko.miru.hk;"; - struct pgm_addrinfo_t hints = { - .ai_family = AF_UNSPEC - }, *res = NULL; - pgm_error_t* err = NULL; - - gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); - if (!retval) { - g_message ("pgm_getaddrinfo: %s", - (err && err->message) ? err->message : "(null)"); - } - fail_unless (FALSE == retval, "pgm_getaddrinfo failed"); - fail_unless (NULL == res, "unexpected result"); -} -END_TEST - -/* target: - * pgm_if_print_all (void) - */ - -START_TEST (test_print_all_pass_001) -{ - pgm_if_print_all (); -} -END_TEST - - -/* target: - * bool - * is_in_net ( - * const struct in_addr* addr, -- in host byte order - * const struct in_addr* netaddr, - * const struct in_addr* netmask - * ) - */ - -struct test_case_net_t { - const char* addr; - const char* netaddr; - const char* netmask; - const gboolean answer; -}; - -static const struct test_case_net_t cases_004[] = { - { "127.0.0.1", "127.0.0.1", "255.0.0.0", TRUE }, - { "127.0.0.1", "127.0.0.1", "255.255.0.0", TRUE }, - { "127.0.0.1", "127.0.0.1", "255.255.255.0", TRUE }, - { "127.0.0.1", "127.0.0.1", "255.255.255.255", TRUE }, - { "127.0.0.1", "127.0.0.0", "255.0.0.0", TRUE }, - { "127.0.0.1", "127.0.0.0", "255.255.0.0", TRUE }, - { "127.0.0.1", "127.0.0.0", "255.255.255.0", TRUE }, - { "127.0.0.1", "127.0.0.0", "255.255.255.255", FALSE }, - { "172.15.1.1", "172.16.0.0", "255.240.0.0", FALSE }, - { "172.16.1.1", "172.16.0.0", "255.240.0.0", TRUE }, - { "172.18.1.1", "172.16.0.0", "255.240.0.0", TRUE }, - { "172.31.1.1", "172.16.0.0", "255.240.0.0", TRUE }, - { "172.32.1.1", "172.16.0.0", "255.240.0.0", FALSE }, -}; - -START_TEST (test_is_in_net_pass_001) -{ - struct in_addr addr, netaddr, netmask; - fail_unless (pgm_inet_pton (AF_INET, cases_004[_i].addr, &addr)); - fail_unless (pgm_inet_pton (AF_INET, cases_004[_i].netaddr, &netaddr)); - fail_unless (pgm_inet_pton (AF_INET, cases_004[_i].netmask, &netmask)); - const gboolean answer = cases_004[_i].answer; - - addr.s_addr = g_ntohl (addr.s_addr); - netaddr.s_addr = g_ntohl (netaddr.s_addr); - netmask.s_addr = g_ntohl (netmask.s_addr); - gboolean result = is_in_net (&addr, &netaddr, &netmask); - - g_message ("result %s (%s)", - result ? "TRUE" : "FALSE", - answer ? "TRUE" : "FALSE"); - - fail_unless (answer == result); -} -END_TEST - -static const struct test_case_net_t cases_005[] = { - { "::1", "::1", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", TRUE }, - { "fe80::203:baff:fe4e:6cc8", "fe80::", "ffff:0000:0000:0000:0000:0000:0000:0000", TRUE }, - { "2002:dec8:d28e::36", "2002:dec8:d28e::", "ffff:ffff:ffff:0000:0000:0000:0000:0000", TRUE }, - { "2002:dec8:d28e::36", "2002:dafa:939:0::", "ffff:ffff:ffff:ffff:0000:0000:0000:0000", FALSE }, -}; - -START_TEST (test_is_in_net6_pass_001) -{ - struct in6_addr addr, netaddr, netmask; - fail_unless (pgm_inet_pton (AF_INET6, cases_005[_i].addr, &addr)); - fail_unless (pgm_inet_pton (AF_INET6, cases_005[_i].netaddr, &netaddr)); - fail_unless (pgm_inet_pton (AF_INET6, cases_005[_i].netmask, &netmask)); - const gboolean answer = cases_005[_i].answer; - - gboolean result = is_in_net6 (&addr, &netaddr, &netmask); - - g_message ("result %s (%s)", - result ? "TRUE" : "FALSE", - answer ? "TRUE" : "FALSE"); - - fail_unless (answer == result); -} -END_TEST - - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_is_in_net = tcase_create ("is_in_net"); - suite_add_tcase (s, tc_is_in_net); - tcase_add_checked_fixture (tc_is_in_net, mock_setup_net, mock_teardown_net); - tcase_add_checked_fixture (tc_is_in_net, mock_setup_unspec, NULL); - tcase_add_loop_test (tc_is_in_net, test_is_in_net_pass_001, 0, G_N_ELEMENTS(cases_004)); - - TCase* tc_is_in_net6 = tcase_create ("is_in_net6"); - suite_add_tcase (s, tc_is_in_net6); - tcase_add_checked_fixture (tc_is_in_net6, mock_setup_net, mock_teardown_net); - tcase_add_checked_fixture (tc_is_in_net6, mock_setup_unspec, NULL); - tcase_add_loop_test (tc_is_in_net6, test_is_in_net6_pass_001, 0, G_N_ELEMENTS(cases_005)); - -/* three variations of all parse-transport tests, one for each valid - * address family value: AF_UNSPEC, AF_INET, AF_INET6. - */ - -/* unspecified address family, ai_family == AF_UNSPEC */ - TCase* tc_parse_transport_unspec = tcase_create ("parse_transport/unspec"); - suite_add_tcase (s, tc_parse_transport_unspec); - tcase_add_checked_fixture (tc_parse_transport_unspec, mock_setup_net, mock_teardown_net); - tcase_add_checked_fixture (tc_parse_transport_unspec, mock_setup_unspec, NULL); - tcase_add_loop_test (tc_parse_transport_unspec, test_parse_transport_pass_001, 0, G_N_ELEMENTS(cases_001)); - tcase_add_loop_test (tc_parse_transport_unspec, test_parse_transport_pass_002, 0, G_N_ELEMENTS(cases_002)); - tcase_add_loop_test (tc_parse_transport_unspec, test_parse_transport_pass_003, 0, G_N_ELEMENTS(cases_003)); - tcase_add_test (tc_parse_transport_unspec, test_parse_transport_pass_004); - tcase_add_test (tc_parse_transport_unspec, test_parse_transport_pass_005); - tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_001); - tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_002); - tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_003); - tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_004); - tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_005); - tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_006); - tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_007); - tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_008); - tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_009); - tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_010); - tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_011); - -/* IP version 4, ai_family = AF_INET */ - TCase* tc_parse_transport_ip4 = tcase_create ("parse_transport/af_inet"); - suite_add_tcase (s, tc_parse_transport_ip4); - tcase_add_checked_fixture (tc_parse_transport_ip4, mock_setup_net, mock_teardown_net); - tcase_add_checked_fixture (tc_parse_transport_ip4, mock_setup_ip4, NULL); - tcase_add_loop_test (tc_parse_transport_ip4, test_parse_transport_pass_001, 0, G_N_ELEMENTS(cases_001)); - tcase_add_loop_test (tc_parse_transport_ip4, test_parse_transport_pass_002, 0, G_N_ELEMENTS(cases_002)); - tcase_add_loop_test (tc_parse_transport_ip4, test_parse_transport_pass_003, 0, G_N_ELEMENTS(cases_003)); - tcase_add_test (tc_parse_transport_ip4, test_parse_transport_pass_004); - tcase_add_test (tc_parse_transport_ip4, test_parse_transport_pass_005); - -/* IP version 6, ai_family = AF_INET6 */ - TCase* tc_parse_transport_ip6 = tcase_create ("parse_transport/af_inet6"); - suite_add_tcase (s, tc_parse_transport_ip6); - tcase_add_checked_fixture (tc_parse_transport_ip6, mock_setup_net, mock_teardown_net); - tcase_add_checked_fixture (tc_parse_transport_ip6, mock_setup_ip6, NULL); - tcase_add_loop_test (tc_parse_transport_ip6, test_parse_transport_pass_001, 0, G_N_ELEMENTS(cases_001)); - tcase_add_loop_test (tc_parse_transport_ip6, test_parse_transport_pass_002, 0, G_N_ELEMENTS(cases_002)); - tcase_add_loop_test (tc_parse_transport_ip6, test_parse_transport_pass_003, 0, G_N_ELEMENTS(cases_003)); - tcase_add_test (tc_parse_transport_ip6, test_parse_transport_pass_004); - tcase_add_test (tc_parse_transport_ip6, test_parse_transport_pass_005); - - TCase* tc_print_all = tcase_create ("print-all"); - tcase_add_checked_fixture (tc_print_all, mock_setup_net, mock_teardown_net); - suite_add_tcase (s, tc_print_all); - tcase_add_test (tc_print_all, test_print_all_pass_001); - - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/checksum.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/checksum.h deleted file mode 100644 index 67f71a6..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/checksum.h +++ /dev/null @@ -1,75 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * PGM checksum routines - * - * Copyright (c) 2006-2008 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_CHECKSUM_H__ -#define __PGM_IMPL_CHECKSUM_H__ - -#include - -PGM_BEGIN_DECLS - -uint16_t pgm_inet_checksum (const void*, uint16_t, uint16_t); -uint16_t pgm_csum_fold (uint32_t) PGM_GNUC_CONST; -uint32_t pgm_csum_block_add (uint32_t, uint32_t, const uint16_t) PGM_GNUC_CONST; -uint32_t pgm_compat_csum_partial (const void*, uint16_t, uint32_t); -uint32_t pgm_compat_csum_partial_copy (const void*restrict, void*restrict, uint16_t, uint32_t); - -static inline uint32_t add32_with_carry (uint32_t, uint32_t) PGM_GNUC_CONST; - -#if defined(__x86_64__) || defined(__i386__) || defined(__i386) || defined(__amd64) -static inline uint32_t add32_with_carry (uint32_t a, uint32_t b) -{ - asm("addl %2, %0 \n\t" - "adcl $0, %0" - : "=r" (a) /* output operands */ - : "0" (a), "r" (b)); /* input operands */ - return a; -} -#elif defined(__sparc__) || defined(__sparc) || defined(__sparcv9) -static inline uint32_t add32_with_carry (uint32_t a, uint32_t b) -{ - asm("addcc %2, %0, %0 \n\t" - "addx %0, %%g0, %0" - : "=r" (a) /* output operands */ - : "0" (a), "r" (b) /* input operands */ - : "cc"); /* list of clobbered registers */ - return a; -} -#else -static inline uint32_t add32_with_carry (uint32_t a, uint32_t b) -{ - a += b; - a = (a >> 16) + (a & 0xffff); - return a; -} -#endif - -# define pgm_csum_partial pgm_compat_csum_partial -# define pgm_csum_partial_copy pgm_compat_csum_partial_copy - -PGM_END_DECLS - -#endif /* __PGM_IMPL_CHECKSUM_H__ */ - diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/engine.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/engine.h deleted file mode 100644 index eeacbe7..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/engine.h +++ /dev/null @@ -1,43 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * PGM engine. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_IMPL_ENGINE_H__ -#define __PGM_IMPL_ENGINE_H__ - -#ifdef _WIN32 -# include -# include -#endif -#include - -PGM_BEGIN_DECLS - -#ifdef _WIN32 -extern LPFN_WSARECVMSG pgm_WSARecvMsg; -#endif - -#ifdef PGM_DEBUG -extern unsigned pgm_loss_rate; -#endif - -PGM_END_DECLS - -#endif /* __PGM_IMPL_ENGINE_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/features.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/features.h deleted file mode 100644 index a8dadf7..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/features.h +++ /dev/null @@ -1,42 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * Compiler feature flags. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_FEATURES_H__ -#define __PGM_IMPL_FEATURES_H__ - -#if defined(_POSIX_C_SOURCE) || defined(__POSIX_VISIBLE) -# if (_POSIX_C_SOURCE - 0) >= 200112L || (__POSIX_VISIBLE - 0) >= 200112L -# define CONFIG_HAVE_FTIME 1 -# define CONFIG_HAVE_GETTIMEOFDAY 1 -# endif -# if (_POSIX_C_SOURCE - 0) >= 199309L || (__POSIX_VISIBLE - 0) >= 199309L -# define CONFIG_HAVE_CLOCK_GETTIME 1 -# endif -#endif -#if defined(_WIN32) -# define CONFIG_HAVE_FTIME -#endif - -#endif /* __PGM_IMPL_FEATURES_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/fixed.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/fixed.h deleted file mode 100644 index f84ef33..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/fixed.h +++ /dev/null @@ -1,140 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * 8-bit and 16-bit shift fixed point math - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_FIXED_H__ -#define __PGM_IMPL_FIXED_H__ - -#include - -PGM_BEGIN_DECLS - -static inline uint_fast32_t pgm_fp8 (unsigned) PGM_GNUC_CONST; -static inline uint_fast32_t pgm_fp16 (unsigned) PGM_GNUC_CONST; -static inline unsigned pgm_fp8tou (uint_fast32_t) PGM_GNUC_CONST; -static inline unsigned pgm_fp16tou (uint_fast32_t) PGM_GNUC_CONST; -static inline uint_fast32_t pgm_fp8mul (uint_fast32_t, uint_fast32_t) PGM_GNUC_CONST; -static inline uint_fast32_t pgm_fp16mul (uint_fast32_t, uint_fast32_t) PGM_GNUC_CONST; -static inline uint_fast32_t pgm_fp8div (uint_fast32_t, uint_fast32_t) PGM_GNUC_CONST; -static inline uint_fast32_t pgm_fp16div (uint_fast32_t, uint_fast32_t) PGM_GNUC_CONST; -static inline uint_fast32_t pgm_fp16pow (uint_fast32_t, uint_fast32_t) PGM_GNUC_CONST; - -static inline -uint_fast32_t -pgm_fp8 ( - unsigned v - ) -{ - return (uint32_t)(v << 8); -} - -static inline -uint_fast32_t -pgm_fp16 ( - unsigned v - ) -{ - return (uint_fast32_t)(v << 16); -} - -static inline -unsigned -pgm_fp8tou ( - uint_fast32_t f - ) -{ - return (f + (1 << 7)) >> 8; -} - -static inline -unsigned -pgm_fp16tou ( - uint_fast32_t f - ) -{ - return (f + (1 << 15)) >> 16; -} - -static inline -uint_fast32_t -pgm_fp8mul ( - uint_fast32_t a, - uint_fast32_t b - ) -{ - return ( a * b + 128 ) >> 8; -} - -static inline -uint_fast32_t -pgm_fp16mul ( - uint_fast32_t a, - uint_fast32_t b - ) -{ - return ( a * b + 32768 ) >> 16; -} - -static inline -uint_fast32_t -pgm_fp8div ( - uint_fast32_t a, - uint_fast32_t b - ) -{ - return ( ( (a << 9) / b ) + 1 ) / 2; -} - -static inline -uint_fast32_t -pgm_fp16div ( - uint_fast32_t a, - uint_fast32_t b - ) -{ - return ( ( (a << 17) / b ) + 1 ) / 2; -} - -static inline -uint_fast32_t -pgm_fp16pow ( - uint_fast32_t x, - uint_fast32_t y - ) -{ - uint_fast32_t result = pgm_fp16 (1); - for (uint_fast32_t i = x; - y; - y >>= 1) - { - if (y & 1) - result = (result * i + 32768) >> 16; - i = (i * i + 32768) >> 16; - } - return result; -} - -PGM_END_DECLS - -#endif /* __PGM_IMPL_FIXED_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/framework.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/framework.h deleted file mode 100644 index 951ad26..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/framework.h +++ /dev/null @@ -1,76 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * Framework collection. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_IMPL_FRAMEWORK_H__ -#define __PGM_IMPL_FRAMEWORK_H__ - -#define __PGM_IMPL_FRAMEWORK_H_INSIDE__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#undef __PGM_IMPL_FRAMEWORK_H_INSIDE__ - -#endif /* __PGM_IMPL_FRAMEWORK_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/galois.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/galois.h deleted file mode 100644 index 2f008ed..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/galois.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Galois field maths. - * - * Copyright (c) 2006-2008 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_GALOIS_H__ -#define __PGM_IMPL_GALOIS_H__ - -#include - -PGM_BEGIN_DECLS - -/* 8 bit wide galois field integer: GF(2⁸) */ -typedef uint8_t __attribute__((__may_alias__)) pgm_gf8_t; - -/* E denotes the encoding symbol length in bytes. - * S denotes the symbol size in units of m-bit elements. When m = 8, - * then S and E are equal. - */ -#define PGM_GF_ELEMENT_BYTES sizeof(pgm_gf8_t) - -/* m defines the length of the elements in the finite field, in bits. - * m belongs to {2..16}. - */ -#define PGM_GF_ELEMENT_BITS ( 8 * PGM_GF_ELEMENT_BYTES ) - -/* q defines the number of elements in the finite field. - */ -#define PGM_GF_NO_ELEMENTS ( 1 << PGM_GF_ELEMENT_BITS ) -#define PGM_GF_MAX ( PGM_GF_NO_ELEMENTS - 1 ) - - -extern const pgm_gf8_t pgm_gflog[PGM_GF_NO_ELEMENTS]; -extern const pgm_gf8_t pgm_gfantilog[PGM_GF_NO_ELEMENTS]; - -#ifdef CONFIG_GALOIS_MUL_LUT -extern const pgm_gf8_t pgm_gftable[PGM_GF_NO_ELEMENTS * PGM_GF_NO_ELEMENTS]; -#endif - -/* In a finite field with characteristic 2, addition and subtraction are - * identical, and are accomplished using the XOR operator. - */ -static inline -pgm_gf8_t -pgm_gfadd ( - pgm_gf8_t a, - pgm_gf8_t b - ) -{ - return a ^ b; -} - -static inline -pgm_gf8_t -pgm_gfadd_equals ( - pgm_gf8_t a, - pgm_gf8_t b - ) -{ - return a ^= b; -} - -static inline -pgm_gf8_t -pgm_gfsub ( - pgm_gf8_t a, - pgm_gf8_t b - ) -{ - return pgm_gfadd (a, b); -} - -static inline -pgm_gf8_t -pgm_gfsub_equals ( - pgm_gf8_t a, - pgm_gf8_t b - ) -{ - return pgm_gfadd_equals (a, b); -} - -static inline -pgm_gf8_t -pgm_gfmul ( - pgm_gf8_t a, - pgm_gf8_t b - ) -{ - if (PGM_UNLIKELY( !(a && b) )) { - return 0; - } - -#ifdef CONFIG_GALOIS_MUL_LUT - return pgm_gftable[ (uint16_t)a << 8 | (uint16_t)b ]; -#else - unsigned sum = pgm_gflog[ a ] + pgm_gflog[ b ]; - return sum >= PGM_GF_MAX ? pgm_gfantilog[ sum - PGM_GF_MAX ] : pgm_gfantilog[ sum ]; -#endif -} - -static inline -pgm_gf8_t -pgm_gfdiv ( - pgm_gf8_t a, - pgm_gf8_t b - ) -{ - if (PGM_UNLIKELY( !a )) { - return 0; - } - - int sum = pgm_gflog[ a ] - pgm_gflog[ b ]; - return sum < 0 ? pgm_gfantilog[ sum + PGM_GF_MAX ] : pgm_gfantilog[ sum ]; -} - -PGM_END_DECLS - -#endif /* __PGM_IMPL_GALOIS_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/getifaddrs.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/getifaddrs.h deleted file mode 100644 index 4732a21..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/getifaddrs.h +++ /dev/null @@ -1,73 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable getifaddrs - * - * Copyright (c) 2006-2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_GETIFADDRS_H__ -#define __PGM_IMPL_GETIFADDRS_H__ - -#ifndef _WIN32 -# include -# include -# include -#else -# include -#endif - -struct pgm_ifaddrs_t; - -#include -#include - -PGM_BEGIN_DECLS - -#ifndef IF_NAMESIZE -# ifdef IFNAMSIZ -# define IF_NAMESIZE IFNAMSIZ -# elif defined(_WIN32) -# define IF_NAMESIZE 40 -# else -# define IF_NAMESIZE 16 -# endif -#endif - -struct pgm_ifaddrs_t -{ - struct pgm_ifaddrs_t* ifa_next; /* Pointer to the next structure. */ - - char* ifa_name; /* Name of this network interface. */ - unsigned int ifa_flags; /* Flags as from SIOCGIFFLAGS ioctl. */ - -#ifdef ifa_addr -# undef ifa_addr -#endif - struct sockaddr* ifa_addr; /* Network address of this interface. */ - struct sockaddr* ifa_netmask; /* Netmask of this interface. */ -}; - -bool pgm_getifaddrs (struct pgm_ifaddrs_t**restrict, pgm_error_t**restrict); -void pgm_freeifaddrs (struct pgm_ifaddrs_t*); - -PGM_END_DECLS - -#endif /* __PGM_IMPL_GETIFADDRS_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/getnodeaddr.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/getnodeaddr.h deleted file mode 100644 index befcf35..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/getnodeaddr.h +++ /dev/null @@ -1,41 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable function to return node IP address. - * - * Copyright (c) 2006-2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_GETNODEADDR_H__ -#define __PGM_IMPL_GETNODEADDR_H__ - -#ifndef _WIN32 -# include -#endif -#include -#include - -PGM_BEGIN_DECLS - -PGM_GNUC_INTERNAL bool pgm_if_getnodeaddr (const sa_family_t, struct sockaddr*restrict, const socklen_t, pgm_error_t**restrict); - -PGM_END_DECLS - -#endif /* __PGM_IMPL_GETNODEADDR_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/hashtable.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/hashtable.h deleted file mode 100644 index 4271cb6..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/hashtable.h +++ /dev/null @@ -1,58 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable hash table. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_HASHTABLE_H__ -#define __PGM_IMPL_HASHTABLE_H__ - -#include - -PGM_BEGIN_DECLS - -typedef struct pgm_hashtable_t pgm_hashtable_t; -typedef uint_fast32_t pgm_hash_t; - -typedef pgm_hash_t (*pgm_hashfunc_t) (const void*); -typedef bool (*pgm_equalfunc_t) (const void*restrict, const void*restrict); - -PGM_GNUC_INTERNAL pgm_hashtable_t* pgm_hashtable_new (pgm_hashfunc_t, pgm_equalfunc_t); -PGM_GNUC_INTERNAL void pgm_hashtable_destroy (pgm_hashtable_t*); -PGM_GNUC_INTERNAL void pgm_hashtable_insert (pgm_hashtable_t*restrict, const void*restrict, void*restrict); -PGM_GNUC_INTERNAL bool pgm_hashtable_remove (pgm_hashtable_t*restrict, const void*restrict); -PGM_GNUC_INTERNAL void pgm_hashtable_remove_all (pgm_hashtable_t*); -PGM_GNUC_INTERNAL void* pgm_hashtable_lookup (const pgm_hashtable_t*restrict, const void*restrict); -PGM_GNUC_INTERNAL void* pgm_hashtable_lookup_extended (const pgm_hashtable_t*restrict, const void*restrict, void*restrict); -PGM_GNUC_INTERNAL void pgm_hashtable_unref (pgm_hashtable_t*); - -/* Hash Functions - */ - -PGM_GNUC_INTERNAL bool pgm_str_equal (const void*restrict, const void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL pgm_hash_t pgm_str_hash (const void*) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL bool pgm_int_equal (const void*restrict, const void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL pgm_hash_t pgm_int_hash (const void*) PGM_GNUC_WARN_UNUSED_RESULT; - -PGM_END_DECLS - -#endif /* __PGM_IMPL_HASHTABLE_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/histogram.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/histogram.h deleted file mode 100644 index 92ddeb9..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/histogram.h +++ /dev/null @@ -1,129 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * histograms. - * - * Copyright (c) 2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_HISTOGRAM_H__ -#define __PGM_IMPL_HISTOGRAM_H__ - -#include -#include -#include -#include - -PGM_BEGIN_DECLS - -typedef int pgm_sample_t; -typedef int pgm_count_t; - -struct pgm_sample_set_t { - pgm_count_t* counts; - unsigned counts_len; - int64_t sum; - int64_t square_sum; -}; - -typedef struct pgm_sample_set_t pgm_sample_set_t; - -struct pgm_histogram_t { - const char* restrict histogram_name; - unsigned bucket_count; - pgm_sample_t declared_min; - pgm_sample_t declared_max; - pgm_sample_t* restrict ranges; - pgm_sample_set_t sample; - bool is_registered; - pgm_slist_t histograms_link; -}; - -typedef struct pgm_histogram_t pgm_histogram_t; - -#define PGM_HISTOGRAM_DEFINE(name, minimum, maximum, count) \ - static pgm_count_t counts[ (count) ]; \ - static pgm_sample_t ranges[ (count) + 1 ]; \ - static pgm_histogram_t counter = { \ - .histogram_name = (name), \ - .bucket_count = (count), \ - .declared_min = (minimum), \ - .declared_max = (maximum), \ - .ranges = ranges, \ - .sample = { \ - .counts = counts, \ - .counts_len = (count), \ - .sum = 0, \ - .square_sum = 0 \ - }, \ - .is_registered = FALSE \ - } - -#ifdef CONFIG_HISTOGRAMS - -# define PGM_HISTOGRAM_TIMES(name, sample) do { \ - PGM_HISTOGRAM_DEFINE(name, pgm_msecs(1), pgm_secs(10), 50); \ - if (!counter.is_registered) { \ - memset (counts, 0, sizeof(counts)); \ - memset (ranges, 0, sizeof(ranges)); \ - pgm_histogram_init (&counter); \ - } \ - pgm_histogram_add_time (&counter, sample); \ - } while (0) - -# define PGM_HISTOGRAM_COUNTS(name, sample) do { \ - PGM_HISTOGRAM_DEFINE(name, 1, 1000000, 50); \ - if (!counter.is_registered) { \ - memset (counts, 0, sizeof(counts)); \ - memset (ranges, 0, sizeof(ranges)); \ - pgm_histogram_init (&counter); \ - } \ - pgm_histogram_add (&counter, (sample)); \ - } while (0) - -#else /* !CONFIG_HISTOGRAMS */ - -# define PGM_HISTOGRAM_TIMES(name, sample) -# define PGM_HISTOGRAM_COUNTS(name, sample) - -#endif /* !CONFIG_HISTOGRAMS */ - - -extern pgm_slist_t* pgm_histograms; - -void pgm_histogram_init (pgm_histogram_t*); -void pgm_histogram_add (pgm_histogram_t*, int); -void pgm_histogram_write_html_graph_all (pgm_string_t*); - -static inline -void -pgm_histogram_add_time ( - pgm_histogram_t*const histogram, - pgm_time_t sample_time - ) -{ - pgm_histogram_add (histogram, (int)pgm_to_msecs (sample_time)); -} - -PGM_END_DECLS - -#endif /* __PGM_IMPL_HISTOGRAM_H__ */ - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/i18n.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/i18n.h deleted file mode 100644 index c97f3de..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/i18n.h +++ /dev/null @@ -1,32 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * i18n & l10n support - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_IMPL_I18N_H__ -#define __PGM_IMPL_I18N_H__ - -#ifdef CONFIG_HAVE_GETTEXT -# include -# define _(String) dgettext (GETTEXT_PACKAGE, String) -#else -# define _(String) (String) -#endif - -#endif /* __PGM_IMPL_I18N_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/indextoaddr.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/indextoaddr.h deleted file mode 100644 index 6c6599e..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/indextoaddr.h +++ /dev/null @@ -1,41 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable interface index to socket address function. - * - * Copyright (c) 2006-2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_INDEXTOADDR_H__ -#define __PGM_IMPL_INDEXTOADDR_H__ - -#ifndef _WIN32 -# include -#endif -#include -#include - -PGM_BEGIN_DECLS - -PGM_GNUC_INTERNAL bool pgm_if_indextoaddr (unsigned, sa_family_t, uint32_t, struct sockaddr*restrict, pgm_error_t**restrict); - -PGM_END_DECLS - -#endif /* __PGM_IMPL_INDEXTOADDR_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/indextoname.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/indextoname.h deleted file mode 100644 index d5d7964..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/indextoname.h +++ /dev/null @@ -1,37 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Windows interface index to interface name function. - * - * Copyright (c) 2006-2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_INDEXTONAME_H__ -#define __PGM_IMPL_INDEXTONAME_H__ - -#include - -PGM_BEGIN_DECLS - -PGM_GNUC_INTERNAL char* pgm_if_indextoname (unsigned, char*); - -PGM_END_DECLS - -#endif /* __PGM_IMPL_INDEXTONAME_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/inet_network.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/inet_network.h deleted file mode 100644 index 64c43fb..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/inet_network.h +++ /dev/null @@ -1,41 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable implementations of inet_network and inet_network6. - * - * Copyright (c) 2006-2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_INET_NETWORK_H__ -#define __PGM_IMPL_INET_NETWORK_H__ - -#ifndef _WIN32 -# include -#endif -#include - -PGM_BEGIN_DECLS - -PGM_GNUC_INTERNAL int pgm_inet_network (const char*restrict, struct in_addr*restrict); -PGM_GNUC_INTERNAL int pgm_inet6_network (const char*restrict, struct in6_addr*restrict); - -PGM_END_DECLS - -#endif /* __PGM_IMPL_INET_NETWORK_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/ip.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/ip.h deleted file mode 100644 index 8f18775..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/ip.h +++ /dev/null @@ -1,150 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Internet header for protocol version 4, RFC 791. - * - * Copyright (c) 1982, 1986, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Copyright (c) 1996-1999 by Internet Software Consortium. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS - * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE - * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL - * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR - * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS - * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS - * SOFTWARE. - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_IP_H__ -#define __PGM_IMPL_IP_H__ - -#ifndef _WIN32 -# include -#endif -#include -#include - -PGM_BEGIN_DECLS - -/* Byte alignment for packet memory maps. - * NB: Solaris and OpenSolaris don't support #pragma pack(push) even on x86. - */ -#if defined( __GNUC__ ) && !defined( sun ) -# pragma pack(push) -#endif -#pragma pack(1) - -/* RFC 791 */ - -/* nb: first four bytes are forced bitfields for win32 "feature" */ -struct pgm_ip -{ -#if (defined( sun ) && defined( _BIT_FIELDS_LTOH )) || (!defined( sun ) && __BYTE_ORDER == __LITTLE_ENDIAN) - unsigned ip_hl:4; /* header length */ - unsigned ip_v:4; /* version */ -#else - unsigned ip_v:4; /* version */ - unsigned ip_hl:4; /* header length */ -#endif - unsigned ip_tos:8; /* type of service */ - unsigned ip_len:16; /* total length */ - uint16_t ip_id; /* identification */ - uint16_t ip_off; /* fragment offset field */ - uint8_t ip_ttl; /* time to live */ - uint8_t ip_p; /* protocol */ - uint16_t ip_sum; /* checksum */ - struct in_addr ip_src, ip_dst; /* source and dest address */ -}; - -PGM_STATIC_ASSERT(sizeof(struct pgm_ip) == 20); - -/* RFC 2460 */ -#ifdef ip6_vfc -# undef ip6_vfc -#endif -#ifdef ip6_plen -# undef ip6_plen -#endif -#ifdef ip6_nxt -# undef ip6_nxt -#endif -#ifdef ip6_hops -# undef ip6_hops -#endif -struct pgm_ip6_hdr -{ - uint32_t ip6_vfc; /* version:4, traffic class:8, flow label:20 */ - uint16_t ip6_plen; /* payload length: packet length - 40 */ - uint8_t ip6_nxt; /* next header type */ - uint8_t ip6_hops; /* hop limit */ - struct in6_addr ip6_src, ip6_dst; /* source and dest address */ -}; - -PGM_STATIC_ASSERT(sizeof(struct pgm_ip6_hdr) == 40); - -#define PGM_IPOPT_EOL 0 /* end of option list */ -#define PGM_IPOPT_NOP 1 /* no operation */ -#define PGM_IPOPT_RR 7 /* record packet route */ -#define PGM_IPOPT_TS 68 /* timestamp */ -#define PGM_IPOPT_SECURITY 130 /* provide s, c, h, tcc */ -#define PGM_IPOPT_LSRR 131 /* loose source route */ -#define PGM_IPOPT_ESO 133 -#define PGM_IPOPT_CIPSO 134 -#define PGM_IPOPT_SATID 136 /* satnet id */ -#define PGM_IPOPT_SSRR 137 /* strict source route */ -#define PGM_IPOPT_RA 148 /* router alert */ - -/* RFC 768 */ -struct pgm_udphdr -{ - uint16_t uh_sport; /* source port */ - uint16_t uh_dport; /* destination port */ - uint16_t uh_ulen; /* udp length */ - uint16_t uh_sum; /* udp checksum */ -}; - -PGM_STATIC_ASSERT(sizeof(struct pgm_udphdr) == 8); - -#if defined( __GNUC__ ) && !defined( sun ) -# pragma pack(pop) -#else -# pragma pack() -#endif - -PGM_END_DECLS - -#endif /* __PGM_IMPL_IP_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/list.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/list.h deleted file mode 100644 index 91a3ed3..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/list.h +++ /dev/null @@ -1,43 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable doubly-linked list. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_LIST_H__ -#define __PGM_IMPL_LIST_H__ - -#include -#include - -PGM_BEGIN_DECLS - -PGM_GNUC_INTERNAL pgm_list_t* pgm_list_append (pgm_list_t*restrict, void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL pgm_list_t* pgm_list_prepend_link (pgm_list_t*restrict, pgm_list_t*restrict) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL pgm_list_t* pgm_list_remove_link (pgm_list_t*, pgm_list_t*) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL pgm_list_t* pgm_list_delete_link (pgm_list_t*, pgm_list_t*) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL pgm_list_t* pgm_list_last (pgm_list_t*) PGM_GNUC_PURE PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL unsigned pgm_list_length (pgm_list_t*) PGM_GNUC_PURE PGM_GNUC_WARN_UNUSED_RESULT; - -PGM_END_DECLS - -#endif /* __PGM_IMPL_LIST_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/math.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/math.h deleted file mode 100644 index 8c7c2ba..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/math.h +++ /dev/null @@ -1,75 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * Shared math routines. - * - * Copyright (c) 2006-2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_MATH_H__ -#define __PGM_IMPL_MATH_H__ - -#include - -PGM_BEGIN_DECLS - -/* fast log base 2 of power of 2 - */ - -static inline unsigned pgm_power2_log2 (unsigned) PGM_GNUC_CONST; - -static inline -unsigned -pgm_power2_log2 ( - unsigned v - ) -{ - static const unsigned int b[] = { 0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0, 0xFF00FF00, 0xFFFF0000 }; - unsigned int r = (v & b[0]) != 0; - for (unsigned i = 4; i > 0; i--) { - r |= ((v & b[i]) != 0) << i; - } - return r; -} - -/* nearest power of 2 - */ - -static inline size_t pgm_nearest_power (size_t, size_t) PGM_GNUC_CONST; - -static inline -size_t -pgm_nearest_power ( - size_t b, - size_t v - ) -{ - if (v > (SIZE_MAX/2)) - return SIZE_MAX; - while (b < v) - b <<= 1; - return b; -} - -unsigned pgm_spaced_primes_closest (unsigned) PGM_GNUC_PURE; - -PGM_END_DECLS - -#endif /* __PGM_IMPL_MATH_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/md5.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/md5.h deleted file mode 100644 index b28ab7d..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/md5.h +++ /dev/null @@ -1,61 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * MD5 hashing algorithm. - * - * MD5 original source GNU C Library: - * Includes functions to compute MD5 message digest of files or memory blocks - * according to the definition of MD5 in RFC 1321 from April 1992. - * - * Copyright (C) 1995, 1996, 2001, 2003 Free Software Foundation, Inc. - * - * This file is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this file; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_MD5_H__ -#define __PGM_IMPL_MD5_H__ - -struct pgm_md5_t; - -#include - -PGM_BEGIN_DECLS - -struct pgm_md5_t -{ - uint32_t A; - uint32_t B; - uint32_t C; - uint32_t D; - - uint32_t total[2]; - uint32_t buflen; - char buffer[128] -#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) - __attribute__ ((__aligned__ (__alignof__ (uint32_t)))) -#endif - ; -}; - -PGM_GNUC_INTERNAL void pgm_md5_init_ctx (struct pgm_md5_t*); -PGM_GNUC_INTERNAL void pgm_md5_process_bytes (struct pgm_md5_t*restrict, const void*restrict, size_t); -PGM_GNUC_INTERNAL void* pgm_md5_finish_ctx (struct pgm_md5_t*, void*); - -PGM_END_DECLS - -#endif /* __PGM_IMPL_MD5_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/mem.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/mem.h deleted file mode 100644 index 9377ced..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/mem.h +++ /dev/null @@ -1,34 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable fail fast memory allocation. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_IMPL_MEM_H__ -#define __PGM_IMPL_MEM_H__ - -#include - -PGM_BEGIN_DECLS - -PGM_GNUC_INTERNAL void pgm_mem_init (void); -PGM_GNUC_INTERNAL void pgm_mem_shutdown (void); - -PGM_END_DECLS - -#endif /* __PGM_IMPL_MEM_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/messages.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/messages.h deleted file mode 100644 index e7065b0..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/messages.h +++ /dev/null @@ -1,352 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * basic message reporting. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_MESSAGES_H__ -#define __PGM_IMPL_MESSAGES_H__ - -#include -#include -#include -#include - -PGM_BEGIN_DECLS - -PGM_GNUC_INTERNAL void pgm__log (const int, const char*, ...) PGM_GNUC_PRINTF (2, 3); -PGM_GNUC_INTERNAL void pgm__logv (const int, const char*, va_list) PGM_GNUC_PRINTF (2, 0); - -#ifdef CONFIG_HAVE_ISO_VARARGS - -/* debug trace level only valid in debug mode */ -# ifdef PGM_DEBUG -# define pgm_debug(...) \ - do { \ - if (pgm_min_log_level == PGM_LOG_LEVEL_DEBUG) \ - pgm__log (PGM_LOG_LEVEL_DEBUG, __VA_ARGS__); \ - } while (0) -# else -# define pgm_debug(...) while (0) -# endif /* !PGM_DEBUG */ - -# define pgm_trace(r,...) \ - do { \ - if (pgm_min_log_level <= PGM_LOG_LEVEL_TRACE && pgm_log_mask & (r)) \ - pgm__log (PGM_LOG_LEVEL_TRACE, __VA_ARGS__); \ - } while (0) -# define pgm_minor(...) \ - do { \ - if (pgm_min_log_level <= PGM_LOG_LEVEL_MINOR) \ - pgm__log (PGM_LOG_LEVEL_MINOR, __VA_ARGS__); \ - } while (0) -# define pgm_info(...) \ - do { \ - if (pgm_min_log_level <= PGM_LOG_LEVEL_NORMAL) \ - pgm__log (PGM_LOG_LEVEL_NORMAL, __VA_ARGS__); \ - } while (0) -# define pgm_warn(...) \ - do { \ - if (pgm_min_log_level <= PGM_LOG_LEVEL_WARNING) \ - pgm__log (PGM_LOG_LEVEL_WARNING, __VA_ARGS__); \ - } while (0) -# define pgm_error(...) \ - do { \ - if (pgm_min_log_level <= PGM_LOG_LEVEL_ERROR) \ - pgm__log (PGM_LOG_LEVEL_ERROR, __VA_ARGS__); \ - } while (0) -# define pgm_fatal(...) \ - do { \ - pgm__log (PGM_LOG_LEVEL_FATAL, __VA_ARGS__); \ - } while (0) - -#elif defined(CONFIG_HAVE_GNUC_VARARGS) - -# ifdef PGM_DEBUG -# define pgm_debug(f...) \ - do { \ - if (pgm_min_log_level == PGM_LOG_LEVEL_DEBUG) \ - pgm__log (PGM_LOG_LEVEL_DEBUG, f); \ - } while (0) -# else -# define pgm_debug(f...) while (0) -# endif /* !PGM_DEBUG */ - -# define pgm_trace(r,f...) if (pgm_min_log_level <= PGM_LOG_LEVEL_TRACE && pgm_log_mask & (r)) \ - pgm__log (PGM_LOG_LEVEL_TRACE, f) -# define pgm_minor(f...) if (pgm_min_log_level <= PGM_LOG_LEVEL_MINOR) pgm__log (PGM_LOG_LEVEL_MINOR, f) -# define pgm_info(f...) if (pgm_min_log_level <= PGM_LOG_LEVEL_NORMAL) pgm__log (PGM_LOG_LEVEL_NORMAL, f) -# define pgm_warn(f...) if (pgm_min_log_level <= PGM_LOG_LEVEL_WARNING) pgm__log (PGM_LOG_LEVEL_WARNING, f) -# define pgm_error(f...) if (pgm_min_log_level <= PGM_LOG_LEVEL_ERROR) pgm__log (PGM_LOG_LEVEL_ERROR, f) -# define pgm_fatal(f...) pgm__log (PGM_LOG_LEVEL_FATAL, f) - -#else /* no varargs macros */ - -/* declare for GCC attributes */ -static inline void pgm_debug (const char*, ...) PGM_GNUC_PRINTF (1, 2); -static inline void pgm_trace (const int, const char*, ...) PGM_GNUC_PRINTF (2, 3); -static inline void pgm_minor (const char*, ...) PGM_GNUC_PRINTF (1, 2); -static inline void pgm_info (const char*, ...) PGM_GNUC_PRINTF (1, 2); -static inline void pgm_warn (const char*, ...) PGM_GNUC_PRINTF (1, 2); -static inline void pgm_error (const char*, ...) PGM_GNUC_PRINTF (1, 2); -static inline void pgm_fatal (const char*, ...) PGM_GNUC_PRINTF (1, 2); - -static inline void pgm_debug (const char* format, ...) { - if (PGM_LOG_LEVEL_DEBUG == pgm_min_log_level) { - va_list args; - va_start (args, format); - pgm__logv (PGM_LOG_LEVEL_DEBUG, format, args); - va_end (args); - } -} - -static inline void pgm_trace (const int role, const char* format, ...) { - if (PGM_LOG_LEVEL_TRACE >= pgm_min_log_level && pgm_log_mask & role) { - va_list args; - va_start (args, format); - pgm__logv (PGM_LOG_LEVEL_TRACE, format, args); - va_end (args); - } -} - -static inline void pgm_minor (const char* format, ...) { - if (PGM_LOG_LEVEL_MINOR >= pgm_min_log_level) { - va_list args; - va_start (args, format); - pgm__logv (PGM_LOG_LEVEL_MINOR, format, args); - va_end (args); - } -} - -static inline void pgm_info (const char* format, ...) { - if (PGM_LOG_LEVEL_NORMAL >= pgm_min_log_level) { - va_list args; - va_start (args, format); - pgm__logv (PGM_LOG_LEVEL_NORMAL, format, args); - va_end (args); - } -} - -static inline void pgm_warn (const char* format, ...) { - if (PGM_LOG_LEVEL_WARNING >= pgm_min_log_level) { - va_list args; - va_start (args, format); - pgm__logv (PGM_LOG_LEVEL_WARNING, format, args); - va_end (args); - } -} - -static inline void pgm_error (const char* format, ...) { - if (PGM_LOG_LEVEL_ERROR >= pgm_min_log_level) { - va_list args; - va_start (args, format); - pgm__logv (PGM_LOG_LEVEL_WARNING, format, args); - va_end (args); - } -} - -static inline void pgm_fatal (const char* format, ...) { - va_list args; - va_start (args, format); - pgm__logv (PGM_LOG_LEVEL_FATAL, format, args); - va_end (args); -} - -#endif /* varargs */ - -#define pgm_warn_if_reached() \ - do { \ - pgm_warn ("file %s: line %d (%s): code should not be reached", \ - __FILE__, __LINE__, __PRETTY_FUNCTION__); \ - } while (0) -#define pgm_warn_if_fail(expr) \ - do { \ - if (PGM_LIKELY (expr)); \ - else \ - pgm_warn ("file %s: line %d (%s): runtime check failed: (%s)", \ - __FILE__, __LINE__, __PRETTY_FUNCTION__, #expr); \ - } while (0) - - -#ifdef PGM_DISABLE_ASSERT - -# define pgm_assert(expr) while (0) -# define pgm_assert_not_reached() while (0) -# define pgm_assert_cmpint(n1, cmp, n2) while (0) -# define pgm_assert_cmpuint(n1, cmp, n2) while (0) - -#elif defined(__GNUC__) - -# define pgm_assert(expr) \ - do { \ - if (PGM_LIKELY(expr)); \ - else { \ - pgm_fatal ("file %s: line %d (%s): assertion failed: (%s)", \ - __FILE__, __LINE__, __PRETTY_FUNCTION__, #expr); \ - abort (); \ - } \ - } while (0) -# define pgm_assert_not_reached() \ - do { \ - pgm_fatal ("file %s: line %d (%s): should not be reached", \ - __FILE__, __LINE__, __PRETTY_FUNCTION__); \ - abort (); \ - } while (0) -# define pgm_assert_cmpint(n1, cmp, n2) \ - do { \ - const int _n1 = (n1), _n2 = (n2); \ - if (PGM_LIKELY(_n1 cmp _n2)); \ - else { \ - pgm_fatal ("file %s: line %d (%s): assertion failed (%s): (%u %s %u)", \ - __FILE__, __LINE__, __PRETTY_FUNCTION__, #n1 " " #cmp " " #n2, _n1, #cmp, _n2); \ - abort (); \ - } \ - } while (0) -# define pgm_assert_cmpuint(n1, cmp, n2) \ - do { \ - const unsigned _n1 = (n1), _n2 = (n2); \ - if (PGM_LIKELY(_n1 cmp _n2)); \ - else { \ - pgm_fatal ("file %s: line %d (%s): assertion failed (%s): (%u %s %u)", \ - __FILE__, __LINE__, __PRETTY_FUNCTION__, #n1 " " #cmp " " #n2, _n1, #cmp, _n2); \ - abort (); \ - } \ - } while (0) - -#else - -# define pgm_assert(expr) \ - do { \ - if (PGM_LIKELY(expr)); \ - else { \ - pgm_fatal ("file %s: line %d: assertion failed: (%s)", \ - __FILE__, __LINE__, #expr); \ - abort (); \ - } \ - } while (0) -# define pgm_assert_not_reached() \ - do { \ - pgm_fatal ("file %s: line %d: assertion failed: (%s)", \ - __FILE__, __LINE__); \ - abort (); \ - } while (0) -# define pgm_assert_cmpint(n1, cmp, n2) \ - do { \ - const int _n1 = (n1), _n2 = (n2); \ - if (PGM_LIKELY(_n1 cmp _n2)); \ - else { \ - pgm_fatal ("file %s: line %d: assertion failed (%s): (%u %s %u)", \ - __FILE__, __LINE__, #n1 " " #cmp " " #n2, _n1, #cmp, _n2); \ - abort (); \ - } \ - } while (0) -# define pgm_assert_cmpuint(n1, cmp, n2) \ - do { \ - const unsigned _n1 = (n1), _n2 = (n2); \ - if (PGM_LIKELY(_n1 cmp _n2)); \ - else { \ - pgm_fatal ("file %s: line %d: assertion failed (%s): (%u %s %u)", \ - __FILE__, __LINE__, #n1 " " #cmp " " #n2, _n1, #cmp, _n2); \ - abort (); \ - } \ - } while (0) - -#endif /* !PGM_DISABLE_ASSERT */ - -#ifdef PGM_DISABLE_CHECKS - -# define pgm_return_if_fail(expr) while (0) -# define pgm_return_val_if_fail(expr, val) while (0) -# define pgm_return_if_reached() return -# define pgm_return_val_if_reached(val) return (val) - -#elif defined(__GNUC__) - -# define pgm_return_if_fail(expr) \ - do { \ - if (PGM_LIKELY(expr)); \ - else { \ - pgm_warn ("file %s: line %d (%s): assertion `%s' failed", \ - __FILE__, __LINE__, __PRETTY_FUNCTION__, #expr); \ - return; \ - } \ - } while (0) -# define pgm_return_val_if_fail(expr, val) \ - do { \ - if (PGM_LIKELY(expr)); \ - else { \ - pgm_warn ("file %s: line %d (%s): assertion `%s' failed", \ - __FILE__, __LINE__, __PRETTY_FUNCTION__, #expr); \ - return (val); \ - } \ - } while (0) -# define pgm_return_if_reached() \ - do { \ - pgm_warn ("file %s: line %d (%s): should not be reached", \ - __FILE__, __LINE__, __PRETTY_FUNCTION__); \ - return; \ - } while (0) -# define pgm_return_val_if_reached(val) \ - do { \ - pgm_warn ("file %s: line %d (%s): should not be reached", \ - __FILE__, __LINE__, __PRETTY_FUNCTION__); \ - return (val); \ - } while (0) - -#else - -# define pgm_return_if_fail(expr) \ - do { \ - if (PGM_LIKELY(expr)); \ - else { \ - pgm_warn ("file %s: line %d: assertion `%s' failed", \ - __FILE__, __LINE__, #expr); \ - return; \ - } \ - } while (0) -# define pgm_return_val_if_fail(expr, val) \ - do { \ - if (PGM_LIKELY(expr)); \ - else { \ - pgm_warn ("file %s: line %d: assertion `%s' failed", \ - __FILE__, __LINE__, #expr); \ - return (val); \ - } \ - } while (0) -# define pgm_return_if_reached() \ - do { \ - pgm_warn ("file %s: line %d): should not be reached", \ - __FILE__, __LINE__); \ - return; \ - } while (0) -# define pgm_return_val_if_reached(val) \ - do { \ - pgm_warn ("file %s: line %d: should not be reached", \ - __FILE__, __LINE__); \ - return (val); \ - } while (0) - -#endif /* !PGM_DISABLE_CHECKS */ - -PGM_END_DECLS - -#endif /* __PGM_IMPL_MESSAGES_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/nametoindex.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/nametoindex.h deleted file mode 100644 index a25bd89..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/nametoindex.h +++ /dev/null @@ -1,40 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Windows interface name to interface index function. - * - * Copyright (c) 2006-2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_NAMETOINDEX_H__ -#define __PGM_IMPL_NAMETOINDEX_H__ - -#ifndef _WIN32 -# include -#endif -#include - -PGM_BEGIN_DECLS - -PGM_GNUC_INTERNAL unsigned pgm_if_nametoindex (const sa_family_t, const char*); - -PGM_END_DECLS - -#endif /* __PGM_IMPL_NAMETOINDEX_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/net.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/net.h deleted file mode 100644 index aa9beaf..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/net.h +++ /dev/null @@ -1,38 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * network send wrapper. - * - * Copyright (c) 2006-2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_IMPL_NET_H__ -#define __PGM_IMPL_NET_H__ - -#ifndef _WIN32 -# include -#endif -#include - -PGM_BEGIN_DECLS - -PGM_GNUC_INTERNAL ssize_t pgm_sendto (pgm_sock_t*restrict, bool, bool, const void*restrict, size_t, const struct sockaddr*restrict, socklen_t); -PGM_GNUC_INTERNAL int pgm_set_nonblocking (int fd[2]); - -PGM_END_DECLS - -#endif /* __PGM_IMPL_NET_H__ */ - diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/notify.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/notify.h deleted file mode 100644 index 1db4475..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/notify.h +++ /dev/null @@ -1,298 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * Low kernel overhead event notify mechanism, or standard pipes. - * - * Copyright (c) 2008 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_NOTIFY_H__ -#define __PGM_IMPL_NOTIFY_H__ - -typedef struct pgm_notify_t pgm_notify_t; - -#ifndef _WIN32 -# include -# include -# ifdef CONFIG_HAVE_EVENTFD -# include -# endif -#else /* _WIN32 */ -# include -# include -#endif -#include -#include - -PGM_BEGIN_DECLS - -struct pgm_notify_t { -#if defined(CONFIG_HAVE_EVENTFD) - int eventfd; -#elif !defined(_WIN32) - int pipefd[2]; -#else - SOCKET s[2]; -#endif /* _WIN32 */ -}; - -#if defined(CONFIG_HAVE_EVENTFD) -# define PGM_NOTIFY_INIT { -1 } -#elif !defined(_WIN32) -# define PGM_NOTIFY_INIT { { -1, -1 } } -#else -# define PGM_NOTIFY_INIT { { INVALID_SOCKET, INVALID_SOCKET } } -#endif - - -static inline -bool -pgm_notify_is_valid ( - pgm_notify_t* notify - ) -{ - if (PGM_UNLIKELY(NULL == notify)) - return FALSE; -#if defined(CONFIG_HAVE_EVENTFD) - if (PGM_UNLIKELY(-1 == notify->eventfd)) - return FALSE; -#elif !defined(_WIN32) - if (PGM_UNLIKELY(-1 == notify->pipefd[0] || -1 == notify->pipefd[1])) - return FALSE; -#else - if (PGM_UNLIKELY(INVALID_SOCKET == notify->s[0] || INVALID_SOCKET == notify->s[1])) - return FALSE; -#endif /* _WIN32 */ - return TRUE; -} - -static inline -int -pgm_notify_init ( - pgm_notify_t* notify - ) -{ - pgm_assert (NULL != notify); - -#if defined(CONFIG_HAVE_EVENTFD) - notify->eventfd = -1; - int retval = eventfd (0, 0); - if (-1 == retval) - return retval; - notify->eventfd = retval; - const int fd_flags = fcntl (notify->eventfd, F_GETFL); - if (-1 != fd_flags) - retval = fcntl (notify->eventfd, F_SETFL, fd_flags | O_NONBLOCK); - return 0; -#elif !defined(_WIN32) - notify->pipefd[0] = notify->pipefd[1] = -1; - int retval = pipe (notify->pipefd); - pgm_assert (0 == retval); -/* set non-blocking */ -/* write-end */ - int fd_flags = fcntl (notify->pipefd[1], F_GETFL); - if (fd_flags != -1) - retval = fcntl (notify->pipefd[1], F_SETFL, fd_flags | O_NONBLOCK); - pgm_assert (notify->pipefd[1]); -/* read-end */ - fd_flags = fcntl (notify->pipefd[0], F_GETFL); - if (fd_flags != -1) - retval = fcntl (notify->pipefd[0], F_SETFL, fd_flags | O_NONBLOCK); - pgm_assert (notify->pipefd[0]); - return retval; -#else -/* use loopback sockets to simulate a pipe suitable for win32/select() */ - struct sockaddr_in addr; - SOCKET listener; - int addrlen = sizeof (addr); - - notify->s[0] = notify->s[1] = INVALID_SOCKET; - - listener = socket (AF_INET, SOCK_STREAM, 0); - pgm_assert (listener != INVALID_SOCKET); - - memset (&addr, 0, sizeof (addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = inet_addr ("127.0.0.1"); - pgm_assert (addr.sin_addr.s_addr != INADDR_NONE); - - int rc = bind (listener, (const struct sockaddr*)&addr, sizeof (addr)); - pgm_assert (rc != SOCKET_ERROR); - - rc = getsockname (listener, (struct sockaddr*)&addr, &addrlen); - pgm_assert (rc != SOCKET_ERROR); - -// Listen for incoming connections. - rc = listen (listener, 1); - pgm_assert (rc != SOCKET_ERROR); - -// Create the socket. - notify->s[1] = WSASocket (AF_INET, SOCK_STREAM, 0, NULL, 0, 0); - pgm_assert (notify->s[1] != INVALID_SOCKET); - -// Connect to the remote peer. - rc = connect (notify->s[1], (struct sockaddr*)&addr, addrlen); - pgm_assert (rc != SOCKET_ERROR); - -// Accept connection. - notify->s[0] = accept (listener, NULL, NULL); - pgm_assert (notify->s[0] != INVALID_SOCKET); - -// Set read-end to non-blocking mode - const unsigned long one = 1; - rc = ioctlsocket (notify->s[0], FIONBIO, &one); - pgm_assert (rc != SOCKET_ERROR); - -// We don't need the listening socket anymore. Close it. - rc = closesocket (listener); - pgm_assert (rc != SOCKET_ERROR); - - return 0; -#endif -} - -static inline -int -pgm_notify_destroy ( - pgm_notify_t* notify - ) -{ - pgm_assert (NULL != notify); - -#if defined(CONFIG_HAVE_EVENTFD) - if (-1 != notify->eventfd) { - close (notify->eventfd); - notify->eventfd = -1; - } -#elif !defined(_WIN32) - if (-1 != notify->pipefd[0]) { - close (notify->pipefd[0]); - notify->pipefd[0] = -1; - } - if (-1 != notify->pipefd[1]) { - close (notify->pipefd[1]); - notify->pipefd[1] = -1; - } -#else - if (INVALID_SOCKET != notify->s[0]) { - closesocket (notify->s[0]); - notify->s[0] = INVALID_SOCKET; - } - if (INVALID_SOCKET != notify->s[1]) { - closesocket (notify->s[1]); - notify->s[1] = INVALID_SOCKET; - } -#endif - return 0; -} - -static inline -int -pgm_notify_send ( - pgm_notify_t* notify - ) -{ - pgm_assert (NULL != notify); - -#if defined(CONFIG_HAVE_EVENTFD) - pgm_assert (-1 != notify->eventfd); - uint64_t u = 1; - ssize_t s = write (notify->eventfd, &u, sizeof(u)); - return (s == sizeof(u)); -#elif !defined(_WIN32) - pgm_assert (-1 != notify->pipefd[1]); - const char one = '1'; - return (1 == write (notify->pipefd[1], &one, sizeof(one))); -#else - pgm_assert (INVALID_SOCKET != notify->s[1]); - const char one = '1'; - return (1 == send (notify->s[1], &one, sizeof(one), 0)); -#endif -} - -static inline -int -pgm_notify_read ( - pgm_notify_t* notify - ) -{ - pgm_assert (NULL != notify); - -#if defined(CONFIG_HAVE_EVENTFD) - pgm_assert (-1 != notify->eventfd); - uint64_t u; - return (sizeof(u) == read (notify->eventfd, &u, sizeof(u))); -#elif !defined(_WIN32) - pgm_assert (-1 != notify->pipefd[0]); - char buf; - return (sizeof(buf) == read (notify->pipefd[0], &buf, sizeof(buf))); -#else - pgm_assert (INVALID_SOCKET != notify->s[0]); - char buf; - return (sizeof(buf) == recv (notify->s[0], &buf, sizeof(buf), 0)); -#endif -} - -static inline -void -pgm_notify_clear ( - pgm_notify_t* notify - ) -{ - pgm_assert (NULL != notify); - -#if defined(CONFIG_HAVE_EVENTFD) - pgm_assert (-1 != notify->eventfd); - uint64_t u; - while (sizeof(u) == read (notify->eventfd, &u, sizeof(u))); -#elif !defined(_WIN32) - pgm_assert (-1 != notify->pipefd[0]); - char buf; - while (sizeof(buf) == read (notify->pipefd[0], &buf, sizeof(buf))); -#else - pgm_assert (INVALID_SOCKET != notify->s[0]); - char buf; - while (sizeof(buf) == recv (notify->s[0], &buf, sizeof(buf), 0)); -#endif -} - -static inline -int -pgm_notify_get_fd ( - pgm_notify_t* notify - ) -{ - pgm_assert (NULL != notify); - -#if defined(CONFIG_HAVE_EVENTFD) - pgm_assert (-1 != notify->eventfd); - return notify->eventfd; -#elif !defined(_WIN32) - pgm_assert (-1 != notify->pipefd[0]); - return notify->pipefd[0]; -#else - pgm_assert (INVALID_SOCKET != notify->s[0]); - return notify->s[0]; -#endif -} - -PGM_END_DECLS - -#endif /* __PGM_IMPL_NOTIFY_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/packet_parse.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/packet_parse.h deleted file mode 100644 index 4416e21..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/packet_parse.h +++ /dev/null @@ -1,45 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * PGM packet formats, RFC 3208. - * - * Copyright (c) 2006 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_IMPL_PACKET_PARSE_H__ -#define __PGM_IMPL_PACKET_PARSE_H__ - -#ifndef _WIN32 -# include -#endif -#include - -PGM_BEGIN_DECLS - -PGM_GNUC_INTERNAL bool pgm_parse_raw (struct pgm_sk_buff_t*const restrict, struct sockaddr*const restrict, pgm_error_t**restrict); -PGM_GNUC_INTERNAL bool pgm_parse_udp_encap (struct pgm_sk_buff_t*const restrict, pgm_error_t**restrict); -PGM_GNUC_INTERNAL bool pgm_verify_spm (const struct pgm_sk_buff_t* const); -PGM_GNUC_INTERNAL bool pgm_verify_spmr (const struct pgm_sk_buff_t* const); -PGM_GNUC_INTERNAL bool pgm_verify_nak (const struct pgm_sk_buff_t* const); -PGM_GNUC_INTERNAL bool pgm_verify_nnak (const struct pgm_sk_buff_t* const); -PGM_GNUC_INTERNAL bool pgm_verify_ncf (const struct pgm_sk_buff_t* const); -PGM_GNUC_INTERNAL bool pgm_verify_poll (const struct pgm_sk_buff_t* const); -PGM_GNUC_INTERNAL bool pgm_verify_polr (const struct pgm_sk_buff_t* const); -PGM_GNUC_INTERNAL bool pgm_verify_ack (const struct pgm_sk_buff_t* const); - -PGM_END_DECLS - -#endif /* __PGM_IMPL_PACKET_PARSE_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/packet_test.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/packet_test.h deleted file mode 100644 index 1b39006..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/packet_test.h +++ /dev/null @@ -1,40 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * PGM packet formats, RFC 3208. - * - * Copyright (c) 2006 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_IMPL_PACKET_TEST_H__ -#define __PGM_IMPL_PACKET_TEST_H__ - -#ifndef _WIN32 -# include -#endif -#include - -PGM_BEGIN_DECLS - -PGM_GNUC_INTERNAL bool pgm_print_packet (const void*, size_t); -PGM_GNUC_INTERNAL const char* pgm_type_string (uint8_t) PGM_GNUC_WARN_UNUSED_RESULT PGM_GNUC_CONST; -PGM_GNUC_INTERNAL const char* pgm_udpport_string (uint16_t) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL const char* pgm_gethostbyaddr (const struct in_addr*) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL void pgm_ipopt_print (const void*, size_t); - -PGM_END_DECLS - -#endif /* __PGM_IMPL_PACKET_TEST_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB.h deleted file mode 100644 index 6f80271..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Note: this file originally auto-generated by mib2c using - * : mib2c.notify.conf,v 5.3 2004/04/15 12:29:19 dts12 Exp $ - */ - -#ifndef __PGM_IMPL_MIB_H__ -#define __PGM_IMPL_MIB_H__ - -#include - -PGM_BEGIN_DECLS - -/* function declarations */ -PGM_GNUC_INTERNAL bool pgm_mib_init (pgm_error_t**); - -PGM_GNUC_INTERNAL int send_pgmStart_trap(void); -PGM_GNUC_INTERNAL int send_pgmStop_trap(void); -PGM_GNUC_INTERNAL int send_pgmNewSourceTrap_trap(void); -PGM_GNUC_INTERNAL int send_pgmClosedSourceTrap_trap(void); -PGM_GNUC_INTERNAL int send_pgmNewReceiverTrap_trap(void); -PGM_GNUC_INTERNAL int send_pgmClosedReceiverTrap_trap(void); -PGM_GNUC_INTERNAL int send_pgmNakFailuresTrap_trap(void); -PGM_GNUC_INTERNAL int send_pgmNewDlrSourceTrap_trap(void); -PGM_GNUC_INTERNAL int send_pgmClosedDlrSourceTrap_trap(void); - -PGM_END_DECLS - -#endif /* __PGM_IMPL_MIB_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB_columns.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB_columns.h deleted file mode 100644 index 545e519..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB_columns.h +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Note: this file originally auto-generated by mib2c using - * : mib2c.column_defines.conf,v 5.1 2002/05/08 05:42:47 hardaker Exp $ - */ -#ifndef PGMMIB_COLUMNS_H -#define PGMMIB_COLUMNS_H - -/* column number definitions for table pgmNeIfConfigTable */ - #define COLUMN_PGMNEIFCONFIGINDEX 1 - #define COLUMN_PGMNEIFPGMENABLE 2 - #define COLUMN_PGMNEIFNAKRPTINTERVAL 3 - #define COLUMN_PGMNEIFNAKRPTRATE 4 - #define COLUMN_PGMNEIFNAKRDATAINTERVAL 5 - #define COLUMN_PGMNEIFNAKELIMINATEINTERVAL 6 - -/* column number definitions for table pgmNeIfPerformanceTable */ - #define COLUMN_PGMNEIFPERFORMANCEINDEX 1 - #define COLUMN_PGMNEIFREXMITSTATES 2 - #define COLUMN_PGMNEIFREXMITTIMEDOUT 3 - #define COLUMN_PGMNEIFINSPMS 4 - #define COLUMN_PGMNEIFOUTSPMS 5 - #define COLUMN_PGMNEIFINPARITYSPMS 6 - #define COLUMN_PGMNEIFOUTPARITYSPMS 7 - #define COLUMN_PGMNEIFINRDATA 8 - #define COLUMN_PGMNEIFOUTRDATA 9 - #define COLUMN_PGMNEIFINPARITYRDATA 10 - #define COLUMN_PGMNEIFOUTPARITYRDATA 11 - #define COLUMN_PGMNEIFINRDATANOSESSIONERRORS 12 - #define COLUMN_PGMNEIFUNIQUENAKS 13 - #define COLUMN_PGMNEIFINNAKS 14 - #define COLUMN_PGMNEIFOUTNAKS 15 - #define COLUMN_PGMNEIFUNIQUEPARITYNAKS 16 - #define COLUMN_PGMNEIFINPARITYNAKS 17 - #define COLUMN_PGMNEIFOUTPARITYNAKS 18 - #define COLUMN_PGMNEIFINNAKNOSESSIONERRORS 19 - #define COLUMN_PGMNEIFINNAKSEQERRORS 20 - #define COLUMN_PGMNEIFINPARITYNAKTGERRORS 21 - #define COLUMN_PGMNEIFINNNAKS 22 - #define COLUMN_PGMNEIFOUTNNAKS 23 - #define COLUMN_PGMNEIFINPARITYNNAKS 24 - #define COLUMN_PGMNEIFOUTPARITYNNAKS 25 - #define COLUMN_PGMNEIFINNNAKNOSESSIONERRORS 26 - #define COLUMN_PGMNEIFINNCFS 27 - #define COLUMN_PGMNEIFOUTNCFS 28 - #define COLUMN_PGMNEIFINPARITYNCFS 29 - #define COLUMN_PGMNEIFOUTPARITYNCFS 30 - #define COLUMN_PGMNEIFINNCFNOSESSIONERRORS 31 - #define COLUMN_PGMNEIFINREDIRECTNCFS 32 - #define COLUMN_PGMNEIFMALFORMED 33 - #define COLUMN_PGMNEIFSPMFROMSOURCE 34 - #define COLUMN_PGMNEIFSPMBADSQN 35 - #define COLUMN_PGMNEIFSPMERROR 36 - #define COLUMN_PGMNEIFPOLLRANDOMIGNORE 37 - #define COLUMN_PGMNEIFPOLLTSISTATEERROR 38 - #define COLUMN_PGMNEIFPOLLPARENTERROR 39 - #define COLUMN_PGMNEIFPOLLTYPEERROR 40 - #define COLUMN_PGMNEIFPOLLERROR 41 - #define COLUMN_PGMNEIFPOLLSUCCESS 42 - #define COLUMN_PGMNEIFPOLLORIGINATED 43 - #define COLUMN_PGMNEIFPOLRNOSTATE 44 - #define COLUMN_PGMNEIFPOLRERROR 45 - #define COLUMN_PGMNEIFPOLRPARITYERROR 46 - #define COLUMN_PGMNEIFPOLRSUCCESS 47 - #define COLUMN_PGMNEIFPOLRORIGINATED 48 - #define COLUMN_PGMNEIFNCFERROR 49 - #define COLUMN_PGMNEIFNCFPARITYERROR 50 - #define COLUMN_PGMNEIFNCFPARTIALPARITY 51 - #define COLUMN_PGMNEIFNCFRECEIVED 52 - #define COLUMN_PGMNEIFNCFANTICIPATED 53 - #define COLUMN_PGMNEIFNCFREDIRECTING 54 - #define COLUMN_PGMNEIFNAKELIMINATED 55 - #define COLUMN_PGMNEIFNAKERROR 56 - #define COLUMN_PGMNEIFNAKPARITYERROR 57 - #define COLUMN_PGMNEIFNNAKELIMINATED 58 - #define COLUMN_PGMNEIFNNAKERROR 59 - #define COLUMN_PGMNEIFNNAKPARITYERROR 60 - #define COLUMN_PGMNEIFNNAKCONGESTIONREPORTS 61 - #define COLUMN_PGMNEIFNAKRETRYEXPIRED 62 - #define COLUMN_PGMNEIFNAKRETRYEXPIREDDLR 63 - #define COLUMN_PGMNEIFNAKFORWARDEDDLR 64 - #define COLUMN_PGMNEIFNAKRETRANSMITTED 65 - #define COLUMN_PGMNEIFRDATAELIMINATEDOIF 66 - #define COLUMN_PGMNEIFRDATAELIMINATEDSQN 67 - #define COLUMN_PGMNEIFINRDATAFRAGMENTS 68 - #define COLUMN_PGMNEIFRDATAFRAGMENTSNOSESSIONERRORS 69 - #define COLUMN_PGMNEIFRDATAFRAGMENTSELIMINATEDOIF 70 - #define COLUMN_PGMNEIFRDATAFRAGMENTSELIMINATEDSQN 71 - #define COLUMN_PGMNEIFOUTRDATAFRAGMENTS 72 - -/* column number definitions for table pgmNeTsiTable */ - #define COLUMN_PGMNETSIGLOBALID 1 - #define COLUMN_PGMNETSIDATASOURCEPORT 2 - #define COLUMN_PGMNETSISTATEBITS 3 - #define COLUMN_PGMNETSIDATADESTINATIONPORT 4 - #define COLUMN_PGMNETSISOURCEADDRESS 5 - #define COLUMN_PGMNETSIGROUPADDRESS 6 - #define COLUMN_PGMNETSIUPSTREAMADDRESS 7 - #define COLUMN_PGMNETSIUPSTREAMIFINDEX 8 - #define COLUMN_PGMNETSIDLRADDRESS 9 - -/* column number definitions for table pgmNeTsiPerformanceTable */ - #define COLUMN_PGMNETSIPERFORMANCEGLOBALID 1 - #define COLUMN_PGMNETSIPERFORMANCEDATASOURCEPORT 2 - #define COLUMN_PGMNETSISESSIONTRAILEDGESEQ 3 - #define COLUMN_PGMNETSISESSIONINCRSEQ 4 - #define COLUMN_PGMNETSILEADEDGESEQ 5 - #define COLUMN_PGMNETSIINSPMS 6 - #define COLUMN_PGMNETSIOUTSPMS 7 - #define COLUMN_PGMNETSIINPARITYSPMS 8 - #define COLUMN_PGMNETSIOUTPARITYSPMS 9 - #define COLUMN_PGMNETSITOTALREXMITSTATES 10 - #define COLUMN_PGMNETSITOTALREXMITTIMEDOUT 11 - #define COLUMN_PGMNETSIINRDATA 12 - #define COLUMN_PGMNETSIOUTRDATA 13 - #define COLUMN_PGMNETSIINPARITYRDATA 14 - #define COLUMN_PGMNETSIOUTPARITYRDATA 15 - #define COLUMN_PGMNETSIINRDATANOSTATEERRORS 16 - #define COLUMN_PGMNETSIUNIQUENAKS 17 - #define COLUMN_PGMNETSIINNAKS 18 - #define COLUMN_PGMNETSIOUTNAKS 19 - #define COLUMN_PGMNETSIUNIQUEPARITYNAKS 20 - #define COLUMN_PGMNETSIINPARITYNAKS 21 - #define COLUMN_PGMNETSIOUTPARITYNAKS 22 - #define COLUMN_PGMNETSIINNAKSEQERRORS 23 - #define COLUMN_PGMNETSIINNNAKS 24 - #define COLUMN_PGMNETSIOUTNNAKS 25 - #define COLUMN_PGMNETSIINPARITYNNAKS 26 - #define COLUMN_PGMNETSIOUTPARITYNNAKS 27 - #define COLUMN_PGMNETSIINNCFS 28 - #define COLUMN_PGMNETSIOUTNCFS 29 - #define COLUMN_PGMNETSIINPARITYNCFS 30 - #define COLUMN_PGMNETSIOUTPARITYNCFS 31 - #define COLUMN_PGMNETSISPMSEQUENCENUMBER 32 - #define COLUMN_PGMNETSITRANSMISSIONGROUPSIZE 33 - #define COLUMN_PGMNETSITIMEOUT 34 - #define COLUMN_PGMNETSILASTTTL 35 - #define COLUMN_PGMNETSILINKLOSSRATE 36 - #define COLUMN_PGMNETSIPATHLOSSRATE 37 - #define COLUMN_PGMNETSIRECEIVERLOSSRATE 38 - #define COLUMN_PGMNETSICONGESTIONREPORTLEAD 39 - #define COLUMN_PGMNETSICONGESTIONREPORTWORSTRECEIVER 40 - -/* column number definitions for table pgmNeTsiRtxTable */ - #define COLUMN_PGMNETSIRTXSEQUENCENUMBER 1 - #define COLUMN_PGMNETSIRTXSEQUENCENUMBERTYPE 2 - #define COLUMN_PGMNETSIRTXREQPARITYTGCOUNT 4 - #define COLUMN_PGMNETSIRTXTIMEOUT 5 - #define COLUMN_PGMNETSIRTXSTATEBITS 6 - -/* column number definitions for table pgmNeTsiRtxIfTable */ - #define COLUMN_PGMNETSIRTXIFINDEX 1 - #define COLUMN_PGMNETSIRTXIFPACKETCOUNT 2 - -/* column number definitions for table pgmNeTsiPolrTable */ - #define COLUMN_PGMNETSIPOLRSOURCE 1 - #define COLUMN_PGMNETSIPOLRSEQUENCENUMBER 2 - -/* column number definitions for table pgmNeTsiPollTable */ - #define COLUMN_PGMNETSIPOLLTYPE 1 - #define COLUMN_PGMNETSIPOLLSEQUENCE 2 - #define COLUMN_PGMNETSIPOLLCHILDBACKOFF 3 - #define COLUMN_PGMNETSIPOLLMASK 4 - #define COLUMN_PGMNETSIPOLLPERIOD 5 - #define COLUMN_PGMNETSIPOLLCOUNT 6 - #define COLUMN_PGMNETSIPOLLTIMEOUT 7 - -/* column number definitions for table pgmSourceTable */ - #define COLUMN_PGMSOURCEGLOBALID 1 - #define COLUMN_PGMSOURCESOURCEPORT 2 - #define COLUMN_PGMSOURCESOURCEADDRESS 3 - #define COLUMN_PGMSOURCEGROUPADDRESS 4 - #define COLUMN_PGMSOURCEDESTPORT 5 - #define COLUMN_PGMSOURCESOURCEGSI 6 - #define COLUMN_PGMSOURCESOURCEPORTNUMBER 7 - -/* column number definitions for table pgmSourceConfigTable */ - #define COLUMN_PGMSOURCECONFIGGLOBALID 1 - #define COLUMN_PGMSOURCECONFIGSOURCEPORT 2 - #define COLUMN_PGMSOURCETTL 3 - #define COLUMN_PGMSOURCEADVMODE 4 - #define COLUMN_PGMSOURCELATEJOIN 5 - #define COLUMN_PGMSOURCETXWMAXRTE 6 - #define COLUMN_PGMSOURCETXWSECS 7 - #define COLUMN_PGMSOURCETXWADVSECS 8 - #define COLUMN_PGMSOURCEADVIVL 9 - #define COLUMN_PGMSOURCESPMIVL 10 - #define COLUMN_PGMSOURCESPMHEARTBEATIVLMIN 11 - #define COLUMN_PGMSOURCESPMHEARTBEATIVLMAX 12 - #define COLUMN_PGMSOURCERDATABACKOFFIVL 13 - #define COLUMN_PGMSOURCEFEC 14 - #define COLUMN_PGMSOURCEFECTRANSMISSIONGRPSIZE 15 - #define COLUMN_PGMSOURCEFECPROACTIVEPARITYSIZE 16 - #define COLUMN_PGMSOURCESPMPATHADDRESS 17 - -/* column number definitions for table pgmSourcePerformanceTable */ - #define COLUMN_PGMSOURCEPERFORMANCEGLOBALID 1 - #define COLUMN_PGMSOURCEPERFORMANCESOURCEPORT 2 - #define COLUMN_PGMSOURCEDATABYTESSENT 3 - #define COLUMN_PGMSOURCEDATAMSGSSENT 4 - #define COLUMN_PGMSOURCEBYTESBUFFERED 5 - #define COLUMN_PGMSOURCEMSGSBUFFERED 6 - #define COLUMN_PGMSOURCEBYTESRETRANSMITTED 7 - #define COLUMN_PGMSOURCEMSGSRETRANSMITTED 8 - #define COLUMN_PGMSOURCEBYTESSENT 9 - #define COLUMN_PGMSOURCERAWNAKSRECEIVED 10 - #define COLUMN_PGMSOURCENAKSIGNORED 11 - #define COLUMN_PGMSOURCECKSUMERRORS 12 - #define COLUMN_PGMSOURCEMALFORMEDNAKS 13 - #define COLUMN_PGMSOURCEPACKETSDISCARDED 14 - #define COLUMN_PGMSOURCENAKSRCVD 15 - #define COLUMN_PGMSOURCEPARITYBYTESRETRANSMITTED 16 - #define COLUMN_PGMSOURCESELECTIVEBYTESRETRANSMITED 17 - #define COLUMN_PGMSOURCEPARITYMSGSRETRANSMITTED 18 - #define COLUMN_PGMSOURCESELECTIVEMSGSRETRANSMITTED 19 - #define COLUMN_PGMSOURCEBYTESADMIT 20 - #define COLUMN_PGMSOURCEMSGSADMIT 21 - #define COLUMN_PGMSOURCEPARITYNAKPACKETSRECEIVED 22 - #define COLUMN_PGMSOURCESELECTIVENAKPACKETSRECEIVED 23 - #define COLUMN_PGMSOURCEPARITYNAKSRECEIVED 24 - #define COLUMN_PGMSOURCESELECTIVENAKSRECEIVED 25 - #define COLUMN_PGMSOURCEPARITYNAKSIGNORED 26 - #define COLUMN_PGMSOURCESELECTIVENAKSIGNORED 27 - #define COLUMN_PGMSOURCEACKERRORS 28 - #define COLUMN_PGMSOURCEPGMCCACKER 29 - #define COLUMN_PGMSOURCETRANSMISSIONCURRENTRATE 30 - #define COLUMN_PGMSOURCEACKPACKETSRECEIVED 31 - #define COLUMN_PGMSOURCENNAKPACKETSRECEIVED 32 - #define COLUMN_PGMSOURCEPARITYNNAKPACKETSRECEIVED 33 - #define COLUMN_PGMSOURCESELECTIVENNAKPACKETSRECEIVED 34 - #define COLUMN_PGMSOURCENNAKSRECEIVED 35 - #define COLUMN_PGMSOURCEPARITYNNAKSRECEIVED 36 - #define COLUMN_PGMSOURCESELECTIVENNAKSRECEIVED 37 - #define COLUMN_PGMSOURCENNAKERRORS 38 - -/* column number definitions for table pgmReceiverTable */ - #define COLUMN_PGMRECEIVERGLOBALID 1 - #define COLUMN_PGMRECEIVERSOURCEPORT 2 - #define COLUMN_PGMRECEIVERINSTANCE 3 - #define COLUMN_PGMRECEIVERGROUPADDRESS 4 - #define COLUMN_PGMRECEIVERDESTPORT 5 - #define COLUMN_PGMRECEIVERSOURCEADDRESS 6 - #define COLUMN_PGMRECEIVERLASTHOP 7 - #define COLUMN_PGMRECEIVERSOURCEGSI 8 - #define COLUMN_PGMRECEIVERSOURCEPORTNUMBER 9 - #define COLUMN_PGMRECEIVERUNIQUEINSTANCE 10 - -/* column number definitions for table pgmReceiverConfigTable */ - #define COLUMN_PGMRECEIVERCONFIGGLOBALID 1 - #define COLUMN_PGMRECEIVERCONFIGSOURCEPORT 2 - #define COLUMN_PGMRECEIVERCONFIGINSTANCE 3 - #define COLUMN_PGMRECEIVERNAKBACKOFFIVL 4 - #define COLUMN_PGMRECEIVERNAKREPEATIVL 5 - #define COLUMN_PGMRECEIVERNAKNCFRETRIES 6 - #define COLUMN_PGMRECEIVERNAKRDATAIVL 7 - #define COLUMN_PGMRECEIVERNAKDATARETRIES 8 - #define COLUMN_PGMRECEIVERSENDNAKS 9 - #define COLUMN_PGMRECEIVERLATEJOIN 10 - #define COLUMN_PGMRECEIVERNAKTTL 11 - #define COLUMN_PGMRECEIVERDELIVERYORDER 12 - #define COLUMN_PGMRECEIVERMCASTNAKS 13 - #define COLUMN_PGMRECEIVERNAKFAILURETHRESHOLDTIMER 14 - #define COLUMN_PGMRECEIVERNAKFAILURETHRESHOLD 15 - -/* column number definitions for table pgmReceiverPerformanceTable */ - #define COLUMN_PGMRECEIVERPERFORMANCEGLOBALID 1 - #define COLUMN_PGMRECEIVERPERFORMANCESOURCEPORT 2 - #define COLUMN_PGMRECEIVERPERFORMANCEINSTANCE 3 - #define COLUMN_PGMRECEIVERDATABYTESRECEIVED 4 - #define COLUMN_PGMRECEIVERDATAMSGSRECEIVED 5 - #define COLUMN_PGMRECEIVERNAKSSENT 6 - #define COLUMN_PGMRECEIVERNAKSRETRANSMITTED 7 - #define COLUMN_PGMRECEIVERNAKFAILURES 8 - #define COLUMN_PGMRECEIVERBYTESRECEIVED 9 - #define COLUMN_PGMRECEIVERNAKSSUPPRESSED 10 - #define COLUMN_PGMRECEIVERCKSUMERRORS 11 - #define COLUMN_PGMRECEIVERMALFORMEDSPMS 12 - #define COLUMN_PGMRECEIVERMALFORMEDODATA 13 - #define COLUMN_PGMRECEIVERMALFORMEDRDATA 14 - #define COLUMN_PGMRECEIVERMALFORMEDNCFS 15 - #define COLUMN_PGMRECEIVERPACKETSDISCARDED 16 - #define COLUMN_PGMRECEIVERLOSSES 17 - #define COLUMN_PGMRECEIVERBYTESDELIVEREDTOAPP 18 - #define COLUMN_PGMRECEIVERMSGSDELIVEREDTOAPP 19 - #define COLUMN_PGMRECEIVERDUPSPMS 20 - #define COLUMN_PGMRECEIVERDUPDATAS 21 - #define COLUMN_PGMRECEIVERDUPPARITIES 22 - #define COLUMN_PGMRECEIVERNAKPACKETSSENT 23 - #define COLUMN_PGMRECEIVERPARITYNAKPACKETSSENT 24 - #define COLUMN_PGMRECEIVERSELECTIVENAKPACKETSSENT 25 - #define COLUMN_PGMRECEIVERPARITYNAKSSENT 26 - #define COLUMN_PGMRECEIVERSELECTIVENAKSSENT 27 - #define COLUMN_PGMRECEIVERPARITYNAKSRETRANSMITTED 28 - #define COLUMN_PGMRECEIVERSELECTIVENAKSRETRANSMITTED 29 - #define COLUMN_PGMRECEIVERNAKSFAILED 30 - #define COLUMN_PGMRECEIVERPARITYNAKSFAILED 31 - #define COLUMN_PGMRECEIVERSELECTIVENAKSFAILED 32 - #define COLUMN_PGMRECEIVERNAKSFAILEDRXWADVANCED 33 - #define COLUMN_PGMRECEIVERNAKSFALEDNCFRETRIESEXCEEDED 34 - #define COLUMN_PGMRECEIVERNAKSFAILEDDATARETRIESEXCEEDED 35 - #define COLUMN_PGMRECEIVERNAKSFAILEDGENEXPIRED 36 - #define COLUMN_PGMRECEIVERNAKFAILURESDELIVERED 37 - #define COLUMN_PGMRECEIVERPARITYNAKSSUPPRESSED 38 - #define COLUMN_PGMRECEIVERSELECTIVENAKSSUPPRESSED 39 - #define COLUMN_PGMRECEIVERNAKERRORS 40 - #define COLUMN_PGMRECEIVEROUTSTANDINGPARITYNAKS 41 - #define COLUMN_PGMRECEIVEROUTSTANDINGSELECTIVENAKS 42 - #define COLUMN_PGMRECEIVERLASTACTIVITY 43 - #define COLUMN_PGMRECEIVERNAKSVCTIMEMIN 44 - #define COLUMN_PGMRECEIVERNAKSVCTIMEMEAN 45 - #define COLUMN_PGMRECEIVERNAKSVCTIMEMAX 46 - #define COLUMN_PGMRECEIVERNAKFAILTIMEMIN 47 - #define COLUMN_PGMRECEIVERNAKFAILTIMEMEAN 48 - #define COLUMN_PGMRECEIVERNAKFAILTIMEMAX 49 - #define COLUMN_PGMRECEIVERNAKTRANSMITMIN 50 - #define COLUMN_PGMRECEIVERNAKTRANSMITMEAN 51 - #define COLUMN_PGMRECEIVERNAKTRANSMITMAX 52 - #define COLUMN_PGMRECEIVERACKSSENT 53 - #define COLUMN_PGMRECEIVERRXWTRAIL 54 - #define COLUMN_PGMRECEIVERRXWLEAD 55 - #define COLUMN_PGMRECEIVERNAKFAILURESLASTINTERVAL 56 - #define COLUMN_PGMRECEIVERLASTINTERVALNAKFAILURES 57 - -/* column number definitions for table pgmDlrSourceTable */ - #define COLUMN_PGMDLRSOURCEGLOBALID 1 - #define COLUMN_PGMDLRSOURCESOURCEPORT 2 - #define COLUMN_PGMDLRSOURCEGROUPADDRESS 3 - #define COLUMN_PGMDLRSOURCESOURCEGSI 4 - #define COLUMN_PGMDLRSOURCESOURCEPORTNUMBER 5 - -/* column number definitions for table pgmDlrSourceConfigTable */ - #define COLUMN_PGMDLRSOURCECONFIGGLOBALID 1 - #define COLUMN_PGMDLRSOURCECONFIGSOURCEPORT 2 - #define COLUMN_PGMDLRSOURCEGROUPTTL 3 - #define COLUMN_PGMDLRSOURCERDATABACKOFFIVL 4 - -/* column number definitions for table pgmDlrSourcePerformanceTable */ - #define COLUMN_PGMDLRSOURCEPERFORMANCEGLOBALID 1 - #define COLUMN_PGMDLRSOURCEPERFORMANCESOURCEPORT 2 - #define COLUMN_PGMDLRSOURCERDATAMSGSSENT 3 - #define COLUMN_PGMDLRSOURCERDATABYTESSENT 4 - #define COLUMN_PGMDLRSOURCEBYTESSENT 5 - #define COLUMN_PGMDLRSOURCENAKSRCVD 6 - #define COLUMN_PGMDLRSOURCENAKSIGNORED 7 - #define COLUMN_PGMDLRSOURCENAKERRORS 8 - #define COLUMN_PGMDLRSOURCEDISCARDS 9 - #define COLUMN_PGMDLRSOURCECKSUMERRORS 10 - #define COLUMN_PGMDLRSOURCENNAKSSENT 11 - #define COLUMN_PGMDLRSOURCEBYTESBUFFERED 12 - #define COLUMN_PGMDLRSOURCEMSGSBUFFERED 13 - #define COLUMN_PGMDLRSOURCEPARITYBYTESRETRANSMITTED 14 - #define COLUMN_PGMDLRSOURCESELECTIVEBYTESRETRANSMITED 15 - #define COLUMN_PGMDLRSOURCEPARITYMSGSRETRANSMITTED 16 - #define COLUMN_PGMDLRSOURCESELECTIVEMSGSRETRANSMITTED 17 - #define COLUMN_PGMDLRSOURCEBYTESADMIT 18 - #define COLUMN_PGMDLRSOURCEMSGSADMIT 19 - #define COLUMN_PGMDLRSOURCENAKPACKETSRECEIVED 20 - #define COLUMN_PGMDLRSOURCEPARITYNAKPACKETSRECEIVED 21 - #define COLUMN_PGMDLRSOURCESELECTIVENAKPACKETSRECEIVED 22 - #define COLUMN_PGMDLRSOURCEPARITYNAKSRECEIVED 23 - #define COLUMN_PGMDLRSOURCESELECTIVENAKSRECEIVED 24 - #define COLUMN_PGMDLRSOURCEPARITYNAKSIGNORED 25 - #define COLUMN_PGMDLRSOURCESELECTIVENAKSIGNORED 26 - #define COLUMN_PGMDLRSOURCEACKERRORS 27 - #define COLUMN_PGMDLRSOURCENNAKERRORS 28 - #define COLUMN_PGMDLRSOURCEACKPACKETSRECEIVED 29 - #define COLUMN_PGMDLRSOURCENNAKPACKETSRECEIVED 30 - #define COLUMN_PGMDLRSOURCEPARITYNNAKPACKETSRECEIVED 31 - #define COLUMN_PGMDLRSOURCESELECTIVENNAKPACKETSRECEIVED 32 - #define COLUMN_PGMDLRSOURCENNAKSRECEIVED 33 - #define COLUMN_PGMDLRSOURCEPARITYNNAKSRECEIVED 34 - #define COLUMN_PGMDLRSOURCESELECTIVENNAKSRECEIVED 35 -#endif /* PGMMIB_COLUMNS_H */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB_enums.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB_enums.h deleted file mode 100644 index 9ae5760..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB_enums.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Note: this file originally auto-generated by mib2c using - * : mib2c.column_enums.conf,v 5.2 2003/02/22 04:09:25 hardaker Exp $ - */ -#ifndef PGMMIB_ENUMS_H -#define PGMMIB_ENUMS_H - -/* enums for column pgmNeIfPgmEnable */ - #define PGMNEIFPGMENABLE_ENABLE 1 - #define PGMNEIFPGMENABLE_DISABLE 2 - -/* enums for column pgmNeTsiStateBits */ - #define PGMNETSISTATEBITS_INITIALISING 0 - #define PGMNETSISTATEBITS_SPMSQNSTATEVALID 1 - #define PGMNETSISTATEBITS_DLRCANPROVIDEPARITY 2 - -/* enums for column pgmNeTsiRtxSequenceNumberType */ - #define PGMNETSIRTXSEQUENCENUMBERTYPE_SELECTIVE 1 - #define PGMNETSIRTXSEQUENCENUMBERTYPE_TG 2 - -/* enums for column pgmNeTsiRtxStateBits */ - #define PGMNETSIRTXSTATEBITS_INITIALISING 0 - #define PGMNETSIRTXSTATEBITS_ELIMINATING 1 - #define PGMNETSIRTXSTATEBITS_REDIRECTING 2 - #define PGMNETSIRTXSTATEBITS_STATECREATEDBYNULLNAK 3 - #define PGMNETSIRTXSTATEBITS_LISTNAKENTRY 4 - #define PGMNETSIRTXSTATEBITS_PARITYSTATE 5 - -/* enums for column pgmNeTsiPollType */ - #define PGMNETSIPOLLTYPE_GENERAL 1 - #define PGMNETSIPOLLTYPE_DLR 2 - -/* enums for column pgmSourceAdvMode */ - #define PGMSOURCEADVMODE_DATA 1 - #define PGMSOURCEADVMODE_TIME 2 - #define PGMSOURCEADVMODE_APPLCTRL 3 - #define PGMSOURCEADVMODE_OTHER 4 - -/* enums for column pgmSourceLateJoin */ - #define PGMSOURCELATEJOIN_ENABLE 1 - #define PGMSOURCELATEJOIN_DISABLE 2 - -/* enums for column pgmSourceFEC */ - #define PGMSOURCEFEC_DISABLED 1 - #define PGMSOURCEFEC_ENABLEDFIXEDPACKETSIZE 2 - #define PGMSOURCEFEC_ENABLEDVARIABLEPACKETSIZE 3 - -/* enums for column pgmReceiverSendNaks */ - #define PGMRECEIVERSENDNAKS_ENABLED 1 - #define PGMRECEIVERSENDNAKS_DISABLED 2 - -/* enums for column pgmReceiverLateJoin */ - #define PGMRECEIVERLATEJOIN_ENABLED 1 - #define PGMRECEIVERLATEJOIN_DISABLED 2 - -/* enums for column pgmReceiverDeliveryOrder */ - #define PGMRECEIVERDELIVERYORDER_UNORDERED 1 - #define PGMRECEIVERDELIVERYORDER_ORDERED 2 - -/* enums for column pgmReceiverMcastNaks */ - #define PGMRECEIVERMCASTNAKS_ENABLED 1 - #define PGMRECEIVERMCASTNAKS_DISABLED 2 - -#endif /* PGMMIB_ENUMS_H */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/processor.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/processor.h deleted file mode 100644 index f7ee31a..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/processor.h +++ /dev/null @@ -1,61 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * Processor macros for cross-platform, cross-compiler froyo. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_PROCESSOR_H__ -#define __PGM_IMPL_PROCESSOR_H__ - -/* Memory prefetch */ -#if defined( sun ) -static inline void pgm_prefetch (const void *x) -{ - asm volatile ( "prefetch [%0], #one_write" - : /* nil */ - : "r" (x)); -} -static inline void pgm_prefetchw (const void *x) -{ - asm volatile ( "prefetch [%0], #n_writes" - : /* nil */ - : "r" (x)); -} -#elif defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) -static inline void pgm_prefetch (const void *x) -{ - __builtin_prefetch (x, 0 /* read */, 0 /* no temporal locality */); -} -static inline void pgm_prefetchw (const void *x) -{ - __builtin_prefetch (x, 1 /* write */, 3 /* high temporal */); -} -#else -static inline void pgm_prefetch (PGM_GNUC_UNUSED const void *x) -{ -} -static inline void pgm_prefetchw (PGM_GNUC_UNUSED const void *x) -{ -} -#endif - -#endif /* __PGM_IMPL_PROCESSOR_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/queue.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/queue.h deleted file mode 100644 index c640e52..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/queue.h +++ /dev/null @@ -1,51 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable double-ended queue. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_QUEUE_H__ -#define __PGM_IMPL_QUEUE_H__ - -typedef struct pgm_queue_t pgm_queue_t; - -#include -#include - -PGM_BEGIN_DECLS - -struct pgm_queue_t -{ - pgm_list_t* head; /* head & tail equal on 1 element */ - pgm_list_t* tail; - unsigned length; -}; - -PGM_GNUC_INTERNAL bool pgm_queue_is_empty (const pgm_queue_t*const) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL void pgm_queue_push_head_link (pgm_queue_t*restrict, pgm_list_t*restrict); -PGM_GNUC_INTERNAL pgm_list_t* pgm_queue_pop_tail_link (pgm_queue_t*); -PGM_GNUC_INTERNAL pgm_list_t* pgm_queue_peek_tail_link (pgm_queue_t*) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL void pgm_queue_unlink (pgm_queue_t*restrict, pgm_list_t*restrict); - -PGM_END_DECLS - -#endif /* __PGM_IMPL_QUEUE_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/rand.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/rand.h deleted file mode 100644 index 0adfd78..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/rand.h +++ /dev/null @@ -1,50 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable weak pseudo-random generator. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_RAND_H__ -#define __PGM_IMPL_RAND_H__ - -typedef struct pgm_rand_t pgm_rand_t; - -#include - -PGM_BEGIN_DECLS - -struct pgm_rand_t { - uint32_t seed; -}; - -PGM_GNUC_INTERNAL void pgm_rand_create (pgm_rand_t*); -PGM_GNUC_INTERNAL uint32_t pgm_rand_int (pgm_rand_t*); -PGM_GNUC_INTERNAL int32_t pgm_rand_int_range (pgm_rand_t*, int32_t, int32_t); -PGM_GNUC_INTERNAL uint32_t pgm_random_int (void); -PGM_GNUC_INTERNAL int32_t pgm_random_int_range (int32_t, int32_t); - -PGM_GNUC_INTERNAL void pgm_rand_init (void); -PGM_GNUC_INTERNAL void pgm_rand_shutdown (void); - -PGM_END_DECLS - -#endif /* __PGM_IMPL_RAND_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/rate_control.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/rate_control.h deleted file mode 100644 index b27b266..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/rate_control.h +++ /dev/null @@ -1,54 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * Rate regulation. - * - * Copyright (c) 2006-2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_RATE_CONTROL_H__ -#define __PGM_IMPL_RATE_CONTROL_H__ - -typedef struct pgm_rate_t pgm_rate_t; - -#include -#include -#include - -PGM_BEGIN_DECLS - -struct pgm_rate_t { - ssize_t rate_per_sec; - ssize_t rate_per_msec; - size_t iphdr_len; - - ssize_t rate_limit; /* signed for math */ - pgm_time_t last_rate_check; - pgm_spinlock_t spinlock; -}; - -PGM_GNUC_INTERNAL void pgm_rate_create (pgm_rate_t*, const ssize_t, const size_t, const uint16_t); -PGM_GNUC_INTERNAL void pgm_rate_destroy (pgm_rate_t*); -PGM_GNUC_INTERNAL bool pgm_rate_check (pgm_rate_t*, const size_t, const bool); -PGM_GNUC_INTERNAL pgm_time_t pgm_rate_remaining (pgm_rate_t*, const size_t); - -PGM_END_DECLS - -#endif /* __PGM_IMPL_RATE_CONTROL_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/receiver.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/receiver.h deleted file mode 100644 index e9c3a75..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/receiver.h +++ /dev/null @@ -1,142 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * PGM receiver socket. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_IMPL_RECEIVER_H__ -#define __PGM_IMPL_RECEIVER_H__ - -typedef struct pgm_peer_t pgm_peer_t; - -#ifndef _WIN32 -# include -#endif -#include -#include - -PGM_BEGIN_DECLS - -/* Performance Counters */ - -enum { - PGM_PC_RECEIVER_DATA_BYTES_RECEIVED, - PGM_PC_RECEIVER_DATA_MSGS_RECEIVED, - PGM_PC_RECEIVER_NAK_FAILURES, - PGM_PC_RECEIVER_BYTES_RECEIVED, -/* PGM_PC_RECEIVER_CKSUM_ERRORS, */ /* inherently same as source */ - PGM_PC_RECEIVER_MALFORMED_SPMS, - PGM_PC_RECEIVER_MALFORMED_ODATA, - PGM_PC_RECEIVER_MALFORMED_RDATA, - PGM_PC_RECEIVER_MALFORMED_NCFS, - PGM_PC_RECEIVER_PACKETS_DISCARDED, - PGM_PC_RECEIVER_LOSSES, -/* PGM_PC_RECEIVER_BYTES_DELIVERED_TO_APP, */ -/* PGM_PC_RECEIVER_MSGS_DELIVERED_TO_APP, */ - PGM_PC_RECEIVER_DUP_SPMS, - PGM_PC_RECEIVER_DUP_DATAS, - PGM_PC_RECEIVER_PARITY_NAK_PACKETS_SENT, - PGM_PC_RECEIVER_SELECTIVE_NAK_PACKETS_SENT, - PGM_PC_RECEIVER_PARITY_NAKS_SENT, - PGM_PC_RECEIVER_SELECTIVE_NAKS_SENT, - PGM_PC_RECEIVER_PARITY_NAKS_RETRANSMITTED, - PGM_PC_RECEIVER_SELECTIVE_NAKS_RETRANSMITTED, - PGM_PC_RECEIVER_PARITY_NAKS_FAILED, - PGM_PC_RECEIVER_SELECTIVE_NAKS_FAILED, - PGM_PC_RECEIVER_NAKS_FAILED_RXW_ADVANCED, - PGM_PC_RECEIVER_NAKS_FAILED_NCF_RETRIES_EXCEEDED, - PGM_PC_RECEIVER_NAKS_FAILED_DATA_RETRIES_EXCEEDED, -/* PGM_PC_RECEIVER_NAKS_FAILED_GEN_EXPIRED */ - PGM_PC_RECEIVER_NAK_FAILURES_DELIVERED, - PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED, - PGM_PC_RECEIVER_NAK_ERRORS, -/* PGM_PC_RECEIVER_LAST_ACTIVITY, */ -/* PGM_PC_RECEIVER_NAK_SVC_TIME_MIN, */ - PGM_PC_RECEIVER_NAK_SVC_TIME_MEAN, -/* PGM_PC_RECEIVER_NAK_SVC_TIME_MAX, */ -/* PGM_PC_RECEIVER_NAK_FAIL_TIME_MIN, */ - PGM_PC_RECEIVER_NAK_FAIL_TIME_MEAN, -/* PGM_PC_RECEIVER_NAK_FAIL_TIME_MAX, */ -/* PGM_PC_RECEIVER_TRANSMIT_MIN, */ - PGM_PC_RECEIVER_TRANSMIT_MEAN, -/* PGM_PC_RECEIVER_TRANSMIT_MAX, */ - PGM_PC_RECEIVER_ACKS_SENT, - -/* marker */ - PGM_PC_RECEIVER_MAX -}; - -struct pgm_peer_t { - volatile uint32_t ref_count; /* atomic integer */ - - pgm_tsi_t tsi; - struct sockaddr_storage group_nla; - struct sockaddr_storage nla, local_nla; /* nla = advertised, local_nla = from packet */ - struct sockaddr_storage poll_nla; /* from parent to direct poll-response */ - struct sockaddr_storage redirect_nla; /* from dlr */ - pgm_time_t polr_expiry; - pgm_time_t spmr_expiry; - pgm_time_t spmr_tstamp; - - pgm_rxw_t* restrict window; - pgm_sock_t* restrict sock; - pgm_list_t peers_link; - pgm_slist_t pending_link; - - unsigned is_fec_enabled:1; - unsigned has_proactive_parity:1; /* indicating availability from this source */ - unsigned has_ondemand_parity:1; - - uint32_t spm_sqn; - pgm_time_t expiry; - - pgm_time_t ack_rb_expiry; /* 0 = no ACK pending */ - pgm_time_t ack_last_tstamp; /* in source time reference */ - pgm_list_t ack_link; - - uint32_t last_poll_sqn; - uint16_t last_poll_round; - pgm_time_t last_packet; - pgm_time_t last_data_tstamp; /* local timestamp of ack_last_tstamp */ - unsigned last_commit; - uint32_t lost_count; - uint32_t last_cumulative_losses; - volatile uint32_t cumulative_stats[PGM_PC_RECEIVER_MAX]; - uint32_t snap_stats[PGM_PC_RECEIVER_MAX]; - - uint32_t min_fail_time; - uint32_t max_fail_time; -}; - -PGM_GNUC_INTERNAL pgm_peer_t* pgm_new_peer (pgm_sock_t*const restrict, const pgm_tsi_t*const restrict, const struct sockaddr*const restrict, const socklen_t, const struct sockaddr*const restrict, const socklen_t, const pgm_time_t); -PGM_GNUC_INTERNAL void pgm_peer_unref (pgm_peer_t*); -PGM_GNUC_INTERNAL int pgm_flush_peers_pending (pgm_sock_t*const restrict, struct pgm_msgv_t**restrict, const struct pgm_msgv_t*const, size_t*const restrict, unsigned*const restrict); -PGM_GNUC_INTERNAL bool pgm_peer_has_pending (pgm_peer_t*const) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL void pgm_peer_set_pending (pgm_sock_t*const, pgm_peer_t*const); -PGM_GNUC_INTERNAL bool pgm_check_peer_state (pgm_sock_t*const, const pgm_time_t); -PGM_GNUC_INTERNAL void pgm_set_reset_error (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_msgv_t*const restrict); -PGM_GNUC_INTERNAL pgm_time_t pgm_min_receiver_expiry (pgm_time_t, pgm_sock_t*) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL bool pgm_on_peer_nak (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL bool pgm_on_data (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL bool pgm_on_ncf (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL bool pgm_on_spm (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL bool pgm_on_poll (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; - -PGM_END_DECLS - -#endif /* __PGM_IMPL_RECEIVER_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/reed_solomon.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/reed_solomon.h deleted file mode 100644 index 98f6734..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/reed_solomon.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Reed-Solomon forward error correction based on Vandermonde matrices - * - * Copyright (c) 2006-2008 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_REED_SOLOMON_H__ -#define __PGM_IMPL_REED_SOLOMON_H__ - -typedef struct pgm_rs_t pgm_rs_t; - -#include -#include - -PGM_BEGIN_DECLS - -struct pgm_rs_t { - uint8_t n, k; /* RS(n, k) */ - pgm_gf8_t* GM; - pgm_gf8_t* RM; -}; - -#define PGM_RS_DEFAULT_N 255 - -PGM_GNUC_INTERNAL void pgm_rs_create (pgm_rs_t*, const uint8_t, const uint8_t); -PGM_GNUC_INTERNAL void pgm_rs_destroy (pgm_rs_t*); -PGM_GNUC_INTERNAL void pgm_rs_encode (pgm_rs_t*restrict, const pgm_gf8_t**restrict, const uint8_t, pgm_gf8_t*restrict, const uint16_t); -PGM_GNUC_INTERNAL void pgm_rs_decode_parity_inline (pgm_rs_t*restrict, pgm_gf8_t**restrict, const uint8_t*restrict, const uint16_t); -PGM_GNUC_INTERNAL void pgm_rs_decode_parity_appended (pgm_rs_t*restrict, pgm_gf8_t**restrict, const uint8_t*restrict, const uint16_t); - -PGM_END_DECLS - -#endif /* __PGM_IMPL_REED_SOLOMON_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/rxw.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/rxw.h deleted file mode 100644 index 89a8921..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/rxw.h +++ /dev/null @@ -1,220 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * basic receive window. - * - * Copyright (c) 2006-2008 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_IMPL_RXW_H__ -#define __PGM_IMPL_RXW_H__ - -typedef struct pgm_rxw_state_t pgm_rxw_state_t; -typedef struct pgm_rxw_t pgm_rxw_t; - -#include - -PGM_BEGIN_DECLS - -enum -{ - PGM_PKT_STATE_ERROR = 0, - PGM_PKT_STATE_BACK_OFF, /* PGM protocol recovery states */ - PGM_PKT_STATE_WAIT_NCF, - PGM_PKT_STATE_WAIT_DATA, - PGM_PKT_STATE_HAVE_DATA, /* data received waiting to commit to application layer */ - PGM_PKT_STATE_HAVE_PARITY, /* contains parity information not original data */ - PGM_PKT_STATE_COMMIT_DATA, /* commited data waiting for purging */ - PGM_PKT_STATE_LOST_DATA /* if recovery fails, but packet has not yet been commited */ -}; - -enum -{ - PGM_RXW_OK = 0, - PGM_RXW_INSERTED, - PGM_RXW_APPENDED, - PGM_RXW_UPDATED, - PGM_RXW_MISSING, - PGM_RXW_DUPLICATE, - PGM_RXW_MALFORMED, - PGM_RXW_BOUNDS, - PGM_RXW_SLOW_CONSUMER, - PGM_RXW_UNKNOWN -}; - -/* must be smaller than PGM skbuff control buffer */ -struct pgm_rxw_state_t { - pgm_time_t timer_expiry; - int pkt_state; - - uint8_t nak_transmit_count; /* 8-bit for size constraints */ - uint8_t ncf_retry_count; - uint8_t data_retry_count; - -/* only valid on tg_sqn::pkt_sqn = 0 */ - unsigned is_contiguous:1; /* transmission group */ -}; - -struct pgm_rxw_t { - const pgm_tsi_t* tsi; - - pgm_queue_t ack_backoff_queue; - pgm_queue_t nak_backoff_queue; - pgm_queue_t wait_ncf_queue; - pgm_queue_t wait_data_queue; -/* window context counters */ - uint32_t lost_count; /* failed to repair */ - uint32_t fragment_count; /* incomplete apdu */ - uint32_t parity_count; /* parity for repairs */ - uint32_t committed_count; /* but still in window */ - - uint16_t max_tpdu; /* maximum packet size */ - uint32_t lead, trail; - uint32_t rxw_trail, rxw_trail_init; - uint32_t commit_lead; - unsigned is_constrained:1; - unsigned is_defined:1; - unsigned has_event:1; /* edge triggered */ - unsigned is_fec_available:1; - pgm_rs_t rs; - uint32_t tg_size; /* transmission group size for parity recovery */ - uint8_t tg_sqn_shift; - - uint32_t bitmap; /* receive status of last 32 packets */ - uint32_t data_loss; /* p */ - uint32_t ack_c_p; /* constant Cᵨ */ - -/* counters all guint32 */ - uint32_t min_fill_time; /* restricted from pgm_time_t */ - uint32_t max_fill_time; - uint32_t min_nak_transmit_count; - uint32_t max_nak_transmit_count; - uint32_t cumulative_losses; - uint32_t bytes_delivered; - uint32_t msgs_delivered; - - size_t size; /* in bytes */ - unsigned alloc; /* in pkts */ -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) - struct pgm_sk_buff_t* pdata[]; -#elif defined(__cplusplus) - struct pgm_sk_buff_t* pdata[1]; -#else - struct pgm_sk_buff_t* pdata[0]; -#endif -}; - - -PGM_GNUC_INTERNAL pgm_rxw_t* pgm_rxw_create (const pgm_tsi_t*const, const uint16_t, const unsigned, const unsigned, const ssize_t, const uint32_t) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL void pgm_rxw_destroy (pgm_rxw_t*const); -PGM_GNUC_INTERNAL int pgm_rxw_add (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*const restrict, const pgm_time_t, const pgm_time_t) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL void pgm_rxw_add_ack (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*const restrict, const pgm_time_t); -PGM_GNUC_INTERNAL void pgm_rxw_remove_ack (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*const restrict); -PGM_GNUC_INTERNAL void pgm_rxw_remove_commit (pgm_rxw_t*const); -PGM_GNUC_INTERNAL ssize_t pgm_rxw_readv (pgm_rxw_t*const restrict, struct pgm_msgv_t** restrict, const unsigned) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL unsigned pgm_rxw_remove_trail (pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL unsigned pgm_rxw_update (pgm_rxw_t*const, const uint32_t, const uint32_t, const pgm_time_t, const pgm_time_t) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL void pgm_rxw_update_fec (pgm_rxw_t*const, const uint8_t); -PGM_GNUC_INTERNAL int pgm_rxw_confirm (pgm_rxw_t*const, uint32_t, pgm_time_t, pgm_time_t, pgm_time_t) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL void pgm_rxw_lost (pgm_rxw_t*const, const uint32_t); -PGM_GNUC_INTERNAL void pgm_rxw_state (pgm_rxw_t* restrict, struct pgm_sk_buff_t*restrict, const int); -PGM_GNUC_INTERNAL struct pgm_sk_buff_t* pgm_rxw_peek (pgm_rxw_t*const, const uint32_t) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL const char* pgm_pkt_state_string (const int) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL const char* pgm_rxw_returns_string (const int) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL void pgm_rxw_dump (const pgm_rxw_t*const); - -/* declare for GCC attributes */ -static inline unsigned pgm_rxw_max_length (const pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; -static inline uint32_t pgm_rxw_length (const pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; -static inline size_t pgm_rxw_size (const pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; -static inline bool pgm_rxw_is_empty (const pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; -static inline bool pgm_rxw_is_full (const pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; -static inline uint32_t pgm_rxw_lead (const pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; -static inline uint32_t pgm_rxw_next_lead (const pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; - -static inline -unsigned -pgm_rxw_max_length ( - const pgm_rxw_t* const window - ) -{ - pgm_assert (NULL != window); - return window->alloc; -} - -static inline -uint32_t -pgm_rxw_length ( - const pgm_rxw_t* const window - ) -{ - pgm_assert (NULL != window); - return ( 1 + window->lead ) - window->trail; -} - -static inline -size_t -pgm_rxw_size ( - const pgm_rxw_t* const window - ) -{ - pgm_assert (NULL != window); - return window->size; -} - -static inline -bool -pgm_rxw_is_empty ( - const pgm_rxw_t* const window - ) -{ - pgm_assert (NULL != window); - return pgm_rxw_length (window) == 0; -} - -static inline -bool -pgm_rxw_is_full ( - const pgm_rxw_t* const window - ) -{ - pgm_assert (NULL != window); - return pgm_rxw_length (window) == pgm_rxw_max_length (window); -} - -static inline -uint32_t -pgm_rxw_lead ( - const pgm_rxw_t* const window - ) -{ - pgm_assert (NULL != window); - return window->lead; -} - -static inline -uint32_t -pgm_rxw_next_lead ( - const pgm_rxw_t* const window - ) -{ - pgm_assert (NULL != window); - return (uint32_t)(pgm_rxw_lead (window) + 1); -} - -PGM_END_DECLS - -#endif /* __PGM_IMPL_RXW_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/slist.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/slist.h deleted file mode 100644 index e71b15d..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/slist.h +++ /dev/null @@ -1,52 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable singly-linked list. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_SLIST_H__ -#define __PGM_IMPL_SLIST_H__ - -typedef struct pgm_slist_t pgm_slist_t; - -#include - -PGM_BEGIN_DECLS - -struct pgm_slist_t -{ - void* restrict data; - struct pgm_slist_t* restrict next; -}; - -PGM_GNUC_INTERNAL pgm_slist_t* pgm_slist_append (pgm_slist_t*restrict, void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL pgm_slist_t* pgm_slist_prepend (pgm_slist_t*restrict, void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL pgm_slist_t* pgm_slist_prepend_link (pgm_slist_t*restrict, pgm_slist_t*restrict) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL pgm_slist_t* pgm_slist_remove (pgm_slist_t*restrict, const void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL pgm_slist_t* pgm_slist_remove_first (pgm_slist_t*) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL void pgm_slist_free (pgm_slist_t*); -PGM_GNUC_INTERNAL pgm_slist_t* pgm_slist_last (pgm_slist_t*) PGM_GNUC_PURE PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL unsigned pgm_slist_length (pgm_slist_t*) PGM_GNUC_PURE PGM_GNUC_WARN_UNUSED_RESULT; - -PGM_END_DECLS - -#endif /* __PGM_IMPL_SLIST_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/sn.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/sn.h deleted file mode 100644 index 667db0b..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/sn.h +++ /dev/null @@ -1,186 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * serial number arithmetic: rfc 1982 - * - * Copyright (c) 2006-2007 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_SN_H__ -#define __PGM_IMPL_SN_H__ - -#include -#include - -PGM_BEGIN_DECLS - -#define PGM_UINT32_SIGN_BIT (1UL<<31) -#define PGM_UINT64_SIGN_BIT (1ULL<<63) - -/* declare for GCC attributes */ -static inline bool pgm_uint32_lt (const uint32_t, const uint32_t) PGM_GNUC_CONST; -static inline bool pgm_uint32_lte (const uint32_t, const uint32_t) PGM_GNUC_CONST; -static inline bool pgm_uint32_gt (const uint32_t, const uint32_t) PGM_GNUC_CONST; -static inline bool pgm_uint32_gte (const uint32_t, const uint32_t) PGM_GNUC_CONST; -static inline bool pgm_uint64_lt (const uint64_t, const uint64_t) PGM_GNUC_CONST; -static inline bool pgm_uint64_lte (const uint64_t, const uint64_t) PGM_GNUC_CONST; -static inline bool pgm_uint64_gt (const uint64_t, const uint64_t) PGM_GNUC_CONST; -static inline bool pgm_uint64_gte (const uint64_t, const uint64_t) PGM_GNUC_CONST; - -/* 32 bit */ -static inline -bool pgm_uint32_lt ( - const uint32_t s, - const uint32_t t - ) -{ - pgm_assert (sizeof(int) >= 4); - return ( ((s) - (t)) & PGM_UINT32_SIGN_BIT ); -} - -static inline -bool -pgm_uint32_lte ( - const uint32_t s, - const uint32_t t - ) -{ - pgm_assert (sizeof(int) >= 4); - return ( ((s) == (t)) || ( ((s) - (t)) & PGM_UINT32_SIGN_BIT ) ); -} - -static inline -bool -pgm_uint32_gt ( - const uint32_t s, - const uint32_t t - ) -{ - pgm_assert (sizeof(int) >= 4); - return ( ((t) - (s)) & PGM_UINT32_SIGN_BIT ); -} - -static inline -bool -pgm_uint32_gte ( - const uint32_t s, - const uint32_t t - ) -{ - pgm_assert (sizeof(int) >= 4); - return ( ((s) == (t)) || ( ((t) - (s)) & PGM_UINT32_SIGN_BIT ) ); -} - -/* 64 bit */ -static inline -bool -pgm_uint64_lt ( - const uint64_t s, - const uint64_t t - ) -{ - if (sizeof(int) == 4) - { - return ( - ( ((s) - (t)) & PGM_UINT64_SIGN_BIT ) - > 0 /* need to force boolean conversion when int = 32bits */ - ); - } - else - { - pgm_assert (sizeof(int) >= 8); - return ( ((s) - (t)) & PGM_UINT64_SIGN_BIT ); - } -} - -static inline -bool -pgm_uint64_lte ( - const uint64_t s, - const uint64_t t - ) -{ - if (sizeof(int) == 4) - { - return ( - ( (s) == (t) ) - || - ( - ( ((s) - (t)) & PGM_UINT64_SIGN_BIT ) - > 0 /* need to force boolean conversion when int = 32bits */ - ) - ); - } - else - { - pgm_assert (sizeof(int) >= 8); - return ( ((s) == (t)) || ( ((s) - (t)) & PGM_UINT64_SIGN_BIT ) ); - } -} - -static inline -bool -pgm_uint64_gt ( - const uint64_t s, - const uint64_t t - ) -{ - if (sizeof(int) == 4) - { - return ( - ( ((t) - (s)) & PGM_UINT64_SIGN_BIT ) - > 0 /* need to force boolean conversion when int = 32bits */ - ); - } - else - { - pgm_assert (sizeof(int) >= 8); - return ( ((t) - (s)) & PGM_UINT64_SIGN_BIT ); - } -} - -static inline -bool -pgm_uint64_gte ( - const uint64_t s, - const uint64_t t - ) -{ - if (sizeof(int) == 4) - { - return ( - ( (s) == (t) ) - || - ( - ( ((t) - (s)) & PGM_UINT64_SIGN_BIT ) - > 0 /* need to force boolean conversion when int = 32bits */ - ) - ); - } - else - { - pgm_assert (sizeof(int) >= 8); - return ( ((s) == (t)) || ( ((t) - (s)) & PGM_UINT64_SIGN_BIT ) ); - } -} - -PGM_END_DECLS - -#endif /* __PGM_IMPL_SN_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/sockaddr.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/sockaddr.h deleted file mode 100644 index cea84c9..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/sockaddr.h +++ /dev/null @@ -1,105 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * struct sockaddr functions independent of in or in6. - * - * Copyright (c) 2006-2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_SOCKADDR_H__ -#define __PGM_IMPL_SOCKADDR_H__ - -#ifndef _WIN32 -# include -#endif -#include - -PGM_BEGIN_DECLS - -/* fallback values where not directly supported */ -#ifndef MSG_DONTWAIT -# define MSG_DONTWAIT 0 -#endif -#ifndef MSG_ERRQUEUE -# define MSG_ERRQUEUE 0x2000 -#endif -#if !defined(EAFNOSUPPORT) && defined(WSAEAFNOSUPPORT) -# define EAFNOSUPPORT WSAEAFNOSUPPORT -#endif - -#ifndef _WIN32 -# define PGM_INVALID_SOCKET -1 -# define PGM_SOCKET_ERROR -1 -# define pgm_closesocket close -# define pgm_sock_errno() (errno) -# define pgm_sock_strerror(e) strerror(e) -# define pgm_error_from_sock_errno pgm_error_from_errno -#else -# define PGM_INVALID_SOCKET INVALID_SOCKET -# define PGM_SOCKET_ERROR SOCKET_ERROR -# define pgm_closesocket closesocket -# define pgm_sock_errno() WSAGetLastError() -# define pgm_sock_strerror(e) pgm_wsastrerror(e) -# define pgm_error_from_sock_errno pgm_error_from_wsa_errno -#endif - -PGM_GNUC_INTERNAL sa_family_t pgm_sockaddr_family (const struct sockaddr* sa); -PGM_GNUC_INTERNAL uint16_t pgm_sockaddr_port (const struct sockaddr* sa); -PGM_GNUC_INTERNAL socklen_t pgm_sockaddr_len (const struct sockaddr* sa); -PGM_GNUC_INTERNAL socklen_t pgm_sockaddr_storage_len (const struct sockaddr_storage* ss); -PGM_GNUC_INTERNAL uint32_t pgm_sockaddr_scope_id (const struct sockaddr* sa); -PGM_GNUC_INTERNAL int pgm_sockaddr_ntop (const struct sockaddr*restrict sa, char*restrict dst, size_t ulen); -PGM_GNUC_INTERNAL int pgm_sockaddr_pton (const char*restrict src, struct sockaddr*restrict dst); -PGM_GNUC_INTERNAL int pgm_sockaddr_is_addr_multicast (const struct sockaddr* sa); -PGM_GNUC_INTERNAL int pgm_sockaddr_is_addr_unspecified (const struct sockaddr* sa); -PGM_GNUC_INTERNAL int pgm_sockaddr_cmp (const struct sockaddr*restrict sa1, const struct sockaddr*restrict sa2); -PGM_GNUC_INTERNAL int pgm_sockaddr_hdrincl (const int s, const sa_family_t sa_family, const bool v); -PGM_GNUC_INTERNAL int pgm_sockaddr_pktinfo (const int s, const sa_family_t sa_family, const bool v); -PGM_GNUC_INTERNAL int pgm_sockaddr_router_alert (const int s, const sa_family_t sa_family, const bool v); -PGM_GNUC_INTERNAL int pgm_sockaddr_tos (const int s, const sa_family_t sa_family, const int tos); -PGM_GNUC_INTERNAL int pgm_sockaddr_join_group (const int s, const sa_family_t sa_family, const struct group_req* gr); -PGM_GNUC_INTERNAL int pgm_sockaddr_leave_group (const int s, const sa_family_t sa_family, const struct group_req* gr); -PGM_GNUC_INTERNAL int pgm_sockaddr_block_source (const int s, const sa_family_t sa_family, const struct group_source_req* gsr); -PGM_GNUC_INTERNAL int pgm_sockaddr_unblock_source (const int s, const sa_family_t sa_family, const struct group_source_req* gsr); -PGM_GNUC_INTERNAL int pgm_sockaddr_join_source_group (const int s, const sa_family_t sa_family, const struct group_source_req* gsr); -PGM_GNUC_INTERNAL int pgm_sockaddr_leave_source_group (const int s, const sa_family_t sa_family, const struct group_source_req* gsr); -#if defined(MCAST_MSFILTER) || defined(SIOCSMSFILTER) -# ifndef GROUP_FILTER_SIZE -# define GROUP_FILTER_SIZE(numsrc) (sizeof (struct group_filter) \ - - sizeof (struct sockaddr_storage) \ - + ((numsrc) \ - * sizeof (struct sockaddr_storage))) -# endif -PGM_GNUC_INTERNAL int pgm_sockaddr_msfilter (const int s, const sa_family_t sa_family, const struct group_filter* gf_list); -#endif -PGM_GNUC_INTERNAL int pgm_sockaddr_multicast_if (int s, const struct sockaddr* address, unsigned ifindex); -PGM_GNUC_INTERNAL int pgm_sockaddr_multicast_loop (const int s, const sa_family_t sa_family, const bool v); -PGM_GNUC_INTERNAL int pgm_sockaddr_multicast_hops (const int s, const sa_family_t sa_family, const unsigned hops); -PGM_GNUC_INTERNAL void pgm_sockaddr_nonblocking (const int s, const bool v); - -PGM_GNUC_INTERNAL const char* pgm_inet_ntop (int af, const void*restrict src, char*restrict dst, socklen_t size); -PGM_GNUC_INTERNAL int pgm_inet_pton (int af, const char*restrict src, void*restrict dst); - -PGM_GNUC_INTERNAL int pgm_nla_to_sockaddr (const void*restrict nla, struct sockaddr*restrict sa); -PGM_GNUC_INTERNAL int pgm_sockaddr_to_nla (const struct sockaddr*restrict sa, void*restrict nla); - -PGM_END_DECLS - -#endif /* __PGM_IMPL_SOCKADDR_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/socket.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/socket.h deleted file mode 100644 index ee175d8..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/socket.h +++ /dev/null @@ -1,182 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * PGM socket. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_IMPL_SOCKET_H__ -#define __PGM_IMPL_SOCKET_H__ - -struct pgm_sock_t; - -#include -#include -#include - -PGM_BEGIN_DECLS - -#ifndef IP_MAX_MEMBERSHIPS -# define IP_MAX_MEMBERSHIPS 20 -#endif - -struct pgm_sock_t { - sa_family_t family; /* communications domain */ - int socket_type; - int protocol; - pgm_tsi_t tsi; - uint16_t dport; - uint16_t udp_encap_ucast_port; - uint16_t udp_encap_mcast_port; - uint32_t rand_node_id; /* node identifier */ - - pgm_rwlock_t lock; /* running / destroyed */ - pgm_mutex_t receiver_mutex; /* receiver API */ - pgm_mutex_t source_mutex; /* source API */ - pgm_spinlock_t txw_spinlock; /* transmit window */ - pgm_mutex_t send_mutex; /* non-router alert socket */ - pgm_mutex_t timer_mutex; /* next timer expiration */ - - bool is_bound; - bool is_connected; - bool is_destroyed; - bool is_reset; - bool is_abort_on_reset; - - bool can_send_data; /* and SPMs */ - bool can_send_nak; /* muted receiver */ - bool can_recv_data; /* send-only */ - bool is_edge_triggered_recv; - bool is_nonblocking; - - struct group_source_req send_gsr; /* multicast */ - struct sockaddr_storage send_addr; /* unicast nla */ - int send_sock; - int send_with_router_alert_sock; - struct group_source_req recv_gsr[IP_MAX_MEMBERSHIPS]; /* sa_family = 0 terminated */ - unsigned recv_gsr_len; - int recv_sock; - - size_t max_apdu; - uint16_t max_tpdu; - uint16_t max_tsdu; /* excluding optional var_pktlen word */ - uint16_t max_tsdu_fragment; - size_t iphdr_len; - bool use_multicast_loop; /* and reuseaddr for UDP encapsulation */ - unsigned hops; - unsigned txw_sqns, txw_secs; - unsigned rxw_sqns, rxw_secs; - ssize_t txw_max_rte, rxw_max_rte; - size_t sndbuf, rcvbuf; /* setsockopt (SO_SNDBUF/SO_RCVBUF) */ - - pgm_txw_t* restrict window; - pgm_rate_t rate_control; - pgm_time_t adv_ivl; /* advancing with data */ - unsigned adv_mode; /* 0 = time, 1 = data */ - bool is_controlled_spm; - bool is_controlled_odata; - bool is_controlled_rdata; - - bool use_cr; /* congestion reports */ - bool use_pgmcc; /* congestion control */ - bool is_pending_crqst; - unsigned ack_c; /* constant C */ - unsigned ack_c_p; /* constant Cᵨ */ - uint32_t ssthresh; /* slow-start threshold */ - uint32_t tokens; - uint32_t cwnd_size; /* congestion window size */ - uint32_t ack_rx_max; - uint32_t ack_bitmap; - uint32_t acks_after_loss; - uint32_t suspended_sqn; - bool is_congested; - pgm_time_t ack_expiry; - pgm_time_t ack_expiry_ivl; - pgm_time_t next_crqst; - pgm_time_t crqst_ivl; - pgm_time_t ack_bo_ivl; - struct sockaddr_storage acker_nla; - uint64_t acker_loss; - - pgm_notify_t ack_notify; - pgm_notify_t rdata_notify; - - pgm_hash_t last_hash_key; - void* restrict last_hash_value; - unsigned last_commit; - size_t blocklen; /* length of buffer blocked */ - bool is_apdu_eagain; /* writer-lock on window_lock exists as send would block */ - bool is_spm_eagain; /* writer-lock in receiver */ - - struct { - size_t data_pkt_offset; - size_t data_bytes_offset; - uint32_t first_sqn; - struct pgm_sk_buff_t* skb; /* references external buffer */ - size_t tsdu_length; - uint32_t unfolded_odata; - size_t apdu_length; - unsigned vector_index; - size_t vector_offset; - bool is_rate_limited; - } pkt_dontwait_state; - - uint32_t spm_sqn; - unsigned spm_ambient_interval; /* microseconds */ - unsigned* restrict spm_heartbeat_interval; /* zero terminated, zero lead-pad */ - unsigned spm_heartbeat_state; /* indexof spm_heartbeat_interval */ - unsigned spm_heartbeat_len; - unsigned peer_expiry; /* from absence of SPMs */ - unsigned spmr_expiry; /* waiting for peer SPMRs */ - - pgm_rand_t rand_; /* for calculating nak_rb_ivl from nak_bo_ivl */ - unsigned nak_data_retries, nak_ncf_retries; - pgm_time_t nak_bo_ivl, nak_rpt_ivl, nak_rdata_ivl; - pgm_time_t next_heartbeat_spm, next_ambient_spm; - - bool use_proactive_parity; - bool use_ondemand_parity; - bool use_var_pktlen; - uint8_t rs_n; - uint8_t rs_k; - uint8_t rs_proactive_h; /* 0 <= proactive-h <= ( n - k ) */ - uint8_t tg_sqn_shift; - struct pgm_sk_buff_t* restrict rx_buffer; - - pgm_rwlock_t peers_lock; - pgm_hashtable_t* restrict peers_hashtable; /* fast lookup */ - pgm_list_t* restrict peers_list; /* easy iteration */ - pgm_slist_t* restrict peers_pending; /* rxw: have or lost data */ - pgm_notify_t pending_notify; /* timer to rx */ - bool is_pending_read; - pgm_time_t next_poll; - - uint32_t cumulative_stats[PGM_PC_SOURCE_MAX]; - uint32_t snap_stats[PGM_PC_SOURCE_MAX]; - pgm_time_t snap_time; -}; - - -/* global variables */ -extern pgm_rwlock_t pgm_sock_list_lock; -extern pgm_slist_t* pgm_sock_list; - -size_t pgm_pkt_offset (bool, sa_family_t); - -PGM_END_DECLS - -#endif /* __PGM_IMPL_SOCKET_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/source.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/source.h deleted file mode 100644 index dae417a..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/source.h +++ /dev/null @@ -1,75 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * PGM source socket. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_IMPL_SOURCE_H__ -#define __PGM_IMPL_SOURCE_H__ - -#include -#include - -PGM_BEGIN_DECLS - -/* Performance Counters */ -enum { - PGM_PC_SOURCE_DATA_BYTES_SENT, - PGM_PC_SOURCE_DATA_MSGS_SENT, /* msgs = packets not APDUs */ -/* PGM_PC_SOURCE_BYTES_BUFFERED, */ /* tx window contents in bytes */ -/* PGM_PC_SOURCE_MSGS_BUFFERED, */ - PGM_PC_SOURCE_BYTES_SENT, -/* PGM_PC_SOURCE_RAW_NAKS_RECEIVED, */ - PGM_PC_SOURCE_CKSUM_ERRORS, - PGM_PC_SOURCE_MALFORMED_NAKS, - PGM_PC_SOURCE_PACKETS_DISCARDED, - PGM_PC_SOURCE_PARITY_BYTES_RETRANSMITTED, - PGM_PC_SOURCE_SELECTIVE_BYTES_RETRANSMITTED, - PGM_PC_SOURCE_PARITY_MSGS_RETRANSMITTED, - PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED, - PGM_PC_SOURCE_PARITY_NAK_PACKETS_RECEIVED, - PGM_PC_SOURCE_SELECTIVE_NAK_PACKETS_RECEIVED, /* total packets */ - PGM_PC_SOURCE_PARITY_NAKS_RECEIVED, - PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED, /* serial numbers */ - PGM_PC_SOURCE_PARITY_NAKS_IGNORED, - PGM_PC_SOURCE_SELECTIVE_NAKS_IGNORED, - PGM_PC_SOURCE_ACK_ERRORS, -/* PGM_PC_SOURCE_PGMCC_ACKER, */ - PGM_PC_SOURCE_TRANSMISSION_CURRENT_RATE, - PGM_PC_SOURCE_ACK_PACKETS_RECEIVED, - PGM_PC_SOURCE_PARITY_NNAK_PACKETS_RECEIVED, - PGM_PC_SOURCE_SELECTIVE_NNAK_PACKETS_RECEIVED, - PGM_PC_SOURCE_PARITY_NNAKS_RECEIVED, - PGM_PC_SOURCE_SELECTIVE_NNAKS_RECEIVED, - PGM_PC_SOURCE_NNAK_ERRORS, - -/* marker */ - PGM_PC_SOURCE_MAX -}; - -PGM_GNUC_INTERNAL bool pgm_send_spm (pgm_sock_t*, int) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL bool pgm_on_deferred_nak (pgm_sock_t*const); -PGM_GNUC_INTERNAL bool pgm_on_spmr (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL bool pgm_on_nak (pgm_sock_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL bool pgm_on_nnak (pgm_sock_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL bool pgm_on_ack (pgm_sock_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; - -PGM_END_DECLS - -#endif /* __PGM_IMPL_SOURCE_H__ */ - diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/sqn_list.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/sqn_list.h deleted file mode 100644 index 4d216ae..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/sqn_list.h +++ /dev/null @@ -1,38 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * PGM sequence list. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_IMPL_SQN_LIST_H__ -#define __PGM_IMPL_SQN_LIST_H__ - -struct pgm_sqn_list_t; - -#include - -PGM_BEGIN_DECLS - -struct pgm_sqn_list_t { - uint8_t len; - uint32_t sqn[63]; /* list of sequence numbers */ -}; - -PGM_END_DECLS - -#endif /* __PGM_IMPL_SQN_LIST_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/string.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/string.h deleted file mode 100644 index 8357c2a..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/string.h +++ /dev/null @@ -1,59 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable string manipulation functions. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_STRING_H__ -#define __PGM_IMPL_STRING_H__ - -typedef struct pgm_string_t pgm_string_t; - -#include -#include - -PGM_BEGIN_DECLS - -struct pgm_string_t { - char* str; - size_t len; - size_t allocated_len; -}; - -PGM_GNUC_INTERNAL char* pgm_strdup (const char*) PGM_GNUC_MALLOC; -PGM_GNUC_INTERNAL int pgm_printf_string_upper_bound (const char*, va_list) PGM_GNUC_PRINTF(1, 0); -PGM_GNUC_INTERNAL int pgm_vasprintf (char**restrict, char const*restrict, va_list args) PGM_GNUC_PRINTF(2, 0); -PGM_GNUC_INTERNAL char* pgm_strdup_vprintf (const char*, va_list) PGM_GNUC_PRINTF(1, 0) PGM_GNUC_MALLOC; -PGM_GNUC_INTERNAL char* pgm_strconcat (const char*, ...) PGM_GNUC_MALLOC PGM_GNUC_NULL_TERMINATED; -PGM_GNUC_INTERNAL char** pgm_strsplit (const char*restrict, const char*restrict, int) PGM_GNUC_MALLOC; -PGM_GNUC_INTERNAL void pgm_strfreev (char**); - -PGM_GNUC_INTERNAL pgm_string_t* pgm_string_new (const char*); -PGM_GNUC_INTERNAL char* pgm_string_free (pgm_string_t*, bool); -PGM_GNUC_INTERNAL void pgm_string_printf (pgm_string_t*restrict, const char*restrict, ...) PGM_GNUC_PRINTF(2, 3); -PGM_GNUC_INTERNAL pgm_string_t* pgm_string_append (pgm_string_t*restrict, const char*restrict); -PGM_GNUC_INTERNAL pgm_string_t* pgm_string_append_c (pgm_string_t*, char); -PGM_GNUC_INTERNAL void pgm_string_append_printf (pgm_string_t*restrict, const char*restrict, ...) PGM_GNUC_PRINTF(2, 3); - -PGM_END_DECLS - -#endif /* __PGM_IMPL_STRING_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/thread.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/thread.h deleted file mode 100644 index e1484d2..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/thread.h +++ /dev/null @@ -1,210 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * mutexes and locks. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_THREAD_H__ -#define __PGM_IMPL_THREAD_H__ - -typedef struct pgm_mutex_t pgm_mutex_t; -typedef struct pgm_spinlock_t pgm_spinlock_t; -typedef struct pgm_cond_t pgm_cond_t; -typedef struct pgm_rwlock_t pgm_rwlock_t; - -#ifndef _WIN32 -# include -# include -#else -# define WIN32_LEAN_AND_MEAN -# include -#endif -#include - -PGM_BEGIN_DECLS - -struct pgm_mutex_t { -#ifndef _WIN32 - pthread_mutex_t pthread_mutex; -#else - HANDLE win32_mutex; -#endif /* !_WIN32 */ -}; - -struct pgm_spinlock_t { -#ifndef _WIN32 - pthread_spinlock_t pthread_spinlock; -#else - CRITICAL_SECTION win32_spinlock; -#endif -}; - -struct pgm_cond_t { -#ifndef _WIN32 - pthread_cond_t pthread_cond; -#elif defined(CONFIG_HAVE_WIN_COND) - CONDITION_VARIABLE win32_cond; -#else - CRITICAL_SECTION win32_spinlock; - size_t len; - size_t allocated_len; - HANDLE* phandle; -#endif /* !_WIN32 */ -}; - -struct pgm_rwlock_t { -#ifndef _WIN32 - pthread_rwlock_t pthread_rwlock; -#elif CONFIG_HAVE_WIN_SRW_LOCK - SRWLOCK win32_lock; - pthread_rwlock_t pthread_rwlock; -#else - CRITICAL_SECTION win32_spinlock; - pgm_cond_t read_cond; - pgm_cond_t write_cond; - unsigned read_counter; - bool have_writer; - unsigned want_to_read; - unsigned want_to_write; -#endif /* !CONFIG_HAVE_WIN_SRW_LOCK */ -}; - -PGM_GNUC_INTERNAL void pgm_mutex_init (pgm_mutex_t*); -PGM_GNUC_INTERNAL void pgm_mutex_free (pgm_mutex_t*); -PGM_GNUC_INTERNAL bool pgm_mutex_trylock (pgm_mutex_t*); - -static inline void pgm_mutex_lock (pgm_mutex_t* mutex) { -#ifndef _WIN32 - pthread_mutex_lock (&mutex->pthread_mutex); -#else - WaitForSingleObject (mutex->win32_mutex, INFINITE); -#endif /* !_WIN32 */ -} - -static inline void pgm_mutex_unlock (pgm_mutex_t* mutex) { -#ifndef _WIN32 - pthread_mutex_unlock (&mutex->pthread_mutex); -#else - ReleaseMutex (mutex->win32_mutex); -#endif /* !_WIN32 */ -} - -PGM_GNUC_INTERNAL void pgm_spinlock_init (pgm_spinlock_t*); -PGM_GNUC_INTERNAL void pgm_spinlock_free (pgm_spinlock_t*); -PGM_GNUC_INTERNAL bool pgm_spinlock_trylock (pgm_spinlock_t*); - -static inline void pgm_spinlock_lock (pgm_spinlock_t* spinlock) { -#ifndef _WIN32 - pthread_spin_lock (&spinlock->pthread_spinlock); -#else - EnterCriticalSection (&spinlock->win32_spinlock); -#endif /* !_WIN32 */ -} - -static inline void pgm_spinlock_unlock (pgm_spinlock_t* spinlock) { -#ifndef _WIN32 - pthread_spin_unlock (&spinlock->pthread_spinlock); -#else - LeaveCriticalSection (&spinlock->win32_spinlock); -#endif /* !_WIN32 */ -} - -PGM_GNUC_INTERNAL void pgm_cond_init (pgm_cond_t*); -PGM_GNUC_INTERNAL void pgm_cond_signal (pgm_cond_t*); -PGM_GNUC_INTERNAL void pgm_cond_broadcast (pgm_cond_t*); -#ifndef _WIN32 -PGM_GNUC_INTERNAL void pgm_cond_wait (pgm_cond_t*, pthread_mutex_t*); -#else -PGM_GNUC_INTERNAL void pgm_cond_wait (pgm_cond_t*, CRITICAL_SECTION*); -#endif -PGM_GNUC_INTERNAL void pgm_cond_free (pgm_cond_t*); - -#ifndef _WIN32 -static inline void pgm_rwlock_reader_lock (pgm_rwlock_t* rwlock) { - pthread_rwlock_rdlock (&rwlock->pthread_rwlock); -} -static inline bool pgm_rwlock_reader_trylock (pgm_rwlock_t* rwlock) { - return !pthread_rwlock_tryrdlock (&rwlock->pthread_rwlock); -} -static inline void pgm_rwlock_reader_unlock(pgm_rwlock_t* rwlock) { - pthread_rwlock_unlock (&rwlock->pthread_rwlock); -} -static inline void pgm_rwlock_writer_lock (pgm_rwlock_t* rwlock) { - pthread_rwlock_wrlock (&rwlock->pthread_rwlock); -} -static inline bool pgm_rwlock_writer_trylock (pgm_rwlock_t* rwlock) { - return !pthread_rwlock_trywrlock (&rwlock->pthread_rwlock); -} -static inline void pgm_rwlock_writer_unlock (pgm_rwlock_t* rwlock) { - pthread_rwlock_unlock (&rwlock->pthread_rwlock); -} -#elif defined(CONFIG_HAVE_WIN_SRW_LOCK) -static inline void pgm_rwlock_reader_lock (pgm_rwlock_t* rwlock) { - AcquireSRWLockShared (&rwlock->win32_lock); -} -static inline bool pgm_rwlock_reader_trylock (pgm_rwlock_t* rwlock) { - return TryAcquireSRWLockShared (&rwlock->win32_lock); -} -static inline void pgm_rwlock_reader_unlock(pgm_rwlock_t* rwlock) { - ReleaseSRWLockShared (&rwlock->win32_lock); -} -static inline void pgm_rwlock_writer_lock (pgm_rwlock_t* rwlock) { - AcquireSRWLockExclusive (&rwlock->win32_lock); -} -static inline bool pgm_rwlock_writer_trylock (pgm_rwlock_t* rwlock) { - return AcquireSRWLockExclusive (&rwlock->win32_lock); -} -static inline void pgm_rwlock_writer_unlock (pgm_rwlock_t* rwlock) { - ReleaseSRWLockExclusive (&rwlock->win32_lock); -} -#else -PGM_GNUC_INTERNAL void pgm_rwlock_init (pgm_rwlock_t*); -PGM_GNUC_INTERNAL void pgm_rwlock_free (pgm_rwlock_t*); -PGM_GNUC_INTERNAL void pgm_rwlock_reader_lock (pgm_rwlock_t*); -PGM_GNUC_INTERNAL bool pgm_rwlock_reader_trylock (pgm_rwlock_t*); -PGM_GNUC_INTERNAL void pgm_rwlock_reader_unlock(pgm_rwlock_t*); -PGM_GNUC_INTERNAL void pgm_rwlock_writer_lock (pgm_rwlock_t*); -PGM_GNUC_INTERNAL bool pgm_rwlock_writer_trylock (pgm_rwlock_t*); -PGM_GNUC_INTERNAL void pgm_rwlock_writer_unlock (pgm_rwlock_t*); -#endif - -PGM_GNUC_INTERNAL void pgm_thread_init (void); -PGM_GNUC_INTERNAL void pgm_thread_shutdown (void); - -static inline -void -pgm_thread_yield (void) -{ -#ifndef _WIN32 -# ifdef _POSIX_PRIORITY_SCHEDULING - sched_yield(); -# else - pthread_yield(); /* requires _GNU */ -# endif -#else - Sleep (0); /* If you specify 0 milliseconds, the thread will relinquish - * the remainder of its time slice but remain ready. - */ -#endif /* _WIN32 */ -} - -PGM_END_DECLS - -#endif /* __PGM_IMPL_THREAD_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/time.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/time.h deleted file mode 100644 index 70c0d37..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/time.h +++ /dev/null @@ -1,51 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * high resolution timers. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_TIME_H__ -#define __PGM_IMPL_TIME_H__ - -#include -#include -#include - -PGM_BEGIN_DECLS - -typedef pgm_time_t (*pgm_time_update_func)(void); - -#define pgm_time_after(a,b) ( (a) > (b) ) -#define pgm_time_before(a,b) ( pgm_time_after((b),(a)) ) - -#define pgm_time_after_eq(a,b) ( (a) >= (b) ) -#define pgm_time_before_eq(a,b) ( pgm_time_after_eq((b),(a)) ) - -extern pgm_time_update_func pgm_time_update_now; - -PGM_GNUC_INTERNAL bool pgm_time_init (pgm_error_t**) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL bool pgm_time_shutdown (void); - -PGM_END_DECLS - -#endif /* __PGM_IMPL_TIME_H__ */ - diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/timer.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/timer.h deleted file mode 100644 index 4f900e4..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/timer.h +++ /dev/null @@ -1,58 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * PGM timer thread. - * - * Copyright (c) 2006-2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_IMPL_TIMER_H__ -#define __PGM_IMPL_TIMER_H__ - -#include -#include - -PGM_BEGIN_DECLS - -PGM_GNUC_INTERNAL bool pgm_timer_prepare (pgm_sock_t*const); -PGM_GNUC_INTERNAL bool pgm_timer_check (pgm_sock_t*const); -PGM_GNUC_INTERNAL pgm_time_t pgm_timer_expiration (pgm_sock_t*const); -PGM_GNUC_INTERNAL bool pgm_timer_dispatch (pgm_sock_t*const); - -static inline -void -pgm_timer_lock ( - pgm_sock_t* const sock - ) -{ - if (sock->can_send_data) - pgm_mutex_lock (&sock->timer_mutex); -} - -static inline -void -pgm_timer_unlock ( - pgm_sock_t* const sock - ) -{ - if (sock->can_send_data) - pgm_mutex_unlock (&sock->timer_mutex); -} - -PGM_END_DECLS - -#endif /* __PGM_IMPL_TIMER_H__ */ - diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/tsi.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/tsi.h deleted file mode 100644 index be93e62..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/tsi.h +++ /dev/null @@ -1,39 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * transport session ID helper functions - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_TSI_H__ -#define __PGM_IMPL_TSI_H__ - -#include -#include -#include - -PGM_BEGIN_DECLS - -PGM_GNUC_INTERNAL pgm_hash_t pgm_tsi_hash (const void*) PGM_GNUC_WARN_UNUSED_RESULT; - -PGM_END_DECLS - -#endif /* __PGM_IMPL_TSI_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/txw.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/txw.h deleted file mode 100644 index cf33eb5..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/txw.h +++ /dev/null @@ -1,204 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * basic transmit window. - * - * Copyright (c) 2006 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_IMPL_TXW_H__ -#define __PGM_IMPL_TXW_H__ - -typedef struct pgm_txw_state_t pgm_txw_state_t; -typedef struct pgm_txw_t pgm_txw_t; - -#include - -PGM_BEGIN_DECLS - -/* must be smaller than PGM skbuff control buffer */ -struct pgm_txw_state_t { - uint32_t unfolded_checksum; /* first 32-bit word must be checksum */ - - unsigned waiting_retransmit:1; /* in retransmit queue */ - unsigned retransmit_count:15; - unsigned nak_elimination_count:16; - - uint8_t pkt_cnt_requested; /* # parity packets to send */ - uint8_t pkt_cnt_sent; /* # parity packets already sent */ -}; - -struct pgm_txw_t { - const pgm_tsi_t* restrict tsi; - -/* option: lockless atomics */ - volatile uint32_t lead; - volatile uint32_t trail; - - pgm_queue_t retransmit_queue; - - pgm_rs_t rs; - uint8_t tg_sqn_shift; - struct pgm_sk_buff_t* restrict parity_buffer; - -/* Advance with data */ - pgm_time_t adv_ivl_expiry; - unsigned increment_window_naks; - unsigned adv_secs; /* TXW_ADV_SECS */ - unsigned adv_sqns; /* TXW_ADV_SECS in sequences */ - - unsigned is_fec_enabled:1; - unsigned adv_mode:1; /* 0 = advance by time, 1 = advance by data */ - - size_t size; /* window content size in bytes */ - unsigned alloc; /* length of pdata[] */ -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) - struct pgm_sk_buff_t* pdata[]; -#elif defined(__cplusplus) - struct pgm_sk_buff_t* pdata[1]; -#else - struct pgm_sk_buff_t* pdata[0]; -#endif -}; - -PGM_GNUC_INTERNAL pgm_txw_t* pgm_txw_create (const pgm_tsi_t*const, const uint16_t, const uint32_t, const unsigned, const ssize_t, const bool, const uint8_t, const uint8_t) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL void pgm_txw_shutdown (pgm_txw_t*const); -PGM_GNUC_INTERNAL void pgm_txw_add (pgm_txw_t*const restrict, struct pgm_sk_buff_t*const restrict); -PGM_GNUC_INTERNAL struct pgm_sk_buff_t* pgm_txw_peek (const pgm_txw_t*const, const uint32_t) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL bool pgm_txw_retransmit_push (pgm_txw_t*const, const uint32_t, const bool, const uint8_t) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL struct pgm_sk_buff_t* pgm_txw_retransmit_try_peek (pgm_txw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; -PGM_GNUC_INTERNAL void pgm_txw_retransmit_remove_head (pgm_txw_t*const); -PGM_GNUC_INTERNAL uint32_t pgm_txw_get_unfolded_checksum (const struct pgm_sk_buff_t*const) PGM_GNUC_PURE; -PGM_GNUC_INTERNAL void pgm_txw_set_unfolded_checksum (struct pgm_sk_buff_t*const, const uint32_t); -PGM_GNUC_INTERNAL void pgm_txw_inc_retransmit_count (struct pgm_sk_buff_t*const); -PGM_GNUC_INTERNAL bool pgm_txw_retransmit_is_empty (const pgm_txw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; - -/* declare for GCC attributes */ -static inline size_t pgm_txw_max_length (const pgm_txw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; -static inline uint32_t pgm_txw_length (const pgm_txw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; -static inline size_t pgm_txw_size (const pgm_txw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; -static inline bool pgm_txw_is_empty (const pgm_txw_t* const) PGM_GNUC_WARN_UNUSED_RESULT; -static inline bool pgm_txw_is_full (const pgm_txw_t* const) PGM_GNUC_WARN_UNUSED_RESULT; -static inline uint32_t pgm_txw_lead (const pgm_txw_t* const) PGM_GNUC_WARN_UNUSED_RESULT; -static inline uint32_t pgm_txw_lead_atomic (const pgm_txw_t* const) PGM_GNUC_WARN_UNUSED_RESULT; -static inline uint32_t pgm_txw_next_lead (const pgm_txw_t* const) PGM_GNUC_WARN_UNUSED_RESULT; -static inline uint32_t pgm_txw_trail (const pgm_txw_t* const) PGM_GNUC_WARN_UNUSED_RESULT; -static inline uint32_t pgm_txw_trail_atomic (const pgm_txw_t* const) PGM_GNUC_WARN_UNUSED_RESULT; - -static inline -size_t -pgm_txw_max_length ( - const pgm_txw_t*const window - ) -{ - pgm_assert (NULL != window); - return window->alloc; -} - -static inline -uint32_t -pgm_txw_length ( - const pgm_txw_t*const window - ) -{ - pgm_assert (NULL != window); - return ( 1 + window->lead ) - window->trail; -} - -static inline -size_t -pgm_txw_size ( - const pgm_txw_t*const window - ) -{ - pgm_assert (NULL != window); - return window->size; -} - -static inline -bool -pgm_txw_is_empty ( - const pgm_txw_t*const window - ) -{ - pgm_assert (NULL != window); - return (0 == pgm_txw_length (window)); -} - -static inline -bool -pgm_txw_is_full ( - const pgm_txw_t*const window - ) -{ - pgm_assert (NULL != window); - return (pgm_txw_length (window) == pgm_txw_max_length (window)); -} - -static inline -uint32_t -pgm_txw_lead ( - const pgm_txw_t*const window - ) -{ - pgm_assert (NULL != window); - return window->lead; -} - -/* atomics may rely on global variables and so cannot be defined __pure__ */ -static inline -uint32_t -pgm_txw_lead_atomic ( - const pgm_txw_t*const window - ) -{ - pgm_assert (NULL != window); - return pgm_atomic_read32 (&window->lead); -} - -static inline -uint32_t -pgm_txw_next_lead ( - const pgm_txw_t*const window - ) -{ - pgm_assert (NULL != window); - return (uint32_t)(pgm_txw_lead (window) + 1); -} - -static inline -uint32_t -pgm_txw_trail ( - const pgm_txw_t*const window - ) -{ - pgm_assert (NULL != window); - return window->trail; -} - -static inline -uint32_t -pgm_txw_trail_atomic ( - const pgm_txw_t*const window - ) -{ - pgm_assert (NULL != window); - return pgm_atomic_read32 (&window->trail); -} - -PGM_END_DECLS - -#endif /* __PGM_IMPL_TXW_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/impl/wsastrerror.h b/3rdparty/openpgm-svn-r1085/pgm/include/impl/wsastrerror.h deleted file mode 100644 index 1be4ef2..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/impl/wsastrerror.h +++ /dev/null @@ -1,37 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Winsock Error strings. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) -# error "Only can be included directly." -#endif - -#ifndef __PGM_IMPL_WSASTRERROR_H__ -#define __PGM_IMPL_WSASTRERROR_H__ - -#include - -PGM_BEGIN_DECLS - -char* pgm_wsastrerror (const int); -char* pgm_adapter_strerror (const int); -char* pgm_win_strerror (char*, size_t, const int); - -PGM_END_DECLS - -#endif /* __PGM_IMPL_WSASTRERROR_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/atomic.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/atomic.h deleted file mode 100644 index 15a5272..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/atomic.h +++ /dev/null @@ -1,140 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * 32-bit atomic operations. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_ATOMIC_H__ -#define __PGM_ATOMIC_H__ - -#ifdef sun -# include -#endif -#include - -static inline -uint32_t -pgm_atomic_exchange_and_add32 ( - volatile uint32_t* atomic, - const uint32_t val - ) -{ -#if defined( __GNUC__ ) && ( defined( __i386__ ) || defined( __x86_64__ ) ) - uint32_t result; - asm volatile ( "lock\n\t" - "xaddl %0, %1" - : "=r" (result), "=m" (*atomic) - : "0" (val), "m" (*atomic) - : "memory", "cc" ); - return result; -#elif defined( __SUNPRO_C ) && (defined( __i386 ) || defined( __amd64 )) - uint32_t result = val; - asm volatile ( "lock\n\t" - "xaddl %0, %1" - :: "r" (result), "m" (*atomic) ); - return result; -#elif defined( sun ) - const uint32_t nv = atomic_add_32_nv (atomic, (int32_t)val); - return nv - val; -#elif defined( __GNUC__ ) && ( __GNUC__ * 100 + __GNUC_MINOR__ >= 401 ) - return __sync_fetch_and_add (atomic, val); -#elif defined( _WIN32 ) - return InterlockedExchangeAdd ((volatile LONG*)atomic, val); -#else -# error "No supported atomic operations for this platform." -#endif -} - -static inline -void -pgm_atomic_add32 ( - volatile uint32_t* atomic, - const uint32_t val - ) -{ -#if defined( __GNUC__ ) && ( defined( __i386__ ) || defined( __x86_64__ ) ) - asm volatile ( "lock\n\t" - "addl %1, %0" - : "=m" (*atomic) - : "ir" (val), "m" (*atomic) - : "memory", "cc" ); -#elif defined( __SUNPRO_C ) && (defined( __i386 ) || defined( __amd64 )) - asm volatile ( "lock\n\t" - "addl %1, %0" - :: "r" (val), "m" (*atomic) ); -#elif defined( sun ) - atomic_add_32 (atomic, (int32_t)val); -#elif defined( __GNUC__ ) && ( __GNUC__ * 100 + __GNUC_MINOR__ >= 401 ) - __sync_fetch_and_add (atomic, val); -#elif defined( _WIN32 ) - InterlockedExchangeAdd ((volatile LONG*)atomic, val); -#endif -} - -static inline -void -pgm_atomic_inc32 ( - volatile uint32_t* atomic - ) -{ -#if (defined( __GNUC__ ) && (defined( __i386__ ) || defined( __x86_64__ ))) || (defined( __SUNPRO_C ) && (defined( __i386 ) || defined( __amd64 ))) - pgm_atomic_add32 (atomic, 1); -#elif defined( sun ) - atomic_inc_32 (atomic); -#elif defined( __GNUC__ ) && ( __GNUC__ * 100 + __GNUC_MINOR__ >= 401 ) - pgm_atomic_add32 (atomic, 1); -#elif defined( _WIN32 ) - InterlockedIncrement ((volatile LONG*)atomic); -#endif -} - -static inline -void -pgm_atomic_dec32 ( - volatile uint32_t* atomic - ) -{ -#if (defined( __GNUC__ ) && (defined( __i386__ ) || defined( __x86_64__ ))) || (defined( __SUNPRO_C ) && (defined( __i386 ) || defined( __amd64 ))) - pgm_atomic_add32 (atomic, (uint32_t)-1); -#elif defined( sun ) - atomic_dec_32 (atomic); -#elif defined( __GNUC__ ) && ( __GNUC__ * 100 + __GNUC_MINOR__ >= 401 ) - pgm_atomic_add32 (atomic, (uint32_t)-1); -#elif defined( _WIN32 ) - InterlockedDecrement ((volatile LONG*)atomic); -#endif -} - -static inline -uint32_t -pgm_atomic_read32 ( - const volatile uint32_t* atomic - ) -{ - return *atomic; -} - -static inline -void -pgm_atomic_write32 ( - volatile uint32_t* atomic, - const uint32_t val - ) -{ - *atomic = val; -} - -#endif /* __PGM_ATOMIC_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/backtrace.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/backtrace.h deleted file mode 100644 index 24f8469..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/backtrace.h +++ /dev/null @@ -1,33 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * Dump back trace to stderr and try gdb. - * - * Copyright (c) 2006 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_BACKTRACE_H__ -#define __PGM_BACKTRACE_H__ - -#include - -PGM_BEGIN_DECLS - -PGM_GNUC_NORETURN void on_sigsegv (int); - -PGM_END_DECLS - -#endif /* __PGM_BACKTRACE_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/engine.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/engine.h deleted file mode 100644 index 43115e8..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/engine.h +++ /dev/null @@ -1,37 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * PGM engine. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_ENGINE_H__ -#define __PGM_ENGINE_H__ - -#include -#include - -PGM_BEGIN_DECLS - -bool pgm_init (pgm_error_t**); -bool pgm_supported (void) PGM_GNUC_WARN_UNUSED_RESULT PGM_GNUC_PURE; -bool pgm_shutdown (void); -void pgm_drop_superuser (void); - -PGM_END_DECLS - -#endif /* __PGM_ENGINE_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/error.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/error.h deleted file mode 100644 index c87755c..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/error.h +++ /dev/null @@ -1,109 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable error reporting. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_ERROR_H__ -#define __PGM_ERROR_H__ - -typedef struct pgm_error_t pgm_error_t; - -#include - -PGM_BEGIN_DECLS - -/* error domains */ -enum -{ - PGM_ERROR_DOMAIN_IF, /* interface and host */ - PGM_ERROR_DOMAIN_PACKET, - PGM_ERROR_DOMAIN_RECV, - PGM_ERROR_DOMAIN_TIME, - PGM_ERROR_DOMAIN_SOCKET, - PGM_ERROR_DOMAIN_ENGINE, - PGM_ERROR_DOMAIN_HTTP, - PGM_ERROR_DOMAIN_SNMP -}; - -/* error codes */ -enum -{ - /* Derived from errno, eai_errno, etc */ - PGM_ERROR_ADDRFAMILY, /* EAI_ADDRFAMILY */ - PGM_ERROR_AFNOSUPPORT, /* EAI_FAMILY */ - PGM_ERROR_AGAIN, - PGM_ERROR_BADE, /* ERROR_INVALID_DATA */ - PGM_ERROR_BADF, - PGM_ERROR_BOUNDS, /* sequence out-of-bounds */ - PGM_ERROR_CKSUM, /* pkt cksum incorrect */ - PGM_ERROR_CONNRESET, - PGM_ERROR_FAIL, /* EAI_FAIL */ - PGM_ERROR_FAULT, - PGM_ERROR_INPROGRESS, /* WSAEINPROGRESS */ - PGM_ERROR_INTR, - PGM_ERROR_INVAL, - PGM_ERROR_MFILE, - PGM_ERROR_NFILE, - PGM_ERROR_NOBUFS, /* ERROR_BUFFER_OVERFLOW */ - PGM_ERROR_NODATA, /* EAI_NODATA */ - PGM_ERROR_NODEV, - PGM_ERROR_NOENT, - PGM_ERROR_NOMEM, - PGM_ERROR_NONAME, /* EAI_NONAME */ - PGM_ERROR_NONET, - PGM_ERROR_NOPROTOOPT, - PGM_ERROR_NOSYS, /* ERROR_NOT_SUPPORTED */ - PGM_ERROR_NOTUNIQ, - PGM_ERROR_NXIO, - PGM_ERROR_PERM, - PGM_ERROR_PROCLIM, /* WSAEPROCLIM */ - PGM_ERROR_PROTO, - PGM_ERROR_RANGE, - PGM_ERROR_SERVICE, /* EAI_SERVICE */ - PGM_ERROR_SOCKTNOSUPPORT, /* EAI_SOCKTYPE */ - PGM_ERROR_SYSNOTAREADY, /* WSASYSNOTAREADY */ - PGM_ERROR_SYSTEM, /* EAI_SYSTEM */ - PGM_ERROR_VERNOTSUPPORTED, /* WSAVERNOTSUPPORTED */ - PGM_ERROR_XDEV, - - PGM_ERROR_FAILED /* generic error */ -}; - -struct pgm_error_t -{ - int domain; - int code; - char* message; -}; - -void pgm_error_free (pgm_error_t*); -void pgm_set_error (pgm_error_t**restrict, int, int, const char*restrict, ...) PGM_GNUC_PRINTF (4, 5); -void pgm_propagate_error (pgm_error_t**restrict, pgm_error_t*restrict); -void pgm_clear_error (pgm_error_t**); -void pgm_prefix_error (pgm_error_t**restrict, const char*restrict, ...) PGM_GNUC_PRINTF (2, 3); - -int pgm_error_from_errno (const int) PGM_GNUC_CONST; -int pgm_error_from_h_errno (const int) PGM_GNUC_CONST; -int pgm_error_from_eai_errno (const int, const int) PGM_GNUC_CONST; -int pgm_error_from_wsa_errno (const int) PGM_GNUC_CONST; -int pgm_error_from_win_errno (const int) PGM_GNUC_CONST; - -PGM_END_DECLS - -#endif /* __PGM_ERROR_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/gsi.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/gsi.h deleted file mode 100644 index 938214a..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/gsi.h +++ /dev/null @@ -1,49 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * global session ID helper functions - * - * Copyright (c) 2006-2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_GSI_H__ -#define __PGM_GSI_H__ - -typedef struct pgm_gsi_t pgm_gsi_t; - -#include -#include - -PGM_BEGIN_DECLS - -#define PGM_GSISTRLEN (sizeof("000.000.000.000.000.000")) -#define PGM_GSI_INIT {{ 0, 0, 0, 0, 0, 0 }} - -struct pgm_gsi_t { - uint8_t identifier[6]; -}; - -bool pgm_gsi_create_from_hostname (pgm_gsi_t*restrict, pgm_error_t**restrict); -bool pgm_gsi_create_from_addr (pgm_gsi_t*restrict, pgm_error_t**restrict); -bool pgm_gsi_create_from_data (pgm_gsi_t*restrict, const uint8_t*restrict, const size_t); -bool pgm_gsi_create_from_string (pgm_gsi_t*restrict, const char*restrict, ssize_t); -int pgm_gsi_print_r (const pgm_gsi_t*restrict, char*restrict, const size_t); -char* pgm_gsi_print (const pgm_gsi_t*); -bool pgm_gsi_equal (const void*restrict, const void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; - -PGM_END_DECLS - -#endif /* __PGM_GSI_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/http.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/http.h deleted file mode 100644 index 5c65d15..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/http.h +++ /dev/null @@ -1,37 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * HTTP administrative interface - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_HTTP_H__ -#define __PGM_HTTP_H__ - -#include -#include - -PGM_BEGIN_DECLS - -#define PGM_HTTP_DEFAULT_SERVER_PORT 4968 - -bool pgm_http_init (uint16_t, pgm_error_t**) PGM_GNUC_WARN_UNUSED_RESULT; -bool pgm_http_shutdown (void); - -PGM_END_DECLS - -#endif /* __PGM_HTTP_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/if.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/if.h deleted file mode 100644 index 814d235..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/if.h +++ /dev/null @@ -1,33 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * network interface handling. - * - * Copyright (c) 2006 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_IF_H__ -#define __PGM_IF_H__ - -#include - -PGM_BEGIN_DECLS - -void pgm_if_print_all (void); - -PGM_END_DECLS - -#endif /* __PGM_IF_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/ip/pgm.hh b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/ip/pgm.hh deleted file mode 100644 index a5e260f..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/ip/pgm.hh +++ /dev/null @@ -1,100 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * PGM protocol - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_IP_PGM_HH__ -#define __PGM_IP_PGM_HH__ - -#if defined(_MSC_VER) && (_MSC_VER >= 1200) -# pragma once -#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) - -namespace pgm { -#define restrict -#include -} - -namespace ip { - -class pgm -{ -public: - /// The type of a PGM endpoint. - typedef pgm_endpoint endpoint; - - /// Construct to represent PGM over IPv4. - static pgm v4() - { - return pgm (PF_INET); - } - - /// Construct to represent PGM over IPv6. - static pgm v6() - { - return pgm (PF_INET6); - } - - /// Obtain an identifier for the type of the protocol. - int type() const - { - return SOCK_SEQPACKET; - } - - /// Obtain an identifier for the protocol. - int protocol() const - { - return IPPROTO_PGM; - } - - /// Obtain an identifier for the protocol family. - int family() const - { - return family_; - } - - /// The PGM socket type. - typedef pgm_socket socket; - - /// Compare two protocols for equality. - friend bool operator== (const pgm& p1, const pgm& p2) - { - return p1.family_ == p2.family_; - } - - /// Compare two protocols for inequality. - friend bool operator!= (const pgm& p1, const pgm& p2) - { - return p1.family_ != p2.family_; - } - -private: - // Construct with a specific family. - explicit pgm (int family) - : family_ (family) - { - } - - int family_; -}; - -} // namespace ip - - -#endif /* __PGM_IP_PGM_HH__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/ip/pgm_endpoint.hh b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/ip/pgm_endpoint.hh deleted file mode 100644 index 1114719..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/ip/pgm_endpoint.hh +++ /dev/null @@ -1,171 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * PGM endpoint - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_IP_PGM_ENDPOINT_HH__ -#define __PGM_IP_PGM_ENDPOINT_HH__ - -#if defined(_MSC_VER) && (_MSC_VER >= 1200) -# pragma once -#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) - -namespace pgm { -#define restrict -#include -} - -namespace ip { - -template -class pgm_endpoint -{ -public: - /// The protocol type associated with the endpoint. - typedef InternetProtocol protocol_type; - - typedef struct cpgm::pgm_sockaddr_t data_type; - - /// Default constructor. - pgm_endpoint() - : data_() - { - data_.sa_port = 0; - cpgm::pgm_tsi_t tmp_addr = PGM_TSI_INIT; - data_.sa_addr = tmp_addr; - } - - /// Construct an endpoint using a port number, specified in host byte - /// order. The GSI will be generated from the node name. - /** - * @par examples - * To initialise a PGM endpoint for port 7500, use: - * @code - * ip::pgm::endpoint ep (7500); - * @endcode - */ - pgm_endpoint (unsigned short port_num) - : data_() - { - data_.sa_port = port_num; - data_.sa_addr.sport = 0; - pgm_gsi_create_from_hostname (&data_.sa_addr.gsi, NULL); - } - - /// Construct an endpoint using a port number and a TSI. - pgm_endpoint (const cpgm::pgm_tsi_t& tsi, unsigned short port_num) - : data_() - { - data_.sa_port = port_num; - data_.sa_addr = tsi; - } - - /// Construct an endpoint using a port number and a memory area. - pgm_endpoint (const void* src, std::size_t len, unsigned short port_num) - : data_() - { - data_.sa_port = port_num; - data_.sa_addr.sport = 0; - pgm_gsi_create_from_data (&data_.sa_addr.gsi, src, len); - } - - /// Copy constructor. - pgm_endpoint (const pgm_endpoint& other) - : data_ (other.data_) - { - } - - /// Assign from another endpoint. - pgm_endpoint& operator= (const pgm_endpoint& other) - { - data_ = other.data_; - return *this; - } - - /// Get the underlying endpoint in the native type. - const data_type* data() const - { - return &data_; - } - - /// Get the underlying size of the endpoint in the native type. - std::size_t size() const - { - return sizeof(data_type); - } - - /// Get the port associated with the endpoint. The port number is always in - /// the host's byte order. - unsigned short port() const - { - return data_.sa_port; - } - - /// Set the port associated with the endpoint. The port number is always in - /// the host's byte order. - void port (unsigned short port_num) - { - data_.sa_port = port_num; - } - - /// Get the TSI associated with the endpoint. - const cpgm::pgm_tsi_t* address() const - { - return &data_.sa_addr; - } - - /// Set the TSI associated with the endpoint. - void address (cpgm::pgm_tsi_t& addr) - { - data_.sa_addr = addr; - } - - /// Compare two endpoints for equality. - friend bool operator== (const pgm_endpoint& e1, - const pgm_endpoint& e2) - { - return e1.address() == e2.address() && e1.port() == e2.port(); - } - - /// Compare two endpoints for inequality. - friend bool operator!= (const pgm_endpoint& e1, - const pgm_endpoint& e2) - { - return e1.address() != e2.address() || e1.port() != e2.port(); - } - - /// Compare endpoints for ordering. - friend bool operator<(const pgm_endpoint& e1, - const pgm_endpoint& e2) - { - if (e1.address() < e2.address()) - return true; - if (e1.address() != e2.address()) - return false; - return e1.port() < e2.port(); - } - -private: - // The underlying PGM socket address. - data_type data_; -}; - -} // namespace ip - -#endif /* __PGM_IP_PGM_ENDPOINT_HH__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/list.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/list.h deleted file mode 100644 index b91abc9..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/list.h +++ /dev/null @@ -1,40 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable doubly-linked list. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_LIST_H__ -#define __PGM_LIST_H__ - -typedef struct pgm_list_t pgm_list_t; - -#include - -PGM_BEGIN_DECLS - -struct pgm_list_t -{ - void* data; - struct pgm_list_t* next; - struct pgm_list_t* prev; -}; - -PGM_END_DECLS - -#endif /* __PGM_LIST_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/log.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/log.h deleted file mode 100644 index 2b6c036..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/log.h +++ /dev/null @@ -1,33 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * basic logging. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_LOG_H__ -#define __PGM_LOG_H__ - -#include - -PGM_BEGIN_DECLS - -bool log_init (void); - -PGM_END_DECLS - -#endif /* __PGM_LOG_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/macros.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/macros.h deleted file mode 100644 index 8808ccb..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/macros.h +++ /dev/null @@ -1,171 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * Pre-processor macros for cross-platform, cross-compiler ice cream. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_MACROS_H__ -#define __PGM_MACROS_H__ - -/* NULL, ptrdiff_t, and size_t - */ - -#include - - -/* GCC function attributes - * http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html - */ - -#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96) - -/* No side-effects except return value, may follow pointers and read globals */ -# define PGM_GNUC_PURE __attribute__((__pure__)) - -/* Returns new memory like malloc() */ -# define PGM_GNUC_MALLOC __attribute__((__malloc__)) - -# define PGM_GNUC_CACHELINE_ALIGNED __attribute__((__aligned__(SMP_CACHE_BYTES), \ - __section__((".data.cacheline_aligned"))) -# define PGM_GNUC_READ_MOSTLY __attribute__((__section__(".data.read_mostly"))) - -#else -# define PGM_GNUC_PURE -# define PGM_GNUC_MALLOC -# define PGM_GNUC_CACHELINE_ALIGNED -# define PGM_GNUC_READ_MOSTLY -#endif - -#if (__GNUC__ >= 4) - -/* Variable argument function with NULL terminated list */ -# define PGM_GNUC_NULL_TERMINATED __attribute__((__sentinel__)) -#else -# define PGM_GNUC_NULL_TERMINATED -#endif - -#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) - -/* malloc() with xth parameter being size */ -# define PGM_GNUC_ALLOC_SIZE(x) __attribute__((__alloc_size__(x))) - -/* malloc() with xth*yth parameters being size */ -# define PGM_GNUC_ALLOC_SIZE2(x,y) __attribute__((__alloc_size__(x,y))) -#else -# define PGM_GNUC_ALLOC_SIZE(x) -# define PGM_GNUC_ALLOC_SIZE2(x,y) -#endif - -#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) - -/* printf() like function */ -# define PGM_GNUC_PRINTF(format, args) __attribute__((__format__ (__printf__, format, args))) -# define PGM_GNUC_FORMAT(format) __attribute__((__format_arg__ (format))) - -/* Function will never return */ -# define PGM_GNUC_NORETURN __attribute__((__noreturn__)) - -/* No side-effects except return value, must not follow pointers or read globals */ -# define PGM_GNUC_CONST __attribute__((__const__)) - -/* Unused function */ -# define PGM_GNUC_UNUSED __attribute__((__unused__)) - -#else /* !__GNUC__ */ -# define PGM_GNUC_PRINTF(format, args) -# define PGM_GNUC_NORETURN -# define PGM_GNUC_CONST -# define PGM_GNUC_UNUSED -#endif /* !__GNUC__ */ - -#if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) - -/* Raise compiler warning if caller ignores return value */ -# define PGM_GNUC_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) - -# ifdef CONFIG_HAVE_DSO_VISIBILITY -/* Hidden visibility */ -# define PGM_GNUC_INTERNAL __attribute__((visibility("hidden"))) -# else -# define PGM_GNUC_INTERNAL -# endif -#else /* !__GNUC__ */ -# define PGM_GNUC_WARN_UNUSED_RESULT -# if defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) && defined(CONFIG_HAVE_DSO_VISIBILITY) -# define PGM_GNUC_INTERNAL __hidden -# else -# define PGM_GNUC_INTERNAL -# endif -#endif /* __GNUC__ */ - - -/* Compiler time assertions, must be on unique lines in the project */ -#define PGM_PASTE_ARGS(identifier1,identifier2) identifier1 ## identifier2 -#define PGM_PASTE(identifier1,identifier2) PGM_PASTE_ARGS (identifier1, identifier2) -#define PGM_STATIC_ASSERT(expr) typedef struct { char compile_time_assertion[(expr) ? 1 : -1]; } PGM_PASTE (_pgm_static_assert_, __LINE__) - -/* Function declaration wrappers for C++ */ -#ifdef __cplusplus -# define PGM_BEGIN_DECLS extern "C" { -# define PGM_END_DECLS } -#else -# define PGM_BEGIN_DECLS -# define PGM_END_DECLS -#endif - -/* Surprisingly still not defined in C99 */ -#ifndef FALSE -# define FALSE (0) -#endif - -#ifndef TRUE -# define TRUE (!FALSE) -#endif - -#undef MAX -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) - -#undef MIN -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) - -#undef ABS -#define ABS(a) (((a) < 0) ? -(a) : (a)) - -#undef CLAMP -#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) - -/* Number of elements */ -#define PGM_N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0])) - -/* Structure offsets */ -#if defined(__GNUC__) && __GNUC__ >= 4 -# define PGM_OFFSETOF(struct_type, member) (offsetof (struct_type, member)) -#else -# define PGM_OFFSETOF(struct_type, member) ((size_t)((char*)&((struct_type*) 0)->member)) -#endif - -/* Branch prediction hint */ -#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) -# define PGM_LIKELY(expr) __builtin_expect ((expr), 1) -# define PGM_UNLIKELY(expr) __builtin_expect ((expr), 0) -#else -# define PGM_LIKELY(expr) (expr) -# define PGM_UNLIKELY(expr) (expr) -#endif - -#endif /* __PGM_MACROS_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/mem.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/mem.h deleted file mode 100644 index eea31fe..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/mem.h +++ /dev/null @@ -1,60 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable fail fast memory allocation. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_MEM_H__ -#define __PGM_MEM_H__ - -#ifdef CONFIG_HAVE_ALLOCA_H -# include -#elif defined(_WIN32) -# include -#else -# include -#endif -#include - -PGM_BEGIN_DECLS - -extern bool pgm_mem_gc_friendly; - -void* pgm_malloc (const size_t) PGM_GNUC_MALLOC PGM_GNUC_ALLOC_SIZE(1); -void* pgm_malloc_n (const size_t, const size_t) PGM_GNUC_MALLOC PGM_GNUC_ALLOC_SIZE2(1, 2); -void* pgm_malloc0 (const size_t) PGM_GNUC_MALLOC PGM_GNUC_ALLOC_SIZE(1); -void* pgm_malloc0_n (const size_t, const size_t) PGM_GNUC_MALLOC PGM_GNUC_ALLOC_SIZE2(1, 2); -void* pgm_memdup (const void*, const size_t) PGM_GNUC_MALLOC; -void* pgm_realloc (void*, const size_t) PGM_GNUC_WARN_UNUSED_RESULT; -void pgm_free (void*); - -/* Convenience memory allocators that wont work well above 32-bit sizes - */ -#define pgm_new(struct_type, n_structs) \ - ((struct_type*)pgm_malloc_n ((size_t)sizeof(struct_type), (size_t)(n_structs))) -#define pgm_new0(struct_type, n_structs) \ - ((struct_type*)pgm_malloc0_n ((size_t)sizeof(struct_type), (size_t)(n_structs))) - -#define pgm_alloca(size) \ - alloca (size) -#define pgm_newa(struct_type, n_structs) \ - ((struct_type*) pgm_alloca (sizeof(struct_type) * (size_t)(n_structs))) - -PGM_END_DECLS - -#endif /* __PGM_MEM_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/messages.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/messages.h deleted file mode 100644 index 6a00b8f..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/messages.h +++ /dev/null @@ -1,66 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * basic message reporting. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_MESSAGES_H__ -#define __PGM_MESSAGES_H__ - -#include - -PGM_BEGIN_DECLS - -/* Set bitmask of log roles in environmental variable PGM_LOG_MASK, - * borrowed from SmartPGM. - */ -enum { - PGM_LOG_ROLE_MEMORY = 0x001, - PGM_LOG_ROLE_NETWORK = 0x002, - PGM_LOG_ROLE_CONFIGURATION = 0x004, - PGM_LOG_ROLE_SESSION = 0x010, - PGM_LOG_ROLE_NAK = 0x020, - PGM_LOG_ROLE_RATE_CONTROL = 0x040, - PGM_LOG_ROLE_TX_WINDOW = 0x080, - PGM_LOG_ROLE_RX_WINDOW = 0x100, - PGM_LOG_ROLE_FEC = 0x400, - PGM_LOG_ROLE_CONGESTION_CONTROL = 0x800 -}; - -enum { - PGM_LOG_LEVEL_DEBUG = 0, - PGM_LOG_LEVEL_TRACE = 1, - PGM_LOG_LEVEL_MINOR = 2, - PGM_LOG_LEVEL_NORMAL = 3, - PGM_LOG_LEVEL_WARNING = 4, - PGM_LOG_LEVEL_ERROR = 5, - PGM_LOG_LEVEL_FATAL = 6 -}; - -extern int pgm_log_mask; -extern int pgm_min_log_level; - -typedef void (*pgm_log_func_t) (const int, const char*restrict, void*restrict); - -pgm_log_func_t pgm_log_set_handler (pgm_log_func_t, void*); -void pgm_messages_init (void); -void pgm_messages_shutdown (void); - -PGM_END_DECLS - -#endif /* __PGM_MESSAGES_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/msgv.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/msgv.h deleted file mode 100644 index f5effcb..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/msgv.h +++ /dev/null @@ -1,54 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Vector message container - * - * Copyright (c) 2006-2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_MSGV_H__ -#define __PGM_MSGV_H__ - -struct pgm_iovec; -struct pgm_msgv_t; - -#include -#include -#include - -PGM_BEGIN_DECLS - -/* struct for scatter/gather I/O */ -struct pgm_iovec { -#ifndef _WIN32 -/* match struct iovec */ - void* iov_base; - size_t iov_len; /* size of iov_base */ -#else -/* match WSABUF */ - u_long iov_len; - char* iov_base; -#endif /* _WIN32 */ -}; - -struct pgm_msgv_t { - uint32_t msgv_len; /* number of elements in skb */ - struct pgm_sk_buff_t* msgv_skb[PGM_MAX_FRAGMENTS]; /* PGM socket buffer array */ -}; - -PGM_END_DECLS - -#endif /* __PGM_MSGV_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/packet.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/packet.h deleted file mode 100644 index b1bb639..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/packet.h +++ /dev/null @@ -1,472 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * PGM packet formats, RFC 3208. - * - * Copyright (c) 2006 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_PACKET_H__ -#define __PGM_PACKET_H__ - -#ifndef _WIN32 -# include -# include -# include -#endif -#include - -PGM_BEGIN_DECLS - -/* protocol number assigned by IANA */ -#ifndef IPPROTO_PGM -# define IPPROTO_PGM 113 -#endif - -/* read from /etc/protocols if available */ -extern int pgm_ipproto_pgm; - - -/* address family indicator, rfc 1700 (ADDRESS FAMILY NUMBERS) */ -#ifndef AFI_IP -# define AFI_IP 1 /* IP (IP version 4) */ -# define AFI_IP6 2 /* IP6 (IP version 6) */ -#endif - -/* UDP ports for UDP encapsulation, as per IBM WebSphere MQ */ -#define DEFAULT_UDP_ENCAP_UCAST_PORT 3055 -#define DEFAULT_UDP_ENCAP_MCAST_PORT 3056 - -/* PGM default ports */ -#define DEFAULT_DATA_DESTINATION_PORT 7500 -#define DEFAULT_DATA_SOURCE_PORT 0 /* random */ - -/* DoS limitation to protocol (MS08-036, KB950762) */ -#ifndef PGM_MAX_APDU -# define PGM_MAX_APDU UINT16_MAX -#endif - -/* Cisco default: 24 (max 8200), Juniper & H3C default: 16, SmartPGM: 64 */ -#ifndef PGM_MAX_FRAGMENTS -# define PGM_MAX_FRAGMENTS 16 -#endif - - -enum pgm_type_e { - PGM_SPM = 0x00, /* 8.1: source path message */ - PGM_POLL = 0x01, /* 14.7.1: poll request */ - PGM_POLR = 0x02, /* 14.7.2: poll response */ - PGM_ODATA = 0x04, /* 8.2: original data */ - PGM_RDATA = 0x05, /* 8.2: repair data */ - PGM_NAK = 0x08, /* 8.3: NAK or negative acknowledgement */ - PGM_NNAK = 0x09, /* 8.3: N-NAK or null negative acknowledgement */ - PGM_NCF = 0x0a, /* 8.3: NCF or NAK confirmation */ - PGM_SPMR = 0x0c, /* 13.6: SPM request */ - PGM_ACK = 0x0d, /* PGMCC: congestion control ACK */ - PGM_MAX = 0xff -}; - -#define PGM_OPT_LENGTH 0x00 /* options length */ -#define PGM_OPT_FRAGMENT 0x01 /* fragmentation */ -#define PGM_OPT_NAK_LIST 0x02 /* list of nak entries */ -#define PGM_OPT_JOIN 0x03 /* late joining */ -#define PGM_OPT_REDIRECT 0x07 /* redirect */ -#define PGM_OPT_SYN 0x0d /* synchronisation */ -#define PGM_OPT_FIN 0x0e /* session end */ -#define PGM_OPT_RST 0x0f /* session reset */ - -#define PGM_OPT_PARITY_PRM 0x08 /* forward error correction parameters */ -#define PGM_OPT_PARITY_GRP 0x09 /* group number */ -#define PGM_OPT_CURR_TGSIZE 0x0a /* group size */ - -#define PGM_OPT_CR 0x10 /* congestion report */ -#define PGM_OPT_CRQST 0x11 /* congestion report request */ - -#define PGM_OPT_PGMCC_DATA 0x12 -#define PGM_OPT_PGMCC_FEEDBACK 0x13 - -#define PGM_OPT_NAK_BO_IVL 0x04 /* nak back-off interval */ -#define PGM_OPT_NAK_BO_RNG 0x05 /* nak back-off range */ -#define PGM_OPT_NBR_UNREACH 0x0b /* neighbour unreachable */ -#define PGM_OPT_PATH_NLA 0x0c /* path nla */ - -#define PGM_OPT_INVALID 0x7f /* option invalidated */ - -/* byte alignment for packet memory maps */ -#if defined( __GNUC__ ) && !defined( sun ) -# pragma pack(push) -#endif -#pragma pack(1) - -/* 8. PGM header */ -struct pgm_header { - uint16_t pgm_sport; /* source port: tsi::sport or UDP port depending on direction */ - uint16_t pgm_dport; /* destination port */ - uint8_t pgm_type; /* version / packet type */ - uint8_t pgm_options; /* options */ -#define PGM_OPT_PARITY 0x80 /* parity packet */ -#define PGM_OPT_VAR_PKTLEN 0x40 /* + variable sized packets */ -#define PGM_OPT_NETWORK 0x02 /* network-significant: must be interpreted by network elements */ -#define PGM_OPT_PRESENT 0x01 /* option extension are present */ - uint16_t pgm_checksum; /* checksum */ - uint8_t pgm_gsi[6]; /* global source id */ - uint16_t pgm_tsdu_length; /* tsdu length */ - /* tpdu length = th length (header + options) + tsdu length */ -}; - -/* 8.1. Source Path Messages (SPM) */ -struct pgm_spm { - uint32_t spm_sqn; /* spm sequence number */ - uint32_t spm_trail; /* trailing edge sequence number */ - uint32_t spm_lead; /* leading edge sequence number */ - uint16_t spm_nla_afi; /* nla afi */ - uint16_t spm_reserved; /* reserved */ - struct in_addr spm_nla; /* path nla */ - /* ... option extensions */ -}; - -struct pgm_spm6 { - uint32_t spm6_sqn; /* spm sequence number */ - uint32_t spm6_trail; /* trailing edge sequence number */ - uint32_t spm6_lead; /* leading edge sequence number */ - uint16_t spm6_nla_afi; /* nla afi */ - uint16_t spm6_reserved; /* reserved */ - struct in6_addr spm6_nla; /* path nla */ - /* ... option extensions */ -}; - -/* 8.2. Data Packet */ -struct pgm_data { - uint32_t data_sqn; /* data packet sequence number */ - uint32_t data_trail; /* trailing edge sequence number */ - /* ... option extensions */ - /* ... data */ -}; - -/* 8.3. Negative Acknowledgments and Confirmations (NAK, N-NAK, & NCF) */ -struct pgm_nak { - uint32_t nak_sqn; /* requested sequence number */ - uint16_t nak_src_nla_afi; /* nla afi */ - uint16_t nak_reserved; /* reserved */ - struct in_addr nak_src_nla; /* source nla */ - uint16_t nak_grp_nla_afi; /* nla afi */ - uint16_t nak_reserved2; /* reserved */ - struct in_addr nak_grp_nla; /* multicast group nla */ - /* ... option extension */ -}; - -struct pgm_nak6 { - uint32_t nak6_sqn; /* requested sequence number */ - uint16_t nak6_src_nla_afi; /* nla afi */ - uint16_t nak6_reserved; /* reserved */ - struct in6_addr nak6_src_nla; /* source nla */ - uint16_t nak6_grp_nla_afi; /* nla afi */ - uint16_t nak6_reserved2; /* reserved */ - struct in6_addr nak6_grp_nla; /* multicast group nla */ - /* ... option extension */ -}; - -/* 9. Option header (max 16 per packet) */ -struct pgm_opt_header { - uint8_t opt_type; /* option type */ -#define PGM_OPT_MASK 0x7f -#define PGM_OPT_END 0x80 /* end of options flag */ - uint8_t opt_length; /* option length */ - uint8_t opt_reserved; -#define PGM_OP_ENCODED 0x8 /* F-bit */ -#define PGM_OPX_MASK 0x3 -#define PGM_OPX_IGNORE 0x0 /* extensibility bits */ -#define PGM_OPX_INVALIDATE 0x1 -#define PGM_OPX_DISCARD 0x2 -#define PGM_OP_ENCODED_NULL 0x80 /* U-bit */ -}; - -/* 9.1. Option extension length - OPT_LENGTH */ -struct pgm_opt_length { - uint8_t opt_type; /* include header as total length overwrites reserved/OPX bits */ - uint8_t opt_length; - uint16_t opt_total_length; /* total length of all options */ -}; - -/* 9.2. Option fragment - OPT_FRAGMENT */ -struct pgm_opt_fragment { - uint8_t opt_reserved; /* reserved */ - uint32_t opt_sqn; /* first sequence number */ - uint32_t opt_frag_off; /* offset */ - uint32_t opt_frag_len; /* length */ -}; - -/* 9.3.5. Option NAK List - OPT_NAK_LIST - * - * GNU C allows opt_sqn[0], ISO C89 requireqs opt_sqn[1], ISO C99 permits opt_sqn[] - */ -struct pgm_opt_nak_list { - uint8_t opt_reserved; /* reserved */ -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) - uint32_t opt_sqn[]; /* requested sequence number [62] */ -#elif defined(__cplusplus) - uint32_t opt_sqn[1]; -#else - uint32_t opt_sqn[0]; -#endif -}; - -/* 9.4.2. Option Join - OPT_JOIN */ -struct pgm_opt_join { - uint8_t opt_reserved; /* reserved */ - uint32_t opt_join_min; /* minimum sequence number */ -}; - -/* 9.5.5. Option Redirect - OPT_REDIRECT */ -struct pgm_opt_redirect { - uint8_t opt_reserved; /* reserved */ - uint16_t opt_nla_afi; /* nla afi */ - uint16_t opt_reserved2; /* reserved */ - struct in_addr opt_nla; /* dlr nla */ -}; - -struct pgm_opt6_redirect { - uint8_t opt6_reserved; /* reserved */ - uint16_t opt6_nla_afi; /* nla afi */ - uint16_t opt6_reserved2; /* reserved */ - struct in6_addr opt6_nla; /* dlr nla */ -}; - -/* 9.6.2. Option Sources - OPT_SYN */ -struct pgm_opt_syn { - uint8_t opt_reserved; /* reserved */ -}; - -/* 9.7.4. Option End Session - OPT_FIN */ -struct pgm_opt_fin { - uint8_t opt_reserved; /* reserved */ -}; - -/* 9.8.4. Option Reset - OPT_RST */ -struct pgm_opt_rst { - uint8_t opt_reserved; /* reserved */ -}; - - -/* - * Forward Error Correction - FEC - */ - -/* 11.8.1. Option Parity - OPT_PARITY_PRM */ -struct pgm_opt_parity_prm { - uint8_t opt_reserved; /* reserved */ -#define PGM_PARITY_PRM_MASK 0x3 -#define PGM_PARITY_PRM_PRO 0x1 /* source provides pro-active parity packets */ -#define PGM_PARITY_PRM_OND 0x2 /* on-demand parity packets */ - uint32_t parity_prm_tgs; /* transmission group size */ -}; - -/* 11.8.2. Option Parity Group - OPT_PARITY_GRP */ -struct pgm_opt_parity_grp { - uint8_t opt_reserved; /* reserved */ - uint32_t prm_group; /* parity group number */ -}; - -/* 11.8.3. Option Current Transmission Group Size - OPT_CURR_TGSIZE */ -struct pgm_opt_curr_tgsize { - uint8_t opt_reserved; /* reserved */ - uint32_t prm_atgsize; /* actual transmission group size */ -}; - -/* - * Congestion Control - */ - -/* 12.7.1. Option Congestion Report - OPT_CR */ -struct pgm_opt_cr { - uint8_t opt_reserved; /* reserved */ -#define PGM_OPT_CR_NEL 0x0 /* OPT_CR_NE_WL report */ -#define PGM_OPT_CR_NEP 0x1 /* OPT_CR_NE_WP report */ -#define PGM_OPT_CR_RXP 0x2 /* OPT_CR_RX_WP report */ - uint32_t opt_cr_lead; /* congestion report reference sqn */ - uint16_t opt_cr_ne_wl; /* ne worst link */ - uint16_t opt_cr_ne_wp; /* ne worst path */ - uint16_t opt_cr_rx_wp; /* rcvr worst path */ - uint16_t opt_reserved2; /* reserved */ - uint16_t opt_nla_afi; /* nla afi */ - uint16_t opt_reserved3; /* reserved */ - uint32_t opt_cr_rcvr; /* worst receivers nla */ -}; - -/* 12.7.2. Option Congestion Report Request - OPT_CRQST */ -struct pgm_opt_crqst { - uint8_t opt_reserved; /* reserved */ -#define PGM_OPT_CRQST_NEL 0x0 /* request OPT_CR_NE_WL report */ -#define PGM_OPT_CRQST_NEP 0x1 /* request OPT_CR_NE_WP report */ -#define PGM_OPT_CRQST_RXP 0x2 /* request OPT_CR_RX_WP report */ -}; - -/* PGMCC. ACK Packet */ -struct pgm_ack { - uint32_t ack_rx_max; /* RX_MAX */ - uint32_t ack_bitmap; /* received packets */ - /* ... option extensions */ -}; - -/* PGMCC Options */ -struct pgm_opt_pgmcc_data { - uint8_t opt_reserved; /* reserved */ - uint32_t opt_tstamp; /* timestamp */ - uint16_t opt_nla_afi; /* nla afi */ - uint16_t opt_reserved2; /* reserved */ - struct in_addr opt_nla; /* ACKER nla */ -}; - -struct pgm_opt6_pgmcc_data { - uint8_t opt6_reserved; /* reserved */ - uint32_t opt6_tstamp; /* timestamp */ - uint16_t opt6_nla_afi; /* nla afi */ - uint16_t opt6_reserved2; /* reserved */ - struct in6_addr opt6_nla; /* ACKER nla */ -}; - -struct pgm_opt_pgmcc_feedback { - uint8_t opt_reserved; /* reserved */ - uint32_t opt_tstamp; /* timestamp */ - uint16_t opt_nla_afi; /* nla afi */ - uint16_t opt_loss_rate; /* loss rate */ - struct in_addr opt_nla; /* ACKER nla */ -}; - -struct pgm_opt6_pgmcc_feedback { - uint8_t opt6_reserved; /* reserved */ - uint32_t opt6_tstamp; /* timestamp */ - uint16_t opt6_nla_afi; /* nla afi */ - uint16_t opt6_loss_rate; /* loss rate */ - struct in6_addr opt6_nla; /* ACKER nla */ -}; - - -/* - * SPM Requests - */ - -/* 13.6. SPM Requests */ -#if 0 -struct pgm_spmr { - /* ... option extensions */ -}; -#endif - - -/* - * Poll Mechanism - */ - -/* 14.7.1. Poll Request */ -struct pgm_poll { - uint32_t poll_sqn; /* poll sequence number */ - uint16_t poll_round; /* poll round */ - uint16_t poll_s_type; /* poll sub-type */ -#define PGM_POLL_GENERAL 0x0 /* general poll */ -#define PGM_POLL_DLR 0x1 /* DLR poll */ - uint16_t poll_nla_afi; /* nla afi */ - uint16_t poll_reserved; /* reserved */ - struct in_addr poll_nla; /* path nla */ - uint32_t poll_bo_ivl; /* poll back-off interval */ - char poll_rand[4]; /* random string */ - uint32_t poll_mask; /* matching bit-mask */ - /* ... option extensions */ -}; - -struct pgm_poll6 { - uint32_t poll6_sqn; /* poll sequence number */ - uint16_t poll6_round; /* poll round */ - uint16_t poll6_s_type; /* poll sub-type */ - uint16_t poll6_nla_afi; /* nla afi */ - uint16_t poll6_reserved; /* reserved */ - struct in6_addr poll6_nla; /* path nla */ - uint32_t poll6_bo_ivl; /* poll back-off interval */ - char poll6_rand[4]; /* random string */ - uint32_t poll6_mask; /* matching bit-mask */ - /* ... option extensions */ -}; - -/* 14.7.2. Poll Response */ -struct pgm_polr { - uint32_t polr_sqn; /* polr sequence number */ - uint16_t polr_round; /* polr round */ - uint16_t polr_reserved; /* reserved */ - /* ... option extensions */ -}; - - -/* - * Implosion Prevention - */ - -/* 15.4.1. Option NAK Back-Off Interval - OPT_NAK_BO_IVL */ -struct pgm_opt_nak_bo_ivl { - uint8_t opt_reserved; /* reserved */ - uint32_t opt_nak_bo_ivl; /* nak back-off interval */ - uint32_t opt_nak_bo_ivl_sqn; /* nak back-off interval sqn */ -}; - -/* 15.4.2. Option NAK Back-Off Range - OPT_NAK_BO_RNG */ -struct pgm_opt_nak_bo_rng { - uint8_t opt_reserved; /* reserved */ - uint32_t opt_nak_max_bo_ivl; /* maximum nak back-off interval */ - uint32_t opt_nak_min_bo_ivl; /* minimum nak back-off interval */ -}; - -/* 15.4.3. Option Neighbour Unreachable - OPT_NBR_UNREACH */ -struct pgm_opt_nbr_unreach { - uint8_t opt_reserved; /* reserved */ -}; - -/* 15.4.4. Option Path - OPT_PATH_NLA */ -struct pgm_opt_path_nla { - uint8_t opt_reserved; /* reserved */ - struct in_addr opt_path_nla; /* path nla */ -}; - -struct pgm_opt6_path_nla { - uint8_t opt6_reserved; /* reserved */ - struct in6_addr opt6_path_nla; /* path nla */ -}; - - -#if defined( __GNUC__ ) && !defined( sun ) -# pragma pack(pop) -#else -# pragma pack() -#endif - -#define PGM_IS_UPSTREAM(t) \ - ((t) == PGM_NAK /* unicast */ \ - || (t) == PGM_NNAK /* unicast */ \ - || (t) == PGM_SPMR /* multicast + unicast */ \ - || (t) == PGM_POLR /* unicast */ \ - || (t) == PGM_ACK) /* unicast */ - -#define PGM_IS_PEER(t) \ - ((t) == PGM_SPMR) /* multicast */ - -#define PGM_IS_DOWNSTREAM(t) \ - ((t) == PGM_SPM /* all types are multicast */ \ - || (t) == PGM_ODATA \ - || (t) == PGM_RDATA \ - || (t) == PGM_POLL \ - || (t) == PGM_NCF) - -PGM_END_DECLS - -#endif /* __PGM_PACKET_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm.h deleted file mode 100644 index a122f48..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm.h +++ /dev/null @@ -1,42 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * OpenPGM, an implementation of the PGM network protocol. - * - * Copyright (c) 2006-2008 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_H__ -#define __PGM_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#endif /* __PGM_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm.hh b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm.hh deleted file mode 100644 index fc3476f..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm.hh +++ /dev/null @@ -1,33 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * OpenPGM C++ wrapper. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_HH__ -#define __PGM_HH__ - -#if defined(_MSC_VER) && (_MSC_VER >= 1200) -# pragma once -#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) - -#include -#include -#include - -#endif /* __PGM_HH__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm_socket.hh b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm_socket.hh deleted file mode 100644 index 9a3f11b..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm_socket.hh +++ /dev/null @@ -1,157 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * PGM socket - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_SOCKET_HH__ -#define __PGM_SOCKET_HH__ - -#if defined(_MSC_VER) && (_MSC_VER >= 1200) -# pragma once -#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) - -#include -#ifndef _WIN32 -# include -# include -#else -# include -#endif - -namespace cpgm { -#define restrict -#include -}; - -template -class pgm_socket -{ -public: - /// The protocol type. - typedef Protocol protocol_type; - - /// The endpoint type. - typedef typename Protocol::endpoint endpoint_type; - - /// The native socket type. - typedef struct cpgm::pgm_sock_t* native_type; - - /// Construct a pgm_socket without opening it. - pgm_socket() - { - } - - // Open a new PGM socket implementation. - bool open (::sa_family_t family, int sock_type, int protocol, cpgm::pgm_error_t** error) - { - return cpgm::pgm_socket (&this->native_type_, family, sock_type, protocol, error); - } - - /// Close a PGM socket implementation. - bool close (bool flush) - { - return pgm_close (this->native_type_, flush); - } - - /// Get the native socket implementation. - native_type native (void) - { - return this->native_type_; - } - - // Bind the datagram socket to the specified local endpoint. - bool bind (const endpoint_type& addr, cpgm::pgm_error_t** error) - { - return pgm_bind (this->native_type_, addr.data(), sizeof(addr.data()), error); - } - - /// Connect the PGM socket to the specified endpoint. - bool connect (cpgm::pgm_error_t** error) - { - return pgm_connect (this->native_type_, error); - } - - /// Set a socket option. - bool set_option (int optname, const void* optval, ::socklen_t optlen) - { - return pgm_setsockopt (this->native_type_, optname, optval, optlen); - } - - /// Get a socket option. - bool get_option (int optname, void* optval, ::socklen_t* optlen) - { - return pgm_getsockopt (this->native_type_, optname, optval, optlen); - } - - /// Get the local endpoint. - endpoint_type local_endpoint() const - { - endpoint_type endpoint; - pgm_getsockname (this->native_type_, &endpoint); - return endpoint; - } - - /// Disable sends or receives on the socket. - bool shutdown (int what) - { - int optname, v = 1; -#ifndef _WIN32 - if (SHUT_RD == what) optname = cpgm::PGM_SEND_ONLY; - else if (SHUT_WR == what) optname = cpgm::PGM_RECV_ONLY; -#else - if (SD_RECEIVE == what) optname = cpgm::PGM_SEND_ONLY; - else if (SD_SEND == what) optname = cpgm::PGM_RECV_ONLY; -#endif - else { - errno = EINVAL; - return false; - } - return pgm_setsockopt (this->native_type_, optname, v, sizeof(v)); - } - - /// Send some data on a connected socket. - int send (const void* buf, std::size_t len, std::size_t* bytes_sent) - { - return pgm_send (this->native_type_, buf, len, bytes_sent); - } - - /// Receive some data from the peer. - int receive (void* buf, std::size_t len, int flags, std::size_t* bytes_read, cpgm::pgm_error_t** error) - { - return pgm_recv (this->native_type_, buf, len, flags, bytes_read, error); - } - - /// Receive a datagram with the endpoint of the sender. - int receive_from (void* buf, std::size_t len, int flags, std::size_t* bytes_read, endpoint_type* from, cpgm::pgm_error_t** error) - { - int ec; - struct cpgm::pgm_sockaddr_t addr; - socklen_t addrlen = sizeof (addr); - ec = pgm_recvfrom (this->native_type_, buf, len, flags, bytes_read, &addr, &addrlen, error); - from->port (addr.sa_port); - from->address (addr.sa_addr); -/* TODO: set data-destination port */ - return ec; - } - -private: - native_type native_type_; -}; - -#endif /* __PGM_SOCKET_HH__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/signal.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/signal.h deleted file mode 100644 index 546fb7e..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/signal.h +++ /dev/null @@ -1,36 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * Re-entrant safe signal handling. - * - * Copyright (c) 2006-2007 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_SIGNAL_H__ -#define __PGM_SIGNAL_H__ - -#include -#include - -typedef void (*pgm_sighandler_t)(int, gpointer); - -G_BEGIN_DECLS - -gboolean pgm_signal_install (int, pgm_sighandler_t, gpointer); - -G_END_DECLS - -#endif /* __PGM_SIGNAL_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/skbuff.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/skbuff.h deleted file mode 100644 index 0701dfb..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/skbuff.h +++ /dev/null @@ -1,243 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * PGM socket buffers - * - * Copyright (c) 2006-2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_SKBUFF_H__ -#define __PGM_SKBUFF_H__ - -#include - -struct pgm_sk_buff_t; - -#include -#include -#include -#include -#include -#include -#include -#include - -PGM_BEGIN_DECLS - -struct pgm_sk_buff_t { - pgm_list_t link_; - - pgm_sock_t* restrict sock; - pgm_time_t tstamp; - pgm_tsi_t tsi; - - uint32_t sequence; - uint32_t __padding; /* push alignment of pgm_sk_buff_t::cb to 8 bytes */ - - char cb[48]; /* control buffer */ - - uint16_t len; /* actual data */ - unsigned zero_padded:1; - - struct pgm_header* pgm_header; - struct pgm_opt_fragment* pgm_opt_fragment; -#define of_apdu_first_sqn pgm_opt_fragment->opt_sqn -#define of_frag_offset pgm_opt_fragment->opt_frag_off -#define of_apdu_len pgm_opt_fragment->opt_frag_len - struct pgm_opt_pgmcc_data* pgm_opt_pgmcc_data; - struct pgm_data* pgm_data; - - void *head, /* all may-alias */ - *data, - *tail, - *end; - uint32_t truesize; - volatile uint32_t users; /* atomic */ -}; - -void pgm_skb_over_panic (const struct pgm_sk_buff_t*const, const uint16_t) PGM_GNUC_NORETURN; -void pgm_skb_under_panic (const struct pgm_sk_buff_t*const, const uint16_t) PGM_GNUC_NORETURN; -bool pgm_skb_is_valid (const struct pgm_sk_buff_t*const) PGM_GNUC_PURE PGM_GNUC_WARN_UNUSED_RESULT; - -/* attribute __pure__ only valid for platforms with atomic ops. - * attribute __malloc__ not used as only part of the memory should be aliased. - * attribute __alloc_size__ does not allow headroom. - */ -static inline struct pgm_sk_buff_t* pgm_alloc_skb (const uint16_t) PGM_GNUC_WARN_UNUSED_RESULT; - -static inline -struct pgm_sk_buff_t* -pgm_alloc_skb ( - const uint16_t size - ) -{ - struct pgm_sk_buff_t* skb; - - skb = (struct pgm_sk_buff_t*)pgm_malloc (size + sizeof(struct pgm_sk_buff_t)); - if (PGM_UNLIKELY(pgm_mem_gc_friendly)) { - memset (skb, 0, size + sizeof(struct pgm_sk_buff_t)); - skb->zero_padded = 1; - } else { - memset (skb, 0, sizeof(struct pgm_sk_buff_t)); - } - skb->truesize = size + sizeof(struct pgm_sk_buff_t); - pgm_atomic_write32 (&skb->users, 1); - skb->head = skb + 1; - skb->data = skb->tail = skb->head; - skb->end = (char*)skb->data + size; - return skb; -} - -/* increase reference count */ -static inline -struct pgm_sk_buff_t* -pgm_skb_get ( - struct pgm_sk_buff_t*const skb - ) -{ - pgm_atomic_inc32 (&skb->users); - return skb; -} - -static inline -void -pgm_free_skb ( - struct pgm_sk_buff_t*const skb - ) -{ - if (pgm_atomic_exchange_and_add32 (&skb->users, (uint32_t)-1) == 1) - pgm_free (skb); -} - -/* add data */ -static inline -void* -pgm_skb_put ( - struct pgm_sk_buff_t* const skb, - const uint16_t len - ) -{ - void* tmp = skb->tail; - skb->tail = (char*)skb->tail + len; - skb->len += len; - if (PGM_UNLIKELY(skb->tail > skb->end)) - pgm_skb_over_panic (skb, len); - return tmp; -} - -static inline -void* -__pgm_skb_pull ( - struct pgm_sk_buff_t*const skb, - const uint16_t len - ) -{ - skb->len -= len; - return skb->data = (char*)skb->data + len; -} - -/* remove data from start of buffer */ -static inline -void* -pgm_skb_pull ( - struct pgm_sk_buff_t*const skb, - const uint16_t len - ) -{ - return PGM_UNLIKELY(len > skb->len) ? NULL : __pgm_skb_pull (skb, len); -} - -static inline uint16_t pgm_skb_headroom (const struct pgm_sk_buff_t*const) PGM_GNUC_PURE PGM_GNUC_WARN_UNUSED_RESULT; -static inline uint16_t pgm_skb_tailroom (const struct pgm_sk_buff_t*const) PGM_GNUC_PURE PGM_GNUC_WARN_UNUSED_RESULT; - -static inline -uint16_t -pgm_skb_headroom ( - const struct pgm_sk_buff_t*const skb - ) -{ - return (char*)skb->data - (char*)skb->head; -} - -static inline -uint16_t -pgm_skb_tailroom ( - const struct pgm_sk_buff_t*const skb - ) -{ - return (char*)skb->end - (char*)skb->tail; -} - -/* reserve space to add data */ -static inline -void -pgm_skb_reserve ( - struct pgm_sk_buff_t*const skb, - const uint16_t len - ) -{ - skb->data = (char*)skb->data + len; - skb->tail = (char*)skb->tail + len; - if (PGM_UNLIKELY(skb->tail > skb->end)) - pgm_skb_over_panic (skb, len); - if (PGM_UNLIKELY(skb->data < skb->head)) - pgm_skb_under_panic (skb, len); -} - -static inline struct pgm_sk_buff_t* pgm_skb_copy (const struct pgm_sk_buff_t* const) PGM_GNUC_WARN_UNUSED_RESULT; - -static inline -struct pgm_sk_buff_t* -pgm_skb_copy ( - const struct pgm_sk_buff_t* const skb - ) -{ - struct pgm_sk_buff_t* newskb; - newskb = (struct pgm_sk_buff_t*)pgm_malloc (skb->truesize); - memcpy (newskb, skb, PGM_OFFSETOF(struct pgm_sk_buff_t, pgm_header)); - newskb->zero_padded = 0; - newskb->truesize = skb->truesize; - pgm_atomic_write32 (&newskb->users, 1); - newskb->head = newskb + 1; - newskb->end = (char*)newskb->head + ((char*)skb->end - (char*)skb->head); - newskb->data = (char*)newskb->head + ((char*)skb->data - (char*)skb->head); - newskb->tail = (char*)newskb->head + ((char*)skb->tail - (char*)skb->head); - newskb->pgm_header = skb->pgm_header ? (struct pgm_header*)((char*)newskb->head + ((char*)skb->pgm_header - (char*)skb->head)) : skb->pgm_header; - newskb->pgm_opt_fragment = skb->pgm_opt_fragment ? (struct pgm_opt_fragment*)((char*)newskb->head + ((char*)skb->pgm_opt_fragment - (char*)skb->head)) : skb->pgm_opt_fragment; - newskb->pgm_data = skb->pgm_data ? (struct pgm_data*)((char*)newskb->head + ((char*)skb->pgm_data - (char*)skb->head)) : skb->pgm_data; - memcpy (newskb->head, skb->head, (char*)skb->end - (char*)skb->head); - return newskb; -} - -static inline -void -pgm_skb_zero_pad ( - struct pgm_sk_buff_t* const skb, - const uint16_t len - ) -{ - if (skb->zero_padded) - return; - - const uint16_t tailroom = MIN(pgm_skb_tailroom (skb), len); - if (tailroom > 0) - memset (skb->tail, 0, tailroom); - skb->zero_padded = 1; -} - -PGM_END_DECLS - -#endif /* __PGM_SKBUFF_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/snmp.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/snmp.h deleted file mode 100644 index 922f4e2..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/snmp.h +++ /dev/null @@ -1,35 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * SNMP - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_SNMP_H__ -#define __PGM_SNMP_H__ - -#include -#include - -PGM_BEGIN_DECLS - -bool pgm_snmp_init (pgm_error_t**) PGM_GNUC_WARN_UNUSED_RESULT; -bool pgm_snmp_shutdown (void); - -PGM_END_DECLS - -#endif /* __PGM_SNMP_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/socket.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/socket.h deleted file mode 100644 index f3a2360..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/socket.h +++ /dev/null @@ -1,170 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * PGM socket. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_SOCKET_H__ -#define __PGM_SOCKET_H__ - -typedef struct pgm_sock_t pgm_sock_t; -struct pgm_sockaddr_t; -struct pgm_addrinfo_t; -struct pgm_fecinto_t; - -#ifdef CONFIG_HAVE_POLL -# include -#endif -#ifdef CONFIG_HAVE_EPOLL -# include -#endif -#ifndef _WIN32 -# include -# include -#endif -#include -#include -#include -#include - -PGM_BEGIN_DECLS - -struct pgm_sockaddr_t { - uint16_t sa_port; /* data-destination port */ - pgm_tsi_t sa_addr; -}; - -struct pgm_addrinfo_t { - sa_family_t ai_family; - uint32_t ai_recv_addrs_len; - struct group_source_req* restrict ai_recv_addrs; - uint32_t ai_send_addrs_len; - struct group_source_req* restrict ai_send_addrs; -}; - -struct pgm_interface_req_t { - uint32_t ir_interface; - uint32_t ir_scope_id; -}; - -struct pgm_fecinfo_t { - uint8_t block_size; - uint8_t proactive_packets; - uint8_t group_size; - bool ondemand_parity_enabled; - bool var_pktlen_enabled; -}; - -struct pgm_pgmccinfo_t { - uint32_t ack_bo_ivl; - uint32_t ack_c; - uint32_t ack_c_p; -}; - -/* socket options */ -enum { - PGM_RECV_SOCK, - PGM_REPAIR_SOCK, - PGM_PENDING_SOCK, - PGM_ACK_SOCK, - PGM_TIME_REMAIN, - PGM_RATE_REMAIN, - PGM_IP_ROUTER_ALERT, - PGM_MTU, - PGM_MULTICAST_LOOP, - PGM_MULTICAST_HOPS, - PGM_TOS, - PGM_SNDBUF, - PGM_RCVBUF, - PGM_AMBIENT_SPM, - PGM_HEARTBEAT_SPM, - PGM_TXW_SQNS, - PGM_TXW_SECS, - PGM_TXW_MAX_RTE, - PGM_PEER_EXPIRY, - PGM_SPMR_EXPIRY, - PGM_RXW_SQNS, - PGM_RXW_SECS, - PGM_RXW_MAX_RTE, - PGM_NAK_BO_IVL, - PGM_NAK_RPT_IVL, - PGM_NAK_RDATA_IVL, - PGM_NAK_DATA_RETRIES, - PGM_NAK_NCF_RETRIES, - PGM_USE_FEC, - PGM_USE_CR, - PGM_USE_PGMCC, - PGM_SEND_ONLY, - PGM_RECV_ONLY, - PGM_PASSIVE, - PGM_ABORT_ON_RESET, - PGM_NOBLOCK, - PGM_SEND_GROUP, - PGM_JOIN_GROUP, - PGM_LEAVE_GROUP, - PGM_BLOCK_SOURCE, - PGM_UNBLOCK_SOURCE, - PGM_JOIN_SOURCE_GROUP, - PGM_LEAVE_SOURCE_GROUP, - PGM_MSFILTER, - PGM_UDP_ENCAP_UCAST_PORT, - PGM_UDP_ENCAP_MCAST_PORT -}; - -/* IO status */ -enum { - PGM_IO_STATUS_ERROR, /* an error occurred */ - PGM_IO_STATUS_NORMAL, /* success */ - PGM_IO_STATUS_RESET, /* session reset */ - PGM_IO_STATUS_FIN, /* session finished */ - PGM_IO_STATUS_EOF, /* socket closed */ - PGM_IO_STATUS_WOULD_BLOCK, /* resource temporarily unavailable */ - PGM_IO_STATUS_RATE_LIMITED, /* would-block on rate limit, check timer */ - PGM_IO_STATUS_TIMER_PENDING, /* would-block with pending timer */ - PGM_IO_STATUS_CONGESTION /* would-block waiting on ACK or timeout */ -}; - -bool pgm_socket (pgm_sock_t**restrict, const sa_family_t, const int, const int, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; -bool pgm_bind (pgm_sock_t*restrict, const struct pgm_sockaddr_t*const restrict, const socklen_t, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; -bool pgm_bind3 (pgm_sock_t*restrict, const struct pgm_sockaddr_t*const restrict, const socklen_t, const struct pgm_interface_req_t*const, const socklen_t, const struct pgm_interface_req_t*const, const socklen_t, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; -bool pgm_connect (pgm_sock_t*restrict, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; -bool pgm_close (pgm_sock_t*, bool); -bool pgm_setsockopt (pgm_sock_t*const restrict, const int, const void*restrict, const socklen_t); -bool pgm_getsockopt (pgm_sock_t*const restrict, const int, void*restrict, socklen_t*restrict); -bool pgm_getaddrinfo (const char*restrict, const struct pgm_addrinfo_t*restrict, struct pgm_addrinfo_t**restrict, pgm_error_t**restrict); -void pgm_freeaddrinfo (struct pgm_addrinfo_t*); -int pgm_send (pgm_sock_t*const restrict, const void*restrict, const size_t, size_t*restrict); -int pgm_sendv (pgm_sock_t*const restrict, const struct pgm_iovec*const restrict, const unsigned, const bool, size_t*restrict); -int pgm_send_skbv (pgm_sock_t*const restrict, struct pgm_sk_buff_t**restrict, const unsigned, const bool, size_t*restrict); -int pgm_recvmsg (pgm_sock_t*const restrict, struct pgm_msgv_t*const restrict, const int, size_t*restrict, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; -int pgm_recvmsgv (pgm_sock_t*const restrict, struct pgm_msgv_t*const restrict, const size_t, const int, size_t*restrict, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; -int pgm_recv (pgm_sock_t*const restrict, void*restrict, const size_t, const int, size_t*const restrict, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; -int pgm_recvfrom (pgm_sock_t*const restrict, void*restrict, const size_t, const int, size_t*restrict, struct pgm_sockaddr_t*restrict, socklen_t*restrict, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; - -bool pgm_getsockname (pgm_sock_t*const restrict, struct pgm_sockaddr_t*restrict, socklen_t*restrict); -int pgm_select_info (pgm_sock_t*const restrict, fd_set*const restrict, fd_set*const restrict, int*const restrict); -#ifdef CONFIG_HAVE_POLL -int pgm_poll_info (pgm_sock_t*const restrict, struct pollfd*const restrict, int*const restrict, const int); -#endif -#ifdef CONFIG_HAVE_EPOLL -int pgm_epoll_ctl (pgm_sock_t*const, const int, const int, const int); -#endif - -PGM_END_DECLS - -#endif /* __PGM_SOCKET_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/time.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/time.h deleted file mode 100644 index 2fd3898..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/time.h +++ /dev/null @@ -1,54 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * high resolution timers. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_TIME_H__ -#define __PGM_TIME_H__ - -#include - -PGM_BEGIN_DECLS - -typedef uint64_t pgm_time_t; -typedef void (*pgm_time_since_epoch_func)(const pgm_time_t*const restrict, time_t*restrict); - -#define pgm_to_secs(t) ((uint64_t)( (t) / 1000000UL )) -#define pgm_to_msecs(t) ((uint64_t)( (t) / 1000UL )) -#define pgm_to_usecs(t) ( (t) ) -#define pgm_to_nsecs(t) ((uint64_t)( (t) * 1000UL )) - -#define pgm_to_secsf(t) ( (double)(t) / 1000000.0 ) -#define pgm_to_msecsf(t) ( (double)(t) / 1000.0 ) -#define pgm_to_usecsf(t) ( (double)(t) ) -#define pgm_to_nsecsf(t) ( (double)(t) * 1000.0 ) - -#define pgm_secs(t) ((uint64_t)( (uint64_t)(t) * 1000000UL )) -#define pgm_msecs(t) ((uint64_t)( (uint64_t)(t) * 1000UL )) -#define pgm_usecs(t) ((uint64_t)( (t) )) -#define pgm_nsecs(t) ((uint64_t)( (t) / 1000UL )) - -#define PGM_TIME_FORMAT PRIu64 - -extern pgm_time_since_epoch_func pgm_time_since_epoch; - -PGM_END_DECLS - -#endif /* __PGM_TIME_H__ */ - diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/tsi.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/tsi.h deleted file mode 100644 index c33ea3d..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/tsi.h +++ /dev/null @@ -1,47 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * transport session ID helper functions - * - * Copyright (c) 2006-2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_TSI_H__ -#define __PGM_TSI_H__ - -typedef struct pgm_tsi_t pgm_tsi_t; - -#include -#include - -PGM_BEGIN_DECLS - -/* maximum length of TSI as a string */ -#define PGM_TSISTRLEN (sizeof("000.000.000.000.000.000.00000")) -#define PGM_TSI_INIT { PGM_GSI_INIT, 0 } - -struct pgm_tsi_t { - pgm_gsi_t gsi; /* global session identifier */ - uint16_t sport; /* source port: a random number to help detect session re-starts */ -}; - -char* pgm_tsi_print (const pgm_tsi_t*) PGM_GNUC_WARN_UNUSED_RESULT; -int pgm_tsi_print_r (const pgm_tsi_t*restrict, char*restrict, size_t); -bool pgm_tsi_equal (const void*restrict, const void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; - -PGM_END_DECLS - -#endif /* __PGM_TSI_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/types.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/types.h deleted file mode 100644 index 6c1c81b..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/types.h +++ /dev/null @@ -1,56 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * Cross-platform data types. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_TYPES_H__ -#define __PGM_TYPES_H__ - -#ifndef _MSC_VER -# include -#endif -#include - -#ifdef _WIN32 -# include -# define sa_family_t ULONG -#endif - -#ifdef _MSC_VER -# include -# define bool BOOL -# define ssize_t SSIZE_T -# define restrict -#elif !defined( __cplusplus) || (__GNUC__ >= 4) -/* g++ v4 handles C99 headers without complaints */ -# include -# include -#else -/* g++ v3 and other ancient compilers */ -# define bool int -# include -#endif - -PGM_BEGIN_DECLS - -/* nc */ - -PGM_END_DECLS - -#endif /* __PGM_TYPES_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/version.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/version.h deleted file mode 100644 index 7928450..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/version.h +++ /dev/null @@ -1,41 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * OpenPGM version. - * - * Copyright (c) 2006-2008 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_VERSION_H__ -#define __PGM_VERSION_H__ - -#include - -PGM_BEGIN_DECLS - -extern const unsigned pgm_major_version; -extern const unsigned pgm_minor_version; -extern const unsigned pgm_micro_version; - -extern const char* pgm_build_date; -extern const char* pgm_build_time; -extern const char* pgm_build_system; -extern const char* pgm_build_machine; -extern const char* pgm_build_revision; - -PGM_END_DECLS - -#endif /* __PGM_VERSION_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/winint.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/winint.h deleted file mode 100644 index 0205cdf..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/winint.h +++ /dev/null @@ -1,198 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * stdint.h for Win32 & Win64 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_WININT_H__ -#define __PGM_WININT_H__ - -#include - -/* 7.18.1.1 Exact-width integer types */ -typedef signed __int8 int8_t; -typedef unsigned __int8 uint8_t; -typedef signed __int16 int16_t; -typedef unsigned __int16 uint16_t; -typedef signed __int32 int32_t; -typedef unsigned __int32 uint32_t; -typedef signed __int64 int64_t; -typedef unsigned __int64 uint64_t; - -/* 7.18.1.2 Minimum-width integer types */ -typedef int8_t int_least8_t; -typedef uint8_t uint_least8_t; -typedef int16_t int_least16_t; -typedef uint16_t uint_least16_t; -typedef int32_t int_least32_t; -typedef uint32_t uint_least32_t; -typedef int64_t int_least64_t; -typedef uint64_t uint_least64_t; - -/* 7.18.1.3 Fastest minimum-width integer types - * Not actually guaranteed to be fastest for all purposes - * Here we use the exact-width types for 8 and 16-bit ints. - */ -typedef int8_t int_fast8_t; -typedef uint8_t uint_fast8_t; -typedef int16_t int_fast16_t; -typedef uint16_t uint_fast16_t; -typedef int32_t int_fast32_t; -typedef uint32_t uint_fast32_t; -typedef int64_t int_fast64_t; -typedef uint64_t uint_fast64_t; - -/* 7.18.1.5 Greatest-width integer types */ -typedef int64_t intmax_t; -typedef uint64_t uintmax_t; - -/* 7.18.2 Limits of specified-width integer types */ -#if !defined ( __cplusplus) || defined (__STDC_LIMIT_MACROS) - -/* 7.18.2.1 Limits of exact-width integer types */ -#define INT8_MIN ((int8_t)_I8_MIN) -#define INT8_MAX _I8_MAX -#define INT16_MIN ((int16_t)_I16_MIN) -#define INT16_MAX _I16_MAX -#define INT32_MIN ((int32_t)_I32_MIN) -#define INT32_MAX _I32_MAX -#define INT64_MIN ((int64_t)_I64_MIN) -#define INT64_MAX _I64_MAX -#define UINT8_MAX _UI8_MAX -#define UINT16_MAX _UI16_MAX -#define UINT32_MAX _UI32_MAX -#define UINT64_MAX _UI64_MAX - -/* 7.18.2.2 Limits of minimum-width integer types */ -#define INT_LEAST8_MIN INT8_MIN -#define INT_LEAST16_MIN INT16_MIN -#define INT_LEAST32_MIN INT32_MIN -#define INT_LEAST64_MIN INT64_MIN - -#define INT_LEAST8_MAX INT8_MAX -#define INT_LEAST16_MAX INT16_MAX -#define INT_LEAST32_MAX INT32_MAX -#define INT_LEAST64_MAX INT64_MAX - -#define UINT_LEAST8_MAX UINT8_MAX -#define UINT_LEAST16_MAX UINT16_MAX -#define UINT_LEAST32_MAX UINT32_MAX -#define UINT_LEAST64_MAX UINT64_MAX - -/* 7.18.2.3 Limits of fastest minimum-width integer types */ -#define INT_FAST8_MIN INT8_MIN -#define INT_FAST16_MIN INT16_MIN -#define INT_FAST32_MIN INT32_MIN -#define INT_FAST64_MIN INT64_MIN - -#define INT_FAST8_MAX INT8_MAX -#define INT_FAST16_MAX INT16_MAX -#define INT_FAST32_MAX INT32_MAX -#define INT_FAST64_MAX INT64_MAX - -#define UINT_FAST8_MAX UINT8_MAX -#define UINT_FAST16_MAX UINT16_MAX -#define UINT_FAST32_MAX UINT32_MAX -#define UINT_FAST64_MAX UINT64_MAX - -/* 7.18.2.4 Limits of integer types capable of holding - object pointers */ -#ifdef _WIN64 -# define INTPTR_MIN INT64_MIN -# define INTPTR_MAX INT64_MAX -# define UINTPTR_MAX UINT64_MAX -#else -# define INTPTR_MIN INT32_MIN -# define INTPTR_MAX INT32_MAX -# define UINTPTR_MAX UINT32_MAX -#endif - -/* 7.18.2.5 Limits of greatest-width integer types */ -#define INTMAX_MIN INT64_MIN -#define INTMAX_MAX INT64_MAX -#define UINTMAX_MAX UINT64_MAX - -/* 7.18.3 Limits of other integer types */ -#ifdef _WIN64 -# define PTRDIFF_MIN INT64_MIN -# define PTRDIFF_MAX INT64_MAX -#else -# define PTRDIFF_MIN INT32_MIN -# define PTRDIFF_MAX INT32_MAX -#endif - -#define SIG_ATOMIC_MIN INT_MIN -#define SIG_ATOMIC_MAX INT_MAX - -#ifndef SIZE_MAX -# ifdef _WIN64 -# define SIZE_MAX UINT64_MAX -# else -# define SIZE_MAX UINT32_MAX -# endif -#endif - -#ifndef WCHAR_MIN /* also in wchar.h */ -# define WCHAR_MIN 0U -# define WCHAR_MAX UINT16_MAX -#endif - -/* - * wint_t is unsigned short for compatibility with MS runtime - */ -#define WINT_MIN 0U -#define WINT_MAX UINT16_MAX - -#endif /* !defined ( __cplusplus) || defined __STDC_LIMIT_MACROS */ - - -/* 7.18.4 Macros for integer constants */ -#if !defined ( __cplusplus) || defined (__STDC_CONSTANT_MACROS) - -/* 7.18.4.1 Macros for minimum-width integer constants - - Accoding to Douglas Gwyn : - "This spec was changed in ISO/IEC 9899:1999 TC1; in ISO/IEC - 9899:1999 as initially published, the expansion was required - to be an integer constant of precisely matching type, which - is impossible to accomplish for the shorter types on most - platforms, because C99 provides no standard way to designate - an integer constant with width less than that of type int. - TC1 changed this to require just an integer constant - *expression* with *promoted* type." - - The trick used here is from Clive D W Feather. -*/ - -#define INT8_C(val) (INT_LEAST8_MAX-INT_LEAST8_MAX+(val)) -#define INT16_C(val) (INT_LEAST16_MAX-INT_LEAST16_MAX+(val)) -#define INT32_C(val) (INT_LEAST32_MAX-INT_LEAST32_MAX+(val)) -/* The 'trick' doesn't work in C89 for long long because, without - suffix, (val) will be evaluated as int, not intmax_t */ -#define INT64_C(val) val##LL - -#define UINT8_C(val) (val) -#define UINT16_C(val) (val) -#define UINT32_C(val) (val##U) -#define UINT64_C(val) val##ULL - -/* 7.18.4.2 Macros for greatest-width integer constants */ -#define INTMAX_C(val) val##LL -#define UINTMAX_C(val) val##ULL - -#endif /* !defined ( __cplusplus) || defined __STDC_CONSTANT_MACROS */ - -#endif /* __PGM_WININT_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/wininttypes.h b/3rdparty/openpgm-svn-r1085/pgm/include/pgm/wininttypes.h deleted file mode 100644 index 5daa7f1..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/include/pgm/wininttypes.h +++ /dev/null @@ -1,254 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * inttypes.h for Win32 & Win64 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_WININTTYPES_H__ -#define __PGM_WININTTYPES_H__ - -#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) - -/* 7.8.1 Macros for format specifiers - * - * MS runtime does not yet understand C9x standard "ll" - * length specifier. It appears to treat "ll" as "l". - * The non-standard I64 length specifier causes warning in GCC, - * but understood by MS runtime functions. - */ - -/* fprintf macros for signed types */ -#define PRId8 "d" -#define PRId16 "hd" -#define PRId32 "I32d" -#define PRId64 "I64d" - -#define PRIdLEAST8 "d" -#define PRIdLEAST16 "hd" -#define PRIdLEAST32 "I32d" -#define PRIdLEAST64 "I64d" - -#define PRIdFAST8 "d" -#define PRIdFAST16 "hd" -#define PRIdFAST32 "I32d" -#define PRIdFAST64 "I64d" - -#define PRIdMAX "I64d" - -#define PRIi8 "i" -#define PRIi16 "hi" -#define PRIi32 "i" -#define PRIi64 "I64i" - -#define PRIiLEAST8 "i" -#define PRIiLEAST16 "hi" -#define PRIiLEAST32 "I32i" -#define PRIiLEAST64 "I64i" - -#define PRIiFAST8 "i" -#define PRIiFAST16 "hi" -#define PRIiFAST32 "I32i" -#define PRIiFAST64 "I64i" - -#define PRIiMAX "I64i" - -#define PRIo8 "o" -#define PRIo16 "ho" -#define PRIo32 "I32o" -#define PRIo64 "I64o" - -#define PRIoLEAST8 "o" -#define PRIoLEAST16 "ho" -#define PRIoLEAST32 "I32o" -#define PRIoLEAST64 "I64o" - -#define PRIoFAST8 "o" -#define PRIoFAST16 "ho" -#define PRIoFAST32 "o" -#define PRIoFAST64 "I64o" - -#define PRIoMAX "I64o" - -/* fprintf macros for unsigned types */ -#define PRIu8 "u" -#define PRIu16 "hu" -#define PRIu32 "I32u" -#define PRIu64 "I64u" - -#define PRIuLEAST8 "u" -#define PRIuLEAST16 "hu" -#define PRIuLEAST32 "I32u" -#define PRIuLEAST64 "I64u" - -#define PRIuFAST8 "u" -#define PRIuFAST16 "hu" -#define PRIuFAST32 "I32u" -#define PRIuFAST64 "I64u" - -#define PRIuMAX "I64u" - -#define PRIx8 "x" -#define PRIx16 "hx" -#define PRIx32 "I32x" -#define PRIx64 "I64x" - -#define PRIxLEAST8 "x" -#define PRIxLEAST16 "hx" -#define PRIxLEAST32 "I32x" -#define PRIxLEAST64 "I64x" - -#define PRIxFAST8 "x" -#define PRIxFAST16 "hx" -#define PRIxFAST32 "I32x" -#define PRIxFAST64 "I64x" - -#define PRIxMAX "I64x" - -#define PRIX8 "X" -#define PRIX16 "hX" -#define PRIX32 "I32X" -#define PRIX64 "I64X" - -#define PRIXLEAST8 "X" -#define PRIXLEAST16 "hX" -#define PRIXLEAST32 "I32X" -#define PRIXLEAST64 "I64X" - -#define PRIXFAST8 "X" -#define PRIXFAST16 "hX" -#define PRIXFAST32 "I32X" -#define PRIXFAST64 "I64X" - -#define PRIXMAX "I64X" - -/* fscanf macros for signed int types */ - -#define SCNd8 "hhd" -#define SCNd16 "hd" -#define SCNd32 "ld" -#define SCNd64 "I64d" - -#define SCNdLEAST8 "hhd" -#define SCNdLEAST16 "hd" -#define SCNdLEAST32 "ld" -#define SCNdLEAST64 "I64d" - -#define SCNdFAST8 "hhd" -#define SCNdFAST16 "hd" -#define SCNdFAST32 "ld" -#define SCNdFAST64 "I64d" - -#define SCNdMAX "I64d" - -#define SCNi8 "hhi" -#define SCNi16 "hi" -#define SCNi32 "li" -#define SCNi64 "I64i" - -#define SCNiLEAST8 "hhi" -#define SCNiLEAST16 "hi" -#define SCNiLEAST32 "li" -#define SCNiLEAST64 "I64i" - -#define SCNiFAST8 "hhi" -#define SCNiFAST16 "hi" -#define SCNiFAST32 "li" -#define SCNiFAST64 "I64i" - -#define SCNiMAX "I64i" - -#define SCNo8 "hho" -#define SCNo16 "ho" -#define SCNo32 "lo" -#define SCNo64 "I64o" - -#define SCNoLEAST8 "hho" -#define SCNoLEAST16 "ho" -#define SCNoLEAST32 "lo" -#define SCNoLEAST64 "I64o" - -#define SCNoFAST8 "hho" -#define SCNoFAST16 "ho" -#define SCNoFAST32 "lo" -#define SCNoFAST64 "I64o" - -#define SCNoMAX "I64o" - -#define SCNx8 "hhx" -#define SCNx16 "hx" -#define SCNx32 "lx" -#define SCNx64 "I64x" - -#define SCNxLEAST8 "hhx" -#define SCNxLEAST16 "hx" -#define SCNxLEAST32 "lx" -#define SCNxLEAST64 "I64x" - -#define SCNxFAST8 "hhx" -#define SCNxFAST16 "hx" -#define SCNxFAST32 "lx" -#define SCNxFAST64 "I64x" - -#define SCNxMAX "I64x" - -/* fscanf macros for unsigned int types */ - -#define SCNu8 "hhu" -#define SCNu16 "hu" -#define SCNu32 "lu" -#define SCNu64 "I64u" - -#define SCNuLEAST8 "hhu" -#define SCNuLEAST16 "hu" -#define SCNuLEAST32 "lu" -#define SCNuLEAST64 "I64u" - -#define SCNuFAST8 "hhu" -#define SCNuFAST16 "hu" -#define SCNuFAST32 "lu" -#define SCNuFAST64 "I64u" - -#define SCNuMAX "I64u" - -#ifdef _WIN64 -# define PRIdPTR "I64d" -# define PRIiPTR "I64i" -# define PRIoPTR "I64o" -# define PRIuPTR "I64u" -# define PRIxPTR "I64x" -# define PRIXPTR "I64X" -# define SCNdPTR "I64d" -# define SCNiPTR "I64i" -# define SCNoPTR "I64o" -# define SCNxPTR "I64x" -# define SCNuPTR "I64u" -#else -# define PRIdPTR "ld" -# define PRIiPTR "li" -# define PRIoPTR "lo" -# define PRIuPTR "lu" -# define PRIxPTR "lx" -# define PRIXPTR "lX" -# define SCNdPTR "ld" -# define SCNiPTR "li" -# define SCNoPTR "lo" -# define SCNxPTR "lx" -# define SCNuPTR "lu" -#endif - -#endif /* !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) */ - -#endif /* __PGM_WININTTYPES_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/indextoaddr.c b/3rdparty/openpgm-svn-r1085/pgm/indextoaddr.c deleted file mode 100644 index 479dffd..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/indextoaddr.c +++ /dev/null @@ -1,98 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable interface index to socket address function. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include - - -//#define INDEXTOADDR_DEBUG - - -/* interfaces indexes refer to the link layer, we want to find the internet layer address. - * the big problem is that multiple IPv6 addresses can be bound to one link - called scopes. - * we can just pick the first scope and let IP routing handle the rest. - */ - -bool -pgm_if_indextoaddr ( - const unsigned ifindex, - const sa_family_t iffamily, - const uint32_t ifscope, - struct sockaddr* restrict ifsa, - pgm_error_t** restrict error - ) -{ - pgm_return_val_if_fail (NULL != ifsa, FALSE); - - if (0 == ifindex) /* any interface or address */ - { - ifsa->sa_family = iffamily; - switch (iffamily) { - case AF_INET: - ((struct sockaddr_in*)ifsa)->sin_addr.s_addr = INADDR_ANY; - break; - - case AF_INET6: - ((struct sockaddr_in6*)ifsa)->sin6_addr = in6addr_any; - break; - - default: - pgm_return_val_if_reached (FALSE); - break; - } - return TRUE; - } - - struct pgm_ifaddrs_t *ifap, *ifa; - if (!pgm_getifaddrs (&ifap, error)) { - pgm_prefix_error (error, - _("Enumerating network interfaces: ")); - return FALSE; - } - - for (ifa = ifap; ifa; ifa = ifa->ifa_next) - { - if (NULL == ifa->ifa_addr || - ifa->ifa_addr->sa_family != iffamily) - continue; - - const unsigned i = pgm_if_nametoindex (iffamily, ifa->ifa_name); - pgm_assert (0 != i); - if (i == ifindex) - { - if (ifscope && ifscope != pgm_sockaddr_scope_id (ifa->ifa_addr)) - continue; - memcpy (ifsa, ifa->ifa_addr, pgm_sockaddr_len(ifa->ifa_addr)); - pgm_freeifaddrs (ifap); - return TRUE; - } - } - - pgm_set_error (error, - PGM_ERROR_DOMAIN_IF, - PGM_ERROR_NODEV, - _("No matching network interface index: %i"), - ifindex); - pgm_freeifaddrs (ifap); - return FALSE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/indextoaddr_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/indextoaddr_unittest.c deleted file mode 100644 index 57222d1..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/indextoaddr_unittest.c +++ /dev/null @@ -1,302 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for portable interface index to socket address function. - * - * Copyright (c) 2009-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* IFF_UP */ -#define _BSD_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - - -/* mock state */ - -struct mock_interface_t { - unsigned int index; - char* name; - unsigned int flags; - struct sockaddr_storage addr; - struct sockaddr_storage netmask; -}; - -static GList *mock_interfaces = NULL; - -struct pgm_ifaddrs_t; -struct pgm_error_t; - -static bool mock_pgm_getifaddrs (struct pgm_ifaddrs_t**, struct pgm_error_t**); -static void mock_pgm_freeifaddrs (struct pgm_ifaddrs_t*); -static unsigned mock_pgm_if_nametoindex (const sa_family_t, const char*); - - -#define pgm_getifaddrs mock_pgm_getifaddrs -#define pgm_freeifaddrs mock_pgm_freeifaddrs -#define pgm_if_nametoindex mock_pgm_if_nametoindex - - -#define INDEXTOADDR_DEBUG -#include "indextoaddr.c" - - -static -gpointer -create_interface ( - const unsigned index, - const char* name, - const char* flags - ) -{ - struct mock_interface_t* new_interface; - - g_assert (name); - g_assert (flags); - - new_interface = g_slice_alloc0 (sizeof(struct mock_interface_t)); - new_interface->index = index; - new_interface->name = g_strdup (name); - - struct sockaddr_in* sin = (gpointer)&new_interface->addr; - struct sockaddr_in6* sin6 = (gpointer)&new_interface->addr; - - gchar** tokens = g_strsplit (flags, ",", 0); - for (guint i = 0; tokens[i]; i++) - { - if (strcmp (tokens[i], "up") == 0) - new_interface->flags |= IFF_UP; - else if (strcmp (tokens[i], "down") == 0) - new_interface->flags |= 0; - else if (strcmp (tokens[i], "loop") == 0) - new_interface->flags |= IFF_LOOPBACK; - else if (strcmp (tokens[i], "broadcast") == 0) - new_interface->flags |= IFF_BROADCAST; - else if (strcmp (tokens[i], "multicast") == 0) - new_interface->flags |= IFF_MULTICAST; - else if (strncmp (tokens[i], "ip=", strlen("ip=")) == 0) { - const char* addr = tokens[i] + strlen("ip="); - g_assert (pgm_sockaddr_pton (addr, (struct sockaddr*)&new_interface->addr)); - } - else if (strncmp (tokens[i], "netmask=", strlen("netmask=")) == 0) { - const char* addr = tokens[i] + strlen("netmask="); - g_assert (pgm_sockaddr_pton (addr, (struct sockaddr*)&new_interface->netmask)); - } - else if (strncmp (tokens[i], "scope=", strlen("scope=")) == 0) { - const char* scope = tokens[i] + strlen("scope="); - g_assert (AF_INET6 == ((struct sockaddr*)&new_interface->addr)->sa_family); - ((struct sockaddr_in6*)&new_interface->addr)->sin6_scope_id = atoi (scope); - } - else - g_error ("parsing failed for flag \"%s\"", tokens[i]); - } - - g_strfreev (tokens); - return new_interface; -} - -#define APPEND_INTERFACE(a,b,c) \ - do { \ - gpointer data = create_interface ((a), (b), (c)); \ - g_assert (data); \ - mock_interfaces = g_list_append (mock_interfaces, data); \ - g_assert (mock_interfaces); g_assert (mock_interfaces->data); \ - } while (0) -static -void -mock_setup_net (void) -{ - APPEND_INTERFACE( 1, "lo", "up,loop"); - APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast"); - APPEND_INTERFACE( 3, "eth1", "down,broadcast,multicast"); - APPEND_INTERFACE( 1, "lo", "up,loop,ip=127.0.0.1,netmask=255.0.0.0"); - APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=10.6.28.33,netmask=255.255.255.0"); - APPEND_INTERFACE( 1, "lo", "up,loop,ip=::1,netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,scope=0"); - APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=2002:dce8:d28e::33,netmask=ffff:ffff:ffff:ffff::0,scope=0"); - APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=fe80::214:5eff:febd:6dda,netmask=ffff:ffff:ffff:ffff::0,scope=2"); -} - -static -void -mock_teardown_net (void) -{ - GList* list; - - list = mock_interfaces; - while (list) { - struct mock_interface_t* interface = list->data; - g_free (interface->name); - g_slice_free1 (sizeof(struct mock_interface_t), interface); - list = list->next; - } - g_list_free (mock_interfaces); -} - -/* mock functions for external references */ - -size_t -pgm_transport_pkt_offset2 ( - const bool can_fragment, - const bool use_pgmcc - ) -{ - return 0; -} - -bool -mock_pgm_getifaddrs ( - struct pgm_ifaddrs_t** ifap, - pgm_error_t** err - ) -{ - if (NULL == ifap) { - return FALSE; - } - - g_debug ("mock_getifaddrs (ifap:%p err:%p)", (gpointer)ifap, (gpointer)err); - - GList* list = mock_interfaces; - int n = g_list_length (list); - struct pgm_ifaddrs_t* ifa = calloc (n, sizeof(struct pgm_ifaddrs_t)); - struct pgm_ifaddrs_t* ift = ifa; - while (list) { - struct mock_interface_t* interface = list->data; - ift->ifa_addr = (gpointer)&interface->addr; - ift->ifa_name = interface->name; - ift->ifa_flags = interface->flags; - ift->ifa_netmask = (gpointer)&interface->netmask; - list = list->next; - if (list) { - ift->ifa_next = ift + 1; - ift = ift->ifa_next; - } - } - - *ifap = ifa; - return TRUE; -} - -static -void -mock_pgm_freeifaddrs ( - struct pgm_ifaddrs_t* ifa - ) -{ - g_debug ("mock_freeifaddrs (ifa:%p)", (gpointer)ifa); - free (ifa); -} - -unsigned -mock_pgm_if_nametoindex ( - const sa_family_t iffamily, - const char* ifname - ) -{ - GList* list = mock_interfaces; - while (list) { - const struct mock_interface_t* interface = list->data; - if (0 == strcmp (ifname, interface->name)) - return interface->index; - list = list->next; - } - return 0; -} - - -/* target: - * bool - * pgm_if_indextoaddr ( - * const unsigned ifindex, - * const sa_family_t iffamily, - * const uint32_t ifscope, - * struct sockaddr* ifsa, - * pgm_error_t** error - * ) - */ - -START_TEST (test_indextoaddr_pass_001) -{ - char saddr[INET6_ADDRSTRLEN]; - struct sockaddr_storage addr; - pgm_error_t* err = NULL; - const unsigned int ifindex = 2; - fail_unless (TRUE == pgm_if_indextoaddr (ifindex, AF_INET, 0, (struct sockaddr*)&addr, &err)); - pgm_sockaddr_ntop ((struct sockaddr*)&addr, saddr, sizeof(saddr)); - g_message ("index:%d -> %s", - ifindex, saddr); - fail_unless (TRUE == pgm_if_indextoaddr (ifindex, AF_INET6, 0, (struct sockaddr*)&addr, &err)); - pgm_sockaddr_ntop ((struct sockaddr*)&addr, saddr, sizeof(saddr)); - g_message ("index:%d -> %s", - ifindex, saddr); -} -END_TEST - -START_TEST (test_indextoaddr_fail_001) -{ - pgm_error_t* err = NULL; - fail_unless (FALSE == pgm_if_indextoaddr (0, 0, 0, NULL, &err)); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_indextoaddr = tcase_create ("init-ctx"); - suite_add_tcase (s, tc_indextoaddr); - tcase_add_checked_fixture (tc_indextoaddr, mock_setup_net, mock_teardown_net); - tcase_add_test (tc_indextoaddr, test_indextoaddr_pass_001); - tcase_add_test (tc_indextoaddr, test_indextoaddr_fail_001); - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/indextoname.c b/3rdparty/openpgm-svn-r1085/pgm/indextoname.c deleted file mode 100644 index c4043ef..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/indextoname.c +++ /dev/null @@ -1,52 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Windows interface index to interface name function. - * - * Copyright (c) 2006-2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifdef _WIN32 -# include -# include -#endif -#include - - -//#define INDEXTONAME_DEBUG - - -char* -pgm_if_indextoname ( - unsigned int ifindex, - char* ifname - ) -{ -#ifndef _WIN32 - return if_indextoname (ifindex, ifname); -#else - pgm_return_val_if_fail (NULL != ifname, NULL); - - MIB_IFROW ifRow = { .dwIndex = ifindex }; - const DWORD dwRetval = GetIfEntry (&ifRow); - if (NO_ERROR != dwRetval) - return NULL; - strcpy (ifname, (char*)ifRow.wszName); - return ifname; -#endif /* _WIN32 */ -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/inet_network.c b/3rdparty/openpgm-svn-r1085/pgm/inet_network.c deleted file mode 100644 index 8cac34b..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/inet_network.c +++ /dev/null @@ -1,237 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable implementations of inet_network and inet_network6. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include - - -//#define INET_NETWORK_DEBUG - -/* locals */ - -static uint32_t cidr_to_netmask (const unsigned) PGM_GNUC_CONST; - - -/* calculate IPv4 netmask from network size, returns address in - * host byte order. - */ - -static -uint32_t -cidr_to_netmask ( - const unsigned cidr - ) -{ - return (cidr == 0) ? 0 : (0xffffffff - (1 << (32 - cidr)) + 1); -} - - -/* Converts a numbers-and-dots notation string into a network number in - * host order. - * Note parameters and return value differs from inet_network(). This - * function will not interpret octal numbers, preceeded with a 0, or - * hexadecimal numbers, preceeded by 0x. - * - * 127 => 127.0.0.0 - * 127.1/8 => 127.0.0.0 -- 127.1.0.0 - * inet_addr() would be 127.0.0.1 - * inet_network() would be 0.0.127.1 - * - * returns 0 on success, returns -1 on invalid address. - */ - -int /* return type to match inet_network() */ -pgm_inet_network ( - const char* restrict s, - struct in_addr* restrict in - ) -{ - pgm_return_val_if_fail (NULL != s, -1); - pgm_return_val_if_fail (NULL != in, -1); - - pgm_debug ("pgm_inet_network (s:\"%s\" in:%p)", - s, (const void*)in); - - const char *p = s; - unsigned val = 0; - int shift = 24; - - in->s_addr = INADDR_ANY; - - while (*p) - { - if (isdigit (*p)) { - val = 10 * val + (*p - '0'); - } else if (*p == '.' || *p == 0) { - if (val > 0xff) { - in->s_addr = INADDR_NONE; - return -1; - } - -//g_trace ("elem %i", val); - - in->s_addr |= val << shift; - val = 0; - shift -= 8; - if (shift < 0 && *p != 0) { - in->s_addr = INADDR_NONE; - return -1; - } - - } else if (*p == '/') { - if (val > 0xff) { - in->s_addr = INADDR_NONE; - return -1; - } -//g_trace ("elem %i", val); - in->s_addr |= val << shift; - p++; val = 0; - while (*p) - { - if (isdigit (*p)) { - val = 10 * val + (*p - '0'); - } else { - in->s_addr = INADDR_NONE; - return -1; - } - p++; - } - if (val == 0 || val > 32) { - in->s_addr = INADDR_NONE; - return -1; - } -//g_trace ("bit mask %i", val); - -/* zero out host bits */ - const struct in_addr netaddr = { .s_addr = cidr_to_netmask (val) }; -#ifdef INET_NETWORK_DEBUG -{ -g_debug ("netaddr %s", inet_ntoa (netaddr)); -} -#endif - in->s_addr &= netaddr.s_addr; - return 0; - - } else if (*p == 'x' || *p == 'X') { /* skip number, e.g. 1.x.x.x */ - if (val > 0) { - in->s_addr = INADDR_NONE; - return -1; - } - - } else { - in->s_addr = INADDR_NONE; - return -1; - } - p++; - } - - in->s_addr |= val << shift; - return 0; -} - -/* Converts a numbers-and-dots notation string into an IPv6 network number. - * - * ::1/128 => 0:0:0:0:0:0:0:1 - * ::1 => 0:0:0:0:0:0:0:1 - * ::1.2.3.4 => 0:0:0:0:1.2.3.4 - * - * returns 0 on success, returns -1 on invalid address. - */ - -int -pgm_inet6_network ( - const char* restrict s, /* NULL terminated */ - struct in6_addr* restrict in6 - ) -{ - pgm_return_val_if_fail (NULL != s, -1); - pgm_return_val_if_fail (NULL != in6, -1); - - pgm_debug ("pgm_inet6_network (s:\"%s\" in6:%p)", - s, (const void*)in6); - -/* inet_pton cannot parse IPv6 addresses with subnet declarations, so - * chop them off. - * - * as we are dealing with network addresses IPv6 zone indices are not important - * so we can use the inet_xtoy functions. - */ - char s2[INET6_ADDRSTRLEN]; - const char *p = s; - char* p2 = s2; - while (*p) { - if (*p == '/') break; - *p2++ = *p++; - } - if (*p == 0) { - if (pgm_inet_pton (AF_INET6, s, in6)) return 0; - pgm_debug ("pgm_inet_pton(AF_INET6) failed on '%s'", s); - memcpy (in6, &in6addr_any, sizeof(in6addr_any)); - return -1; - } - - *p2 = 0; - pgm_debug ("net part %s", s2); - if (!pgm_inet_pton (AF_INET6, s2, in6)) { - pgm_debug ("pgm_inet_pton(AF_INET) failed parsing network part '%s'", s2); - memcpy (in6, &in6addr_any, sizeof(in6addr_any)); - return -1; - } - -#ifdef INET_NETWORK_DEBUG - char sdebug[INET6_ADDRSTRLEN]; - pgm_debug ("IPv6 network address: %s", pgm_inet_ntop(AF_INET6, in6, sdebug, sizeof(sdebug))); -#endif - - p++; - unsigned val = 0; - while (*p) - { - if (isdigit(*p)) { - val = 10 * val + (*p - '0'); - } else { - pgm_debug ("failed parsing subnet size due to character '%c'", *p); - memcpy (in6, &in6addr_any, sizeof(in6addr_any)); - return -1; - } - p++; - } - if (val == 0 || val > 128) { - pgm_debug ("subnet size invalid (%d)", val); - memcpy (in6, &in6addr_any, sizeof(in6addr_any)); - return -1; - } - pgm_debug ("subnet size %i", val); - -/* zero out host bits */ - const unsigned suffix_length = 128 - val; - for (int i = suffix_length, j = 15; i > 0; i -= 8, --j) - { - in6->s6_addr[ j ] &= i >= 8 ? 0x00 : (unsigned)(( 0xffU << i ) & 0xffU ); - } - - pgm_debug ("effective IPv6 network address after subnet mask: %s", pgm_inet_ntop(AF_INET6, in6, s2, sizeof(s2))); - - return 0; -} - -/* eof */ - diff --git a/3rdparty/openpgm-svn-r1085/pgm/inet_network_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/inet_network_unittest.c deleted file mode 100644 index 2739215..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/inet_network_unittest.c +++ /dev/null @@ -1,203 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for portable implementations of inet_network and inet_network6. - * - * Copyright (c) 2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - - -/* mock state */ - -/* mock functions for external references */ - -size_t -pgm_transport_pkt_offset2 ( - const bool can_fragment, - const bool use_pgmcc - ) -{ - return 0; -} - - -#define INET_NETWORK_DEBUG -#include "inet_network.c" - - -/* target: - * int - * pgm_inet_network ( - * const char* s, - * struct in_addr* in -- in host byte order - * ) - */ - -struct test_case_t { - const char* network; - const char* answer; -}; - -static const struct test_case_t cases_001[] = { - { "127", "127.0.0.0" }, /* different to inet_addr/inet_network */ - { "127/8", "127.0.0.0" }, - { "127.1/8", "127.0.0.0" }, - { "127.1", "127.1.0.0" }, /* different to inet_addr/inet_network */ - { "127.x.x.x", "127.0.0.0" }, - { "127.X.X.X", "127.0.0.0" }, - { "127.0.0.0", "127.0.0.0" }, - { "127.0.0.1/8", "127.0.0.0" }, - { "127.0.0.1/32", "127.0.0.1" }, - { "10.0.0.0/8", "10.0.0.0" }, /* RFC1918 class A */ - { "10.255.255.255/8", "10.0.0.0" }, - { "172.16.0.0/12", "172.16.0.0" }, /* RFC1918 class B */ - { "172.31.255.255/12", "172.16.0.0" }, - { "192.168.0.0/16", "192.168.0.0" }, /* RFC1918 class C */ - { "192.168.255.255/16", "192.168.0.0" }, - { "169.254.0.0/16", "169.254.0.0" }, /* RFC3927 link-local */ - { "192.88.99.0/24", "192.88.99.0" }, /* RFC3068 6to4 relay anycast */ - { "224.0.0.0/4", "224.0.0.0" }, /* RFC3171 multicast */ - { "0.0.0.0", "0.0.0.0" }, - { "255.255.255.255", "255.255.255.255" }, -}; - -START_TEST (test_inet_network_pass_001) -{ - const char* network = cases_001[_i].network; - const char* answer = cases_001[_i].answer; - - struct in_addr host_order, network_order; - fail_unless (0 == pgm_inet_network (network, &host_order)); - network_order.s_addr = g_htonl (host_order.s_addr); - - g_message ("Resolved \"%s\" to \"%s\"", - network, inet_ntoa (network_order)); - -{ -struct in_addr t = { .s_addr = g_htonl (inet_network (network)) }; -g_message ("inet_network (%s) = %s", network, inet_ntoa (t)); -} - - fail_unless (0 == strcmp (answer, inet_ntoa (network_order))); -} -END_TEST - -START_TEST (test_inet_network_fail_001) -{ - fail_unless (-1 == pgm_inet_network (NULL, NULL)); -} -END_TEST - -START_TEST (test_inet_network_fail_002) -{ - const char* network = "192.168.0.1/0"; - - struct in_addr host_order; - fail_unless (-1 == pgm_inet_network (network, &host_order)); -} -END_TEST - -/* target: - * int - * pgm_inet6_network ( - * const char* s, - * struct in6_addr* in6 - * ) - */ - -static const struct test_case_t cases6_001[] = { - { "::1/128", "::1" }, - { "2002:dec8:d28e::36/64", "2002:dec8:d28e::" }, /* 6to4 */ - { "fe80::203:baff:fe4e:6cc8/10", "fe80::" }, /* link-local */ - { "ff02::1/8", "ff00::" }, /* multicast */ - { "fc00:6::61/7", "fc00::" }, /* ULA */ -}; - -START_TEST (test_inet6_network_pass_001) -{ - const char* network = cases6_001[_i].network; - const char* answer = cases6_001[_i].answer; - - char snetwork[INET6_ADDRSTRLEN]; - struct in6_addr addr; - fail_unless (0 == pgm_inet6_network (network, &addr)); - g_message ("Resolved \"%s\" to \"%s\"", - network, pgm_inet_ntop (AF_INET6, &addr, snetwork, sizeof(snetwork))); - - fail_unless (0 == strcmp (answer, snetwork)); -} -END_TEST - -START_TEST (test_inet6_network_fail_001) -{ - fail_unless (-1 == pgm_inet6_network (NULL, NULL)); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_inet_network = tcase_create ("inet-network"); - suite_add_tcase (s, tc_inet_network); - tcase_add_loop_test (tc_inet_network, test_inet_network_pass_001, 0, G_N_ELEMENTS(cases_001)); - tcase_add_test (tc_inet_network, test_inet_network_fail_001); - - TCase* tc_inet6_network = tcase_create ("inet6-network"); - suite_add_tcase (s, tc_inet6_network); - tcase_add_loop_test (tc_inet6_network, test_inet6_network_pass_001, 0, G_N_ELEMENTS(cases6_001)); - tcase_add_test (tc_inet6_network, test_inet6_network_fail_001); - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/ip_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/ip_unittest.c deleted file mode 100644 index 48bb601..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/ip_unittest.c +++ /dev/null @@ -1,362 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for ip stack. - * - * Copyright (c) 2009-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - - -/* getsockopt(3SOCKET) - * level is the protocol number of the protocl that controls the option. - */ -#ifndef SOL_IP -# define SOL_IP IPPROTO_IP -#endif -#ifndef SOL_IPV6 -# define SOL_IPV6 IPPROTO_IPV6 -#endif - -/* mock state */ - -size_t -pgm_transport_pkt_offset2 ( - const bool can_fragment, - const bool use_pgmcc - ) -{ - return 0; -} - - -#define PGM_COMPILATION -#include "impl/sockaddr.h" -#include "impl/indextoaddr.h" -#include "impl/ip.h" - - -/* target: - * testing platform capability to loop send multicast packets to a listening - * receive socket. - */ - -START_TEST (test_multicast_loop_pass_001) -{ - struct sockaddr_in addr; - memset (&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = inet_addr ("239.192.0.1"); - - int recv_sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); - fail_if (-1 == recv_sock, "socket failed"); - struct sockaddr_in recv_addr; - memcpy (&recv_addr, &addr, sizeof(addr)); - recv_addr.sin_port = 7500; - fail_unless (0 == bind (recv_sock, (struct sockaddr*)&recv_addr, pgm_sockaddr_len ((struct sockaddr*)&recv_addr)), "bind failed"); - struct group_req gr; - memset (&gr, 0, sizeof(gr)); - ((struct sockaddr*)&gr.gr_group)->sa_family = addr.sin_family; - ((struct sockaddr_in*)&gr.gr_group)->sin_addr.s_addr = addr.sin_addr.s_addr; - fail_unless (0 == setsockopt (recv_sock, SOL_IP, MCAST_JOIN_GROUP, (const char*)&gr, sizeof(gr)), "setsockopt failed"); - fail_unless (0 == pgm_sockaddr_multicast_loop (recv_sock, AF_INET, FALSE), "multicast_loop failed"); - - int send_sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); - fail_if (-1 == send_sock, "socket failed"); - struct sockaddr_in send_addr; - memcpy (&send_addr, &addr, sizeof(addr)); - fail_unless (0 == bind (send_sock, (struct sockaddr*)&send_addr, pgm_sockaddr_len ((struct sockaddr*)&send_addr)), "bind failed"); - struct sockaddr_in if_addr; - fail_unless (TRUE == pgm_if_indextoaddr (0, AF_INET, 0, (struct sockaddr*)&if_addr, NULL), "if_indextoaddr failed"); - fail_unless (0 == pgm_sockaddr_multicast_if (send_sock, (struct sockaddr*)&if_addr, 0), "multicast_if failed"); - fail_unless (0 == pgm_sockaddr_multicast_loop (send_sock, AF_INET, TRUE), "multicast_loop failed"); - - const char data[] = "apple pie"; - addr.sin_port = 7500; - ssize_t bytes_sent = sendto (send_sock, data, sizeof(data), 0, (struct sockaddr*)&addr, pgm_sockaddr_len ((struct sockaddr*)&addr)); - if (-1 == bytes_sent) - g_message ("sendto: %s", strerror (errno)); - fail_unless (sizeof(data) == bytes_sent, "sendto underrun"); - - char recv_data[1024]; - ssize_t bytes_read = recv (recv_sock, recv_data, sizeof(recv_data), MSG_DONTWAIT); - if (-1 == bytes_read) - g_message ("sendto: %s", strerror (errno)); - fail_unless (sizeof(data) == bytes_read, "recv underrun"); - - fail_unless (0 == close (recv_sock), "close failed"); - fail_unless (0 == close (send_sock), "close failed"); -} -END_TEST - -/* target: - * testing whether unicast bind accepts packets to multicast join on a - * different port. - */ - -START_TEST (test_port_bind_pass_001) -{ - struct sockaddr_in addr; - memset (&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = inet_addr ("239.192.0.1"); - - int recv_sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); - fail_if (-1 == recv_sock, "socket failed"); - struct sockaddr_in recv_addr; - memcpy (&recv_addr, &addr, sizeof(addr)); - recv_addr.sin_port = 3056; - fail_unless (0 == bind (recv_sock, (struct sockaddr*)&recv_addr, pgm_sockaddr_len ((struct sockaddr*)&recv_addr)), "bind failed"); - struct group_req gr; - memset (&gr, 0, sizeof(gr)); - ((struct sockaddr*)&gr.gr_group)->sa_family = addr.sin_family; - ((struct sockaddr_in*)&gr.gr_group)->sin_addr.s_addr = addr.sin_addr.s_addr; - ((struct sockaddr_in*)&gr.gr_group)->sin_port = 3055; - fail_unless (0 == setsockopt (recv_sock, SOL_IP, MCAST_JOIN_GROUP, (const char*)&gr, sizeof(gr)), "setsockopt failed"); - fail_unless (0 == pgm_sockaddr_multicast_loop (recv_sock, AF_INET, FALSE), "multicast_loop failed"); - - int send_sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); - fail_if (-1 == send_sock, "socket failed"); - struct sockaddr_in send_addr; - memcpy (&send_addr, &addr, sizeof(addr)); - fail_unless (0 == bind (send_sock, (struct sockaddr*)&send_addr, pgm_sockaddr_len ((struct sockaddr*)&send_addr)), "bind failed"); - struct sockaddr_in if_addr; - fail_unless (TRUE == pgm_if_indextoaddr (0, AF_INET, 0, (struct sockaddr*)&if_addr, NULL), "if_indextoaddr failed"); - fail_unless (0 == pgm_sockaddr_multicast_if (send_sock, (struct sockaddr*)&if_addr, 0), "multicast_if failed"); - fail_unless (0 == pgm_sockaddr_multicast_loop (send_sock, AF_INET, TRUE), "multicast_loop failed"); - - const char data[] = "apple pie"; - addr.sin_port = 3056; - ssize_t bytes_sent = sendto (send_sock, data, sizeof(data), 0, (struct sockaddr*)&addr, pgm_sockaddr_len ((struct sockaddr*)&addr)); - if (-1 == bytes_sent) - g_message ("sendto: %s", strerror (errno)); - fail_unless (sizeof(data) == bytes_sent, "sendto underrun"); - - char recv_data[1024]; - ssize_t bytes_read = recv (recv_sock, recv_data, sizeof(recv_data), MSG_DONTWAIT); - if (-1 == bytes_read) - g_message ("recv: %s", strerror (errno)); - if (sizeof(data) != bytes_read) - g_message ("recv returned %d bytes expected %d.", bytes_read, sizeof(data)); - fail_unless (sizeof(data) == bytes_read, "recv underrun"); - - fail_unless (0 == close (recv_sock), "close failed"); - fail_unless (0 == close (send_sock), "close failed"); -} -END_TEST - -/* target: - * test setting hop limit, aka time-to-live. - * - * NB: whilst convenient, we cannot use SOCK_RAW & IPPROTO_UDP on Solaris 10 - * as it crashes the IP stack. - */ - -START_TEST (test_hop_limit_pass_001) -{ - struct sockaddr_in addr; - memset (&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = inet_addr ("239.192.0.1"); - - int recv_sock = socket (AF_INET, SOCK_RAW, 113); - fail_if (-1 == recv_sock, "socket failed"); - struct sockaddr_in recv_addr; - memcpy (&recv_addr, &addr, sizeof(addr)); - recv_addr.sin_port = 7500; - fail_unless (0 == bind (recv_sock, (struct sockaddr*)&recv_addr, pgm_sockaddr_len ((struct sockaddr*)&recv_addr)), "bind failed"); - struct group_req gr; - memset (&gr, 0, sizeof(gr)); - ((struct sockaddr*)&gr.gr_group)->sa_family = addr.sin_family; - ((struct sockaddr_in*)&gr.gr_group)->sin_addr.s_addr = addr.sin_addr.s_addr; - fail_unless (0 == setsockopt (recv_sock, SOL_IP, MCAST_JOIN_GROUP, (const char*)&gr, sizeof(gr)), "setsockopt failed"); - fail_unless (0 == pgm_sockaddr_multicast_loop (recv_sock, AF_INET, FALSE), "multicast_loop failed"); - fail_unless (0 == pgm_sockaddr_hdrincl (recv_sock, AF_INET, TRUE), "hdrincl failed"); - - int send_sock = socket (AF_INET, SOCK_RAW, 113); - fail_if (-1 == send_sock, "socket failed"); - struct sockaddr_in send_addr; - memcpy (&send_addr, &addr, sizeof(addr)); - fail_unless (0 == bind (send_sock, (struct sockaddr*)&send_addr, pgm_sockaddr_len ((struct sockaddr*)&send_addr)), "bind failed"); - struct sockaddr_in if_addr; - fail_unless (TRUE == pgm_if_indextoaddr (0, AF_INET, 0, (struct sockaddr*)&if_addr, NULL), "if_indextoaddr failed"); - fail_unless (0 == pgm_sockaddr_multicast_if (send_sock, (struct sockaddr*)&if_addr, 0), "multicast_if failed"); - fail_unless (0 == pgm_sockaddr_multicast_loop (send_sock, AF_INET, TRUE), "multicast_loop failed"); - fail_unless (0 == pgm_sockaddr_multicast_hops (send_sock, AF_INET, 16), "multicast_hops failed"); - - const char data[] = "apple pie"; - addr.sin_port = 7500; - ssize_t bytes_sent = sendto (send_sock, data, sizeof(data), 0, (struct sockaddr*)&addr, pgm_sockaddr_len ((struct sockaddr*)&addr)); - if (-1 == bytes_sent) - g_message ("sendto: %s", strerror (errno)); - fail_unless (sizeof(data) == bytes_sent, "sendto underrun"); - - char recv_data[1024]; - ssize_t bytes_read = recv (recv_sock, recv_data, sizeof(recv_data), MSG_DONTWAIT); - if (-1 == bytes_read) - g_message ("recv: %s", strerror (errno)); - const size_t pkt_len = sizeof(struct pgm_ip) + sizeof(data); - if (pkt_len != bytes_read) - g_message ("recv returned %zd bytes expected %zu.", bytes_read, pkt_len); - fail_unless (pkt_len == bytes_read, "recv underrun"); - const struct pgm_ip* iphdr = (void*)recv_data; - fail_unless (4 == iphdr->ip_v, "Incorrect IP version, found %u expecting 4.", iphdr->ip_v); - fail_unless (16 == iphdr->ip_ttl, "hop count mismatch, found %u expecting 16.", iphdr->ip_ttl); - - fail_unless (0 == close (recv_sock), "close failed"); - fail_unless (0 == close (send_sock), "close failed"); -} -END_TEST - -/* target: - * router alert. - */ - -START_TEST (test_router_alert_pass_001) -{ - struct sockaddr_in addr; - memset (&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = inet_addr ("239.192.0.1"); - - int recv_sock = socket (AF_INET, SOCK_RAW, 113); - fail_if (-1 == recv_sock, "socket failed"); - struct sockaddr_in recv_addr; - memcpy (&recv_addr, &addr, sizeof(addr)); - recv_addr.sin_port = 7500; - fail_unless (0 == bind (recv_sock, (struct sockaddr*)&recv_addr, pgm_sockaddr_len ((struct sockaddr*)&recv_addr)), "bind failed"); - struct group_req gr; - memset (&gr, 0, sizeof(gr)); - ((struct sockaddr*)&gr.gr_group)->sa_family = addr.sin_family; - ((struct sockaddr_in*)&gr.gr_group)->sin_addr.s_addr = addr.sin_addr.s_addr; - fail_unless (0 == setsockopt (recv_sock, SOL_IP, MCAST_JOIN_GROUP, (const char*)&gr, sizeof(gr)), "setsockopt failed"); - fail_unless (0 == pgm_sockaddr_multicast_loop (recv_sock, AF_INET, FALSE), "multicast_loop failed"); - fail_unless (0 == pgm_sockaddr_hdrincl (recv_sock, AF_INET, TRUE), "hdrincl failed"); - - int send_sock = socket (AF_INET, SOCK_RAW, 113); - fail_if (-1 == send_sock, "socket failed"); - struct sockaddr_in send_addr; - memcpy (&send_addr, &addr, sizeof(addr)); - fail_unless (0 == bind (send_sock, (struct sockaddr*)&send_addr, pgm_sockaddr_len ((struct sockaddr*)&send_addr)), "bind failed"); - struct sockaddr_in if_addr; - fail_unless (TRUE == pgm_if_indextoaddr (0, AF_INET, 0, (struct sockaddr*)&if_addr, NULL), "if_indextoaddr failed"); - fail_unless (0 == pgm_sockaddr_multicast_if (send_sock, (struct sockaddr*)&if_addr, 0), "multicast_if failed"); - fail_unless (0 == pgm_sockaddr_multicast_loop (send_sock, AF_INET, TRUE), "multicast_loop failed"); - fail_unless (0 == pgm_sockaddr_router_alert (send_sock, AF_INET, TRUE), "router_alert failed"); - - const char data[] = "apple pie"; - addr.sin_port = 7500; - ssize_t bytes_sent = sendto (send_sock, data, sizeof(data), 0, (struct sockaddr*)&addr, pgm_sockaddr_len ((struct sockaddr*)&addr)); - if (-1 == bytes_sent) - g_message ("sendto: %s", strerror (errno)); - fail_unless (sizeof(data) == bytes_sent, "sendto underrun"); - - char recv_data[1024]; - ssize_t bytes_read = recv (recv_sock, recv_data, sizeof(recv_data), MSG_DONTWAIT); - if (-1 == bytes_read) - g_message ("recv: %s", strerror (errno)); - const size_t ra_iphdr_len = sizeof(uint32_t) + sizeof(struct pgm_ip); - const size_t ra_pkt_len = ra_iphdr_len + sizeof(data); - if (ra_pkt_len != bytes_read) - g_message ("recv returned %zd bytes expected %zu.", bytes_read, ra_pkt_len); - fail_unless (ra_pkt_len == bytes_read, "recv underrun"); - const struct pgm_ip* iphdr = (void*)recv_data; - fail_unless (4 == iphdr->ip_v, "Incorrect IP version, found %u expecting 4.", iphdr->ip_v); - if (ra_iphdr_len != (iphdr->ip_hl << 2)) { - g_message ("IP header length mismatch, found %zu expecting %zu.", - (size_t)(iphdr->ip_hl << 2), ra_iphdr_len); - } - g_message ("IP header length = %zu", (size_t)(iphdr->ip_hl << 2)); - const uint32_t* ipopt = (const void*)&recv_data[ iphdr->ip_hl << 2 ]; - const uint32_t ipopt_ra = ((uint32_t)PGM_IPOPT_RA << 24) | (0x04 << 16); - const uint32_t router_alert = htonl(ipopt_ra); - if (router_alert == *ipopt) { - g_message ("IP option router alert found after IP header length."); - ipopt += sizeof(uint32_t); - } else { - ipopt = (const void*)&recv_data[ sizeof(struct pgm_ip) ]; - fail_unless (router_alert == *ipopt, "IP router alert option not found."); - g_message ("IP option router alert found before end of IP header length."); - } - g_message ("Final IP header length = %zu", (size_t)((const char*)ipopt - (const char*)recv_data)); - - fail_unless (0 == close (recv_sock), "close failed"); - fail_unless (0 == close (send_sock), "close failed"); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_multicast_loop = tcase_create ("multicast loop"); - suite_add_tcase (s, tc_multicast_loop); - tcase_add_test (tc_multicast_loop, test_multicast_loop_pass_001); - - TCase* tc_port_bind = tcase_create ("port bind"); - suite_add_tcase (s, tc_port_bind); - tcase_add_test (tc_port_bind, test_port_bind_pass_001); - - TCase* tc_hop_limit = tcase_create ("hop limit"); - suite_add_tcase (s, tc_hop_limit); - tcase_add_test (tc_hop_limit, test_hop_limit_pass_001); - - TCase* tc_router_alert = tcase_create ("router alert"); - suite_add_tcase (s, tc_router_alert); - tcase_add_test (tc_router_alert, test_router_alert_pass_001); - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/list.c b/3rdparty/openpgm-svn-r1085/pgm/list.c deleted file mode 100644 index 7b22f17..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/list.c +++ /dev/null @@ -1,146 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable doubly-linked list. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - - -//#define LIST_DEBUG - -pgm_list_t* -pgm_list_append ( - pgm_list_t* restrict list, - void* restrict data - ) -{ - pgm_list_t* new_list; - pgm_list_t* last; - - new_list = pgm_new (pgm_list_t, 1); - new_list->data = data; - new_list->next = NULL; - - if (list) - { - last = pgm_list_last (list); - last->next = new_list; - new_list->prev = last; - return list; - } - else - { - new_list->prev = NULL; - return new_list; - } -} - -pgm_list_t* -pgm_list_prepend_link ( - pgm_list_t* restrict list, - pgm_list_t* restrict link_ - ) -{ - pgm_list_t* new_list = link_; - - pgm_return_val_if_fail (NULL != link_, list); - - new_list->next = list; - new_list->prev = NULL; - - if (list) - list->prev = new_list; - return new_list; -} - -static inline -pgm_list_t* -_pgm_list_remove_link ( - pgm_list_t* list, /* list and link_ may be the same */ - pgm_list_t* link_ - ) -{ - if (PGM_LIKELY (NULL != link_)) - { - if (link_->prev) - link_->prev->next = link_->next; - if (link_->next) - link_->next->prev = link_->prev; - - if (link_ == list) - list = list->next; - - link_->next = link_->prev = NULL; - } - return list; -} - -pgm_list_t* -pgm_list_remove_link ( - pgm_list_t* list, /* list and link_ may be the same */ - pgm_list_t* link_ - ) -{ - return _pgm_list_remove_link (list, link_); -} - -pgm_list_t* -pgm_list_delete_link ( - pgm_list_t* list, /* list and link_ may be the same */ - pgm_list_t* link_ - ) -{ - pgm_list_t* new_list = _pgm_list_remove_link (list, link_); - pgm_free (link_); - - return new_list; -} - -/* Has pure attribute as NULL is a valid list - */ - -pgm_list_t* -pgm_list_last ( - pgm_list_t* list - ) -{ - if (PGM_LIKELY (NULL != list)) { - while (list->next) - list = list->next; - } - return list; -} - -unsigned -pgm_list_length ( - pgm_list_t* list - ) -{ - unsigned length = 0; - - while (list) - { - length++; - list = list->next; - } - - return length; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/log.c b/3rdparty/openpgm-svn-r1085/pgm/log.c deleted file mode 100644 index af2aec5..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/log.c +++ /dev/null @@ -1,151 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * basic logging. - * - * Copyright (c) 2006-2007 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#ifndef G_OS_WIN32 -# include -#endif -#include -#include "pgm/log.h" - - -/* globals */ - -#define TIME_FORMAT "%Y-%m-%d %H:%M:%S " - -static int log_timezone PGM_GNUC_READ_MOSTLY = 0; -static char log_hostname[NI_MAXHOST + 1] PGM_GNUC_READ_MOSTLY; - -static void glib_log_handler (const gchar*, GLogLevelFlags, const gchar*, gpointer); -static void pgm_log_handler (const int, const char*, void*); - - -/* calculate time zone offset in seconds - */ - -bool -log_init ( void ) -{ -/* time zone offset */ - time_t t = time(NULL); - struct tm sgmt, *gmt = &sgmt; - *gmt = *gmtime(&t); - struct tm* loc = localtime(&t); - log_timezone = (loc->tm_hour - gmt->tm_hour) * 60 * 60 + - (loc->tm_min - gmt->tm_min) * 60; - int dir = loc->tm_year - gmt->tm_year; - if (!dir) dir = loc->tm_yday - gmt->tm_yday; - log_timezone += dir * 24 * 60 * 60; -// printf ("timezone offset %u seconds.\n", log_timezone); - gethostname (log_hostname, sizeof(log_hostname)); - g_log_set_handler ("Pgm", G_LOG_LEVEL_MASK, glib_log_handler, NULL); - g_log_set_handler ("Pgm-Http", G_LOG_LEVEL_MASK, glib_log_handler, NULL); - g_log_set_handler ("Pgm-Snmp", G_LOG_LEVEL_MASK, glib_log_handler, NULL); - g_log_set_handler (NULL, G_LOG_LEVEL_MASK, glib_log_handler, NULL); - pgm_log_set_handler (pgm_log_handler, NULL); - return 0; -} - -/* log callback - */ -static void -glib_log_handler ( - const gchar* log_domain, - G_GNUC_UNUSED GLogLevelFlags log_level, - const gchar* message, - G_GNUC_UNUSED gpointer unused_data - ) -{ -#ifdef G_OS_UNIX - struct iovec iov[7]; - struct iovec* v = iov; - time_t now; - time (&now); - const struct tm* time_ptr = localtime(&now); - char tbuf[1024]; - strftime(tbuf, sizeof(tbuf), TIME_FORMAT, time_ptr); - v->iov_base = tbuf; - v->iov_len = strlen(tbuf); - v++; - v->iov_base = log_hostname; - v->iov_len = strlen(log_hostname); - v++; - if (log_domain) { - v->iov_base = " "; - v->iov_len = 1; - v++; - v->iov_base = log_domain; - v->iov_len = strlen(log_domain); - v++; - } - v->iov_base = ": "; - v->iov_len = 2; - v++; - v->iov_base = message; - v->iov_len = strlen(message); - v++; - v->iov_base = "\n"; - v->iov_len = 1; - v++; - writev (STDOUT_FILENO, iov, v - iov); -#else - time_t now; - time (&now); - const struct tm* time_ptr = localtime(&now); - char s[1024]; - strftime(s, sizeof(s), TIME_FORMAT, time_ptr); - write (STDOUT_FILENO, s, strlen(s)); - write (STDOUT_FILENO, log_hostname, strlen(log_hostname)); - if (log_domain) { - write (STDOUT_FILENO, " ", 1); - write (STDOUT_FILENO, log_domain, strlen(log_domain)); - } - write (STDOUT_FILENO, ": ", 2); - write (STDOUT_FILENO, message, strlen(message)); - write (STDOUT_FILENO, "\n", 1); -#endif -} - -static void -pgm_log_handler ( - const int pgm_log_level, - const char* message, - G_GNUC_UNUSED void* closure - ) -{ - GLogLevelFlags glib_log_level; - - switch (pgm_log_level) { - case PGM_LOG_LEVEL_DEBUG: glib_log_level = G_LOG_LEVEL_DEBUG; break; - case PGM_LOG_LEVEL_TRACE: glib_log_level = G_LOG_LEVEL_DEBUG; break; - case PGM_LOG_LEVEL_MINOR: glib_log_level = G_LOG_LEVEL_INFO; break; - case PGM_LOG_LEVEL_NORMAL: glib_log_level = G_LOG_LEVEL_MESSAGE; break; - case PGM_LOG_LEVEL_WARNING: glib_log_level = G_LOG_LEVEL_WARNING; break; - case PGM_LOG_LEVEL_ERROR: glib_log_level = G_LOG_LEVEL_CRITICAL; break; - case PGM_LOG_LEVEL_FATAL: glib_log_level = G_LOG_LEVEL_ERROR; break; - } - - g_log ("Pgm", glib_log_level, message, NULL); -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/math.c b/3rdparty/openpgm-svn-r1085/pgm/math.c deleted file mode 100644 index c2a1b4e..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/math.c +++ /dev/null @@ -1,75 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable math. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - - -//#define MATH_DEBUG - - -static const unsigned primes[] = -{ - 11, - 19, - 37, - 73, - 109, - 163, - 251, - 367, - 557, - 823, - 1237, - 1861, - 2777, - 4177, - 6247, - 9371, - 14057, - 21089, - 31627, - 47431, - 71143, - 106721, - 160073, - 240101, - 360163, - 540217, - 810343, - 1215497, - 1823231, - 2734867, - 4102283, - 6153409, - 9230113, - 13845163, -}; - -unsigned -pgm_spaced_primes_closest (unsigned num) -{ - for (unsigned i = 0; i < PGM_N_ELEMENTS(primes); i++) - if (primes[i] > num) - return primes[i]; - return primes[PGM_N_ELEMENTS(primes) - 1]; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/md5.c b/3rdparty/openpgm-svn-r1085/pgm/md5.c deleted file mode 100644 index 03bad97..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/md5.c +++ /dev/null @@ -1,368 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * MD5 hashing algorithm. - * - * MD5 original source GNU C Library: - * Includes functions to compute MD5 message digest of files or memory blocks - * according to the definition of MD5 in RFC 1321 from April 1992. - * - * Copyright (C) 1995, 1996, 2001, 2003 Free Software Foundation, Inc. - * - * This file is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this file; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - - -//#define MD5_DEBUG - - -/* locals */ - -static void _pgm_md5_process_block (struct pgm_md5_t*restrict, const void*restrict, size_t); -static void* _pgm_md5_read_ctx (const struct pgm_md5_t*, void*restrict); - - -/* This array contains the bytes used to pad the buffer to the next - * 64-byte boundary. (RFC 1321, 3.1: Step 1) */ -static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; - - -#if __BYTE_ORDER == __BIG_ENDIAN -# define SWAP(n) \ - (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) -#else -# define SWAP(n) (n) -#endif - - -/* Initialize structure containing state of computation. - (RFC 1321, 3.3: Step 3) */ - -void -pgm_md5_init_ctx ( - struct pgm_md5_t* ctx - ) -{ -/* pre-conditions */ - pgm_assert (NULL != ctx); - - ctx->A = 0x67452301; - ctx->B = 0xefcdab89; - ctx->C = 0x98badcfe; - ctx->D = 0x10325476; - - ctx->total[0] = ctx->total[1] = 0; - ctx->buflen = 0; -} - -/* These are the four functions used in the four steps of the MD5 algorithm - and defined in the RFC 1321. The first function is a little bit optimized - (as found in Colin Plumbs public domain implementation). */ -#define FF(b, c, d) (d ^ (b & (c ^ d))) -#define FG(b, c, d) FF (d, b, c) -#define FH(b, c, d) (b ^ c ^ d) -#define FI(b, c, d) (c ^ (b | ~d)) - -/* Process LEN bytes of BUFFER, accumulating context into CTX. - It is assumed that LEN % 64 == 0. */ - -static -void -_pgm_md5_process_block ( - struct pgm_md5_t* restrict ctx, - const void* restrict buffer, - size_t len - ) -{ -/* pre-conditions */ - pgm_assert (NULL != buffer); - pgm_assert (len > 0); - pgm_assert (NULL != ctx); - - uint32_t correct_words[16]; - const uint32_t *words = buffer; - const size_t nwords = len / sizeof (uint32_t); - const uint32_t *endp = words + nwords; - uint32_t A = ctx->A; - uint32_t B = ctx->B; - uint32_t C = ctx->C; - uint32_t D = ctx->D; - -/* First increment the byte count. RFC 1321 specifies the possible - length of the file up to 2^64 bits. Here we only compute the - number of bytes. Do a double word increment. */ - ctx->total[0] += len; - if (ctx->total[0] < len) - ++ctx->total[1]; - -/* Process all bytes in the buffer with 64 bytes in each round of - the loop. */ - while (words < endp) - { - uint32_t *cwp = correct_words; - uint32_t A_save = A; - uint32_t B_save = B; - uint32_t C_save = C; - uint32_t D_save = D; - -/* First round: using the given function, the context and a constant - the next context is computed. Because the algorithms processing - unit is a 32-bit word and it is determined to work on words in - little endian byte order we perhaps have to change the byte order - before the computation. To reduce the work for the next steps - we store the swapped words in the array CORRECT_WORDS. */ - -#define OP(a, b, c, d, s, T) \ - do \ - { \ - a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \ - ++words; \ - CYCLIC (a, s); \ - a += b; \ - } \ - while (0) - -/* It is unfortunate that C does not provide an operator for - * cyclic rotation. Hope the C compiler is smart enough. */ -#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) - -/* Before we start, one word to the strange constants. - They are defined in RFC 1321 as - - T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64, or - perl -e 'foreach(1..64){printf "0x%08x\n", int (4294967296 * abs (sin $_))}' - */ - -/* Round 1. */ - OP (A, B, C, D, 7, 0xd76aa478); - OP (D, A, B, C, 12, 0xe8c7b756); - OP (C, D, A, B, 17, 0x242070db); - OP (B, C, D, A, 22, 0xc1bdceee); - OP (A, B, C, D, 7, 0xf57c0faf); - OP (D, A, B, C, 12, 0x4787c62a); - OP (C, D, A, B, 17, 0xa8304613); - OP (B, C, D, A, 22, 0xfd469501); - OP (A, B, C, D, 7, 0x698098d8); - OP (D, A, B, C, 12, 0x8b44f7af); - OP (C, D, A, B, 17, 0xffff5bb1); - OP (B, C, D, A, 22, 0x895cd7be); - OP (A, B, C, D, 7, 0x6b901122); - OP (D, A, B, C, 12, 0xfd987193); - OP (C, D, A, B, 17, 0xa679438e); - OP (B, C, D, A, 22, 0x49b40821); - -/* For the second to fourth round we have the possibly swapped words - in CORRECT_WORDS. Redefine the macro to take an additional first - argument specifying the function to use. */ -#undef OP -#define OP(f, a, b, c, d, k, s, T) \ - do \ - { \ - a += f (b, c, d) + correct_words[k] + T; \ - CYCLIC (a, s); \ - a += b; \ - } \ - while (0) - -/* Round 2. */ - OP (FG, A, B, C, D, 1, 5, 0xf61e2562); - OP (FG, D, A, B, C, 6, 9, 0xc040b340); - OP (FG, C, D, A, B, 11, 14, 0x265e5a51); - OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); - OP (FG, A, B, C, D, 5, 5, 0xd62f105d); - OP (FG, D, A, B, C, 10, 9, 0x02441453); - OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); - OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); - OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); - OP (FG, D, A, B, C, 14, 9, 0xc33707d6); - OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); - OP (FG, B, C, D, A, 8, 20, 0x455a14ed); - OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); - OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); - OP (FG, C, D, A, B, 7, 14, 0x676f02d9); - OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); - -/* Round 3. */ - OP (FH, A, B, C, D, 5, 4, 0xfffa3942); - OP (FH, D, A, B, C, 8, 11, 0x8771f681); - OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); - OP (FH, B, C, D, A, 14, 23, 0xfde5380c); - OP (FH, A, B, C, D, 1, 4, 0xa4beea44); - OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); - OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); - OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); - OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); - OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); - OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); - OP (FH, B, C, D, A, 6, 23, 0x04881d05); - OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); - OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); - OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); - OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); - -/* Round 4. */ - OP (FI, A, B, C, D, 0, 6, 0xf4292244); - OP (FI, D, A, B, C, 7, 10, 0x432aff97); - OP (FI, C, D, A, B, 14, 15, 0xab9423a7); - OP (FI, B, C, D, A, 5, 21, 0xfc93a039); - OP (FI, A, B, C, D, 12, 6, 0x655b59c3); - OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); - OP (FI, C, D, A, B, 10, 15, 0xffeff47d); - OP (FI, B, C, D, A, 1, 21, 0x85845dd1); - OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); - OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); - OP (FI, C, D, A, B, 6, 15, 0xa3014314); - OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); - OP (FI, A, B, C, D, 4, 6, 0xf7537e82); - OP (FI, D, A, B, C, 11, 10, 0xbd3af235); - OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); - OP (FI, B, C, D, A, 9, 21, 0xeb86d391); - -/* Add the starting values of the context. */ - A += A_save; - B += B_save; - C += C_save; - D += D_save; - } - -/* Put checksum in context given as argument. */ - ctx->A = A; - ctx->B = B; - ctx->C = C; - ctx->D = D; -} - -void -pgm_md5_process_bytes ( - struct pgm_md5_t* restrict ctx, - const void* restrict buffer, - size_t len - ) -{ -/* pre-conditions */ - if (len > 0) { - pgm_assert (NULL != buffer); - } - pgm_assert (NULL != ctx); - - if (len >= 64) - { -#if !_STRING_ARCH_unaligned -/* To check alignment gcc has an appropriate operator. Other - compilers don't. */ -# if __GNUC__ >= 2 -# define UNALIGNED_P(p) (((uintptr_t)p) % __alignof__ (uint32_t) != 0) -# else -# define UNALIGNED_P(p) (((uintptr_t)p) % sizeof(uint32_t) != 0) -# endif - if (UNALIGNED_P (buffer)) - while (len > 64) - { - _pgm_md5_process_block (ctx, memcpy (ctx->buffer, buffer, 64), 64); - buffer = (const char*)buffer + 64; - len -= 64; - } - else -#endif - { - _pgm_md5_process_block (ctx, buffer, len & ~63); - buffer = (const char*)buffer + (len & ~63); - len &= 63; - } - } - -/* Move remaining bytes in internal buffer. */ - if (len > 0) - { - size_t left_over = ctx->buflen; - - memcpy (&ctx->buffer[left_over], buffer, len); - left_over += len; - if (left_over >= 64) - { - _pgm_md5_process_block (ctx, ctx->buffer, 64); - left_over -= 64; - memcpy (ctx->buffer, &ctx->buffer[64], left_over); - } - ctx->buflen = left_over; - } -} - -/* Put result from CTX in first 16 bytes following RESBUF. The result - must be in little endian byte order. - - IMPORTANT: On some systems it is required that RESBUF is correctly - aligned for a 32 bits value. */ - -static -void* -_pgm_md5_read_ctx ( - const struct pgm_md5_t* restrict ctx, - void* restrict resbuf - ) -{ -/* pre-conditions */ - pgm_assert (NULL != ctx); - pgm_assert (NULL != resbuf); - - ((uint32_t*)resbuf)[0] = SWAP (ctx->A); - ((uint32_t*)resbuf)[1] = SWAP (ctx->B); - ((uint32_t*)resbuf)[2] = SWAP (ctx->C); - ((uint32_t*)resbuf)[3] = SWAP (ctx->D); - - return resbuf; -} - -/* Process the remaining bytes in the internal buffer and the usual - prolog according to the standard and write the result to RESBUF. - - IMPORTANT: On some systems it is required that RESBUF is correctly - aligned for a 32 bits value. */ - -void* -pgm_md5_finish_ctx ( - struct pgm_md5_t* restrict ctx, - void* restrict resbuf - ) -{ -/* pre-conditions */ - pgm_assert (NULL != ctx); - pgm_assert (NULL != resbuf); - -/* Take yet unprocessed bytes into account. */ - const uint32_t bytes = ctx->buflen; - size_t pad; - -/* Now count remaining bytes. */ - ctx->total[0] += bytes; - if (ctx->total[0] < bytes) - ++ctx->total[1]; - - pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; - memcpy (&ctx->buffer[bytes], fillbuf, pad); - - /* Put the 64-bit file length in *bits* at the end of the buffer. */ - *(uint32_t*) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3); - *(uint32_t*) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) | - (ctx->total[0] >> 29)); - -/* Process last bytes. */ - _pgm_md5_process_block (ctx, ctx->buffer, bytes + pad + 8); - - return _pgm_md5_read_ctx (ctx, resbuf); -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/md5_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/md5_unittest.c deleted file mode 100644 index 836af1f..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/md5_unittest.c +++ /dev/null @@ -1,189 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for MD5 hashing (not actual algorithm). - * - * Copyright (c) 2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include -#include -#include -#include - - -/* mock state */ - -/* mock functions for external references */ - -size_t -pgm_transport_pkt_offset2 ( - const bool can_fragment, - const bool use_pgmcc - ) -{ - return 0; -} - - -#define MD5_DEBUG -#include "md5.c" - - -/* target: - * void - * pgm_md5_init_ctx ( - * struct pgm_md5_t* ctx - * ) - */ - -START_TEST (test_init_ctx_pass_001) -{ - struct pgm_md5_t ctx; - memset (&ctx, 0, sizeof(ctx)); - pgm_md5_init_ctx (&ctx); -} -END_TEST - -START_TEST (test_init_ctx_fail_001) -{ - pgm_md5_init_ctx (NULL); - fail ("reached"); -} -END_TEST - -/* target: - * void - * pgm_md5_process_bytes ( - * struct pgm_md5_t* ctx, - * const void* buffer, - * size_t len - * ) - */ - -START_TEST (test_process_bytes_pass_001) -{ - const char buffer[] = "i am not a string."; - struct pgm_md5_t ctx; - memset (&ctx, 0, sizeof(ctx)); - pgm_md5_init_ctx (&ctx); - pgm_md5_process_bytes (&ctx, buffer, sizeof(buffer)); -} -END_TEST - -START_TEST (test_process_bytes_fail_001) -{ - const char buffer[] = "i am not a string."; - pgm_md5_process_bytes (NULL, buffer, sizeof(buffer)); -} -END_TEST - -/* target: - * void* - * pgm_md5_finish_ctx ( - * struct pgm_md5_t* ctx, - * void* resbuf - * ) - */ - -START_TEST (test_finish_ctx_pass_001) -{ - const char* buffer = "i am not a string."; - const char* answer = "ef71-1617-4eef-9737-5e2b-5d7a-d015-b064"; - - char md5[1024]; - char resblock[16]; - struct pgm_md5_t ctx; - memset (&ctx, 0, sizeof(ctx)); - memset (resblock, 0, sizeof(resblock)); - pgm_md5_init_ctx (&ctx); - pgm_md5_process_bytes (&ctx, buffer, strlen(buffer)+1); - pgm_md5_finish_ctx (&ctx, resblock); - sprintf (md5, "%02.2hhx%02.2hhx-%02.2hhx%02.2hhx-%02.2hhx%02.2hhx-%02.2hhx%02.2hhx-%02.2hhx%02.2hhx-%02.2hhx%02.2hhx-%02.2hhx%02.2hhx-%02.2hhx%02.2hhx", - resblock[0], resblock[1], - resblock[2], resblock[3], - resblock[4], resblock[5], - resblock[6], resblock[7], - resblock[8], resblock[9], - resblock[10], resblock[11], - resblock[12], resblock[13], - resblock[14], resblock[15]); - g_message ("md5: %s", md5); - - fail_unless (0 == strcmp (md5, answer), "md5 mismatch"); -} -END_TEST - -START_TEST (test_finish_ctx_fail_001) -{ - char resblock[16]; - pgm_md5_finish_ctx (NULL, resblock); - fail ("reached"); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_init_ctx = tcase_create ("init-ctx"); - suite_add_tcase (s, tc_init_ctx); - tcase_add_test (tc_init_ctx, test_init_ctx_pass_001); - tcase_add_test_raise_signal (tc_init_ctx, test_init_ctx_fail_001, SIGABRT); - - TCase* tc_process_bytes = tcase_create ("process_bytes"); - suite_add_tcase (s, tc_process_bytes); - tcase_add_test (tc_process_bytes, test_process_bytes_pass_001); - tcase_add_test_raise_signal (tc_process_bytes, test_process_bytes_fail_001, SIGABRT); - - TCase* tc_finish_ctx = tcase_create ("finish-ctx"); - suite_add_tcase (s, tc_finish_ctx); - tcase_add_test (tc_finish_ctx, test_finish_ctx_pass_001); - tcase_add_test_raise_signal (tc_finish_ctx, test_finish_ctx_fail_001, SIGABRT); - - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/mem.c b/3rdparty/openpgm-svn-r1085/pgm/mem.c deleted file mode 100644 index 85a6dee..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/mem.c +++ /dev/null @@ -1,249 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable fail fast memory allocation. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#ifdef _WIN32 -# define strcasecmp stricmp -#endif -#include -#include - - -//#define MEM_DEBUG - - -/* globals */ - -bool pgm_mem_gc_friendly PGM_GNUC_READ_MOSTLY = FALSE; - - -/* locals */ - -struct pgm_debug_key_t { - const char* key; - unsigned value; -}; -typedef struct pgm_debug_key_t pgm_debug_key_t; - -static volatile uint32_t mem_ref_count = 0; - - -static -bool -debug_key_matches ( - const char* restrict key, - const char* restrict token, - unsigned length - ) -{ - for (; length; length--, key++, token++) - { - const char k = (*key == '_') ? '-' : tolower (*key ); - const char t = (*token == '_') ? '-' : tolower (*token); - if (k != t) - return FALSE; - } - return *key == '\0'; -} - -static -unsigned -pgm_parse_debug_string ( - const char* restrict string, - const pgm_debug_key_t* restrict keys, - const unsigned nkeys - ) -{ - unsigned result = 0; - - if (NULL == string) - return result; - - if (!strcasecmp (string, "all")) - { - for (unsigned i = 0; i < nkeys; i++) - result |= keys[i].value; - } - else if (!strcasecmp (string, "help")) - { - fprintf (stderr, "Supported debug values:"); - for (unsigned i = 0; i < nkeys; i++) - fprintf (stderr, " %s", keys[i].key); - fprintf (stderr, "\n"); - } - else - { - while (string) { - const char* q = strpbrk (string, ":;, \t"); - if (!q) - q = string + strlen (string); - for (unsigned i = 0; i < nkeys; i++) - if (debug_key_matches (keys[i].key, string, q - string)) - result |= keys[i].value; - string = q; - if (*string) - string++; - } - } - return result; -} - -void -pgm_mem_init (void) -{ - static const pgm_debug_key_t keys[] = { - { "gc-friendly", 1 }, - }; - - if (pgm_atomic_exchange_and_add32 (&mem_ref_count, 1) > 0) - return; - - const char *val = getenv ("PGM_DEBUG"); - const unsigned flags = !val ? 0 : pgm_parse_debug_string (val, keys, PGM_N_ELEMENTS (keys)); - if (flags & 1) - pgm_mem_gc_friendly = TRUE; -} - -void -pgm_mem_shutdown (void) -{ - pgm_return_if_fail (pgm_atomic_read32 (&mem_ref_count) > 0); - - if (pgm_atomic_exchange_and_add32 (&mem_ref_count, (uint32_t)-1) != 1) - return; - - /* nop */ -} - -/* malloc wrappers to hard fail */ -void* -pgm_malloc ( - const size_t n_bytes - ) -{ - if (PGM_LIKELY (n_bytes)) - { - void* mem = malloc (n_bytes); - if (mem) - return mem; - - pgm_fatal ("file %s: line %d (%s): failed to allocate %zu bytes", - __FILE__, __LINE__, __PRETTY_FUNCTION__, - n_bytes); - abort (); - } - return NULL; -} - -#define SIZE_OVERFLOWS(a,b) (PGM_UNLIKELY ((a) > SIZE_MAX / (b))) - -void* -pgm_malloc_n ( - const size_t n_blocks, - const size_t block_bytes - ) -{ - if (SIZE_OVERFLOWS (n_blocks, block_bytes)) { - pgm_fatal ("file %s: line %d (%s): overflow allocating %zu*%zu bytes", - __FILE__, __LINE__, __PRETTY_FUNCTION__, - n_blocks, block_bytes); - } - return pgm_malloc (n_blocks * block_bytes); -} - -void* -pgm_malloc0 ( - const size_t n_bytes - ) -{ - if (PGM_LIKELY (n_bytes)) - { - void* mem = calloc (1, n_bytes); - if (mem) - return mem; - - pgm_fatal ("file %s: line %d (%s): failed to allocate %zu bytes", - __FILE__, __LINE__, __PRETTY_FUNCTION__, - n_bytes); - abort (); - } - return NULL; -} - -void* -pgm_malloc0_n ( - const size_t n_blocks, - const size_t block_bytes - ) -{ - if (PGM_LIKELY (n_blocks && block_bytes)) - { - void* mem = calloc (n_blocks, block_bytes); - if (mem) - return mem; - - pgm_fatal ("file %s: line %d (%s): failed to allocate %zu*%zu bytes", - __FILE__, __LINE__, __PRETTY_FUNCTION__, - n_blocks, block_bytes); - abort (); - } - return NULL; -} - -void* -pgm_memdup ( - const void* mem, - const size_t n_bytes - ) -{ - void* new_mem; - - if (PGM_LIKELY (NULL != mem)) - { - new_mem = pgm_malloc (n_bytes); - memcpy (new_mem, mem, n_bytes); - } - else - new_mem = NULL; - - return new_mem; -} - -void* -pgm_realloc ( - void* mem, - const size_t n_bytes - ) -{ - return realloc (mem, n_bytes); -} - -void -pgm_free ( - void* mem - ) -{ - if (PGM_LIKELY (NULL != mem)) - free (mem); -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/memcheck b/3rdparty/openpgm-svn-r1085/pgm/memcheck deleted file mode 100755 index fbfe59c..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/memcheck +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh - -G_SLICE=always-malloc \ -G_DEBUG=gc-friendly \ - valgrind \ - -v \ - --tool=memcheck \ - --leak-check=full \ - --num-callers=40 \ - --gen-suppressions=no \ - --show-reachable=yes \ - --suppressions=valgrind.supp \ - $* diff --git a/3rdparty/openpgm-svn-r1085/pgm/messages.c b/3rdparty/openpgm-svn-r1085/pgm/messages.c deleted file mode 100644 index 9fa281b..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/messages.c +++ /dev/null @@ -1,173 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * basic message reporting. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include - - -/* globals */ - -/* bit mask for trace role modules */ -int pgm_log_mask PGM_GNUC_READ_MOSTLY = 0xffff; -int pgm_min_log_level PGM_GNUC_READ_MOSTLY = PGM_LOG_LEVEL_NORMAL; - - -/* locals */ - -static const char log_levels[8][6] = { - "Uknown", - "Debug", - "Trace", - "Minor", - "Info", - "Warn", - "Error", - "Fatal" -}; - -static volatile uint32_t messages_ref_count = 0; -static pgm_mutex_t messages_mutex; -static pgm_log_func_t log_handler PGM_GNUC_READ_MOSTLY = NULL; -static void* log_handler_closure PGM_GNUC_READ_MOSTLY = NULL; - -static inline const char* log_level_text (const int) PGM_GNUC_PURE; - - -static inline -const char* -log_level_text ( - const int log_level - ) -{ - switch (log_level) { - default: return log_levels[0]; - case PGM_LOG_LEVEL_DEBUG: return log_levels[1]; - case PGM_LOG_LEVEL_TRACE: return log_levels[2]; - case PGM_LOG_LEVEL_MINOR: return log_levels[3]; - case PGM_LOG_LEVEL_NORMAL: return log_levels[4]; - case PGM_LOG_LEVEL_WARNING: return log_levels[5]; - case PGM_LOG_LEVEL_ERROR: return log_levels[6]; - case PGM_LOG_LEVEL_FATAL: return log_levels[7]; - } -} - -/* reference counted init and shutdown - */ - -void -pgm_messages_init (void) -{ - if (pgm_atomic_exchange_and_add32 (&messages_ref_count, 1) > 0) - return; - - pgm_mutex_init (&messages_mutex); - - const char* log_mask = getenv ("PGM_LOG_MASK"); - if (NULL != log_mask) { - unsigned int value = 0; - if (1 == sscanf (log_mask, "0x%4x", &value)) - pgm_log_mask = value; - } - const char *min_log_level = getenv ("PGM_MIN_LOG_LEVEL"); - if (NULL != min_log_level) { - switch (min_log_level[0]) { - case 'D': pgm_min_log_level = PGM_LOG_LEVEL_DEBUG; break; - case 'T': pgm_min_log_level = PGM_LOG_LEVEL_TRACE; break; - case 'M': pgm_min_log_level = PGM_LOG_LEVEL_MINOR; break; - case 'N': pgm_min_log_level = PGM_LOG_LEVEL_NORMAL; break; - case 'W': pgm_min_log_level = PGM_LOG_LEVEL_WARNING; break; - case 'E': pgm_min_log_level = PGM_LOG_LEVEL_ERROR; break; - case 'F': pgm_min_log_level = PGM_LOG_LEVEL_FATAL; break; - default: break; - } - } -} - -void -pgm_messages_shutdown (void) -{ - pgm_return_if_fail (pgm_atomic_read32 (&messages_ref_count) > 0); - - if (pgm_atomic_exchange_and_add32 (&messages_ref_count, (uint32_t)-1) != 1) - return; - - pgm_mutex_free (&messages_mutex); -} - -/* set application handler for log messages, returns previous value, - * default handler value is NULL. - */ - -pgm_log_func_t -pgm_log_set_handler ( - pgm_log_func_t handler, - void* closure - ) -{ - pgm_log_func_t previous_handler; - pgm_mutex_lock (&messages_mutex); - previous_handler = log_handler; - log_handler = handler; - log_handler_closure = closure; - pgm_mutex_unlock (&messages_mutex); - return previous_handler; -} - -void -pgm__log ( - const int log_level, - const char* format, - ... - ) -{ - va_list args; - - va_start (args, format); - pgm__logv (log_level, format, args); - va_end (args); -} - -void -pgm__logv ( - const int log_level, - const char* format, - va_list args - ) -{ - char tbuf[ 1024 ]; - - pgm_mutex_lock (&messages_mutex); - const int offset = sprintf (tbuf, "%s: ", log_level_text (log_level)); - vsnprintf (tbuf+offset, sizeof(tbuf)-offset, format, args); - tbuf[ sizeof(tbuf) ] = '\0'; - if (log_handler) - log_handler (log_level, tbuf, log_handler_closure); - else { -/* ignore return value */ - write (STDOUT_FILENO, tbuf, strlen (tbuf)); - write (STDOUT_FILENO, "\n", 1); - } - - pgm_mutex_unlock (&messages_mutex); -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/mibs/PGM-MIB-petrova-01.txt b/3rdparty/openpgm-svn-r1085/pgm/mibs/PGM-MIB-petrova-01.txt deleted file mode 100644 index 28ff72c..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/mibs/PGM-MIB-petrova-01.txt +++ /dev/null @@ -1,5459 +0,0 @@ ----------------------------------------------------------------- --- --- Pragmatic General Multicast (PGM) MIB --- ----------------------------------------------------------------- --- --- --- Full MIB for the PGM protocol incorporating Network Element --- (router), source, receiver and DLR functionality --- --- extracted from draft-petrova-pgmmib-01.txt - -PGM-MIB DEFINITIONS ::= BEGIN -IMPORTS - OBJECT-TYPE, Counter32, Integer32, Unsigned32, NOTIFICATION-TYPE, - MODULE-IDENTITY, IpAddress, TimeTicks, experimental, BITS - FROM SNMPv2-SMI - MODULE-COMPLIANCE, OBJECT-GROUP, NOTIFICATION-GROUP - FROM SNMPv2-CONF - InterfaceIndex - FROM IF-MIB; - -pgmMIB MODULE-IDENTITY - LAST-UPDATED "200205010000Z" - ORGANIZATION - "Cisco Systems + Tibco Software Inc + Nortel Networks" - CONTACT-INFO - " Richard Edmonstone - redmonst@cisco.com - +44 131 561 3621 - Cisco Systems, Inc. - 170 West Tasman Drive, - San Jose, CA 95134 - USA - - Rajiv Raghunarayan - raraghun@cisco.com - +91 80 532 1300 - Cisco Systems, Inc. - 170 West Tasman Drive, - San Jose, CA 95134 - USA - - Devendra Raut - draut@nortelnetworks.com - (408)495-2859 - Nortel Networks - 4401 Great America Parkway, - Santa Clara, CA 95052 - - Moses Sun - mosun@nortelnetworks.com - (979)694-7156 - Nortel Networks - 4401 Great America Parkway - Santa Clara, CA, - USA - - Todd L. Montgomery - tmontgomery@tibco.com - (304)291-5972 - Tibco Software Inc. - 29W110 Butterfield Rd, Suite 205 - Warrenville, IL 60555 - USA - - Michael Garwood - mgarwood@tibco.com - (630)393-7363 ext.275 - Tibco Software Inc. - 29W110 Butterfield Rd, Suite 205 - Warrenville, IL 60555 - USA - - Luna Petrova - lpetrova@tibco.com - (630)393-7363 ext.330 - Tibco Software Inc. - 29W110 Butterfield Rd, Suite 205 - Warrenville, IL 60555 - USA" - DESCRIPTION - "The MIB module for managing PGM implementations." - REVISION "200205010000Z" - DESCRIPTION - "Rev 2.0: SNMP Notifications added to the MIB." - ::= { experimental 112 } -- assigned by IANA. - -pgm OBJECT IDENTIFIER ::= { pgmMIB 1 } -pgmNetworkElement OBJECT IDENTIFIER ::= { pgm 1 } -pgmSource OBJECT IDENTIFIER ::= { pgm 2 } -pgmReceiver OBJECT IDENTIFIER ::= { pgm 3 } -pgmDLR OBJECT IDENTIFIER ::= { pgm 4 } -pgmNotificationPrefix OBJECT IDENTIFIER ::= { pgmMIB 2 } - --- PGM Network Element - -pgmNeEnable OBJECT-TYPE - SYNTAX INTEGER { - enable(1), - disable(2) - } - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Enable/Disable Parameter indicates whether - this PGM operation is enabled or disabled." - DEFVAL { enable } - ::= { pgmNetworkElement 1 } - -pgmNeSessionLifeTime OBJECT-TYPE - SYNTAX Unsigned32(0..2147483647) - UNITS "seconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "The length of the idle time (seconds) following - which a PGM session will be aged out. An idle PGM - session means there is no SPM message received - from the upstream. - Value of 0 indicates no timeout." - DEFVAL { 300 } - ::= { pgmNetworkElement 2 } - -pgmNeMaxReXmitStates OBJECT-TYPE - SYNTAX Integer32(-2..2147483647) - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "The Maximum number of retransmission state entries. - The value of -1 means network element has no - limitation. - The value of -2 means not supported by this - implementation." - ::= { pgmNetworkElement 3 } - -pgmNeMaxSessions OBJECT-TYPE - SYNTAX Integer32(-2..2147483647) - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "The maximum number of state sessions supported. - The value of -1 means network element has no - limitation. - The value of -2 means not supported by this - implementation." - ::= { pgmNetworkElement 4 } - --- The PGM NE Network Interface - --- The PGM NE Network Interface tables contain --- per-interface information about the PGM protocol. --- The information is grouped into three major categories: --- fault, configuration and performance management. - -pgmNeInterface OBJECT IDENTIFIER ::= { pgmNetworkElement 100 } - -pgmNeTotalInterfacesNumberOfEntries OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of entries in the PGM Interface - table." - ::= { pgmNeInterface 1 } - --- The PGM NE Network Interface configuration table - -pgmNeIfConfigTable OBJECT-TYPE - SYNTAX SEQUENCE OF PgmNeIfConfigEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The table holding per interface configuration - information relating to PGM Network Element - operation." - ::= {pgmNeInterface 3} - -pgmNeIfConfigEntry OBJECT-TYPE - SYNTAX PgmNeIfConfigEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Per Interface Configuration Information." - INDEX { pgmNeIfConfigIndex } - ::= { pgmNeIfConfigTable 1 } - -PgmNeIfConfigEntry ::= SEQUENCE { - pgmNeIfConfigIndex - InterfaceIndex, - pgmNeIfPgmEnable - INTEGER, - pgmNeIfNakRptInterval - Unsigned32, - pgmNeIfNakRptRate - Unsigned32, - pgmNeIfNakRdataInterval - Unsigned32, - pgmNeIfNakEliminateInterval - Unsigned32 - } - -pgmNeIfConfigIndex OBJECT-TYPE - SYNTAX InterfaceIndex - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "A unique value for each interface. Its value - ranges between 1 and the value of ifNumber. The - value for each interface must remain constant at - least from one re-initialization of the entity's - network management system to the next - re-initialization." - ::= { pgmNeIfConfigEntry 1 } - -pgmNeIfPgmEnable OBJECT-TYPE - SYNTAX INTEGER { - enable(1), - disable(2) - } - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Allows PGM to be enabled and disabled per - Network Interface. - - PGM can be enabled or disabled per Network - Interface, only if PGM is enabled for this - Network Element." - ::= { pgmNeIfConfigEntry 2 } - -pgmNeIfNakRptInterval OBJECT-TYPE - SYNTAX Unsigned32(1..4294967295) - UNITS "milliseconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "The length of time (milliseconds) for which a - network element will repeat a NAK while waiting - for a corresponding NCF. This interval is counted - down from the transmission of a NAK." - DEFVAL { 100 } - ::= { pgmNeIfConfigEntry 3 } - -pgmNeIfNakRptRate OBJECT-TYPE - SYNTAX Unsigned32(1..4294967295) - UNITS "number of NAKs per second" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "The rate at which NAKs are repeated." - DEFVAL { 2 } - ::= { pgmNeIfConfigEntry 4 } - -pgmNeIfNakRdataInterval OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "seconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "The length of time (milliseconds) for which - a network element will wait for the - corresponding RDATA. This interval is counted - down from the time a matching NCF is received. - This value must be greater than the - pgmNeIfNakEliminateInterval." - DEFVAL { 10000 } - ::= { pgmNeIfConfigEntry 5 } - -pgmNeIfNakEliminateInterval OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "milliseconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "The length of time (milliseconds) for which - a network element will eliminate NAKs for - a specific TSI/SQN. This interval is counted - down from the time the first NAK is - established. This value must - be smaller than pgmNeIfNakRdataInterval." - DEFVAL { 5000 } - ::= { pgmNeIfConfigEntry 6 } - --- The PGM NE Interface performance table. --- This is primarily statistical information --- about packets received and sent on the interface - -pgmNeIfPerformanceTable OBJECT-TYPE - SYNTAX SEQUENCE OF PgmNeIfPerformanceEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The table holding per interface performance - information related to PGM Network Element - operation." - ::= {pgmNeInterface 4} - -pgmNeIfPerformanceEntry OBJECT-TYPE - SYNTAX PgmNeIfPerformanceEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Per Interface Information for Network Elements." - INDEX { pgmNeIfPerformanceIndex } - ::= { pgmNeIfPerformanceTable 1 } - -PgmNeIfPerformanceEntry ::= SEQUENCE { - pgmNeIfPerformanceIndex - InterfaceIndex, - pgmNeIfReXmitStates - Counter32, - pgmNeIfReXmitTimedOut - Counter32, - pgmNeIfInSpms - Counter32, - pgmNeIfOutSpms - Counter32, - pgmNeIfInParitySpms - Counter32, - pgmNeIfOutParitySpms - Counter32, - pgmNeIfInRdata - Counter32, - pgmNeIfOutRdata - Counter32, - pgmNeIfInParityRdata - Counter32, - pgmNeIfOutParityRdata - Counter32, - pgmNeIfInRdataNoSessionErrors - Counter32, - pgmNeIfUniqueNaks - Counter32, - pgmNeIfInNaks - Counter32, - pgmNeIfOutNaks - Counter32, - pgmNeIfUniqueParityNaks - Counter32, - pgmNeIfInParityNaks - Counter32, - pgmNeIfOutParityNaks - Counter32, - pgmNeIfInNakNoSessionErrors - Counter32, - pgmNeIfInNakSeqErrors - Counter32, - pgmNeIfInParityNakTgErrors - Counter32, - pgmNeIfInNnaks - Counter32, - pgmNeIfOutNnaks - Counter32, - pgmNeIfInParityNnaks - Counter32, - pgmNeIfOutParityNnaks - Counter32, - pgmNeIfInNnakNoSessionErrors - Counter32, - pgmNeIfInNcfs - Counter32, - pgmNeIfOutNcfs - Counter32, - pgmNeIfInParityNcfs - Counter32, - pgmNeIfOutParityNcfs - Counter32, - pgmNeIfInNcfNoSessionErrors - Counter32, - pgmNeIfInRedirectNcfs - Counter32, - pgmNeIfMalformed - Counter32, - pgmNeIfSpmFromSource - Counter32, - pgmNeIfSpmBadSqn - Counter32, - pgmNeIfSpmError - Counter32, - pgmNeIfPollRandomIgnore - Counter32, - pgmNeIfPollTsiStateError - Counter32, - pgmNeIfPollParentError - Counter32, - pgmNeIfPollTypeError - Counter32, - pgmNeIfPollError - Counter32, - pgmNeIfPollSuccess - Counter32, - pgmNeIfPollOriginated - Counter32, - pgmNeIfPolrNoState - Counter32, - pgmNeIfPolrError - Counter32, - pgmNeIfPolrParityError - Counter32, - pgmNeIfPolrSuccess - Counter32, - pgmNeIfPolrOriginated - Counter32, - pgmNeIfNcfError - Counter32, - pgmNeIfNcfParityError - Counter32, - pgmNeIfNcfPartialParity - Counter32, - pgmNeIfNcfReceived - Counter32, - pgmNeIfNcfAnticipated - Counter32, - pgmNeIfNcfRedirecting - Counter32, - pgmNeIfNakEliminated - Counter32, - pgmNeIfNakError - Counter32, - pgmNeIfNakParityError - Counter32, - pgmNeIfNNakEliminated - Counter32, - pgmNeIfNNakError - Counter32, - pgmNeIfNNakParityError - Counter32, - pgmNeIfNNakCongestionReports - Counter32, - pgmNeIfNakRetryExpired - Counter32, - pgmNeIfNakRetryExpiredDLR - Counter32, - pgmNeIfNakForwardedDLR - Counter32, - pgmNeIfNakRetransmitted - Counter32, - pgmNeIfRdataEliminatedOIF - Counter32, - pgmNeIfRdataEliminatedSqn - Counter32, - pgmNeIfInRdataFragments - Counter32, - pgmNeIfRdataFragmentsNoSessionErrors - Counter32, - pgmNeIfRdataFragmentsEliminatedOIF - Counter32, - pgmNeIfRdataFragmentsEliminatedSqn - Counter32, - pgmNeIfOutRdataFragments - Counter32 -} - -pgmNeIfPerformanceIndex OBJECT-TYPE - SYNTAX InterfaceIndex - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "A unique value for each interface. Its value - ranges between 1 and the value of ifNumber. - The value for each interface must remain - constant at least from one re-initialization - of the entity's network management system - to the next re-initialization." - ::= { pgmNeIfPerformanceEntry 1 } - -pgmNeIfReXmitStates OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total retransmit state entries for this - interface." - ::= { pgmNeIfPerformanceEntry 2 } - -pgmNeIfReXmitTimedOut OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of timed-out retransmit state - entries for this interface." - ::= { pgmNeIfPerformanceEntry 3 } - -pgmNeIfInSpms OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of SPMs received on the PGM - interface." - ::= { pgmNeIfPerformanceEntry 4 } - -pgmNeIfOutSpms OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of SPMs sent out from the PGM - interface." - ::= { pgmNeIfPerformanceEntry 5 } - -pgmNeIfInParitySpms OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of parity SPMs received on the - PGM interface." - ::= { pgmNeIfPerformanceEntry 6 } - -pgmNeIfOutParitySpms OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of parity SPMs sent out from the - PGM interface." - ::= { pgmNeIfPerformanceEntry 7 } - -pgmNeIfInRdata OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of RDATA received on the PGM - interface." - ::= { pgmNeIfPerformanceEntry 8 } - -pgmNeIfOutRdata OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of RDATA sent out from the - PGM interface." - ::= { pgmNeIfPerformanceEntry 9 } - -pgmNeIfInParityRdata OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of parity RDATA received on the - PGM interface." - ::= { pgmNeIfPerformanceEntry 10 } - -pgmNeIfOutParityRdata OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of parity RDATA sent out from - the PGM interface." - ::= { pgmNeIfPerformanceEntry 11 } - -pgmNeIfInRdataNoSessionErrors OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The number of received RDATA discarded because - of no session." - ::= { pgmNeIfPerformanceEntry 12 } - -pgmNeIfUniqueNaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of unique NAKs received on - this interface." - ::= { pgmNeIfPerformanceEntry 13 } - -pgmNeIfInNaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of NAKs received on the PGM - interface." - ::= { pgmNeIfPerformanceEntry 14 } - -pgmNeIfOutNaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of NAKs sent out from the - PGM interface." - ::= { pgmNeIfPerformanceEntry 15 } - -pgmNeIfUniqueParityNaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of unique parity NAKs received - on this interface." - ::= { pgmNeIfPerformanceEntry 16 } - -pgmNeIfInParityNaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of parity NAKs received on the - PGM interface." - ::= { pgmNeIfPerformanceEntry 17 } - -pgmNeIfOutParityNaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of parity NAKs sent out from - the PGM interface." - ::= { pgmNeIfPerformanceEntry 18 } - -pgmNeIfInNakNoSessionErrors OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The number of received NAKs discarded because of - no session." - ::= { pgmNeIfPerformanceEntry 19 } - -pgmNeIfInNakSeqErrors OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The number of received NAKs discarded because - of out of sequence (out of retransmit window)." - ::= { pgmNeIfPerformanceEntry 20 } - -pgmNeIfInParityNakTgErrors OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The number of received parity NAKs discarded - because out of parity TG window." - ::= { pgmNeIfPerformanceEntry 21 } - -pgmNeIfInNnaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of NNAKs received on the PGM - interface." - ::= { pgmNeIfPerformanceEntry 22 } - -pgmNeIfOutNnaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of NNAKs sent out from the - PGM interface." - ::= { pgmNeIfPerformanceEntry 23 } - -pgmNeIfInParityNnaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of parity NNAKs received on - the PGM interface." - ::= { pgmNeIfPerformanceEntry 24 } - -pgmNeIfOutParityNnaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of parity NNAKs sent out from - the PGM interface." - ::= { pgmNeIfPerformanceEntry 25 } - -pgmNeIfInNnakNoSessionErrors OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The number of received NNAKs discarded because - of no session." - ::= { pgmNeIfPerformanceEntry 26 } - -pgmNeIfInNcfs OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of NCFs received on the PGM - interface." - ::= { pgmNeIfPerformanceEntry 27 } - -pgmNeIfOutNcfs OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of NCFs sent out from the PGM - interface." - ::= { pgmNeIfPerformanceEntry 28 } - -pgmNeIfInParityNcfs OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of parity NCFs received on the - PGM interface." - ::= { pgmNeIfPerformanceEntry 29 } - -pgmNeIfOutParityNcfs OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of parity NCFs sent out from - the PGM interface." - ::= { pgmNeIfPerformanceEntry 30 } - -pgmNeIfInNcfNoSessionErrors OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The number of received NCFs discarded because - of no session." - ::= { pgmNeIfPerformanceEntry 31 } - -pgmNeIfInRedirectNcfs OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The number of redirected NCFs received on the - PGM interface." - ::= { pgmNeIfPerformanceEntry 32 } - -pgmNeIfMalformed OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of malformed PGM packets." - ::= { pgmNeIfPerformanceEntry 33 } - -pgmNeIfSpmFromSource OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of SPM packets received from source." - ::= { pgmNeIfPerformanceEntry 34 } - -pgmNeIfSpmBadSqn OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of SPM packets discarded due to bad - SQN." - ::= { pgmNeIfPerformanceEntry 35 } - -pgmNeIfSpmError OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of SPM packets discarded due to - operational error. Some examples of operational - errors are failure to create TSI state for SPM, - parity SPM for a TSI with no parity." - ::= { pgmNeIfPerformanceEntry 36 } - -pgmNeIfPollRandomIgnore OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of POLL packets not replied due to random - condition failing." - ::= { pgmNeIfPerformanceEntry 37 } - -pgmNeIfPollTsiStateError OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of POLL packets discarded due to no - matching TSI state." - ::= { pgmNeIfPerformanceEntry 38 } - -pgmNeIfPollParentError OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of POLL packets discarded due to - unknown parent." - ::= { pgmNeIfPerformanceEntry 39 } - -pgmNeIfPollTypeError OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of POLL packets discarded due to failed - type matching." - ::= { pgmNeIfPerformanceEntry 40 } - -pgmNeIfPollError OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of POLL packets discarded due to - operational error." - ::= { pgmNeIfPerformanceEntry 41 } - -pgmNeIfPollSuccess OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of successfully scheduled POLRs." - ::= { pgmNeIfPerformanceEntry 42 } - -pgmNeIfPollOriginated OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of polls originated on this interface." - ::= { pgmNeIfPerformanceEntry 43 } - -pgmNeIfPolrNoState OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of POLRs discarded due to no matching - state." - ::= { pgmNeIfPerformanceEntry 44 } - -pgmNeIfPolrError OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of POLRs discarded due to operational - error." - ::= { pgmNeIfPerformanceEntry 45 } - -pgmNeIfPolrParityError OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of parity POLRs received for non-parity - TSI." - ::= { pgmNeIfPerformanceEntry 46 } - -pgmNeIfPolrSuccess OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of POLRs recorded successfully." - ::= { pgmNeIfPerformanceEntry 47 } - -pgmNeIfPolrOriginated OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of POLRs originated by this interface." - ::= { pgmNeIfPerformanceEntry 48 } - -pgmNeIfNcfError OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of NCFs ignored due to no packet memory, - due to packet processing errors." - ::= { pgmNeIfPerformanceEntry 49 } - -pgmNeIfNcfParityError OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of NCFs ignored. Incremented when a parity - NCF is received on a session for which no parity - capability has been advertised in the session's - SPMs." - ::= { pgmNeIfPerformanceEntry 50 } - -pgmNeIfNcfPartialParity OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of NCFs ignored due to not enough parity - blocks acknowledged." - ::= { pgmNeIfPerformanceEntry 51 } - -pgmNeIfNcfReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of NCFs that confirm an outstanding NAK." - ::= { pgmNeIfPerformanceEntry 52 } - -pgmNeIfNcfAnticipated OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of NCFs that cause NAK anticipation." - ::= { pgmNeIfPerformanceEntry 53 } - -pgmNeIfNcfRedirecting OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of NCFs received as consequence of - redirected NAK." - ::= { pgmNeIfPerformanceEntry 54 } - -pgmNeIfNakEliminated OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of NAKs eliminated by retransmission - state." - ::= { pgmNeIfPerformanceEntry 55 } - -pgmNeIfNakError OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of errors creating retransmission state - or NAK, due to NAK packet processing." - ::= { pgmNeIfPerformanceEntry 56 } - -pgmNeIfNakParityError OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of NAKs ignored, due to no parity - available. Incremented when parity NAK is - received on this session, for which no parity - capability has been advartised." - ::= { pgmNeIfPerformanceEntry 57 } - -pgmNeIfNNakEliminated OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of NNAKs eliminated by retransmission - state." - ::= { pgmNeIfPerformanceEntry 58 } - -pgmNeIfNNakError OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of errors encountered creating - retransmission state OR nak." - ::= { pgmNeIfPerformanceEntry 59 } - -pgmNeIfNNakParityError OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of Null NAKs ignored, due to no parity - available. Incremented when parity NNAK is - received on this session, for which no parity - capability has been advartised." - ::= { pgmNeIfPerformanceEntry 60 } - -pgmNeIfNNakCongestionReports OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of NAKs forwarded as NNAK as congestion - report only." - ::= { pgmNeIfPerformanceEntry 61 } - -pgmNeIfNakRetryExpired OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of NAKs timed out after - retrying." - ::= { pgmNeIfPerformanceEntry 62 } - -pgmNeIfNakRetryExpiredDLR OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of NAKs unconfirmed by DLR." - ::= { pgmNeIfPerformanceEntry 63 } - -pgmNeIfNakForwardedDLR OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of NAKs forwarded out this i/f to DLR - with retransmission state." - ::= { pgmNeIfPerformanceEntry 64 } - -pgmNeIfNakRetransmitted OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Total number of NAKs retransmitted out this i/f." - ::= { pgmNeIfPerformanceEntry 65 } - -pgmNeIfRdataEliminatedOIF OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of RDATA packets eliminated by lack of - OIF's." - ::= { pgmNeIfPerformanceEntry 66 } - -pgmNeIfRdataEliminatedSqn OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of RDATA packets eliminated by lack of - SQN." - ::= { pgmNeIfPerformanceEntry 67 } - -pgmNeIfInRdataFragments OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Total number of RDATA fragments received." - ::= { pgmNeIfPerformanceEntry 68 } - -pgmNeIfRdataFragmentsNoSessionErrors OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of RDATA fragments eliminated by lack of - GSI." - ::= { pgmNeIfPerformanceEntry 69 } - -pgmNeIfRdataFragmentsEliminatedOIF OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of RDATA fragments eliminated by lack of - OIFs." - ::= { pgmNeIfPerformanceEntry 70 } - -pgmNeIfRdataFragmentsEliminatedSqn OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of RDATA fragments eliminated by lack of - SQN." - ::= { pgmNeIfPerformanceEntry 71 } - -pgmNeIfOutRdataFragments OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Total number of RDATA fragments forwarded." - ::= { pgmNeIfPerformanceEntry 72 } - --- --- PGM Network Element Transport Session Identifier --- -pgmNeTsi OBJECT IDENTIFIER ::= { pgmNetworkElement 101 } - -pgmNeTotalTsiNumberOfEntries OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of sessions in the PGM NE TSI - table." - ::= { pgmNeTsi 1 } - --- The PGM Transport Session Identifier (TSI) table --- The TSI information is grouped into three major categories: --- fault, configuration and performance management. - --- The PGM NE TSI fault management table --- This table contains state and some general --- per TSI information - -pgmNeTsiTable OBJECT-TYPE - SYNTAX SEQUENCE OF PgmNeTsiEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The table holding per-tsi state information." - ::= {pgmNeTsi 2} - -pgmNeTsiEntry OBJECT-TYPE - SYNTAX PgmNeTsiEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Transport Session is identified by Global ID and - Source Port." - INDEX { pgmNeTsiGlobalId, pgmNeTsiDataSourcePort } - ::= { pgmNeTsiTable 1 } - -PgmNeTsiEntry ::= SEQUENCE { - pgmNeTsiGlobalId - OCTET STRING, - pgmNeTsiDataSourcePort - Unsigned32, - pgmNeTsiStateBits - BITS, - pgmNeTsiDataDestinationPort - Unsigned32, - pgmNeTsiSourceAddress - IpAddress, - pgmNeTsiGroupAddress - IpAddress, - pgmNeTsiUpstreamAddress - IpAddress, - pgmNeTsiUpstreamIfIndex - InterfaceIndex, - pgmNeTsiDlrAddress - IpAddress - } - -pgmNeTsiGlobalId OBJECT-TYPE - SYNTAX OCTET STRING (SIZE (12)) - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The Globally unique source identifier for this - transport session." - ::= {pgmNeTsiEntry 1 } - -pgmNeTsiDataSourcePort OBJECT-TYPE - SYNTAX Unsigned32 (0..65535) - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Data source port." - ::= {pgmNeTsiEntry 2} - -pgmNeTsiStateBits OBJECT-TYPE - SYNTAX BITS { initialising(0), - spmSqnStateValid(1), - dlrCanProvideParity(2) } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "State associated with the TSI." - ::= {pgmNeTsiEntry 3 } - -pgmNeTsiDataDestinationPort OBJECT-TYPE - SYNTAX Unsigned32 (0..65535) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Data destination port." - ::= {pgmNeTsiEntry 4 } - -pgmNeTsiSourceAddress OBJECT-TYPE - SYNTAX IpAddress - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "IP address of the source." - ::= {pgmNeTsiEntry 5 } - -pgmNeTsiGroupAddress OBJECT-TYPE - SYNTAX IpAddress - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Multicast group destination address." - ::= {pgmNeTsiEntry 6 } - -pgmNeTsiUpstreamAddress OBJECT-TYPE - SYNTAX IpAddress - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The IP address of the upstream PGM neighbouring - element for this TSI." - ::= { pgmNeTsiEntry 7 } - -pgmNeTsiUpstreamIfIndex OBJECT-TYPE - SYNTAX InterfaceIndex - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The index of the upstream PGM element for the - entry." - ::= { pgmNeTsiEntry 8 } - -pgmNeTsiDlrAddress OBJECT-TYPE - SYNTAX IpAddress - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "IP Address of a known DLR that will be used if - required." - ::= {pgmNeTsiEntry 9 } - - --- PGM Network Element TSI Configuration Management Table --- Since the Network Element cannot be configured --- per TSI, configuration table is not implemented - - --- PGM Network Element TSI Performance Management Table - -pgmNeTsiPerformanceTable OBJECT-TYPE - SYNTAX SEQUENCE OF PgmNeTsiPerformanceEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The table holding details of every transport - flow known by the Network Element." - ::= {pgmNeTsi 4} - -pgmNeTsiPerformanceEntry OBJECT-TYPE - SYNTAX PgmNeTsiPerformanceEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Transport session description." - INDEX { pgmNeTsiPerformanceGlobalId, - pgmNeTsiPerformanceDataSourcePort } - ::= { pgmNeTsiPerformanceTable 1 } - -PgmNeTsiPerformanceEntry ::= SEQUENCE { - pgmNeTsiPerformanceGlobalId - OCTET STRING, - pgmNeTsiPerformanceDataSourcePort - Unsigned32, - pgmNeTsiSessionTrailEdgeSeq - Counter32, - pgmNeTsiSessionIncrSeq - Counter32, - pgmNeTsiLeadEdgeSeq - Counter32, - pgmNeTsiInSpms - Counter32, - pgmNeTsiOutSpms - Counter32, - pgmNeTsiInParitySpms - Counter32, - pgmNeTsiOutParitySpms - Counter32, - pgmNeTsiTotalReXmitStates - Counter32, - pgmNeTsiTotalReXmitTimedOut - Counter32, - pgmNeTsiInRdata - Counter32, - pgmNeTsiOutRdata - Counter32, - pgmNeTsiInParityRdata - Counter32, - pgmNeTsiOutParityRdata - Counter32, - pgmNeTsiInRdataNoStateErrors - Counter32, - pgmNeTsiUniqueNaks - Counter32, - pgmNeTsiInNaks - Counter32, - pgmNeTsiOutNaks - Counter32, - pgmNeTsiUniqueParityNaks - Counter32, - pgmNeTsiInParityNaks - Counter32, - pgmNeTsiOutParityNaks - Counter32, - pgmNeTsiInNakSeqErrors - Counter32, - pgmNeTsiInNnaks - Counter32, - pgmNeTsiOutNnaks - Counter32, - pgmNeTsiInParityNnaks - Counter32, - pgmNeTsiOutParityNnaks - Counter32, - pgmNeTsiInNcfs - Counter32, - pgmNeTsiOutNcfs - Counter32, - pgmNeTsiInParityNcfs - Counter32, - pgmNeTsiOutParityNcfs - Counter32, - pgmNeTsiSpmSequenceNumber - Unsigned32, - pgmNeTsiTransmissionGroupSize - Unsigned32, - pgmNeTsiTimeout - TimeTicks, - pgmNeTsiLastTtl - Unsigned32, - pgmNeTsiLinkLossRate - Unsigned32, - pgmNeTsiPathLossRate - Unsigned32, - pgmNeTsiReceiverLossRate - Unsigned32, - pgmNeTsiCongestionReportLead - Unsigned32, - pgmNeTsiCongestionReportWorstReceiver - IpAddress -} - -pgmNeTsiPerformanceGlobalId OBJECT-TYPE - SYNTAX OCTET STRING (SIZE (12)) - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The Globally unique source identifier for this - transport session." - ::= {pgmNeTsiPerformanceEntry 1 } - -pgmNeTsiPerformanceDataSourcePort OBJECT-TYPE - SYNTAX Unsigned32 (0..65535) - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Data source port." - ::= {pgmNeTsiPerformanceEntry 2} - -pgmNeTsiSessionTrailEdgeSeq OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The trailing edge sequence of the transmit - window." - ::= { pgmNeTsiPerformanceEntry 3 } - -pgmNeTsiSessionIncrSeq OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The sequence number defining the leading edge of - the increment window." - ::= { pgmNeTsiPerformanceEntry 4 } - -pgmNeTsiLeadEdgeSeq OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The leading edge sequence of the transmit - window." - ::= { pgmNeTsiPerformanceEntry 5 } - -pgmNeTsiInSpms OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of SPMs received for this - session." - ::= { pgmNeTsiPerformanceEntry 6 } - -pgmNeTsiOutSpms OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of SPMs sent out for this - session." - ::= { pgmNeTsiPerformanceEntry 7 } - -pgmNeTsiInParitySpms OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of Parity SPMs received for - this session." - ::= { pgmNeTsiPerformanceEntry 8 } - -pgmNeTsiOutParitySpms OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of Parity SPMs sent out for - this session." - ::= { pgmNeTsiPerformanceEntry 9 } - -pgmNeTsiTotalReXmitStates OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total retransmit states for this session." - ::= { pgmNeTsiPerformanceEntry 10 } - -pgmNeTsiTotalReXmitTimedOut OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total timed-out retransmit state entries for - this session." - ::= { pgmNeTsiPerformanceEntry 11 } - -pgmNeTsiInRdata OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of RDATAs received for this - session." - ::= { pgmNeTsiPerformanceEntry 12 } - -pgmNeTsiOutRdata OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of RDATAs sent out from this - session." - ::= { pgmNeTsiPerformanceEntry 13 } - -pgmNeTsiInParityRdata OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of parity RDATAs received for - this session." - ::= { pgmNeTsiPerformanceEntry 14 } - -pgmNeTsiOutParityRdata OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of parity RDATAs sent out from - this session." - ::= { pgmNeTsiPerformanceEntry 15 } - -pgmNeTsiInRdataNoStateErrors OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of received RDATA discarded - due to no retransmit state." - ::= { pgmNeTsiPerformanceEntry 16 } - -pgmNeTsiUniqueNaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of unique NAKs received for - this session." - ::= { pgmNeTsiPerformanceEntry 17 } - -pgmNeTsiInNaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of NAKs received for this - session." - ::= { pgmNeTsiPerformanceEntry 18 } - -pgmNeTsiOutNaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of NAKs sent out from this - session." - ::= { pgmNeTsiPerformanceEntry 19 } - -pgmNeTsiUniqueParityNaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of unique parity NAKs received - for this session." - ::= { pgmNeTsiPerformanceEntry 20 } - -pgmNeTsiInParityNaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of parity NAKs received for - this session." - ::= { pgmNeTsiPerformanceEntry 21 } - -pgmNeTsiOutParityNaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of parity NAKs sent out from - this session." - ::= { pgmNeTsiPerformanceEntry 22 } - -pgmNeTsiInNakSeqErrors OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of received NAKs discarded - because of out of sequence (out of retransmit - window)." - ::= { pgmNeTsiPerformanceEntry 23 } - -pgmNeTsiInNnaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of NNAKs received for this - session." - ::= { pgmNeTsiPerformanceEntry 24 } - -pgmNeTsiOutNnaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of NNAKs sent out from this - session." - ::= { pgmNeTsiPerformanceEntry 25 } - -pgmNeTsiInParityNnaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of parity NNAKs received for - this session." - ::= { pgmNeTsiPerformanceEntry 26 } - -pgmNeTsiOutParityNnaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of parity NNAKs sent out from - this session." - ::= { pgmNeTsiPerformanceEntry 27 } - -pgmNeTsiInNcfs OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of NCFs received for this - session." - ::= { pgmNeTsiPerformanceEntry 28 } - -pgmNeTsiOutNcfs OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of NCFs sent out from this - session." - ::= { pgmNeTsiPerformanceEntry 29 } - -pgmNeTsiInParityNcfs OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of parity NCFs received for - this session." - ::= { pgmNeTsiPerformanceEntry 30 } - -pgmNeTsiOutParityNcfs OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of Parity NCFs sent out from - this session." - ::= { pgmNeTsiPerformanceEntry 31 } - -pgmNeTsiSpmSequenceNumber OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Sequence number of the last seen SPM." - ::= {pgmNeTsiPerformanceEntry 32 } - -pgmNeTsiTransmissionGroupSize OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Advertised size of the transmission group for - this transport session." - ::= {pgmNeTsiPerformanceEntry 33 } - -pgmNeTsiTimeout OBJECT-TYPE - SYNTAX TimeTicks - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Time left for this entry to expire." - ::= {pgmNeTsiPerformanceEntry 34 } - -pgmNeTsiLastTtl OBJECT-TYPE - SYNTAX Unsigned32(1..255) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "IP TTL of last seen valid SPM." - ::= {pgmNeTsiPerformanceEntry 35 } - -pgmNeTsiLinkLossRate OBJECT-TYPE - SYNTAX Unsigned32(0..100) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Worst reported link loss rate for congestion - control. This is reported as a percentage." - ::= {pgmNeTsiPerformanceEntry 36 } - -pgmNeTsiPathLossRate OBJECT-TYPE - SYNTAX Unsigned32 (0..100) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Worst reported path loss rate for congestion - control. This is reported as a percentage." - ::= {pgmNeTsiPerformanceEntry 37 } - -pgmNeTsiReceiverLossRate OBJECT-TYPE - SYNTAX Unsigned32 (0..100) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Worst reported receiver loss rate for congestion - control. This is reported as a percentage." - ::= {pgmNeTsiPerformanceEntry 38 } - -pgmNeTsiCongestionReportLead OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Data lead sequence number associated with the - worst reported receiver loss rate." - ::= {pgmNeTsiPerformanceEntry 39 } - -pgmNeTsiCongestionReportWorstReceiver OBJECT-TYPE - SYNTAX IpAddress - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "IP address of the receiver that reported the - worst receiver loss rate." - ::= {pgmNeTsiPerformanceEntry 40 } - --- The PGM Retransmission table - --- The PGM Retransmission table contains --- information about current retransmission requests. --- This information is held per sequence number, or in --- the case of FEC, every transmission group, for which --- retransmission has been requested. - -pgmNeTsiRtxNumberOfEntries OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of entries in the retransmission table." - ::= { pgmNeTsi 5 } - -pgmNeTsiRtxTable OBJECT-TYPE - SYNTAX SEQUENCE OF PgmNeTsiRtxEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The table holding information for every sequence - number, or in the case of FEC, every - transmission group, for which retransmission has - been requested." - ::= {pgmNeTsi 6 } - -pgmNeTsiRtxEntry OBJECT-TYPE - SYNTAX PgmNeTsiRtxEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Per sequence number / transmission group - information." - INDEX { pgmNeTsiGlobalId, - pgmNeTsiDataSourcePort, - pgmNeTsiRtxSequenceNumber, - pgmNeTsiRtxSequenceNumberType } - ::= { pgmNeTsiRtxTable 1 } - -PgmNeTsiRtxEntry ::= SEQUENCE { - pgmNeTsiRtxSequenceNumber - Unsigned32, - pgmNeTsiRtxSequenceNumberType - INTEGER, - pgmNeTsiRtxReqParityTgCount - Counter32, - pgmNeTsiRtxTimeout - TimeTicks, - pgmNeTsiRtxStateBits - BITS -} - -pgmNeTsiRtxSequenceNumber OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "For non-parity retransmission, a sequence number. - For parity retransmission, a transmission group - and packet count." - ::= {pgmNeTsiRtxEntry 1 } - -pgmNeTsiRtxSequenceNumberType OBJECT-TYPE - SYNTAX INTEGER { - selective(1), - tg(2) - } - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Selective Sequence Number and TG Sequence - Number." - ::= {pgmNeTsiRtxEntry 2 } - -pgmNeTsiRtxReqParityTgCount OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The Requested number of missing parity packets - of specific Tg. The largest counter of the - received NAK will be stored in this mib. This - variable is valid for parity packets only." - ::= { pgmNeTsiRtxEntry 4 } - -pgmNeTsiRtxTimeout OBJECT-TYPE - SYNTAX TimeTicks - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "When this state will expire." - ::= {pgmNeTsiRtxEntry 5 } - -pgmNeTsiRtxStateBits OBJECT-TYPE - SYNTAX BITS { - initialising(0), - eliminating(1), - redirecting(2), - stateCreatedByNullNAK(3), - listNAKentry(4), - parityState(5) - } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "State associated with retransmission entry." - ::= {pgmNeTsiRtxEntry 6 } - --- The PGM Retransmission interfaces table - --- The PGM Retransmission interfaces table contains --- information about what interfaces will be sent --- retransmitted data for a particular --- retransmission entry - -pgmNeTsiRtxIfNumberOfEntries OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of entries in the retransmission - interfaces table." - ::= { pgmNeTsi 7 } - -pgmNeTsiRtxIfTable OBJECT-TYPE - SYNTAX SEQUENCE OF PgmNeTsiRtxIfEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The table holding information of every - interface for which retransmit state for - a particular sequence number or transmission - group has to be sent." - ::= {pgmNeTsi 8} - -pgmNeTsiRtxIfEntry OBJECT-TYPE - SYNTAX PgmNeTsiRtxIfEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Destination interfaces for a particular - retransmit state." - INDEX { pgmNeTsiGlobalId, - pgmNeTsiDataSourcePort, - pgmNeTsiRtxSequenceNumber, - pgmNeTsiRtxSequenceNumberType, - pgmNeTsiRtxIfIndex } - ::= { pgmNeTsiRtxIfTable 1 } - -PgmNeTsiRtxIfEntry ::= SEQUENCE { - pgmNeTsiRtxIfIndex - InterfaceIndex, - pgmNeTsiRtxIfPacketCount - Counter32 -} - -pgmNeTsiRtxIfIndex OBJECT-TYPE - SYNTAX InterfaceIndex - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "A unique value for each interface. Its value - ranges between 1 and the value of ifNumber. - The value for each interface must remain - constant at least from one re-initialization - of the entity's network management system to - the next re-initialization." - ::= { pgmNeTsiRtxIfEntry 1 } - -pgmNeTsiRtxIfPacketCount OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of repair data packets still to be - retransmitted on this interface. For non-parity - retransmission this will never have a value - greater than 1. For parity retransmission, - any number can be present." - ::= { pgmNeTsiRtxIfEntry 2 } - --- The PGM Poll Response table - --- The PGM Poll Response table contains information --- about PGM parent's of this network element who are --- currently polling it. - -pgmNeTsiPolrNumberOfEntries OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of entries in the poll response table." - ::= { pgmNeTsi 9 } - -pgmNeTsiPolrTable OBJECT-TYPE - SYNTAX SEQUENCE OF PgmNeTsiPolrEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The table holding state information about what - PGM parents are polling this Network Element." - ::= { pgmNeTsi 10 } - -pgmNeTsiPolrEntry OBJECT-TYPE - SYNTAX PgmNeTsiPolrEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "State information for a Network Element that - is being polled by its parents" - INDEX { pgmNeTsiGlobalId, - pgmNeTsiDataSourcePort, - pgmNeTsiPolrSource } - ::= { pgmNeTsiPolrTable 1 } - -PgmNeTsiPolrEntry ::= SEQUENCE { - pgmNeTsiPolrSource - IpAddress, - pgmNeTsiPolrSequenceNumber - Unsigned32 -} - -pgmNeTsiPolrSource OBJECT-TYPE - SYNTAX IpAddress - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "IP Address of parent who is polling this - device." - ::= { pgmNeTsiPolrEntry 1 } - -pgmNeTsiPolrSequenceNumber OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Sequence number of last POLR from the source." - ::= { pgmNeTsiPolrEntry 2 } - --- The PGM Poll table - --- The PGM Poll table contains information related to --- polling that this Network Element is doing for --- its children - -pgmNeTsiPollNumberOfEntries OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of entries in the poll table." - ::= { pgmNeTsi 11 } - -pgmNeTsiPollTable OBJECT-TYPE - SYNTAX SEQUENCE OF PgmNeTsiPollEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The table holding state information related - to polling that this Network Element is doing - for its children." - ::= { pgmNeTsi 12 } - -pgmNeTsiPollEntry OBJECT-TYPE - SYNTAX PgmNeTsiPollEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "State information for a Network Element that - is polling its children." - INDEX { pgmNeTsiGlobalId, - pgmNeTsiDataSourcePort, - pgmNeTsiPollType } - ::= { pgmNeTsiPollTable 1 } - -PgmNeTsiPollEntry ::= SEQUENCE { - pgmNeTsiPollType - INTEGER, - pgmNeTsiPollSequence - Unsigned32, - pgmNeTsiPollChildBackoff - Unsigned32, - pgmNeTsiPollMask - Unsigned32, - pgmNeTsiPollPeriod - Unsigned32, - pgmNeTsiPollCount - Counter32, - pgmNeTsiPollTimeout - TimeTicks -} - -pgmNeTsiPollType OBJECT-TYPE - SYNTAX INTEGER { - general(1), - dlr(2) - } - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Type of Poll." - ::= { pgmNeTsiPollEntry 1 } - -pgmNeTsiPollSequence OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Sequence number of the most recent POLL packet - that we sent." - ::= { pgmNeTsiPollEntry 2 } - -pgmNeTsiPollChildBackoff OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Backoff advertised to be used by child of poll." - ::= { pgmNeTsiPollEntry 3 } - -pgmNeTsiPollMask OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Mask being used in poll." - ::= { pgmNeTsiPollEntry 4 } - -pgmNeTsiPollPeriod OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Period of poll." - ::= { pgmNeTsiPollEntry 5 } - -pgmNeTsiPollCount OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of Poll responses (POLRs) received." - ::= { pgmNeTsiPollEntry 6 } - -pgmNeTsiPollTimeout OBJECT-TYPE - SYNTAX TimeTicks - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Remaining Time Ticks to next poll." - ::= { pgmNeTsiPollEntry 7 } - - --- --- PGM Source --- - --- PGM Source general management information - - -pgmSourceSaveDefaults OBJECT-TYPE - SYNTAX INTEGER { initial (1), - save (2), - pending (3), - success (4), - failure (5) } - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Flag used to initiate the storing - of all default variable values to - non-volatile storage and to report the - result of the operation. - The following values can only be read, - never written : - initial(1) - returned prior to any requests - for saving the default configuration - pending(3) - saving in progress - success(4) - returned when a save(2) request - is successful - failure(5) - returned when a save(2) request - is unsuccessful - - The following values can only be written, - never read : - save(2) - to indicate that the default - configuration should be saved." - ::= { pgmSource 1 } - -pgmSourceLastUpdateTime OBJECT-TYPE - SYNTAX TimeTicks - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of TimeTicks since the last update - of the non-volatile storage." - ::= { pgmSource 2 } - -pgmSourceDefaultTtl OBJECT-TYPE - SYNTAX Unsigned32(1..255) - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Default TTL used by the PGM Source." - ::= { pgmSource 3 } - -pgmSourceDefaultAdvMode OBJECT-TYPE - SYNTAX INTEGER { data(1), - time(2), - applctrl(3), - other(4) } - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Flag to indicate that the transmit window is - advanced with data, by time, under application - control, or any other method." - ::= { pgmSource 4 } - -pgmSourceDefaultLateJoin OBJECT-TYPE - SYNTAX INTEGER { - enable(1), - disable(2) - } - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Flag to indicate whether or not the sender will - accept late joiners." - ::= { pgmSource 5 } - -pgmSourceDefaultTxwMaxRte OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Maximum transmit rate in bytes/second." - ::= { pgmSource 6 } - -pgmSourceDefaultTxwSecs OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "seconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Transmit window size in seconds." - ::= { pgmSource 7 } - -pgmSourceDefaultTxwAdvSecs OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "seconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Transmit window advance in seconds. This value - should always be set to a value smaller than - the pgmSourceTxwSecs." - ::= { pgmSource 8 } - -pgmSourceDefaultAdvIvl OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "milliseconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Advance interval in milliseconds. Always a - valid parameter when advancing with time. - Valid only in cases of absence of lost data - when advancing with data." - ::= { pgmSource 9 } - -pgmSourceDefaultSpmIvl OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "milliseconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "SPM interval in milliseconds." - ::= { pgmSource 10 } - -pgmSourceDefaultSpmHeartBeatIvlMin OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "milliseconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "SPM heartbeat interval in milliseconds." - ::= { pgmSource 11 } - -pgmSourceDefaultSpmHeartBeatIvlMax OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "milliseconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Maximum SPM heartbeat interval in milliseconds." - ::= { pgmSource 12 } - -pgmSourceDefaultRdataBackoffIvl OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "milliseconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "RDATA backoff interval in milliseconds." - ::= { pgmSource 13 } - -pgmSourceDefaultFECProactiveParitySize OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Number of proactive parity messages per FEC - block." - ::= { pgmSource 14 } - -pgmSourceDefaultGroupAddress OBJECT-TYPE - SYNTAX IpAddress - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The default IP Multicast group address - used by the sender." - ::= { pgmSource 15 } - -pgmSourceUpdateSinceLastSave OBJECT-TYPE - SYNTAX INTEGER - { - notUpdated(1), - updated(2) - } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Specifies if any of the Source Default - variables have been updated or not, - since the last successful pgmSourceSaveDefaults. - notUpdated - none of the default Source - variables were set after the last - successful save to a non-volatile - storage. - updated - at least one of the default Source - variables were set after the last - successful save to a non-volatile - storage." - ::= { pgmSource 16 } - --- PGM Source per TSI management information - -pgmSourceTsi OBJECT IDENTIFIER ::= { pgmSource 100 } - -pgmSourceNumberOfEntries OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of PGM Source sessions." - ::= { pgmSourceTsi 1 } - --- PGM Source Fault Management Table - -pgmSourceTable OBJECT-TYPE - SYNTAX SEQUENCE OF PgmSourceEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The table holding per TSI fault - management and general information - related to PGM Source." - ::= {pgmSourceTsi 2} - -pgmSourceEntry OBJECT-TYPE - SYNTAX PgmSourceEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Per PGM sender information." - INDEX { pgmSourceGlobalId, - pgmSourceSourcePort } - ::= { pgmSourceTable 1 } - -PgmSourceEntry ::= SEQUENCE { - pgmSourceGlobalId - OCTET STRING, - pgmSourceSourcePort - Unsigned32, - pgmSourceSourceAddress - IpAddress, - pgmSourceGroupAddress - IpAddress, - pgmSourceDestPort - Unsigned32, - pgmSourceSourceGsi - OCTET STRING, - pgmSourceSourcePortNumber - Unsigned32 - } - -pgmSourceGlobalId OBJECT-TYPE - SYNTAX OCTET STRING (SIZE (12)) - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Globally unique session identifier (GSI)." - ::= { pgmSourceEntry 1 } - -pgmSourceSourcePort OBJECT-TYPE - SYNTAX Unsigned32(0..65535) - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Source port number." - ::= { pgmSourceEntry 2 } - -pgmSourceSourceAddress OBJECT-TYPE - SYNTAX IpAddress - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Source IP address." - ::= { pgmSourceEntry 3 } - -pgmSourceGroupAddress OBJECT-TYPE - SYNTAX IpAddress - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "IP Multicast group address used by the - sender." - ::= { pgmSourceEntry 4 } - -pgmSourceDestPort OBJECT-TYPE - SYNTAX Unsigned32 (0..65535) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Destination port number." - ::= { pgmSourceEntry 5 } - -pgmSourceSourceGsi OBJECT-TYPE - SYNTAX OCTET STRING (SIZE (12)) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Globally unique session identifier (GSI)." - ::= { pgmSourceEntry 6 } - -pgmSourceSourcePortNumber OBJECT-TYPE - SYNTAX Unsigned32(0..65535) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Source port number." - ::= { pgmSourceEntry 7 } - - --- PGM Source Configuration Management Table - -pgmSourceConfigTable OBJECT-TYPE - SYNTAX SEQUENCE OF PgmSourceConfigEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The table holding per TSI - configuration information - related to the PGM Source." - ::= {pgmSourceTsi 3} - -pgmSourceConfigEntry OBJECT-TYPE - SYNTAX PgmSourceConfigEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Per PGM sender information." - INDEX { pgmSourceConfigGlobalId, - pgmSourceConfigSourcePort } - ::= { pgmSourceConfigTable 1 } - -PgmSourceConfigEntry ::= SEQUENCE { - pgmSourceConfigGlobalId - OCTET STRING, - pgmSourceConfigSourcePort - Unsigned32, - pgmSourceTtl - Unsigned32, - pgmSourceAdvMode - INTEGER, - pgmSourceLateJoin - INTEGER, - pgmSourceTxwMaxRte - Unsigned32, - pgmSourceTxwSecs - Unsigned32, - pgmSourceTxwAdvSecs - Unsigned32, - pgmSourceAdvIvl - Unsigned32, - pgmSourceSpmIvl - Unsigned32, - pgmSourceSpmHeartBeatIvlMin - Unsigned32, - pgmSourceSpmHeartBeatIvlMax - Unsigned32, - pgmSourceRdataBackoffIvl - Unsigned32, - pgmSourceFEC - INTEGER, - pgmSourceFECTransmissionGrpSize - Unsigned32, - pgmSourceFECProactiveParitySize - Unsigned32, - pgmSourceSpmPathAddress - IpAddress - } - -pgmSourceConfigGlobalId OBJECT-TYPE - SYNTAX OCTET STRING (SIZE (12)) - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Globally unique session identifier (GSI)." - ::= { pgmSourceConfigEntry 1 } - -pgmSourceConfigSourcePort OBJECT-TYPE - SYNTAX Unsigned32(0..65535) - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Source port number." - ::= { pgmSourceConfigEntry 2 } - -pgmSourceTtl OBJECT-TYPE - SYNTAX Unsigned32(1..255) - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "TTL used by sender." - ::= { pgmSourceConfigEntry 3 } - -pgmSourceAdvMode OBJECT-TYPE - SYNTAX INTEGER { data(1), - time(2), - applctrl(3), - other(4) } - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Flag to indicate that the transmit window is - advanced with data, by time, under application - control, or any other method." - ::= { pgmSourceConfigEntry 4 } - -pgmSourceLateJoin OBJECT-TYPE - SYNTAX INTEGER { - enable(1), - disable(2) - } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Flag to indicate whether or not the sender will - accept late joiners." - ::= { pgmSourceConfigEntry 5 } - -pgmSourceTxwMaxRte OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Maximum transmit rate in bytes/second." - ::= { pgmSourceConfigEntry 6 } - -pgmSourceTxwSecs OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "seconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Transmit window size in seconds." - ::= { pgmSourceConfigEntry 7 } - -pgmSourceTxwAdvSecs OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "seconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Transmit window advance in seconds. This value - should always be set to a value smaller than - the pgmSourceTxwSecs." - ::= { pgmSourceConfigEntry 8 } - -pgmSourceAdvIvl OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "milliseconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Advance interval in milliseconds. Always a - valid parameter when advancing with time. - Valid only in cases of absence of lost data - when advancing with data." - ::= { pgmSourceConfigEntry 9 } - -pgmSourceSpmIvl OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "milliseconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "SPM interval in milliseconds." - ::= { pgmSourceConfigEntry 10 } - -pgmSourceSpmHeartBeatIvlMin OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "milliseconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "SPM heartbeat interval in milliseconds." - ::= { pgmSourceConfigEntry 11 } - -pgmSourceSpmHeartBeatIvlMax OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "milliseconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Maximum SPM heartbeat interval in milliseconds." - ::= { pgmSourceConfigEntry 12 } - -pgmSourceRdataBackoffIvl OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "milliseconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "RDATA backoff interval in milliseconds." - ::= { pgmSourceConfigEntry 13 } - -pgmSourceFEC OBJECT-TYPE - SYNTAX INTEGER { disabled(1), - enabledFixedPacketSize(2), - enabledVariablePacketSize(3) } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Flag to indicate whether or not FEC is enabled - and whether it supports variable or fixed size - messages." - ::= { pgmSourceConfigEntry 14 } - -pgmSourceFECTransmissionGrpSize OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "FEC transmission group size." - ::= { pgmSourceConfigEntry 15 } - -pgmSourceFECProactiveParitySize OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Number of proactive parity messages per FEC - block." - ::= { pgmSourceConfigEntry 16 } - -pgmSourceSpmPathAddress OBJECT-TYPE - SYNTAX IpAddress - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Ip Address for the NAKs to be sent, - in case that NE is not set." - ::= { pgmSourceConfigEntry 17 } - --- PGM Source Performance Management Table - -pgmSourcePerformanceTable OBJECT-TYPE - SYNTAX SEQUENCE OF PgmSourcePerformanceEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The table holding per TSI performance - information related to the PGM Source." - ::= {pgmSourceTsi 4} - -pgmSourcePerformanceEntry OBJECT-TYPE - SYNTAX PgmSourcePerformanceEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Per PGM sender information." - INDEX { pgmSourcePerformanceGlobalId, - pgmSourcePerformanceSourcePort } - ::= { pgmSourcePerformanceTable 1 } - -PgmSourcePerformanceEntry ::= SEQUENCE { - pgmSourcePerformanceGlobalId - OCTET STRING, - pgmSourcePerformanceSourcePort - Unsigned32, - pgmSourceDataBytesSent - Counter32, - pgmSourceDataMsgsSent - Counter32, - pgmSourceBytesBuffered - Counter32, - pgmSourceMsgsBuffered - Counter32, - pgmSourceBytesRetransmitted - Counter32, - pgmSourceMsgsRetransmitted - Counter32, - pgmSourceBytesSent - Counter32, - pgmSourceRawNaksReceived - Counter32, - pgmSourceNaksIgnored - Counter32, - pgmSourceCksumErrors - Counter32, - pgmSourceMalformedNaks - Counter32, - pgmSourcePacketsDiscarded - Counter32, - pgmSourceNaksRcvd - Counter32, - pgmSourceParityBytesRetransmitted - Counter32, - pgmSourceSelectiveBytesRetransmited - Counter32, - pgmSourceParityMsgsRetransmitted - Counter32, - pgmSourceSelectiveMsgsRetransmitted - Counter32, - pgmSourceBytesAdmit - Counter32, - pgmSourceMsgsAdmit - Counter32, - pgmSourceParityNakPacketsReceived - Counter32, - pgmSourceSelectiveNakPacketsReceived - Counter32, - pgmSourceParityNaksReceived - Counter32, - pgmSourceSelectiveNaksReceived - Counter32, - pgmSourceParityNaksIgnored - Counter32, - pgmSourceSelectiveNaksIgnored - Counter32, - pgmSourceAckErrors - Counter32, - pgmSourcePgmCCAcker - IpAddress, - pgmSourceTransmissionCurrentRate - Counter32, - pgmSourceAckPacketsReceived - Counter32, - pgmSourceNNakPacketsReceived - Counter32, - pgmSourceParityNNakPacketsReceived - Counter32, - pgmSourceSelectiveNNakPacketsReceived - Counter32, - pgmSourceNNaksReceived - Counter32, - pgmSourceParityNNaksReceived - Counter32, - pgmSourceSelectiveNNaksReceived - Counter32, - pgmSourceNNakErrors - Counter32 -} - -pgmSourcePerformanceGlobalId OBJECT-TYPE - SYNTAX OCTET STRING (SIZE (12)) - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Globally unique source identifier (GSI)." - ::= { pgmSourcePerformanceEntry 1 } - -pgmSourcePerformanceSourcePort OBJECT-TYPE - SYNTAX Unsigned32(0..65535) - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Source port number." - ::= { pgmSourcePerformanceEntry 2 } - -pgmSourceDataBytesSent OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of data bytes sent for this TSI." - ::= { pgmSourcePerformanceEntry 3 } - -pgmSourceDataMsgsSent OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of data messages sent for this TSI." - ::= { pgmSourcePerformanceEntry 4 } - -pgmSourceBytesBuffered OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of bytes currently buffered for this - TSI." - ::= { pgmSourcePerformanceEntry 5 } - -pgmSourceMsgsBuffered OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of messages currently buffered for - this TSI." - ::= { pgmSourcePerformanceEntry 6 } - -pgmSourceBytesRetransmitted OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of bytes retransmitted for this TSI." - ::= { pgmSourcePerformanceEntry 7 } - -pgmSourceMsgsRetransmitted OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of messages retransmitted for this TSI." - ::= { pgmSourcePerformanceEntry 8 } - -pgmSourceBytesSent OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The number of bytes send for this TSI. Includes - IP header and non-data messages." - ::= { pgmSourcePerformanceEntry 9 } - -pgmSourceRawNaksReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Raw number of NAK packets received." - ::= { pgmSourcePerformanceEntry 10 } - -pgmSourceNaksIgnored OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of ignored Naks for this TSI, due to - duplicate NAKs reception." - ::= { pgmSourcePerformanceEntry 11 } - -pgmSourceCksumErrors OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of checksum errors for this TSI." - ::= { pgmSourcePerformanceEntry 12 } - -pgmSourceMalformedNaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of malformed NAK packets." - ::= { pgmSourcePerformanceEntry 13 } - -pgmSourcePacketsDiscarded OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of discarded data packets. This counter - is used to count all discarded incoming packets - per TSI in cases of duplicates, header and - packet errors, etc." - ::= { pgmSourcePerformanceEntry 14 } - -pgmSourceNaksRcvd OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of Sequence Numbers NAKed." - ::= { pgmSourcePerformanceEntry 15 } - -pgmSourceParityBytesRetransmitted OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of bytes sent in parity retransmissions." - ::= { pgmSourcePerformanceEntry 16 } - -pgmSourceSelectiveBytesRetransmited OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of bytes sent in selective retransmissions." - ::= { pgmSourcePerformanceEntry 17 } - -pgmSourceParityMsgsRetransmitted OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of parity retransmissions sent." - ::= { pgmSourcePerformanceEntry 18 } - -pgmSourceSelectiveMsgsRetransmitted OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of selective retransmissions sent." - ::= { pgmSourcePerformanceEntry 19 } - -pgmSourceBytesAdmit OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of bytes currently in the rate controled - admit queue. Includes IP header, UDP header if - encapsulated, PGM header, and data." - ::= { pgmSourcePerformanceEntry 20 } - -pgmSourceMsgsAdmit OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of messages currently in the rate controled - admit queue. Includes data messages, retransmissions, - and SPMs." - ::= { pgmSourcePerformanceEntry 21 } - -pgmSourceParityNakPacketsReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of parity NAK packets received." - ::= { pgmSourcePerformanceEntry 22 } - -pgmSourceSelectiveNakPacketsReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of selective NAK packets received." - ::= { pgmSourcePerformanceEntry 23 } - -pgmSourceParityNaksReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual parity NAKs received." - ::= { pgmSourcePerformanceEntry 24 } - -pgmSourceSelectiveNaksReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual selective NAKs received." - ::= { pgmSourcePerformanceEntry 25 } - -pgmSourceParityNaksIgnored OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual parity NAKs ignored." - ::= { pgmSourcePerformanceEntry 26 } - -pgmSourceSelectiveNaksIgnored OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual selective NAKs ignored." - ::= { pgmSourcePerformanceEntry 27 } - -pgmSourceAckErrors OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of ACK packets received with error in - them, different than checksum error." - ::= { pgmSourcePerformanceEntry 28 } - -pgmSourcePgmCCAcker OBJECT-TYPE - SYNTAX IpAddress - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Ip Address of the currently designated pgm - congestion control ACKER." - ::= { pgmSourcePerformanceEntry 29 } - -pgmSourceTransmissionCurrentRate OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Current transmission rate." - ::= { pgmSourcePerformanceEntry 30 } - -pgmSourceAckPacketsReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of ACK packets received." - ::= { pgmSourcePerformanceEntry 31 } - -pgmSourceNNakPacketsReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of Null NAKs received." - ::= { pgmSourcePerformanceEntry 32 } - -pgmSourceParityNNakPacketsReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of parity Null NAK packets received." - ::= { pgmSourcePerformanceEntry 33 } - -pgmSourceSelectiveNNakPacketsReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of selective Null NAK packets received." - ::= { pgmSourcePerformanceEntry 34 } - -pgmSourceNNaksReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual NAKs, received in Null - NAK packets." - ::= { pgmSourcePerformanceEntry 35 } - -pgmSourceParityNNaksReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual parity NAKs, - received in Null NAK packets." - ::= { pgmSourcePerformanceEntry 36 } - -pgmSourceSelectiveNNaksReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual NAKs, - received in Null NAK packets." - ::= { pgmSourcePerformanceEntry 37 } - -pgmSourceNNakErrors OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of Null NAK packets received that contain - error, different than checksum error." - ::= { pgmSourcePerformanceEntry 38 } - --- --- PGM Receiver --- - --- PGM Receiver general management information - -pgmReceiverSaveDefaults OBJECT-TYPE - SYNTAX INTEGER { initial (1), - save (2), - pending (3), - success (4), - failure (5) } - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Flag used to initiate the storing - of all default variable values to - non-volatile storage and to report the - result of the operation. - The following values can only be read, - never written : - initial(1) - returned prior to any requests - for saving the default configuration - pending(3) - saving in progress - success(4) - returned when a save(2) request - is successful - failure(5) - returned when a save(2) request - is unsuccessful - - The following values can only be written, - never read : - save(2) - to indicate that the default - configuration should be saved." - ::= { pgmReceiver 1 } - -pgmReceiverLastUpdateTime OBJECT-TYPE - SYNTAX TimeTicks - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of TimeTicks since the last update - of the non-volatile storage." - ::= { pgmReceiver 2 } - -pgmReceiverDefaultNakBackoffIvl OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "NAK random backoff interval." - ::= { pgmReceiver 3 } - -pgmReceiverDefaultNakRepeatIvl OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "NAK repeat interval." - ::= { pgmReceiver 4 } - -pgmReceiverDefaultNakNcfRetries OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Max NAK retries while witing for matching NCF." - ::= { pgmReceiver 5 } - -pgmReceiverDefaultNakRdataIvl OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Default NAK RDATA interval, i.e the amount of - time to cease NAKs for a particular piece of - data after a corresponding NCF has been received." - ::= { pgmReceiver 6 } - -pgmReceiverDefaultNakDataRetries OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Max NAK retries while waiting for missing data." - ::= { pgmReceiver 7 } - -pgmReceiverDefaultSendNaks OBJECT-TYPE - SYNTAX INTEGER { - enabled(1), - disabled(2) - } - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Flag to indicate whether or not receiver should - send NAKs or be totally passive." - ::= { pgmReceiver 8 } - -pgmReceiverDefaultLateJoin OBJECT-TYPE - SYNTAX INTEGER { - enabled(1), - disabled(2) - } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Flag to indicate whether or not the receiver - should wait for a OPT_JOIN SPM before - attempting to late join." - ::= { pgmReceiver 9 } - -pgmReceiverDefaultNakTtl OBJECT-TYPE - SYNTAX Unsigned32(1..255) - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "TTL on NAK packets sent for loss." - ::= { pgmReceiver 10 } - -pgmReceiverDefaultDeliveryOrder OBJECT-TYPE - SYNTAX INTEGER { - unordered(1), - ordered(2) - } - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Packet Delivery Order for the receiving - application." - ::= { pgmReceiver 11 } - -pgmReceiverDefaultNextPgmHop OBJECT-TYPE - SYNTAX IpAddress - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Next hop PGM router address. This option - sets the default address to send NAKs to, - instead of sending to the last hop address." - ::= { pgmReceiver 12 } - -pgmReceiverDefaultGroupAddress OBJECT-TYPE - SYNTAX IpAddress - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Default IP Multicast group address - used by the sender." - ::= { pgmReceiver 13 } - -pgmReceiverUpdateSinceLastSave OBJECT-TYPE - SYNTAX INTEGER - { - notUpdated(1), - updated(2) - } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Specifies if any of the Receiver Default - variables have been updated or not, - since the last successful pgmSourceSaveDefaults. - notUpdated - none of the default Receiver - variables were set after the last - successful save to a non-volatile - storage. - updated - at least one of the default Receiver - variables were set after the last - successful save to a non-volatile - storage." - ::= { pgmReceiver 14 } - -pgmReceiverDefaultNakFailureThresholdTimer OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "seconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Timer that defines the default - interval of time during which unrecoverable - lost packets are monitored - for purposes of SNMP trap generation." - ::= { pgmReceiver 15 } - -pgmReceiverDefaultNakFailureThreshold OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "The default number of unrecoverable - lost packets within the defined interval - after which an SNMP trap is generated." - ::= { pgmReceiver 16 } - - --- PGM Receiver per Receiver management information - -pgmReceiverTsi OBJECT IDENTIFIER ::= { pgmReceiver 100 } - -pgmReceiverNumberOfEntries OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of PGM Receivers." - ::= { pgmReceiverTsi 1 } - --- PGM Receiver Fault Management Table - -pgmReceiverTable OBJECT-TYPE - SYNTAX SEQUENCE OF PgmReceiverEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The table holding per TSI fault - management and general information - related to the PGM Receiver." - ::= {pgmReceiverTsi 2} - -pgmReceiverEntry OBJECT-TYPE - SYNTAX PgmReceiverEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Per PGM receiver fault management - and general information." - INDEX { pgmReceiverGlobalId, - pgmReceiverSourcePort, - pgmReceiverInstance } - ::= { pgmReceiverTable 1 } - -PgmReceiverEntry ::= SEQUENCE { - pgmReceiverGlobalId - OCTET STRING, - pgmReceiverSourcePort - Unsigned32, - pgmReceiverInstance - Unsigned32, - pgmReceiverGroupAddress - IpAddress, - pgmReceiverDestPort - Unsigned32, - pgmReceiverSourceAddress - IpAddress, - pgmReceiverLastHop - IpAddress, - pgmReceiverSourceGsi - OCTET STRING, - pgmReceiverSourcePortNumber - Unsigned32, - pgmReceiverUniqueInstance - Unsigned32 - } - -pgmReceiverGlobalId OBJECT-TYPE - SYNTAX OCTET STRING (SIZE (12)) - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Globally unique source identifier (GSI)." - ::= { pgmReceiverEntry 1 } - -pgmReceiverSourcePort OBJECT-TYPE - SYNTAX Unsigned32(0..65535) - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Source port number." - ::= { pgmReceiverEntry 2 } - -pgmReceiverInstance OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Positive number, uniquely identifying - a Receiver." - ::= { pgmReceiverEntry 3 } - -pgmReceiverGroupAddress OBJECT-TYPE - SYNTAX IpAddress - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "IP Multicast group address used by the sender." - ::= { pgmReceiverEntry 4 } - -pgmReceiverDestPort OBJECT-TYPE - SYNTAX Unsigned32(0..65535) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Destination port number." - ::= { pgmReceiverEntry 5 } - -pgmReceiverSourceAddress OBJECT-TYPE - SYNTAX IpAddress - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Source IP address number." - ::= { pgmReceiverEntry 6 } - -pgmReceiverLastHop OBJECT-TYPE - SYNTAX IpAddress - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Last hop PGM router address." - ::= { pgmReceiverEntry 7 } - -pgmReceiverSourceGsi OBJECT-TYPE - SYNTAX OCTET STRING (SIZE (12)) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Globally unique source identifier (GSI)." - ::= { pgmReceiverEntry 8 } - -pgmReceiverSourcePortNumber OBJECT-TYPE - SYNTAX Unsigned32(0..65535) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Source port number." - ::= { pgmReceiverEntry 9 } - -pgmReceiverUniqueInstance OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Positive number, uniquely identifying - a Receiver." - ::= { pgmReceiverEntry 10 } - --- PGM Receiver Configuration Management Table - -pgmReceiverConfigTable OBJECT-TYPE - SYNTAX SEQUENCE OF PgmReceiverConfigEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The table holding per TSI configuration - management information related - to the PGM Receiver." - ::= {pgmReceiverTsi 3 } - -pgmReceiverConfigEntry OBJECT-TYPE - SYNTAX PgmReceiverConfigEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Per PGM receiver configuration management - information." - INDEX { pgmReceiverConfigGlobalId, - pgmReceiverConfigSourcePort, - pgmReceiverConfigInstance } - ::= { pgmReceiverConfigTable 1 } - -PgmReceiverConfigEntry ::= SEQUENCE { - pgmReceiverConfigGlobalId - OCTET STRING, - pgmReceiverConfigSourcePort - Unsigned32, - pgmReceiverConfigInstance - Unsigned32, - pgmReceiverNakBackoffIvl - Unsigned32, - pgmReceiverNakRepeatIvl - Unsigned32, - pgmReceiverNakNcfRetries - Unsigned32, - pgmReceiverNakRdataIvl - Unsigned32, - pgmReceiverNakDataRetries - Unsigned32, - pgmReceiverSendNaks - INTEGER, - pgmReceiverLateJoin - INTEGER, - pgmReceiverNakTtl - Unsigned32, - pgmReceiverDeliveryOrder - INTEGER, - pgmReceiverMcastNaks - INTEGER, - pgmReceiverNakFailureThresholdTimer - Unsigned32, - pgmReceiverNakFailureThreshold - Unsigned32 - } - -pgmReceiverConfigGlobalId OBJECT-TYPE - SYNTAX OCTET STRING (SIZE (12)) - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Globally unique source identifier (GSI)." - ::= { pgmReceiverConfigEntry 1 } - -pgmReceiverConfigSourcePort OBJECT-TYPE - SYNTAX Unsigned32(0..65535) - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Source port number." - ::= { pgmReceiverConfigEntry 2 } - -pgmReceiverConfigInstance OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Positive number, uniquely identifying - a Receiver." - ::= { pgmReceiverConfigEntry 3 } - -pgmReceiverNakBackoffIvl OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "NAK random backoff interval." - ::= { pgmReceiverConfigEntry 4 } - -pgmReceiverNakRepeatIvl OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "NAK repeat interval." - ::= { pgmReceiverConfigEntry 5 } - -pgmReceiverNakNcfRetries OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Max NAK retries while witing for matching NCF." - ::= { pgmReceiverConfigEntry 6 } - -pgmReceiverNakRdataIvl OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "NAK RDATA interval." - ::= { pgmReceiverConfigEntry 7 } - -pgmReceiverNakDataRetries OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Max NAK retries while waiting for missing data." - ::= { pgmReceiverConfigEntry 8 } - -pgmReceiverSendNaks OBJECT-TYPE - SYNTAX INTEGER { - enabled(1), - disabled(2) - } - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Flag to indicate whether or not receiver should - send NAKs or be totally passive." - ::= { pgmReceiverConfigEntry 9 } - -pgmReceiverLateJoin OBJECT-TYPE - SYNTAX INTEGER { - enabled(1), - disabled(2) - } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Flag to indicate whether or not the receiver - should wait for a OPT_JOIN SPM before - attempting to late join." - ::= { pgmReceiverConfigEntry 10 } - -pgmReceiverNakTtl OBJECT-TYPE - SYNTAX Unsigned32(1..255) - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "TTL on NAK packets sent for loss." - ::= { pgmReceiverConfigEntry 11 } - -pgmReceiverDeliveryOrder OBJECT-TYPE - SYNTAX INTEGER { - unordered(1), - ordered(2) - } - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Packet Delivery Order for the receiving - application." - ::= { pgmReceiverConfigEntry 12 } - -pgmReceiverMcastNaks OBJECT-TYPE - SYNTAX INTEGER { - enabled(1), - disabled(2) - } - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Flag to indicate whether or not receiver should - send multicast NAKs." - ::= { pgmReceiverConfigEntry 13 } - -pgmReceiverNakFailureThresholdTimer OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "seconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Timer that defines per receiver - interval of time during which unrecoverable - lost packets are monitored - for purposes of SNMP trap generation." - ::= { pgmReceiverConfigEntry 14 } - -pgmReceiverNakFailureThreshold OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "The number of unrecoverable lost packets - within the defined interval - after which an SNMP trap is generated." - ::= { pgmReceiverConfigEntry 15 } - - --- PGM Receiver Performance Management Table - -pgmReceiverPerformanceTable OBJECT-TYPE - SYNTAX SEQUENCE OF PgmReceiverPerformanceEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The table holding per TSI - performance management information - related to the PGM Receiver." - ::= {pgmReceiverTsi 4} - -pgmReceiverPerformanceEntry OBJECT-TYPE - SYNTAX PgmReceiverPerformanceEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Per PGM Receiver session performance - management information." - INDEX { pgmReceiverPerformanceGlobalId, - pgmReceiverPerformanceSourcePort, - pgmReceiverPerformanceInstance } - ::= { pgmReceiverPerformanceTable 1 } - -PgmReceiverPerformanceEntry ::= SEQUENCE { - pgmReceiverPerformanceGlobalId - OCTET STRING, - pgmReceiverPerformanceSourcePort - Unsigned32, - pgmReceiverPerformanceInstance - Unsigned32, - pgmReceiverDataBytesReceived - Counter32, - pgmReceiverDataMsgsReceived - Counter32, - pgmReceiverNaksSent - Counter32, - pgmReceiverNaksRetransmitted - Counter32, - pgmReceiverNakFailures - Counter32, - pgmReceiverBytesReceived - Counter32, - pgmReceiverNaksSuppressed - Counter32, - pgmReceiverCksumErrors - Counter32, - pgmReceiverMalformedSpms - Counter32, - pgmReceiverMalformedOdata - Counter32, - pgmReceiverMalformedRdata - Counter32, - pgmReceiverMalformedNcfs - Counter32, - pgmReceiverPacketsDiscarded - Counter32, - pgmReceiverLosses - Counter32, - pgmReceiverBytesDeliveredToApp - Counter32, - pgmReceiverMsgsDeliveredToApp - Counter32, - pgmReceiverDupSpms - Counter32, - pgmReceiverDupDatas - Counter32, - pgmReceiverDupParities - Counter32, - pgmReceiverNakPacketsSent - Counter32, - pgmReceiverParityNakPacketsSent - Counter32, - pgmReceiverSelectiveNakPacketsSent - Counter32, - pgmReceiverParityNaksSent - Counter32, - pgmReceiverSelectiveNaksSent - Counter32, - pgmReceiverParityNaksRetransmitted - Counter32, - pgmReceiverSelectiveNaksRetransmitted - Counter32, - pgmReceiverNaksFailed - Counter32, - pgmReceiverParityNaksFailed - Counter32, - pgmReceiverSelectiveNaksFailed - Counter32, - pgmReceiverNaksFailedRxwAdvanced - Counter32, - pgmReceiverNaksFaledNcfRetriesExceeded - Counter32, - pgmReceiverNaksFailedDataRetriesExceeded - Counter32, - pgmReceiverNaksFailedGenExpired - Counter32, - pgmReceiverNakFailuresDelivered - Counter32, - pgmReceiverParityNaksSuppressed - Counter32, - pgmReceiverSelectiveNaksSuppressed - Counter32, - pgmReceiverNakErrors - Counter32, - pgmReceiverOutstandingParityNaks - Counter32, - pgmReceiverOutstandingSelectiveNaks - Counter32, - pgmReceiverLastActivity - Counter32, - pgmReceiverNakSvcTimeMin - Counter32, - pgmReceiverNakSvcTimeMean - Counter32, - pgmReceiverNakSvcTimeMax - Counter32, - pgmReceiverNakFailTimeMin - Counter32, - pgmReceiverNakFailTimeMean - Counter32, - pgmReceiverNakFailTimeMax - Counter32, - pgmReceiverNakTransmitMin - Counter32, - pgmReceiverNakTransmitMean - Counter32, - pgmReceiverNakTransmitMax - Counter32, - pgmReceiverAcksSent - Counter32, - pgmReceiverRxwTrail - Counter32, - pgmReceiverRxwLead - Counter32, - pgmReceiverNakFailuresLastInterval - Counter32, - pgmReceiverLastIntervalNakFailures - Counter32 -} - -pgmReceiverPerformanceGlobalId OBJECT-TYPE - SYNTAX OCTET STRING (SIZE (12)) - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Globally unique source identifier (GSI)." - ::= { pgmReceiverPerformanceEntry 1 } - -pgmReceiverPerformanceSourcePort OBJECT-TYPE - SYNTAX Unsigned32(0..65535) - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Source port number." - ::= { pgmReceiverPerformanceEntry 2 } - -pgmReceiverPerformanceInstance OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Positive number, uniquely identifying - a Receiver." - ::= { pgmReceiverPerformanceEntry 3 } - -pgmReceiverDataBytesReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of data bytes received for this PGM - Receiver session." - ::= { pgmReceiverPerformanceEntry 4 } - -pgmReceiverDataMsgsReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of data messages received for this - PGM Receiver session." - ::= { pgmReceiverPerformanceEntry 5 } - -pgmReceiverNaksSent OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of NAKs sent for this session." - ::= { pgmReceiverPerformanceEntry 6 } - -pgmReceiverNaksRetransmitted OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of NAKs retransmitted for this - session." - ::= { pgmReceiverPerformanceEntry 7 } - -pgmReceiverNakFailures OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of NAK failures for this session. - This counter represents the number of - unrecoverable/unrepairable data packets." - ::= { pgmReceiverPerformanceEntry 8 } - -pgmReceiverBytesReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of bytes received for this session. - It counts all bytes received, including IP - and PGM header and non-data messages." - ::= { pgmReceiverPerformanceEntry 9 } - -pgmReceiverNaksSuppressed OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of suppressed NAKs." - ::= { pgmReceiverPerformanceEntry 10 } - -pgmReceiverCksumErrors OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of checksum errors for this session." - ::= { pgmReceiverPerformanceEntry 11 } - -pgmReceiverMalformedSpms OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of malformed SPMs for this session." - ::= { pgmReceiverPerformanceEntry 12 } - -pgmReceiverMalformedOdata OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of malformed ODATA packets for this - session." - ::= { pgmReceiverPerformanceEntry 13 } - -pgmReceiverMalformedRdata OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of malformed RDATA packets for this - session." - ::= { pgmReceiverPerformanceEntry 14 } - -pgmReceiverMalformedNcfs OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of malformed NCF packets for this - session." - ::= { pgmReceiverPerformanceEntry 15 } - -pgmReceiverPacketsDiscarded OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of discarded packets for this - session." - ::= { pgmReceiverPerformanceEntry 16 } - -pgmReceiverLosses OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of detected missed packets for - this session. This counter is incremented - every time a Receiver detects a missing - packet." - ::= { pgmReceiverPerformanceEntry 17 } - -pgmReceiverBytesDeliveredToApp OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of bytes, delivered to the - application." - ::= { pgmReceiverPerformanceEntry 18 } - -pgmReceiverMsgsDeliveredToApp OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of messages, delivered to the - application." - ::= { pgmReceiverPerformanceEntry 19 } - -pgmReceiverDupSpms OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of duplicate SPMs." - ::= { pgmReceiverPerformanceEntry 20 } - -pgmReceiverDupDatas OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of duplicate RDATA/ODATA." - ::= { pgmReceiverPerformanceEntry 21 } - -pgmReceiverDupParities OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of duplicate parities seen." - ::= { pgmReceiverPerformanceEntry 22 } - -pgmReceiverNakPacketsSent OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of NAK packets sent. - Includes parity and selective." - ::= { pgmReceiverPerformanceEntry 23 } - -pgmReceiverParityNakPacketsSent OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of parity NAK packets sent." - ::= { pgmReceiverPerformanceEntry 24 } - -pgmReceiverSelectiveNakPacketsSent OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of selective NAK packets sent." - ::= { pgmReceiverPerformanceEntry 25 } - -pgmReceiverParityNaksSent OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual parity NAK packets sent." - ::= { pgmReceiverPerformanceEntry 26 } - -pgmReceiverSelectiveNaksSent OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual selective NAK packets sent." - ::= { pgmReceiverPerformanceEntry 27 } - -pgmReceiverParityNaksRetransmitted OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual parity NAKs retransmitted." - ::= { pgmReceiverPerformanceEntry 28 } - -pgmReceiverSelectiveNaksRetransmitted OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual selective NAKs retransmitted." - ::= { pgmReceiverPerformanceEntry 29 } - -pgmReceiverNaksFailed OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual NAKs that failed." - ::= { pgmReceiverPerformanceEntry 30 } - -pgmReceiverParityNaksFailed OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual parity NAKs that failed." - ::= { pgmReceiverPerformanceEntry 31 } - -pgmReceiverSelectiveNaksFailed OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual selective NAKs that failed." - ::= { pgmReceiverPerformanceEntry 32 } - -pgmReceiverNaksFailedRxwAdvanced OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual NAKs that failed, due to the - window being advanced over them." - ::= { pgmReceiverPerformanceEntry 33 } - -pgmReceiverNaksFaledNcfRetriesExceeded OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual NAKs that failed, due to ncf - retry limit exceeded." - ::= { pgmReceiverPerformanceEntry 34 } - -pgmReceiverNaksFailedDataRetriesExceeded OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual NAKs that failed, due to data - retry limit exceeded." - ::= { pgmReceiverPerformanceEntry 35 } - -pgmReceiverNaksFailedGenExpired OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual NAKs that failed, due to NAK - generation interval expiring before it - could be repaired." - ::= { pgmReceiverPerformanceEntry 36 } - -pgmReceiverNakFailuresDelivered OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of NAK failures delivered to application." - ::= { pgmReceiverPerformanceEntry 37 } - -pgmReceiverParityNaksSuppressed OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual parity NAKs that were - suppressed from being sent due to reception - of an NCF or ODATA/RDATA for the loss." - ::= { pgmReceiverPerformanceEntry 38 } - -pgmReceiverSelectiveNaksSuppressed OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual selective NAKs that were - suppressed from being sent due to reception - of an NCF or ODATA/RDATA for the loss." - ::= { pgmReceiverPerformanceEntry 39 } - -pgmReceiverNakErrors OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of NAK packets, that contained - errors in them." - ::= { pgmReceiverPerformanceEntry 40 } - -pgmReceiverOutstandingParityNaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Current number of outstanding individual parity - NAKs that are waiting to be repaired." - ::= { pgmReceiverPerformanceEntry 41 } - -pgmReceiverOutstandingSelectiveNaks OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Current number of outstanding individual selective - NAKs that are waiting to be repaired." - ::= { pgmReceiverPerformanceEntry 42 } - -pgmReceiverLastActivity OBJECT-TYPE - SYNTAX Counter32 - UNITS "seconds" - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Last time activity was observed from - the Source. In seconds since the epoch, - January 1, 1970." - ::= { pgmReceiverPerformanceEntry 43 } - -pgmReceiverNakSvcTimeMin OBJECT-TYPE - SYNTAX Counter32 - UNITS "miliseconds" - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The min time that it took for a loss - to be repaired." - ::= { pgmReceiverPerformanceEntry 44 } - -pgmReceiverNakSvcTimeMean OBJECT-TYPE - SYNTAX Counter32 - UNITS "miliseconds" - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The mean time that it took for all losses - to be repaired." - ::= { pgmReceiverPerformanceEntry 45 } - -pgmReceiverNakSvcTimeMax OBJECT-TYPE - SYNTAX Counter32 - UNITS "miliseconds" - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The max time that it took for a loss - to be repaired." - ::= { pgmReceiverPerformanceEntry 46 } - -pgmReceiverNakFailTimeMin OBJECT-TYPE - SYNTAX Counter32 - UNITS "miliseconds" - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The min time it took for a loss - to be considered unrecoverable." - ::= { pgmReceiverPerformanceEntry 47 } - -pgmReceiverNakFailTimeMean OBJECT-TYPE - SYNTAX Counter32 - UNITS "miliseconds" - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The mean time it took for all losses - to be considered unrecoverable." - ::= { pgmReceiverPerformanceEntry 48 } - -pgmReceiverNakFailTimeMax OBJECT-TYPE - SYNTAX Counter32 - UNITS "miliseconds" - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The max time it took for a loss - to be considered unrecoverable." - ::= { pgmReceiverPerformanceEntry 49 } - -pgmReceiverNakTransmitMin OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The min number of times an individual NAK - needed to be retransmitted before it was repaired." - ::= { pgmReceiverPerformanceEntry 50 } - -pgmReceiverNakTransmitMean OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The mean number of times an individual NAK - needed to be retransmitted before it was repaired." - ::= { pgmReceiverPerformanceEntry 51 } - -pgmReceiverNakTransmitMax OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The max number of times an individual NAK - needed to be retransmitted before it was repaired." - ::= { pgmReceiverPerformanceEntry 52 } - -pgmReceiverAcksSent OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The number of ACKs sent from the congestion - control operation." - ::= { pgmReceiverPerformanceEntry 53 } - -pgmReceiverRxwTrail OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Sequence number of the trailing edge of - the transmission window as is being advertised - by the sender." - ::= { pgmReceiverPerformanceEntry 54 } - -pgmReceiverRxwLead OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Sequence number of the leading edge of - the transmission window as is being advertised - by the sender." - ::= { pgmReceiverPerformanceEntry 55 } - -pgmReceiverNakFailuresLastInterval OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The actual number of seconds since the - last pgmReceiverLastIntervalNakFailures - counter reset due to number of nak failures - threshold exceeded." - ::= { pgmReceiverPerformanceEntry 56 } - -pgmReceiverLastIntervalNakFailures OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of actual unrecoverable failures for - the requested threshold interval for this session." - ::= { pgmReceiverPerformanceEntry 57 } - --- --- Designated Local Repairer (DLR) --- - --- Designated Local Repairer (DLR) Default Configuration - -pgmDlrSaveDefaults OBJECT-TYPE - SYNTAX INTEGER { initial (1), - save (2), - pending (3), - success (4), - failure (5) } - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Flag used to initiate the storing - of all default variable values to - non-volatile storage and to report the - result of the operation. - The following values can only be read, - never written : - initial(1) - returned prior to any requests - for saving the default configuration - pending(3) - saving in progress - success(4) - returned when a save(2) request - is successful - failure(5) - returned when a save(2) request - is unsuccessful - - The following values can only be written, - never read : - save(2) - to indicate that the default - configuration should be saved." - ::= { pgmDLR 1 } - -pgmDlrLastUpdateTime OBJECT-TYPE - SYNTAX TimeTicks - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of TimeTicks since the last update - of the non-volatile storage." - ::= { pgmDLR 2 } - -pgmDlrGroupAddress OBJECT-TYPE - SYNTAX IpAddress - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Multicast group address to listen for traffic - on." - ::= { pgmDLR 3 } - -pgmDlrCacheRtx OBJECT-TYPE - SYNTAX INTEGER - { - cacheOFF(1), - cacheON(2) - } - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Specifies if the NE should also cache data for - retransmission or simply suppress duplicate - NAKs and forward the NAKs to it's parent NE or - sender." - ::= { pgmDLR 4 } - -pgmDlrActivityIvl OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Specifies the delay between activity checks - for specific PGM sessions." - ::= { pgmDLR 5 } - -pgmDlrMaxRate OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Specifies the maximum rate (in bps) for - retransmissions." - ::= { pgmDLR 6 } - -pgmDlrParentNeAddr OBJECT-TYPE - SYNTAX IpAddress - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Ip Address of the NE to send all NAKs to." - ::= { pgmDLR 7 } - -pgmDlrUpdateSinceLastSave OBJECT-TYPE - SYNTAX INTEGER - { - notUpdated(1), - updated(2) - } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Specifies if any of the Dlr Default - variables have been updated or not, - since the last successful pgmDlrSaveDefaults. - notUpdated - none of the default Dlr - variables were set after the last - successful save to a non-volatile - storage. - updated - at least one of the default Dlr - variables were set after the last - successful save to a non-volatile - storage." - ::= { pgmDLR 8 } - - --- --- PGM DLR Source/Re-transmitter Sessions --- -pgmDlrSource OBJECT IDENTIFIER ::= { pgmDLR 100 } - -pgmDlrSourceNumberOfEntries OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of PGM Source sessions for - the PGM DLR." - ::= { pgmDlrSource 1 } - --- PGM Dlr Source Fault Management Table - -pgmDlrSourceTable OBJECT-TYPE - SYNTAX SEQUENCE OF PgmDlrSourceEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The table holding per TSI fault - management and general information - related to the PGM DLR Source sessions." - ::= {pgmDlrSource 2} - -pgmDlrSourceEntry OBJECT-TYPE - SYNTAX PgmDlrSourceEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Per PGM DLR Source sessions fault - management information." - INDEX { pgmDlrSourceGlobalId, - pgmDlrSourceSourcePort } - ::= { pgmDlrSourceTable 1 } - -PgmDlrSourceEntry ::= SEQUENCE { - pgmDlrSourceGlobalId - OCTET STRING, - pgmDlrSourceSourcePort - Unsigned32, - pgmDlrSourceGroupAddress - IpAddress, - pgmDlrSourceSourceGsi - OCTET STRING, - pgmDlrSourceSourcePortNumber - Unsigned32 - } - -pgmDlrSourceGlobalId OBJECT-TYPE - SYNTAX OCTET STRING (SIZE (12)) - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Globally unique source identifier (GSI)." - ::= { pgmDlrSourceEntry 1 } - -pgmDlrSourceSourcePort OBJECT-TYPE - SYNTAX Unsigned32(0..65535) - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Source port number." - ::= { pgmDlrSourceEntry 2 } - -pgmDlrSourceGroupAddress OBJECT-TYPE - SYNTAX IpAddress - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Multicast group interface address - to send multicast packets on." - ::= { pgmDlrSourceEntry 3 } - -pgmDlrSourceSourceGsi OBJECT-TYPE - SYNTAX OCTET STRING (SIZE (12)) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Globally unique source identifier (GSI)." - ::= { pgmDlrSourceEntry 4 } - -pgmDlrSourceSourcePortNumber OBJECT-TYPE - SYNTAX Unsigned32(0..65535) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Source port number." - ::= { pgmDlrSourceEntry 5 } - --- PGM DLR Source Configuration Management Table - -pgmDlrSourceConfigTable OBJECT-TYPE - SYNTAX SEQUENCE OF PgmDlrSourceConfigEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The table holding per TSI configuration - management information related to the - PGM DLR Source sessions." - ::= {pgmDlrSource 3} - -pgmDlrSourceConfigEntry OBJECT-TYPE - SYNTAX PgmDlrSourceConfigEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Per PGM DLR Source sessions configuration - management information." - INDEX { pgmDlrSourceConfigGlobalId, - pgmDlrSourceConfigSourcePort } - ::= { pgmDlrSourceConfigTable 1 } - -PgmDlrSourceConfigEntry ::= SEQUENCE { - pgmDlrSourceConfigGlobalId - OCTET STRING, - pgmDlrSourceConfigSourcePort - Unsigned32, - pgmDlrSourceGroupTtl - Unsigned32, - pgmDlrSourceRdataBackoffIvl - Unsigned32 - } - -pgmDlrSourceConfigGlobalId OBJECT-TYPE - SYNTAX OCTET STRING (SIZE (12)) - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Globally unique source identifier (GSI)." - ::= { pgmDlrSourceConfigEntry 1 } - -pgmDlrSourceConfigSourcePort OBJECT-TYPE - SYNTAX Unsigned32(0..65535) - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Source port number." - ::= { pgmDlrSourceConfigEntry 2 } - -pgmDlrSourceGroupTtl OBJECT-TYPE - SYNTAX Unsigned32(1..255) - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "This option sets the default TTL to use for - multicast packets. " - ::= { pgmDlrSourceConfigEntry 3 } - -pgmDlrSourceRdataBackoffIvl OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "milliseconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "This option sets the default RDATA backoff - interval. The value is expressed in milliseconds. - The value of 0 indicates no backoff." - ::= { pgmDlrSourceConfigEntry 4 } - - --- PGM DLR Source Performance Management Table - -pgmDlrSourcePerformanceTable OBJECT-TYPE - SYNTAX SEQUENCE OF PgmDlrSourcePerformanceEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The table holding per TSI performance - management information related to the - PGM DLR Source sessions." - ::= {pgmDlrSource 4} - -pgmDlrSourcePerformanceEntry OBJECT-TYPE - SYNTAX PgmDlrSourcePerformanceEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Per PGM DLR Source performance management - information." - INDEX { pgmDlrSourcePerformanceGlobalId, - pgmDlrSourcePerformanceSourcePort } - ::= { pgmDlrSourcePerformanceTable 1 } - -PgmDlrSourcePerformanceEntry ::= SEQUENCE { - pgmDlrSourcePerformanceGlobalId - OCTET STRING, - pgmDlrSourcePerformanceSourcePort - Unsigned32, - pgmDlrSourceRdataMsgsSent - Counter32, - pgmDlrSourceRdataBytesSent - Counter32, - pgmDlrSourceBytesSent - Counter32, - pgmDlrSourceNaksRcvd - Counter32, - pgmDlrSourceNaksIgnored - Counter32, - pgmDlrSourceNakErrors - Counter32, - pgmDlrSourceDiscards - Counter32, - pgmDlrSourceCksumErrors - Counter32, - pgmDlrSourceNNaksSent - Counter32, - pgmDlrSourceBytesBuffered - Counter32, - pgmDlrSourceMsgsBuffered - Counter32, - pgmDlrSourceParityBytesRetransmitted - Counter32, - pgmDlrSourceSelectiveBytesRetransmited - Counter32, - pgmDlrSourceParityMsgsRetransmitted - Counter32, - pgmDlrSourceSelectiveMsgsRetransmitted - Counter32, - pgmDlrSourceBytesAdmit - Counter32, - pgmDlrSourceMsgsAdmit - Counter32, - pgmDlrSourceNakPacketsReceived - Counter32, - pgmDlrSourceParityNakPacketsReceived - Counter32, - pgmDlrSourceSelectiveNakPacketsReceived - Counter32, - pgmDlrSourceParityNaksReceived - Counter32, - pgmDlrSourceSelectiveNaksReceived - Counter32, - pgmDlrSourceParityNaksIgnored - Counter32, - pgmDlrSourceSelectiveNaksIgnored - Counter32, - pgmDlrSourceAckErrors - Counter32, - pgmDlrSourceNNakErrors - Counter32, - pgmDlrSourceAckPacketsReceived - Counter32, - pgmDlrSourceNNakPacketsReceived - Counter32, - pgmDlrSourceParityNNakPacketsReceived - Counter32, - pgmDlrSourceSelectiveNNakPacketsReceived - Counter32, - pgmDlrSourceNNaksReceived - Counter32, - pgmDlrSourceParityNNaksReceived - Counter32, - pgmDlrSourceSelectiveNNaksReceived - Counter32 - } - -pgmDlrSourcePerformanceGlobalId OBJECT-TYPE - SYNTAX OCTET STRING (SIZE (12)) - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Globally unique source identifier (GSI)." - ::= { pgmDlrSourcePerformanceEntry 1 } - -pgmDlrSourcePerformanceSourcePort OBJECT-TYPE - SYNTAX Unsigned32(0..65535) - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Source port number." - ::= { pgmDlrSourcePerformanceEntry 2 } - -pgmDlrSourceRdataMsgsSent OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of Repair Data (RDATA) packets sent for - this PGM DLR." - ::= { pgmDlrSourcePerformanceEntry 3 } - -pgmDlrSourceRdataBytesSent OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of RDATA bytes sent." - ::= { pgmDlrSourcePerformanceEntry 4 } - -pgmDlrSourceBytesSent OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of bytes sent. This includes IP and - PGM header and non-data msgs." - ::= { pgmDlrSourcePerformanceEntry 5 } - -pgmDlrSourceNaksRcvd OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of NAKs received on this TSI." - ::= { pgmDlrSourcePerformanceEntry 6 } - -pgmDlrSourceNaksIgnored OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of NAKs ignored on this TSI, due to - duplicates." - ::= { pgmDlrSourcePerformanceEntry 7 } - -pgmDlrSourceNakErrors OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of malformed NAKs on this TSI." - ::= { pgmDlrSourcePerformanceEntry 8 } - -pgmDlrSourceDiscards OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of discarded packets on this TSI. - This counter is used to count all discarded - incoming packets per TSI in cases of - duplicates, header and packet errors, etc." - ::= { pgmDlrSourcePerformanceEntry 9 } - -pgmDlrSourceCksumErrors OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of checksum errors on this TSI." - ::= { pgmDlrSourcePerformanceEntry 10 } - -pgmDlrSourceNNaksSent OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of Null NAKs (in number of packets) - sent by this PGM DLR session." - ::= { pgmDlrSourcePerformanceEntry 11 } - -pgmDlrSourceBytesBuffered OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of bytes currently buffered for this - TSI." - ::= { pgmDlrSourcePerformanceEntry 12 } - -pgmDlrSourceMsgsBuffered OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of messages currently buffered for - this TSI." - ::= { pgmDlrSourcePerformanceEntry 13 } - -pgmDlrSourceParityBytesRetransmitted OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of bytes sent in parity retransmissions." - ::= { pgmDlrSourcePerformanceEntry 14 } - -pgmDlrSourceSelectiveBytesRetransmited OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of bytes sent in selective retransmissions." - ::= { pgmDlrSourcePerformanceEntry 15 } - -pgmDlrSourceParityMsgsRetransmitted OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of parity retransmissions sent." - ::= { pgmDlrSourcePerformanceEntry 16 } - -pgmDlrSourceSelectiveMsgsRetransmitted OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of selective retransmissions sent." - ::= { pgmDlrSourcePerformanceEntry 17 } - -pgmDlrSourceBytesAdmit OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of bytes currently in the rate controled - admit queue. Includes IP header, UDP header if - encapsulated, PGM header, and data." - ::= { pgmDlrSourcePerformanceEntry 18 } - -pgmDlrSourceMsgsAdmit OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of messages currently in the rate controled - admit queue. Includes data messages, retransmissions, - and SPMs." - ::= { pgmDlrSourcePerformanceEntry 19 } - -pgmDlrSourceNakPacketsReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of NAK packets received." - ::= { pgmDlrSourcePerformanceEntry 20 } - -pgmDlrSourceParityNakPacketsReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of parity NAK packets received." - ::= { pgmDlrSourcePerformanceEntry 21 } - -pgmDlrSourceSelectiveNakPacketsReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of selective NAK packets received." - ::= { pgmDlrSourcePerformanceEntry 22 } - -pgmDlrSourceParityNaksReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual parity NAKs received." - ::= { pgmDlrSourcePerformanceEntry 23 } - -pgmDlrSourceSelectiveNaksReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual selective NAKs received." - ::= { pgmDlrSourcePerformanceEntry 24 } - -pgmDlrSourceParityNaksIgnored OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual parity NAKs ignored." - ::= { pgmDlrSourcePerformanceEntry 25 } - -pgmDlrSourceSelectiveNaksIgnored OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual selective NAKs ignored." - ::= { pgmDlrSourcePerformanceEntry 26 } - -pgmDlrSourceAckErrors OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of ACK packets received with error in - them, different than checksum error." - ::= { pgmDlrSourcePerformanceEntry 27 } - -pgmDlrSourceNNakErrors OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of Null NAK packets received that contain - error, rSifferent than checksum error." - ::= { pgmDlrSourcePerformanceEntry 28 } - -pgmDlrSourceAckPacketsReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of ACK packets received." - ::= { pgmDlrSourcePerformanceEntry 29 } - -pgmDlrSourceNNakPacketsReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of Null NAKs received." - ::= { pgmDlrSourcePerformanceEntry 30 } - -pgmDlrSourceParityNNakPacketsReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of parity Null NAK packets received." - ::= { pgmDlrSourcePerformanceEntry 31 } - -pgmDlrSourceSelectiveNNakPacketsReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of selective Null NAK packets received." - ::= { pgmDlrSourcePerformanceEntry 32 } - -pgmDlrSourceNNaksReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual NAKs, received in Null - NAK packets." - ::= { pgmDlrSourcePerformanceEntry 33 } - -pgmDlrSourceParityNNaksReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual parity NAKs, - received in Null NAK packets." - ::= { pgmDlrSourcePerformanceEntry 34 } - -pgmDlrSourceSelectiveNNaksReceived OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Number of individual NAKs, - received in Null NAK packets." - ::= { pgmDlrSourcePerformanceEntry 35 } - --- Notifications - -pgmNotifications OBJECT IDENTIFIER ::= - { pgmNotificationPrefix 0 } - -pgmStart NOTIFICATION-TYPE - STATUS current - DESCRIPTION - "This trap is sent when the pgm snmp agent starts" - ::= { pgmNotifications 1 } - -pgmStop NOTIFICATION-TYPE - STATUS current - DESCRIPTION - "This trap is sent when the pgm snmp agent terminates" - ::= { pgmNotifications 2 } - --- PGM Source Specific Traps - -pgmNewSourceTrap NOTIFICATION-TYPE - OBJECTS { - pgmSourceSourceGsi, - pgmSourceSourcePortNumber - } - STATUS current - DESCRIPTION - "New Source Session created." - ::= { pgmNotifications 3 } - -pgmClosedSourceTrap NOTIFICATION-TYPE - OBJECTS { - pgmSourceSourceGsi, - pgmSourceSourcePortNumber - } - STATUS current - DESCRIPTION - "Source Session closed." - ::= { pgmNotifications 4 } - --- PGM Receiver Specific Traps - -pgmNewReceiverTrap NOTIFICATION-TYPE - OBJECTS { - pgmReceiverSourceGsi, - pgmReceiverSourcePortNumber, - pgmReceiverUniqueInstance - } - STATUS current - DESCRIPTION - "New Receiver Session created. - This trap is optional." - ::= { pgmNotifications 5 } - -pgmClosedReceiverTrap NOTIFICATION-TYPE - OBJECTS { - pgmReceiverSourceGsi, - pgmReceiverSourcePortNumber, - pgmReceiverUniqueInstance - } - STATUS current - DESCRIPTION - "Receiver Session closed. - This trap is optional." - ::= { pgmNotifications 6 } - -pgmNakFailuresTrap NOTIFICATION-TYPE - OBJECTS { - pgmReceiverSourceGsi, - pgmReceiverSourcePortNumber, - pgmReceiverUniqueInstance, - pgmReceiverNakFailureThresholdTimer, - pgmReceiverNakFailureThreshold, - pgmReceiverNakFailuresLastInterval, - pgmReceiverLastIntervalNakFailures - } - STATUS current - DESCRIPTION - "The number of unrecovered lost packets - exceeded the threshold limit for the - corresponding threshold interval." - ::= { pgmNotifications 7 } - --- PGM Dlr Source Specific Traps - -pgmNewDlrSourceTrap NOTIFICATION-TYPE - OBJECTS { - pgmDlrSourceSourceGsi, - pgmDlrSourceSourcePortNumber - } - STATUS current - DESCRIPTION - "New Dlr Source Session created." - ::= { pgmNotifications 8 } - -pgmClosedDlrSourceTrap NOTIFICATION-TYPE - OBJECTS { - pgmDlrSourceSourceGsi, - pgmDlrSourceSourcePortNumber - } - STATUS current - DESCRIPTION - "Dlr Source Session closed." - ::= { pgmNotifications 9 } - --- Conformance information - -pgmMIBConformance OBJECT IDENTIFIER ::= { pgmMIB 3 } -pgmMIBCompliances OBJECT IDENTIFIER ::= { pgmMIBConformance 1 } -pgmMIBGroups OBJECT IDENTIFIER ::= { pgmMIBConformance 2 } - --- Compliance statements - -pgmNetworkElementMIBCompliance MODULE-COMPLIANCE - STATUS current - DESCRIPTION - "The compliance statement for devices running as PGM - Network Elements." - MODULE -- this module - MANDATORY-GROUPS { pgmNetworkElementMIBGroup } - - ::= { pgmMIBCompliances 1 } - -pgmSourceMIBCompliance MODULE-COMPLIANCE - STATUS current - DESCRIPTION - "The compliance statement for devices running as PGM - sources." - MODULE -- this module - MANDATORY-GROUPS { pgmSourceMIBGroup } - - ::= { pgmMIBCompliances 2 } - -pgmReceiverMIBCompliance MODULE-COMPLIANCE - STATUS current - DESCRIPTION - "The compliance statement for devices running as PGM - receivers." - MODULE -- this module - MANDATORY-GROUPS { pgmReceiverMIBGroup } - - ::= { pgmMIBCompliances 3 } - -pgmDLRMIBCompliance MODULE-COMPLIANCE - STATUS current - DESCRIPTION - "The compliance statement for devices running as PGM - designated local repairers (DLR)." - MODULE -- this module - MANDATORY-GROUPS { pgmDLRMIBGroup, - pgmReceiverMIBGroup } - - ::= { pgmMIBCompliances 4 } - -pgmTrapsMIBCompliance MODULE-COMPLIANCE - STATUS current - DESCRIPTION - "The compliance statement for PGM traps." - MODULE -- this module - MANDATORY-GROUPS { pgmTrapsMIBGroup, - pgmTrapsSourceMIBGroup, - pgmTrapsReceiverMIBGroup, - pgmTrapsDlrSourceMIBGroup } - ::= { pgmMIBCompliances 5 } - --- Units of conformance - -pgmNetworkElementMIBGroup OBJECT-GROUP - OBJECTS { pgmNeEnable, - pgmNeSessionLifeTime, - pgmNeMaxReXmitStates, - pgmNeMaxSessions, - pgmNeTotalInterfacesNumberOfEntries, - pgmNeIfPgmEnable, - pgmNeIfNakRptInterval, - pgmNeIfNakRptRate, - pgmNeIfNakRdataInterval, - pgmNeIfNakEliminateInterval, - pgmNeIfReXmitStates, - pgmNeIfReXmitTimedOut, - pgmNeIfInSpms, - pgmNeIfOutSpms, - pgmNeIfInParitySpms, - pgmNeIfOutParitySpms, - pgmNeIfInRdata, - pgmNeIfOutRdata, - pgmNeIfInParityRdata, - pgmNeIfOutParityRdata, - pgmNeIfInRdataNoSessionErrors, - pgmNeIfUniqueNaks, - pgmNeIfInNaks, - pgmNeIfOutNaks, - pgmNeIfUniqueParityNaks, - pgmNeIfInParityNaks, - pgmNeIfOutParityNaks, - pgmNeIfInNakNoSessionErrors, - pgmNeIfInNakSeqErrors, - pgmNeIfInParityNakTgErrors, - pgmNeIfInNnaks, - pgmNeIfOutNnaks, - pgmNeIfInParityNnaks, - pgmNeIfOutParityNnaks, - pgmNeIfInNnakNoSessionErrors, - pgmNeIfInNcfs, - pgmNeIfOutNcfs, - pgmNeIfInParityNcfs, - pgmNeIfOutParityNcfs, - pgmNeIfInNcfNoSessionErrors, - pgmNeIfInRedirectNcfs, - pgmNeIfMalformed, - pgmNeIfSpmFromSource, - pgmNeIfSpmBadSqn, - pgmNeIfSpmError, - pgmNeIfPollRandomIgnore, - pgmNeIfPollTsiStateError, - pgmNeIfPollParentError, - pgmNeIfPollTypeError, - pgmNeIfPollError, - pgmNeIfPollSuccess, - pgmNeIfPollOriginated, - pgmNeIfPolrNoState, - pgmNeIfPolrError, - pgmNeIfPolrParityError, - pgmNeIfPolrSuccess, - pgmNeIfPolrOriginated, - pgmNeIfNcfError, - pgmNeIfNcfParityError, - pgmNeIfNcfPartialParity, - pgmNeIfNcfReceived, - pgmNeIfNcfAnticipated, - pgmNeIfNcfRedirecting, - pgmNeIfNakEliminated, - pgmNeIfNakError, - pgmNeIfNakParityError, - pgmNeIfNNakEliminated, - pgmNeIfNNakError, - pgmNeIfNNakParityError, - pgmNeIfNNakCongestionReports, - pgmNeIfNakRetryExpired, - pgmNeIfNakRetryExpiredDLR, - pgmNeIfNakForwardedDLR, - pgmNeIfNakRetransmitted, - pgmNeIfRdataEliminatedOIF, - pgmNeIfRdataEliminatedSqn, - pgmNeIfInRdataFragments, - pgmNeIfRdataFragmentsNoSessionErrors, - pgmNeIfRdataFragmentsEliminatedOIF, - pgmNeIfRdataFragmentsEliminatedSqn, - pgmNeIfOutRdataFragments, - pgmNeTotalTsiNumberOfEntries, - pgmNeTsiStateBits, - pgmNeTsiDataDestinationPort, - pgmNeTsiSourceAddress, - pgmNeTsiGroupAddress, - pgmNeTsiUpstreamAddress, - pgmNeTsiUpstreamIfIndex, - pgmNeTsiDlrAddress, - pgmNeTsiSessionTrailEdgeSeq, - pgmNeTsiSessionIncrSeq, - pgmNeTsiLeadEdgeSeq, - pgmNeTsiInSpms, - pgmNeTsiOutSpms, - pgmNeTsiInParitySpms, - pgmNeTsiOutParitySpms, - pgmNeTsiTotalReXmitStates, - pgmNeTsiTotalReXmitTimedOut, - pgmNeTsiInRdata, - pgmNeTsiOutRdata, - pgmNeTsiInParityRdata, - pgmNeTsiOutParityRdata, - pgmNeTsiInRdataNoStateErrors, - pgmNeTsiUniqueNaks, - pgmNeTsiInNaks, - pgmNeTsiOutNaks, - pgmNeTsiUniqueParityNaks, - pgmNeTsiInParityNaks, - pgmNeTsiOutParityNaks, - pgmNeTsiInNakSeqErrors, - pgmNeTsiInNnaks, - pgmNeTsiOutNnaks, - pgmNeTsiInParityNnaks, - pgmNeTsiOutParityNnaks, - pgmNeTsiInNcfs, - pgmNeTsiOutNcfs, - pgmNeTsiInParityNcfs, - pgmNeTsiOutParityNcfs, - pgmNeTsiSpmSequenceNumber, - pgmNeTsiTransmissionGroupSize, - pgmNeTsiTimeout, - pgmNeTsiLastTtl, - pgmNeTsiLinkLossRate, - pgmNeTsiPathLossRate, - pgmNeTsiReceiverLossRate, - pgmNeTsiCongestionReportLead, - pgmNeTsiCongestionReportWorstReceiver, - pgmNeTsiRtxNumberOfEntries, - pgmNeTsiRtxReqParityTgCount, - pgmNeTsiRtxTimeout, - pgmNeTsiRtxStateBits, - pgmNeTsiRtxIfNumberOfEntries, - pgmNeTsiRtxIfPacketCount, - pgmNeTsiPolrNumberOfEntries, - pgmNeTsiPolrSequenceNumber, - pgmNeTsiPollNumberOfEntries, - pgmNeTsiPollSequence, - pgmNeTsiPollChildBackoff, - pgmNeTsiPollMask, - pgmNeTsiPollPeriod, - pgmNeTsiPollCount, - pgmNeTsiPollTimeout } - STATUS current - DESCRIPTION - "A collection of objects to support - management of PGM Network Elements." - ::= { pgmMIBGroups 1 } - -pgmSourceMIBGroup OBJECT-GROUP - OBJECTS { pgmSourceSaveDefaults, - pgmSourceLastUpdateTime, - pgmSourceDefaultTtl, - pgmSourceDefaultAdvMode, - pgmSourceDefaultLateJoin, - pgmSourceDefaultTxwMaxRte, - pgmSourceDefaultTxwSecs, - pgmSourceDefaultTxwAdvSecs, - pgmSourceDefaultAdvIvl, - pgmSourceDefaultSpmIvl, - pgmSourceDefaultSpmHeartBeatIvlMin, - pgmSourceDefaultSpmHeartBeatIvlMax, - pgmSourceDefaultRdataBackoffIvl, - pgmSourceDefaultFECProactiveParitySize, - pgmSourceDefaultGroupAddress, - pgmSourceUpdateSinceLastSave, - pgmSourceNumberOfEntries, - pgmSourceSourceAddress, - pgmSourceGroupAddress, - pgmSourceDestPort, - pgmSourceSourceGsi, - pgmSourceSourcePortNumber, - pgmSourceTtl, - pgmSourceAdvMode, - pgmSourceLateJoin, - pgmSourceTxwMaxRte, - pgmSourceTxwSecs, - pgmSourceTxwAdvSecs, - pgmSourceAdvIvl, - pgmSourceSpmIvl, - pgmSourceSpmHeartBeatIvlMin, - pgmSourceSpmHeartBeatIvlMax, - pgmSourceRdataBackoffIvl, - pgmSourceFEC, - pgmSourceFECTransmissionGrpSize, - pgmSourceFECProactiveParitySize, - pgmSourceSpmPathAddress, - pgmSourceDataBytesSent, - pgmSourceDataMsgsSent, - pgmSourceBytesBuffered, - pgmSourceMsgsBuffered, - pgmSourceBytesRetransmitted, - pgmSourceMsgsRetransmitted, - pgmSourceBytesSent, - pgmSourceRawNaksReceived, - pgmSourceNaksIgnored, - pgmSourceCksumErrors, - pgmSourceMalformedNaks, - pgmSourcePacketsDiscarded, - pgmSourceNaksRcvd, - pgmSourceParityBytesRetransmitted, - pgmSourceSelectiveBytesRetransmited, - pgmSourceParityMsgsRetransmitted, - pgmSourceSelectiveMsgsRetransmitted, - pgmSourceBytesAdmit, - pgmSourceMsgsAdmit, - pgmSourceParityNakPacketsReceived, - pgmSourceSelectiveNakPacketsReceived, - pgmSourceParityNaksReceived, - pgmSourceSelectiveNaksReceived, - pgmSourceParityNaksIgnored, - pgmSourceSelectiveNaksIgnored, - pgmSourceAckErrors, - pgmSourcePgmCCAcker, - pgmSourceTransmissionCurrentRate, - pgmSourceAckPacketsReceived, - pgmSourceNNakPacketsReceived, - pgmSourceParityNNakPacketsReceived, - pgmSourceSelectiveNNakPacketsReceived, - pgmSourceNNaksReceived, - pgmSourceParityNNaksReceived, - pgmSourceSelectiveNNaksReceived, - pgmSourceNNakErrors } - STATUS current - DESCRIPTION - "A collection of objects to support management of - PGM sources." - ::= { pgmMIBGroups 2 } - -pgmReceiverMIBGroup OBJECT-GROUP - OBJECTS { pgmReceiverSaveDefaults, - pgmReceiverLastUpdateTime, - pgmReceiverDefaultNakBackoffIvl, - pgmReceiverDefaultNakRepeatIvl, - pgmReceiverDefaultNakNcfRetries, - pgmReceiverDefaultNakRdataIvl, - pgmReceiverDefaultNakDataRetries, - pgmReceiverDefaultSendNaks, - pgmReceiverDefaultLateJoin, - pgmReceiverDefaultNakTtl, - pgmReceiverDefaultDeliveryOrder, - pgmReceiverDefaultNextPgmHop, - pgmReceiverDefaultGroupAddress, - pgmReceiverUpdateSinceLastSave, - pgmReceiverDefaultNakFailureThresholdTimer, - pgmReceiverDefaultNakFailureThreshold, - pgmReceiverNumberOfEntries, - pgmReceiverGroupAddress, - pgmReceiverDestPort, - pgmReceiverSourceAddress, - pgmReceiverLastHop, - pgmReceiverSourceGsi, - pgmReceiverSourcePortNumber, - pgmReceiverUniqueInstance, - pgmReceiverNakBackoffIvl, - pgmReceiverNakRepeatIvl, - pgmReceiverNakNcfRetries, - pgmReceiverNakRdataIvl, - pgmReceiverNakDataRetries, - pgmReceiverSendNaks, - pgmReceiverLateJoin, - pgmReceiverNakTtl, - pgmReceiverDeliveryOrder, - pgmReceiverMcastNaks, - pgmReceiverNakFailureThresholdTimer, - pgmReceiverNakFailureThreshold, - pgmReceiverDataBytesReceived, - pgmReceiverDataMsgsReceived, - pgmReceiverNaksSent, - pgmReceiverNaksRetransmitted, - pgmReceiverNakFailures, - pgmReceiverBytesReceived, - pgmReceiverNaksSuppressed, - pgmReceiverCksumErrors, - pgmReceiverMalformedSpms, - pgmReceiverMalformedOdata, - pgmReceiverMalformedRdata, - pgmReceiverMalformedNcfs, - pgmReceiverPacketsDiscarded, - pgmReceiverLosses, - pgmReceiverBytesDeliveredToApp, - pgmReceiverMsgsDeliveredToApp, - pgmReceiverDupSpms, - pgmReceiverDupDatas, - pgmReceiverDupParities, - pgmReceiverNakPacketsSent, - pgmReceiverParityNakPacketsSent, - pgmReceiverSelectiveNakPacketsSent, - pgmReceiverParityNaksSent, - pgmReceiverSelectiveNaksSent, - pgmReceiverParityNaksRetransmitted, - pgmReceiverSelectiveNaksRetransmitted, - pgmReceiverNaksFailed, - pgmReceiverParityNaksFailed, - pgmReceiverSelectiveNaksFailed, - pgmReceiverNaksFailedRxwAdvanced, - pgmReceiverNaksFaledNcfRetriesExceeded, - pgmReceiverNaksFailedDataRetriesExceeded, - pgmReceiverNaksFailedGenExpired, - pgmReceiverNakFailuresDelivered, - pgmReceiverParityNaksSuppressed, - pgmReceiverSelectiveNaksSuppressed, - pgmReceiverNakErrors, - pgmReceiverOutstandingParityNaks, - pgmReceiverOutstandingSelectiveNaks, - pgmReceiverLastActivity, - pgmReceiverNakSvcTimeMin, - pgmReceiverNakSvcTimeMean, - pgmReceiverNakSvcTimeMax, - pgmReceiverNakFailTimeMin, - pgmReceiverNakFailTimeMean, - pgmReceiverNakFailTimeMax, - pgmReceiverNakTransmitMin, - pgmReceiverNakTransmitMean, - pgmReceiverNakTransmitMax, - pgmReceiverAcksSent, - pgmReceiverRxwTrail, - pgmReceiverRxwLead, - pgmReceiverNakFailuresLastInterval, - pgmReceiverLastIntervalNakFailures } - STATUS current - DESCRIPTION - "A collection of objects to support management of - PGM receivers." - ::= { pgmMIBGroups 3 } - -pgmDLRMIBGroup OBJECT-GROUP - OBJECTS { pgmDlrSaveDefaults, - pgmDlrLastUpdateTime, - pgmDlrGroupAddress, - pgmDlrCacheRtx, - pgmDlrActivityIvl, - pgmDlrMaxRate, - pgmDlrParentNeAddr, - pgmDlrUpdateSinceLastSave, - pgmDlrSourceNumberOfEntries, - pgmDlrSourceGroupAddress, - pgmDlrSourceSourceGsi, - pgmDlrSourceSourcePortNumber, - pgmDlrSourceGroupTtl, - pgmDlrSourceRdataBackoffIvl, - pgmDlrSourceRdataMsgsSent, - pgmDlrSourceRdataBytesSent, - pgmDlrSourceBytesSent, - pgmDlrSourceNaksRcvd, - pgmDlrSourceNaksIgnored, - pgmDlrSourceNakErrors, - pgmDlrSourceDiscards, - pgmDlrSourceCksumErrors, - pgmDlrSourceNNaksSent, - pgmDlrSourceBytesBuffered, - pgmDlrSourceMsgsBuffered, - pgmDlrSourceParityBytesRetransmitted, - pgmDlrSourceSelectiveBytesRetransmited, - pgmDlrSourceParityMsgsRetransmitted, - pgmDlrSourceSelectiveMsgsRetransmitted, - pgmDlrSourceBytesAdmit, - pgmDlrSourceMsgsAdmit, - pgmDlrSourceNakPacketsReceived, - pgmDlrSourceParityNakPacketsReceived, - pgmDlrSourceSelectiveNakPacketsReceived, - pgmDlrSourceParityNaksReceived, - pgmDlrSourceSelectiveNaksReceived, - pgmDlrSourceParityNaksIgnored, - pgmDlrSourceSelectiveNaksIgnored, - pgmDlrSourceAckErrors, - pgmDlrSourceNNakErrors, - pgmDlrSourceAckPacketsReceived, - pgmDlrSourceNNakPacketsReceived, - pgmDlrSourceParityNNakPacketsReceived, - pgmDlrSourceSelectiveNNakPacketsReceived, - pgmDlrSourceNNaksReceived, - pgmDlrSourceParityNNaksReceived, - pgmDlrSourceSelectiveNNaksReceived } - STATUS current - DESCRIPTION - "A collection of objects to support management of - PGM designated local repairers (DLR)." - ::= { pgmMIBGroups 4 } - -pgmTrapsMIBGroup NOTIFICATION-GROUP - NOTIFICATIONS { pgmStart, - pgmStop } - STATUS current - DESCRIPTION - "A collection of objects to support pgm - specific traps." - ::= { pgmMIBGroups 5 } - -pgmTrapsSourceMIBGroup NOTIFICATION-GROUP - NOTIFICATIONS { pgmNewSourceTrap, - pgmClosedSourceTrap } - STATUS current - DESCRIPTION - "A collection of objects to support pgm - source specific traps." - ::= { pgmMIBGroups 6 } - -pgmTrapsReceiverMIBGroup NOTIFICATION-GROUP - NOTIFICATIONS { pgmNewReceiverTrap, - pgmClosedReceiverTrap, - pgmNakFailuresTrap } - STATUS current - DESCRIPTION - "A collection of objects to support pgm - receiver specific traps." - ::= { pgmMIBGroups 7 } - -pgmTrapsDlrSourceMIBGroup NOTIFICATION-GROUP - NOTIFICATIONS { pgmNewDlrSourceTrap, - pgmClosedDlrSourceTrap } - STATUS current - DESCRIPTION - "A collection of objects to support pgm - dlr source specific traps." - ::= { pgmMIBGroups 8 } - - -END - diff --git a/3rdparty/openpgm-svn-r1085/pgm/mld-semantics.txt b/3rdparty/openpgm-svn-r1085/pgm/mld-semantics.txt deleted file mode 100644 index 135400d..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/mld-semantics.txt +++ /dev/null @@ -1,52 +0,0 @@ - previous request following request return - ----------------- ----------------- ----------- - MCAST_JOIN_GROUP MCAST_JOIN_GROUP EADDRINUSE - MCAST_JOIN_GROUP MCAST_LEAVE_GROUP 0 - MCAST_JOIN_GROUP MCAST_JOIN_SOURCE_GROUP EINVAL - MCAST_JOIN_GROUP MCAST_LEAVE_SOURCE_GROUP EINVAL - MCAST_JOIN_GROUP MCAST_BLOCK_SOURCE 0 - MCAST_JOIN_SOURCE_GROUP MCAST_JOIN_GROUP EADDRINUSE - MCAST_JOIN_SOURCE_GROUP MCAST_LEAVE_GROUP 0 - MCAST_JOIN_SOURCE_GROUP MCAST_JOIN_SOURCE_GROUP (*1) - MCAST_JOIN_SOURCE_GROUP MCAST_LEAVE_SOURCE_GROUP (*2) - MCAST_JOIN_SOURCE_GROUP MCAST_BLOCK_SOURCE EINVAL - MCAST_JOIN_SOURCE_GROUP MCAST_UNBLOCK_SOURCE EINVAL - MCAST_BLOCK_SOURCE MCAST_JOIN_GROUP EADDRINUSE - MCAST_BLOCK_SOURCE MCAST_LEAVE_GROUP 0 - MCAST_BLOCK_SOURCE MCAST_JOIN_SOURCE_GROUP EINVAL - MCAST_BLOCK_SOURCE MCAST_LEAVE_SOURCE_GROUP EINVAL - MCAST_BLOCK_SOURCE MCAST_BLOCK_SOURCE (*1) - MCAST_BLOCK_SOURCE MCAST_UNBLOCK_SOURCE (*2) - -(*1) EADDRNOTAVAIL if source address is same of filtered one. Otherwise 0. -(*2) EADDRNOTAVAIL if source address is not same of filtered one. Otherwise 0. - - -http://planete.inria.fr/Hitoshi.Asaeda/mldv2/README.txt - - -The following steps apply for any-source applications: - - Use MCAST_JOIN_GROUP to join a group. - Use MCAST_BLOCK_SOURCE to turn off a given source, if required. - Use MCAST_UNBLOCK_SOURCE to re-allow a blocked source, if required. - Use MCAST_LEAVE_GROUP to leave the group. - -The following steps apply for controlled-source applications: - - Use MCAST_JOIN_SOURCE_GROUP to join each group/source pair. - Use MCAST_LEAVE_SOURCE_GROUP to leave each group/source, or use MCAST_LEAVE_GROUP to leave all sources, if the same group address is used by all sources. - -The following steps apply for any-source applications: - - Use IP_ADD_MEMBERSHIP to join a group (IPV6_ADD_MEMBERSHIP for IPv6). - Use IP_BLOCK_SOURCE to turn off a given source, if required. - Use IP_UNBLOCK_SOURCE to re-allow a blocked source, if required. - Use IP_DROP_MEMBERSHIP to leave the group (IPV6_DROP_MEMBERSHIP for IPv6). - -The following steps apply for controlled-source applications: - - Use IP_ADD_SOURCE_MEMBERSHIP to join each group/source pair. - Use IP_DROP_SOURCE_MEMBERSHIP to leave each group/source, or use IP_DROP_MEMBERSHIP to leave all sources, if the same group address is used by all sources. - -http://msdn.microsoft.com/en-us/library/ms738558(VS.85).aspx diff --git a/3rdparty/openpgm-svn-r1085/pgm/msfec.txt b/3rdparty/openpgm-svn-r1085/pgm/msfec.txt deleted file mode 100644 index 4b2c23a..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/msfec.txt +++ /dev/null @@ -1,33 +0,0 @@ -FEC parameters for Microsoft's PGM stack - - -FECBlockSize (n) [FECGroupSize+1, 255] -Maximum number of packets that can be sent for any group, including original data and parity packets. Maximum and default value is 255. - -FECProActivePackets -Number of packets to send proactively with each group. Use this option when the network is dispersed, and upstream NAK requests are expensive. - -FECGroupSize (k) [2, 128] -Number of packets to be treated as one group for the purpose of computing parity packets. Group size must be a power of two. In lossy networks, keep the group size relatively small. - -fFECOnDemandParityEnabled -Specifies whether the sender is enabled for sending parity repair packets. When TRUE, receivers should only request parity repair packets. - - -Reed Solomon codes: - - encode/decode time (us) -RS(255, 2) 4/6 -RS(255, 4) 7/10 -RS(255, 8) 14/18 -RS(255, 16) 29/34 -RS(255, 32) 57/64 -RS(255, 64) 119/134 -RS(255, 128) 236/fail(278) - -reference platform: Intel Xeon CPU 3.20Ghz - - -Implementation exact copy of Luigi Rizzo FEC code as demonstrated in RMDP: - -http://info.iet.unipi.it/~luigi/fec.html diff --git a/3rdparty/openpgm-svn-r1085/pgm/nametoindex.c b/3rdparty/openpgm-svn-r1085/pgm/nametoindex.c deleted file mode 100644 index 28444d1..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/nametoindex.c +++ /dev/null @@ -1,249 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Windows interface name to interface index function. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifdef _WIN32 -# include -# include -#endif -#include -#include - - -//#define NAMETOINDEX_DEBUG - -#define MAX_TRIES 3 -#define DEFAULT_BUFFER_SIZE 4096 - - -#ifdef _WIN32 -static inline -void* -_pgm_heap_alloc ( - const size_t n_bytes - ) -{ -# ifdef CONFIG_USE_HEAPALLOC - return HeapAlloc (GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, n_bytes); -# else - return pgm_malloc (n_bytes); -# endif -} - -static inline -void -_pgm_heap_free ( - void* mem - ) -{ -# ifdef CONFIG_USE_HEAPALLOC - HeapFree (GetProcessHeap(), 0, mem); -# else - pgm_free (mem); -# endif -} - -/* Retrieve adapter index via name. - * Wine edition: First try GetAdapterIndex() then fallback to enumerating - * adapters via GetAdaptersInfo(). - * - * On error returns zero, no errors are defined. - */ - -static -unsigned /* type matching if_nametoindex() */ -_pgm_getadaptersinfo_nametoindex ( - const sa_family_t iffamily, - const char* ifname - ) -{ - pgm_return_val_if_fail (NULL != ifname, 0); - - pgm_assert (AF_INET6 != iffamily); - - DWORD dwRet, ifIndex; - ULONG ulOutBufLen = DEFAULT_BUFFER_SIZE; - PIP_ADAPTER_INFO pAdapterInfo = NULL; - PIP_ADAPTER_INFO pAdapter = NULL; - -/* loop to handle interfaces coming online causing a buffer overflow - * between first call to list buffer length and second call to enumerate. - */ - for (unsigned i = MAX_TRIES; i; i--) - { - pgm_debug ("IP_ADAPTER_INFO buffer length %lu bytes.", ulOutBufLen); - pAdapterInfo = (IP_ADAPTER_INFO*)_pgm_heap_alloc (ulOutBufLen); - dwRet = GetAdaptersInfo (pAdapterInfo, &ulOutBufLen); - if (ERROR_BUFFER_OVERFLOW == dwRet) { - _pgm_heap_free (pAdapterInfo); - pAdapterInfo = NULL; - } else { - break; - } - } - - switch (dwRet) { - case ERROR_SUCCESS: /* NO_ERROR */ - break; - case ERROR_BUFFER_OVERFLOW: - pgm_warn (_("GetAdaptersInfo repeatedly failed with ERROR_BUFFER_OVERFLOW.")); - if (pAdapterInfo) - _pgm_heap_free (pAdapterInfo); - return 0; - default: - pgm_warn (_("GetAdaptersInfo failed")); - if (pAdapterInfo) - _pgm_heap_free (pAdapterInfo); - return 0; - } - - for (pAdapter = pAdapterInfo; - pAdapter; - pAdapter = pAdapter->Next) - { - for (IP_ADDR_STRING *pIPAddr = &pAdapter->IpAddressList; - pIPAddr; - pIPAddr = pIPAddr->Next) - { -/* skip null adapters */ - if (strlen (pIPAddr->IpAddress.String) == 0) - continue; - - if (0 == strncmp (ifname, pAdapter->AdapterName, IF_NAMESIZE)) { - ifIndex = pAdapter->Index; - _pgm_heap_free (pAdapterInfo); - return ifIndex; - } - } - } - - if (pAdapterInfo) - _pgm_heap_free (pAdapterInfo); - return 0; -} - -/* Retrieve adapter index via name. - * Windows edition: First try GetAdapterIndex() then fallback to enumerating - * adapters via GetAdaptersAddresses(). - * - * On error returns zero, no errors are defined. - */ - -static -unsigned /* type matching if_nametoindex() */ -_pgm_getadaptersaddresses_nametoindex ( - const sa_family_t iffamily, - const char* ifname - ) -{ - pgm_return_val_if_fail (NULL != ifname, 0); - - ULONG ifIndex; - DWORD dwSize = DEFAULT_BUFFER_SIZE, dwRet; - IP_ADAPTER_ADDRESSES *pAdapterAddresses = NULL, *adapter; - -/* first see if GetAdapterIndex is working - */ - dwRet = GetAdapterIndex ((const LPWSTR)ifname, &ifIndex); - if (NO_ERROR == dwRet) - return ifIndex; - -/* fallback to finding index via iterating adapter list */ - -/* loop to handle interfaces coming online causing a buffer overflow - * between first call to list buffer length and second call to enumerate. - */ - for (unsigned i = MAX_TRIES; i; i--) - { - pAdapterAddresses = (IP_ADAPTER_ADDRESSES*)_pgm_heap_alloc (dwSize); - dwRet = GetAdaptersAddresses (AF_UNSPEC, - GAA_FLAG_SKIP_ANYCAST | - GAA_FLAG_SKIP_DNS_SERVER | - GAA_FLAG_SKIP_FRIENDLY_NAME | - GAA_FLAG_SKIP_MULTICAST, - NULL, - pAdapterAddresses, - &dwSize); - if (ERROR_BUFFER_OVERFLOW == dwRet) { - _pgm_heap_free (pAdapterAddresses); - pAdapterAddresses = NULL; - } else { - break; - } - } - - switch (dwRet) { - case ERROR_SUCCESS: - break; - case ERROR_BUFFER_OVERFLOW: - pgm_warn (_("GetAdaptersAddresses repeatedly failed with ERROR_BUFFER_OVERFLOW")); - if (pAdapterAddresses) - _pgm_heap_free (pAdapterAddresses); - return 0; - default: - pgm_warn (_("GetAdaptersAddresses failed")); - if (pAdapterAddresses) - _pgm_heap_free (pAdapterAddresses); - return 0; - } - - for (adapter = pAdapterAddresses; - adapter; - adapter = adapter->Next) - { - if (0 == strcmp (ifname, adapter->AdapterName)) { - ifIndex = AF_INET6 == iffamily ? adapter->Ipv6IfIndex : adapter->IfIndex; - _pgm_heap_free (pAdapterAddresses); - return ifIndex; - } - } - - if (pAdapterAddresses) - _pgm_heap_free (pAdapterAddresses); - return 0; -} -#endif /* _WIN32 */ - -/* Retrieve interface index for a specified adapter name. - * On error returns zero, no errors are defined. - */ - -unsigned /* type matching if_nametoindex() */ -pgm_if_nametoindex ( -#ifndef _WIN32 - PGM_GNUC_UNUSED const sa_family_t iffamily, -#else - const sa_family_t iffamily, -#endif - const char* ifname - ) -{ - pgm_return_val_if_fail (NULL != ifname, 0); - -#ifndef _WIN32 - return if_nametoindex (ifname); -#elif defined(CONFIG_TARGET_WINE) - return _pgm_getadaptersinfo_nametoindex (iffamily, ifname); -#else - return _pgm_getadaptersaddresses_nametoindex (iffamily, ifname); -#endif -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/net-snmp.txt b/3rdparty/openpgm-svn-r1085/pgm/net-snmp.txt deleted file mode 100644 index 549bcc2..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/net-snmp.txt +++ /dev/null @@ -1,34 +0,0 @@ -net-snmp is hard coded on mib location, only the mib file names can -be specified outside of snmptranslate. - -$ mkdir -p ~/.snmp/mibs -$ cp mibs/PGM-MIB-petrova-01.txt ~/.snmp/mibs - -Basic test: - -$ snmptranslate -m ALL -IR pgmMIB -PGM-MIB::pgmMIB - -Display full pretty tree: - -$ snmptranslate -m ALL -Tp -IR pgmMIB -+--pgmMIB(112) - | - +--pgm(1) - | | - | +--pgmNetworkElement(1) - | | | -... - - -Now the framework tool can be used: - -$ env MIBS="+ALL" mib2c pgmMIB - -... - -To run with SNMP install snmpd, enable "master agentx" and walk: - -$ env MIBS="+ALL" snmpwalk -v 1 -c public localhost pgmMIB -End of MIB - diff --git a/3rdparty/openpgm-svn-r1085/pgm/net.c b/3rdparty/openpgm-svn-r1085/pgm/net.c deleted file mode 100644 index 5b2f9e9..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/net.c +++ /dev/null @@ -1,175 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * network send wrapper. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#ifdef CONFIG_HAVE_POLL -# include -#endif -#ifndef _WIN32 -# include -# include -# include -#endif -#include -#include -#include -#include - - -#define NET_DEBUG - - -#if !defined(ENETUNREACH) && defined(WSAENETUNREACH) -# define ENETUNREACH WSAENETUNREACH -#endif -#if !defined(EHOSTUNREACH) && defined(WSAEHOSTUNREACH) -# define EHOSTUNREACH WSAEHOSTUNREACH -#endif -#if !defined(ENOBUFS) && defined(WSAENOBUFS) -# define ENOBUFS WSAENOBUFS -#endif - - -/* locked and rate regulated sendto - * - * on success, returns number of bytes sent. on error, -1 is returned, and - * errno set appropriately. - */ - -ssize_t -pgm_sendto ( - pgm_sock_t* sock, - bool use_rate_limit, - bool use_router_alert, - const void* restrict buf, - size_t len, - const struct sockaddr* restrict to, - socklen_t tolen - ) -{ - pgm_assert( NULL != sock ); - pgm_assert( NULL != buf ); - pgm_assert( len > 0 ); - pgm_assert( NULL != to ); - pgm_assert( tolen > 0 ); - -#ifdef NET_DEBUG - char saddr[INET_ADDRSTRLEN]; - pgm_sockaddr_ntop (to, saddr, sizeof(saddr)); - pgm_debug ("pgm_sendto (sock:%p use_rate_limit:%s use_router_alert:%s buf:%p len:%zu to:%s [toport:%d] tolen:%d)", - (const void*)sock, - use_rate_limit ? "TRUE" : "FALSE", - use_router_alert ? "TRUE" : "FALSE", - (const void*)buf, - len, - saddr, - ntohs (((const struct sockaddr_in*)to)->sin_port), - (int)tolen); -#endif - - const int send_sock = use_router_alert ? sock->send_with_router_alert_sock : sock->send_sock; - - if (use_rate_limit && - !pgm_rate_check (&sock->rate_control, len, sock->is_nonblocking)) - { - errno = ENOBUFS; - return (const ssize_t)-1; - } - - if (!use_router_alert && sock->can_send_data) - pgm_mutex_lock (&sock->send_mutex); - - ssize_t sent = sendto (send_sock, buf, len, 0, to, (socklen_t)tolen); - pgm_debug ("sendto returned %zd", sent); - if (sent < 0) { - int save_errno = pgm_sock_errno(); - if (PGM_UNLIKELY(errno != ENETUNREACH && /* Network is unreachable */ - errno != EHOSTUNREACH && /* No route to host */ - errno != EAGAIN)) /* would block on non-blocking send */ - { -#ifdef CONFIG_HAVE_POLL -/* poll for cleared socket */ - struct pollfd p = { - .fd = send_sock, - .events = POLLOUT, - .revents = 0 - }; - const int ready = poll (&p, 1, 500 /* ms */); -#else - fd_set writefds; - FD_ZERO(&writefds); - FD_SET(send_sock, &writefds); - struct timeval tv = { - .tv_sec = 0, - .tv_usec = 500 /* ms */ * 1000 - }; - const int ready = select (1, NULL, &writefds, NULL, &tv); -#endif /* CONFIG_HAVE_POLL */ - if (ready > 0) - { - sent = sendto (send_sock, buf, len, 0, to, (socklen_t)tolen); - if ( sent < 0 ) - { - save_errno = pgm_sock_errno(); - pgm_warn (_("sendto() %s failed: %s"), - inet_ntoa( ((const struct sockaddr_in*)to)->sin_addr ), - pgm_sock_strerror (save_errno)); - } - } - else if (ready == 0) - { - pgm_warn (_("sendto() %s failed: socket timeout."), - inet_ntoa( ((const struct sockaddr_in*)to)->sin_addr )); - } - else - { - save_errno = pgm_sock_errno(); - pgm_warn (_("blocked socket failed: %s"), - pgm_sock_strerror (save_errno)); - } - } - } - - if (!use_router_alert && sock->can_send_data) - pgm_mutex_unlock (&sock->send_mutex); - return sent; -} - -/* socket helper, for setting pipe ends non-blocking - * - * on success, returns 0. on error, returns -1, and sets errno appropriately. - */ - -int -pgm_set_nonblocking ( - int fd[2] - ) -{ -/* pre-conditions */ - pgm_assert (fd[0]); - pgm_assert (fd[1]); - - pgm_sockaddr_nonblocking (fd[0], TRUE); - pgm_sockaddr_nonblocking (fd[1], TRUE); - return 0; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/net_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/net_unittest.c deleted file mode 100644 index fc684ca..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/net_unittest.c +++ /dev/null @@ -1,375 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for network send wrapper. - * - * Copyright (c) 2009-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include -#include - - -/* mock state */ - -#define pgm_rate_check mock_pgm_rate_check -#define sendto mock_sendto -#define poll mock_poll -#define select mock_select -#define fcntl mock_fcntl - -#define NET_DEBUG -#include "net.c" - - -static -pgm_sock_t* -generate_sock (void) -{ - pgm_sock_t* sock = g_malloc0 (sizeof(pgm_sock_t)); - return sock; -} - -static -char* -flags_string ( - int flags - ) -{ - static char s[1024]; - - s[0] = '\0'; - if (flags & MSG_OOB) - strcat (s, "MSG_OOB"); -#define MSG(flag) \ - do { \ - if (flags & flag) { \ - strcat (s, s[0] ? ("|" #flag) : (#flag)); \ - } \ - } while (0) -#ifdef MSG_PEEK - MSG(MSG_PEEK); -#endif -#ifdef MSG_DONTROUTE - MSG(MSG_DONTROUTE); -#endif -#ifdef MSG_CTRUNC - MSG(MSG_CTRUNC); -#endif -#ifdef MSG_PROXY - MSG(MSG_PROXY); -#endif -#ifdef MSG_TRUNC - MSG(MSG_TRUNC); -#endif -#ifdef MSG_DONTWAIT - MSG(MSG_DONTWAIT); -#endif -#ifdef MSG_EOR - MSG(MSG_EOR); -#endif -#ifdef MSG_WAITALL - MSG(MSG_WAITALL); -#endif -#ifdef MSG_FIN - MSG(MSG_FIN); -#endif -#ifdef MSG_SYN - MSG(MSG_SYN); -#endif -#ifdef MSG_CONFIRM - MSG(MSG_CONFIRM); -#endif -#ifdef MSG_RST - MSG(MSG_RST); -#endif -#ifdef MSG_ERRQUEUE - MSG(MSG_ERRQUEUE); -#endif -#ifdef MSG_NOSIGNAL - MSG(MSG_NOSIGNAL); -#endif -#ifdef MSG_MORE - MSG(MSG_MORE); -#endif -#ifdef MSG_CMSG_CLOEXEC - MSG(MSG_CMSG_CLOEXEC); -#endif - if (!s[0]) { - if (flags) - sprintf (s, "0x%x", flags); - else - strcpy (s, "0"); - } - return s; -} - - -/* mock functions for external references */ - -size_t -pgm_pkt_offset ( - const bool can_fragment, - const sa_family_t pgmcc_family /* 0 = disable */ - ) -{ - return 0; -} - -PGM_GNUC_INTERNAL -bool -mock_pgm_rate_check ( - pgm_rate_t* bucket, - const size_t data_size, - const bool is_nonblocking - ) -{ - g_debug ("mock_pgm_rate_check (bucket:%p data-size:%zu is-nonblocking:%s)", - (gpointer)bucket, data_size, is_nonblocking ? "TRUE" : "FALSE"); - return TRUE; -} - -ssize_t -mock_sendto ( - int s, - const void* buf, - size_t len, - int flags, - const struct sockaddr* to, - socklen_t tolen - ) -{ - char saddr[INET6_ADDRSTRLEN]; - pgm_sockaddr_ntop (to, saddr, sizeof(saddr)); - g_debug ("mock_sendto (s:%i buf:%p len:%d flags:%s to:%s tolen:%d)", - s, buf, len, flags_string (flags), saddr, tolen); - return len; -} - -#ifdef CONFIG_HAVE_POLL -int -mock_poll ( - struct pollfd* fds, - nfds_t nfds, - int timeout - ) -{ - g_debug ("mock_poll (fds:%p nfds:%d timeout:%d)", - (gpointer)fds, (int)nfds, timeout); - return 0; -} -#else -int -mock_select ( - int nfds, - fd_set* readfds, - fd_set* writefds, - fd_set* exceptfds, - struct timeval* timeout - ) -{ - g_debug ("mock_select (nfds:%d readfds:%p writefds:%p exceptfds:%p timeout:%p)", - nfds, (gpointer)readfds, (gpointer)writefds, (gpointer)exceptfds, (gpointer)timeout); - return 0; -} -#endif - -int -mock_fcntl ( - int fd, - int cmd, - ... - ) -{ - long arg; - va_list args; - if (F_GETFL == cmd) { - g_debug ("mock_fcntl (fd:%d cmd:F_GETFL)", fd); - return 0; - } - if (F_SETFL == cmd) { - va_start (args, cmd); - arg = va_arg (args, long); - va_end (args); - g_debug ("mock_fcntl (fd:%d cmd:F_SETFL arg:%ld)", fd, arg); - return arg; - } - g_assert_not_reached(); -} - - -/* target: - * ssize_t - * pgm_sendto ( - * pgm_sock_t* sock, - * bool use_rate_limit, - * bool use_router_alert, - * const void* buf, - * size_t len, - * const struct sockaddr* to, - * socklen_t tolen - * ) - */ - -START_TEST (test_sendto_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - const char* buf = "i am not a string"; - struct sockaddr_in addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr ("172.12.90.1") - }; - gssize len = pgm_sendto (sock, FALSE, FALSE, buf, sizeof(buf), (struct sockaddr*)&addr, sizeof(addr)); - fail_unless (sizeof(buf) == len, "sendto underrun"); -} -END_TEST - -START_TEST (test_sendto_fail_001) -{ - const char* buf = "i am not a string"; - struct sockaddr_in addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr ("172.12.90.1") - }; - gssize len = pgm_sendto (NULL, FALSE, FALSE, buf, sizeof(buf), (struct sockaddr*)&addr, sizeof(addr)); - fail ("reached"); -} -END_TEST - -START_TEST (test_sendto_fail_002) -{ - pgm_sock_t* sock = generate_sock (); - const char* buf = "i am not a string"; - struct sockaddr_in addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr ("172.12.90.1") - }; - gssize len = pgm_sendto (sock, FALSE, FALSE, NULL, sizeof(buf), (struct sockaddr*)&addr, sizeof(addr)); - fail ("reached"); -} -END_TEST - -START_TEST (test_sendto_fail_003) -{ - pgm_sock_t* sock = generate_sock (); - const char* buf = "i am not a string"; - struct sockaddr_in addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr ("172.12.90.1") - }; - gssize len = pgm_sendto (sock, FALSE, FALSE, buf, 0, (struct sockaddr*)&addr, sizeof(addr)); - fail ("reached"); -} -END_TEST - -START_TEST (test_sendto_fail_004) -{ - pgm_sock_t* sock = generate_sock (); - const char* buf = "i am not a string"; - struct sockaddr_in addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr ("172.12.90.1") - }; - gssize len = pgm_sendto (sock, FALSE, FALSE, buf, sizeof(buf), NULL, sizeof(addr)); - fail ("reached"); -} -END_TEST - -START_TEST (test_sendto_fail_005) -{ - pgm_sock_t* sock = generate_sock (); - const char* buf = "i am not a string"; - struct sockaddr_in addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr ("172.12.90.1") - }; - gssize len = pgm_sendto (sock, FALSE, FALSE, buf, sizeof(buf), (struct sockaddr*)&addr, 0); - fail ("reached"); -} -END_TEST - -/* target: - * int - * pgm_set_nonblocking ( - * int filedes[2] - * ) - */ - -START_TEST (test_set_nonblocking_pass_001) -{ - int filedes[2] = { fileno (stdout), fileno (stderr) }; - int retval = pgm_set_nonblocking (filedes); -} -END_TEST - -START_TEST (test_set_nonblocking_fail_001) -{ - int filedes[2] = { 0, 0 }; - int retval = pgm_set_nonblocking (filedes); - fail ("reached"); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_sendto = tcase_create ("sendto"); - suite_add_tcase (s, tc_sendto); - tcase_add_test (tc_sendto, test_sendto_pass_001); - tcase_add_test_raise_signal (tc_sendto, test_sendto_fail_001, SIGABRT); - tcase_add_test_raise_signal (tc_sendto, test_sendto_fail_002, SIGABRT); - tcase_add_test_raise_signal (tc_sendto, test_sendto_fail_003, SIGABRT); - tcase_add_test_raise_signal (tc_sendto, test_sendto_fail_004, SIGABRT); - tcase_add_test_raise_signal (tc_sendto, test_sendto_fail_005, SIGABRT); - - TCase* tc_set_nonblocking = tcase_create ("set-nonblocking"); - suite_add_tcase (s, tc_set_nonblocking); - tcase_add_test (tc_set_nonblocking, test_set_nonblocking_pass_001); - tcase_add_test_raise_signal (tc_set_nonblocking, test_set_nonblocking_fail_001, SIGABRT); - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/options.txt b/3rdparty/openpgm-svn-r1085/pgm/options.txt deleted file mode 100644 index cb01da0..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/options.txt +++ /dev/null @@ -1,158 +0,0 @@ - OPT_LENGTH 0x00 - Option's Length - -pgm_opt_header -pgm_opt_length - -first option, always present. - --------------------------------------------------------------------------------- - - OPT_FRAGMENT 0x01 - Fragmentation - -pgm_opt_header -pgm_opt_fragment - -may be present for odata, rdata. 'MAY' exist for others, although a bit strange. - --------------------------------------------------------------------------------- - - OPT_NAK_LIST 0x02 - List of NAK entries - -pgm_opt_header -pgm_opt_nak_list - -may be present for naks. - --------------------------------------------------------------------------------- - - OPT_JOIN 0x03 - Late Joining - -pgm_opt_header -pgm_opt_join - -may be present for odata, rdata, spm. - -requires SPM to learn NLA already so not overly useful with odata/rdata, could be -used with video streaming to last i-frame data sequence number. - --------------------------------------------------------------------------------- - - OPT_REDIRECT 0x07 - Redirect - -pgm_opt_header -pgm_opt_redirect -pgm_opt_redirect6 - -should be present for polrs from a dlr. - --------------------------------------------------------------------------------- - - OPT_SYN 0x0D - Synchronization - -pgm_opt_header -pgm_opt_syn - -must only appear with odata or rdata. - --------------------------------------------------------------------------------- - - OPT_FIN 0x0E - Session Fin receivers, conventional - feedbackish - -pgm_opt_header -opt_opt_fin - -may be present for odata, rdata, must appear in following spms. - --------------------------------------------------------------------------------- - - OPT_RST 0x0F - Session Reset - -pgm_opt_header -pgm_opt_rst - -must only appear in spms. not many 'unrecoverable error conditions' exist though. - --------------------------------------------------------------------------------- - - OPT_PARITY - -must appear in odata or rdata to indicate pro-active or on-demand parity data, -nak to request parity repair data, ncf to confirm parity nak. - - - OPT_VAR_PKTLEN - -may be present in odata or data to indicate variable size packets. - - - OPT_PARITY_PRM 0x08 - Forward Error Correction Parameters - -pgm_opt_header -pgm_opt_parity_prm - -appended to spms to inform of pro-active or on-demand parity. - --------------------------------------------------------------------------------- - - OPT_PARITY_GRP 0x09 - Forward Error Correction Group Number - -pgm_opt_parity_grp - -appended to odata and rdata parity packets. - --------------------------------------------------------------------------------- - - OPT_CURR_TGSIZE 0x0A - Forward Error Correction Group Size - -pgm_opt_curr_tgsize - -must appear in last odata or rdata packet of variable transmission group, may -appear in spms. - --------------------------------------------------------------------------------- - - OPT_CR 0x10 - Congestion Report - -pgm_opt_header -pgm_opt_cr - --------------------------------------------------------------------------------- - - OPT_CRQST 0x11 - Congestion Report Request - -pgm_opt_header -pgm_opt_crqst - --------------------------------------------------------------------------------- - - OPT_NAK_BO_IVL 0x04 - NAK Back-Off Interval - -pgm_opt_header -pgm_opt_nak_bo_ivl - --------------------------------------------------------------------------------- - - OPT_NAK_BO_RNG 0x05 - NAK Back-Off Range - -pgm_opt_header -pgm_opt_nak_bo_rng - --------------------------------------------------------------------------------- - - OPT_NBR_UNREACH 0x0B - Neighbor Unreachable - -pgm_opt_header -pgm_opt_nbr_unreach - --------------------------------------------------------------------------------- - - OPT_PATH_NLA 0x0C - Path NLA - -pgm_opt_header -pgm_opt_path_nla -pgm_opt6_path_nla - --------------------------------------------------------------------------------- - - OPT_INVALID 0x7F - Option invalidated diff --git a/3rdparty/openpgm-svn-r1085/pgm/packet_parse.c b/3rdparty/openpgm-svn-r1085/pgm/packet_parse.c deleted file mode 100644 index 0fad8ca..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/packet_parse.c +++ /dev/null @@ -1,615 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * PGM packet formats, RFC 3208. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define __STDC_FORMAT_MACROS -#include -#include -#include -#include - - -//#define PACKET_DEBUG - -#ifndef PACKET_DEBUG -# define PGM_DISABLE_ASSERT -#endif - - -/* locals */ - -static bool pgm_parse (struct pgm_sk_buff_t*const restrict, pgm_error_t**restrict); - - -/* Parse a raw-IP packet for IP and PGM header and any payload. - */ - -#define PGM_MIN_SIZE ( \ - sizeof(struct pgm_ip) + /* IPv4 header */ \ - sizeof(struct pgm_header) /* PGM header */ \ - ) - -bool -pgm_parse_raw ( - struct pgm_sk_buff_t* const restrict skb, /* data will be modified */ - struct sockaddr* const restrict dst, - pgm_error_t** restrict error - ) -{ -/* pre-conditions */ - pgm_assert (NULL != skb); - pgm_assert (NULL != dst); - - pgm_debug ("pgm_parse_raw (skb:%p dst:%p error:%p)", - (const void*)skb, (const void*)dst, (const void*)error); - -/* minimum size should be IPv4 header plus PGM header, check IP version later */ - if (PGM_UNLIKELY(skb->len < PGM_MIN_SIZE)) - { - pgm_set_error (error, - PGM_ERROR_DOMAIN_PACKET, - PGM_ERROR_BOUNDS, - _("IP packet too small at %" PRIu16 " bytes, expecting at least %" PRIu16 " bytes."), - skb->len, (uint16_t)PGM_MIN_SIZE); - return FALSE; - } - -/* IP packet header: IPv4 - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * |Version| HL | ToS | Length | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Fragment ID |R|D|M| Fragment Offset | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | TTL | Protocol | Checksum | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Source IP Address | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Destination IP Address | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | IP Options when present ... - * +-+-+-+-+-+-+-+-+-+-+-+-+ ... - * | Data ... - * +-+-+- ... - * - * IPv6 - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * |Version| Traffic Class | Flow Label | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Payload Length | Next Header | Hop Limit | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | | - * | Source IP Address | - * | | - * | | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | | - * | Destination IP Address | - * | | - * | | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | IP Options when present ... - * +-+-+-+-+-+-+-+-+-+-+-+-+ ... - * | Data ... - * +-+-+- ... - * - */ - -/* decode IP header */ - const struct pgm_ip* ip = (struct pgm_ip*)skb->data; - switch (ip->ip_v) { - case 4: { - struct sockaddr_in* sin = (struct sockaddr_in*)dst; - sin->sin_family = AF_INET; - sin->sin_addr.s_addr = ip->ip_dst.s_addr; - break; - } - - case 6: - pgm_set_error (error, - PGM_ERROR_DOMAIN_PACKET, - PGM_ERROR_AFNOSUPPORT, - _("IPv6 is not supported for raw IP header parsing.")); - return FALSE; - - default: - pgm_set_error (error, - PGM_ERROR_DOMAIN_PACKET, - PGM_ERROR_AFNOSUPPORT, - _("IP header reports an invalid version %d."), - ip->ip_v); - return FALSE; - } - - const size_t ip_header_length = ip->ip_hl * 4; /* IP header length in 32bit octets */ - if (PGM_UNLIKELY(ip_header_length < sizeof(struct pgm_ip))) - { - pgm_set_error (error, - PGM_ERROR_DOMAIN_PACKET, - PGM_ERROR_BOUNDS, - _("IP header reports an invalid header length %zu bytes."), - ip_header_length); - return FALSE; - } - -#ifndef CONFIG_HOST_ORDER_IP_LEN - size_t packet_length = ntohs (ip->ip_len); /* total packet length */ -#else - size_t packet_length = ip->ip_len; /* total packet length */ -#endif - - -/* ip_len can equal packet_length - ip_header_length in FreeBSD/NetBSD - * Stevens/Fenner/Rudolph, Unix Network Programming Vol.1, p.739 - * - * RFC3828 allows partial packets such that len < packet_length with UDP lite - */ - if (skb->len == packet_length + ip_header_length) { - packet_length += ip_header_length; - } - - if (PGM_UNLIKELY(skb->len < packet_length)) { /* redundant: often handled in kernel */ - pgm_set_error (error, - PGM_ERROR_DOMAIN_PACKET, - PGM_ERROR_BOUNDS, - _("IP packet received at %" PRIu16 " bytes whilst IP header reports %zu bytes."), - skb->len, packet_length); - return FALSE; - } - -/* packets that fail checksum will generally not be passed upstream except with rfc3828 - */ -#if PGM_CHECK_IN_CKSUM - const uint16_t sum = in_cksum (data, packet_length, 0); - if (PGM_UNLIKELY(0 != sum)) { - const uint16_t ip_sum = ntohs (ip->ip_sum); - pgm_set_error (error, - PGM_ERROR_DOMAIN_PACKET, - PGM_ERROR_CKSUM, - _("IP packet checksum mismatch, reported 0x%x whilst calculated 0x%x."), - ip_sum, sum); - return FALSE; - } -#endif - -/* fragmentation offset, bit 0: 0, bit 1: do-not-fragment, bit 2: more-fragments */ -#ifndef CONFIG_HOST_ORDER_IP_OFF - const uint16_t offset = ntohs (ip->ip_off); -#else - const uint16_t offset = ip->ip_off; -#endif - if (PGM_UNLIKELY((offset & 0x1fff) != 0)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_PACKET, - PGM_ERROR_PROTO, - _("IP header reports packet fragmentation, offset %u."), - offset & 0x1fff); - return FALSE; - } - -/* PGM payload, header looks as follows: - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Source Port | Destination Port | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Type | Options | Checksum | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Global Source ID ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | ... Global Source ID | TSDU Length | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Type specific data ... - * +-+-+-+-+-+-+-+-+-+- ... - */ - - skb->pgm_header = (void*)( (char*)skb->data + ip_header_length ); - -/* advance DATA pointer to PGM packet */ - skb->data = skb->pgm_header; - skb->len -= ip_header_length; - return pgm_parse (skb, error); -} - -bool -pgm_parse_udp_encap ( - struct pgm_sk_buff_t* restrict skb, /* will be modified */ - pgm_error_t** restrict error - ) -{ - pgm_assert (NULL != skb); - - if (PGM_UNLIKELY(skb->len < sizeof(struct pgm_header))) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_PACKET, - PGM_ERROR_BOUNDS, - _("UDP payload too small for PGM packet at %" PRIu16 " bytes, expecting at least %zu bytes."), - skb->len, sizeof(struct pgm_header)); - return FALSE; - } - -/* DATA payload is PGM packet, no headers */ - skb->pgm_header = skb->data; - return pgm_parse (skb, error); -} - -/* will modify packet contents to calculate and check PGM checksum - */ -static -bool -pgm_parse ( - struct pgm_sk_buff_t* const restrict skb, /* will be modified to calculate checksum */ - pgm_error_t** restrict error - ) -{ -/* pre-conditions */ - pgm_assert (NULL != skb); - -/* pgm_checksum == 0 means no transmitted checksum */ - if (skb->pgm_header->pgm_checksum) - { - const uint16_t sum = skb->pgm_header->pgm_checksum; - skb->pgm_header->pgm_checksum = 0; - const uint16_t pgm_sum = pgm_csum_fold (pgm_csum_partial ((const char*)skb->pgm_header, skb->len, 0)); - skb->pgm_header->pgm_checksum = sum; - if (PGM_UNLIKELY(pgm_sum != sum)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_PACKET, - PGM_ERROR_CKSUM, - _("PGM packet checksum mismatch, reported 0x%x whilst calculated 0x%x."), - pgm_sum, sum); - return FALSE; - } - } else { - if (PGM_ODATA == skb->pgm_header->pgm_type || - PGM_RDATA == skb->pgm_header->pgm_type) - { - pgm_set_error (error, - PGM_ERROR_DOMAIN_PACKET, - PGM_ERROR_PROTO, - _("PGM checksum missing whilst mandatory for %cDATA packets."), - PGM_ODATA == skb->pgm_header->pgm_type ? 'O' : 'R'); - return FALSE; - } - pgm_debug ("No PGM checksum :O"); - } - -/* copy packets source transport identifier */ - memcpy (&skb->tsi.gsi, skb->pgm_header->pgm_gsi, sizeof(pgm_gsi_t)); - skb->tsi.sport = skb->pgm_header->pgm_sport; - return TRUE; -} - -/* 8.1. Source Path Messages (SPM) - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | SPM's Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Trailing Edge Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Leading Edge Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | NLA AFI | Reserved | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Path NLA ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ - * | Option Extensions when present ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * - * NLA = Network Layer Address - * NLA AFI = NLA Address Family Indicator: rfc 1700 (ADDRESS FAMILY NUMBERS) - * => Path NLA = IP address of last network element - */ - -#define PGM_MIN_SPM_SIZE ( sizeof(struct pgm_spm) ) - -bool -pgm_verify_spm ( - const struct pgm_sk_buff_t* const skb - ) -{ -/* pre-conditions */ - pgm_assert (NULL != skb); - - const struct pgm_spm* spm = (const struct pgm_spm*)skb->data; - switch (ntohs (spm->spm_nla_afi)) { -/* truncated packet */ - case AFI_IP6: - if (PGM_UNLIKELY(skb->len < sizeof(struct pgm_spm6))) - return FALSE; - break; - case AFI_IP: - if (PGM_UNLIKELY(skb->len < sizeof(struct pgm_spm))) - return FALSE; - break; - - default: - return FALSE; - } - - return TRUE; -} - -/* 14.7.1. Poll Request - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | POLL's Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | POLL's Round | POLL's Sub-type | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | NLA AFI | Reserved | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Path NLA ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ - * | POLL's Back-off Interval | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Random String | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Matching Bit-Mask | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Option Extensions when present ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * - * Sent to ODATA multicast group with IP Router Alert option. - */ - -#define PGM_MIN_POLL_SIZE ( sizeof(struct pgm_poll) ) - -bool -pgm_verify_poll ( - const struct pgm_sk_buff_t* const skb - ) -{ -/* pre-conditions */ - pgm_assert (NULL != skb); - - const struct pgm_poll* poll4 = (const struct pgm_poll*)skb->data; - switch (ntohs (poll4->poll_nla_afi)) { -/* truncated packet */ - case AFI_IP6: - if (PGM_UNLIKELY(skb->len < sizeof(struct pgm_poll6))) - return FALSE; - break; - case AFI_IP: - if (PGM_UNLIKELY(skb->len < sizeof(struct pgm_poll))) - return FALSE; - break; - - default: - return FALSE; - } - - return TRUE; -} - -/* 14.7.2. Poll Response - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | POLR's Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | POLR's Round | reserved | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Option Extensions when present ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - -bool -pgm_verify_polr ( - const struct pgm_sk_buff_t* const skb - ) -{ -/* pre-conditions */ - pgm_assert (NULL != skb); - -/* truncated packet */ - if (PGM_UNLIKELY(skb->len < sizeof(struct pgm_polr))) - return FALSE; - return TRUE; -} - -/* 8.2. Data Packet - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Data Packet Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Trailing Edge Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Option Extensions when present ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Data ... - * +-+-+- ... - */ - -/* no verification api */ - -/* 8.3. NAK - * - * Technically the AFI of the source and multicast group can be different - * but that would be very wibbly wobbly. One example is using a local DLR - * with a IPv4 address to reduce NAK cost for recovery on wide IPv6 - * distribution. - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Requested Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | NLA AFI | Reserved | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Source NLA ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ - * | NLA AFI | Reserved | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Multicast Group NLA ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ - * | Option Extensions when present ... - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... - */ - -#define PGM_MIN_NAK_SIZE ( sizeof(struct pgm_nak) ) - -bool -pgm_verify_nak ( - const struct pgm_sk_buff_t* const skb - ) -{ -/* pre-conditions */ - pgm_assert (NULL != skb); - - pgm_debug ("pgm_verify_nak (skb:%p)", (const void*)skb); - -/* truncated packet */ - if (PGM_UNLIKELY(skb->len < PGM_MIN_NAK_SIZE)) - return FALSE; - - const struct pgm_nak* nak = (struct pgm_nak*)skb->data; - const uint16_t nak_src_nla_afi = ntohs (nak->nak_src_nla_afi); - uint16_t nak_grp_nla_afi = 0; - -/* check source NLA: unicast address of the ODATA sender */ - switch (nak_src_nla_afi) { - case AFI_IP: - nak_grp_nla_afi = ntohs (nak->nak_grp_nla_afi); - break; - - case AFI_IP6: - nak_grp_nla_afi = ntohs (((const struct pgm_nak6*)nak)->nak6_grp_nla_afi); - break; - - default: - return FALSE; - } - -/* check multicast group NLA */ - switch (nak_grp_nla_afi) { - case AFI_IP6: - switch (nak_src_nla_afi) { -/* IPv4 + IPv6 NLA */ - case AFI_IP: - if (PGM_UNLIKELY(skb->len < ( sizeof(struct pgm_nak) + sizeof(struct in6_addr) - sizeof(struct in_addr) ))) - return FALSE; - break; - -/* IPv6 + IPv6 NLA */ - case AFI_IP6: - if (PGM_UNLIKELY(skb->len < sizeof(struct pgm_nak6))) - return FALSE; - break; - } - - case AFI_IP: - break; - - default: - return FALSE; - } - - return TRUE; -} - -/* 8.3. N-NAK - */ - -bool -pgm_verify_nnak ( - const struct pgm_sk_buff_t* const skb - ) -{ -/* pre-conditions */ - pgm_assert (NULL != skb); - - return pgm_verify_nak (skb); -} - -/* 8.3. NCF - */ - -bool -pgm_verify_ncf ( - const struct pgm_sk_buff_t* const skb - ) -{ -/* pre-conditions */ - pgm_assert (NULL != skb); - - return pgm_verify_nak (skb); -} - -/* 13.6. SPM Request - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Option Extensions when present ... - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... - */ - -bool -pgm_verify_spmr ( - PGM_GNUC_UNUSED const struct pgm_sk_buff_t* skb - ) -{ -/* pre-conditions */ - pgm_assert (NULL != skb); - - return TRUE; -} - -/* PGMCC: ACK - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | RX_MAX | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Received Packet Bitmap | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Option Extensions when present ... - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... - */ - -#define PGM_MIN_ACK_SIZE ( sizeof(struct pgm_ack) ) - -bool -pgm_verify_ack ( - PGM_GNUC_UNUSED const struct pgm_sk_buff_t* skb - ) -{ -/* pre-conditions */ - pgm_assert (NULL != skb); - - return TRUE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/packet_parse_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/packet_parse_unittest.c deleted file mode 100644 index 94b06d2..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/packet_parse_unittest.c +++ /dev/null @@ -1,382 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for PGM packet handling. - * - * Copyright (c) 2009-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include -#include -#include -#include - - -/* mock state */ - -#define PACKET_DEBUG -#include "packet_parse.c" - - -static -struct pgm_sk_buff_t* -generate_raw_pgm (void) -{ - const char source[] = "i am not a string"; - const guint source_len = sizeof(source); - struct pgm_sk_buff_t* skb; - GError* err = NULL; - - skb = pgm_alloc_skb (1500); - skb->sock = (pgm_sock_t*)0x1; - skb->tstamp = 0x1; - skb->data = skb->head; - skb->len = sizeof(struct pgm_ip) + sizeof(struct pgm_header) + sizeof(struct pgm_data) + source_len; - skb->tail = (guint8*)skb->data + skb->len; - -/* add IP header */ - struct pgm_ip* iphdr = skb->data; - iphdr->ip_hl = sizeof(struct pgm_ip) / 4; - iphdr->ip_v = 4; - iphdr->ip_tos = 0; - iphdr->ip_len = g_htons (skb->len); - iphdr->ip_id = 0; - iphdr->ip_off = 0; - iphdr->ip_ttl = 16; - iphdr->ip_p = IPPROTO_PGM; - iphdr->ip_sum = 0; - iphdr->ip_src.s_addr = inet_addr ("127.0.0.1"); - iphdr->ip_dst.s_addr = inet_addr ("127.0.0.2"); - -/* add PGM header */ - struct pgm_header* pgmhdr = (gpointer)(iphdr + 1); - pgmhdr->pgm_sport = g_htons ((guint16)1000); - pgmhdr->pgm_dport = g_htons ((guint16)7500); - pgmhdr->pgm_type = PGM_ODATA; - pgmhdr->pgm_options = 0; - pgmhdr->pgm_gsi[0] = 1; - pgmhdr->pgm_gsi[1] = 2; - pgmhdr->pgm_gsi[2] = 3; - pgmhdr->pgm_gsi[3] = 4; - pgmhdr->pgm_gsi[4] = 5; - pgmhdr->pgm_gsi[5] = 6; - pgmhdr->pgm_tsdu_length = g_htons (source_len); - -/* add ODATA header */ - struct pgm_data* datahdr = (gpointer)(pgmhdr + 1); - datahdr->data_sqn = g_htonl ((guint32)0); - datahdr->data_trail = g_htonl ((guint32)-1); - -/* add payload */ - gpointer data = (gpointer)(datahdr + 1); - memcpy (data, source, source_len); - -/* finally PGM checksum */ - pgmhdr->pgm_checksum = 0; - pgmhdr->pgm_checksum = pgm_csum_fold (pgm_csum_partial (pgmhdr, sizeof(struct pgm_header) + sizeof(struct pgm_data) + source_len, 0)); - -/* and IP checksum */ - iphdr->ip_sum = pgm_inet_checksum (skb->head, skb->len, 0); - - return skb; -} - -static -struct pgm_sk_buff_t* -generate_udp_encap_pgm (void) -{ - const char source[] = "i am not a string"; - const guint source_len = sizeof(source); - struct pgm_sk_buff_t* skb; - GError* err = NULL; - - skb = pgm_alloc_skb (1500); - skb->sock = (pgm_sock_t*)0x1; - skb->tstamp = 0x1; - skb->data = skb->head; - skb->len = sizeof(struct pgm_header) + sizeof(struct pgm_data) + source_len; - skb->tail = (guint8*)skb->data + skb->len; - -/* add PGM header */ - struct pgm_header* pgmhdr = skb->head; - pgmhdr->pgm_sport = g_htons ((guint16)1000); - pgmhdr->pgm_dport = g_htons ((guint16)7500); - pgmhdr->pgm_type = PGM_ODATA; - pgmhdr->pgm_options = 0; - pgmhdr->pgm_gsi[0] = 1; - pgmhdr->pgm_gsi[1] = 2; - pgmhdr->pgm_gsi[2] = 3; - pgmhdr->pgm_gsi[3] = 4; - pgmhdr->pgm_gsi[4] = 5; - pgmhdr->pgm_gsi[5] = 6; - pgmhdr->pgm_tsdu_length = g_htons (source_len); - -/* add ODATA header */ - struct pgm_data* datahdr = (gpointer)(pgmhdr + 1); - datahdr->data_sqn = g_htonl ((guint32)0); - datahdr->data_trail = g_htonl ((guint32)-1); - -/* add payload */ - gpointer data = (gpointer)(datahdr + 1); - memcpy (data, source, source_len); - -/* finally PGM checksum */ - pgmhdr->pgm_checksum = 0; - pgmhdr->pgm_checksum = pgm_csum_fold (pgm_csum_partial (pgmhdr, sizeof(struct pgm_header) + sizeof(struct pgm_data) + source_len, 0)); - - return skb; -} - -/* mock functions for external references */ - -size_t -pgm_pkt_offset ( - const bool can_fragment, - const sa_family_t pgmcc_family /* 0 = disable */ - ) -{ - return 0; -} - - -/* target: - * bool - * pgm_parse_raw ( - * struct pgm_sk_buff_t* const skb, - * struct sockaddr* const addr, - * pgm_error_t** error - * ) - */ - -START_TEST (test_parse_raw_pass_001) -{ - struct sockaddr_storage addr; - pgm_error_t* err = NULL; - struct pgm_sk_buff_t* skb = generate_raw_pgm (); - gboolean success = pgm_parse_raw (skb, (struct sockaddr*)&addr, &err); - if (!success && err) { - g_error ("Parsing raw packet: %s", err->message); - } - fail_unless (TRUE == success, "parse_raw failed"); - char saddr[INET6_ADDRSTRLEN]; - pgm_sockaddr_ntop ((struct sockaddr*)&addr, saddr, sizeof(saddr)); - g_message ("Decoded destination NLA: %s", saddr); -} -END_TEST - -START_TEST (test_parse_raw_fail_001) -{ - struct sockaddr_storage addr; - pgm_error_t* err = NULL; - pgm_parse_raw (NULL, (struct sockaddr*)&addr, &err); - fail ("reached"); -} -END_TEST - -/* target: - * bool - * pgm_parse_udp_encap ( - * struct pgm_sk_buff_t* const skb, - * pgm_error_t** error - * ) - */ - -START_TEST (test_parse_udp_encap_pass_001) -{ - pgm_error_t* err = NULL; - struct pgm_sk_buff_t* skb = generate_udp_encap_pgm (); - gboolean success = pgm_parse_udp_encap (skb, &err); - if (!success && err) { - g_error ("Parsing UDP encapsulated packet: %s", err->message); - } - fail_unless (TRUE == success, "parse_udp_encap failed"); -} -END_TEST - -START_TEST (test_parse_udp_encap_fail_001) -{ - pgm_error_t* err = NULL; - pgm_parse_udp_encap (NULL, &err); - fail ("reached"); -} -END_TEST - -/* target: - * bool - * pgm_verify_spm ( - * struct pgm_sk_buff_t* const skb - * ) - */ - -START_TEST (test_verify_spm_pass_001) -{ -} -END_TEST - -START_TEST (test_verify_spm_fail_001) -{ - pgm_verify_spm (NULL); - fail ("reached"); -} -END_TEST - -/* target: - * bool - * pgm_verify_spmr ( - * struct pgm_sk_buff_t* const skb - * ) - */ - -START_TEST (test_verify_spmr_pass_001) -{ -} -END_TEST - -START_TEST (test_verify_spmr_fail_001) -{ - pgm_verify_spmr (NULL); - fail ("reached"); -} -END_TEST - -/* target: - * bool - * pgm_verify_nak ( - * struct pgm_sk_buff_t* const skb - * ) - */ - -START_TEST (test_verify_nak_pass_001) -{ -} -END_TEST - -START_TEST (test_verify_nak_fail_001) -{ - pgm_verify_nak (NULL); - fail ("reached"); -} -END_TEST - -/* target: - * bool - * pgm_verify_nnak ( - * struct pgm_sk_buff_t* const skb - * ) - */ - -START_TEST (test_verify_nnak_pass_001) -{ -} -END_TEST - -START_TEST (test_verify_nnak_fail_001) -{ - pgm_verify_nnak (NULL); - fail ("reached"); -} -END_TEST - -/* target: - * bool - * pgm_verify_ncf ( - * struct pgm_sk_buff_t* const skb - * ) - */ - -START_TEST (test_verify_ncf_pass_001) -{ -} -END_TEST - -START_TEST (test_verify_ncf_fail_001) -{ - pgm_verify_ncf (NULL); - fail ("reached"); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_parse_raw = tcase_create ("parse-raw"); - suite_add_tcase (s, tc_parse_raw); - tcase_add_test (tc_parse_raw, test_parse_raw_pass_001); - tcase_add_test_raise_signal (tc_parse_raw, test_parse_raw_fail_001, SIGABRT); - - TCase* tc_parse_udp_encap = tcase_create ("parse-udp-encap"); - suite_add_tcase (s, tc_parse_udp_encap); - tcase_add_test (tc_parse_udp_encap, test_parse_udp_encap_pass_001); - tcase_add_test_raise_signal (tc_parse_udp_encap, test_parse_udp_encap_fail_001, SIGABRT); - - TCase* tc_verify_spm = tcase_create ("verify-spm"); - suite_add_tcase (s, tc_verify_spm); - tcase_add_test (tc_verify_spm, test_verify_spm_pass_001); - tcase_add_test_raise_signal (tc_verify_spm, test_verify_spm_fail_001, SIGABRT); - - TCase* tc_verify_spmr = tcase_create ("verify-spmr"); - suite_add_tcase (s, tc_verify_spmr); - tcase_add_test (tc_verify_spmr, test_verify_spmr_pass_001); - tcase_add_test_raise_signal (tc_verify_spmr, test_verify_spmr_fail_001, SIGABRT); - - TCase* tc_verify_nak = tcase_create ("verify-nak"); - suite_add_tcase (s, tc_verify_nak); - tcase_add_test (tc_verify_nak, test_verify_nak_pass_001); - tcase_add_test_raise_signal (tc_verify_nak, test_verify_nak_fail_001, SIGABRT); - - TCase* tc_verify_nnak = tcase_create ("verify-nnak"); - suite_add_tcase (s, tc_verify_nnak); - tcase_add_test (tc_verify_nnak, test_verify_nnak_pass_001); - tcase_add_test_raise_signal (tc_verify_nnak, test_verify_nnak_fail_001, SIGABRT); - - TCase* tc_verify_ncf = tcase_create ("verify-ncf"); - suite_add_tcase (s, tc_verify_ncf); - tcase_add_test (tc_verify_ncf, test_verify_ncf_pass_001); - tcase_add_test_raise_signal (tc_verify_ncf, test_verify_ncf_fail_001, SIGABRT); - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/packet_test.c b/3rdparty/openpgm-svn-r1085/pgm/packet_test.c deleted file mode 100644 index 6ac469f..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/packet_test.c +++ /dev/null @@ -1,1158 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * PGM packet formats, RFC 3208. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define __STDC_FORMAT_MACROS -#include -#include -#include -#ifndef _WIN32 -# include -# include -# include -# include -#endif -#include -#include -#include - - -//#define PACKET_DEBUG - - -static bool pgm_print_spm (const struct pgm_header* const, const void*, const size_t); -static bool pgm_print_poll (const struct pgm_header* const, const void*, const size_t); -static bool pgm_print_polr (const struct pgm_header* const, const void*, const size_t); -static bool pgm_print_odata (const struct pgm_header* const, const void*, const size_t); -static bool pgm_print_rdata (const struct pgm_header* const, const void*, const size_t); -static bool pgm_print_nak (const struct pgm_header* const, const void*, const size_t); -static bool pgm_print_nnak (const struct pgm_header* const, const void*, const size_t); -static bool pgm_print_ncf (const struct pgm_header* const, const void*, const size_t); -static bool pgm_print_spmr (const struct pgm_header* const, const void*, const size_t); -static bool pgm_print_ack (const struct pgm_header* const, const void*, const size_t); -static ssize_t pgm_print_options (const void*, size_t); - -bool -pgm_print_packet ( - const void* data, - size_t len - ) -{ -/* pre-conditions */ - pgm_assert (NULL != data); - pgm_assert (len > 0); - -/* minimum size should be IP header plus PGM header */ - if (len < (sizeof(struct pgm_ip) + sizeof(struct pgm_header))) - { - printf ("Packet size too small: %zu bytes, expecting at least %zu bytes.\n", - len, sizeof(struct pgm_ip) + sizeof(struct pgm_header)); - return FALSE; - } - -/* decode IP header */ - const struct pgm_ip* ip = (const struct pgm_ip*)data; - if (ip->ip_v != 4) /* IP version, 4 or 6 */ - { - puts ("not IP4 packet :/"); /* v6 not currently handled */ - return FALSE; - } - printf ("IP "); - - const size_t ip_header_length = ip->ip_hl * 4; /* IP header length in 32bit octets */ - if (ip_header_length < sizeof(struct pgm_ip)) - { - puts ("bad IP header length :("); - return FALSE; - } - - size_t packet_length = ntohs(ip->ip_len); /* total packet length */ - -/* ip_len can equal packet_length - ip_header_length in FreeBSD/NetBSD - * Stevens/Fenner/Rudolph, Unix Network Programming Vol.1, p.739 - * - * RFC3828 allows partial packets such that len < packet_length with UDP lite - */ - if (len == packet_length + ip_header_length) { - packet_length += ip_header_length; - } - - if (len < packet_length) { /* redundant: often handled in kernel */ - puts ("truncated IP packet"); - return FALSE; - } - -/* TCP Segmentation Offload (TSO) might have zero length here */ - if (packet_length < ip_header_length) { - puts ("bad length :("); - return FALSE; - } - - const uint16_t offset = ntohs(ip->ip_off); - -/* 3 bits routing priority, 4 bits type of service: delay, throughput, reliability, cost */ - printf ("(tos 0x%x", (int)ip->ip_tos); - switch (ip->ip_tos & 0x3) - { - case 1: printf (",ECT(1)"); break; - case 2: printf (",ECT(0)"); break; - case 3: printf (",CE"); break; - default: break; - } - -/* time to live */ - if (ip->ip_ttl >= 1) printf (", ttl %u", ip->ip_ttl); - -/* fragmentation */ -#define IP_RDF 0x8000 -#define IP_DF 0x4000 -#define IP_MF 0x2000 -#define IP_OFFMASK 0x1fff - - printf (", id %u, offset %u, flags [%s%s]", - ntohs(ip->ip_id), - (offset & 0x1fff) * 8, - ((offset & IP_DF) ? "DF" : ""), - ((offset & IP_MF) ? "+" : "")); - printf (", length %zu", packet_length); - -/* IP options */ - if ((ip_header_length - sizeof(struct pgm_ip)) > 0) { - printf (", options ("); - pgm_ipopt_print((const void*)(ip + 1), ip_header_length - sizeof(struct pgm_ip)); - printf (" )"); - } - -/* packets that fail checksum will generally not be passed upstream except with rfc3828 - */ - const uint16_t ip_sum = pgm_inet_checksum(data, packet_length, 0); - if (ip_sum != 0) { - const uint16_t encoded_ip_sum = ntohs(ip->ip_sum); - printf (", bad cksum! %i", encoded_ip_sum); - } - - printf (") "); - -/* fragmentation offset, bit 0: 0, bit 1: do-not-fragment, bit 2: more-fragments */ - if ((offset & 0x1fff) != 0) { - puts ("fragmented packet :/"); - return FALSE; - } - -/* PGM payload, header looks as follows: - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Source Port | Destination Port | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Type | Options | Checksum | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Global Source ID ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | ... Global Source ID | TSDU Length | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Type specific data ... - * +-+-+-+-+-+-+-+-+-+- ... - */ - const struct pgm_header* pgm_header = (const struct pgm_header*)((const char*)data + ip_header_length); - const size_t pgm_length = packet_length - ip_header_length; - - if (pgm_length < sizeof(pgm_header)) { - puts ("bad packet size :("); - return FALSE; - } - - printf ("%s.%s > ", - pgm_gethostbyaddr((const struct in_addr*)&ip->ip_src), pgm_udpport_string(pgm_header->pgm_sport)); - printf ("%s.%s: PGM\n", - pgm_gethostbyaddr((const struct in_addr*)&ip->ip_dst), pgm_udpport_string(pgm_header->pgm_dport)); - - printf ("type: %s [%i] (version=%i, reserved=%i)\n" - "options: extensions=%s, network-significant=%s, parity packet=%s (variable size=%s)\n" - "global source id: %i.%i.%i.%i.%i.%i\n" - "tsdu length: %i\n", - - /* packet type */ /* packet version */ /* reserved = 0x0 */ - pgm_type_string(pgm_header->pgm_type & 0xf), - (pgm_header->pgm_type & 0xf), ((pgm_header->pgm_type & 0xc0) >> 6), ((pgm_header->pgm_type & 0x30) >> 4), - -/* bit 0 set => one or more option extensions are present */ - ((pgm_header->pgm_options & (0x1 << 7)) ? "true" : "false"), -/* bit 1 set => one or more options are network-significant */ - ((pgm_header->pgm_options & (0x1 << 6)) ? "true" : "false"), -/* bit 7 set => parity packet (OPT_PARITY) */ - ((pgm_header->pgm_options & (0x1 << 0)) ? "true" : "false"), -/* bit 6 set => parity packet for variable packet sizes (OPT_VAR_PKTLEN) */ - ((pgm_header->pgm_options & (0x1 << 1)) ? "true" : "false"), - - pgm_header->pgm_gsi[0], pgm_header->pgm_gsi[1], pgm_header->pgm_gsi[2], pgm_header->pgm_gsi[3], pgm_header->pgm_gsi[4], pgm_header->pgm_gsi[5], - ntohs(pgm_header->pgm_tsdu_length)); - - if (pgm_header->pgm_checksum) - { -#if 0 - const uint16_t encoded_pgm_sum = pgm_header->pgm_checksum; -/* requires modification of data buffer */ - pgm_header->pgm_checksum = 0; - const uint16_t pgm_sum = pgm_csum_fold (pgm_csum_partial((const char*)pgm_header, pgm_length, 0)); - if (pgm_sum != encoded_pgm_sum) { - printf ("PGM checksum incorrect, packet %x calculated %x :(\n", encoded_pgm_sum, pgm_sum); - return FALSE; - } -#endif - } else { - puts ("No PGM checksum :O"); - } - -/* now decode PGM packet types */ - const void* pgm_data = pgm_header + 1; - const size_t pgm_data_length = pgm_length - sizeof(pgm_header); /* can equal zero for SPMR's */ - - bool err = FALSE; - switch (pgm_header->pgm_type) { - case PGM_SPM: err = pgm_print_spm (pgm_header, pgm_data, pgm_data_length); break; - case PGM_POLL: err = pgm_print_poll (pgm_header, pgm_data, pgm_data_length); break; - case PGM_POLR: err = pgm_print_polr (pgm_header, pgm_data, pgm_data_length); break; - case PGM_ODATA: err = pgm_print_odata (pgm_header, pgm_data, pgm_data_length); break; - case PGM_RDATA: err = pgm_print_rdata (pgm_header, pgm_data, pgm_data_length); break; - case PGM_NAK: err = pgm_print_nak (pgm_header, pgm_data, pgm_data_length); break; - case PGM_NNAK: err = pgm_print_nnak (pgm_header, pgm_data, pgm_data_length); break; - case PGM_NCF: err = pgm_print_ncf (pgm_header, pgm_data, pgm_data_length); break; - case PGM_SPMR: err = pgm_print_spmr (pgm_header, pgm_data, pgm_data_length); break; - case PGM_ACK: err = pgm_print_ack (pgm_header, pgm_data, pgm_data_length); break; - default: puts ("unknown packet type :("); break; - } - - return err; -} - -/* 8.1. Source Path Messages (SPM) - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | SPM's Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Trailing Edge Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Leading Edge Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | NLA AFI | Reserved | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Path NLA ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ - * | Option Extensions when present ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * - * NLA = Network Layer Address - * NLA AFI = NLA Address Family Indicator: rfc 1700 (ADDRESS FAMILY NUMBERS) - * => Path NLA = IP address of last network element - */ - -#define PGM_MIN_SPM_SIZE ( sizeof(struct pgm_spm) ) - -static -bool -pgm_print_spm ( - const struct pgm_header* const header, - const void* data, - const size_t len - ) -{ -/* pre-conditions */ - pgm_assert (NULL != header); - pgm_assert (NULL != data); - pgm_assert (len > 0); - - printf ("SPM: "); - - if (len < PGM_MIN_SPM_SIZE) { - puts ("packet truncated :("); - return FALSE; - } - - const struct pgm_spm * spm = (const struct pgm_spm *)data; - const struct pgm_spm6* spm6 = (const struct pgm_spm6*)data; - const uint16_t spm_nla_afi = ntohs (spm->spm_nla_afi); - - printf ("sqn %" PRIu32 " trail %" PRIu32 "lu lead %" PRIu32 "lu nla-afi %u ", - ntohl(spm->spm_sqn), - ntohl(spm->spm_trail), - ntohl(spm->spm_lead), - spm_nla_afi); /* address family indicator */ - - char s[INET6_ADDRSTRLEN]; - const void* pgm_opt; - size_t pgm_opt_len; - switch (spm_nla_afi) { - case AFI_IP: - pgm_inet_ntop (AF_INET, &spm->spm_nla, s, sizeof(s)); - pgm_opt = (const uint8_t*)data + sizeof(struct pgm_spm); - pgm_opt_len = len - sizeof(struct pgm_spm); - break; - - case AFI_IP6: - if (len < sizeof (struct pgm_spm6)) { - puts ("packet truncated :("); - return FALSE; - } - - pgm_inet_ntop (AF_INET6, &spm6->spm6_nla, s, sizeof(s)); - pgm_opt = (const uint8_t*)data + sizeof(struct pgm_spm6); - pgm_opt_len = len - sizeof(struct pgm_spm6); - break; - - default: - printf ("unsupported afi"); - return FALSE; - } - - printf ("%s", s); - -/* option extensions */ - if (header->pgm_options & PGM_OPT_PRESENT && - pgm_print_options (pgm_opt, pgm_opt_len) < 0 ) - { - return FALSE; - } - - printf ("\n"); - return TRUE; -} - -/* 14.7.1. Poll Request - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | POLL's Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | POLL's Round | POLL's Sub-type | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | NLA AFI | Reserved | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Path NLA ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ - * | POLL's Back-off Interval | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Random String | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Matching Bit-Mask | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Option Extensions when present ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * - * Sent to ODATA multicast group with IP Router Alert option. - */ - -#define PGM_MIN_POLL_SIZE ( sizeof(struct pgm_poll) ) - -static -bool -pgm_print_poll ( - const struct pgm_header* const header, - const void* data, - const size_t len - ) -{ -/* pre-conditions */ - pgm_assert (NULL != header); - pgm_assert (NULL != data); - pgm_assert (len > 0); - - printf ("POLL: "); - - if (len < PGM_MIN_POLL_SIZE) { - puts ("packet truncated :("); - return FALSE; - } - - const struct pgm_poll * poll4 = (const struct pgm_poll *)data; - const struct pgm_poll6* poll6 = (const struct pgm_poll6*)data; - const uint16_t poll_nla_afi = ntohs (poll4->poll_nla_afi); - - printf ("sqn %" PRIu32 " round %u sub-type %u nla-afi %u ", - ntohl(poll4->poll_sqn), - ntohs(poll4->poll_round), - ntohs(poll4->poll_s_type), - poll_nla_afi); /* address family indicator */ - - char s[INET6_ADDRSTRLEN]; - const void* pgm_opt; - size_t pgm_opt_len; - switch (poll_nla_afi) { - case AFI_IP: - pgm_inet_ntop (AF_INET, &poll4->poll_nla, s, sizeof(s)); - pgm_opt = (const uint8_t*)data + sizeof(struct pgm_poll); - pgm_opt_len = len - sizeof(struct pgm_poll); - printf ("%s", s); - -/* back-off interval in microseconds */ - printf (" bo_ivl %u", poll4->poll_bo_ivl); - -/* random string */ - printf (" rand [%c%c%c%c]", - isprint (poll4->poll_rand[0]) ? poll4->poll_rand[0] : '.', - isprint (poll4->poll_rand[1]) ? poll4->poll_rand[1] : '.', - isprint (poll4->poll_rand[2]) ? poll4->poll_rand[2] : '.', - isprint (poll4->poll_rand[3]) ? poll4->poll_rand[3] : '.' ); - -/* matching bit-mask */ - printf (" mask 0x%x", poll4->poll_mask); - break; - - case AFI_IP6: - if (len < sizeof (struct pgm_poll6)) { - puts ("packet truncated :("); - return FALSE; - } - - pgm_inet_ntop (AF_INET6, &poll6->poll6_nla, s, sizeof (s)); - pgm_opt = (const uint8_t*)data + sizeof(struct pgm_poll6); - pgm_opt_len = len - sizeof(struct pgm_poll6); - printf ("%s", s); - -/* back-off interval in microseconds */ - printf (" bo_ivl %u", poll6->poll6_bo_ivl); - -/* random string */ - printf (" rand [%c%c%c%c]", - isprint (poll6->poll6_rand[0]) ? poll6->poll6_rand[0] : '.', - isprint (poll6->poll6_rand[1]) ? poll6->poll6_rand[1] : '.', - isprint (poll6->poll6_rand[2]) ? poll6->poll6_rand[2] : '.', - isprint (poll6->poll6_rand[3]) ? poll6->poll6_rand[3] : '.' ); - -/* matching bit-mask */ - printf (" mask 0x%x", poll6->poll6_mask); - break; - - default: - printf ("unsupported afi"); - return FALSE; - } - - -/* option extensions */ - if (header->pgm_options & PGM_OPT_PRESENT && - pgm_print_options (pgm_opt, pgm_opt_len) < 0 ) - { - return FALSE; - } - - printf ("\n"); - return TRUE; -} - -/* 14.7.2. Poll Response - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | POLR's Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | POLR's Round | reserved | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Option Extensions when present ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - -static -bool -pgm_print_polr ( - const struct pgm_header* const header, - const void* data, - const size_t len - ) -{ -/* pre-conditions */ - pgm_assert (NULL != header); - pgm_assert (NULL != data); - pgm_assert (len > 0); - - printf ("POLR: "); - - if (len < sizeof(struct pgm_polr)) { - puts ("packet truncated :("); - return FALSE; - } - - const struct pgm_polr* polr = (const struct pgm_polr*)data; - - printf("sqn %" PRIu32 " round %u", - ntohl(polr->polr_sqn), - ntohs(polr->polr_round)); - - const void* pgm_opt = (const uint8_t*)data + sizeof(struct pgm_polr); - size_t pgm_opt_len = len - sizeof(struct pgm_polr); - -/* option extensions */ - if (header->pgm_options & PGM_OPT_PRESENT && - pgm_print_options (pgm_opt, pgm_opt_len) < 0 ) - { - return FALSE; - } - - printf ("\n"); - return TRUE; -} - -/* 8.2. Data Packet - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Data Packet Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Trailing Edge Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Option Extensions when present ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Data ... - * +-+-+- ... - */ - -static -bool -pgm_print_odata ( - const struct pgm_header* const header, - const void* data, - const size_t len - ) -{ -/* pre-conditions */ - pgm_assert (NULL != header); - pgm_assert (NULL != data); - pgm_assert (len > 0); - - printf ("ODATA: "); - - if (len < sizeof(struct pgm_data)) { - puts ("packet truncated :("); - return FALSE; - } - - const struct pgm_data* odata = (const struct pgm_data*)data; - - printf ("sqn %" PRIu32 " trail %" PRIu32 " [", - ntohl(odata->data_sqn), - ntohl(odata->data_trail)); - -/* option extensions */ - const void* pgm_opt = (const uint8_t*)data + sizeof(struct pgm_data); - size_t pgm_opt_len = len - sizeof(struct pgm_data); - const char* payload = pgm_opt; - - if (header->pgm_options & PGM_OPT_PRESENT) { - const ssize_t opt_len = pgm_print_options (pgm_opt, pgm_opt_len); - if (opt_len < 0) - return FALSE; - payload += opt_len; - } - -/* data */ - const char* end = payload + ntohs (header->pgm_tsdu_length); - while (payload < end) { - if (isprint (*payload)) - putchar (*payload); - else - putchar ('.'); - payload++; - } - - printf ("]\n"); - return TRUE; -} - -/* 8.2. Repair Data - */ - -static -bool -pgm_print_rdata ( - const struct pgm_header* const header, - const void* data, - const size_t len - ) -{ -/* pre-conditions */ - pgm_assert (NULL != header); - pgm_assert (NULL != data); - pgm_assert (len > 0); - - printf ("RDATA: "); - - if (len < sizeof(struct pgm_data)) { - puts ("packet truncated :("); - return FALSE; - } - - const struct pgm_data* rdata = (const struct pgm_data*)data; - - printf ("sqn %" PRIu32 " trail %" PRIu32 " [", - ntohl (rdata->data_sqn), - ntohl (rdata->data_trail)); - -/* option extensions */ - const void* pgm_opt = (const uint8_t*)data + sizeof(struct pgm_data); - size_t pgm_opt_len = len - sizeof(struct pgm_data); - const char* payload = pgm_opt; - - if (header->pgm_options & PGM_OPT_PRESENT) { - const ssize_t opt_len = pgm_print_options (pgm_opt, pgm_opt_len); - if (opt_len < 0) - return FALSE; - payload += opt_len; - } - -/* data */ - const char* end = payload + ntohs (header->pgm_tsdu_length); - while (payload < end) { - if (isprint (*payload)) - putchar (*payload); - else - putchar ('.'); - payload++; - } - - printf ("]\n"); - return TRUE; -} - -/* 8.3. NAK - * - * Technically the AFI of the source and multicast group can be different - * but that would be very wibbly wobbly. One example is using a local DLR - * with a IPv4 address to reduce NAK cost for recovery on wide IPv6 - * distribution. - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Requested Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | NLA AFI | Reserved | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Source NLA ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ - * | NLA AFI | Reserved | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Multicast Group NLA ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ - * | Option Extensions when present ... - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... - */ - -#define PGM_MIN_NAK_SIZE ( sizeof(struct pgm_nak) ) - -static -bool -pgm_print_nak ( - const struct pgm_header* const header, - const void* data, - const size_t len - ) -{ -/* pre-conditions */ - pgm_assert (NULL != header); - pgm_assert (NULL != data); - pgm_assert (len > 0); - - printf ("NAK: "); - - if (len < PGM_MIN_NAK_SIZE) { - puts ("packet truncated :("); - return FALSE; - } - - const struct pgm_nak * nak = (const struct pgm_nak *)data; - const struct pgm_nak6* nak6 = (const struct pgm_nak6*)data; - const uint16_t nak_src_nla_afi = ntohs (nak->nak_src_nla_afi); - - printf ("sqn %" PRIu32 " src ", - ntohl(nak->nak_sqn)); - - char s[INET6_ADDRSTRLEN]; - const void* pgm_opt; - size_t pgm_opt_len; - -/* source nla */ - switch (nak_src_nla_afi) { - case AFI_IP: { - const uint16_t nak_grp_nla_afi = ntohs (nak->nak_grp_nla_afi); - if (nak_src_nla_afi != nak_grp_nla_afi) { - puts ("different source & group afi very wibbly wobbly :("); - return FALSE; - } - - pgm_inet_ntop (AF_INET, &nak->nak_src_nla, s, sizeof(s)); - pgm_opt = (const uint8_t*)data + sizeof(struct pgm_nak); - pgm_opt_len = len - sizeof(struct pgm_nak); - printf ("%s grp ", s); - - pgm_inet_ntop (AF_INET, &nak->nak_grp_nla, s, sizeof(s)); - printf ("%s", s); - break; - } - - case AFI_IP6: { - if (len < sizeof (struct pgm_nak6)) { - puts ("packet truncated :("); - return FALSE; - } - - const uint16_t nak_grp_nla_afi = ntohs (nak6->nak6_grp_nla_afi); - if (nak_src_nla_afi != nak_grp_nla_afi) { - puts ("different source & group afi very wibbly wobbly :("); - return FALSE; - } - - pgm_inet_ntop (AF_INET6, &nak6->nak6_src_nla, s, sizeof(s)); - pgm_opt = (const uint8_t*)data + sizeof(struct pgm_nak6); - pgm_opt_len = len - sizeof(struct pgm_nak6); - printf ("%s grp ", s); - - pgm_inet_ntop (AF_INET6, &nak6->nak6_grp_nla, s, sizeof(s)); - printf ("%s", s); - break; - } - - default: - puts ("unsupported afi"); - return FALSE; - } - - -/* option extensions */ - if (header->pgm_options & PGM_OPT_PRESENT && - pgm_print_options (pgm_opt, pgm_opt_len) < 0 ) - { - return FALSE; - } - - printf ("\n"); - return TRUE; -} - -/* 8.3. N-NAK - */ - -static -bool -pgm_print_nnak ( - PGM_GNUC_UNUSED const struct pgm_header* const header, - PGM_GNUC_UNUSED const void* data, - const size_t len - ) -{ -/* pre-conditions */ - pgm_assert (NULL != header); - pgm_assert (NULL != data); - pgm_assert (len > 0); - - printf ("N-NAK: "); - - if (len < sizeof(struct pgm_nak)) { - puts ("packet truncated :("); - return FALSE; - } - -// struct pgm_nak* nnak = (struct pgm_nak*)data; - - return TRUE; -} - -/* 8.3. NCF - */ - -bool -pgm_print_ncf ( - PGM_GNUC_UNUSED const struct pgm_header* const header, - PGM_GNUC_UNUSED const void* data, - const size_t len - ) -{ -/* pre-conditions */ - pgm_assert (NULL != header); - pgm_assert (NULL != data); - pgm_assert (len > 0); - - printf ("NCF: "); - - if (len < sizeof(struct pgm_nak)) { - puts ("packet truncated :("); - return FALSE; - } - -// struct pgm_nak* ncf = (struct pgm_nak*)data; - - return TRUE; -} - -/* 13.6. SPM Request - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Option Extensions when present ... - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... - */ - -static -bool -pgm_print_spmr ( - const struct pgm_header* const header, - const void* data, - const size_t len - ) -{ -/* pre-conditions */ - pgm_assert (NULL != header); - pgm_assert (NULL != data); - pgm_assert (len > 0); - - printf ("SPMR: "); - -/* option extensions */ - if (header->pgm_options & PGM_OPT_PRESENT && - pgm_print_options (data, len) < 0 ) - { - return FALSE; - } - - printf ("\n"); - return TRUE; -} - -/* PGMCC: ACK - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | RX_MAX | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Received Packet Bitmap | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Option Extensions when present ... - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... - */ - -static -bool -pgm_print_ack ( - const struct pgm_header* const header, - const void* data, - const size_t len - ) -{ -/* pre-conditions */ - pgm_assert (NULL != header); - pgm_assert (NULL != data); - pgm_assert (len > 0); - - printf ("ACK: "); - - const struct pgm_ack* ack = (const struct pgm_ack*)data; - char bitmap[33]; - - for (unsigned i = 31; i; i--) - bitmap[i] = (ack->ack_bitmap & (1 << i)) ? '1' : '0'; - bitmap[32] = '\0'; - - printf ("rx_max %" PRIu32 " bitmap [%s] ", - ntohl(ack->ack_rx_max), bitmap); - -/* option extensions */ - if (header->pgm_options & PGM_OPT_PRESENT && - pgm_print_options (data, len) < 0 ) - { - return FALSE; - } - - printf ("\n"); - return TRUE; -} - - -/* Parse PGM options fields, alters contents of packet. - * - * returns -1 on failure, or total length in octets of the option fields - */ - -static -ssize_t -pgm_print_options ( - const void* data, - size_t len - ) -{ -/* pre-conditions */ - pgm_assert (NULL != data); - pgm_assert (len > 0); - - printf (" OPTIONS:"); - if (len < sizeof(struct pgm_opt_length)) { - puts (" packet truncated :("); - return -1; - } - - const struct pgm_opt_length* opt_len = (const struct pgm_opt_length*)data; - if (opt_len->opt_length != sizeof(struct pgm_opt_length)) { - printf (" bad opt_length length %u\n", (unsigned)opt_len->opt_length); - return -1; - } - - uint16_t opt_total_length = ntohs (opt_len->opt_total_length); - printf (" total len %u ", opt_total_length); - if (opt_total_length < (sizeof(struct pgm_opt_length) + sizeof(struct pgm_opt_header)) || - opt_total_length > len) - { - puts ("bad total length"); - return -1; - } - -/* total length includes opt_length option */ - opt_total_length -= sizeof(struct pgm_opt_length); - const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)(opt_len + 1); - -/* iterate through options (max 16) */ - unsigned count = 16; - while (opt_total_length && count) - { - if (opt_total_length < sizeof(struct pgm_opt_header) || - opt_header->opt_length > opt_total_length) - { - puts ("short on option data :o"); - return -1; - } - - if (opt_header->opt_type & PGM_OPT_END) { - printf ("OPT_END+"); - } - - switch (opt_header->opt_type & PGM_OPT_MASK) { - case PGM_OPT_FRAGMENT: - printf ("OPT_FRAGMENT "); - break; - - case PGM_OPT_NAK_LIST: - printf ("OPT_NAK_LIST "); - break; - - case PGM_OPT_JOIN: - printf ("OPT_JOIN "); - break; - - case PGM_OPT_REDIRECT: - printf ("OPT_REDIRECT "); - break; - - case PGM_OPT_SYN: - printf ("OPT_SYN "); - break; - - case PGM_OPT_FIN: - printf ("OPT_FIN "); - break; - - case PGM_OPT_RST: - printf ("OPT_RST "); - break; - - case PGM_OPT_PARITY_PRM: - printf ("OPT_PARITY_PRM "); - break; - - case PGM_OPT_CURR_TGSIZE: - printf ("OPT_CURR_TGSIZE "); - break; - - case PGM_OPT_CR: - printf ("OPT_CR "); - break; - - case PGM_OPT_CRQST: - printf ("OPT_CRQST "); - break; - - case PGM_OPT_PGMCC_DATA: - printf ("OPT_PGMCC_DATA "); - break; - - case PGM_OPT_PGMCC_FEEDBACK: - printf ("OPT_PGMCC_FEEDBACK "); - break; - - case PGM_OPT_NAK_BO_IVL: - printf ("OPT_NAK_BO_IVL "); - break; - - case PGM_OPT_NAK_BO_RNG: - printf ("OPT_NAK_BO_RNG "); - break; - - case PGM_OPT_NBR_UNREACH: - printf ("OPT_NBR_UNREACH "); - break; - - case PGM_OPT_PATH_NLA: - printf ("OPT_PATH_NLA "); - break; - - default: - printf ("OPT-%u{%u} ", opt_header->opt_type & PGM_OPT_MASK, opt_header->opt_length); - break; - } - - opt_total_length -= opt_header->opt_length; - opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); - - count--; - } - - if (!count) { - puts ("too many options found"); - return -1; - } - - return ((const uint8_t*)opt_header - (const uint8_t*)data); -} - -const char* -pgm_type_string ( - uint8_t type - ) -{ - const char* c; - - switch (type) { - case PGM_SPM: c = "PGM_SPM"; break; - case PGM_POLL: c = "PGM_POLL"; break; - case PGM_POLR: c = "PGM_POLR"; break; - case PGM_ODATA: c = "PGM_ODATA"; break; - case PGM_RDATA: c = "PGM_RDATA"; break; - case PGM_NAK: c = "PGM_NAK"; break; - case PGM_NNAK: c = "PGM_NNAK"; break; - case PGM_NCF: c = "PGM_NCF"; break; - case PGM_SPMR: c = "PGM_SPMR"; break; - case PGM_ACK: c = "PGM_ACK"; break; - default: c = "(unknown)"; break; - } - - return c; -} - -const char* -pgm_udpport_string ( - uint16_t port - ) -{ - static pgm_hashtable_t *services = NULL; - - if (!services) { - services = pgm_hashtable_new (pgm_int_hash, pgm_int_equal); - } - - const int hash_key = port; - void* service_string = pgm_hashtable_lookup (services, &hash_key); - if (service_string != NULL) { - return service_string; - } - - struct servent* se = getservbyport (port, "udp"); - if (se == NULL) { - char buf[sizeof("00000")]; - snprintf(buf, sizeof(buf), "%u", ntohs(port)); - service_string = pgm_strdup(buf); - } else { - service_string = pgm_strdup(se->s_name); - } - pgm_hashtable_insert (services, &hash_key, service_string); - return service_string; -} - -const char* -pgm_gethostbyaddr ( - const struct in_addr* ap - ) -{ - static pgm_hashtable_t *hosts = NULL; - - if (!hosts) { - hosts = pgm_hashtable_new (pgm_str_hash, pgm_int_equal); - } - - const int hash_key = (int)ap->s_addr; - void* host_string = pgm_hashtable_lookup (hosts, &hash_key); - if (host_string != NULL) { - return host_string; - } - - struct hostent* he = gethostbyaddr((const char*)ap, sizeof(struct in_addr), AF_INET); - if (he == NULL) { - struct in_addr in; - memcpy (&in, ap, sizeof(in)); - host_string = pgm_strdup(inet_ntoa(in)); - } else { - host_string = pgm_strdup(he->h_name); - } - pgm_hashtable_insert (hosts, &hash_key, host_string); - return host_string; -} - -void -pgm_ipopt_print ( - const void* ipopt, - size_t length - ) -{ -/* pre-conditions */ - pgm_assert (NULL != ipopt); - - const char* op = ipopt; - - while (length) - { - char len = (*op == PGM_IPOPT_NOP || *op == PGM_IPOPT_EOL) ? 1 : op[1]; - switch (*op) { - case PGM_IPOPT_EOL: printf(" eol"); break; - case PGM_IPOPT_NOP: printf(" nop"); break; - case PGM_IPOPT_RR: printf(" rr"); break; /* 1 route */ - case PGM_IPOPT_TS: printf(" ts"); break; /* 1 TS */ -#if 0 - case PGM_IPOPT_SECURITY: printf(" sec-level"); break; - case PGM_IPOPT_LSRR: printf(" lsrr"); break; /* 1 route */ - case PGM_IPOPT_SATID: printf(" satid"); break; - case PGM_IPOPT_SSRR: printf(" ssrr"); break; /* 1 route */ -#endif - default: printf(" %x{%d}", (int)*op, (int)len); break; - } - - if (!len) { - puts ("invalid IP opt length"); - return; - } - - op += len; - length -= len; - } -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/packet_test_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/packet_test_unittest.c deleted file mode 100644 index 7edefbb..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/packet_test_unittest.c +++ /dev/null @@ -1,169 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for PGM packet handling. - * - * Copyright (c) 2009-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include - - -/* mock state */ - -#define PACKET_DEBUG -#include "packet_test.c" - - -static -struct pgm_sk_buff_t* -generate_raw_pgm (void) -{ - const char source[] = "i am not a string"; - const guint source_len = sizeof(source); - struct pgm_sk_buff_t* skb; - GError* err = NULL; - - skb = pgm_alloc_skb (1500); - skb->sock = (pgm_sock_t*)0x1; - skb->tstamp = 0x1; - skb->data = skb->head; - skb->len = sizeof(struct pgm_ip) + sizeof(struct pgm_header) + sizeof(struct pgm_data) + source_len; - skb->tail = (guint8*)skb->data + skb->len; - -/* add IP header */ - struct pgm_ip* iphdr = skb->data; - iphdr->ip_hl = sizeof(struct pgm_ip) / 4; - iphdr->ip_v = 4; - iphdr->ip_tos = 0; - iphdr->ip_len = g_htons (skb->len); - iphdr->ip_id = 0; - iphdr->ip_off = 0; - iphdr->ip_ttl = 16; - iphdr->ip_p = IPPROTO_PGM; - iphdr->ip_sum = 0; - iphdr->ip_src.s_addr = inet_addr ("127.0.0.1"); - iphdr->ip_dst.s_addr = inet_addr ("127.0.0.2"); - -/* add PGM header */ - struct pgm_header* pgmhdr = (gpointer)(iphdr + 1); - pgmhdr->pgm_sport = g_htons ((guint16)1000); - pgmhdr->pgm_dport = g_htons ((guint16)7500); - pgmhdr->pgm_type = PGM_ODATA; - pgmhdr->pgm_options = 0; - pgmhdr->pgm_gsi[0] = 1; - pgmhdr->pgm_gsi[1] = 2; - pgmhdr->pgm_gsi[2] = 3; - pgmhdr->pgm_gsi[3] = 4; - pgmhdr->pgm_gsi[4] = 5; - pgmhdr->pgm_gsi[5] = 6; - pgmhdr->pgm_tsdu_length = g_htons (source_len); - -/* add ODATA header */ - struct pgm_data* datahdr = (gpointer)(pgmhdr + 1); - datahdr->data_sqn = g_htonl ((guint32)0); - datahdr->data_trail = g_htonl ((guint32)-1); - -/* add payload */ - gpointer data = (gpointer)(datahdr + 1); - memcpy (data, source, source_len); - -/* finally PGM checksum */ - pgmhdr->pgm_checksum = 0; - pgmhdr->pgm_checksum = pgm_csum_fold (pgm_csum_partial (pgmhdr, sizeof(struct pgm_header) + sizeof(struct pgm_data) + source_len, 0)); - -/* and IP checksum */ - iphdr->ip_sum = pgm_inet_checksum (skb->head, skb->len, 0); - - return skb; -} - - -/* mock functions for external references */ - -size_t -pgm_pkt_offset ( - const bool can_fragment, - const sa_family_t pgmcc_family /* 0 = disable */ - ) -{ - return 0; -} - - -/* target: - * gboolean - * pgm_print_packet ( - * gpointer data, - * gsize len - * ) - */ - -START_TEST (test_print_packet_pass_001) -{ - struct pgm_sk_buff_t* skb = generate_raw_pgm (); - pgm_print_packet (skb->head, skb->len); -} -END_TEST - -START_TEST (test_print_packet_fail_001) -{ - pgm_print_packet (NULL, 0); - fail ("reached"); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_print_packet = tcase_create ("print-packet"); - suite_add_tcase (s, tc_print_packet); - tcase_add_test (tc_print_packet, test_print_packet_pass_001); - tcase_add_test_raise_signal (tc_print_packet, test_print_packet_fail_001, SIGABRT); - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/pgmMIB.c b/3rdparty/openpgm-svn-r1085/pgm/pgmMIB.c deleted file mode 100644 index 1226c50..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/pgmMIB.c +++ /dev/null @@ -1,3212 +0,0 @@ -/* - * Note: this file originally auto-generated by mib2c using - * : mib2c.notify.conf,v 5.3 2004/04/15 12:29:19 dts12 Exp $ - */ - -#include -#include -#include - -#include -#include -#include -#include - -#include "pgm/snmp.h" -#include "impl/pgmMIB.h" -#include "impl/pgmMIB_columns.h" -#include "impl/pgmMIB_enums.h" - - -//#define PGMMIB_DEBUG - - -/* locals */ - -struct pgm_snmp_context_t { - pgm_slist_t* list; - pgm_list_t* node; - int index; /* table index */ - unsigned instance; /* unique number per node */ -}; - -typedef struct pgm_snmp_context_t pgm_snmp_context_t; - -static const oid snmptrap_oid[] = {1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0}; - - -/* functions */ - -static int initialize_table_pgmSourceTable(void); -static Netsnmp_Node_Handler pgmSourceTable_handler; -static Netsnmp_First_Data_Point pgmSourceTable_get_first_data_point; -static Netsnmp_Next_Data_Point pgmSourceTable_get_next_data_point; -static Netsnmp_Free_Loop_Context pgmSourceTable_free_loop_context; - -static int initialize_table_pgmSourceConfigTable(void); -static Netsnmp_Node_Handler pgmSourceConfigTable_handler; -static Netsnmp_First_Data_Point pgmSourceConfigTable_get_first_data_point; -static Netsnmp_Next_Data_Point pgmSourceConfigTable_get_next_data_point; -static Netsnmp_Free_Loop_Context pgmSourceConfigTable_free_loop_context; - -static int initialize_table_pgmSourcePerformanceTable(void); -static Netsnmp_Node_Handler pgmSourcePerformanceTable_handler; -static Netsnmp_First_Data_Point pgmSourcePerformanceTable_get_first_data_point; -static Netsnmp_Next_Data_Point pgmSourcePerformanceTable_get_next_data_point; -static Netsnmp_Free_Loop_Context pgmSourcePerformanceTable_free_loop_context; - -static int initialize_table_pgmReceiverTable(void); -static Netsnmp_Node_Handler pgmReceiverTable_handler; -static Netsnmp_First_Data_Point pgmReceiverTable_get_first_data_point; -static Netsnmp_Next_Data_Point pgmReceiverTable_get_next_data_point; -static Netsnmp_Free_Loop_Context pgmReceiverTable_free_loop_context; - -static int initialize_table_pgmReceiverConfigTable(void); -static Netsnmp_Node_Handler pgmReceiverConfigTable_handler; -static Netsnmp_First_Data_Point pgmReceiverConfigTable_get_first_data_point; -static Netsnmp_Next_Data_Point pgmReceiverConfigTable_get_next_data_point; -static Netsnmp_Free_Loop_Context pgmReceiverConfigTable_free_loop_context; - -static int initialize_table_pgmReceiverPerformanceTable(void); -static Netsnmp_Node_Handler pgmReceiverPerformanceTable_handler; -static Netsnmp_First_Data_Point pgmReceiverPerformanceTable_get_first_data_point; -static Netsnmp_Next_Data_Point pgmReceiverPerformanceTable_get_next_data_point; -static Netsnmp_Free_Loop_Context pgmReceiverPerformanceTable_free_loop_context; - - -bool -pgm_mib_init ( - pgm_error_t** error - ) -{ - if (MIB_REGISTERED_OK != initialize_table_pgmSourceTable()) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SNMP, - PGM_ERROR_FAILED, - _("pgmSourceTable registration: see SNMP log for further details.")); - return FALSE; - } - if (MIB_REGISTERED_OK != initialize_table_pgmSourceConfigTable()) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SNMP, - PGM_ERROR_FAILED, - _("pgmSourceConfigTable registration: see SNMP log for further details.")); - return FALSE; - } - if (MIB_REGISTERED_OK != initialize_table_pgmSourcePerformanceTable()) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SNMP, - PGM_ERROR_FAILED, - _("pgmSourcePerformanceTable registration: see SNMP log for further details.")); - return FALSE; - } - if (MIB_REGISTERED_OK != initialize_table_pgmReceiverTable()) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SNMP, - PGM_ERROR_FAILED, - _("pgmReceiverTable registration: see SNMP log for further details.")); - return FALSE; - } - if (MIB_REGISTERED_OK != initialize_table_pgmReceiverConfigTable()) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SNMP, - PGM_ERROR_FAILED, - _("pgmReceiverConfigTable registration: see SNMP log for further details.")); - return FALSE; - } - if (MIB_REGISTERED_OK != initialize_table_pgmReceiverPerformanceTable()) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SNMP, - PGM_ERROR_FAILED, - _("pgmReceiverPerformanceTable registration: see SNMP log for further details.")); - return FALSE; - } - - return TRUE; -} - -/* - * pgmSourceTable - * - * returns MIB_REGISTERED_OK on success, failures include: - * MIB_REGISTRATION_FAILED - * MIB_DUPLICATE_REGISTRATION - * SNMPERR_GENERR - */ - -static -int -initialize_table_pgmSourceTable (void) -{ - pgm_debug ("initialize_table_pgmSourceTable ()"); - - static const oid pgmSourceTable_oid[] = {1,3,6,1,3,112,1,2,100,2}; - netsnmp_table_registration_info* table_info = NULL; - netsnmp_iterator_info* iinfo = NULL; - netsnmp_handler_registration* reg = NULL; - - reg = netsnmp_create_handler_registration ("pgmSourceTable", pgmSourceTable_handler, - pgmSourceTable_oid, OID_LENGTH( pgmSourceTable_oid ), - HANDLER_CAN_RONLY); - if (!reg) - goto error; - - table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); - if (!table_info) - goto error; - - table_info->min_column = COLUMN_PGMSOURCESOURCEADDRESS; - table_info->max_column = COLUMN_PGMSOURCESOURCEPORTNUMBER; - - netsnmp_table_helper_add_indexes (table_info, - ASN_OCTET_STR, /* index: pgmSourceGlobalId */ - ASN_UNSIGNED, /* index: pgmSourceSourcePort */ - 0); - - iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info ); - if (!iinfo) - goto error; - - iinfo->get_first_data_point = pgmSourceTable_get_first_data_point; - iinfo->get_next_data_point = pgmSourceTable_get_next_data_point; - iinfo->free_loop_context_at_end = pgmSourceTable_free_loop_context; - iinfo->table_reginfo = table_info; - - return netsnmp_register_table_iterator (reg, iinfo); - -error: - if (table_info && table_info->indexes) /* table_data_free_func() is internal */ - snmp_free_var (table_info->indexes); - SNMP_FREE( table_info ); - SNMP_FREE( iinfo ); - netsnmp_handler_registration_free (reg); - - return -1; -} - -/* called for first row of data in SNMP table - * - * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context, - * optionally returns my_data_context. - * - * returns answer or NULL - */ - -static -netsnmp_variable_list* -pgmSourceTable_get_first_data_point( - void** my_loop_context, /* valid through one query of multiple "data points" */ - void** my_data_context, /* answer blob which is passed to handler() */ - netsnmp_variable_list* put_index_data, /* answer */ - netsnmp_iterator_info* mydata /* iinfo on init() */ - ) -{ -/* pre-conditions */ - pgm_assert (NULL != my_loop_context); - pgm_assert (NULL != my_data_context); - pgm_assert (NULL != put_index_data); - pgm_assert (NULL != mydata); - - pgm_debug ("pgmSourceTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", - (const void*)my_loop_context, - (const void*)my_data_context, - (const void*)put_index_data, - (const void*)mydata); - - pgm_rwlock_reader_lock (&pgm_sock_list_lock); - - if (!pgm_sock_list) { - pgm_rwlock_reader_unlock (&pgm_sock_list_lock); - return NULL; - } - -/* create our own context for this SNMP loop */ - pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1); - context->list = pgm_sock_list; - *my_loop_context = context; - -/* pass on for generic row access */ - return pgmSourceTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata); -} - -static -netsnmp_variable_list* -pgmSourceTable_get_next_data_point( - void** my_loop_context, - void** my_data_context, - netsnmp_variable_list* put_index_data, - netsnmp_iterator_info* mydata - ) -{ -/* pre-conditions */ - pgm_assert (NULL != my_loop_context); - pgm_assert (NULL != my_data_context); - pgm_assert (NULL != put_index_data); - pgm_assert (NULL != mydata); - - pgm_debug ("pgmSourceTable_get_next_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", - (const void*)my_loop_context, - (const void*)my_data_context, - (const void*)put_index_data, - (const void*)mydata); - - pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context; - netsnmp_variable_list *idx = put_index_data; - - if (!context->list) - return NULL; - - pgm_sock_t* sock = context->list->data; - -/* pgmSourceGlobalId */ - char gsi[ PGM_GSISTRLEN ]; - pgm_gsi_print_r (&sock->tsi.gsi, gsi, sizeof(gsi)); - snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi)); - idx = idx->next_variable; - -/* pgmSourceSourcePort */ - const unsigned sport = ntohs (sock->tsi.sport); - snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport)); - - *my_data_context = sock; - context->list = context->list->next; - - return put_index_data; -} - -static -void -pgmSourceTable_free_loop_context ( - void* my_loop_context, - netsnmp_iterator_info* mydata - ) -{ -/* pre-conditions */ - pgm_assert (NULL != my_loop_context); - pgm_assert (NULL != mydata); - - pgm_debug ("pgmSourceTable_free_loop_context (my_loop_context:%p mydata:%p)", - (const void*)my_loop_context, - (const void*)mydata); - - pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context; - pgm_free (context); - my_loop_context = NULL; - - pgm_rwlock_reader_unlock (&pgm_sock_list_lock); -} - -static -int -pgmSourceTable_handler ( - netsnmp_mib_handler* handler, - netsnmp_handler_registration* reginfo, - netsnmp_agent_request_info* reqinfo, - netsnmp_request_info* requests - ) -{ -/* pre-conditions */ - pgm_assert (NULL != handler); - pgm_assert (NULL != reginfo); - pgm_assert (NULL != reqinfo); - pgm_assert (NULL != requests); - - pgm_debug ("pgmSourceTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)", - (const void*)handler, - (const void*)reginfo, - (const void*)reqinfo, - (const void*)requests); - - switch (reqinfo->mode) { - -/* Read-support (also covers GetNext requests) */ - - case MODE_GET: - for (netsnmp_request_info* request = requests; - request; - request = request->next) - { - const pgm_sock_t* sock = (pgm_sock_t*)netsnmp_extract_iterator_context (request); - - if (!sock) { - netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE); - continue; - } - - netsnmp_variable_list *var = request->requestvb; - netsnmp_table_request_info* table_info = netsnmp_extract_table_info (request); - - if (!table_info) { - snmp_log (LOG_ERR, "pgmSourceTable_handler: empty table request info.\n"); - continue; - } - - switch (table_info->colnum) { - - case COLUMN_PGMSOURCESOURCEADDRESS: - { - struct sockaddr_in s4; - if (AF_INET == sock->send_gsr.gsr_source.ss_family) - memcpy (&s4, &sock->send_gsr.gsr_source, sizeof(s4)); - else - memset (&s4, 0, sizeof(s4)); - snmp_set_var_typed_value (var, ASN_IPADDRESS, - (const u_char*)&s4.sin_addr.s_addr, - sizeof(struct in_addr) ); - } - break; - - case COLUMN_PGMSOURCEGROUPADDRESS: - { - struct sockaddr_in s4; - if (AF_INET == sock->send_gsr.gsr_group.ss_family) - memcpy (&s4, &sock->send_gsr.gsr_group, sizeof(s4)); - else - memset (&s4, 0, sizeof(s4)); - snmp_set_var_typed_value (var, ASN_IPADDRESS, - (const u_char*)&s4.sin_addr.s_addr, - sizeof(struct in_addr) ); - } - break; - - case COLUMN_PGMSOURCEDESTPORT: - { - const unsigned dport = ntohs (sock->dport); - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)&dport, sizeof(dport) ); - } - break; - -/* copy index[0] */ - case COLUMN_PGMSOURCESOURCEGSI: - snmp_set_var_typed_value (var, ASN_OCTET_STR, - (const u_char*)table_info->indexes->val.string, - table_info->indexes->val_len); - break; - -/* copy index[1] */ - case COLUMN_PGMSOURCESOURCEPORTNUMBER: - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)table_info->indexes->next_variable->val.integer, - table_info->indexes->next_variable->val_len); - - break; - - default: - snmp_log (LOG_ERR, "pgmSourceTable_handler: unknown column.\n"); - break; - } - } - break; - - case MODE_SET_RESERVE1: - default: - snmp_log (LOG_ERR, "pgmSourceTable_handler: unsupported mode.\n"); - break; - - } - - return SNMP_ERR_NOERROR; -} - -/* - * pgmSourceConfigTable - * - */ - -static -int -initialize_table_pgmSourceConfigTable(void) -{ - pgm_debug ("initialize_table_pgmSourceConfigTable ()"); - - static const oid pgmSourceConfigTable_oid[] = {1,3,6,1,3,112,1,2,100,3}; - netsnmp_table_registration_info* table_info = NULL; - netsnmp_iterator_info* iinfo = NULL; - netsnmp_handler_registration* reg = NULL; - - reg = netsnmp_create_handler_registration ("pgmSourceConfigTable", pgmSourceConfigTable_handler, - pgmSourceConfigTable_oid, OID_LENGTH( pgmSourceConfigTable_oid ), - HANDLER_CAN_RONLY); - if (!reg) - goto error; - - table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); - if (!table_info) - goto error; - - table_info->min_column = COLUMN_PGMSOURCETTL; - table_info->max_column = COLUMN_PGMSOURCESPMPATHADDRESS; - - netsnmp_table_helper_add_indexes (table_info, - ASN_OCTET_STR, /* index: pgmSourceConfigGlobalId */ - ASN_UNSIGNED, /* index: pgmSourceConfigSourcePort */ - 0); - - iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info ); - if (!iinfo) - goto error; - - iinfo->get_first_data_point = pgmSourceConfigTable_get_first_data_point; - iinfo->get_next_data_point = pgmSourceConfigTable_get_next_data_point; - iinfo->free_loop_context_at_end = pgmSourceConfigTable_free_loop_context; - iinfo->table_reginfo = table_info; - - return netsnmp_register_table_iterator (reg, iinfo); - -error: - if (table_info && table_info->indexes) /* table_data_free_func() is internal */ - snmp_free_var (table_info->indexes); - SNMP_FREE( table_info ); - SNMP_FREE( iinfo ); - netsnmp_handler_registration_free (reg); - - return -1; -} - -/* called for first row of data in SNMP table - * - * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context, - * optionally returns my_data_context. - * - * returns answer or NULL - */ - -static -netsnmp_variable_list* -pgmSourceConfigTable_get_first_data_point( - void** my_loop_context, /* valid through one query of multiple "data points" */ - void** my_data_context, /* answer blob which is passed to handler() */ - netsnmp_variable_list* put_index_data, /* answer */ - netsnmp_iterator_info* mydata /* iinfo on init() */ - ) -{ -/* pre-conditions */ - pgm_assert (NULL != my_loop_context); - pgm_assert (NULL != my_data_context); - pgm_assert (NULL != put_index_data); - pgm_assert (NULL != mydata); - - pgm_debug ("pgmSourceConfigTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", - (const void*)my_loop_context, - (const void*)my_data_context, - (const void*)put_index_data, - (const void*)mydata); - - pgm_rwlock_reader_lock (&pgm_sock_list_lock); - - if (!pgm_sock_list) { - pgm_rwlock_reader_unlock (&pgm_sock_list_lock); - return NULL; - } - -/* create our own context for this SNMP loop */ - pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1); - context->list = pgm_sock_list; - *my_loop_context = context; - -/* pass on for generic row access */ - return pgmSourceConfigTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata); -} - -static -netsnmp_variable_list* -pgmSourceConfigTable_get_next_data_point( - void** my_loop_context, - void** my_data_context, - netsnmp_variable_list* put_index_data, - netsnmp_iterator_info* mydata - ) -{ -/* pre-conditions */ - pgm_assert (NULL != my_loop_context); - pgm_assert (NULL != my_data_context); - pgm_assert (NULL != put_index_data); - pgm_assert (NULL != mydata); - - pgm_debug ("pgmSourceConfigTable_get_next_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", - (const void*)my_loop_context, - (const void*)my_data_context, - (const void*)put_index_data, - (const void*)mydata); - - pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context; - netsnmp_variable_list *idx = put_index_data; - - if (!context->list) - return NULL; - - pgm_sock_t* sock = context->list->data; - -/* pgmSourceGlobalId */ - char gsi[ PGM_GSISTRLEN ]; - pgm_gsi_print_r (&sock->tsi.gsi, gsi, sizeof(gsi)); - snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi)); - idx = idx->next_variable; - -/* pgmSourceSourcePort */ - const unsigned sport = ntohs (sock->tsi.sport); - snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport)); - - *my_data_context = sock; - context->list = context->list->next; - - return put_index_data; -} - -static -void -pgmSourceConfigTable_free_loop_context ( - void* my_loop_context, - netsnmp_iterator_info* mydata - ) -{ -/* pre-conditions */ - pgm_assert (NULL != my_loop_context); - pgm_assert (NULL != mydata); - - pgm_debug ("pgmSourceConfigTable_free_loop_context (my_loop_context:%p mydata:%p)", - (const void*)my_loop_context, - (const void*)mydata); - - pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context; - pgm_free (context); - my_loop_context = NULL; - - pgm_rwlock_reader_unlock (&pgm_sock_list_lock); -} - -static -int -pgmSourceConfigTable_handler ( - netsnmp_mib_handler* handler, - netsnmp_handler_registration* reginfo, - netsnmp_agent_request_info* reqinfo, - netsnmp_request_info* requests - ) -{ -/* pre-conditions */ - pgm_assert (NULL != handler); - pgm_assert (NULL != reginfo); - pgm_assert (NULL != reqinfo); - pgm_assert (NULL != requests); - - pgm_debug ("pgmSourceConfigTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)", - (const void*)handler, - (const void*)reginfo, - (const void*)reqinfo, - (const void*)requests); - - switch (reqinfo->mode) { - -/* Read-support (also covers GetNext requests) */ - - case MODE_GET: - for (netsnmp_request_info* request = requests; - request; - request = request->next) - { - const pgm_sock_t* sock = (pgm_sock_t*)netsnmp_extract_iterator_context (request); - - if (!sock) { - netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE); - continue; - } - - netsnmp_variable_list *var = request->requestvb; - netsnmp_table_request_info* table_info = netsnmp_extract_table_info (request); - - if (!table_info) { - snmp_log (LOG_ERR, "pgmSourceTable_handler: empty table request info.\n"); - continue; - } - - switch (table_info->colnum) { - - case COLUMN_PGMSOURCETTL: - { - const unsigned hops = sock->hops; - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)&hops, sizeof(hops) ); - } - break; - - case COLUMN_PGMSOURCEADVMODE: - { - const unsigned adv_mode = 0 == sock->adv_mode ? PGMSOURCEADVMODE_TIME : PGMSOURCEADVMODE_DATA; - snmp_set_var_typed_value (var, ASN_INTEGER, - (const u_char*)&adv_mode, sizeof(adv_mode) ); - } - break; - -/* FIXED: pgmSourceLateJoin = disable(2) */ - case COLUMN_PGMSOURCELATEJOIN: - { - const unsigned late_join = PGMSOURCELATEJOIN_DISABLE; - snmp_set_var_typed_value (var, ASN_INTEGER, - (const u_char*)&late_join, sizeof(late_join) ); - } - break; - - case COLUMN_PGMSOURCETXWMAXRTE: - { - const unsigned txw_max_rte = sock->txw_max_rte; - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)&txw_max_rte, sizeof(txw_max_rte) ); - } - break; - - case COLUMN_PGMSOURCETXWSECS: - { - const unsigned txw_secs = sock->txw_secs; - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)&txw_secs, sizeof(txw_secs) ); - } - break; - -/* FIXED: TXW_ADV_SECS = 0 */ - case COLUMN_PGMSOURCETXWADVSECS: - { - const unsigned txw_adv_secs = 0; - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)&txw_adv_secs, sizeof(txw_adv_secs) ); - } - break; - -/* FIXED: pgmSourceAdvIvl = TXW_ADV_SECS * 1000 = 0 */ - case COLUMN_PGMSOURCEADVIVL: - { - const unsigned adv_ivl = 0; - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)&adv_ivl, sizeof(adv_ivl) ); - } - break; - - case COLUMN_PGMSOURCESPMIVL: - { - const unsigned spm_ivl = pgm_to_msecs (sock->spm_ambient_interval); - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)&spm_ivl, sizeof(spm_ivl) ); - } - break; - -/* TODO: IHB_MIN */ - case COLUMN_PGMSOURCESPMHEARTBEATIVLMIN: - { - const unsigned ihb_min = 0; - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)&ihb_min, sizeof(ihb_min) ); - } - break; - -/* TODO: IHB_MAX */ - case COLUMN_PGMSOURCESPMHEARTBEATIVLMAX: - { - const unsigned ihb_max = 0; - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)&ihb_max, sizeof(ihb_max) ); - } - break; - -/* NAK_BO_IVL */ - case COLUMN_PGMSOURCERDATABACKOFFIVL: - { - const unsigned nak_bo_ivl = pgm_to_msecs (sock->nak_bo_ivl); - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)&nak_bo_ivl, sizeof(nak_bo_ivl) ); - } - break; - -/* FIXED: pgmSourceFEC = disabled(1) */ - case COLUMN_PGMSOURCEFEC: - { - const unsigned fec = (sock->use_ondemand_parity || sock->use_proactive_parity) ? 1 : 0; - snmp_set_var_typed_value (var, ASN_INTEGER, - (const u_char*)&fec, sizeof(fec) ); - } - break; - -/* FIXED: pgmSourceFECTransmissionGrpSize = 0 */ - case COLUMN_PGMSOURCEFECTRANSMISSIONGRPSIZE: - { - const unsigned fec_tgs = sock->rs_k; - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)&fec_tgs, sizeof(fec_tgs) ); - } - break; - -/* FIXED: pgmSourceFECProactiveParitySize = 0 */ - case COLUMN_PGMSOURCEFECPROACTIVEPARITYSIZE: - { - const unsigned fec_paps = sock->rs_proactive_h; - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)&fec_paps, sizeof(fec_paps) ); - } - break; - -/* IPv6 not supported */ - case COLUMN_PGMSOURCESPMPATHADDRESS: - { - struct sockaddr_in s4; - if (AF_INET == sock->recv_gsr[0].gsr_source.ss_family) - memcpy (&s4, &sock->recv_gsr[0].gsr_source, sizeof(s4)); - else - memset (&s4, 0, sizeof(s4)); - snmp_set_var_typed_value (var, ASN_IPADDRESS, - (const u_char*)&s4.sin_addr.s_addr, - sizeof(struct in_addr) ); - } - break; - - default: - snmp_log (LOG_ERR, "pgmSourceConfigTable_handler: unknown column.\n"); - break; - } - } - break; - - case MODE_SET_RESERVE1: - default: - snmp_log (LOG_ERR, "pgmSourceConfigTable_handler: unsupported mode.\n"); - break; - - } - - return SNMP_ERR_NOERROR; -} - -/* - * pgmSourcePerformanceTable - */ - -static -int -initialize_table_pgmSourcePerformanceTable (void) -{ - pgm_debug ("initialize_table_pgmSourcePerformanceTable ()"); - - static const oid pgmSourcePerformanceTable_oid[] = {1,3,6,1,3,112,1,2,100,4}; - netsnmp_table_registration_info* table_info = NULL; - netsnmp_iterator_info* iinfo = NULL; - netsnmp_handler_registration* reg = NULL; - - reg = netsnmp_create_handler_registration ("pgmSourcePerformanceTable", pgmSourcePerformanceTable_handler, - pgmSourcePerformanceTable_oid, OID_LENGTH( pgmSourcePerformanceTable_oid ), - HANDLER_CAN_RONLY); - if (!reg) - goto error; - - table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); - if (!table_info) - goto error; - - table_info->min_column = COLUMN_PGMSOURCEDATABYTESSENT; - table_info->max_column = COLUMN_PGMSOURCENNAKERRORS; - - netsnmp_table_helper_add_indexes (table_info, - ASN_OCTET_STR, /* index: pgmSourceGlobalId */ - ASN_UNSIGNED, /* index: pgmSourceSourcePort */ - 0); - - iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info ); - if (!iinfo) - goto error; - - iinfo->get_first_data_point = pgmSourcePerformanceTable_get_first_data_point; - iinfo->get_next_data_point = pgmSourcePerformanceTable_get_next_data_point; - iinfo->free_loop_context_at_end = pgmSourcePerformanceTable_free_loop_context; - iinfo->table_reginfo = table_info; - - return netsnmp_register_table_iterator (reg, iinfo); - -error: - if (table_info && table_info->indexes) /* table_data_free_func() is internal */ - snmp_free_var (table_info->indexes); - SNMP_FREE( table_info ); - SNMP_FREE( iinfo ); - netsnmp_handler_registration_free (reg); - - return -1; -} - -/* called for first row of data in SNMP table - * - * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context, - * optionally returns my_data_context. - * - * returns answer or NULL - */ - -static -netsnmp_variable_list* -pgmSourcePerformanceTable_get_first_data_point ( - void** my_loop_context, /* valid through one query of multiple "data points" */ - void** my_data_context, /* answer blob which is passed to handler() */ - netsnmp_variable_list* put_index_data, /* answer */ - netsnmp_iterator_info* mydata /* iinfo on init() */ - ) -{ -/* pre-conditions */ - pgm_assert (NULL != my_loop_context); - pgm_assert (NULL != my_data_context); - pgm_assert (NULL != put_index_data); - pgm_assert (NULL != mydata); - - pgm_debug ("pgmSourcePerformanceTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", - (const void*)my_loop_context, - (const void*)my_data_context, - (const void*)put_index_data, - (const void*)mydata); - - pgm_rwlock_reader_lock (&pgm_sock_list_lock); - - if (!pgm_sock_list) { - pgm_rwlock_reader_unlock (&pgm_sock_list_lock); - return NULL; - } - -/* create our own context for this SNMP loop */ - pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1); - context->list = pgm_sock_list; - *my_loop_context = context; - -/* pass on for generic row access */ - return pgmSourcePerformanceTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata); -} - -static -netsnmp_variable_list* -pgmSourcePerformanceTable_get_next_data_point ( - void** my_loop_context, - void** my_data_context, - netsnmp_variable_list* put_index_data, - netsnmp_iterator_info* mydata - ) -{ -/* pre-conditions */ - pgm_assert (NULL != my_loop_context); - pgm_assert (NULL != my_data_context); - pgm_assert (NULL != put_index_data); - pgm_assert (NULL != mydata); - - pgm_debug ("pgmSourcePerformanceTable_get_next_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", - (const void*)my_loop_context, - (const void*)my_data_context, - (const void*)put_index_data, - (const void*)mydata); - - pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context; - netsnmp_variable_list *idx = put_index_data; - - if (!context->list) - return NULL; - - pgm_sock_t* sock = context->list->data; - -/* pgmSourceGlobalId */ - char gsi[ PGM_GSISTRLEN ]; - pgm_gsi_print_r (&sock->tsi.gsi, gsi, sizeof(gsi)); - snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi)); - idx = idx->next_variable; - -/* pgmSourceSourcePort */ - const unsigned sport = ntohs (sock->tsi.sport); - snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport)); - - *my_data_context = sock; - context->list = context->list->next; - - return put_index_data; -} - -static -void -pgmSourcePerformanceTable_free_loop_context ( - void* my_loop_context, - netsnmp_iterator_info* mydata - ) -{ -/* pre-conditions */ - pgm_assert (NULL != my_loop_context); - pgm_assert (NULL != mydata); - - pgm_debug ("pgmPerformanceSourceTable_free_loop_context (my_loop_context:%p mydata:%p)", - (const void*)my_loop_context, - (const void*)mydata); - - pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context; - pgm_free (context); - my_loop_context = NULL; - - pgm_rwlock_reader_unlock (&pgm_sock_list_lock); -} - -static -int -pgmSourcePerformanceTable_handler ( - netsnmp_mib_handler* handler, - netsnmp_handler_registration* reginfo, - netsnmp_agent_request_info* reqinfo, - netsnmp_request_info* requests - ) -{ -/* pre-conditions */ - pgm_assert (NULL != handler); - pgm_assert (NULL != reginfo); - pgm_assert (NULL != reqinfo); - pgm_assert (NULL != requests); - - pgm_debug ("pgmSourcePerformanceTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)", - (const void*)handler, - (const void*)reginfo, - (const void*)reqinfo, - (const void*)requests); - - switch (reqinfo->mode) { - -/* Read-support (also covers GetNext requests) */ - - case MODE_GET: - for (netsnmp_request_info* request = requests; - request; - request = request->next) - { - const pgm_sock_t* sock = (pgm_sock_t*)netsnmp_extract_iterator_context (request); - - if (!sock) { - netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE); - continue; - } - - const pgm_txw_t* window = (const pgm_txw_t*)sock->window; - - netsnmp_variable_list *var = request->requestvb; - netsnmp_table_request_info* table_info = netsnmp_extract_table_info (request); - - if (!table_info) { - snmp_log (LOG_ERR, "pgmSourceTable_handler: empty table request info.\n"); - continue; - } - - switch (table_info->colnum) { - - case COLUMN_PGMSOURCEDATABYTESSENT: - { - const unsigned data_bytes = sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&data_bytes, sizeof(data_bytes) ); - } - break; - - case COLUMN_PGMSOURCEDATAMSGSSENT: - { - const unsigned data_msgs = sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&data_msgs, sizeof(data_msgs) ); - } - break; - - case COLUMN_PGMSOURCEBYTESBUFFERED: - { - const unsigned bytes_buffered = sock->can_send_data ? pgm_txw_size (window) : 0; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&bytes_buffered, sizeof(bytes_buffered) ); - } - break; - - case COLUMN_PGMSOURCEMSGSBUFFERED: - { - const unsigned msgs_buffered = sock->can_send_data ? pgm_txw_length (window) : 0; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&msgs_buffered, sizeof(msgs_buffered) ); - } - break; - -/* PGM_PC_SOURCE_SELECTIVE_BYTES_RETRANSMITTED + COLUMN_PGMSOURCEPARITYBYTESRETRANSMITTED */ - case COLUMN_PGMSOURCEBYTESRETRANSMITTED: - { - const unsigned bytes_resent = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_BYTES_RETRANSMITTED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&bytes_resent, sizeof(bytes_resent) ); - } - break; - -/* PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED + COLUMN_PGMSOURCEPARITYMSGSRETRANSMITTED */ - case COLUMN_PGMSOURCEMSGSRETRANSMITTED: - { - const unsigned msgs_resent = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&msgs_resent, sizeof(msgs_resent) ); - } - break; - - case COLUMN_PGMSOURCEBYTESSENT: - { - const unsigned bytes_sent = sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&bytes_sent, sizeof(bytes_sent) ); - } - break; - -/* COLUMN_PGMSOURCEPARITYNAKPACKETSRECEIVED + COLUMN_PGMSOURCESELECTIVENAKPACKETSRECEIVED */ - case COLUMN_PGMSOURCERAWNAKSRECEIVED: - { - const unsigned nak_packets = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&nak_packets, sizeof(nak_packets) ); - } - break; - -/* PGM_PC_SOURCE_SELECTIVE_NAKS_IGNORED + COLUMN_PGMSOURCEPARITYNAKSIGNORED */ - case COLUMN_PGMSOURCENAKSIGNORED: - { - const unsigned naks_ignored = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_IGNORED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&naks_ignored, sizeof(naks_ignored) ); - } - break; - - case COLUMN_PGMSOURCECKSUMERRORS: - { - const unsigned cksum_errors = sock->cumulative_stats[PGM_PC_SOURCE_CKSUM_ERRORS]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&cksum_errors, sizeof(cksum_errors) ); - } - break; - - case COLUMN_PGMSOURCEMALFORMEDNAKS: - { - const unsigned malformed_naks = sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&malformed_naks, sizeof(malformed_naks) ); - } - break; - - case COLUMN_PGMSOURCEPACKETSDISCARDED: - { - const unsigned packets_discarded = sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&packets_discarded, sizeof(packets_discarded) ); - } - break; - -/* PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED + COLUMN_PGMSOURCEPARITYNAKSRECEIVED */ - case COLUMN_PGMSOURCENAKSRCVD: - { - const unsigned naks_received = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&naks_received, sizeof(naks_received) ); - } - break; - -/* FIXED: 0 */ - case COLUMN_PGMSOURCEPARITYBYTESRETRANSMITTED: - { - const unsigned parity_bytes_resent = 0; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&parity_bytes_resent, sizeof(parity_bytes_resent) ); - } - break; - - case COLUMN_PGMSOURCESELECTIVEBYTESRETRANSMITED: - { - const unsigned selective_bytes_resent = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_BYTES_RETRANSMITTED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&selective_bytes_resent, sizeof(selective_bytes_resent) ); - } - break; - -/* FIXED: 0 */ - case COLUMN_PGMSOURCEPARITYMSGSRETRANSMITTED: - { - const unsigned parity_msgs_resent = 0; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&parity_msgs_resent, sizeof(parity_msgs_resent) ); - } - break; - - case COLUMN_PGMSOURCESELECTIVEMSGSRETRANSMITTED: - { - const unsigned selective_msgs_resent = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&selective_msgs_resent, sizeof(selective_msgs_resent) ); - } - break; - -/* FIXED: 0 */ - case COLUMN_PGMSOURCEBYTESADMIT: - { - const unsigned bytes_admit = 0; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&bytes_admit, sizeof(bytes_admit) ); - } - break; - -/* FIXED: 0 */ - case COLUMN_PGMSOURCEMSGSADMIT: - { - const unsigned msgs_admit = 0; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&msgs_admit, sizeof(msgs_admit) ); - } - break; - -/* FIXED: 0 */ - case COLUMN_PGMSOURCEPARITYNAKPACKETSRECEIVED: - { - const unsigned parity_nak_packets = 0; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&parity_nak_packets, sizeof(parity_nak_packets) ); - } - break; - - case COLUMN_PGMSOURCESELECTIVENAKPACKETSRECEIVED: - { - const unsigned selective_nak_packets = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&selective_nak_packets, sizeof(selective_nak_packets) ); - } - break; - -/* FIXED: 0 */ - case COLUMN_PGMSOURCEPARITYNAKSRECEIVED: - { - const unsigned parity_naks = 0; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&parity_naks, sizeof(parity_naks) ); - } - break; - - case COLUMN_PGMSOURCESELECTIVENAKSRECEIVED: - { - const unsigned selective_naks = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&selective_naks, sizeof(selective_naks) ); - } - break; - -/* FIXED: 0 */ - case COLUMN_PGMSOURCEPARITYNAKSIGNORED: - { - const unsigned parity_naks_ignored = 0; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&parity_naks_ignored, sizeof(parity_naks_ignored) ); - } - break; - - case COLUMN_PGMSOURCESELECTIVENAKSIGNORED: - { - const unsigned selective_naks_ignored = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_IGNORED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&selective_naks_ignored, sizeof(selective_naks_ignored) ); - } - break; - - case COLUMN_PGMSOURCEACKERRORS: - { - const unsigned ack_errors = sock->cumulative_stats[PGM_PC_SOURCE_ACK_ERRORS];; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&ack_errors, sizeof(ack_errors) ); - } - break; - - case COLUMN_PGMSOURCEPGMCCACKER: - { - struct sockaddr_in s4; - if (AF_INET == sock->acker_nla.ss_family) - memcpy (&s4, &sock->acker_nla, sizeof(s4)); - else - memset (&s4, 0, sizeof(s4)); - snmp_set_var_typed_value (var, ASN_IPADDRESS, - (const u_char*)&s4.sin_addr.s_addr, - sizeof(struct in_addr) ); - } - break; - - case COLUMN_PGMSOURCETRANSMISSIONCURRENTRATE: - { - const unsigned tx_current_rate = sock->cumulative_stats[PGM_PC_SOURCE_TRANSMISSION_CURRENT_RATE]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&tx_current_rate, sizeof(tx_current_rate) ); - } - break; - - case COLUMN_PGMSOURCEACKPACKETSRECEIVED: - { - const unsigned ack_packets = sock->cumulative_stats[PGM_PC_SOURCE_ACK_PACKETS_RECEIVED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&ack_packets, sizeof(ack_packets) ); - } - break; - -/* COLUMN_PGMSOURCEPARITYNNAKPACKETSRECEIVED + COLUMN_PGMSOURCESELECTIVENNAKPACKETSRECEIVED */ - case COLUMN_PGMSOURCENNAKPACKETSRECEIVED: - { - const unsigned nnak_packets = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAK_PACKETS_RECEIVED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&nnak_packets, sizeof(nnak_packets) ); - } - break; - -/* FIXED: 0 */ - case COLUMN_PGMSOURCEPARITYNNAKPACKETSRECEIVED: - { - const unsigned parity_nnak_packets = 0; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&parity_nnak_packets, sizeof(parity_nnak_packets) ); - } - break; - - case COLUMN_PGMSOURCESELECTIVENNAKPACKETSRECEIVED: - { - const unsigned selective_nnak_packets = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAK_PACKETS_RECEIVED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&selective_nnak_packets, sizeof(selective_nnak_packets) ); - } - break; - -/* COLUMN_PGMSOURCEPARITYNNAKSRECEIVED + COLUMN_PGMSOURCESELECTIVENNAKSRECEIVED */ - case COLUMN_PGMSOURCENNAKSRECEIVED: - { - const unsigned nnaks_received = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAKS_RECEIVED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&nnaks_received, sizeof(nnaks_received) ); - } - break; - -/* FIXED: 0 */ - case COLUMN_PGMSOURCEPARITYNNAKSRECEIVED: - { - const unsigned parity_nnaks = 0; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&parity_nnaks, sizeof(parity_nnaks) ); - } - break; - - case COLUMN_PGMSOURCESELECTIVENNAKSRECEIVED: - { - const unsigned selective_nnaks = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAKS_RECEIVED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&selective_nnaks, sizeof(selective_nnaks) ); - } - break; - - case COLUMN_PGMSOURCENNAKERRORS: - { - const unsigned malformed_nnaks = sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&malformed_nnaks, sizeof(malformed_nnaks) ); - } - break; - - default: - snmp_log (LOG_ERR, "pgmSourcePerformanceTable_handler: unknown column.\n"); - break; - } - } - break; - - case MODE_SET_RESERVE1: - default: - snmp_log (LOG_ERR, "pgmSourcePerformanceTable_handler: unsupported mode.\n"); - break; - - } - - return SNMP_ERR_NOERROR; -} - -/* - * pgmReceiverTable - */ - -static -int -initialize_table_pgmReceiverTable(void) -{ - pgm_debug ("initialize_table_pgmReceiverTable ()"); - - static const oid pgmReceiverTable_oid[] = {1,3,6,1,3,112,1,3,100,2}; - netsnmp_table_registration_info* table_info = NULL; - netsnmp_iterator_info* iinfo = NULL; - netsnmp_handler_registration* reg = NULL; - - reg = netsnmp_create_handler_registration ("pgmReceiverTable", pgmReceiverTable_handler, - pgmReceiverTable_oid, OID_LENGTH( pgmReceiverTable_oid ), - HANDLER_CAN_RONLY); - if (!reg) - goto error; - - table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); - if (!table_info) - goto error; - - table_info->min_column = COLUMN_PGMRECEIVERGROUPADDRESS; - table_info->max_column = COLUMN_PGMRECEIVERUNIQUEINSTANCE; - - netsnmp_table_helper_add_indexes (table_info, - ASN_OCTET_STR, /* index: pgmReceiverGlobalId */ - ASN_UNSIGNED, /* index: pgmReceiverSourcePort */ - ASN_UNSIGNED, /* index: pgmReceiverInstance */ - 0); - - iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info ); - if (!iinfo) - goto error; - - iinfo->get_first_data_point = pgmReceiverTable_get_first_data_point; - iinfo->get_next_data_point = pgmReceiverTable_get_next_data_point; - iinfo->free_loop_context_at_end = pgmReceiverTable_free_loop_context; - iinfo->table_reginfo = table_info; - - return netsnmp_register_table_iterator (reg, iinfo); - -error: - if (table_info && table_info->indexes) /* table_data_free_func() is internal */ - snmp_free_var (table_info->indexes); - SNMP_FREE( table_info ); - SNMP_FREE( iinfo ); - netsnmp_handler_registration_free (reg); - - return -1; -} - -/* called for first row of data in SNMP table - * - * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context, - * optionally returns my_data_context. - * - * returns answer or NULL - */ - -static -netsnmp_variable_list* -pgmReceiverTable_get_first_data_point ( - void** my_loop_context, /* valid through one query of multiple "data points" */ - void** my_data_context, /* answer blob which is passed to handler() */ - netsnmp_variable_list* put_index_data, /* answer */ - netsnmp_iterator_info* mydata /* iinfo on init() */ - ) -{ -/* pre-conditions */ - pgm_assert (NULL != my_loop_context); - pgm_assert (NULL != my_data_context); - pgm_assert (NULL != put_index_data); - pgm_assert (NULL != mydata); - - pgm_debug ("pgmReceiverTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", - (const void*)my_loop_context, - (const void*)my_data_context, - (const void*)put_index_data, - (const void*)mydata); - - pgm_rwlock_reader_lock (&pgm_sock_list_lock); - - if (!pgm_sock_list) { - pgm_rwlock_reader_unlock (&pgm_sock_list_lock); - return NULL; - } - -/* create our own context for this SNMP loop */ - pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1); - -/* hunt to find first node, through all socks */ - for (context->list = pgm_sock_list; - context->list; - context->list = context->list->next) - { -/* and through all peers for each sock */ - pgm_sock_t* sock = (pgm_sock_t*)context->list->data; - pgm_rwlock_reader_lock (&sock->peers_lock); - context->node = sock->peers_list; - if (context->node) { -/* maintain this sock's peers lock */ - break; - } - - pgm_rwlock_reader_unlock (&sock->peers_lock); - } - -/* no node found */ - if (!context->node) { - pgm_free (context); - pgm_rwlock_reader_unlock (&pgm_sock_list_lock); - return NULL; - } - - *my_loop_context = context; - -/* pass on for generic row access */ - return pgmReceiverTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata); -} - -static -netsnmp_variable_list* -pgmReceiverTable_get_next_data_point ( - void** my_loop_context, - void** my_data_context, - netsnmp_variable_list* put_index_data, - netsnmp_iterator_info* mydata - ) -{ -/* pre-conditions */ - pgm_assert (NULL != my_loop_context); - pgm_assert (NULL != my_data_context); - pgm_assert (NULL != put_index_data); - pgm_assert (NULL != mydata); - - pgm_debug ("pgmReceiverTable_get_next_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", - (const void*)my_loop_context, - (const void*)my_data_context, - (const void*)put_index_data, - (const void*)mydata); - - pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context; - netsnmp_variable_list *idx = put_index_data; - - if (!context->list) - return NULL; - - pgm_sock_t* sock = context->list->data; - - if (!context->node) - return NULL; - - pgm_peer_t* peer = context->node->data; - -/* pgmReceiverGlobalId */ - char gsi[ PGM_GSISTRLEN ]; - pgm_gsi_print_r (&peer->tsi.gsi, gsi, sizeof(gsi)); - snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi)); - idx = idx->next_variable; - -/* pgmReceiverSourcePort */ - const unsigned sport = ntohs (peer->tsi.sport); - snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport)); - idx = idx->next_variable; - -/* pgmReceiverInstance */ - const unsigned instance = context->instance++; - snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&instance, sizeof(instance)); - -/* set data context to pass to handler callback */ - *my_data_context = peer; - -/* hunt for next valid node */ - if (context->node->next) { - context->node = context->node->next; - } else { - context->node = NULL; - while (context->list->next) - { - pgm_rwlock_reader_unlock (&sock->peers_lock); - context->list = context->list->next; - sock = context->list->data; - pgm_rwlock_reader_lock (&sock->peers_lock); - context->node = sock->peers_list; - if (context->node) { -/* keep lock */ - break; - } - } - } - - return put_index_data; -} - -static -void -pgmReceiverTable_free_loop_context ( - void* my_loop_context, - netsnmp_iterator_info* mydata - ) -{ -/* pre-conditions */ - pgm_assert (NULL != my_loop_context); - pgm_assert (NULL != mydata); - - pgm_debug ("pgmReceiverTable_free_loop_context (my_loop_context:%p mydata:%p)", - (const void*)my_loop_context, - (const void*)mydata); - - pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context; - -/* check for intra-peer state */ - if (context->list) { - pgm_sock_t* sock = context->list->data; - pgm_rwlock_reader_unlock (&sock->peers_lock); - } - - pgm_free (context); - my_loop_context = NULL; - - pgm_rwlock_reader_unlock (&pgm_sock_list_lock); -} - -static -int -pgmReceiverTable_handler ( - netsnmp_mib_handler* handler, - netsnmp_handler_registration* reginfo, - netsnmp_agent_request_info* reqinfo, - netsnmp_request_info* requests - ) -{ -/* pre-conditions */ - pgm_assert (NULL != handler); - pgm_assert (NULL != reginfo); - pgm_assert (NULL != reqinfo); - pgm_assert (NULL != requests); - - pgm_debug ("pgmReceiverTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)", - (const void*)handler, - (const void*)reginfo, - (const void*)reqinfo, - (const void*)requests); - - switch (reqinfo->mode) { - -/* Read-support (also covers GetNext requests) */ - - case MODE_GET: - for (netsnmp_request_info* request = requests; - request; - request = request->next) - { - const pgm_peer_t* peer = (pgm_peer_t*)netsnmp_extract_iterator_context (request); - - if (!peer) { - netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE); - continue; - } - - netsnmp_variable_list *var = request->requestvb; - netsnmp_table_request_info* table_info = netsnmp_extract_table_info(request); - - if (table_info == NULL) { - snmp_log (LOG_ERR, "pgmReceiverTable_handler: empty table request info.\n"); - continue; - } - - switch (table_info->colnum) { - - case COLUMN_PGMRECEIVERGROUPADDRESS: - { - struct sockaddr_in s4; - if (AF_INET == peer->group_nla.ss_family) - memcpy (&s4, &peer->group_nla, sizeof(s4)); - else - memset (&s4, 0, sizeof(s4)); - snmp_set_var_typed_value (var, ASN_IPADDRESS, - (const u_char*)&s4.sin_addr.s_addr, - sizeof(struct in_addr) ); - } - break; - -/* by definition same as sock */ - case COLUMN_PGMRECEIVERDESTPORT: - { - const unsigned dport = ntohs (peer->sock->dport); - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)&dport, sizeof(dport) ); - } - break; - - case COLUMN_PGMRECEIVERSOURCEADDRESS: - { - struct sockaddr_in s4; - if (AF_INET == peer->nla.ss_family) - memcpy (&s4, &peer->nla, sizeof(s4)); - else - memset (&s4, 0, sizeof(s4)); - snmp_set_var_typed_value (var, ASN_IPADDRESS, - (const u_char*)&s4.sin_addr.s_addr, - sizeof(struct in_addr) ); - } - break; - - case COLUMN_PGMRECEIVERLASTHOP: - { - struct sockaddr_in s4; - if (AF_INET == peer->local_nla.ss_family) - memcpy (&s4, &peer->local_nla, sizeof(s4)); - else - memset (&s4, 0, sizeof(s4)); - snmp_set_var_typed_value (var, ASN_IPADDRESS, - (const u_char*)&s4.sin_addr.s_addr, - sizeof(struct in_addr) ); - } - break; - -/* copy index[0] */ - case COLUMN_PGMRECEIVERSOURCEGSI: - snmp_set_var_typed_value (var, ASN_OCTET_STR, - (const u_char*)table_info->indexes->val.string, - table_info->indexes->val_len); - break; - -/* copy index[1] */ - case COLUMN_PGMRECEIVERSOURCEPORTNUMBER: - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)table_info->indexes->next_variable->val.integer, - table_info->indexes->next_variable->val_len); - break; - -/* copy index[2] */ - case COLUMN_PGMRECEIVERUNIQUEINSTANCE: - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)table_info->indexes->next_variable->next_variable->val.integer, - table_info->indexes->next_variable->next_variable->val_len); - break; - - default: - snmp_log (LOG_ERR, "pgmReceiverTable_handler: unknown column.\n"); - break; - } - } - break; - - case MODE_SET_RESERVE1: - default: - snmp_log (LOG_ERR, "pgmReceiverTable_handler: unsupported mode.\n"); - break; - - } - - return SNMP_ERR_NOERROR; -} - -/* - * pgmReceiverConfigTable - * - */ - -static -int -initialize_table_pgmReceiverConfigTable(void) -{ - pgm_debug ("initialize_table_pgmReceiverConfigTable ()"); - - static const oid pgmReceiverConfigTable_oid[] = {1,3,6,1,3,112,1,3,100,3}; - netsnmp_table_registration_info* table_info = NULL; - netsnmp_iterator_info* iinfo = NULL; - netsnmp_handler_registration* reg = NULL; - - reg = netsnmp_create_handler_registration ("pgmReceiverConfigTable", pgmReceiverConfigTable_handler, - pgmReceiverConfigTable_oid, OID_LENGTH(pgmReceiverConfigTable_oid), - HANDLER_CAN_RONLY); - if (!reg) - goto error; - - table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); - if (!table_info) - goto error; - - table_info->min_column = COLUMN_PGMRECEIVERNAKBACKOFFIVL; - table_info->max_column = COLUMN_PGMRECEIVERNAKFAILURETHRESHOLD; - - netsnmp_table_helper_add_indexes (table_info, - ASN_OCTET_STR, /* index: pgmReceiverConfigGlobalId */ - ASN_UNSIGNED, /* index: pgmReceiverConfigSourcePort */ - ASN_UNSIGNED, /* index: pgmReceiverInstance */ - 0); - - iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info ); - if (!iinfo) - goto error; - - iinfo->get_first_data_point = pgmReceiverConfigTable_get_first_data_point; - iinfo->get_next_data_point = pgmReceiverConfigTable_get_next_data_point; - iinfo->free_loop_context_at_end = pgmReceiverConfigTable_free_loop_context; - iinfo->table_reginfo = table_info; - - return netsnmp_register_table_iterator (reg, iinfo); - -error: - if (table_info && table_info->indexes) /* table_data_free_func() is internal */ - snmp_free_var (table_info->indexes); - SNMP_FREE( table_info ); - SNMP_FREE( iinfo ); - netsnmp_handler_registration_free (reg); - - return -1; -} - -/* called for first row of data in SNMP table - * - * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context, - * optionally returns my_data_context. - * - * returns answer or NULL - */ - -static -netsnmp_variable_list* -pgmReceiverConfigTable_get_first_data_point( - void** my_loop_context, /* valid through one query of multiple "data points" */ - void** my_data_context, /* answer blob which is passed to handler() */ - netsnmp_variable_list* put_index_data, /* answer */ - netsnmp_iterator_info* mydata /* iinfo on init() */ - ) -{ -/* pre-conditions */ - pgm_assert (NULL != my_loop_context); - pgm_assert (NULL != my_data_context); - pgm_assert (NULL != put_index_data); - pgm_assert (NULL != mydata); - - pgm_debug ("pgmReceiverConfigTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", - (const void*)my_loop_context, - (const void*)my_data_context, - (const void*)put_index_data, - (const void*)mydata); - - pgm_rwlock_reader_lock (&pgm_sock_list_lock); - - if (!pgm_sock_list) { - pgm_rwlock_reader_unlock (&pgm_sock_list_lock); - return NULL; - } - -/* create our own context for this SNMP loop */ - pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1); - -/* hunt to find first node, through all socks */ - for (context->list = pgm_sock_list; - context->list; - context->list = context->list->next) - { -/* and through all peers for each sock */ - pgm_sock_t* sock = (pgm_sock_t*)context->list->data; - pgm_rwlock_reader_lock (&sock->peers_lock); - context->node = sock->peers_list; - if (context->node) - break; - - pgm_rwlock_reader_unlock (&sock->peers_lock); - } - -/* no node found */ - if (!context->node) { - pgm_free (context); - pgm_rwlock_reader_unlock (&pgm_sock_list_lock); - return NULL; - } - - *my_loop_context = context; - -/* pass on for generic row access */ - return pgmReceiverConfigTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata); -} - -static -netsnmp_variable_list* -pgmReceiverConfigTable_get_next_data_point( - void** my_loop_context, - void** my_data_context, - netsnmp_variable_list* put_index_data, - netsnmp_iterator_info* mydata - ) -{ -/* pre-conditions */ - pgm_assert (NULL != my_loop_context); - pgm_assert (NULL != my_data_context); - pgm_assert (NULL != put_index_data); - pgm_assert (NULL != mydata); - - pgm_debug ("pgmReceiverConfigTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", - (const void*)my_loop_context, - (const void*)my_data_context, - (const void*)put_index_data, - (const void*)mydata); - - pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context; - netsnmp_variable_list *idx = put_index_data; - - if (!context->list) - return NULL; - - pgm_sock_t* sock = context->list->data; - - if (!context->node) - return NULL; - - pgm_peer_t* peer = context->node->data; - -/* pgmReceiverGlobalId */ - char gsi[ PGM_GSISTRLEN ]; - pgm_gsi_print_r (&peer->tsi.gsi, gsi, sizeof(gsi)); - snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi)); - idx = idx->next_variable; - -/* pgmReceiverSourcePort */ - const unsigned sport = ntohs (peer->tsi.sport); - snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport)); - idx = idx->next_variable; - -/* pgmReceiverInstance */ - const unsigned instance = context->instance++; - snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&instance, sizeof(instance)); - -/* set data context to pass to handler callback */ - *my_data_context = peer; - -/* hunt for next valid node */ - if (context->node->next) { - context->node = context->node->next; - } else { - context->node = NULL; - while (context->list->next) - { - pgm_rwlock_reader_unlock (&sock->peers_lock); - context->list = context->list->next; - sock = context->list->data; - pgm_rwlock_reader_lock (&sock->peers_lock); - context->node = sock->peers_list; - if (context->node) { -/* keep lock */ - break; - } - } - } - - return put_index_data; -} - -static -void -pgmReceiverConfigTable_free_loop_context ( - void* my_loop_context, - netsnmp_iterator_info* mydata - ) -{ -/* pre-conditions */ - pgm_assert (NULL != my_loop_context); - pgm_assert (NULL != mydata); - - pgm_debug ("pgmReceiverConfigTable_free_loop_context (my_loop_context:%p mydata:%p)", - (const void*)my_loop_context, - (const void*)mydata); - - pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context; - -/* check for intra-peer state */ - if (context->list) { - pgm_sock_t* sock = context->list->data; - pgm_rwlock_reader_unlock (&sock->peers_lock); - } - - pgm_free (context); - my_loop_context = NULL; - - pgm_rwlock_reader_unlock (&pgm_sock_list_lock); -} - -static -int -pgmReceiverConfigTable_handler ( - netsnmp_mib_handler* handler, - netsnmp_handler_registration* reginfo, - netsnmp_agent_request_info* reqinfo, - netsnmp_request_info* requests - ) -{ -/* pre-conditions */ - pgm_assert (NULL != handler); - pgm_assert (NULL != reginfo); - pgm_assert (NULL != reqinfo); - pgm_assert (NULL != requests); - - pgm_debug ("pgmReceiverConfigTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)", - (const void*)handler, - (const void*)reginfo, - (const void*)reqinfo, - (const void*)requests); - - switch (reqinfo->mode) { - -/* Read-support (also covers GetNext requests) */ - - case MODE_GET: - for (netsnmp_request_info* request = requests; - request; - request = request->next) - { - const pgm_peer_t* peer = (pgm_peer_t*)netsnmp_extract_iterator_context(request); - - if (peer == NULL) { - netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE); - continue; - } - - netsnmp_variable_list *var = request->requestvb; - netsnmp_table_request_info* table_info = netsnmp_extract_table_info(request); - - if (table_info == NULL) { - snmp_log (LOG_ERR, "pgmReceiverTable_handler: empty table request info.\n"); - continue; - } - - switch (table_info->colnum) { - -/* nak_bo_ivl from sock */ - case COLUMN_PGMRECEIVERNAKBACKOFFIVL: - { - const unsigned nak_bo_ivl = peer->sock->nak_bo_ivl; - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)&nak_bo_ivl, sizeof(nak_bo_ivl) ); - } - break; - -/* nak_rpt_ivl from sock */ - case COLUMN_PGMRECEIVERNAKREPEATIVL: - { - const unsigned nak_rpt_ivl = peer->sock->nak_rpt_ivl; - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)&nak_rpt_ivl, sizeof(nak_rpt_ivl) ); - } - break; - -/* nak_ncf_retries from sock */ - case COLUMN_PGMRECEIVERNAKNCFRETRIES: - { - const unsigned nak_ncf_retries = peer->sock->nak_ncf_retries; - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)&nak_ncf_retries, sizeof(nak_ncf_retries) ); - } - break; - -/* nak_rdata_ivl from sock */ - case COLUMN_PGMRECEIVERNAKRDATAIVL: - { - const unsigned nak_rdata_ivl = peer->sock->nak_rdata_ivl; - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)&nak_rdata_ivl, sizeof(nak_rdata_ivl) ); - } - break; - -/* nak_data_retries from sock */ - case COLUMN_PGMRECEIVERNAKDATARETRIES: - { - const unsigned nak_data_retries = peer->sock->nak_data_retries; - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)&nak_data_retries, sizeof(nak_data_retries) ); - } - break; - -/* FIXED: pgmReceiverSendNaks = enabled(1) */ - case COLUMN_PGMRECEIVERSENDNAKS: - { - const unsigned send_naks = PGMRECEIVERSENDNAKS_ENABLED; - snmp_set_var_typed_value (var, ASN_INTEGER, - (const u_char*)&send_naks, sizeof(send_naks) ); - } - break; - -/* FIXED: pgmReceiverLateJoin = disabled(2) */ - case COLUMN_PGMRECEIVERLATEJOIN: - { - const unsigned late_join = PGMRECEIVERLATEJOIN_DISABLED; - snmp_set_var_typed_value (var, ASN_INTEGER, - (const u_char*)&late_join, sizeof(late_join) ); - } - break; - -/* FIXED: 1 for multicast */ - case COLUMN_PGMRECEIVERNAKTTL: - { - const unsigned nak_hops = 1; - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)&nak_hops, sizeof(nak_hops) ); - } - break; - -/* FIXED: pgmReceiverDeliveryOrder = ordered(2) */ - case COLUMN_PGMRECEIVERDELIVERYORDER: - { - const unsigned delivery_order = PGMRECEIVERDELIVERYORDER_ORDERED; - snmp_set_var_typed_value (var, ASN_INTEGER, - (const u_char*)&delivery_order, sizeof(delivery_order) ); - } - break; - -/* FIXED: pgmReceiverMcastNaks = disabled(2) */ - case COLUMN_PGMRECEIVERMCASTNAKS: - { - const unsigned mcast_naks = PGMRECEIVERMCASTNAKS_DISABLED; - snmp_set_var_typed_value (var, ASN_INTEGER, - (const u_char*)&mcast_naks, sizeof(mcast_naks) ); - } - break; - -/* TODO: traps */ - case COLUMN_PGMRECEIVERNAKFAILURETHRESHOLDTIMER: - case COLUMN_PGMRECEIVERNAKFAILURETHRESHOLD: - { - const unsigned threshold = 0; - snmp_set_var_typed_value (var, ASN_UNSIGNED, - (const u_char*)&threshold, sizeof(threshold) ); - } - break; - - default: - snmp_log (LOG_ERR, "pgmReceiverTable_handler: unknown column.\n"); - break; - } - } - break; - - case MODE_SET_RESERVE1: - default: - snmp_log (LOG_ERR, "pgmReceiverTable_handler: unsupported mode.\n"); - break; - - } - - return SNMP_ERR_NOERROR; -} - -/* - * pgmReceiverPerformanceTable - */ - -static -int -initialize_table_pgmReceiverPerformanceTable (void) -{ - pgm_debug ("initialize_table_pgmReceiverPerformanceTable ()"); - - static const oid pgmReceiverPerformanceTable_oid[] = {1,3,6,1,3,112,1,3,100,4}; - netsnmp_table_registration_info* table_info = NULL; - netsnmp_iterator_info* iinfo = NULL; - netsnmp_handler_registration* reg = NULL; - - reg = netsnmp_create_handler_registration ("pgmReceiverPerformanceTable", pgmReceiverPerformanceTable_handler, - pgmReceiverPerformanceTable_oid, OID_LENGTH( pgmReceiverPerformanceTable_oid ), - HANDLER_CAN_RONLY); - if (!reg) - goto error; - - table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); - if (!table_info) - goto error; - - table_info->min_column = COLUMN_PGMRECEIVERDATABYTESRECEIVED; - table_info->max_column = COLUMN_PGMRECEIVERLASTINTERVALNAKFAILURES; - - netsnmp_table_helper_add_indexes (table_info, - ASN_OCTET_STR, /* index: pgmReceiverGlobalId */ - ASN_UNSIGNED, /* index: pgmReceiverSourcePort */ - ASN_UNSIGNED, /* index: pgmReceiverInstance */ - 0); - - iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info ); - if (!iinfo) - goto error; - - iinfo->get_first_data_point = pgmReceiverPerformanceTable_get_first_data_point; - iinfo->get_next_data_point = pgmReceiverPerformanceTable_get_next_data_point; - iinfo->free_loop_context_at_end = pgmReceiverPerformanceTable_free_loop_context; - iinfo->table_reginfo = table_info; - - return netsnmp_register_table_iterator (reg, iinfo); - -error: - if (table_info && table_info->indexes) /* table_data_free_func() is internal */ - snmp_free_var (table_info->indexes); - SNMP_FREE( table_info ); - SNMP_FREE( iinfo ); - netsnmp_handler_registration_free (reg); - - return -1; -} - -/* called for first row of data in SNMP table - * - * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context, - * optionally returns my_data_context. - * - * returns answer or NULL - */ - -static -netsnmp_variable_list* -pgmReceiverPerformanceTable_get_first_data_point ( - void** my_loop_context, /* valid through one query of multiple "data points" */ - void** my_data_context, /* answer blob which is passed to handler() */ - netsnmp_variable_list* put_index_data, /* answer */ - netsnmp_iterator_info* mydata /* iinfo on init() */ - ) -{ -/* pre-conditions */ - pgm_assert (NULL != my_loop_context); - pgm_assert (NULL != my_data_context); - pgm_assert (NULL != put_index_data); - pgm_assert (NULL != mydata); - - pgm_debug ("pgmReceiverPerformanceTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", - (const void*)my_loop_context, - (const void*)my_data_context, - (const void*)put_index_data, - (const void*)mydata); - - pgm_rwlock_reader_lock (&pgm_sock_list_lock); - - if (!pgm_sock_list) { - pgm_rwlock_reader_unlock (&pgm_sock_list_lock); - return NULL; - } - -/* create our own context for this SNMP loop */ - pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1); - -/* hunt to find first node, through all socks */ - for (context->list = pgm_sock_list; - context->list; - context->list = context->list->next) - { -/* and through all peers for each sock */ - pgm_sock_t* sock = (pgm_sock_t*)context->list->data; - pgm_rwlock_reader_lock (&sock->peers_lock); - context->node = sock->peers_list; - if (context->node) - break; - - pgm_rwlock_reader_unlock (&sock->peers_lock); - } - -/* no node found */ - if (!context->node) { - pgm_free (context); - pgm_rwlock_reader_unlock (&pgm_sock_list_lock); - return NULL; - } - - *my_loop_context = context; - -/* pass on for generic row access */ - return pgmReceiverPerformanceTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata); -} - -static -netsnmp_variable_list* -pgmReceiverPerformanceTable_get_next_data_point ( - void** my_loop_context, - void** my_data_context, - netsnmp_variable_list* put_index_data, - netsnmp_iterator_info* mydata - ) -{ -/* pre-conditions */ - pgm_assert (NULL != my_loop_context); - pgm_assert (NULL != my_data_context); - pgm_assert (NULL != put_index_data); - pgm_assert (NULL != mydata); - - pgm_debug ("pgmReceiverPerformanceTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", - (const void*)my_loop_context, - (const void*)my_data_context, - (const void*)put_index_data, - (const void*)mydata); - - pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context; - netsnmp_variable_list *idx = put_index_data; - - if (!context->list) - return NULL; - - pgm_sock_t* sock = context->list->data; - - if (!context->node) - return NULL; - - pgm_peer_t* peer = context->node->data; - -/* pgmReceiverGlobalId */ - char gsi[ PGM_GSISTRLEN ]; - pgm_gsi_print_r (&peer->tsi.gsi, gsi, sizeof(gsi)); - snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi)); - idx = idx->next_variable; - -/* pgmReceiverSourcePort */ - const unsigned sport = ntohs (peer->tsi.sport); - snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport)); - idx = idx->next_variable; - -/* pgmReceiverInstance */ - const unsigned instance = context->instance++; - snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&instance, sizeof(instance)); - -/* set data context to pass to handler callback */ - *my_data_context = peer; - -/* hunt for next valid node */ - if (context->node->next) { - context->node = context->node->next; - } else { - context->node = NULL; - while (context->list->next) - { - pgm_rwlock_reader_unlock (&sock->peers_lock); - context->list = context->list->next; - sock = context->list->data; - pgm_rwlock_reader_lock (&sock->peers_lock); - context->node = sock->peers_list; - - if (context->node) - break; - } - } - - return put_index_data; -} - -static -void -pgmReceiverPerformanceTable_free_loop_context ( - void* my_loop_context, - netsnmp_iterator_info* mydata - ) -{ -/* pre-conditions */ - pgm_assert (NULL != my_loop_context); - pgm_assert (NULL != mydata); - - pgm_debug ("pgmReceiverPerformanceTable_free_loop_context (my_loop_context:%p mydata:%p)", - (const void*)my_loop_context, - (const void*)mydata); - - pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context; - -/* check for intra-peer state */ - if (context->list) { - pgm_sock_t* sock = context->list->data; - pgm_rwlock_reader_unlock (&sock->peers_lock); - } - - pgm_free (context); - my_loop_context = NULL; - - pgm_rwlock_reader_unlock (&pgm_sock_list_lock); -} - -static -int -pgmReceiverPerformanceTable_handler ( - netsnmp_mib_handler* handler, - netsnmp_handler_registration* reginfo, - netsnmp_agent_request_info* reqinfo, - netsnmp_request_info* requests - ) -{ -/* pre-conditions */ - pgm_assert (NULL != handler); - pgm_assert (NULL != reginfo); - pgm_assert (NULL != reqinfo); - pgm_assert (NULL != requests); - - pgm_debug ("pgmReceiverPerformanceTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)", - (const void*)handler, - (const void*)reginfo, - (const void*)reqinfo, - (const void*)requests); - - switch (reqinfo->mode) { - -/* Read-support (also covers GetNext requests) */ - - case MODE_GET: - for (netsnmp_request_info* request = requests; - request; - request = request->next) - { - const pgm_peer_t* peer = (pgm_peer_t*)netsnmp_extract_iterator_context (request); - - if (!peer) { - netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE); - continue; - } - - const pgm_rxw_t* window = (const pgm_rxw_t*)peer->window; - - netsnmp_variable_list *var = request->requestvb; - netsnmp_table_request_info* table_info = netsnmp_extract_table_info (request); - - if (!table_info) { - snmp_log (LOG_ERR, "pgmReceiverTable_handler: empty table request info.\n"); - continue; - } - - switch (table_info->colnum) { - - case COLUMN_PGMRECEIVERDATABYTESRECEIVED: - { - const unsigned data_bytes = peer->cumulative_stats[PGM_PC_RECEIVER_DATA_BYTES_RECEIVED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&data_bytes, sizeof(data_bytes) ); - } - break; - - case COLUMN_PGMRECEIVERDATAMSGSRECEIVED: - { - const unsigned data_msgs = peer->cumulative_stats[PGM_PC_RECEIVER_DATA_MSGS_RECEIVED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&data_msgs, sizeof(data_msgs) ); - } - break; - -/* total */ - case COLUMN_PGMRECEIVERNAKSSENT: - { - const unsigned naks_sent = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SENT]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&naks_sent, sizeof(naks_sent) ); - } - break; - -/* total */ - case COLUMN_PGMRECEIVERNAKSRETRANSMITTED: - { - const unsigned naks_resent = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_RETRANSMITTED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&naks_resent, sizeof(naks_resent) ); - } - break; - -/* total */ - case COLUMN_PGMRECEIVERNAKFAILURES: - { - const unsigned nak_failures = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_FAILED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&nak_failures, sizeof(nak_failures) ); - } - break; - - case COLUMN_PGMRECEIVERBYTESRECEIVED: - { - const unsigned bytes_received = peer->cumulative_stats[PGM_PC_RECEIVER_BYTES_RECEIVED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&bytes_received, sizeof(bytes_received) ); - } - break; - -/* total */ - case COLUMN_PGMRECEIVERNAKSSUPPRESSED: - { - const unsigned naks_suppressed = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&naks_suppressed, sizeof(naks_suppressed) ); - } - break; - -/* bogus: same as source checksum errors */ - case COLUMN_PGMRECEIVERCKSUMERRORS: - { - const unsigned cksum_errors = peer->sock->cumulative_stats[PGM_PC_SOURCE_CKSUM_ERRORS]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&cksum_errors, sizeof(cksum_errors) ); - } - break; - - case COLUMN_PGMRECEIVERMALFORMEDSPMS: - { - const unsigned malformed_spms = peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_SPMS]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&malformed_spms, sizeof(malformed_spms) ); - } - break; - - case COLUMN_PGMRECEIVERMALFORMEDODATA: - { - const unsigned malformed_odata = peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_ODATA]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&malformed_odata, sizeof(malformed_odata) ); - } - break; - - case COLUMN_PGMRECEIVERMALFORMEDRDATA: - { - const unsigned malformed_rdata = peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_RDATA]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&malformed_rdata, sizeof(malformed_rdata) ); - } - break; - - case COLUMN_PGMRECEIVERMALFORMEDNCFS: - { - const unsigned malformed_ncfs = peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_NCFS]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&malformed_ncfs, sizeof(malformed_ncfs) ); - } - break; - - case COLUMN_PGMRECEIVERPACKETSDISCARDED: - { - const unsigned packets_discarded = peer->cumulative_stats[PGM_PC_RECEIVER_PACKETS_DISCARDED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&packets_discarded, sizeof(packets_discarded) ); - } - break; - - case COLUMN_PGMRECEIVERLOSSES: - { - const unsigned losses = window->cumulative_losses; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&losses, sizeof(losses) ); - } - break; - - case COLUMN_PGMRECEIVERBYTESDELIVEREDTOAPP: - { - const unsigned bytes_delivered =window->bytes_delivered; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&bytes_delivered, sizeof(bytes_delivered) ); - } - break; - - case COLUMN_PGMRECEIVERMSGSDELIVEREDTOAPP: - { - const unsigned msgs_delivered = window->msgs_delivered; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&msgs_delivered, sizeof(msgs_delivered) ); - } - break; - - case COLUMN_PGMRECEIVERDUPSPMS: - { - const unsigned dup_spms = peer->cumulative_stats[PGM_PC_RECEIVER_DUP_SPMS]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&dup_spms, sizeof(dup_spms) ); - } - break; - - case COLUMN_PGMRECEIVERDUPDATAS: - { - const unsigned dup_data = peer->cumulative_stats[PGM_PC_RECEIVER_DUP_DATAS]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&dup_data, sizeof(dup_data) ); - } - break; - -/* FIXED: 0 */ - case COLUMN_PGMRECEIVERDUPPARITIES: - { - const unsigned dup_parity = 0; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&dup_parity, sizeof(dup_parity) ); - } - break; - -/* COLUMN_PGMRECEIVERPARITYNAKPACKETSSENT + COLUMN_PGMRECEIVERSELECTIVENAKPACKETSSENT */ - case COLUMN_PGMRECEIVERNAKPACKETSSENT: - { - const unsigned nak_packets = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAK_PACKETS_SENT]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&nak_packets, sizeof(nak_packets) ); - } - break; - -/* FIXED: 0 */ - case COLUMN_PGMRECEIVERPARITYNAKPACKETSSENT: - { - const unsigned parity_naks = 0; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&parity_naks, sizeof(parity_naks) ); - } - break; - - case COLUMN_PGMRECEIVERSELECTIVENAKPACKETSSENT: - { - const unsigned nak_packets = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAK_PACKETS_SENT]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&nak_packets, sizeof(nak_packets) ); - } - break; - -/* FIXED: 0 */ - case COLUMN_PGMRECEIVERPARITYNAKSSENT: - { - const unsigned parity_naks = 0; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&parity_naks, sizeof(parity_naks) ); - } - break; - - case COLUMN_PGMRECEIVERSELECTIVENAKSSENT: - { - const unsigned naks_sent = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SENT]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&naks_sent, sizeof(naks_sent) ); - } - break; - -/* FIXED: 0 */ - case COLUMN_PGMRECEIVERPARITYNAKSRETRANSMITTED: - { - const unsigned parity_resent = 0; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&parity_resent, sizeof(parity_resent) ); - } - break; - - case COLUMN_PGMRECEIVERSELECTIVENAKSRETRANSMITTED: - { - const unsigned naks_resent = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_RETRANSMITTED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&naks_resent, sizeof(naks_resent) ); - } - break; - -/* COLUMN_PGMRECEIVERPARITYNAKSFAILED + COLUMN_PGMRECEIVERSELECTIVENAKSFAILED */ - case COLUMN_PGMRECEIVERNAKSFAILED: - { - const unsigned naks_failed = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_FAILED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&naks_failed, sizeof(naks_failed) ); - } - break; - -/* FIXED: 0 */ - case COLUMN_PGMRECEIVERPARITYNAKSFAILED: - { - const unsigned parity_failed = 0; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&parity_failed, sizeof(parity_failed) ); - } - break; - - case COLUMN_PGMRECEIVERSELECTIVENAKSFAILED: - { - const unsigned naks_failed = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_FAILED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&naks_failed, sizeof(naks_failed) ); - } - break; - - case COLUMN_PGMRECEIVERNAKSFAILEDRXWADVANCED: - { - const unsigned rxw_failed = peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_RXW_ADVANCED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&rxw_failed, sizeof(rxw_failed) ); - } - break; - - case COLUMN_PGMRECEIVERNAKSFALEDNCFRETRIESEXCEEDED: - { - const unsigned ncf_retries = peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_NCF_RETRIES_EXCEEDED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&ncf_retries, sizeof(ncf_retries) ); - } - break; - - case COLUMN_PGMRECEIVERNAKSFAILEDDATARETRIESEXCEEDED: - { - const unsigned data_retries = peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_DATA_RETRIES_EXCEEDED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&data_retries, sizeof(data_retries) ); - } - break; - -/* FIXED: 0 - absolutely no idea what this means */ - case COLUMN_PGMRECEIVERNAKSFAILEDGENEXPIRED: - { - const unsigned happy_pandas = 0; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&happy_pandas, sizeof(happy_pandas) ); - } - break; - - case COLUMN_PGMRECEIVERNAKFAILURESDELIVERED: - { - const unsigned delivered = peer->cumulative_stats[PGM_PC_RECEIVER_NAK_FAILURES_DELIVERED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&delivered, sizeof(delivered) ); - } - break; - -/* FIXED: 0 */ - case COLUMN_PGMRECEIVERPARITYNAKSSUPPRESSED: - { - const unsigned suppressed = 0; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&suppressed, sizeof(suppressed) ); - } - break; - - case COLUMN_PGMRECEIVERSELECTIVENAKSSUPPRESSED: - { - const unsigned suppressed = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&suppressed, sizeof(suppressed) ); - } - break; - - case COLUMN_PGMRECEIVERNAKERRORS: - { - const unsigned malformed_naks = peer->cumulative_stats[PGM_PC_RECEIVER_NAK_ERRORS]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&malformed_naks, sizeof(malformed_naks) ); - } - break; - -/* FIXED: 0 */ - case COLUMN_PGMRECEIVEROUTSTANDINGPARITYNAKS: - { - const unsigned outstanding_parity = 0; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&outstanding_parity, sizeof(outstanding_parity) ); - } - break; - - case COLUMN_PGMRECEIVEROUTSTANDINGSELECTIVENAKS: - { - const unsigned outstanding_selective = window->nak_backoff_queue.length + - window->wait_ncf_queue.length + - window->wait_data_queue.length; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&outstanding_selective, sizeof(outstanding_selective) ); - } - break; - - case COLUMN_PGMRECEIVERLASTACTIVITY: - { - union { - unsigned uint_value; - time_t time_t_value; - } last_activity; - pgm_time_since_epoch (&peer->last_packet, &last_activity.time_t_value); - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&last_activity.uint_value, sizeof(last_activity.uint_value) ); - } - break; - - case COLUMN_PGMRECEIVERNAKSVCTIMEMIN: - { - const unsigned min_repair_time = window->min_fill_time; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&min_repair_time, sizeof(min_repair_time) ); - } - break; - - case COLUMN_PGMRECEIVERNAKSVCTIMEMEAN: - { - const unsigned mean_repair_time = peer->cumulative_stats[PGM_PC_RECEIVER_NAK_SVC_TIME_MEAN]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&mean_repair_time, sizeof(mean_repair_time) ); - } - break; - - case COLUMN_PGMRECEIVERNAKSVCTIMEMAX: - { - const unsigned max_repair_time = window->max_fill_time; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&max_repair_time, sizeof(max_repair_time) ); - } - break; - - case COLUMN_PGMRECEIVERNAKFAILTIMEMIN: - { - const unsigned min_fail_time = peer->min_fail_time; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&min_fail_time, sizeof(min_fail_time) ); - } - break; - - case COLUMN_PGMRECEIVERNAKFAILTIMEMEAN: - { - const unsigned mean_fail_time = peer->cumulative_stats[PGM_PC_RECEIVER_NAK_FAIL_TIME_MEAN]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&mean_fail_time, sizeof(mean_fail_time) ); - } - break; - - case COLUMN_PGMRECEIVERNAKFAILTIMEMAX: - { - const unsigned max_fail_time = peer->max_fail_time; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&max_fail_time, sizeof(max_fail_time) ); - } - break; - - case COLUMN_PGMRECEIVERNAKTRANSMITMIN: - { - const unsigned min_transmit_count = window->min_nak_transmit_count; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&min_transmit_count, sizeof(min_transmit_count) ); - } - break; - - case COLUMN_PGMRECEIVERNAKTRANSMITMEAN: - { - const unsigned mean_transmit_count = peer->cumulative_stats[PGM_PC_RECEIVER_TRANSMIT_MEAN]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&mean_transmit_count, sizeof(mean_transmit_count) ); - } - break; - - case COLUMN_PGMRECEIVERNAKTRANSMITMAX: - { - const unsigned max_transmit_count = window->max_nak_transmit_count; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&max_transmit_count, sizeof(max_transmit_count) ); - } - break; - - case COLUMN_PGMRECEIVERACKSSENT: - { - const unsigned acks_sent = peer->cumulative_stats[PGM_PC_RECEIVER_ACKS_SENT]; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&acks_sent, sizeof(acks_sent) ); - } - break; - - case COLUMN_PGMRECEIVERRXWTRAIL: - { - const unsigned rxw_trail = window->rxw_trail; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&rxw_trail, sizeof(rxw_trail) ); - } - break; - - case COLUMN_PGMRECEIVERRXWLEAD: - { - const unsigned rxw_lead = window->lead; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&rxw_lead, sizeof(rxw_lead) ); - } - break; - -/* TODO: traps */ - case COLUMN_PGMRECEIVERNAKFAILURESLASTINTERVAL: - case COLUMN_PGMRECEIVERLASTINTERVALNAKFAILURES: - { - const unsigned failures = 0; - snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ - (const u_char*)&failures, sizeof(failures) ); - } - break; - - default: - snmp_log (LOG_ERR, "pgmReceiverTable_handler: unknown column.\n"); - break; - } - } - break; - - case MODE_SET_RESERVE1: - default: - snmp_log (LOG_ERR, "pgmReceiverTable_handler: unsupported mode.\n"); - break; - - } - - return SNMP_ERR_NOERROR; -} - -/* - * SNMP TRAPS - */ - -int -send_pgmStart_trap (void) -{ - pgm_debug ("send_pgmStart_trap ()"); - - netsnmp_variable_list *var_list = NULL; - static const oid pgmStart_oid[] = { 1,3,6,1,3,112,2,0,1 }; - -/* - * Set the snmpTrapOid.0 value - */ - snmp_varlist_add_variable (&var_list, - snmptrap_oid, OID_LENGTH( snmptrap_oid ), - ASN_OBJECT_ID, - (const u_char*)pgmStart_oid, sizeof(pgmStart_oid)); -/* - * Add any extra (optional) objects here - */ - -/* - * Send the trap to the list of configured destinations - * and clean up - */ - send_v2trap (var_list); - snmp_free_varbind (var_list); - return SNMP_ERR_NOERROR; -} - -int -send_pgmStop_trap (void) -{ - pgm_debug ("send_pgmStop_trap ()"); - - netsnmp_variable_list *var_list = NULL; - static const oid pgmStop_oid[] = { 1,3,6,1,3,112,2,0,2 }; - -/* - * Set the snmpTrapOid.0 value - */ - snmp_varlist_add_variable (&var_list, - snmptrap_oid, OID_LENGTH(snmptrap_oid), - ASN_OBJECT_ID, - (const u_char*)pgmStop_oid, sizeof(pgmStop_oid)); - - -/* - * Add any extra (optional) objects here - */ - -/* - * Send the trap to the list of configured destinations - * and clean up - */ - send_v2trap (var_list); - snmp_free_varbind (var_list); - return SNMP_ERR_NOERROR; -} - -int -send_pgmNewSourceTrap_trap (void) -{ - pgm_debug ("send_pgmNewSourceTrap_trap ()"); - - netsnmp_variable_list *var_list = NULL; - static const oid pgmNewSourceTrap_oid[] = { 1,3,6,1,3,112,2,0,3 }; - static const oid pgmSourceSourceGsi_oid[] = { 1,3,6,1,3,112,1,2,100,2,1,6, /* insert index here */ }; - static const oid pgmSourceSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,2,100,2,1,7, /* insert index here */ }; - -/* - * Set the snmpTrapOid.0 value - */ - snmp_varlist_add_variable (&var_list, - snmptrap_oid, OID_LENGTH(snmptrap_oid), - ASN_OBJECT_ID, - (const u_char*)pgmNewSourceTrap_oid, sizeof(pgmNewSourceTrap_oid)); -/* - * Add any objects from the trap definition - */ - snmp_varlist_add_variable (&var_list, - pgmSourceSourceGsi_oid, OID_LENGTH(pgmSourceSourceGsi_oid), - ASN_OCTET_STR, -/* Set an appropriate value for pgmSourceSourceGsi */ - NULL, 0); - snmp_varlist_add_variable (&var_list, - pgmSourceSourcePortNumber_oid, OID_LENGTH(pgmSourceSourcePortNumber_oid), - ASN_UNSIGNED, -/* Set an appropriate value for pgmSourceSourcePortNumber */ - NULL, 0); -/* - * Add any extra (optional) objects here - */ - -/* - * Send the trap to the list of configured destinations - * and clean up - */ - send_v2trap (var_list); - snmp_free_varbind (var_list); - return SNMP_ERR_NOERROR; -} - -int -send_pgmClosedSourceTrap_trap (void) -{ - pgm_debug ("send_pgmClosedSourceTrap_trap ()"); - - netsnmp_variable_list *var_list = NULL; - static const oid pgmClosedSourceTrap_oid[] = { 1,3,6,1,3,112,2,0,4 }; - static const oid pgmSourceSourceGsi_oid[] = { 1,3,6,1,3,112,1,2,100,2,1,6, /* insert index here */ }; - static const oid pgmSourceSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,2,100,2,1,7, /* insert index here */ }; - -/* - * Set the snmpTrapOid.0 value - */ - snmp_varlist_add_variable (&var_list, - snmptrap_oid, OID_LENGTH(snmptrap_oid), - ASN_OBJECT_ID, - (const u_char*)pgmClosedSourceTrap_oid, sizeof(pgmClosedSourceTrap_oid)); -/* - * Add any objects from the trap definition - */ - snmp_varlist_add_variable (&var_list, - pgmSourceSourceGsi_oid, OID_LENGTH(pgmSourceSourceGsi_oid), - ASN_OCTET_STR, -/* Set an appropriate value for pgmSourceSourceGsi */ - NULL, 0); - snmp_varlist_add_variable (&var_list, - pgmSourceSourcePortNumber_oid, OID_LENGTH(pgmSourceSourcePortNumber_oid), - ASN_UNSIGNED, -/* Set an appropriate value for pgmSourceSourcePortNumber */ - NULL, 0); -/* - * Add any extra (optional) objects here - */ - -/* - * Send the trap to the list of configured destinations - * and clean up - */ - send_v2trap (var_list); - snmp_free_varbind (var_list); - return SNMP_ERR_NOERROR; -} - -int -send_pgmNewReceiverTrap_trap (void) -{ - pgm_debug ("send_pgmNewReceiverTrap_trap ()"); - - netsnmp_variable_list *var_list = NULL; - static const oid pgmNewReceiverTrap_oid[] = { 1,3,6,1,3,112,2,0,5 }; - static const oid pgmReceiverSourceGsi_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,8, /* insert index here */ }; - static const oid pgmReceiverSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,9, /* insert index here */ }; - static const oid pgmReceiverUniqueInstance_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,10, /* insert index here */ }; - -/* - * Set the snmpTrapOid.0 value - */ - snmp_varlist_add_variable (&var_list, - snmptrap_oid, OID_LENGTH(snmptrap_oid), - ASN_OBJECT_ID, - (const u_char*)pgmNewReceiverTrap_oid, sizeof(pgmNewReceiverTrap_oid)); -/* - * Add any objects from the trap definition - */ - snmp_varlist_add_variable (&var_list, - pgmReceiverSourceGsi_oid, OID_LENGTH(pgmReceiverSourceGsi_oid), - ASN_OCTET_STR, -/* Set an appropriate value for pgmReceiverSourceGsi */ - NULL, 0); - snmp_varlist_add_variable (&var_list, - pgmReceiverSourcePortNumber_oid, OID_LENGTH(pgmReceiverSourcePortNumber_oid), - ASN_UNSIGNED, -/* Set an appropriate value for pgmReceiverSourcePortNumber */ - NULL, 0); - snmp_varlist_add_variable (&var_list, - pgmReceiverUniqueInstance_oid, OID_LENGTH(pgmReceiverUniqueInstance_oid), - ASN_UNSIGNED, -/* Set an appropriate value for pgmReceiverUniqueInstance */ - NULL, 0); -/* - * Add any extra (optional) objects here - */ - -/* - * Send the trap to the list of configured destinations - * and clean up - */ - send_v2trap (var_list); - snmp_free_varbind (var_list); - return SNMP_ERR_NOERROR; -} - -int -send_pgmClosedReceiverTrap_trap (void) -{ - pgm_debug ("send_pgmClosedReceiverTrap_trap ()"); - - netsnmp_variable_list *var_list = NULL; - static const oid pgmClosedReceiverTrap_oid[] = { 1,3,6,1,3,112,2,0,6 }; - static const oid pgmReceiverSourceGsi_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,8, /* insert index here */ }; - static const oid pgmReceiverSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,9, /* insert index here */ }; - static const oid pgmReceiverUniqueInstance_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,10, /* insert index here */ }; - -/* - * Set the snmpTrapOid.0 value - */ - snmp_varlist_add_variable (&var_list, - snmptrap_oid, OID_LENGTH(snmptrap_oid), - ASN_OBJECT_ID, - (const u_char*)pgmClosedReceiverTrap_oid, sizeof(pgmClosedReceiverTrap_oid)); -/* - * Add any objects from the trap definition - */ - snmp_varlist_add_variable (&var_list, - pgmReceiverSourceGsi_oid, OID_LENGTH(pgmReceiverSourceGsi_oid), - ASN_OCTET_STR, -/* Set an appropriate value for pgmReceiverSourceGsi */ - NULL, 0); - snmp_varlist_add_variable (&var_list, - pgmReceiverSourcePortNumber_oid, OID_LENGTH(pgmReceiverSourcePortNumber_oid), - ASN_UNSIGNED, -/* Set an appropriate value for pgmReceiverSourcePortNumber */ - NULL, 0); - snmp_varlist_add_variable (&var_list, - pgmReceiverUniqueInstance_oid, OID_LENGTH(pgmReceiverUniqueInstance_oid), - ASN_UNSIGNED, -/* Set an appropriate value for pgmReceiverUniqueInstance */ - NULL, 0); -/* - * Add any extra (optional) objects here - */ - -/* - * Send the trap to the list of configured destinations - * and clean up - */ - send_v2trap (var_list); - snmp_free_varbind (var_list); - return SNMP_ERR_NOERROR; -} - -int -send_pgmNakFailuresTrap_trap (void) -{ - pgm_debug ("send_pgmNakFailuresTrap_trap ()"); - - netsnmp_variable_list *var_list = NULL; - static const oid pgmNakFailuresTrap_oid[] = { 1,3,6,1,3,112,2,0,7 }; - static const oid pgmReceiverSourceGsi_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,8, /* insert index here */ }; - static const oid pgmReceiverSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,9, /* insert index here */ }; - static const oid pgmReceiverUniqueInstance_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,10, /* insert index here */ }; - static const oid pgmReceiverNakFailureThresholdTimer_oid[] = { 1,3,6,1,3,112,1,3,100,3,1,14, /* insert index here */ }; - static const oid pgmReceiverNakFailureThreshold_oid[] = { 1,3,6,1,3,112,1,3,100,3,1,15, /* insert index here */ }; - static const oid pgmReceiverNakFailuresLastInterval_oid[] = { 1,3,6,1,3,112,1,3,100,4,1,56, /* insert index here */ }; - static const oid pgmReceiverLastIntervalNakFailures_oid[] = { 1,3,6,1,3,112,1,3,100,4,1,57, /* insert index here */ }; - -/* - * Set the snmpTrapOid.0 value - */ - snmp_varlist_add_variable (&var_list, - snmptrap_oid, OID_LENGTH(snmptrap_oid), - ASN_OBJECT_ID, - (const u_char*)pgmNakFailuresTrap_oid, sizeof(pgmNakFailuresTrap_oid)); -/* - * Add any objects from the trap definition - */ - snmp_varlist_add_variable (&var_list, - pgmReceiverSourceGsi_oid, OID_LENGTH(pgmReceiverSourceGsi_oid), - ASN_OCTET_STR, -/* Set an appropriate value for pgmReceiverSourceGsi */ - NULL, 0); - snmp_varlist_add_variable (&var_list, - pgmReceiverSourcePortNumber_oid, OID_LENGTH(pgmReceiverSourcePortNumber_oid), - ASN_UNSIGNED, -/* Set an appropriate value for pgmReceiverSourcePortNumber */ - NULL, 0); - snmp_varlist_add_variable (&var_list, - pgmReceiverUniqueInstance_oid, OID_LENGTH(pgmReceiverUniqueInstance_oid), - ASN_UNSIGNED, -/* Set an appropriate value for pgmReceiverUniqueInstance */ - NULL, 0); - snmp_varlist_add_variable (&var_list, - pgmReceiverNakFailureThresholdTimer_oid, OID_LENGTH(pgmReceiverNakFailureThresholdTimer_oid), - ASN_UNSIGNED, -/* Set an appropriate value for pgmReceiverNakFailureThresholdTimer */ - NULL, 0); - snmp_varlist_add_variable (&var_list, - pgmReceiverNakFailureThreshold_oid, OID_LENGTH(pgmReceiverNakFailureThreshold_oid), - ASN_UNSIGNED, -/* Set an appropriate value for pgmReceiverNakFailureThreshold */ - NULL, 0); - snmp_varlist_add_variable (&var_list, - pgmReceiverNakFailuresLastInterval_oid, OID_LENGTH(pgmReceiverNakFailuresLastInterval_oid), - ASN_COUNTER, -/* Set an appropriate value for pgmReceiverNakFailuresLastInterval */ - NULL, 0); - snmp_varlist_add_variable (&var_list, - pgmReceiverLastIntervalNakFailures_oid, OID_LENGTH(pgmReceiverLastIntervalNakFailures_oid), - ASN_COUNTER, -/* Set an appropriate value for pgmReceiverLastIntervalNakFailures */ - NULL, 0); -/* - * Add any extra (optional) objects here - */ - -/* - * Send the trap to the list of configured destinations - * and clean up - */ - send_v2trap (var_list); - snmp_free_varbind (var_list); - return SNMP_ERR_NOERROR; -} - -int -send_pgmNewDlrSourceTrap_trap (void) -{ - pgm_debug ("send_pgmNewDlrSourceTrap_trap ()"); - - netsnmp_variable_list *var_list = NULL; - static const oid pgmNewDlrSourceTrap_oid[] = { 1,3,6,1,3,112,2,0,8 }; - static const oid pgmDlrSourceSourceGsi_oid[] = { 1,3,6,1,3,112,1,4,100,2,1,4, /* insert index here */ }; - static const oid pgmDlrSourceSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,4,100,2,1,5, /* insert index here */ }; - -/* - * Set the snmpTrapOid.0 value - */ - snmp_varlist_add_variable (&var_list, - snmptrap_oid, OID_LENGTH(snmptrap_oid), - ASN_OBJECT_ID, - (const u_char*)pgmNewDlrSourceTrap_oid, sizeof(pgmNewDlrSourceTrap_oid)); -/* - * Add any objects from the trap definition - */ - snmp_varlist_add_variable (&var_list, - pgmDlrSourceSourceGsi_oid, OID_LENGTH(pgmDlrSourceSourceGsi_oid), - ASN_OCTET_STR, -/* Set an appropriate value for pgmDlrSourceSourceGsi */ - NULL, 0); - snmp_varlist_add_variable (&var_list, - pgmDlrSourceSourcePortNumber_oid, OID_LENGTH(pgmDlrSourceSourcePortNumber_oid), - ASN_UNSIGNED, -/* Set an appropriate value for pgmDlrSourceSourcePortNumber */ - NULL, 0); -/* - * Add any extra (optional) objects here - */ - -/* - * Send the trap to the list of configured destinations - * and clean up - */ - send_v2trap (var_list); - snmp_free_varbind (var_list); - return SNMP_ERR_NOERROR; -} - -int -send_pgmClosedDlrSourceTrap_trap (void) -{ - pgm_debug ("send_pgmClosedDlrSourceTrap_trap ()"); - - netsnmp_variable_list *var_list = NULL; - static const oid pgmClosedDlrSourceTrap_oid[] = { 1,3,6,1,3,112,2,0,9 }; - static const oid pgmDlrSourceSourceGsi_oid[] = { 1,3,6,1,3,112,1,4,100,2,1,4, /* insert index here */ }; - static const oid pgmDlrSourceSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,4,100,2,1,5, /* insert index here */ }; - -/* - * Set the snmpTrapOid.0 value - */ - snmp_varlist_add_variable (&var_list, - snmptrap_oid, OID_LENGTH(snmptrap_oid), - ASN_OBJECT_ID, - (const u_char*)pgmClosedDlrSourceTrap_oid, sizeof(pgmClosedDlrSourceTrap_oid)); - -/* - * Add any objects from the trap definition - */ - snmp_varlist_add_variable (&var_list, - pgmDlrSourceSourceGsi_oid, OID_LENGTH(pgmDlrSourceSourceGsi_oid), - ASN_OCTET_STR, -/* Set an appropriate value for pgmDlrSourceSourceGsi */ - NULL, 0); - snmp_varlist_add_variable (&var_list, - pgmDlrSourceSourcePortNumber_oid, OID_LENGTH(pgmDlrSourceSourcePortNumber_oid), - ASN_UNSIGNED, -/* Set an appropriate value for pgmDlrSourceSourcePortNumber */ - NULL, 0); -/* - * Add any extra (optional) objects here - */ - -/* - * Send the trap to the list of configured destinations - * and clean up - */ - send_v2trap (var_list); - snmp_free_varbind (var_list); - return SNMP_ERR_NOERROR; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/pgmMIB_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/pgmMIB_unittest.c deleted file mode 100644 index 4cb4672..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/pgmMIB_unittest.c +++ /dev/null @@ -1,257 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for PGM MIB routines. - * - * Copyright (c) 2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include -#include -#include - -#include - - -/* mock state */ - -static GStaticRWLock mock_pgm_transport_list_lock = G_STATIC_RW_LOCK_INIT; -static GSList* mock_pgm_transport_list = NULL; - - -/* mock functions for external references */ - -static -netsnmp_handler_registration* -mock_netsnmp_create_handler_registration ( - const char* name, - Netsnmp_Node_Handler* handler_access_method, - oid* reg_oid, - size_t reg_oid_len, - int modes - ) -{ - netsnmp_handler_registration* handler = g_malloc0 (sizeof(netsnmp_handler_registration)); - return handler; -} - -static -void -mock_netsnmp_handler_registration_free ( - netsnmp_handler_registration* handler - ) -{ - g_assert (NULL != handler); - g_free (handler); -} - -static -void -mock_netsnmp_table_helper_add_indexes ( - netsnmp_table_registration_info* tinfo, - ... - ) -{ -} - -static -int -mock_netsnmp_register_table_iterator ( - netsnmp_handler_registration* reginfo, - netsnmp_iterator_info* iinfo - ) -{ - return MIB_REGISTERED_OK; -} - -static -int -mock_netsnmp_set_request_error ( - netsnmp_agent_request_info* reqinfo, - netsnmp_request_info* request, - int error_value - ) -{ - return 0; -} - -static -void* -mock_netsnmp_extract_iterator_context ( - netsnmp_request_info* reqinfo - ) -{ - return (void*)0x1; -} - -static -netsnmp_table_request_info* -mock_netsnmp_extract_table_info ( - netsnmp_request_info* reqinfo - ) -{ - return NULL; -} - -static -int -mock_snmp_set_var_typed_value ( - netsnmp_variable_list* newvar, - u_char type, - const u_char* val_str, - size_t val_len - ) -{ - return 0; -} - -static -netsnmp_variable_list* -mock_snmp_varlist_add_variable ( - netsnmp_variable_list** varlist, - const oid* oid, - size_t name_length, - u_char type, - const u_char* value, - size_t len - ) -{ - return NULL; -} - -static -void -mock_snmp_free_varbind ( - netsnmp_variable_list* var - ) -{ -} - -static -void -mock_snmp_free_var ( - netsnmp_variable_list* var - ) -{ -} - -static -int -mock_snmp_log ( - int priority, - const char* format, - ... - ) -{ - return 0; -} - -static -void -mock_send_v2trap ( - netsnmp_variable_list* var - ) -{ -} - -/** time module */ - -static -void -mock_pgm_time_since_epoch ( - pgm_time_t* pgm_time_t_time, - time_t* time_t_time - ) -{ - *time_t_time = pgm_to_secs (*pgm_time_t_time + 0); -} - - -#define netsnmp_create_handler_registration mock_netsnmp_create_handler_registration -#define netsnmp_handler_registration_free mock_netsnmp_handler_registration_free -#define netsnmp_table_helper_add_indexes mock_netsnmp_table_helper_add_indexes -#define netsnmp_register_table_iterator mock_netsnmp_register_table_iterator -#define netsnmp_set_request_error mock_netsnmp_set_request_error -#define netsnmp_extract_iterator_context mock_netsnmp_extract_iterator_context -#define netsnmp_extract_table_info mock_netsnmp_extract_table_info -#define snmp_set_var_typed_value mock_snmp_set_var_typed_value -#define snmp_varlist_add_variable mock_snmp_varlist_add_variable -#define snmp_free_varbind mock_snmp_free_varbind -#define snmp_free_var mock_snmp_free_var -#define snmp_log mock_snmp_log -#define send_v2trap mock_send_v2trap -#define pgm_transport_list mock_pgm_transport_list -#define pgm_transport_list_lock mock_pgm_transport_list_lock -#define pgm_time_since_epoch mock_pgm_time_since_epoch - -#define PGMMIB_DEBUG -#include "pgmMIB.c" - - -/* target: - * gboolean - * pgm_mib_init ( - * GError** error - * ) - */ - -START_TEST (test_init_pass_001) -{ - GError* err = NULL; - fail_unless (TRUE == pgm_mib_init (&err)); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_init = tcase_create ("init"); - suite_add_tcase (s, tc_init); - tcase_add_test (tc_init, test_init_pass_001); - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/plan.txt b/3rdparty/openpgm-svn-r1085/pgm/plan.txt deleted file mode 100644 index b1747ae..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/plan.txt +++ /dev/null @@ -1,238 +0,0 @@ -pgmdump -------- - -View all packets like tcpdump, but updated to full spec and allow dump of payload. - - -pgmtop ------- - -Dump realtime packet statistics in a ncurses display, mix of top/htop/netop. - - -basic_send ----------- - -Send an ODATA packet and terminate. - -Accept string payload and network parameters on command line. - -Send to multicast or send to unicast AFI. - -IPv4/6. - -Define optional session start, late join tags. - - -spm_idle --------- - -Idle in an event loop sending out SPM packets. - - -stream_send ------------ - -Send a constant stream of ODATA and SPM packets. - - -basic_http ----------- - -Simple embedded web server - - -basic_recv ----------- - -Listen to packets indicating data loss, view details through web interface. - - -basic_container ---------------- - -Test performance of glib containers for fast allocating for a dynamic transmit window. - - -basic_txw ---------- - -Test performance of a basic transmit window implementation. - - -nak_txw -------- - -Test performance of random access to packets inside the window. - - -stream_send_with_nak --------------------- - -Respond to NAK's with RDATA. - - -basic_recv_with_nak -------------------- - -Listen to packets and send NAK's to rebuild data. - - -dumpif ------- - -Display all IP based interfaces and basic details. - - -testif ------- - -Test various combinations of network specification. - - -sw_calc -------- - -Basic calculation tests of wrap-around sliding windows and a leading edge. - - -basic_recv_with_rxw -------------------- - -Listen to packets buffered with a receive window. - - -test_cpu_timers ---------------- - -Calculate drift between processors, cores, and hyper-threads. - - -pgmsend --------- - -basic_send updated to use transmit window. - - -pgmrecv --------- - -basic_recv_with_rxw without web interface, primary displays messages from pgmsend. - - -syncrecv --------- - -pgmrecv implemented outside GLib with a synchronous coding paradigm. - - -pgmping -------- - -Dual mode: one to send fixed messages like pgmsend and listen for response, two to listen for -messages and reply. - - -block_send ----------- - -Send APDUs over ODATA. - - -(pgmrecv can receive APDUs) - -test_rs -------- - -Test 8-bit symbol Reed Solomon encoding and decoding with errors & erasures. - -test_fec --------- - -Test fec creation and recovery. - - -send_with_fec --------------------- - -Send APDUs over ODATA with FEC. - - - -Scenarios to reproduce -********************** - -- Packet loss in stream causing NAK generation. -- Link saturation in sending causing API feedback. -- Link peak stable speed. -- Maxium NAK generation to determine NCF/RDATA throughput. -- Corrupt packets with invalid IP checksum (? generate IP HDR in sender) -- Corrupt packets with invalid PGM checksum. -- Invalid packet values. -- NAK to NCF latency. -- NAK to RDATA latency. -- Publish bandwidth: total, per packet type, payload, per recipient (?) -- Subscribe bandwidth: total, per packet type, payload, per publisher (?) -- Restarting a session with similar or dissimilar sequence numbering. - -Outstanding questions -********************* - -- Is it faster to use chunks containing multiple packets instead of one MTU - per packet. Does aligning with system page size matter? -- Can g_timers be dropped easily for gettimeofday and avoid floating point math? Possible - to pass timer upstream with contiguous data for easy access. -- Can time evaluation be dropped to at most once per main loop event? -- Does code work 32 bit and is it optimal? -- Should trash stacks be monitored and controlled externally? For example, clearing - up after bursts or administrative control. -- Should trash stacks have a upper limit to then free to the slice allocator? -- Should lost packets be managed as ranges or individual sequence numbers, how - does each method affect performance? - -* The initial draft of PGM included OPT_RANGE option for NAKs to specify a range of lost - packets, this was replaced in the final draft with NAK lists. Some research hints that - ranges are suitable: - - http://www.isoc.org/inet2001/CD_proceedings/T54/T54.htm - http://tools.ietf.org/html/draft-speakman-pgm-spec-01 - -- Are place holders necessary? Can state timeouts be managed without a per sequence number - object? For example by the next data object, or an extra object for an ncf extended window: - note that nak packet generation should easily dwarfs time spent unless advantage is taken - of the additional 62 naks in a opt_nak_list. Caution has to be taken with the cost of - splitting a range when a packet is inserted in the middle, although idealy it should - be sequential from the trailing edge. -- Is it better to have send sockets per transport, or shared, bound to each interface? -- Cost of sharing state helper lists between receive windows? On culling a peer the lists - have to be purged. Saves iterating the hash list of receivers. -- Encapsulated UDP lists two ports, 3305 for broadcast, 3306 for unicast, how is this - supposed to map to regular PGM, and why the split? - -Basic TODO list -*************** - -- Ensure standardised error handling and status reporting. -- Implement mechanism for data-loss feedback. -- OPT_SYN & OPT_FIN on sending side. -- Shared trash stacks between multiple receive windows (contexts). -- Shared trash stacks between transmit and receive windows. -- FEC: compatibility with SmartPGM FEC, MS FEC? -- NAK backoff learning. -- Full conformance testing. (nak backoffs remaining) -- Unit testing. -- System testing with valgrind. -- Performance testing with oprofile. -- Basic DLR. -- Implement PGM sender (only) support thread? -- eventfd instead of pipes for recent Linux kernels. - -Optionals -********* - -- Some form of broadcast statistics for passive monitoring. -- (fix) BCH Reed-Solomon codec as possibly faster alternative, albeit incompatible with Microsoft. -- Recommendations as per the RMT working group of the IETF for AL-FEC codecs: Raptor codes, LDPC- - staircase and LDPC-triangle codes. -- XDR based messaging format as example of binary encoded messaging. - diff --git a/3rdparty/openpgm-svn-r1085/pgm/queue.c b/3rdparty/openpgm-svn-r1085/pgm/queue.c deleted file mode 100644 index 351c7ef..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/queue.c +++ /dev/null @@ -1,110 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable double-ended queue. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - - -//#define QUEUE_DEBUG - -bool -pgm_queue_is_empty ( - const pgm_queue_t*const queue - ) -{ - pgm_return_val_if_fail (queue != NULL, TRUE); - - return queue->head == NULL; -} - -void -pgm_queue_push_head_link ( - pgm_queue_t* restrict queue, - pgm_list_t* restrict head_link - ) -{ - pgm_return_if_fail (queue != NULL); - pgm_return_if_fail (head_link != NULL); - pgm_return_if_fail (head_link->prev == NULL); - pgm_return_if_fail (head_link->next == NULL); - - head_link->next = queue->head; - if (queue->head) - queue->head->prev = head_link; - else - queue->tail = head_link; - queue->head = head_link; - queue->length++; -} - -pgm_list_t* -pgm_queue_pop_tail_link ( - pgm_queue_t* queue - ) -{ - pgm_return_val_if_fail (queue != NULL, NULL); - - if (queue->tail) - { - pgm_list_t *node = queue->tail; - - queue->tail = node->prev; - if (queue->tail) - { - queue->tail->next = NULL; - node->prev = NULL; - } - else - queue->head = NULL; - queue->length--; - - return node; - } - - return NULL; -} - -pgm_list_t* -pgm_queue_peek_tail_link ( - pgm_queue_t* queue - ) -{ - pgm_return_val_if_fail (queue != NULL, NULL); - - return queue->tail; -} - -void -pgm_queue_unlink ( - pgm_queue_t* restrict queue, - pgm_list_t* restrict target_link - ) -{ - pgm_return_if_fail (queue != NULL); - pgm_return_if_fail (target_link != NULL); - - if (target_link == queue->tail) - queue->tail = queue->tail->prev; - - queue->head = pgm_list_remove_link (queue->head, target_link); - queue->length--; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/rand.c b/3rdparty/openpgm-svn-r1085/pgm/rand.c deleted file mode 100644 index 91b71eb..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/rand.c +++ /dev/null @@ -1,137 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable weak pseudo-random generator. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _WIN32 -# include -# include -#endif -#include - - -//#define RAND_DEBUG - - -/* locals */ - -static pgm_rand_t global_rand = { .seed = 0 }; -static volatile uint32_t rand_ref_count = 0; -static pgm_mutex_t rand_mutex; - - -void -pgm_rand_init (void) -{ - if (pgm_atomic_exchange_and_add32 (&rand_ref_count, 1) > 0) - return; - - pgm_mutex_init (&rand_mutex); -} - -void -pgm_rand_shutdown (void) -{ - pgm_return_if_fail (pgm_atomic_read32 (&rand_ref_count) > 0); - - if (pgm_atomic_exchange_and_add32 (&rand_ref_count, (uint32_t)-1) != 1) - return; - - pgm_mutex_free (&rand_mutex); -} - -void -pgm_rand_create ( - pgm_rand_t* new_rand - ) -{ -/* pre-conditions */ - pgm_assert (NULL != new_rand); - -#ifndef _WIN32 -/* attempt to read seed from kernel - */ - FILE* fp; - do { - fp = fopen ("/dev/urandom", "rb"); - } while (PGM_UNLIKELY(EINTR == errno)); - if (fp) { - size_t items_read; - do { - items_read = fread (&new_rand->seed, sizeof(new_rand->seed), 1, fp); - } while (PGM_UNLIKELY(EINTR == errno)); - fclose (fp); - if (1 == items_read) - return; - } -#endif /* !_WIN32 */ - const pgm_time_t now = pgm_time_update_now(); - new_rand->seed = (uint32_t)pgm_to_msecs (now); -} - -/* derived from POSIX.1-2001 example implementation of rand() - */ - -uint32_t -pgm_rand_int ( - pgm_rand_t* r - ) -{ -/* pre-conditions */ - pgm_assert (NULL != r); - - r->seed = r->seed * 1103515245 + 12345; - return r->seed; -} - -int32_t -pgm_rand_int_range ( - pgm_rand_t* r, - int32_t begin, - int32_t end - ) -{ -/* pre-conditions */ - pgm_assert (NULL != r); - - return begin + pgm_rand_int (r) % (end - begin); -} - -uint32_t -pgm_random_int (void) -{ - pgm_mutex_lock (&rand_mutex); - if (PGM_UNLIKELY(!global_rand.seed)) - pgm_rand_create (&global_rand); - const uint32_t rand_value = pgm_rand_int (&global_rand); - pgm_mutex_unlock (&rand_mutex); - return rand_value; -} - -int32_t -pgm_random_int_range ( - int32_t begin, - int32_t end - ) -{ - const uint32_t rand_value = pgm_random_int(); - return begin + rand_value % (end - begin); -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/rate_control.c b/3rdparty/openpgm-svn-r1085/pgm/rate_control.c deleted file mode 100644 index 2baceeb..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/rate_control.c +++ /dev/null @@ -1,158 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Rate regulation. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - - -/* create machinery for rate regulation. - * the rate_per_sec is ammortized over millisecond time periods. - * - * NB: bucket MUST be memset 0 before calling. - */ - -void -pgm_rate_create ( - pgm_rate_t* bucket, - const ssize_t rate_per_sec, /* 0 = disable */ - const size_t iphdr_len, - const uint16_t max_tpdu - ) -{ -/* pre-conditions */ - pgm_assert (NULL != bucket); - pgm_assert (rate_per_sec >= max_tpdu); - - bucket->rate_per_sec = rate_per_sec; - bucket->iphdr_len = iphdr_len; - bucket->last_rate_check = pgm_time_update_now (); -/* pre-fill bucket */ - if ((rate_per_sec / 1000) >= max_tpdu) { - bucket->rate_per_msec = bucket->rate_per_sec / 1000; - bucket->rate_limit = bucket->rate_per_msec; - } else { - bucket->rate_limit = bucket->rate_per_sec; - } - pgm_spinlock_init (&bucket->spinlock); -} - -void -pgm_rate_destroy ( - pgm_rate_t* bucket - ) -{ -/* pre-conditions */ - pgm_assert (NULL != bucket); - - pgm_spinlock_free (&bucket->spinlock); -} - -/* check bit bucket whether an operation can proceed or should wait. - * - * returns TRUE when leaky bucket permits unless non-blocking flag is set. - * returns FALSE if operation should block and non-blocking flag is set. - */ - -bool -pgm_rate_check ( - pgm_rate_t* bucket, - const size_t data_size, - const bool is_nonblocking - ) -{ - int new_rate_limit; - -/* pre-conditions */ - pgm_assert (NULL != bucket); - pgm_assert (data_size > 0); - - if (0 == bucket->rate_per_sec) - return TRUE; - - pgm_spinlock_lock (&bucket->spinlock); - pgm_time_t now = pgm_time_update_now(); - pgm_time_t time_since_last_rate_check = now - bucket->last_rate_check; - - if (bucket->rate_per_msec) - { - if (time_since_last_rate_check > pgm_msecs(1)) - new_rate_limit = bucket->rate_per_msec; - else { - new_rate_limit = bucket->rate_limit + ((bucket->rate_per_msec * time_since_last_rate_check) / 1000UL); - if (new_rate_limit > bucket->rate_per_msec) - new_rate_limit = bucket->rate_per_msec; - } - } - else - { - if (time_since_last_rate_check > pgm_secs(1)) - new_rate_limit = bucket->rate_per_sec; - else { - new_rate_limit = bucket->rate_limit + ((bucket->rate_per_sec * time_since_last_rate_check) / 1000000UL); - if (new_rate_limit > bucket->rate_per_sec) - new_rate_limit = bucket->rate_per_sec; - } - } - - new_rate_limit -= ( bucket->iphdr_len + data_size ); - if (is_nonblocking && new_rate_limit < 0) { - pgm_spinlock_unlock (&bucket->spinlock); - return FALSE; - } - - bucket->rate_limit = new_rate_limit; - bucket->last_rate_check = now; - if (bucket->rate_limit < 0) { - int sleep_amount; - do { - pgm_thread_yield(); - now = pgm_time_update_now(); - time_since_last_rate_check = now - bucket->last_rate_check; - sleep_amount = pgm_to_secs (bucket->rate_per_sec * time_since_last_rate_check); - } while (sleep_amount + bucket->rate_limit < 0); - bucket->rate_limit += sleep_amount; - bucket->last_rate_check = now; - } - pgm_spinlock_unlock (&bucket->spinlock); - return TRUE; -} - -pgm_time_t -pgm_rate_remaining ( - pgm_rate_t* bucket, - const size_t n - ) -{ -/* pre-conditions */ - pgm_assert (NULL != bucket); - - if (PGM_UNLIKELY(0 == bucket->rate_per_sec)) - return 0; - - pgm_spinlock_lock (&bucket->spinlock); - const pgm_time_t now = pgm_time_update_now(); - const pgm_time_t time_since_last_rate_check = now - bucket->last_rate_check; - const int bucket_bytes = bucket->rate_limit + pgm_to_secs (bucket->rate_per_sec * time_since_last_rate_check) - n; - pgm_spinlock_unlock (&bucket->spinlock); - - return bucket_bytes >= 0 ? 0 : (bucket->rate_per_sec / -bucket_bytes); -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/rate_control_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/rate_control_unittest.c deleted file mode 100644 index 7da5128..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/rate_control_unittest.c +++ /dev/null @@ -1,241 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for rate regulation. - * - * Copyright (c) 2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define __STDC_FORMAT_MACROS -#include -#include -#include -#include -#include -#include - - -/* mock state */ - - -#define pgm_time_now mock_pgm_time_now -#define pgm_time_update_now mock_pgm_time_update_now - -#define RATE_CONTROL_DEBUG -#include "rate_control.c" - -static pgm_time_t mock_pgm_time_now = 0x1; -static pgm_time_t _mock_pgm_time_update_now (void); -pgm_time_update_func mock_pgm_time_update_now = _mock_pgm_time_update_now; - - -/* mock functions for external references */ - -size_t -pgm_transport_pkt_offset2 ( - const bool can_fragment, - const bool use_pgmcc - ) -{ - return 0; -} - -static -pgm_time_t -_mock_pgm_time_update_now (void) -{ - g_debug ("mock_pgm_time_now: %" PGM_TIME_FORMAT, mock_pgm_time_now); - return mock_pgm_time_now; -} - - -/* target: - * void - * pgm_rate_create ( - * pgm_rate_t* bucket_, - * const ssize_t rate_per_sec, - * const size_t iphdr_len, - * const uint16_t max_tpdu - * ) - */ - -START_TEST (test_create_pass_001) -{ - pgm_rate_t rate; - memset (&rate, 0, sizeof(rate)); - pgm_rate_create (&rate, 100*1000, 10, 1500); -} -END_TEST - -START_TEST (test_create_fail_001) -{ - pgm_rate_create (NULL, 0, 0, 1500); - fail ("reached"); -} -END_TEST - -/* target: - * void - * pgm_rate_destroy ( - * pgm_rate_t* bucket - * ) - */ - -START_TEST (test_destroy_pass_001) -{ - pgm_rate_t rate; - memset (&rate, 0, sizeof(rate)); - pgm_rate_create (&rate, 100*1000, 10, 1500); - pgm_rate_destroy (&rate); -} -END_TEST - -START_TEST (test_destroy_fail_001) -{ - pgm_rate_destroy (NULL); - fail ("reached"); -} -END_TEST - -/* target: - * bool - * pgm_rate_check ( - * pgm_rate_t* bucket, - * const size_t data_size, - * const bool is_nonblocking - * ) - * - * 001: should use seconds resolution to allow 2 packets through then fault. - */ - -START_TEST (test_check_pass_001) -{ - pgm_rate_t rate; - memset (&rate, 0, sizeof(rate)); - pgm_rate_create (&rate, 2*1010, 10, 1500); - mock_pgm_time_now += pgm_secs(2); - fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); - fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); - fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); - pgm_rate_destroy (&rate); -} -END_TEST - -START_TEST (test_check_fail_001) -{ - pgm_rate_check (NULL, 1000, FALSE); - fail ("reached"); -} -END_TEST - -/* 002: assert that only one packet should pass through small bucket - */ - -START_TEST (test_check_pass_002) -{ - pgm_rate_t rate; - memset (&rate, 0, sizeof(rate)); - pgm_rate_create (&rate, 2*900, 10, 1500); - mock_pgm_time_now += pgm_secs(2); - fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); - fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); - pgm_rate_destroy (&rate); -} -END_TEST - -/* 003: millisecond resolution should initiate millisecond fills. - */ - -START_TEST (test_check_pass_003) -{ - pgm_rate_t rate; - memset (&rate, 0, sizeof(rate)); - pgm_rate_create (&rate, 2*1010*1000, 10, 1500); - mock_pgm_time_now += pgm_secs(2); - fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); - fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); - fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); -/* duplicate check at same time point */ - fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); -/* advance time causing a millisecond fill to occur */ - mock_pgm_time_now += pgm_msecs(1); - fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); - fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); - fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); -/* advance time to fill bucket enough for only one packet */ - mock_pgm_time_now += pgm_usecs(500); - fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); - fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); -/* advance time to fill the bucket a little but not enough for one packet */ - mock_pgm_time_now += pgm_usecs(100); - fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); -/* advance time a lot, should be limited to millisecond fill rate */ - mock_pgm_time_now += pgm_secs(10); - fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); - fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); - fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); - pgm_rate_destroy (&rate); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_create = tcase_create ("create"); - suite_add_tcase (s, tc_create); - tcase_add_test (tc_create, test_create_pass_001); - tcase_add_test_raise_signal (tc_create, test_create_fail_001, SIGABRT); - - TCase* tc_destroy = tcase_create ("destroy"); - suite_add_tcase (s, tc_destroy); - tcase_add_test (tc_destroy, test_destroy_pass_001); - tcase_add_test_raise_signal (tc_destroy, test_destroy_fail_001, SIGABRT); - - TCase* tc_check = tcase_create ("check"); - suite_add_tcase (s, tc_check); - tcase_add_test (tc_check, test_check_pass_001); - tcase_add_test (tc_check, test_check_pass_002); - tcase_add_test (tc_check, test_check_pass_003); - tcase_add_test_raise_signal (tc_check, test_check_fail_001, SIGABRT); - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/receiver.c b/3rdparty/openpgm-svn-r1085/pgm/receiver.c deleted file mode 100644 index 8f26353..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/receiver.c +++ /dev/null @@ -1,2268 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * PGM receiver socket. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define __STDC_FORMAT_MACROS -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -//#define RECEIVER_DEBUG -//#define SPM_DEBUG - -#ifndef RECEIVER_DEBUG -# define PGM_DISABLE_ASSERT -#endif - -#if !defined(ENOBUFS) && defined(WSAENOBUFS) -# define ENOBUFS WSAENOBUFS -#endif -#if !defined(ECONNRESET) && defined(WSAECONNRESET) -# define ECONNRESET WSAECONNRESET -#endif - -static bool send_spmr (pgm_sock_t*const restrict, pgm_peer_t*const restrict); -static bool send_nak (pgm_sock_t*const restrict, pgm_peer_t*const restrict, const uint32_t); -static bool send_parity_nak (pgm_sock_t*const restrict, pgm_peer_t*const restrict, const unsigned, const unsigned); -static bool send_nak_list (pgm_sock_t*const restrict, pgm_peer_t*const restrict, const struct pgm_sqn_list_t*const restrict); -static bool nak_rb_state (pgm_peer_t*, const pgm_time_t); -static void nak_rpt_state (pgm_peer_t*, const pgm_time_t); -static void nak_rdata_state (pgm_peer_t*, const pgm_time_t); -static inline pgm_peer_t* _pgm_peer_ref (pgm_peer_t*); -static bool on_general_poll (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict); -static bool on_dlr_poll (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict); - - -/* helpers for pgm_peer_t */ -static inline -pgm_time_t -next_ack_rb_expiry ( - const pgm_rxw_t* window - ) -{ - pgm_assert (NULL != window); - pgm_assert (NULL != window->ack_backoff_queue.tail); - - const struct pgm_peer_t* peer = (const struct pgm_peer_t*)window->ack_backoff_queue.tail; - return peer->ack_rb_expiry; -} - -static inline -pgm_time_t -next_nak_rb_expiry ( - const pgm_rxw_t* window - ) -{ - pgm_assert (NULL != window); - pgm_assert (NULL != window->nak_backoff_queue.tail); - - const struct pgm_sk_buff_t* skb = (const struct pgm_sk_buff_t*)window->nak_backoff_queue.tail; - const pgm_rxw_state_t* state = (const pgm_rxw_state_t*)&skb->cb; - return state->timer_expiry; -} - -static inline -pgm_time_t -next_nak_rpt_expiry ( - const pgm_rxw_t* window - ) -{ - pgm_assert (NULL != window); - pgm_assert (NULL != window->wait_ncf_queue.tail); - - const struct pgm_sk_buff_t* skb = (const struct pgm_sk_buff_t*)window->wait_ncf_queue.tail; - const pgm_rxw_state_t* state = (const pgm_rxw_state_t*)&skb->cb; - return state->timer_expiry; -} - -static inline -pgm_time_t -next_nak_rdata_expiry ( - const pgm_rxw_t* window - ) -{ - pgm_assert (NULL != window); - pgm_assert (NULL != window->wait_data_queue.tail); - - const struct pgm_sk_buff_t* skb = (const struct pgm_sk_buff_t*)window->wait_data_queue.tail; - const pgm_rxw_state_t* state = (const pgm_rxw_state_t*)&skb->cb; - return state->timer_expiry; -} - -/* calculate ACK_RB_IVL. - */ -static inline -uint32_t -ack_rb_ivl ( - pgm_sock_t* sock - ) /* not const as rand() updates the seed */ -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert_cmpuint (sock->ack_bo_ivl, >, 1); - - return pgm_rand_int_range (&sock->rand_, 1 /* us */, sock->ack_bo_ivl); -} - -/* calculate NAK_RB_IVL as random time interval 1 - NAK_BO_IVL. - */ -static inline -uint32_t -nak_rb_ivl ( - pgm_sock_t* sock - ) /* not const as rand() updates the seed */ -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert_cmpuint (sock->nak_bo_ivl, >, 1); - - return pgm_rand_int_range (&sock->rand_, 1 /* us */, sock->nak_bo_ivl); -} - -/* mark sequence as recovery failed. - */ - -static -void -cancel_skb ( - pgm_sock_t* restrict sock, - pgm_peer_t* restrict peer, - const struct pgm_sk_buff_t* restrict skb, - const pgm_time_t now - ) -{ - pgm_assert (NULL != sock); - pgm_assert (NULL != peer); - pgm_assert (NULL != skb); - pgm_assert_cmpuint (now, >=, skb->tstamp); - - pgm_trace (PGM_LOG_ROLE_RX_WINDOW, _("Lost data #%u due to cancellation."), skb->sequence); - - const uint32_t fail_time = now - skb->tstamp; - if (!peer->max_fail_time) - peer->max_fail_time = peer->min_fail_time = fail_time; - else if (fail_time > peer->max_fail_time) - peer->max_fail_time = fail_time; - else if (fail_time < peer->min_fail_time) - peer->min_fail_time = fail_time; - - pgm_rxw_lost (peer->window, skb->sequence); - PGM_HISTOGRAM_TIMES("Rx.FailTime", fail_time); - -/* mark receiver window for flushing on next recv() */ - pgm_peer_set_pending (sock, peer); -} - -/* check whether this receiver is the designated acker for the source - */ - -static inline -bool -_pgm_is_acker ( - const pgm_peer_t* restrict peer, - const struct pgm_sk_buff_t* restrict skb - ) -{ - struct sockaddr_storage acker_nla; - -/* pre-conditions */ - pgm_assert (NULL != peer); - pgm_assert (NULL != skb); - pgm_assert (NULL != skb->pgm_opt_pgmcc_data); - - pgm_nla_to_sockaddr (&skb->pgm_opt_pgmcc_data->opt_nla_afi, (struct sockaddr*)&acker_nla); - return (0 == pgm_sockaddr_cmp ((struct sockaddr*)&acker_nla, (struct sockaddr*)&peer->sock->send_addr)); -} - -/* is the source holding an acker election - */ - -static inline -bool -_pgm_is_acker_election ( - const struct pgm_sk_buff_t* restrict skb - ) -{ - pgm_assert (NULL != skb); - pgm_assert (NULL != skb->pgm_opt_pgmcc_data); - - const unsigned acker_afi = ntohs (skb->pgm_opt_pgmcc_data->opt_nla_afi); - switch (acker_afi) { - case AFI_IP: - if (INADDR_ANY == skb->pgm_opt_pgmcc_data->opt_nla.s_addr) - return TRUE; - break; - - case AFI_IP6: - if (0 == memcmp (&skb->pgm_opt_pgmcc_data->opt_nla, &in6addr_any, sizeof(in6addr_any))) - return TRUE; - break; - - default: break; - } - - return FALSE; -} - -/* add state for an ACK on a data packet. - */ - -static inline -void -_pgm_add_ack ( - pgm_peer_t* const restrict peer, - const pgm_time_t ack_rb_expiry - ) -{ - peer->ack_rb_expiry = ack_rb_expiry; - pgm_queue_push_head_link (&peer->window->ack_backoff_queue, &peer->ack_link); -} - -/* remove outstanding ACK - */ - -static inline -void -_pgm_remove_ack ( - pgm_peer_t* const restrict peer - ) -{ - pgm_assert (!pgm_queue_is_empty (&peer->window->ack_backoff_queue)); - pgm_queue_unlink (&peer->window->ack_backoff_queue, &peer->ack_link); - peer->ack_rb_expiry = 0; -} - -/* increase reference count for peer object - * - * on success, returns peer object. - */ - -static inline -pgm_peer_t* -_pgm_peer_ref ( - pgm_peer_t* peer - ) -{ -/* pre-conditions */ - pgm_assert (NULL != peer); - - pgm_atomic_inc32 (&peer->ref_count); - return peer; -} - -/* decrease reference count of peer object, destroying on last reference. - */ - -void -pgm_peer_unref ( - pgm_peer_t* peer - ) -{ -/* pre-conditions */ - pgm_assert (NULL != peer); - - if (pgm_atomic_exchange_and_add32 (&peer->ref_count, (uint32_t)-1) != 1) - return; - -/* receive window */ - pgm_rxw_destroy (peer->window); - peer->window = NULL; - -/* object */ - pgm_free (peer); - peer = NULL; -} - -/* find PGM options in received SKB. - * - * returns TRUE if opt_fragment is found, otherwise FALSE is returned. - */ - -static -bool -get_pgm_options ( - struct pgm_sk_buff_t* const skb - ) -{ -/* pre-conditions */ - pgm_assert (NULL != skb); - pgm_assert (NULL != skb->pgm_data); - - struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(skb->pgm_data + 1); - bool found_opt = FALSE; - - pgm_assert (opt_header->opt_type == PGM_OPT_LENGTH); - pgm_assert (opt_header->opt_length == sizeof(struct pgm_opt_length)); - - pgm_debug ("get_pgm_options (skb:%p)", - (const void*)skb); - - skb->pgm_opt_fragment = NULL; - skb->pgm_opt_pgmcc_data = NULL; - -/* always at least two options, first is always opt_length */ - do { - opt_header = (struct pgm_opt_header*)((char*)opt_header + opt_header->opt_length); -/* option overflow */ - if (PGM_UNLIKELY((char*)opt_header > (char*)skb->data)) - break; - - switch (opt_header->opt_type & PGM_OPT_MASK) { - case PGM_OPT_FRAGMENT: - skb->pgm_opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); - found_opt = TRUE; - break; - - case PGM_OPT_PGMCC_DATA: - skb->pgm_opt_pgmcc_data = (struct pgm_opt_pgmcc_data*)(opt_header + 1); - found_opt = TRUE; - break; - - default: break; - } - - } while (!(opt_header->opt_type & PGM_OPT_END)); - return found_opt; -} - -/* a peer in the context of the sock is another party on the network sending PGM - * packets. for each peer we need a receive window and network layer address (nla) to - * which nak requests can be forwarded to. - * - * on success, returns new peer object. - */ - -pgm_peer_t* -pgm_new_peer ( - pgm_sock_t* const restrict sock, - const pgm_tsi_t* const restrict tsi, - const struct sockaddr* const restrict src_addr, - const socklen_t src_addrlen, - const struct sockaddr* const restrict dst_addr, - const socklen_t dst_addrlen, - const pgm_time_t now - ) -{ - pgm_peer_t* peer; - -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != src_addr); - pgm_assert (src_addrlen > 0); - pgm_assert (NULL != dst_addr); - pgm_assert (dst_addrlen > 0); - -#ifdef PGM_DEBUG - char saddr[INET6_ADDRSTRLEN], daddr[INET6_ADDRSTRLEN]; - pgm_sockaddr_ntop (src_addr, saddr, sizeof(saddr)); - pgm_sockaddr_ntop (dst_addr, daddr, sizeof(daddr)); - pgm_debug ("pgm_new_peer (sock:%p tsi:%s src-addr:%s src-addrlen:%u dst-addr:%s dst-addrlen:%u)", - (void*)sock, pgm_tsi_print (tsi), saddr, (unsigned)src_addrlen, daddr, (unsigned)dst_addrlen); -#endif - - peer = pgm_new0 (pgm_peer_t, 1); - peer->expiry = now + sock->peer_expiry; - peer->sock = sock; - memcpy (&peer->tsi, tsi, sizeof(pgm_tsi_t)); - memcpy (&peer->group_nla, dst_addr, dst_addrlen); - memcpy (&peer->local_nla, src_addr, src_addrlen); -/* port at same location for sin/sin6 */ - ((struct sockaddr_in*)&peer->local_nla)->sin_port = htons (sock->udp_encap_ucast_port); - ((struct sockaddr_in*)&peer->nla)->sin_port = htons (sock->udp_encap_ucast_port); - -/* lock on rx window */ - peer->window = pgm_rxw_create (&peer->tsi, - sock->max_tpdu, - sock->rxw_sqns, - sock->rxw_secs, - sock->rxw_max_rte, - sock->ack_c_p); - peer->spmr_expiry = now + sock->spmr_expiry; - -/* add peer to hash table and linked list */ - pgm_rwlock_writer_lock (&sock->peers_lock); - pgm_peer_t* entry = _pgm_peer_ref (peer); - pgm_hashtable_insert (sock->peers_hashtable, &peer->tsi, entry); - peer->peers_link.data = peer; - sock->peers_list = pgm_list_prepend_link (sock->peers_list, &peer->peers_link); - pgm_rwlock_writer_unlock (&sock->peers_lock); - - pgm_timer_lock (sock); - if (pgm_time_after( sock->next_poll, peer->spmr_expiry )) - sock->next_poll = peer->spmr_expiry; - pgm_timer_unlock (sock); - return peer; -} - -/* copy any contiguous buffers in the peer list to the provided - * message vector. - * returns -ENOBUFS if the vector is full, returns -ECONNRESET if - * data loss is detected, returns 0 when all peers flushed. - */ - -int -pgm_flush_peers_pending ( - pgm_sock_t* const restrict sock, - struct pgm_msgv_t** restrict pmsg, - const struct pgm_msgv_t* const msg_end, /* at least pmsg + 1, same object */ - size_t* const restrict bytes_read, /* added to, not set */ - unsigned* const restrict data_read - ) -{ - int retval = 0; - -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != pmsg); - pgm_assert (NULL != *pmsg); - pgm_assert (NULL != msg_end); - pgm_assert (NULL != bytes_read); - pgm_assert (NULL != data_read); - - pgm_debug ("pgm_flush_peers_pending (sock:%p pmsg:%p msg-end:%p bytes-read:%p data-read:%p)", - (const void*)sock, (const void*)pmsg, (const void*)msg_end, (const void*)bytes_read, (const void*)data_read); - - while (sock->peers_pending) - { - pgm_peer_t* peer = sock->peers_pending->data; - if (peer->last_commit && peer->last_commit < sock->last_commit) - pgm_rxw_remove_commit (peer->window); - const ssize_t peer_bytes = pgm_rxw_readv (peer->window, pmsg, msg_end - *pmsg + 1); - - if (peer->last_cumulative_losses != ((pgm_rxw_t*)peer->window)->cumulative_losses) - { - sock->is_reset = TRUE; - peer->lost_count = ((pgm_rxw_t*)peer->window)->cumulative_losses - peer->last_cumulative_losses; - peer->last_cumulative_losses = ((pgm_rxw_t*)peer->window)->cumulative_losses; - } - - if (peer_bytes >= 0) - { - (*bytes_read) += peer_bytes; - (*data_read) ++; - peer->last_commit = sock->last_commit; - if (*pmsg > msg_end) { /* commit full */ - retval = -ENOBUFS; - break; - } - } else - peer->last_commit = 0; - if (PGM_UNLIKELY(sock->is_reset)) { - retval = -ECONNRESET; - break; - } -/* clear this reference and move to next */ - sock->peers_pending = pgm_slist_remove_first (sock->peers_pending); - } - - return retval; -} - -/* edge trigerred has receiver pending events - */ - -bool -pgm_peer_has_pending ( - pgm_peer_t* const peer - ) -{ -/* pre-conditions */ - pgm_assert (NULL != peer); - - if (NULL == peer->pending_link.data && ((pgm_rxw_t*)peer->window)->has_event) { - ((pgm_rxw_t*)peer->window)->has_event = 0; - return TRUE; - } - return FALSE; -} - -/* set receiver in pending event queue - */ - -void -pgm_peer_set_pending ( - pgm_sock_t* const restrict sock, - pgm_peer_t* const restrict peer - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != peer); - - if (peer->pending_link.data) return; - peer->pending_link.data = peer; - sock->peers_pending = pgm_slist_prepend_link (sock->peers_pending, &peer->pending_link); -} - -/* Create a new error SKB detailing data loss. - */ - -void -pgm_set_reset_error ( - pgm_sock_t* const restrict sock, - pgm_peer_t* const restrict source, - struct pgm_msgv_t* const restrict msgv - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != source); - pgm_assert (NULL != msgv); - - struct pgm_sk_buff_t* error_skb = pgm_alloc_skb (0); - error_skb->sock = sock; - error_skb->tstamp = pgm_time_update_now (); - memcpy (&error_skb->tsi, &source->tsi, sizeof(pgm_tsi_t)); - error_skb->sequence = source->lost_count; - msgv->msgv_skb[0] = error_skb; - msgv->msgv_len = 1; -} - -/* SPM indicate start of a session, continued presence of a session, or flushing final packets - * of a session. - * - * returns TRUE on valid packet, FALSE on invalid packet or duplicate SPM sequence number. - */ - -bool -pgm_on_spm ( - pgm_sock_t* const restrict sock, - pgm_peer_t* const restrict source, - struct pgm_sk_buff_t* const restrict skb - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != source); - pgm_assert (NULL != skb); - - pgm_debug("pgm_on_spm (sock:%p source:%p skb:%p)", - (const void*)sock, (const void*)source, (const void*)skb); - - if (PGM_UNLIKELY(!pgm_verify_spm (skb))) { - pgm_trace(PGM_LOG_ROLE_NETWORK,_("Discarded invalid SPM.")); - source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_SPMS]++; - return FALSE; - } - - const struct pgm_spm* spm = (struct pgm_spm*) skb->data; - const struct pgm_spm6* spm6 = (struct pgm_spm6*)skb->data; - const uint32_t spm_sqn = ntohl (spm->spm_sqn); - -/* check for advancing sequence number, or first SPM */ - if (PGM_LIKELY(pgm_uint32_gte (spm_sqn, source->spm_sqn))) - { -/* copy NLA for replies */ - pgm_nla_to_sockaddr (&spm->spm_nla_afi, (struct sockaddr*)&source->nla); - -/* save sequence number */ - source->spm_sqn = spm_sqn; - -/* update receive window */ - const pgm_time_t nak_rb_expiry = skb->tstamp + nak_rb_ivl (sock); - const unsigned naks = pgm_rxw_update (source->window, - ntohl (spm->spm_lead), - ntohl (spm->spm_trail), - skb->tstamp, - nak_rb_expiry); - if (naks) { - pgm_timer_lock (sock); - if (pgm_time_after (sock->next_poll, nak_rb_expiry)) - sock->next_poll = nak_rb_expiry; - pgm_timer_unlock (sock); - } - -/* mark receiver window for flushing on next recv() */ - const pgm_rxw_t* window = source->window; - if (window->cumulative_losses != source->last_cumulative_losses && - !source->pending_link.data) - { - sock->is_reset = TRUE; - source->lost_count = window->cumulative_losses - source->last_cumulative_losses; - source->last_cumulative_losses = window->cumulative_losses; - pgm_peer_set_pending (sock, source); - } - } - else - { /* does not advance SPM sequence number */ - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded duplicate SPM.")); - source->cumulative_stats[PGM_PC_RECEIVER_DUP_SPMS]++; - return FALSE; - } - -/* check whether peer can generate parity packets */ - if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) - { - const struct pgm_opt_length* opt_len = (AF_INET6 == source->nla.ss_family) ? - (const struct pgm_opt_length*)(spm6 + 1) : - (const struct pgm_opt_length*)(spm + 1); - if (PGM_UNLIKELY(opt_len->opt_type != PGM_OPT_LENGTH)) - { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed SPM.")); - source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_SPMS]++; - return FALSE; - } - if (PGM_UNLIKELY(opt_len->opt_length != sizeof(struct pgm_opt_length))) - { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed SPM.")); - source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_SPMS]++; - return FALSE; - } -/* TODO: check for > 16 options & past packet end */ - const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; - do { - opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); - if ((opt_header->opt_type & PGM_OPT_MASK) == PGM_OPT_PARITY_PRM) - { - const struct pgm_opt_parity_prm* opt_parity_prm = (const struct pgm_opt_parity_prm*)(opt_header + 1); - if (PGM_UNLIKELY((opt_parity_prm->opt_reserved & PGM_PARITY_PRM_MASK) == 0)) - { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed SPM.")); - source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_SPMS]++; - return FALSE; - } - - const uint32_t parity_prm_tgs = ntohl (opt_parity_prm->parity_prm_tgs); - if (PGM_UNLIKELY(parity_prm_tgs < 2 || parity_prm_tgs > 128)) - { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed SPM.")); - source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_SPMS]++; - return FALSE; - } - - source->has_proactive_parity = opt_parity_prm->opt_reserved & PGM_PARITY_PRM_PRO; - source->has_ondemand_parity = opt_parity_prm->opt_reserved & PGM_PARITY_PRM_OND; - if (source->has_proactive_parity || source->has_ondemand_parity) { - source->is_fec_enabled = 1; - pgm_rxw_update_fec (source->window, parity_prm_tgs); - } - } - } while (!(opt_header->opt_type & PGM_OPT_END)); - } - -/* either way bump expiration timer */ - source->expiry = skb->tstamp + sock->peer_expiry; - source->spmr_expiry = 0; - if (source->spmr_tstamp > 0) { - PGM_HISTOGRAM_TIMES("Rx.SpmRequestResponseTime", skb->tstamp - source->spmr_tstamp); - source->spmr_tstamp = 0; - } - return TRUE; -} - -/* Multicast peer-to-peer NAK handling, pretty much the same as a NCF but different direction - * - * if NAK is valid, returns TRUE. on error, FALSE is returned. - */ - -bool -pgm_on_peer_nak ( - pgm_sock_t* const restrict sock, - pgm_peer_t* const restrict peer, - struct pgm_sk_buff_t* const restrict skb - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != peer); - pgm_assert (NULL != skb); - - pgm_debug ("pgm_on_peer_nak (sock:%p peer:%p skb:%p)", - (const void*)sock, (const void*)peer, (const void*)skb); - - if (PGM_UNLIKELY(!pgm_verify_nak (skb))) - { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded invalid multicast NAK.")); - peer->cumulative_stats[PGM_PC_RECEIVER_NAK_ERRORS]++; - return FALSE; - } - - const struct pgm_nak* nak = (struct pgm_nak*) skb->data; - const struct pgm_nak6* nak6 = (struct pgm_nak6*)skb->data; - -/* NAK_SRC_NLA must not contain our sock unicast NLA */ - struct sockaddr_storage nak_src_nla; - pgm_nla_to_sockaddr (&nak->nak_src_nla_afi, (struct sockaddr*)&nak_src_nla); - if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&nak_src_nla, (struct sockaddr*)&sock->send_addr) == 0)) - { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded multicast NAK on NLA mismatch.")); - return FALSE; - } - -/* NAK_GRP_NLA contains one of our sock receive multicast groups: the sources send multicast group */ - struct sockaddr_storage nak_grp_nla; - pgm_nla_to_sockaddr ((AF_INET6 == nak_src_nla.ss_family) ? &nak6->nak6_grp_nla_afi : &nak->nak_grp_nla_afi, (struct sockaddr*)&nak_grp_nla); - bool found = FALSE; - for (unsigned i = 0; i < sock->recv_gsr_len; i++) - { - if (pgm_sockaddr_cmp ((struct sockaddr*)&nak_grp_nla, (struct sockaddr*)&sock->recv_gsr[i].gsr_group) == 0) - { - found = TRUE; - break; - } - } - - if (PGM_UNLIKELY(!found)) { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded multicast NAK on multicast group mismatch.")); - return FALSE; - } - -/* handle as NCF */ - int status = pgm_rxw_confirm (peer->window, - ntohl (nak->nak_sqn), - skb->tstamp, - skb->tstamp + sock->nak_rdata_ivl, - skb->tstamp + nak_rb_ivl(sock)); - if (PGM_RXW_UPDATED == status || PGM_RXW_APPENDED == status) - peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED]++; - -/* check NAK list */ - const uint32_t* nak_list = NULL; - unsigned nak_list_len = 0; - if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) - { - const struct pgm_opt_length* opt_len = (AF_INET6 == nak_src_nla.ss_family) ? - (const struct pgm_opt_length*)(nak6 + 1) : - (const struct pgm_opt_length*)(nak + 1); - if (PGM_UNLIKELY(opt_len->opt_type != PGM_OPT_LENGTH)) - { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed multicast NAK.")); - peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_NCFS]++; - return FALSE; - } - if (PGM_UNLIKELY(opt_len->opt_length != sizeof(struct pgm_opt_length))) - { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed multicast NAK.")); - peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_NCFS]++; - return FALSE; - } -/* TODO: check for > 16 options & past packet end */ - const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; - do { - opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); - if ((opt_header->opt_type & PGM_OPT_MASK) == PGM_OPT_NAK_LIST) - { - nak_list = ((const struct pgm_opt_nak_list*)(opt_header + 1))->opt_sqn; - nak_list_len = ( opt_header->opt_length - sizeof(struct pgm_opt_header) - sizeof(uint8_t) ) / sizeof(uint32_t); - break; - } - } while (!(opt_header->opt_type & PGM_OPT_END)); - } - - while (nak_list_len) { - status = pgm_rxw_confirm (peer->window, - ntohl (*nak_list), - skb->tstamp, - skb->tstamp + sock->nak_rdata_ivl, - skb->tstamp + nak_rb_ivl(sock)); - if (PGM_RXW_UPDATED == status || PGM_RXW_APPENDED == status) - peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED]++; - nak_list++; - nak_list_len--; - } - -/* mark receiver window for flushing on next recv() */ - const pgm_rxw_t* window = peer->window; - if (window->cumulative_losses != peer->last_cumulative_losses && - !peer->pending_link.data) - { - sock->is_reset = TRUE; - peer->lost_count = window->cumulative_losses - peer->last_cumulative_losses; - peer->last_cumulative_losses = window->cumulative_losses; - pgm_peer_set_pending (sock, peer); - } - return TRUE; -} - -/* NCF confirming receipt of a NAK from this sock or another on the LAN segment. - * - * Packet contents will match exactly the sent NAK, although not really that helpful. - * - * if NCF is valid, returns TRUE. on error, FALSE is returned. - */ - -bool -pgm_on_ncf ( - pgm_sock_t* const restrict sock, - pgm_peer_t* const restrict source, - struct pgm_sk_buff_t* const restrict skb - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != source); - pgm_assert (NULL != skb); - - pgm_debug ("pgm_on_ncf (sock:%p source:%p skb:%p)", - (const void*)sock, (const void*)source, (const void*)skb); - - if (PGM_UNLIKELY(!pgm_verify_ncf (skb))) - { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded invalid NCF.")); - source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_NCFS]++; - return FALSE; - } - - const struct pgm_nak* ncf = (struct pgm_nak*) skb->data; - const struct pgm_nak6* ncf6 = (struct pgm_nak6*)skb->data; - -/* NCF_SRC_NLA may contain our sock unicast NLA, we don't really care */ - struct sockaddr_storage ncf_src_nla; - pgm_nla_to_sockaddr (&ncf->nak_src_nla_afi, (struct sockaddr*)&ncf_src_nla); - -#if 0 - if (PGM(pgm_sockaddr_cmp ((struct sockaddr*)&ncf_src_nla, (struct sockaddr*)&sock->send_addr) != 0)) { - g_trace ("INFO", "Discarded NCF on NLA mismatch."); - peer->cumulative_stats[PGM_PC_RECEIVER_PACKETS_DISCARDED]++; - return FALSE; - } -#endif - -/* NCF_GRP_NLA contains our sock multicast group */ - struct sockaddr_storage ncf_grp_nla; - pgm_nla_to_sockaddr ((AF_INET6 == ncf_src_nla.ss_family) ? &ncf6->nak6_grp_nla_afi : &ncf->nak_grp_nla_afi, (struct sockaddr*)&ncf_grp_nla); - if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&ncf_grp_nla, (struct sockaddr*)&sock->send_gsr.gsr_group) != 0)) - { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded NCF on multicast group mismatch.")); - return FALSE; - } - - const pgm_time_t ncf_rdata_ivl = skb->tstamp + sock->nak_rdata_ivl; - const pgm_time_t ncf_rb_ivl = skb->tstamp + nak_rb_ivl(sock); - int status = pgm_rxw_confirm (source->window, - ntohl (ncf->nak_sqn), - skb->tstamp, - ncf_rdata_ivl, - ncf_rb_ivl); - if (PGM_RXW_UPDATED == status || PGM_RXW_APPENDED == status) - { - const pgm_time_t ncf_ivl = (PGM_RXW_APPENDED == status) ? ncf_rb_ivl : ncf_rdata_ivl; - pgm_timer_lock (sock); - if (pgm_time_after (sock->next_poll, ncf_ivl)) { - sock->next_poll = ncf_ivl; - } - pgm_timer_unlock (sock); - source->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED]++; - } - -/* check NCF list */ - const uint32_t* ncf_list = NULL; - unsigned ncf_list_len = 0; - if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) - { - const struct pgm_opt_length* opt_len = (AF_INET6 == ncf_src_nla.ss_family) ? - (const struct pgm_opt_length*)(ncf6 + 1) : - (const struct pgm_opt_length*)(ncf + 1); - if (PGM_UNLIKELY(opt_len->opt_type != PGM_OPT_LENGTH)) - { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed NCF.")); - source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_NCFS]++; - return FALSE; - } - if (PGM_UNLIKELY(opt_len->opt_length != sizeof(struct pgm_opt_length))) - { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed NCF.")); - source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_NCFS]++; - return FALSE; - } -/* TODO: check for > 16 options & past packet end */ - const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; - do { - opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); - if ((opt_header->opt_type & PGM_OPT_MASK) == PGM_OPT_NAK_LIST) - { - ncf_list = ((const struct pgm_opt_nak_list*)(opt_header + 1))->opt_sqn; - ncf_list_len = ( opt_header->opt_length - sizeof(struct pgm_opt_header) - sizeof(uint8_t) ) / sizeof(uint32_t); - break; - } - } while (!(opt_header->opt_type & PGM_OPT_END)); - } - - pgm_debug ("NCF contains 1+%d sequence numbers.", ncf_list_len); - while (ncf_list_len) - { - status = pgm_rxw_confirm (source->window, - ntohl (*ncf_list), - skb->tstamp, - ncf_rdata_ivl, - ncf_rb_ivl); - if (PGM_RXW_UPDATED == status || PGM_RXW_APPENDED == status) - source->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED]++; - ncf_list++; - ncf_list_len--; - } - -/* mark receiver window for flushing on next recv() */ - const pgm_rxw_t* window = source->window; - if (window->cumulative_losses != source->last_cumulative_losses && - !source->pending_link.data) - { - sock->is_reset = TRUE; - source->lost_count = window->cumulative_losses - source->last_cumulative_losses; - source->last_cumulative_losses = window->cumulative_losses; - pgm_peer_set_pending (sock, source); - } - return TRUE; -} - -/* send SPM-request to a new peer, this packet type has no contents - * - * on success, TRUE is returned, if operation would block FALSE is - * returned. - */ - -static -bool -send_spmr ( - pgm_sock_t* const restrict sock, - pgm_peer_t* const restrict source - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != source); - - pgm_debug ("send_spmr (sock:%p source:%p)", - (const void*)sock, (const void*)source); - - const size_t tpdu_length = sizeof(struct pgm_header); - char buf[ tpdu_length ]; - struct pgm_header* header = (struct pgm_header*)buf; - memcpy (header->pgm_gsi, &source->tsi.gsi, sizeof(pgm_gsi_t)); -/* dport & sport reversed communicating upstream */ - header->pgm_sport = sock->dport; - header->pgm_dport = source->tsi.sport; - header->pgm_type = PGM_SPMR; - header->pgm_options = 0; - header->pgm_tsdu_length = 0; - header->pgm_checksum = 0; - header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); - -/* send multicast SPMR TTL 1 */ - pgm_sockaddr_multicast_hops (sock->send_sock, sock->send_gsr.gsr_group.ss_family, 1); - ssize_t sent = pgm_sendto (sock, - FALSE, /* not rate limited */ - FALSE, /* regular socket */ - header, - tpdu_length, - (struct sockaddr*)&sock->send_gsr.gsr_group, - pgm_sockaddr_len ((struct sockaddr*)&sock->send_gsr.gsr_group)); - if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) - return FALSE; - -/* send unicast SPMR with regular TTL */ - pgm_sockaddr_multicast_hops (sock->send_sock, sock->send_gsr.gsr_group.ss_family, sock->hops); - sent = pgm_sendto (sock, - FALSE, - FALSE, - header, - tpdu_length, - (struct sockaddr*)&source->local_nla, - pgm_sockaddr_len ((struct sockaddr*)&source->local_nla)); - if (sent < 0 && EAGAIN == errno) - return FALSE; - - sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT] += tpdu_length * 2; - return TRUE; -} - -/* send selective NAK for one sequence number. - * - * on success, TRUE is returned, returns FALSE if would block on operation. - */ - -static -bool -send_nak ( - pgm_sock_t* const restrict sock, - pgm_peer_t* const restrict source, - const uint32_t sequence - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != source); - - pgm_debug ("send_nak (sock:%p peer:%p sequence:%" PRIu32 ")", - (void*)sock, (void*)source, sequence); - - size_t tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_nak); - if (AF_INET6 == source->nla.ss_family) - tpdu_length += sizeof(struct pgm_nak6) - sizeof(struct pgm_nak); - char buf[ tpdu_length ]; - struct pgm_header* header = (struct pgm_header*)buf; - struct pgm_nak* nak = (struct pgm_nak* )(header + 1); - struct pgm_nak6* nak6 = (struct pgm_nak6*)(header + 1); - memcpy (header->pgm_gsi, &source->tsi.gsi, sizeof(pgm_gsi_t)); - -/* dport & sport swap over for a nak */ - header->pgm_sport = sock->dport; - header->pgm_dport = source->tsi.sport; - header->pgm_type = PGM_NAK; - header->pgm_options = 0; - header->pgm_tsdu_length = 0; - -/* NAK */ - nak->nak_sqn = htonl (sequence); - -/* source nla */ - pgm_sockaddr_to_nla ((struct sockaddr*)&source->nla, (char*)&nak->nak_src_nla_afi); - -/* group nla: we match the NAK NLA to the same as advertised by the source, we might - * be listening to multiple multicast groups - */ - pgm_sockaddr_to_nla ((struct sockaddr*)&source->group_nla, - (AF_INET6 == source->nla.ss_family) ? (char*)&nak6->nak6_grp_nla_afi : (char*)&nak->nak_grp_nla_afi); - - header->pgm_checksum = 0; - header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); - - const ssize_t sent = pgm_sendto (sock, - FALSE, /* not rate limited */ - TRUE, /* with router alert */ - header, - tpdu_length, - (struct sockaddr*)&source->nla, - pgm_sockaddr_len((struct sockaddr*)&source->nla)); - if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) - return FALSE; - - source->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAK_PACKETS_SENT]++; - source->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SENT]++; - return TRUE; -} - -/* Send a parity NAK requesting on-demand parity packet generation. - * - * on success, TRUE is returned, returns FALSE if operation would block. - */ - -static -bool -send_parity_nak ( - pgm_sock_t* const restrict sock, - pgm_peer_t* const restrict source, - const uint32_t nak_tg_sqn, /* transmission group (shifted) */ - const uint32_t nak_pkt_cnt /* count of parity packets to request */ - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != source); - pgm_assert (nak_pkt_cnt > 0); - - pgm_debug ("send_parity_nak (sock:%p source:%p nak-tg-sqn:%" PRIu32 " nak-pkt-cnt:%" PRIu32 ")", - (void*)sock, (void*)source, nak_tg_sqn, nak_pkt_cnt); - - size_t tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_nak); - if (AF_INET6 == source->nla.ss_family) - tpdu_length += sizeof(struct pgm_nak6) - sizeof(struct pgm_nak); - char buf[ tpdu_length ]; - struct pgm_header* header = (struct pgm_header*)buf; - struct pgm_nak* nak = (struct pgm_nak* )(header + 1); - struct pgm_nak6* nak6 = (struct pgm_nak6*)(header + 1); - memcpy (header->pgm_gsi, &source->tsi.gsi, sizeof(pgm_gsi_t)); - -/* dport & sport swap over for a nak */ - header->pgm_sport = sock->dport; - header->pgm_dport = source->tsi.sport; - header->pgm_type = PGM_NAK; - header->pgm_options = PGM_OPT_PARITY; /* this is a parity packet */ - header->pgm_tsdu_length = 0; - -/* NAK */ - nak->nak_sqn = htonl (nak_tg_sqn | (nak_pkt_cnt - 1) ); - -/* source nla */ - pgm_sockaddr_to_nla ((struct sockaddr*)&source->nla, (char*)&nak->nak_src_nla_afi); - -/* group nla: we match the NAK NLA to the same as advertised by the source, we might - * be listening to multiple multicast groups - */ - pgm_sockaddr_to_nla ((struct sockaddr*)&source->group_nla, - (AF_INET6 == source->nla.ss_family) ? (char*)&nak6->nak6_grp_nla_afi : (char*)&nak->nak_grp_nla_afi ); - header->pgm_checksum = 0; - header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); - - const ssize_t sent = pgm_sendto (sock, - FALSE, /* not rate limited */ - TRUE, /* with router alert */ - header, - tpdu_length, - (struct sockaddr*)&source->nla, - pgm_sockaddr_len((struct sockaddr*)&source->nla)); - if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) - return FALSE; - - source->cumulative_stats[PGM_PC_RECEIVER_PARITY_NAK_PACKETS_SENT]++; - source->cumulative_stats[PGM_PC_RECEIVER_PARITY_NAKS_SENT]++; - return TRUE; -} - -/* A NAK packet with a OPT_NAK_LIST option extension - * - * on success, TRUE is returned. on error, FALSE is returned. - */ - -static -bool -send_nak_list ( - pgm_sock_t* const restrict sock, - pgm_peer_t* const restrict source, - const struct pgm_sqn_list_t* const restrict sqn_list - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != source); - pgm_assert (NULL != sqn_list); - pgm_assert_cmpuint (sqn_list->len, >, 1); - pgm_assert_cmpuint (sqn_list->len, <=, 63); - -#ifdef RECEIVER_DEBUG - char list[1024]; - sprintf (list, "%" PRIu32, sqn_list->sqn[0]); - for (unsigned i = 1; i < sqn_list->len; i++) { - char sequence[2 + strlen("4294967295")]; - sprintf (sequence, " %" PRIu32, sqn_list->sqn[i]); - strcat (list, sequence); - } - pgm_debug("send_nak_list (sock:%p source:%p sqn-list:[%s])", - (const void*)sock, (const void*)source, list); -#endif - - size_t tpdu_length = sizeof(struct pgm_header) + - sizeof(struct pgm_nak) + - sizeof(struct pgm_opt_length) + /* includes header */ - sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_nak_list) + - ( (sqn_list->len-1) * sizeof(uint32_t) ); - if (AF_INET6 == source->nla.ss_family) - tpdu_length += sizeof(struct pgm_nak6) - sizeof(struct pgm_nak); - char buf[ tpdu_length ]; - if (PGM_UNLIKELY(pgm_mem_gc_friendly)) - memset (buf, 0, tpdu_length); - struct pgm_header* header = (struct pgm_header*)buf; - struct pgm_nak* nak = (struct pgm_nak* )(header + 1); - struct pgm_nak6* nak6 = (struct pgm_nak6*)(header + 1); - memcpy (header->pgm_gsi, &source->tsi.gsi, sizeof(pgm_gsi_t)); - -/* dport & sport swap over for a nak */ - header->pgm_sport = sock->dport; - header->pgm_dport = source->tsi.sport; - header->pgm_type = PGM_NAK; - header->pgm_options = PGM_OPT_PRESENT | PGM_OPT_NETWORK; - header->pgm_tsdu_length = 0; - -/* NAK */ - nak->nak_sqn = htonl (sqn_list->sqn[0]); - -/* source nla */ - pgm_sockaddr_to_nla ((struct sockaddr*)&source->nla, (char*)&nak->nak_src_nla_afi); - -/* group nla */ - pgm_sockaddr_to_nla ((struct sockaddr*)&source->group_nla, - (AF_INET6 == source->nla.ss_family) ? (char*)&nak6->nak6_grp_nla_afi : (char*)&nak->nak_grp_nla_afi); - -/* OPT_NAK_LIST */ - struct pgm_opt_length* opt_len = (AF_INET6 == source->nla.ss_family) ? (struct pgm_opt_length*)(nak6 + 1) : (struct pgm_opt_length*)(nak + 1); - opt_len->opt_type = PGM_OPT_LENGTH; - opt_len->opt_length = sizeof(struct pgm_opt_length); - opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + - sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_nak_list) + - ( (sqn_list->len-1) * sizeof(uint32_t) ) ); - struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); - opt_header->opt_type = PGM_OPT_NAK_LIST | PGM_OPT_END; - opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) - + ( (sqn_list->len-1) * sizeof(uint32_t) ); - struct pgm_opt_nak_list* opt_nak_list = (struct pgm_opt_nak_list*)(opt_header + 1); - opt_nak_list->opt_reserved = 0; - - for (unsigned i = 1; i < sqn_list->len; i++) - opt_nak_list->opt_sqn[i-1] = htonl (sqn_list->sqn[i]); - - header->pgm_checksum = 0; - header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); - - const ssize_t sent = pgm_sendto (sock, - FALSE, /* not rate limited */ - FALSE, /* regular socket */ - header, - tpdu_length, - (struct sockaddr*)&source->nla, - pgm_sockaddr_len((struct sockaddr*)&source->nla)); - if ( sent != (ssize_t)tpdu_length ) - return FALSE; - - source->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAK_PACKETS_SENT]++; - source->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SENT] += 1 + sqn_list->len; - return TRUE; -} - -/* send ACK upstream to source - * - * on success, TRUE is returned. on error, FALSE is returned. - */ - -static -bool -send_ack ( - pgm_sock_t* const restrict sock, - pgm_peer_t* const restrict source, - const pgm_time_t now - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != source); - - pgm_debug ("send_ack (sock:%p source:%p now:%" PGM_TIME_FORMAT ")", - (const void*)sock, (const void*)source, now); - - size_t tpdu_length = sizeof(struct pgm_header) + - sizeof(struct pgm_ack) + - sizeof(struct pgm_opt_length) + /* includes header */ - sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_pgmcc_feedback); - if (AF_INET6 == sock->send_addr.ss_family) - tpdu_length += sizeof(struct pgm_opt6_pgmcc_feedback) - sizeof(struct pgm_opt_pgmcc_feedback); - char buf[ tpdu_length ]; - if (PGM_UNLIKELY(pgm_mem_gc_friendly)) - memset (buf, 0, tpdu_length); - struct pgm_header* header = (struct pgm_header*)buf; - struct pgm_ack* ack = (struct pgm_ack*)(header + 1); - memcpy (header->pgm_gsi, &source->tsi.gsi, sizeof(pgm_gsi_t)); - -/* dport & sport swap over for an ack */ - header->pgm_sport = sock->dport; - header->pgm_dport = source->tsi.sport; - header->pgm_type = PGM_ACK; - header->pgm_options = PGM_OPT_PRESENT; - header->pgm_tsdu_length = 0; - -/* ACK */ - ack->ack_rx_max = htonl (pgm_rxw_lead (source->window)); - ack->ack_bitmap = htonl (source->window->bitmap); - -/* OPT_PGMCC_FEEDBACK */ - struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(ack + 1); - opt_len->opt_type = PGM_OPT_LENGTH; - opt_len->opt_length = sizeof(struct pgm_opt_length); - opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + - sizeof(struct pgm_opt_header) + - (AF_INET6 == sock->send_addr.ss_family) ? - sizeof(struct pgm_opt6_pgmcc_feedback) : - sizeof(struct pgm_opt_pgmcc_feedback) ); - struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); - opt_header->opt_type = PGM_OPT_PGMCC_FEEDBACK | PGM_OPT_END; - opt_header->opt_length = sizeof(struct pgm_opt_header) + - ( (AF_INET6 == sock->send_addr.ss_family) ? - sizeof(struct pgm_opt6_pgmcc_feedback) : - sizeof(struct pgm_opt_pgmcc_feedback) ); - struct pgm_opt_pgmcc_feedback* opt_pgmcc_feedback = (struct pgm_opt_pgmcc_feedback*)(opt_header + 1); - opt_pgmcc_feedback->opt_reserved = 0; - - const uint32_t t = source->ack_last_tstamp + pgm_to_msecs( now - source->last_data_tstamp ); - opt_pgmcc_feedback->opt_tstamp = htonl (t); - pgm_sockaddr_to_nla ((struct sockaddr*)&sock->send_addr, (char*)&opt_pgmcc_feedback->opt_nla_afi); - opt_pgmcc_feedback->opt_loss_rate = htonl (source->window->data_loss); - - header->pgm_checksum = 0; - header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); - - const ssize_t sent = pgm_sendto (sock, - FALSE, /* not rate limited */ - FALSE, /* regular socket */ - header, - tpdu_length, - (struct sockaddr*)&source->nla, - pgm_sockaddr_len((struct sockaddr*)&source->nla)); - if ( sent != (ssize_t)tpdu_length ) - return FALSE; - - source->cumulative_stats[PGM_PC_RECEIVER_ACKS_SENT]++; - return TRUE; -} - -/* check all receiver windows for ACKer elections, on expiration send an ACK. - * - * returns TRUE on success, returns FALSE if operation would block. - */ - -static -bool -ack_rb_state ( - pgm_peer_t* peer, - const pgm_time_t now - ) -{ -/* pre-conditions */ - pgm_assert (NULL != peer); - - pgm_debug ("ack_rb_state (peer:%p now:%" PGM_TIME_FORMAT ")", - (const void*)peer, now); - - pgm_rxw_t* window = peer->window; - pgm_sock_t* sock = peer->sock; - pgm_list_t* list; - - list = window->ack_backoff_queue.tail; - if (!list) { - pgm_assert (window->ack_backoff_queue.head == NULL); - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Backoff queue is empty in ack_rb_state.")); - return TRUE; - } else { - pgm_assert (window->ack_backoff_queue.head != NULL); - } - -/* have not learned this peers NLA */ - const bool is_valid_nla = (0 != peer->nla.ss_family); - - while (list) - { - pgm_list_t* next_list_el = list->prev; - -/* check for ACK backoff expiration */ - if (pgm_time_after_eq(now, peer->ack_rb_expiry)) - { -/* unreliable delivery */ - _pgm_remove_ack (peer); - - if (PGM_UNLIKELY(!is_valid_nla)) { - pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Unable to send ACK due to unknown NLA.")); - list = next_list_el; - continue; - } - - pgm_assert (!pgm_sockaddr_is_addr_unspecified ((struct sockaddr*)&peer->nla)); - - if (!send_ack (sock, peer, now)) - return FALSE; - } - else - { /* packet expires some time later */ - break; - } - - list = next_list_el; - } - - if (window->ack_backoff_queue.length == 0) - { - pgm_assert ((struct rxw_packet*)window->ack_backoff_queue.head == NULL); - pgm_assert ((struct rxw_packet*)window->ack_backoff_queue.tail == NULL); - } - else - { - pgm_assert ((struct rxw_packet*)window->ack_backoff_queue.head != NULL); - pgm_assert ((struct rxw_packet*)window->ack_backoff_queue.tail != NULL); - } - - if (window->ack_backoff_queue.tail) - { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Next expiry set in %f seconds."), - pgm_to_secsf(next_ack_rb_expiry(window) - now)); - } - else - { - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("ACK backoff queue empty.")); - } - return TRUE; -} - -/* check all receiver windows for packets in BACK-OFF_STATE, on expiration send a NAK. - * update sock::next_nak_rb_timestamp for next expiration time. - * - * peer object is locked before entry. - * - * returns TRUE on success, returns FALSE if operation would block. - */ - -static -bool -nak_rb_state ( - pgm_peer_t* peer, - const pgm_time_t now - ) -{ -/* pre-conditions */ - pgm_assert (NULL != peer); - - pgm_debug ("nak_rb_state (peer:%p now:%" PGM_TIME_FORMAT ")", - (const void*)peer, now); - - pgm_rxw_t* window = peer->window; - pgm_sock_t* sock = peer->sock; - pgm_list_t* list; - struct pgm_sqn_list_t nak_list = { .len = 0 }; - -/* send all NAKs first, lack of data is blocking contiguous processing and its - * better to get the notification out a.s.a.p. even though it might be waiting - * in a kernel queue. - * - * alternative: after each packet check for incoming data and return to the - * event loop. bias for shorter loops as retry count increases. - */ - list = window->nak_backoff_queue.tail; - if (!list) { - pgm_assert (window->nak_backoff_queue.head == NULL); - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Backoff queue is empty in nak_rb_state.")); - return TRUE; - } else { - pgm_assert (window->nak_backoff_queue.head != NULL); - } - - unsigned dropped_invalid = 0; - -/* have not learned this peers NLA */ - const bool is_valid_nla = 0 != peer->nla.ss_family; - -/* TODO: process BOTH selective and parity NAKs? */ - -/* calculate current transmission group for parity enabled peers */ - if (peer->has_ondemand_parity) - { - const uint32_t tg_sqn_mask = 0xffffffff << window->tg_sqn_shift; - -/* NAKs only generated previous to current transmission group */ - const uint32_t current_tg_sqn = window->lead & tg_sqn_mask; - - uint32_t nak_tg_sqn = 0; - uint32_t nak_pkt_cnt = 0; - -/* parity NAK generation */ - - while (list) - { - pgm_list_t* next_list_el = list->prev; - struct pgm_sk_buff_t* skb = (struct pgm_sk_buff_t*)list; - pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; - -/* check this packet for state expiration */ - if (pgm_time_after_eq (now, state->timer_expiry)) - { - if (PGM_UNLIKELY(!is_valid_nla)) { - dropped_invalid++; - pgm_rxw_lost (window, skb->sequence); -/* mark receiver window for flushing on next recv() */ - pgm_peer_set_pending (sock, peer); - list = next_list_el; - continue; - } - -/* TODO: parity nak lists */ - const uint32_t tg_sqn = skb->sequence & tg_sqn_mask; - if ( ( nak_pkt_cnt && tg_sqn == nak_tg_sqn ) || - ( !nak_pkt_cnt && tg_sqn != current_tg_sqn ) ) - { - pgm_rxw_state (window, skb, PGM_PKT_STATE_WAIT_NCF); - - if (!nak_pkt_cnt++) - nak_tg_sqn = tg_sqn; - state->nak_transmit_count++; - -#ifdef PGM_ABSOLUTE_EXPIRY - state->timer_expiry += sock->nak_rpt_ivl; - while (pgm_time_after_eq (now, state->timer_expiry)) { - state->timer_expiry += sock->nak_rpt_ivl; - state->ncf_retry_count++; - } -#else - state->timer_expiry = now + sock->nak_rpt_ivl; -#endif - pgm_timer_lock (sock); - if (pgm_time_after (sock->next_poll, state->timer_expiry)) - sock->next_poll = state->timer_expiry; - pgm_timer_unlock (sock); - } - else - { /* different transmission group */ - break; - } - } - else - { /* packet expires some time later */ - break; - } - - list = next_list_el; - } - - if (nak_pkt_cnt && !send_parity_nak (sock, peer, nak_tg_sqn, nak_pkt_cnt)) - return FALSE; - } - else - { - -/* select NAK generation */ - - while (list) - { - pgm_list_t* next_list_el = list->prev; - struct pgm_sk_buff_t* skb = (struct pgm_sk_buff_t*)list; - pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; - -/* check this packet for state expiration */ - if (pgm_time_after_eq(now, state->timer_expiry)) - { - if (PGM_UNLIKELY(!is_valid_nla)) { - dropped_invalid++; - pgm_rxw_lost (window, skb->sequence); -/* mark receiver window for flushing on next recv() */ - pgm_peer_set_pending (sock, peer); - list = next_list_el; - continue; - } - - pgm_rxw_state (window, skb, PGM_PKT_STATE_WAIT_NCF); - nak_list.sqn[nak_list.len++] = skb->sequence; - state->nak_transmit_count++; - -/* we have two options here, calculate the expiry time in the new state relative to the current - * state execution time, skipping missed expirations due to delay in state processing, or base - * from the actual current time. - */ -#ifdef PGM_ABSOLUTE_EXPIRY - state->timer_expiry += sock->nak_rpt_ivl; - while (pgm_time_after_eq(now, state->timer_expiry)){ - state->timer_expiry += sock->nak_rpt_ivl; - state->ncf_retry_count++; - } -#else - state->timer_expiry = now + sock->nak_rpt_ivl; -pgm_trace(PGM_LOG_ROLE_NETWORK,_("nak_rpt_expiry in %f seconds."), - pgm_to_secsf( state->timer_expiry - now ) ); -#endif - pgm_timer_lock (sock); - if (pgm_time_after (sock->next_poll, state->timer_expiry)) - sock->next_poll = state->timer_expiry; - pgm_timer_unlock (sock); - - if (nak_list.len == PGM_N_ELEMENTS(nak_list.sqn)) { - if (sock->can_send_nak && !send_nak_list (sock, peer, &nak_list)) - return FALSE; - nak_list.len = 0; - } - } - else - { /* packet expires some time later */ - break; - } - - list = next_list_el; - } - - if (sock->can_send_nak && nak_list.len) - { - if (nak_list.len > 1 && !send_nak_list (sock, peer, &nak_list)) - return FALSE; - else if (!send_nak (sock, peer, nak_list.sqn[0])) - return FALSE; - } - - } - - if (PGM_UNLIKELY(dropped_invalid)) - { - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Dropped %u messages due to invalid NLA."), dropped_invalid); - -/* mark receiver window for flushing on next recv() */ - if (window->cumulative_losses != peer->last_cumulative_losses && - !peer->pending_link.data) - { - sock->is_reset = TRUE; - peer->lost_count = window->cumulative_losses - peer->last_cumulative_losses; - peer->last_cumulative_losses = window->cumulative_losses; - pgm_peer_set_pending (sock, peer); - } - } - - if (window->nak_backoff_queue.length == 0) - { - pgm_assert ((struct rxw_packet*)window->nak_backoff_queue.head == NULL); - pgm_assert ((struct rxw_packet*)window->nak_backoff_queue.tail == NULL); - } - else - { - pgm_assert ((struct rxw_packet*)window->nak_backoff_queue.head != NULL); - pgm_assert ((struct rxw_packet*)window->nak_backoff_queue.tail != NULL); - } - - if (window->nak_backoff_queue.tail) - { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Next expiry set in %f seconds."), - pgm_to_secsf(next_nak_rb_expiry(window) - now)); - } - else - { - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("NAK backoff queue empty.")); - } - return TRUE; -} - -/* check this peer for NAK state timers, uses the tail of each queue for the nearest - * timer execution. - * - * returns TRUE on complete sweep, returns FALSE if operation would block. - */ - -bool -pgm_check_peer_state ( - pgm_sock_t* sock, - const pgm_time_t now - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - - pgm_debug ("pgm_check_peer_state (sock:%p now:%" PGM_TIME_FORMAT ")", - (const void*)sock, now); - - if (!sock->peers_list) - return TRUE; - - pgm_list_t* list = sock->peers_list; - do { - pgm_list_t* next = list->next; - pgm_peer_t* peer = list->data; - pgm_rxw_t* window = peer->window; - - if (peer->spmr_expiry) - { - if (pgm_time_after_eq (now, peer->spmr_expiry)) - { - if (sock->can_send_nak) { - if (!send_spmr (sock, peer)) { - return FALSE; - } - peer->spmr_tstamp = now; - } - peer->spmr_expiry = 0; - } - } - - if (window->ack_backoff_queue.tail) - { - if (pgm_time_after_eq (now, next_ack_rb_expiry (window))) - if (!ack_rb_state (peer, now)) { - return FALSE; - } - } - - if (window->nak_backoff_queue.tail) - { - if (pgm_time_after_eq (now, next_nak_rb_expiry (window))) - if (!nak_rb_state (peer, now)) { - return FALSE; - } - } - - if (window->wait_ncf_queue.tail) - { - if (pgm_time_after_eq (now, next_nak_rpt_expiry (window))) - nak_rpt_state (peer, now); - } - - if (window->wait_data_queue.tail) - { - if (pgm_time_after_eq (now, next_nak_rdata_expiry (window))) - nak_rdata_state (peer, now); - } - -/* expired, remove from hash table and linked list */ - if (pgm_time_after_eq (now, peer->expiry)) - { - if (peer->pending_link.data) - { - pgm_trace (PGM_LOG_ROLE_SESSION,_("Peer expiration postponed due to committing data, tsi %s"), pgm_tsi_print (&peer->tsi)); - peer->expiry += sock->peer_expiry; - } - else if (window->committed_count) - { - pgm_trace (PGM_LOG_ROLE_SESSION,_("Peer expiration postponed due to committed data, tsi %s"), pgm_tsi_print (&peer->tsi)); - peer->expiry += sock->peer_expiry; - } - else - { - pgm_trace (PGM_LOG_ROLE_SESSION,_("Peer expired, tsi %s"), pgm_tsi_print (&peer->tsi)); - pgm_hashtable_remove (sock->peers_hashtable, &peer->tsi); - sock->peers_list = pgm_list_remove_link (sock->peers_list, &peer->peers_link); - if (sock->last_hash_value == peer) - sock->last_hash_value = NULL; - pgm_peer_unref (peer); - } - } - - list = next; - } while (list); - -/* check for waiting contiguous packets */ - if (sock->peers_pending && !sock->is_pending_read) - { - pgm_debug ("prod rx thread"); - pgm_notify_send (&sock->pending_notify); - sock->is_pending_read = TRUE; - } - return TRUE; -} - -/* find the next state expiration time among the socks peers. - * - * on success, returns the earliest of the expiration parameter or next - * peer expiration time. - */ - -pgm_time_t -pgm_min_receiver_expiry ( - pgm_time_t expiration, /* absolute time */ - pgm_sock_t* sock - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - - pgm_debug ("pgm_min_receiver_expiry (expiration:%" PGM_TIME_FORMAT " sock:%p)", - expiration, (const void*)sock); - - if (!sock->peers_list) - return expiration; - - pgm_list_t* list = sock->peers_list; - do { - pgm_list_t* next = list->next; - pgm_peer_t* peer = (pgm_peer_t*)list->data; - pgm_rxw_t* window = peer->window; - - if (peer->spmr_expiry) - { - if (pgm_time_after_eq (expiration, peer->spmr_expiry)) - expiration = peer->spmr_expiry; - } - - if (window->ack_backoff_queue.tail) - { - if (pgm_time_after_eq (expiration, next_ack_rb_expiry (window))) - expiration = next_ack_rb_expiry (window); - } - - if (window->nak_backoff_queue.tail) - { - if (pgm_time_after_eq (expiration, next_nak_rb_expiry (window))) - expiration = next_nak_rb_expiry (window); - } - - if (window->wait_ncf_queue.tail) - { - if (pgm_time_after_eq (expiration, next_nak_rpt_expiry (window))) - expiration = next_nak_rpt_expiry (window); - } - - if (window->wait_data_queue.tail) - { - if (pgm_time_after_eq (expiration, next_nak_rdata_expiry (window))) - expiration = next_nak_rdata_expiry (window); - } - - list = next; - } while (list); - - return expiration; -} - -/* check WAIT_NCF_STATE, on expiration move back to BACK-OFF_STATE, on exceeding NAK_NCF_RETRIES - * cancel the sequence number. - */ -static -void -nak_rpt_state ( - pgm_peer_t* peer, - const pgm_time_t now - ) -{ -/* pre-conditions */ - pgm_assert (NULL != peer); - - pgm_debug ("nak_rpt_state (peer:%p now:%" PGM_TIME_FORMAT ")", - (void*)peer, now); - - pgm_rxw_t* window = peer->window; - pgm_sock_t* sock = peer->sock; - pgm_list_t* list = window->wait_ncf_queue.tail; - - unsigned dropped_invalid = 0; - unsigned dropped = 0; - -/* have not learned this peers NLA */ - const bool is_valid_nla = 0 != peer->nla.ss_family; - - while (list) - { - pgm_list_t* next_list_el = list->prev; - struct pgm_sk_buff_t* skb = (struct pgm_sk_buff_t*)list; - pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; - -/* check this packet for state expiration */ - if (pgm_time_after_eq (now, state->timer_expiry)) - { - if (PGM_UNLIKELY(!is_valid_nla)) { - dropped_invalid++; - pgm_rxw_lost (window, skb->sequence); -/* mark receiver window for flushing on next recv() */ - pgm_peer_set_pending (sock, peer); - list = next_list_el; - continue; - } - - if (++state->ncf_retry_count >= sock->nak_ncf_retries) - { - dropped++; - cancel_skb (sock, peer, skb, now); - peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_NCF_RETRIES_EXCEEDED]++; - } - else - { -/* retry */ -// state->timer_expiry += nak_rb_ivl(sock); - state->timer_expiry = now + nak_rb_ivl (sock); - pgm_rxw_state (window, skb, PGM_PKT_STATE_BACK_OFF); - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("NCF retry #%u attempt %u/%u."), skb->sequence, state->ncf_retry_count, sock->nak_ncf_retries); - } - } - else - { -/* packet expires some time later */ - pgm_trace(PGM_LOG_ROLE_RX_WINDOW,_("NCF retry #%u is delayed %f seconds."), - skb->sequence, pgm_to_secsf (state->timer_expiry - now)); - break; - } - - list = next_list_el; - } - - if (window->wait_ncf_queue.length == 0) - { - pgm_assert ((pgm_rxw_state_t*)window->wait_ncf_queue.head == NULL); - pgm_assert ((pgm_rxw_state_t*)window->wait_ncf_queue.tail == NULL); - } - else - { - pgm_assert ((pgm_rxw_state_t*)window->wait_ncf_queue.head != NULL); - pgm_assert ((pgm_rxw_state_t*)window->wait_ncf_queue.tail != NULL); - } - - if (PGM_UNLIKELY(dropped_invalid)) { - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Dropped %u messages due to invalid NLA."), dropped_invalid); - } - - if (PGM_UNLIKELY(dropped)) { - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Dropped %u messages due to ncf cancellation, " - "rxw_sqns %" PRIu32 - " bo %" PRIu32 - " ncf %" PRIu32 - " wd %" PRIu32 - " lost %" PRIu32 - " frag %" PRIu32), - dropped, - pgm_rxw_length (window), - window->nak_backoff_queue.length, - window->wait_ncf_queue.length, - window->wait_data_queue.length, - window->lost_count, - window->fragment_count); - } - -/* mark receiver window for flushing on next recv() */ - if (PGM_UNLIKELY(window->cumulative_losses != peer->last_cumulative_losses && - !peer->pending_link.data)) - { - sock->is_reset = TRUE; - peer->lost_count = window->cumulative_losses - peer->last_cumulative_losses; - peer->last_cumulative_losses = window->cumulative_losses; - pgm_peer_set_pending (sock, peer); - } - - if (window->wait_ncf_queue.tail) - { - if (next_nak_rpt_expiry (window) > now) - { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Next expiry set in %f seconds."), pgm_to_secsf (next_nak_rpt_expiry (window) - now)); - } else { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Next expiry set in -%f seconds."), pgm_to_secsf (now - next_nak_rpt_expiry (window))); - } - } - else - { - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Wait ncf queue empty.")); - } -} - -/* check WAIT_DATA_STATE, on expiration move back to BACK-OFF_STATE, on exceeding NAK_DATA_RETRIES - * canel the sequence number. - */ -static -void -nak_rdata_state ( - pgm_peer_t* peer, - const pgm_time_t now - ) -{ -/* pre-conditions */ - pgm_assert (NULL != peer); - - pgm_debug ("nak_rdata_state (peer:%p now:%" PGM_TIME_FORMAT ")", - (const void*)peer, now); - - pgm_rxw_t* window = peer->window; - pgm_sock_t* sock = peer->sock; - pgm_list_t* list = window->wait_data_queue.tail; - - unsigned dropped_invalid = 0; - unsigned dropped = 0; - -/* have not learned this peers NLA */ - const bool is_valid_nla = 0 != peer->nla.ss_family; - - while (list) - { - pgm_list_t* next_list_el = list->prev; - struct pgm_sk_buff_t* rdata_skb = (struct pgm_sk_buff_t*)list; - pgm_assert (NULL != rdata_skb); - pgm_rxw_state_t* rdata_state = (pgm_rxw_state_t*)&rdata_skb->cb; - -/* check this packet for state expiration */ - if (pgm_time_after_eq (now, rdata_state->timer_expiry)) - { - if (PGM_UNLIKELY(!is_valid_nla)) { - dropped_invalid++; - pgm_rxw_lost (window, rdata_skb->sequence); -/* mark receiver window for flushing on next recv() */ - pgm_peer_set_pending (sock, peer); - list = next_list_el; - continue; - } - - if (++rdata_state->data_retry_count >= sock->nak_data_retries) - { - dropped++; - cancel_skb (sock, peer, rdata_skb, now); - peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_DATA_RETRIES_EXCEEDED]++; - list = next_list_el; - continue; - } - -// rdata_state->timer_expiry += nak_rb_ivl(sock); - rdata_state->timer_expiry = now + nak_rb_ivl (sock); - pgm_rxw_state (window, rdata_skb, PGM_PKT_STATE_BACK_OFF); - -/* retry back to back-off state */ - pgm_trace(PGM_LOG_ROLE_RX_WINDOW,_("Data retry #%u attempt %u/%u."), rdata_skb->sequence, rdata_state->data_retry_count, sock->nak_data_retries); - } - else - { /* packet expires some time later */ - break; - } - - - list = next_list_el; - } - - if (window->wait_data_queue.length == 0) - { - pgm_assert (NULL == (pgm_rxw_state_t*)window->wait_data_queue.head); - pgm_assert (NULL == (pgm_rxw_state_t*)window->wait_data_queue.tail); - } - else - { - pgm_assert (NULL != (pgm_rxw_state_t*)window->wait_data_queue.head); - pgm_assert (NULL != (pgm_rxw_state_t*)window->wait_data_queue.tail); - } - - if (PGM_UNLIKELY(dropped_invalid)) { - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Dropped %u messages due to invalid NLA."), dropped_invalid); - } - - if (PGM_UNLIKELY(dropped)) { - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Dropped %u messages due to data cancellation."), dropped); - } - -/* mark receiver window for flushing on next recv() */ - if (PGM_UNLIKELY(window->cumulative_losses != peer->last_cumulative_losses && - !peer->pending_link.data)) - { - sock->is_reset = TRUE; - peer->lost_count = window->cumulative_losses - peer->last_cumulative_losses; - peer->last_cumulative_losses = window->cumulative_losses; - pgm_peer_set_pending (sock, peer); - } - - if (window->wait_data_queue.tail) { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Next expiry set in %f seconds."), pgm_to_secsf (next_nak_rdata_expiry (window) - now)); - } else { - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Wait data queue empty.")); - } -} - -/* ODATA or RDATA packet with any of the following options: - * - * OPT_FRAGMENT - this TPDU part of a larger APDU. - * - * Ownership of skb is taken and must be passed to the receive window or destroyed. - * - * returns TRUE is skb has been replaced, FALSE is remains unchanged and can be recycled. - */ - -bool -pgm_on_data ( - pgm_sock_t* const restrict sock, - pgm_peer_t* const restrict source, - struct pgm_sk_buff_t* const restrict skb - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != source); - pgm_assert (NULL != skb); - - pgm_debug ("pgm_on_data (sock:%p source:%p skb:%p)", - (void*)sock, (void*)source, (void*)skb); - - unsigned msg_count = 0; - const pgm_time_t nak_rb_expiry = skb->tstamp + nak_rb_ivl (sock); - pgm_time_t ack_rb_expiry = 0; - const unsigned tsdu_length = ntohs (skb->pgm_header->pgm_tsdu_length); - - skb->pgm_data = skb->data; - - const unsigned opt_total_length = (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) ? ntohs(*(uint16_t*)( (char*)( skb->pgm_data + 1 ) + sizeof(uint16_t))) : 0; - -/* advance data pointer to payload */ - pgm_skb_pull (skb, sizeof(struct pgm_data) + opt_total_length); - - if (opt_total_length > 0 && /* there are options */ - get_pgm_options (skb) && /* valid options */ - sock->use_pgmcc && /* PGMCC is enabled */ - NULL != skb->pgm_opt_pgmcc_data && /* PGMCC options */ - 0 == source->ack_rb_expiry) /* not partaking in a current election */ - { - ack_rb_expiry = skb->tstamp + ack_rb_ivl (sock); - } - - const int add_status = pgm_rxw_add (source->window, skb, skb->tstamp, nak_rb_expiry); - -/* skb reference is now invalid */ - bool flush_naks = FALSE; - - switch (add_status) { - case PGM_RXW_MISSING: - flush_naks = TRUE; -/* fall through */ - case PGM_RXW_INSERTED: - case PGM_RXW_APPENDED: - msg_count++; - break; - - case PGM_RXW_DUPLICATE: - source->cumulative_stats[PGM_PC_RECEIVER_DUP_DATAS]++; - goto discarded; - - case PGM_RXW_MALFORMED: - source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_ODATA]++; -/* fall through */ - case PGM_RXW_BOUNDS: -discarded: - return FALSE; - - default: pgm_assert_not_reached(); break; - } - -/* valid data */ - PGM_HISTOGRAM_COUNTS("Rx.DataBytesReceived", tsdu_length); - source->cumulative_stats[PGM_PC_RECEIVER_DATA_BYTES_RECEIVED] += tsdu_length; - source->cumulative_stats[PGM_PC_RECEIVER_DATA_MSGS_RECEIVED] += msg_count; - -/* congestion control */ - if (0 != ack_rb_expiry) - { -/* save source timestamp and local timestamp for RTT calculation */ - source->ack_last_tstamp = ntohl (skb->pgm_opt_pgmcc_data->opt_tstamp); - source->last_data_tstamp = skb->tstamp; - if (_pgm_is_acker (source, skb)) - { - if (PGM_UNLIKELY(pgm_sockaddr_is_addr_unspecified ((struct sockaddr*)&source->nla))) - { - pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Unable to send ACK due to unknown NLA.")); - } - else if (PGM_UNLIKELY(!send_ack (sock, source, skb->tstamp))) - { - pgm_debug ("send_ack failed"); - } - ack_rb_expiry = 0; - } - else if (_pgm_is_acker_election (skb)) - { - pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("ACKer election.")); - _pgm_add_ack (source, ack_rb_expiry); - } - else if (0 != source->window->ack_backoff_queue.length) - { -/* purge ACK backoff queue as host is not elected ACKer */ - _pgm_remove_ack (source); - ack_rb_expiry = 0; - } - else - { -/* no election, not the elected ACKer, no outstanding ACKs */ - ack_rb_expiry = 0; - } - } - - if (flush_naks || 0 != ack_rb_expiry) { -/* flush out 1st time nak packets */ - pgm_timer_lock (sock); - if (flush_naks && pgm_time_after (sock->next_poll, nak_rb_expiry)) - sock->next_poll = nak_rb_expiry; - if (0 != ack_rb_expiry && pgm_time_after (sock->next_poll, ack_rb_expiry)) - sock->next_poll = ack_rb_expiry; - pgm_timer_unlock (sock); - } - return TRUE; -} - -/* POLLs are generated by PGM Parents (Sources or Network Elements). - * - * returns TRUE on valid packet, FALSE on invalid packet. - */ - -bool -pgm_on_poll ( - pgm_sock_t* const restrict sock, - pgm_peer_t* const restrict source, - struct pgm_sk_buff_t* const restrict skb - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != source); - pgm_assert (NULL != skb); - - pgm_debug ("pgm_on_poll (sock:%p source:%p skb:%p)", - (void*)sock, (void*)source, (void*)skb); - - if (PGM_UNLIKELY(!pgm_verify_poll (skb))) { - pgm_trace(PGM_LOG_ROLE_NETWORK,_("Discarded invalid POLL.")); - return FALSE; - } - - struct pgm_poll* poll4 = (struct pgm_poll*) skb->data; - struct pgm_poll6* poll6 = (struct pgm_poll6*)skb->data; - uint32_t poll_rand; - memcpy (&poll_rand, (AFI_IP6 == ntohs (poll4->poll_nla_afi)) ? poll6->poll6_rand : poll4->poll_rand, sizeof(poll_rand)); - const uint32_t poll_mask = (AFI_IP6 == ntohs (poll4->poll_nla_afi)) ? ntohl (poll6->poll6_mask) : ntohl (poll4->poll_mask); - -/* Check for probability match */ - if (poll_mask && - (sock->rand_node_id & poll_mask) != poll_rand) - { -/* discard early */ - return FALSE; - } - -/* scoped per path nla - * TODO: manage list of pollers per peer - */ - const uint32_t poll_sqn = ntohl (poll4->poll_sqn); - const uint16_t poll_round = ntohs (poll4->poll_round); - -/* Check for new poll round */ - if (poll_round && - poll_sqn != source->last_poll_sqn) - { - return FALSE; - } - -/* save sequence and round of valid poll */ - source->last_poll_sqn = poll_sqn; - source->last_poll_round = poll_round; - - const uint16_t poll_s_type = ntohs (poll4->poll_s_type); - -/* Check poll type */ - switch (poll_s_type) { - case PGM_POLL_GENERAL: - return on_general_poll (sock, source, skb); - - case PGM_POLL_DLR: - return on_dlr_poll (sock, source, skb); - - default: -/* unknown sub-type, discard */ - break; - } - - return FALSE; -} - -/* Used to count PGM children */ - -static -bool -on_general_poll ( - pgm_sock_t* const restrict sock, - pgm_peer_t* const restrict source, - struct pgm_sk_buff_t* const restrict skb - ) -{ - struct pgm_poll* poll4 = (struct pgm_poll*) skb->data; - struct pgm_poll6* poll6 = (struct pgm_poll6*)skb->data; - -/* TODO: cancel any pending poll-response */ - -/* defer response based on provided back-off interval */ - const uint32_t poll_bo_ivl = (AFI_IP6 == ntohs (poll4->poll_nla_afi)) ? ntohl (poll6->poll6_bo_ivl) : ntohl (poll4->poll_bo_ivl); - source->polr_expiry = skb->tstamp + pgm_rand_int_range (&sock->rand_, 0, poll_bo_ivl); - pgm_nla_to_sockaddr (&poll4->poll_nla_afi, (struct sockaddr*)&source->poll_nla); -/* TODO: schedule poll-response */ - - return TRUE; -} - -/* Used to count off-tree DLRs */ - -static -bool -on_dlr_poll ( - PGM_GNUC_UNUSED pgm_sock_t* const restrict sock, - PGM_GNUC_UNUSED pgm_peer_t* const restrict source, - PGM_GNUC_UNUSED struct pgm_sk_buff_t* const restrict skb - ) -{ -/* we are not a DLR */ - return FALSE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/receiver_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/receiver_unittest.c deleted file mode 100644 index 5221a0b..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/receiver_unittest.c +++ /dev/null @@ -1,857 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for PGM receiver transport. - * - * Copyright (c) 2009-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include -#include - - -/* mock state */ - -#define TEST_NETWORK "" -#define TEST_PORT 7500 -#define TEST_MAX_TPDU 1500 -#define TEST_TXW_SQNS 32 -#define TEST_RXW_SQNS 32 -#define TEST_HOPS 16 -#define TEST_SPM_AMBIENT ( pgm_secs(30) ) -#define TEST_SPM_HEARTBEAT_INIT { pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(1300), pgm_secs(7), pgm_secs(16), pgm_secs(25), pgm_secs(30) } -#define TEST_PEER_EXPIRY ( pgm_secs(300) ) -#define TEST_SPMR_EXPIRY ( pgm_msecs(250) ) -#define TEST_NAK_BO_IVL ( pgm_msecs(50) ) -#define TEST_NAK_RPT_IVL ( pgm_secs(2) ) -#define TEST_NAK_RDATA_IVL ( pgm_secs(2) ) -#define TEST_NAK_DATA_RETRIES 5 -#define TEST_NAK_NCF_RETRIES 2 - - -#define pgm_histogram_add mock_pgm_histogram_add -#define pgm_verify_spm mock_pgm_verify_spm -#define pgm_verify_nak mock_pgm_verify_nak -#define pgm_verify_ncf mock_pgm_verify_ncf -#define pgm_verify_poll mock_pgm_verify_poll -#define pgm_sendto mock_pgm_sendto -#define pgm_time_now mock_pgm_time_now -#define pgm_time_update_now mock_pgm_time_update_now -#define pgm_rxw_destroy mock_pgm_rxw_destroy -#define pgm_rxw_create mock_pgm_rxw_create -#define pgm_rxw_update mock_pgm_rxw_update -#define pgm_rxw_update_fec mock_pgm_rxw_update_fec -#define pgm_rxw_confirm mock_pgm_rxw_confirm -#define pgm_rxw_lost mock_pgm_rxw_lost -#define pgm_rxw_state mock_pgm_rxw_state -#define pgm_rxw_add mock_pgm_rxw_add -#define pgm_rxw_remove_commit mock_pgm_rxw_remove_commit -#define pgm_rxw_readv mock_pgm_rxw_readv -#define pgm_csum_fold mock_pgm_csum_fold -#define pgm_compat_csum_partial mock_pgm_compat_csum_partial -#define pgm_histogram_init mock_pgm_histogram_init - - -#define RECEIVER_DEBUG -#include "receiver.c" - - -static -void -mock_setup (void) -{ - if (!g_thread_supported ()) g_thread_init (NULL); -} - -static -struct pgm_sock_t* -generate_sock (void) -{ - struct pgm_sock_t* sock = g_malloc0 (sizeof(struct pgm_sock_t)); - return sock; -} - -static -pgm_peer_t* -generate_peer (void) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - pgm_peer_t* peer = g_malloc0 (sizeof(pgm_peer_t)); - peer->window = g_malloc0 (sizeof(pgm_rxw_t)); - pgm_atomic_inc32 (&peer->ref_count); - return peer; -} - -/** socket module */ -static -int -mock_pgm_poll_info ( - pgm_sock_t* const sock, - struct pollfd* fds, - int* n_fds, - int events - ) -{ -} - -static -gboolean -mock_pgm_on_nak ( - pgm_sock_t* const sock, - struct pgm_sk_buff_t* const skb - ) -{ - return TRUE; -} - -static -gboolean -mock_pgm_on_nnak ( - pgm_sock_t* const sock, - struct pgm_sk_buff_t* const skb - ) -{ - return TRUE; -} - -static -gboolean -mock_pgm_on_spmr ( - pgm_sock_t* const sock, - pgm_peer_t* const peer, - struct pgm_sk_buff_t* const skb - ) -{ - return TRUE; -} - -/** net module */ -PGM_GNUC_INTERNAL -ssize_t -mock_pgm_sendto ( - pgm_sock_t* sock, - bool use_rate_limit, - bool use_router_alert, - const void* buf, - size_t len, - const struct sockaddr* to, - socklen_t tolen - ) -{ - return len; -} - -/** time module */ -static pgm_time_t mock_pgm_time_now = 0x1; -static pgm_time_t _mock_pgm_time_update_now (void); -pgm_time_update_func mock_pgm_time_update_now = _mock_pgm_time_update_now; - -pgm_time_t -_mock_pgm_time_update_now (void) -{ - return mock_pgm_time_now; -} - -/* packet module */ -bool -mock_pgm_verify_spm ( - const struct pgm_sk_buff_t* const skb - ) -{ - return TRUE; -} - -bool -mock_pgm_verify_nak ( - const struct pgm_sk_buff_t* const skb - ) -{ - return TRUE; -} - -bool -mock_pgm_verify_ncf ( - const struct pgm_sk_buff_t* const skb - ) -{ - return TRUE; -} - -bool -mock_pgm_verify_poll ( - const struct pgm_sk_buff_t* const skb - ) -{ - return TRUE; -} - -/* receive window module */ -pgm_rxw_t* -mock_pgm_rxw_create ( - const pgm_tsi_t* tsi, - const uint16_t tpdu_size, - const unsigned sqns, - const unsigned secs, - const ssize_t max_rte, - const uint32_t ack_c_p - ) -{ - return g_malloc0 (sizeof(pgm_rxw_t)); -} - -void -mock_pgm_rxw_destroy ( - pgm_rxw_t* const window - ) -{ - g_assert (NULL != window); - g_free (window); -} - -int -mock_pgm_rxw_confirm ( - pgm_rxw_t* const window, - const uint32_t sequence, - const pgm_time_t now, - const pgm_time_t nak_rdata_expiry, - const pgm_time_t nak_rb_expiry - ) -{ - return PGM_RXW_DUPLICATE; -} - -void -mock_pgm_rxw_lost ( - pgm_rxw_t* const window, - const uint32_t sequence - ) -{ -} - -void -mock_pgm_rxw_state ( - pgm_rxw_t* const window, - struct pgm_sk_buff_t* const skb, - const int new_state - ) -{ -} - -unsigned -mock_pgm_rxw_update ( - pgm_rxw_t* const window, - const uint32_t txw_lead, - const uint32_t txw_trail, - const pgm_time_t now, - const pgm_time_t nak_rb_expiry - ) -{ - return 0; -} - -void -mock_pgm_rxw_update_fec ( - pgm_rxw_t* const window, - const uint8_t rs_k - ) -{ -} - -int -mock_pgm_rxw_add ( - pgm_rxw_t* const window, - struct pgm_sk_buff_t* const skb, - const pgm_time_t now, - const pgm_time_t nak_rb_expiry - ) -{ - return PGM_RXW_APPENDED; -} - -void -mock_pgm_rxw_remove_commit ( - pgm_rxw_t* const window - ) -{ -} - -ssize_t -mock_pgm_rxw_readv ( - pgm_rxw_t* const window, - struct pgm_msgv_t** pmsg, - const unsigned pmsglen - ) -{ - return 0; -} - -/* checksum module */ -uint16_t -mock_pgm_csum_fold ( - uint32_t csum - ) -{ - return 0x0; -} - -uint32_t -mock_pgm_compat_csum_partial ( - const void* addr, - uint16_t len, - uint32_t csum - ) -{ - return 0x0; -} - -void -mock_pgm_histogram_init ( - pgm_histogram_t* histogram - ) -{ -} - -void -mock_pgm_histogram_add ( - pgm_histogram_t* histogram, - int value - ) -{ -} - -/* mock functions for external references */ - -size_t -pgm_pkt_offset ( - const bool can_fragment, - const sa_family_t pgmcc_family /* 0 = disable */ - ) -{ - return 0; -} - - -/* target: - * void - * pgm_peer_unref ( - * pgm_peer_t* peer - * ) - */ - -/* last ref */ -START_TEST (test_peer_unref_pass_001) -{ - pgm_peer_t* peer = generate_peer(); - pgm_peer_unref (peer); -} -END_TEST - -/* non-last ref */ -START_TEST (test_peer_unref_pass_002) -{ - pgm_peer_t* peer = _pgm_peer_ref (generate_peer()); - pgm_peer_unref (peer); -} -END_TEST - - -START_TEST (test_peer_unref_fail_001) -{ - pgm_peer_unref (NULL); - fail ("reached"); -} -END_TEST - -/* target: - * bool - * pgm_check_peer_state ( - * pgm_sock_t* sock, - * const pgm_time_t now - * ) - */ - -START_TEST (test_check_peer_state_pass_001) -{ - pgm_sock_t* sock = generate_sock(); - sock->is_bound = TRUE; - pgm_check_peer_state (sock, mock_pgm_time_now); -} -END_TEST - -START_TEST (test_check_peer_state_fail_001) -{ - pgm_check_peer_state (NULL, mock_pgm_time_now); - fail ("reached"); -} -END_TEST - -/* target: - * pgm_time_t - * pgm_min_receiver_expiry ( - * pgm_time_t expiration, - * pgm_sock_t* sock - * ) - */ - -START_TEST (test_min_receiver_expiry_pass_001) -{ - pgm_sock_t* sock = generate_sock(); - sock->is_bound = TRUE; - const pgm_time_t expiration = pgm_secs(1); - pgm_time_t next_expiration = pgm_min_receiver_expiry (expiration, sock); -} -END_TEST - -START_TEST (test_min_receiver_expiry_fail_001) -{ - const pgm_time_t expiration = pgm_secs(1); - pgm_min_receiver_expiry (expiration, NULL); - fail ("reached"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_RXW_SQNS - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_rxw_sqns_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - const int optname = PGM_RXW_SQNS; - const int rxw_sqns = 100; - const void* optval = &rxw_sqns; - const socklen_t optlen = sizeof(int); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_rxw_sqns failed"); -} -END_TEST - -START_TEST (test_set_rxw_sqns_fail_001) -{ - const int optname = PGM_RXW_SQNS; - const int rxw_sqns = 100; - const void* optval = &rxw_sqns; - const socklen_t optlen = sizeof(int); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_rxw_sqns failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_RXW_SECS, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_rxw_secs_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - const int optname = PGM_RXW_SECS; - const int rxw_secs = 10; - const void* optval = &rxw_secs; - const socklen_t optlen = sizeof(int); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_rxw_secs failed"); -} -END_TEST - -START_TEST (test_set_rxw_secs_fail_001) -{ - const int optname = PGM_RXW_SECS; - const int rxw_secs = 10; - const void* optval = &rxw_secs; - const socklen_t optlen = sizeof(int); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_rxw_secs failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_RXW_MAX_RTE, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_rxw_max_rte_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - const int optname = PGM_RXW_MAX_RTE; - const int rxw_max_rte = 100*1000; - const void* optval = &rxw_max_rte; - const socklen_t optlen = sizeof(int); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_rxw_max_rte failed"); -} -END_TEST - -START_TEST (test_set_rxw_max_rte_fail_001) -{ - const int optname = PGM_RXW_MAX_RTE; - const int rxw_max_rte = 100*1000; - const void* optval = &rxw_max_rte; - const socklen_t optlen = sizeof(int); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_rxw_max_rte failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_PEER_EXPIRY, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_peer_expiry_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - const int optname = PGM_PEER_EXPIRY; - const int peer_expiry = pgm_secs(100); - const void* optval = &peer_expiry; - const socklen_t optlen = sizeof(int); -/* pre-checking should verify value to spm ambient interval - sock->spm_ambient_interval = pgm_secs(30); - */ - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_peer_expiry failed"); -} -END_TEST - -START_TEST (test_set_peer_expiry_fail_001) -{ - const int optname = PGM_PEER_EXPIRY; - const int peer_expiry = pgm_secs(100); - const void* optval = &peer_expiry; - const socklen_t optlen = sizeof(int); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_peer_expiry failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_SPMR_EXPIRY, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_spmr_expiry_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - const int optname = PGM_SPMR_EXPIRY; - const int spmr_expiry = pgm_secs(10); - const void* optval = &spmr_expiry; - const socklen_t optlen = sizeof(int); -/* pre-checking should verify value to spm ambient interval - sock->spm_ambient_interval = pgm_secs(30); - */ - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_spmr_expiry failed"); -} -END_TEST - -START_TEST (test_set_spmr_expiry_fail_001) -{ - const int optname = PGM_SPMR_EXPIRY; - const int spmr_expiry = pgm_secs(10); - const void* optval = &spmr_expiry; - const socklen_t optlen = sizeof(int); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_spmr_expiry failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_NAK_BO_IVL, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_nak_bo_ivl_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - const int optname = PGM_NAK_BO_IVL; - const int nak_bo_ivl = pgm_msecs(1000); - const void* optval = &nak_bo_ivl; - const socklen_t optlen = sizeof(int); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_nak_bo_ivl failed"); -} -END_TEST - -START_TEST (test_set_nak_bo_ivl_fail_001) -{ - const int optname = PGM_NAK_BO_IVL; - const int nak_bo_ivl = pgm_msecs(1000); - const void* optval = &nak_bo_ivl; - const socklen_t optlen = sizeof(int); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_nak_bo_ivl failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_NAK_RPT_IVL, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_nak_rpt_ivl_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - const int optname = PGM_NAK_RPT_IVL; - const int nak_rpt_ivl = pgm_msecs(1000); - const void* optval = &nak_rpt_ivl; - const socklen_t optlen = sizeof(int); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_nak_rpt_ivl failed"); -} -END_TEST - -START_TEST (test_set_nak_rpt_ivl_fail_001) -{ - const int optname = PGM_NAK_RPT_IVL; - const int nak_rpt_ivl = pgm_msecs(1000); - const void* optval = &nak_rpt_ivl; - const socklen_t optlen = sizeof(int); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_nak_rpt_ivl failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_NAK_RDATA_IVL, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_nak_rdata_ivl_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - const int optname = PGM_NAK_RDATA_IVL; - const int nak_rdata_ivl = pgm_msecs(1000); - const void* optval = &nak_rdata_ivl; - const socklen_t optlen = sizeof(int); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_nak_rdata_ivl failed"); -} -END_TEST - -START_TEST (test_set_nak_rdata_ivl_fail_001) -{ - const int optname = PGM_NAK_RDATA_IVL; - const int nak_rdata_ivl = pgm_msecs(1000); - const void* optval = &nak_rdata_ivl; - const socklen_t optlen = sizeof(int); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_nak_rdata_ivl failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_NAK_DATA_RETRIES, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_nak_data_retries_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - const int optname = PGM_NAK_DATA_RETRIES; - const int retries = 1000; - const void* optval = &retries; - const socklen_t optlen = sizeof(int); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_nak_data_retries failed"); -} -END_TEST - -START_TEST (test_set_nak_data_retries_fail_001) -{ - const int optname = PGM_NAK_DATA_RETRIES; - const int retries = 1000; - const void* optval = &retries; - const socklen_t optlen = sizeof(int); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_nak_data_retries failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_NAK_NCF_RETRIES, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_nak_ncf_retries_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - const int optname = PGM_NAK_NCF_RETRIES; - const int retries = 1000; - const void* optval = &retries; - const socklen_t optlen = sizeof(int); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_ncf_data_retries failed"); -} -END_TEST - -START_TEST (test_set_nak_ncf_retries_fail_001) -{ - const int optname = PGM_NAK_NCF_RETRIES; - const int retries = 1000; - const void* optval = &retries; - const socklen_t optlen = sizeof(int); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_ncf_data_retries failed"); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_peer_unref = tcase_create ("peer_unref"); - suite_add_tcase (s, tc_peer_unref); - tcase_add_checked_fixture (tc_peer_unref, mock_setup, NULL); - tcase_add_test (tc_peer_unref, test_peer_unref_pass_001); - tcase_add_test_raise_signal (tc_peer_unref, test_peer_unref_fail_001, SIGABRT); - -/* formally check-peer-nak-state */ - TCase* tc_check_peer_state = tcase_create ("check-peer-state"); - suite_add_tcase (s, tc_check_peer_state); - tcase_add_checked_fixture (tc_check_peer_state, mock_setup, NULL); - tcase_add_test (tc_check_peer_state, test_check_peer_state_pass_001); - tcase_add_test_raise_signal (tc_check_peer_state, test_check_peer_state_fail_001, SIGABRT); - -/* formally min-nak-expiry */ - TCase* tc_min_receiver_expiry = tcase_create ("min-receiver-expiry"); - suite_add_tcase (s, tc_min_receiver_expiry); - tcase_add_checked_fixture (tc_min_receiver_expiry, mock_setup, NULL); - tcase_add_test (tc_min_receiver_expiry, test_min_receiver_expiry_pass_001); - tcase_add_test_raise_signal (tc_min_receiver_expiry, test_min_receiver_expiry_fail_001, SIGABRT); - - TCase* tc_set_rxw_sqns = tcase_create ("set-rxw_sqns"); - suite_add_tcase (s, tc_set_rxw_sqns); - tcase_add_checked_fixture (tc_set_rxw_sqns, mock_setup, NULL); - tcase_add_test (tc_set_rxw_sqns, test_set_rxw_sqns_pass_001); - tcase_add_test (tc_set_rxw_sqns, test_set_rxw_sqns_fail_001); - - TCase* tc_set_rxw_secs = tcase_create ("set-rxw-secs"); - suite_add_tcase (s, tc_set_rxw_secs); - tcase_add_checked_fixture (tc_set_rxw_secs, mock_setup, NULL); - tcase_add_test (tc_set_rxw_secs, test_set_rxw_secs_pass_001); - tcase_add_test (tc_set_rxw_secs, test_set_rxw_secs_fail_001); - - TCase* tc_set_rxw_max_rte = tcase_create ("set-rxw-max-rte"); - suite_add_tcase (s, tc_set_rxw_max_rte); - tcase_add_checked_fixture (tc_set_rxw_max_rte, mock_setup, NULL); - tcase_add_test (tc_set_rxw_max_rte, test_set_rxw_max_rte_pass_001); - tcase_add_test (tc_set_rxw_max_rte, test_set_rxw_max_rte_fail_001); - - TCase* tc_set_peer_expiry = tcase_create ("set-peer-expiry"); - suite_add_tcase (s, tc_set_peer_expiry); - tcase_add_checked_fixture (tc_set_peer_expiry, mock_setup, NULL); - tcase_add_test (tc_set_peer_expiry, test_set_peer_expiry_pass_001); - tcase_add_test (tc_set_peer_expiry, test_set_peer_expiry_fail_001); - - TCase* tc_set_spmr_expiry = tcase_create ("set-spmr-expiry"); - suite_add_tcase (s, tc_set_spmr_expiry); - tcase_add_checked_fixture (tc_set_spmr_expiry, mock_setup, NULL); - tcase_add_test (tc_set_spmr_expiry, test_set_spmr_expiry_pass_001); - tcase_add_test (tc_set_spmr_expiry, test_set_spmr_expiry_fail_001); - - TCase* tc_set_nak_bo_ivl = tcase_create ("set-nak-bo-ivl"); - suite_add_tcase (s, tc_set_nak_bo_ivl); - tcase_add_checked_fixture (tc_set_nak_bo_ivl, mock_setup, NULL); - tcase_add_test (tc_set_nak_bo_ivl, test_set_nak_bo_ivl_pass_001); - tcase_add_test (tc_set_nak_bo_ivl, test_set_nak_bo_ivl_fail_001); - - TCase* tc_set_nak_rpt_ivl = tcase_create ("set-nak-rpt-ivl"); - suite_add_tcase (s, tc_set_nak_rpt_ivl); - tcase_add_checked_fixture (tc_set_nak_rpt_ivl, mock_setup, NULL); - tcase_add_test (tc_set_nak_rpt_ivl, test_set_nak_rpt_ivl_pass_001); - tcase_add_test (tc_set_nak_rpt_ivl, test_set_nak_rpt_ivl_fail_001); - - TCase* tc_set_nak_rdata_ivl = tcase_create ("set-nak-rdata-ivl"); - suite_add_tcase (s, tc_set_nak_rdata_ivl); - tcase_add_checked_fixture (tc_set_nak_rdata_ivl, mock_setup, NULL); - tcase_add_test (tc_set_nak_rdata_ivl, test_set_nak_rdata_ivl_pass_001); - tcase_add_test (tc_set_nak_rdata_ivl, test_set_nak_rdata_ivl_fail_001); - - TCase* tc_set_nak_data_retries = tcase_create ("set-nak-data-retries"); - suite_add_tcase (s, tc_set_nak_data_retries); - tcase_add_checked_fixture (tc_set_nak_data_retries, mock_setup, NULL); - tcase_add_test (tc_set_nak_data_retries, test_set_nak_data_retries_pass_001); - tcase_add_test (tc_set_nak_data_retries, test_set_nak_data_retries_fail_001); - - TCase* tc_set_nak_ncf_retries = tcase_create ("set-nak-ncf-retries"); - suite_add_tcase (s, tc_set_nak_ncf_retries); - tcase_add_checked_fixture (tc_set_nak_ncf_retries, mock_setup, NULL); - tcase_add_test (tc_set_nak_ncf_retries, test_set_nak_ncf_retries_pass_001); - tcase_add_test (tc_set_nak_ncf_retries, test_set_nak_ncf_retries_fail_001); - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/recv.c b/3rdparty/openpgm-svn-r1085/pgm/recv.c deleted file mode 100644 index 63cbce5..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/recv.c +++ /dev/null @@ -1,1059 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Transport recv API. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define _GNU_SOURCE -#include -#ifndef _WIN32 -# include -# include -# include /* _GNU_SOURCE for in6_pktinfo */ -#else -# include -# include -#endif -#include -#include -#include -#include -#include -#include - - -//#define RECV_DEBUG - -#ifndef RECV_DEBUG -# define PGM_DISABLE_ASSERT -#endif - -#ifdef _WIN32 -# define cmsghdr wsacmsghdr -# define CMSG_FIRSTHDR(msg) WSA_CMSG_FIRSTHDR(msg) -# define CMSG_NXTHDR(msg, cmsg) WSA_CMSG_NXTHDR(msg, cmsg) -# define CMSG_DATA(cmsg) WSA_CMSG_DATA(cmsg) -# define CMSG_SPACE(len) WSA_CMSG_SPACE(len) -# define CMSG_LEN(len) WSA_CMSG_LEN(len) -#endif - - -/* read a packet into a PGM skbuff - * on success returns packet length, on closed socket returns 0, - * on error returns -1. - */ - -static -ssize_t -recvskb ( - pgm_sock_t* const restrict sock, - struct pgm_sk_buff_t* const restrict skb, - const int flags, - struct sockaddr* const restrict src_addr, - const socklen_t src_addrlen, - struct sockaddr* const restrict dst_addr, - const socklen_t dst_addrlen - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != skb); - pgm_assert (NULL != src_addr); - pgm_assert (src_addrlen > 0); - pgm_assert (NULL != dst_addr); - pgm_assert (dst_addrlen > 0); - - pgm_debug ("recvskb (sock:%p skb:%p flags:%d src-addr:%p src-addrlen:%d dst-addr:%p dst-addrlen:%d)", - (void*)sock, (void*)skb, flags, (void*)src_addr, (int)src_addrlen, (void*)dst_addr, (int)dst_addrlen); - - if (PGM_UNLIKELY(sock->is_destroyed)) - return 0; - -#ifdef CONFIG_TARGET_WINE - socklen_t fromlen = src_addrlen; - const ssize_t len = recvfrom (sock->recv_sock, skb->head, sock->max_tpdu, 0, src_addr, &fromlen); - if (len <= 0) - return len; -#else - struct pgm_iovec iov = { - .iov_base = skb->head, - .iov_len = sock->max_tpdu - }; - char aux[ 1024 ]; -# ifndef _WIN32 - struct msghdr msg = { - .msg_name = src_addr, - .msg_namelen = src_addrlen, - .msg_iov = (void*)&iov, - .msg_iovlen = 1, - .msg_control = aux, - .msg_controllen = sizeof(aux), - .msg_flags = 0 - }; - - ssize_t len = recvmsg (sock->recv_sock, &msg, flags); - if (len <= 0) - return len; -# else /* !_WIN32 */ - WSAMSG msg = { - .name = (LPSOCKADDR)src_addr, - .namelen = src_addrlen, - .lpBuffers = (LPWSABUF)&iov, - .dwBufferCount = 1, - .dwFlags = 0 - }; - msg.Control.buf = aux; - msg.Control.len = sizeof(aux); - DWORD len; - if (SOCKET_ERROR == pgm_WSARecvMsg (sock->recv_sock, &msg, &len, NULL, NULL)) { - return -1; - } -# endif /* !_WIN32 */ -#endif /* !CONFIG_TARGET_WINE */ - -#ifdef PGM_DEBUG - if (PGM_UNLIKELY(pgm_loss_rate > 0)) { - const unsigned percent = pgm_rand_int_range (&sock->rand_, 0, 100); - if (percent <= pgm_loss_rate) { - pgm_debug ("Simulated packet loss"); -# ifndef _WIN32 - errno = EAGAIN; -# else - WSASetLastError (WSAEWOULDBLOCK); -# endif - return -1; - } - } -#endif - - skb->sock = sock; - skb->tstamp = pgm_time_update_now(); - skb->data = skb->head; - skb->len = len; - skb->zero_padded = 0; - skb->tail = (char*)skb->data + len; - -#ifdef CONFIG_TARGET_WINE - pgm_assert (pgm_sockaddr_len (&sock->recv_gsr[0].gsr_group) <= dst_addrlen); - memcpy (dst_addr, &sock->recv_gsr[0].gsr_group, pgm_sockaddr_len (&sock->recv_gsr[0].gsr_group)); -#else - if (sock->udp_encap_ucast_port || - AF_INET6 == pgm_sockaddr_family (src_addr)) - { -#ifdef CONFIG_HAVE_WSACMSGHDR - WSACMSGHDR* cmsg; -#else - struct cmsghdr* cmsg; -#endif - for (cmsg = CMSG_FIRSTHDR(&msg); - cmsg != NULL; - cmsg = CMSG_NXTHDR(&msg, cmsg)) - { -/* both IP_PKTINFO and IP_RECVDSTADDR exist on OpenSolaris, so capture - * each type if defined. - */ -#ifdef IP_PKTINFO - if (IPPROTO_IP == cmsg->cmsg_level && - IP_PKTINFO == cmsg->cmsg_type) - { - const void* pktinfo = CMSG_DATA(cmsg); -/* discard on invalid address */ - if (PGM_UNLIKELY(NULL == pktinfo)) { - pgm_debug ("in_pktinfo is NULL"); - return -1; - } - const struct in_pktinfo* in = pktinfo; - struct sockaddr_in s4; - memset (&s4, 0, sizeof(s4)); - s4.sin_family = AF_INET; - s4.sin_addr.s_addr = in->ipi_addr.s_addr; - memcpy (dst_addr, &s4, sizeof(s4)); - break; - } -#endif -#ifdef IP_RECVDSTADDR - if (IPPROTO_IP == cmsg->cmsg_level && - IP_RECVDSTADDR == cmsg->cmsg_type) - { - const void* recvdstaddr = CMSG_DATA(cmsg); -/* discard on invalid address */ - if (PGM_UNLIKELY(NULL == recvdstaddr)) { - pgm_debug ("in_recvdstaddr is NULL"); - return -1; - } - const struct in_addr* in = recvdstaddr; - struct sockaddr_in s4; - memset (&s4, 0, sizeof(s4)); - s4.sin_family = AF_INET; - s4.sin_addr.s_addr = in->s_addr; - memcpy (dst_addr, &s4, sizeof(s4)); - break; - } -#endif -#if !defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR) -# error "No defined CMSG type for IPv4 destination address." -#endif - - if (IPPROTO_IPV6 == cmsg->cmsg_level && - IPV6_PKTINFO == cmsg->cmsg_type) - { - const void* pktinfo = CMSG_DATA(cmsg); -/* discard on invalid address */ - if (PGM_UNLIKELY(NULL == pktinfo)) { - pgm_debug ("in6_pktinfo is NULL"); - return -1; - } - const struct in6_pktinfo* in6 = pktinfo; - struct sockaddr_in6 s6; - memset (&s6, 0, sizeof(s6)); - s6.sin6_family = AF_INET6; - s6.sin6_addr = in6->ipi6_addr; - s6.sin6_scope_id = in6->ipi6_ifindex; - memcpy (dst_addr, &s6, sizeof(s6)); -/* does not set flow id */ - break; - } - } - } -#endif - return len; -} - -/* upstream = receiver to source, peer-to-peer = receive to receiver - * - * NB: SPMRs can be upstream or peer-to-peer, if the packet is multicast then its - * a peer-to-peer message, if its unicast its an upstream message. - * - * returns TRUE on valid processed packet, returns FALSE on discarded packet. - */ - -static -bool -on_upstream ( - pgm_sock_t* const restrict sock, - struct pgm_sk_buff_t* const restrict skb - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != skb); - pgm_assert_cmpuint (skb->pgm_header->pgm_dport, ==, sock->tsi.sport); - - pgm_debug ("on_upstream (sock:%p skb:%p)", - (const void*)sock, (const void*)skb); - - if (PGM_UNLIKELY(!sock->can_send_data)) { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded packet for muted source.")); - goto out_discarded; - } - -/* unicast upstream message, note that dport & sport are reversed */ - if (PGM_UNLIKELY(skb->pgm_header->pgm_sport != sock->dport)) { -/* its upstream/peer-to-peer for another session */ - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded packet on data-destination port mismatch.")); - goto out_discarded; - } - - if (PGM_UNLIKELY(!pgm_gsi_equal (&skb->tsi.gsi, &sock->tsi.gsi))) { -/* its upstream/peer-to-peer for another session */ - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded packet on data-destination port mismatch.")); - goto out_discarded; - } - -/* advance SKB pointer to PGM type header */ - skb->data = (char*)skb->data + sizeof(struct pgm_header); - skb->len -= sizeof(struct pgm_header); - - switch (skb->pgm_header->pgm_type) { - case PGM_NAK: - if (PGM_UNLIKELY(!pgm_on_nak (sock, skb))) - goto out_discarded; - break; - - case PGM_NNAK: - if (PGM_UNLIKELY(!pgm_on_nnak (sock, skb))) - goto out_discarded; - break; - - case PGM_SPMR: - if (PGM_UNLIKELY(!pgm_on_spmr (sock, NULL, skb))) - goto out_discarded; - break; - - case PGM_ACK: - if (PGM_UNLIKELY(!pgm_on_ack (sock, skb))) - goto out_discarded; - break; - - case PGM_POLR: - default: - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded unsupported PGM type packet.")); - goto out_discarded; - } - - return TRUE; -out_discarded: - sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED]++; - return FALSE; -} - -/* peer to peer message, either multicast NAK or multicast SPMR. - * - * returns TRUE on valid processed packet, returns FALSE on discarded packet. - */ - -static -bool -on_peer ( - pgm_sock_t* const restrict sock, - struct pgm_sk_buff_t* const restrict skb, - pgm_peer_t** restrict source - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != skb); - pgm_assert_cmpuint (skb->pgm_header->pgm_dport, !=, sock->tsi.sport); - pgm_assert (NULL != source); - - pgm_debug ("on_peer (sock:%p skb:%p source:%p)", - (const void*)sock, (const void*)skb, (const void*)source); - -/* we are not the source */ - if (PGM_UNLIKELY(!sock->can_recv_data)) { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded packet for muted receiver.")); - goto out_discarded; - } - -/* unicast upstream message, note that dport & sport are reversed */ - if (PGM_UNLIKELY(skb->pgm_header->pgm_sport != sock->dport)) { -/* its upstream/peer-to-peer for another session */ - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded packet on data-destination port mismatch.")); - goto out_discarded; - } - -/* check to see the source this peer-to-peer message is about is in our peer list */ - pgm_tsi_t upstream_tsi; - memcpy (&upstream_tsi.gsi, &skb->tsi.gsi, sizeof(pgm_gsi_t)); - upstream_tsi.sport = skb->pgm_header->pgm_dport; - - pgm_rwlock_reader_lock (&sock->peers_lock); - *source = pgm_hashtable_lookup (sock->peers_hashtable, &upstream_tsi); - pgm_rwlock_reader_unlock (&sock->peers_lock); - if (PGM_UNLIKELY(NULL == *source)) { -/* this source is unknown, we don't care about messages about it */ - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded peer packet about new source.")); - goto out_discarded; - } - -/* advance SKB pointer to PGM type header */ - skb->data = (char*)skb->data + sizeof(struct pgm_header); - skb->len -= sizeof(struct pgm_header); - - switch (skb->pgm_header->pgm_type) { - case PGM_NAK: - if (PGM_UNLIKELY(!pgm_on_peer_nak (sock, *source, skb))) - goto out_discarded; - break; - - case PGM_SPMR: - if (PGM_UNLIKELY(!pgm_on_spmr (sock, *source, skb))) - goto out_discarded; - break; - - case PGM_NNAK: - case PGM_POLR: - default: - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded unsupported PGM type packet.")); - goto out_discarded; - } - - return TRUE; -out_discarded: - if (*source) - (*source)->cumulative_stats[PGM_PC_RECEIVER_PACKETS_DISCARDED]++; - else if (sock->can_send_data) - sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED]++; - return FALSE; -} - -/* source to receiver message - * - * returns TRUE on valid processed packet, returns FALSE on discarded packet. - */ - -static -bool -on_downstream ( - pgm_sock_t* const restrict sock, - struct pgm_sk_buff_t* const restrict skb, - struct sockaddr* const restrict src_addr, - struct sockaddr* const restrict dst_addr, - pgm_peer_t** restrict source - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != skb); - pgm_assert (NULL != src_addr); - pgm_assert (NULL != dst_addr); - pgm_assert (NULL != source); - -#ifdef RECV_DEBUG - char saddr[INET6_ADDRSTRLEN], daddr[INET6_ADDRSTRLEN]; - pgm_sockaddr_ntop (src_addr, saddr, sizeof(saddr)); - pgm_sockaddr_ntop (dst_addr, daddr, sizeof(daddr)); - pgm_debug ("on_downstream (sock:%p skb:%p src-addr:%s dst-addr:%s source:%p)", - (const void*)sock, (const void*)skb, saddr, daddr, (const void*)source); -#endif - - if (PGM_UNLIKELY(!sock->can_recv_data)) { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded packet for muted receiver.")); - goto out_discarded; - } - -/* pgm packet DPORT contains our sock DPORT */ - if (PGM_UNLIKELY(skb->pgm_header->pgm_dport != sock->dport)) { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded packet on data-destination port mismatch.")); - goto out_discarded; - } - -/* search for TSI peer context or create a new one */ - if (PGM_LIKELY(pgm_tsi_hash (&skb->tsi) == sock->last_hash_key && - NULL != sock->last_hash_value)) - { - *source = sock->last_hash_value; - } - else - { - pgm_rwlock_reader_lock (&sock->peers_lock); - *source = pgm_hashtable_lookup_extended (sock->peers_hashtable, &skb->tsi, &sock->last_hash_key); - pgm_rwlock_reader_unlock (&sock->peers_lock); - if (PGM_UNLIKELY(NULL == *source)) { - *source = pgm_new_peer (sock, - &skb->tsi, - (struct sockaddr*)src_addr, pgm_sockaddr_len(src_addr), - (struct sockaddr*)dst_addr, pgm_sockaddr_len(dst_addr), - skb->tstamp); - } - sock->last_hash_value = *source; - } - - (*source)->cumulative_stats[PGM_PC_RECEIVER_BYTES_RECEIVED] += skb->len; - (*source)->last_packet = skb->tstamp; - - skb->data = (void*)( skb->pgm_header + 1 ); - skb->len -= sizeof(struct pgm_header); - -/* handle PGM packet type */ - switch (skb->pgm_header->pgm_type) { - case PGM_ODATA: - case PGM_RDATA: - if (PGM_UNLIKELY(!pgm_on_data (sock, *source, skb))) - goto out_discarded; - sock->rx_buffer = pgm_alloc_skb (sock->max_tpdu); - break; - - case PGM_NCF: - if (PGM_UNLIKELY(!pgm_on_ncf (sock, *source, skb))) - goto out_discarded; - break; - - case PGM_SPM: - if (PGM_UNLIKELY(!pgm_on_spm (sock, *source, skb))) - goto out_discarded; - -/* update group NLA if appropriate */ - if (PGM_LIKELY(pgm_sockaddr_is_addr_multicast ((struct sockaddr*)dst_addr))) - memcpy (&(*source)->group_nla, dst_addr, pgm_sockaddr_len(dst_addr)); - break; - -#ifdef CONFIG_PGM_POLLING - case PGM_POLL: - if (PGM_UNLIKELY(!pgm_on_poll (sock, *source, skb))) - goto out_discarded; - break; -#endif - - default: - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded unsupported PGM type packet.")); - goto out_discarded; - } - - return TRUE; -out_discarded: - if (*source) - (*source)->cumulative_stats[PGM_PC_RECEIVER_PACKETS_DISCARDED]++; - else if (sock->can_send_data) - sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED]++; - return FALSE; -} - -/* process a pgm packet - * - * returns TRUE on valid processed packet, returns FALSE on discarded packet. - */ -static -bool -on_pgm ( - pgm_sock_t* const restrict sock, - struct pgm_sk_buff_t* const restrict skb, - struct sockaddr* const restrict src_addr, - struct sockaddr* const restrict dst_addr, - pgm_peer_t** restrict source - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != skb); - pgm_assert (NULL != src_addr); - pgm_assert (NULL != dst_addr); - pgm_assert (NULL != source); - -#ifdef RECV_DEBUG - char saddr[INET6_ADDRSTRLEN], daddr[INET6_ADDRSTRLEN]; - pgm_sockaddr_ntop (src_addr, saddr, sizeof(saddr)); - pgm_sockaddr_ntop (dst_addr, daddr, sizeof(daddr)); - pgm_debug ("on_pgm (sock:%p skb:%p src-addr:%s dst-addr:%s source:%p)", - (const void*)sock, (const void*)skb, saddr, daddr, (const void*)source); -#endif - - if (PGM_IS_DOWNSTREAM (skb->pgm_header->pgm_type)) - return on_downstream (sock, skb, src_addr, dst_addr, source); - if (skb->pgm_header->pgm_dport == sock->tsi.sport) - { - if (PGM_IS_UPSTREAM (skb->pgm_header->pgm_type) || - PGM_IS_PEER (skb->pgm_header->pgm_type)) - { - return on_upstream (sock, skb); - } - } - else if (PGM_IS_PEER (skb->pgm_header->pgm_type)) - return on_peer (sock, skb, source); - - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded unknown PGM packet.")); - if (sock->can_send_data) - sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED]++; - return FALSE; -} - -/* block on receiving socket whilst holding sock::waiting-mutex - * returns EAGAIN for waiting data, returns EINTR for waiting timer event, - * returns ENOENT on closed sock, and returns EFAULT for libc error. - */ - -static -int -wait_for_event ( - pgm_sock_t* const sock - ) -{ - int n_fds = 3; - -/* pre-conditions */ - pgm_assert (NULL != sock); - - pgm_debug ("wait_for_event (sock:%p)", (const void*)sock); - - do { - if (PGM_UNLIKELY(sock->is_destroyed)) - return ENOENT; - - if (sock->can_send_data && !pgm_txw_retransmit_is_empty (sock->window)) -/* tight loop on blocked send */ - pgm_on_deferred_nak (sock); - -#ifdef CONFIG_HAVE_POLL - struct pollfd fds[ n_fds ]; - memset (fds, 0, sizeof(fds)); - const int status = pgm_poll_info (sock, fds, &n_fds, POLLIN); - pgm_assert (-1 != status); -#else - fd_set readfds; - FD_ZERO(&readfds); - const int status = pgm_select_info (sock, &readfds, NULL, &n_fds); - pgm_assert (-1 != status); -#endif /* CONFIG_HAVE_POLL */ - -/* flush any waiting notifications */ - if (sock->is_pending_read) { - pgm_notify_clear (&sock->pending_notify); - sock->is_pending_read = FALSE; - } - - int timeout; - if (sock->can_send_data && !pgm_txw_retransmit_is_empty (sock->window)) - timeout = 0; - else - timeout = pgm_timer_expiration (sock); - -#ifdef CONFIG_HAVE_POLL - const int ready = poll (fds, n_fds, timeout /* μs */ / 1000 /* to ms */); -#else - struct timeval tv_timeout = { - .tv_sec = timeout > 1000000L ? timeout / 1000000UL : 0, - .tv_usec = timeout > 1000000L ? timeout % 1000000UL : timeout - }; - const int ready = select (n_fds, &readfds, NULL, NULL, &tv_timeout); -#endif - if (PGM_UNLIKELY(-1 == ready)) { - pgm_debug ("block returned errno=%i",errno); - return EFAULT; - } else if (ready > 0) { - pgm_debug ("recv again on empty"); - return EAGAIN; - } - } while (pgm_timer_check (sock)); - pgm_debug ("state generated event"); - return EINTR; -} - -/* data incoming on receive sockets, can be from a sender or receiver, or simply bogus. - * for IPv4 we receive the IP header to handle fragmentation, for IPv6 we cannot, but the - * underlying stack handles this for us. - * - * recvmsgv reads a vector of apdus each contained in a IO scatter/gather array. - * - * can be called due to event from incoming socket(s) or timer induced data loss. - * - * On success, returns PGM_IO_STATUS_NORMAL and saves the count of bytes read - * into _bytes_read. With non-blocking sockets a block returns - * PGM_IO_STATUS_WOULD_BLOCK. When rate limited sending repair data, returns - * PGM_IO_STATUS_RATE_LIMITED and caller should wait. During recovery state, - * returns PGM_IO_STATUS_TIMER_PENDING and caller should also wait. On - * unrecoverable dataloss, returns PGM_IO_STATUS_CONN_RESET. If connection is - * closed, returns PGM_IO_STATUS_EOF. On error, returns PGM_IO_STATUS_ERROR. - */ - -int -pgm_recvmsgv ( - pgm_sock_t* const restrict sock, - struct pgm_msgv_t* const restrict msg_start, - const size_t msg_len, - const int flags, /* MSG_DONTWAIT for non-blocking */ - size_t* restrict _bytes_read, /* may be NULL */ - pgm_error_t** restrict error - ) -{ - int status = PGM_IO_STATUS_WOULD_BLOCK; - - pgm_debug ("pgm_recvmsgv (sock:%p msg-start:%p msg-len:%zu flags:%d bytes-read:%p error:%p)", - (void*)sock, (void*)msg_start, msg_len, flags, (void*)_bytes_read, (void*)error); - -/* parameters */ - pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); - if (PGM_LIKELY(msg_len)) pgm_return_val_if_fail (NULL != msg_start, PGM_IO_STATUS_ERROR); - -/* shutdown */ - if (PGM_UNLIKELY(!pgm_rwlock_reader_trylock (&sock->lock))) - pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); - -/* state */ - if (PGM_UNLIKELY(!sock->is_bound || sock->is_destroyed)) - { - pgm_rwlock_reader_unlock (&sock->lock); - pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); - } - -/* pre-conditions */ - pgm_assert (NULL != sock->rx_buffer); - pgm_assert (sock->max_tpdu > 0); - if (sock->can_recv_data) { - pgm_assert (NULL != sock->peers_hashtable); - pgm_assert_cmpuint (sock->nak_bo_ivl, >, 1); - pgm_assert (pgm_notify_is_valid (&sock->pending_notify)); - } - -/* receiver */ - pgm_mutex_lock (&sock->receiver_mutex); - - if (PGM_UNLIKELY(sock->is_reset)) { - pgm_assert (NULL != sock->peers_pending); - pgm_assert (NULL != sock->peers_pending->data); - pgm_peer_t* peer = sock->peers_pending->data; - if (flags & MSG_ERRQUEUE) - pgm_set_reset_error (sock, peer, msg_start); - else if (error) { - char tsi[PGM_TSISTRLEN]; - pgm_tsi_print_r (&peer->tsi, tsi, sizeof(tsi)); - pgm_set_error (error, - PGM_ERROR_DOMAIN_RECV, - PGM_ERROR_CONNRESET, - _("Transport has been reset on unrecoverable loss from %s."), - tsi); - } - if (!sock->is_abort_on_reset) - sock->is_reset = !sock->is_reset; - pgm_mutex_unlock (&sock->receiver_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - return PGM_IO_STATUS_RESET; - } - -/* timer status */ - if (pgm_timer_check (sock) && - !pgm_timer_dispatch (sock)) - { -/* block on send-in-recv */ - status = PGM_IO_STATUS_RATE_LIMITED; - } -/* NAK status */ - else if (sock->can_send_data) - { - if (!pgm_txw_retransmit_is_empty (sock->window)) - { - if (!pgm_on_deferred_nak (sock)) - status = PGM_IO_STATUS_RATE_LIMITED; - } - else - pgm_notify_clear (&sock->rdata_notify); - } - - size_t bytes_read = 0; - unsigned data_read = 0; - struct pgm_msgv_t* pmsg = msg_start; - const struct pgm_msgv_t* msg_end = msg_start + msg_len - 1; - - if (PGM_UNLIKELY(0 == ++(sock->last_commit))) - ++(sock->last_commit); - - /* second, flush any remaining contiguous messages from previous call(s) */ - if (sock->peers_pending) { - if (0 != pgm_flush_peers_pending (sock, &pmsg, msg_end, &bytes_read, &data_read)) - goto out; -/* returns on: reset or full buffer */ - } - -/* read the data: - * - * We cannot actually block here as packets pushed by the timers need to be addressed too. - */ - struct sockaddr_storage src, dst; - ssize_t len; - size_t bytes_received = 0; - -recv_again: - - len = recvskb (sock, - sock->rx_buffer, /* PGM skbuff */ - 0, - (struct sockaddr*)&src, - sizeof(src), - (struct sockaddr*)&dst, - sizeof(dst)); - if (len < 0) - { -#ifndef _WIN32 - const int save_errno = errno; - if (PGM_LIKELY(EAGAIN == save_errno)) { - goto check_for_repeat; - } - status = PGM_IO_STATUS_ERROR; - pgm_set_error (error, - PGM_ERROR_DOMAIN_RECV, - pgm_error_from_errno (save_errno), - _("Transport socket error: %s"), - strerror (save_errno)); -#else - const int save_wsa_errno = WSAGetLastError (); - if (PGM_LIKELY(WSAEWOULDBLOCK == save_wsa_errno)) { - goto check_for_repeat; - } - status = PGM_IO_STATUS_ERROR; - pgm_set_error (error, - PGM_ERROR_DOMAIN_RECV, - pgm_error_from_wsa_errno (save_wsa_errno), - _("Transport socket error: %s"), - pgm_wsastrerror (save_wsa_errno)); -#endif /* !_WIN32 */ - goto out; - } - else if (0 == len) - { -/* cannot return NORMAL/0 as that is valid payload with SKB */ - status = PGM_IO_STATUS_EOF; - goto out; - } - else - { - bytes_received += len; - } - - pgm_error_t* err = NULL; - const bool is_valid = (sock->udp_encap_ucast_port || AF_INET6 == src.ss_family) ? - pgm_parse_udp_encap (sock->rx_buffer, &err) : - pgm_parse_raw (sock->rx_buffer, (struct sockaddr*)&dst, &err); - if (PGM_UNLIKELY(!is_valid)) - { -/* inherently cannot determine PGM_PC_RECEIVER_CKSUM_ERRORS unless only one receiver */ - pgm_trace (PGM_LOG_ROLE_NETWORK, - _("Discarded invalid packet: %s"), - (err && err->message) ? err->message : "(null)"); - pgm_error_free (err); - if (sock->can_send_data) { - if (err && PGM_ERROR_CKSUM == err->code) - sock->cumulative_stats[PGM_PC_SOURCE_CKSUM_ERRORS]++; - sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED]++; - } - goto recv_again; - } - - pgm_peer_t* source = NULL; - if (PGM_UNLIKELY(!on_pgm (sock, sock->rx_buffer, (struct sockaddr*)&src, (struct sockaddr*)&dst, &source))) - goto recv_again; - -/* check whether this source has waiting data */ - if (source && pgm_peer_has_pending (source)) { - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("New pending data.")); - pgm_peer_set_pending (sock, source); - } - -flush_pending: -/* flush any congtiguous packets generated by the receipt of this packet */ - if (sock->peers_pending) - { - if (0 != pgm_flush_peers_pending (sock, &pmsg, msg_end, &bytes_read, &data_read)) - { -/* recv vector is now full */ - goto out; - } - } - -check_for_repeat: -/* repeat if non-blocking and not full */ - if (sock->is_nonblocking || - flags & MSG_DONTWAIT) - { - if (len > 0 && pmsg <= msg_end) { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Recv again on not-full")); - goto recv_again; /* \:D/ */ - } - } - else - { -/* repeat if blocking and empty, i.e. received non data packet. - */ - if (0 == data_read) { - const int wait_status = wait_for_event (sock); - switch (wait_status) { - case EAGAIN: - goto recv_again; - case EINTR: - if (!pgm_timer_dispatch (sock)) - goto check_for_repeat; - goto flush_pending; - case ENOENT: - pgm_mutex_unlock (&sock->receiver_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - return PGM_IO_STATUS_EOF; - case EFAULT: - pgm_set_error (error, - PGM_ERROR_DOMAIN_RECV, - pgm_error_from_errno (errno), - _("Waiting for event: %s"), -#ifndef _WIN32 - strerror (errno) -#else - pgm_wsastrerror (WSAGetLastError()) /* from select() */ -#endif - ); - pgm_mutex_unlock (&sock->receiver_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - return PGM_IO_STATUS_ERROR; - default: - pgm_assert_not_reached(); - } - } - } - -out: - if (0 == data_read) - { -/* clear event notification */ - if (sock->is_pending_read) { - pgm_notify_clear (&sock->pending_notify); - sock->is_pending_read = FALSE; - } -/* report data loss */ - if (PGM_UNLIKELY(sock->is_reset)) { - pgm_assert (NULL != sock->peers_pending); - pgm_assert (NULL != sock->peers_pending->data); - pgm_peer_t* peer = sock->peers_pending->data; - if (flags & MSG_ERRQUEUE) - pgm_set_reset_error (sock, peer, msg_start); - else if (error) { - char tsi[PGM_TSISTRLEN]; - pgm_tsi_print_r (&peer->tsi, tsi, sizeof(tsi)); - pgm_set_error (error, - PGM_ERROR_DOMAIN_RECV, - PGM_ERROR_CONNRESET, - _("Transport has been reset on unrecoverable loss from %s."), - tsi); - } - if (!sock->is_abort_on_reset) - sock->is_reset = !sock->is_reset; - pgm_mutex_unlock (&sock->receiver_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - return PGM_IO_STATUS_RESET; - } - pgm_mutex_unlock (&sock->receiver_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - if (PGM_IO_STATUS_WOULD_BLOCK == status && - ( sock->can_send_data || - ( sock->can_recv_data && NULL != sock->peers_list ))) - { - status = PGM_IO_STATUS_TIMER_PENDING; - } - return status; - } - - if (sock->peers_pending) - { -/* set event notification for additional available data */ - if (sock->is_pending_read && sock->is_edge_triggered_recv) - { -/* empty pending-pipe */ - pgm_notify_clear (&sock->pending_notify); - sock->is_pending_read = FALSE; - } - else if (!sock->is_pending_read && !sock->is_edge_triggered_recv) - { -/* fill pending-pipe */ - pgm_notify_send (&sock->pending_notify); - sock->is_pending_read = TRUE; - } - } - - if (NULL != _bytes_read) - *_bytes_read = bytes_read; - pgm_mutex_unlock (&sock->receiver_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - return PGM_IO_STATUS_NORMAL; -} - -/* read one contiguous apdu and return as a IO scatter/gather array. msgv is owned by - * the caller, tpdu contents are owned by the receive window. - * - * on success, returns PGM_IO_STATUS_NORMAL. - */ - -int -pgm_recvmsg ( - pgm_sock_t* const restrict sock, - struct pgm_msgv_t* const restrict msgv, - const int flags, /* MSG_DONTWAIT for non-blocking */ - size_t* restrict bytes_read, /* may be NULL */ - pgm_error_t** restrict error - ) -{ - pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); - pgm_return_val_if_fail (NULL != msgv, PGM_IO_STATUS_ERROR); - - pgm_debug ("pgm_recvmsg (sock:%p msgv:%p flags:%d bytes_read:%p error:%p)", - (const void*)sock, (const void*)msgv, flags, (const void*)bytes_read, (const void*)error); - - return pgm_recvmsgv (sock, msgv, 1, flags, bytes_read, error); -} - -/* vanilla read function. copies from the receive window to the provided buffer - * location. the caller must provide an adequately sized buffer to store the largest - * expected apdu or else it will be truncated. - * - * on success, returns PGM_IO_STATUS_NORMAL. - */ - -int -pgm_recvfrom ( - pgm_sock_t* const restrict sock, - void* restrict buf, - const size_t buflen, - const int flags, /* MSG_DONTWAIT for non-blocking */ - size_t* restrict _bytes_read, /* may be NULL */ - struct pgm_sockaddr_t* restrict from, /* may be NULL */ - socklen_t* restrict fromlen, - pgm_error_t** restrict error - ) -{ - struct pgm_msgv_t msgv; - size_t bytes_read = 0; - - pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); - if (PGM_LIKELY(buflen)) pgm_return_val_if_fail (NULL != buf, PGM_IO_STATUS_ERROR); - if (fromlen) { - pgm_return_val_if_fail (NULL != from, PGM_IO_STATUS_ERROR); - pgm_return_val_if_fail (sizeof (struct pgm_sockaddr_t) == *fromlen, PGM_IO_STATUS_ERROR); - } - - pgm_debug ("pgm_recvfrom (sock:%p buf:%p buflen:%zu flags:%d bytes-read:%p from:%p from:%p error:%p)", - (const void*)sock, buf, buflen, flags, (const void*)_bytes_read, (const void*)from, (const void*)fromlen, (const void*)error); - - const int status = pgm_recvmsg (sock, &msgv, flags & ~(MSG_ERRQUEUE), &bytes_read, error); - if (PGM_IO_STATUS_NORMAL != status) - return status; - - size_t bytes_copied = 0; - struct pgm_sk_buff_t** skb = msgv.msgv_skb; - struct pgm_sk_buff_t* pskb = *skb; - - if (from) { - from->sa_port = ntohs (sock->dport); - from->sa_addr.sport = ntohs (pskb->tsi.sport); - memcpy (&from->sa_addr.gsi, &pskb->tsi.gsi, sizeof(pgm_gsi_t)); - } - - while (bytes_copied < bytes_read) { - size_t copy_len = pskb->len; - if (bytes_copied + copy_len > buflen) { - pgm_warn (_("APDU truncated, original length %zu bytes."), - bytes_read); - copy_len = buflen - bytes_copied; - bytes_read = buflen; - } - memcpy ((char*)buf + bytes_copied, pskb->data, copy_len); - bytes_copied += copy_len; - pskb = *(++skb); - } - if (_bytes_read) - *_bytes_read = bytes_copied; - return PGM_IO_STATUS_NORMAL; -} - -/* Basic recv operation, copying data from window to application. - * - * on success, returns PGM_IO_STATUS_NORMAL. - */ - -int -pgm_recv ( - pgm_sock_t* const restrict sock, - void* restrict buf, - const size_t buflen, - const int flags, /* MSG_DONTWAIT for non-blocking */ - size_t* const restrict bytes_read, /* may be NULL */ - pgm_error_t** restrict error - ) -{ - pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); - if (PGM_LIKELY(buflen)) pgm_return_val_if_fail (NULL != buf, PGM_IO_STATUS_ERROR); - - pgm_debug ("pgm_recv (sock:%p buf:%p buflen:%zu flags:%d bytes-read:%p error:%p)", - (const void*)sock, buf, buflen, flags, (const void*)bytes_read, (const void*)error); - - return pgm_recvfrom (sock, buf, buflen, flags, bytes_read, NULL, NULL, error); -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/recv_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/recv_unittest.c deleted file mode 100644 index 2719897..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/recv_unittest.c +++ /dev/null @@ -1,1600 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for transport recv api - * - * Copyright (c) 2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include /* _GNU_SOURCE for in6_pktinfo */ -#include -#include -#include - - -/* mock state */ - -#define TEST_NETWORK "" -#define TEST_DPORT 7500 -#define TEST_SPORT 1000 -#define TEST_SRC_ADDR "127.0.0.1" -#define TEST_END_ADDR "127.0.0.2" -#define TEST_GROUP_ADDR "239.192.0.1" -#define TEST_PEER_ADDR "127.0.0.6" -#define TEST_DLR_ADDR "127.0.0.9" -#define TEST_MAX_TPDU 1500 -#define TEST_TXW_SQNS 32 -#define TEST_RXW_SQNS 32 -#define TEST_HOPS 16 -#define TEST_SPM_AMBIENT ( pgm_secs(30) ) -#define TEST_SPM_HEARTBEAT_INIT { pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(1300), pgm_secs(7), pgm_secs(16), pgm_secs(25), pgm_secs(30) } -#define TEST_PEER_EXPIRY ( pgm_secs(300) ) -#define TEST_SPMR_EXPIRY ( pgm_msecs(250) ) -#define TEST_NAK_BO_IVL ( pgm_msecs(50) ) -#define TEST_NAK_RPT_IVL ( pgm_secs(2) ) -#define TEST_NAK_RDATA_IVL ( pgm_secs(2) ) -#define TEST_NAK_DATA_RETRIES 5 -#define TEST_NAK_NCF_RETRIES 2 - -struct mock_recvmsg_t { - struct msghdr* mr_msg; - ssize_t mr_retval; - int mr_errno; -}; - -struct pgm_peer_t; - -GList* mock_recvmsg_list = NULL; -static int mock_pgm_type = -1; -static gboolean mock_reset_on_spmr = FALSE; -static gboolean mock_data_on_spmr = FALSE; -static struct pgm_peer_t* mock_peer = NULL; -GList* mock_data_list = NULL; -unsigned mock_pgm_loss_rate = 0; - - -static ssize_t mock_recvmsg (int, struct msghdr*, int); - -#define pgm_parse_raw mock_pgm_parse_raw -#define pgm_parse_udp_encap mock_pgm_parse_udp_encap -#define pgm_verify_spm mock_pgm_verify_spm -#define pgm_verify_nak mock_pgm_verify_nak -#define pgm_verify_ncf mock_pgm_verify_ncf -#define pgm_poll_info mock_pgm_poll_info -#define pgm_set_reset_error mock_pgm_set_reset_error -#define pgm_flush_peers_pending mock_pgm_flush_peers_pending -#define pgm_peer_has_pending mock_pgm_peer_has_pending -#define pgm_peer_set_pending mock_pgm_peer_set_pending -#define pgm_txw_retransmit_is_empty mock_pgm_txw_retransmit_is_empty -#define pgm_rxw_create mock_pgm_rxw_create -#define pgm_rxw_readv mock_pgm_rxw_readv -#define pgm_new_peer mock_pgm_new_peer -#define pgm_on_data mock_pgm_on_data -#define pgm_on_spm mock_pgm_on_spm -#define pgm_on_ack mock_pgm_on_ack -#define pgm_on_nak mock_pgm_on_nak -#define pgm_on_deferred_nak mock_pgm_on_deferred_nak -#define pgm_on_peer_nak mock_pgm_on_peer_nak -#define pgm_on_nnak mock_pgm_on_nnak -#define pgm_on_ncf mock_pgm_on_ncf -#define pgm_on_spmr mock_pgm_on_spmr -#define pgm_sendto mock_pgm_sendto -#define pgm_timer_prepare mock_pgm_timer_prepare -#define pgm_timer_check mock_pgm_timer_check -#define pgm_timer_expiration mock_pgm_timer_expiration -#define pgm_timer_dispatch mock_pgm_timer_dispatch -#define pgm_time_now mock_pgm_time_now -#define pgm_time_update_now mock_pgm_time_update_now -#define recvmsg mock_recvmsg -#define pgm_loss_rate mock_pgm_loss_rate - -#define RECV_DEBUG -#include "recv.c" - - -pgm_rxw_t* mock_pgm_rxw_create (const pgm_tsi_t*, const uint16_t, const unsigned, const unsigned, const ssize_t, const uint32_t); -static pgm_time_t _mock_pgm_time_update_now (void); -pgm_time_update_func mock_pgm_time_update_now = _mock_pgm_time_update_now; - - -static -void -mock_setup (void) -{ - if (!g_thread_supported ()) g_thread_init (NULL); -} - -static -struct pgm_sock_t* -generate_sock (void) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, g_htons(TEST_SPORT) }; - struct pgm_sock_t* sock = g_new0 (struct pgm_sock_t, 1); - sock->window = g_new0 (pgm_txw_t, 1); - memcpy (&sock->tsi, &tsi, sizeof(pgm_tsi_t)); - sock->is_bound = TRUE; - sock->rx_buffer = pgm_alloc_skb (TEST_MAX_TPDU); - sock->max_tpdu = TEST_MAX_TPDU; - sock->rxw_sqns = TEST_RXW_SQNS; - sock->dport = g_htons(TEST_DPORT); - sock->can_send_data = TRUE; - sock->can_send_nak = TRUE; - sock->can_recv_data = TRUE; - sock->peers_hashtable = pgm_hashtable_new (pgm_tsi_hash, pgm_tsi_equal); - pgm_rand_create (&sock->rand_); - sock->nak_bo_ivl = 100*1000; - pgm_notify_init (&sock->pending_notify); - pgm_notify_init (&sock->rdata_notify); - return sock; -} - -static -struct pgm_sk_buff_t* -generate_packet (void) -{ - struct pgm_sk_buff_t* skb; - - skb = pgm_alloc_skb (TEST_MAX_TPDU); - skb->data = skb->head; - skb->len = sizeof(struct pgm_ip) + sizeof(struct pgm_header); - skb->tail = (guint8*)skb->data + skb->len; - -/* add IP header */ - struct pgm_ip* iphdr = skb->data; - iphdr->ip_hl = sizeof(struct pgm_ip) / 4; - iphdr->ip_v = 4; - iphdr->ip_tos = 0; - iphdr->ip_id = 0; - iphdr->ip_off = 0; - iphdr->ip_ttl = 16; - iphdr->ip_p = IPPROTO_PGM; - iphdr->ip_sum = 0; - iphdr->ip_src.s_addr = inet_addr (TEST_SRC_ADDR); - iphdr->ip_dst.s_addr = inet_addr (TEST_GROUP_ADDR); - -/* add PGM header */ - struct pgm_header* pgmhdr = (gpointer)(iphdr + 1); - pgmhdr->pgm_sport = g_htons ((guint16)TEST_SPORT); - pgmhdr->pgm_dport = g_htons ((guint16)TEST_DPORT); - pgmhdr->pgm_options = 0; - pgmhdr->pgm_gsi[0] = 1; - pgmhdr->pgm_gsi[1] = 2; - pgmhdr->pgm_gsi[2] = 3; - pgmhdr->pgm_gsi[3] = 4; - pgmhdr->pgm_gsi[4] = 5; - pgmhdr->pgm_gsi[5] = 6; - pgmhdr->pgm_tsdu_length = 0; - pgmhdr->pgm_checksum = 0; - - skb->pgm_header = pgmhdr; - return skb; -} - -static -void -generate_odata ( - const char* source, - const guint source_len, - const guint32 data_sqn, - const guint32 data_trail, - gpointer* packet, - gsize* len - ) -{ - struct pgm_sk_buff_t* skb = generate_packet (); - pgm_skb_put (skb, sizeof(struct pgm_data) + source_len); - -/* add ODATA header */ - struct pgm_data* datahdr = (gpointer)(skb->pgm_header + 1); - datahdr->data_sqn = g_htonl (data_sqn); - datahdr->data_trail = g_htonl (data_trail); - -/* add payload */ - gpointer data = (gpointer)(datahdr + 1); - memcpy (data, source, source_len); - -/* finalize PGM header */ - struct pgm_header* pgmhdr = skb->pgm_header; - pgmhdr->pgm_type = PGM_ODATA; - pgmhdr->pgm_tsdu_length = g_htons (source_len); - -/* finalize IP header */ - struct pgm_ip* iphdr = skb->data; - iphdr->ip_len = g_htons (skb->len); - - *packet = skb->head; - *len = skb->len; -} - -static -void -generate_spm ( - const guint32 spm_sqn, - const guint32 spm_trail, - const guint32 spm_lead, - gpointer* packet, - gsize* len - ) -{ - struct pgm_sk_buff_t* skb = generate_packet (); - pgm_skb_put (skb, sizeof(struct pgm_spm)); - -/* add SPM header */ - struct pgm_spm* spm = (gpointer)(skb->pgm_header + 1); - spm->spm_sqn = g_htonl (spm_sqn); - spm->spm_trail = g_htonl (spm_trail); - spm->spm_lead = g_htonl (spm_lead); - -/* finalize PGM header */ - struct pgm_header* pgmhdr = skb->pgm_header; - pgmhdr->pgm_type = PGM_SPM; - -/* finalize IP header */ - struct pgm_ip* iphdr = skb->data; - iphdr->ip_len = g_htons (skb->len); - - *packet = skb->head; - *len = skb->len; -} - -static -void -generate_nak ( - const guint32 nak_sqn, - gpointer* packet, - gsize* len - ) -{ - struct pgm_sk_buff_t* skb = generate_packet (); - pgm_skb_put (skb, sizeof(struct pgm_spm)); - -/* update IP header */ - struct pgm_ip* iphdr = skb->data; - iphdr->ip_src.s_addr = inet_addr (TEST_END_ADDR); - iphdr->ip_dst.s_addr = inet_addr (TEST_SRC_ADDR); - -/* update PGM header */ - struct pgm_header* pgmhdr = skb->pgm_header; - pgmhdr->pgm_sport = g_htons ((guint16)TEST_DPORT); - pgmhdr->pgm_dport = g_htons ((guint16)TEST_SPORT); - -/* add NAK header */ - struct pgm_nak* nak = (gpointer)(skb->pgm_header + 1); - nak->nak_sqn = g_htonl (nak_sqn); - -/* finalize PGM header */ - pgmhdr->pgm_type = PGM_NAK; - -/* finalize IP header */ - iphdr->ip_len = g_htons (skb->len); - - *packet = skb->head; - *len = skb->len; -} - -static -void -generate_peer_nak ( - const guint32 nak_sqn, - gpointer* packet, - gsize* len - ) -{ - struct pgm_sk_buff_t* skb = generate_packet (); - pgm_skb_put (skb, sizeof(struct pgm_spm)); - -/* update IP header */ - struct pgm_ip* iphdr = skb->data; - iphdr->ip_src.s_addr = inet_addr (TEST_PEER_ADDR); - iphdr->ip_dst.s_addr = inet_addr (TEST_GROUP_ADDR); - -/* update PGM header */ - struct pgm_header* pgmhdr = skb->pgm_header; - pgmhdr->pgm_sport = g_htons ((guint16)TEST_DPORT); - pgmhdr->pgm_dport = g_htons ((guint16)TEST_SPORT); - -/* add NAK header */ - struct pgm_nak* nak = (gpointer)(skb->pgm_header + 1); - nak->nak_sqn = g_htonl (nak_sqn); - -/* finalize PGM header */ - pgmhdr->pgm_type = PGM_NAK; - -/* finalize IP header */ - iphdr->ip_len = g_htons (skb->len); - - *packet = skb->head; - *len = skb->len; -} - -static -void -generate_nnak ( - const guint32 nak_sqn, - gpointer* packet, - gsize* len - ) -{ - struct pgm_sk_buff_t* skb = generate_packet (); - pgm_skb_put (skb, sizeof(struct pgm_nak)); - -/* update IP header */ - struct pgm_ip* iphdr = skb->data; - iphdr->ip_src.s_addr = inet_addr (TEST_DLR_ADDR); - iphdr->ip_dst.s_addr = inet_addr (TEST_SRC_ADDR); - -/* update PGM header */ - struct pgm_header* pgmhdr = skb->pgm_header; - pgmhdr->pgm_sport = g_htons ((guint16)TEST_DPORT); - pgmhdr->pgm_dport = g_htons ((guint16)TEST_SPORT); - -/* add NNAK header */ - struct pgm_nak* nak = (gpointer)(skb->pgm_header + 1); - nak->nak_sqn = g_htonl (nak_sqn); - -/* finalize PGM header */ - pgmhdr->pgm_type = PGM_NNAK; - -/* finalize IP header */ - iphdr->ip_len = g_htons (skb->len); - - *packet = skb->head; - *len = skb->len; -} - -static -void -generate_ncf ( - const guint32 nak_sqn, - gpointer* packet, - gsize* len - ) -{ - struct pgm_sk_buff_t* skb = generate_packet (); - pgm_skb_put (skb, sizeof(struct pgm_nak)); - -/* add NAK header */ - struct pgm_nak* nak = (gpointer)(skb->pgm_header + 1); - nak->nak_sqn = g_htonl (nak_sqn); - -/* finalize PGM header */ - struct pgm_header* pgmhdr = skb->pgm_header; - pgmhdr->pgm_type = PGM_NCF; - -/* finalize IP header */ - struct pgm_ip* iphdr = skb->data; - iphdr->ip_len = g_htons (skb->len); - - *packet = skb->head; - *len = skb->len; -} - -static -void -generate_spmr ( - gpointer* packet, - gsize* len - ) -{ - struct pgm_sk_buff_t* skb = generate_packet (); - -/* update IP header */ - struct pgm_ip* iphdr = skb->data; - iphdr->ip_src.s_addr = inet_addr (TEST_END_ADDR); - iphdr->ip_dst.s_addr = inet_addr (TEST_SRC_ADDR); - -/* update PGM header */ - struct pgm_header* pgmhdr = skb->pgm_header; - pgmhdr->pgm_sport = g_htons ((guint16)TEST_DPORT); - pgmhdr->pgm_dport = g_htons ((guint16)TEST_SPORT); - -/* finalize PGM header */ - pgmhdr->pgm_type = PGM_SPMR; - -/* finalize IP header */ - iphdr->ip_len = g_htons (skb->len); - - *packet = skb->head; - *len = skb->len; -} - -static -void -generate_peer_spmr ( - gpointer* packet, - gsize* len - ) -{ - struct pgm_sk_buff_t* skb = generate_packet (); - -/* update IP header */ - struct pgm_ip* iphdr = skb->data; - iphdr->ip_src.s_addr = inet_addr (TEST_PEER_ADDR); - iphdr->ip_dst.s_addr = inet_addr (TEST_GROUP_ADDR); - -/* update PGM header */ - struct pgm_header* pgmhdr = skb->pgm_header; - pgmhdr->pgm_sport = g_htons ((guint16)TEST_DPORT); - pgmhdr->pgm_dport = g_htons ((guint16)TEST_SPORT); - -/* finalize PGM header */ - pgmhdr->pgm_type = PGM_SPMR; - -/* finalize IP header */ - iphdr->ip_len = g_htons (skb->len); - - *packet = skb->head; - *len = skb->len; -} - -static -void -generate_msghdr ( - const gpointer packet, - const gsize packet_len - ) -{ - struct pgm_ip* iphdr = packet; - struct sockaddr_in addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = iphdr->ip_src.s_addr - }; - struct iovec iov = { - .iov_base = packet, - .iov_len = packet_len - }; - struct cmsghdr* packet_cmsg = g_malloc0 (sizeof(struct cmsghdr) + sizeof(struct in_pktinfo)); - packet_cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(struct in_pktinfo); - packet_cmsg->cmsg_level = IPPROTO_IP; - packet_cmsg->cmsg_type = IP_PKTINFO; - struct in_pktinfo packet_info = { - .ipi_ifindex = 2, - .ipi_spec_dst = iphdr->ip_src.s_addr, /* local address */ - .ipi_addr = iphdr->ip_dst.s_addr /* destination address */ - }; - memcpy ((char*)(packet_cmsg + 1), &packet_info, sizeof(struct in_pktinfo)); - struct msghdr packet_msg = { - .msg_name = g_memdup (&addr, sizeof(addr)), /* source address */ - .msg_namelen = sizeof(addr), - .msg_iov = g_memdup (&iov, sizeof(iov)), - .msg_iovlen = 1, - .msg_control = &packet_cmsg, - .msg_controllen = sizeof(struct cmsghdr) + sizeof(struct in_pktinfo), - .msg_flags = 0 - }; - - struct mock_recvmsg_t* mr = g_malloc (sizeof(struct mock_recvmsg_t)); - mr->mr_msg = g_memdup (&packet_msg, sizeof(packet_msg)); - mr->mr_errno = 0; - mr->mr_retval = packet_len; - mock_recvmsg_list = g_list_append (mock_recvmsg_list, mr); -} - -static -void -push_block_event (void) -{ -/* block */ - struct mock_recvmsg_t* mr = g_malloc (sizeof(struct mock_recvmsg_t)); - mr->mr_msg = NULL; - mr->mr_errno = EAGAIN; - mr->mr_retval = -1; - mock_recvmsg_list = g_list_append (mock_recvmsg_list, mr); -} - -/** packet module */ -bool -mock_pgm_parse_raw ( - struct pgm_sk_buff_t* const skb, - struct sockaddr* const dst, - pgm_error_t** error - ) -{ - const struct pgm_ip* ip = (struct pgm_ip*)skb->data; - struct sockaddr_in* sin = (struct sockaddr_in*)dst; - sin->sin_family = AF_INET; - sin->sin_addr.s_addr = ip->ip_dst.s_addr; - const gsize ip_header_length = ip->ip_hl * 4; - skb->pgm_header = (gpointer)( (guint8*)skb->data + ip_header_length ); - skb->data = skb->pgm_header; - skb->len -= ip_header_length; - memcpy (&skb->tsi.gsi, skb->pgm_header->pgm_gsi, sizeof(pgm_gsi_t)); - skb->tsi.sport = skb->pgm_header->pgm_sport; - return TRUE; -} - -bool -mock_pgm_parse_udp_encap ( - struct pgm_sk_buff_t* const skb, - pgm_error_t** error - ) -{ - skb->pgm_header = skb->data; - memcpy (&skb->tsi.gsi, skb->pgm_header->pgm_gsi, sizeof(pgm_gsi_t)); - skb->tsi.sport = skb->pgm_header->pgm_sport; - return TRUE; -} - -bool -mock_pgm_verify_spm ( - const struct pgm_sk_buff_t* const skb - ) -{ - return TRUE; -} - -bool -mock_pgm_verify_nak ( - const struct pgm_sk_buff_t* const skb - ) -{ - return TRUE; -} - -bool -mock_pgm_verify_ncf ( - const struct pgm_sk_buff_t* const skb - ) -{ - return TRUE; -} - -/** socket module */ -int -mock_pgm_poll_info ( - pgm_sock_t* const sock, - struct pollfd* fds, - int* n_fds, - int events - ) -{ -} - -pgm_peer_t* -mock__pgm_peer_ref ( - pgm_peer_t* peer - ) -{ - pgm_atomic_inc32 (&peer->ref_count); - return peer; -} - -PGM_GNUC_INTERNAL -pgm_peer_t* -mock_pgm_new_peer ( - pgm_sock_t* const sock, - const pgm_tsi_t* const tsi, - const struct sockaddr* const src_addr, - const socklen_t src_addr_len, - const struct sockaddr* const dst_addr, - const socklen_t dst_addr_len, - const pgm_time_t now - ) -{ - pgm_peer_t* peer = g_malloc0 (sizeof(pgm_peer_t)); - peer->expiry = now + sock->peer_expiry; - peer->sock = sock; - memcpy (&peer->tsi, tsi, sizeof(pgm_tsi_t)); - memcpy (&peer->group_nla, dst_addr, dst_addr_len); - memcpy (&peer->local_nla, src_addr, src_addr_len); - ((struct sockaddr_in*)&peer->local_nla)->sin_port = g_htons (sock->udp_encap_ucast_port); - ((struct sockaddr_in*)&peer->nla)->sin_port = g_htons (sock->udp_encap_ucast_port); - peer->window = mock_pgm_rxw_create (&peer->tsi, - sock->max_tpdu, - sock->rxw_sqns, - sock->rxw_secs, - sock->rxw_max_rte, - sock->ack_c_p); - peer->spmr_expiry = now + sock->spmr_expiry; - gpointer entry = mock__pgm_peer_ref(peer); - pgm_hashtable_insert (sock->peers_hashtable, &peer->tsi, entry); - peer->peers_link.next = sock->peers_list; - peer->peers_link.data = peer; - if (sock->peers_list) - sock->peers_list->prev = &peer->peers_link; - sock->peers_list = &peer->peers_link; - return peer; -} - -PGM_GNUC_INTERNAL -void -mock_pgm_set_reset_error ( - pgm_sock_t* const sock, - pgm_peer_t* const source, - struct pgm_msgv_t* const msgv - ) -{ -} - -PGM_GNUC_INTERNAL -int -mock_pgm_flush_peers_pending ( - pgm_sock_t* const sock, - struct pgm_msgv_t** pmsg, - const struct pgm_msgv_t* const msg_end, - size_t* const bytes_read, - unsigned* const data_read - ) -{ - if (mock_data_list) { - size_t len = 0; - unsigned count = 0; - while (mock_data_list && *pmsg <= msg_end) { - struct pgm_msgv_t* mock_msgv = mock_data_list->data; - (*pmsg)->msgv_len = mock_msgv->msgv_len; - for (unsigned i = 0; i < mock_msgv->msgv_len; i++) { - (*pmsg)->msgv_skb[i] = mock_msgv->msgv_skb[i]; - len += mock_msgv->msgv_skb[i]->len; - } - count++; - (*pmsg)++; - mock_data_list = g_list_delete_link (mock_data_list, mock_data_list); - } - *bytes_read = len; - *data_read = count; - if (*pmsg > msg_end) - return -ENOBUFS; - } - return 0; -} - -PGM_GNUC_INTERNAL -bool -mock_pgm_peer_has_pending ( - pgm_peer_t* const peer - ) -{ - return FALSE; -} - -PGM_GNUC_INTERNAL -void -mock_pgm_peer_set_pending ( - pgm_sock_t* const sock, - pgm_peer_t* const peer - ) -{ - g_assert (NULL != sock); - g_assert (NULL != peer); - if (peer->pending_link.data) return; - peer->pending_link.data = peer; - peer->pending_link.next = sock->peers_pending; - sock->peers_pending = &peer->pending_link; -} - -PGM_GNUC_INTERNAL -bool -mock_pgm_on_data ( - pgm_sock_t* const sock, - pgm_peer_t* const sender, - struct pgm_sk_buff_t* const skb - ) -{ - g_debug ("mock_pgm_on_data (sock:%p sender:%p skb:%p)", - (gpointer)sock, (gpointer)sender, (gpointer)skb); - mock_pgm_type = PGM_ODATA; - ((pgm_rxw_t*)sender->window)->has_event = 1; - return TRUE; -} - -PGM_GNUC_INTERNAL -bool -mock_pgm_on_ack ( - pgm_sock_t* const sock, - struct pgm_sk_buff_t* const skb - ) -{ - g_debug ("mock_pgm_on_ack (sock:%p skb:%p)", - (gpointer)sock, (gpointer)skb); - mock_pgm_type = PGM_ACK; - return TRUE; -} - -PGM_GNUC_INTERNAL -bool -mock_pgm_on_deferred_nak ( - pgm_sock_t* const sock - ) -{ - return TRUE; -} - -PGM_GNUC_INTERNAL -bool -mock_pgm_on_nak ( - pgm_sock_t* const sock, - struct pgm_sk_buff_t* const skb - ) -{ - g_debug ("mock_pgm_on_nak (sock:%p skb:%p)", - (gpointer)sock, (gpointer)skb); - mock_pgm_type = PGM_NAK; - return TRUE; -} - -PGM_GNUC_INTERNAL -bool -mock_pgm_on_peer_nak ( - pgm_sock_t* const sock, - pgm_peer_t* const sender, - struct pgm_sk_buff_t* const skb - ) -{ - g_debug ("mock_pgm_on_peer_nak (sock:%p sender:%p skb:%p)", - (gpointer)sock, (gpointer)sender, (gpointer)skb); - mock_pgm_type = PGM_NAK; - return TRUE; -} - -PGM_GNUC_INTERNAL -bool -mock_pgm_on_ncf ( - pgm_sock_t* const sock, - pgm_peer_t* const sender, - struct pgm_sk_buff_t* const skb - ) -{ - g_debug ("mock_pgm_on_ncf (sock:%p sender:%p skb:%p)", - (gpointer)sock, (gpointer)sender, (gpointer)skb); - mock_pgm_type = PGM_NCF; - return TRUE; -} - -PGM_GNUC_INTERNAL -bool -mock_pgm_on_nnak ( - pgm_sock_t* const sock, - struct pgm_sk_buff_t* const skb - ) -{ - g_debug ("mock_pgm_on_nnak (sock:%p skb:%p)", - (gpointer)sock, (gpointer)skb); - mock_pgm_type = PGM_NNAK; - return TRUE; -} - -PGM_GNUC_INTERNAL -bool -mock_pgm_on_spm ( - pgm_sock_t* const sock, - pgm_peer_t* const sender, - struct pgm_sk_buff_t* const skb - ) -{ - g_debug ("mock_pgm_on_spm (sock:%p sender:%p skb:%p)", - (gpointer)sock, (gpointer)sender, (gpointer)skb); - mock_pgm_type = PGM_SPM; - return TRUE; -} - -PGM_GNUC_INTERNAL -bool -mock_pgm_on_spmr ( - pgm_sock_t* const sock, - pgm_peer_t* const peer, - struct pgm_sk_buff_t* const skb - ) -{ - g_debug ("mock_pgm_on_spmr (sock:%p peer:%p skb:%p)", - (gpointer)sock, (gpointer)peer, (gpointer)skb); - mock_pgm_type = PGM_SPMR; - if (mock_reset_on_spmr) { - sock->is_reset = TRUE; - mock_pgm_peer_set_pending (sock, mock_peer); - } - if (mock_data_on_spmr) { - mock_pgm_peer_set_pending (sock, mock_peer); - } - return TRUE; -} - -/** transmit window */ -PGM_GNUC_INTERNAL -bool -mock_pgm_txw_retransmit_is_empty ( - const pgm_txw_t*const window - ) -{ - return TRUE; -} - -/** receive window */ -pgm_rxw_t* -mock_pgm_rxw_create ( - const pgm_tsi_t* tsi, - const uint16_t tpdu_size, - const unsigned sqns, - const unsigned secs, - const ssize_t max_rte, - const uint32_t ack_c_p - ) -{ - return g_new0 (pgm_rxw_t, 1); -} - -ssize_t -mock_pgm_rxw_readv ( - pgm_rxw_t* const window, - struct pgm_msgv_t** pmsg, - const unsigned pmsglen - ) -{ - return -1; -} - -/** net module */ -PGM_GNUC_INTERNAL -ssize_t -mock_pgm_sendto ( - pgm_sock_t* sock, - bool use_rate_limit, - bool use_router_alert, - const void* buf, - size_t len, - const struct sockaddr* to, - socklen_t tolen - ) -{ - return len; -} - -/** timer module */ -PGM_GNUC_INTERNAL -bool -mock_pgm_timer_prepare ( - pgm_sock_t* const sock - ) -{ - return FALSE; -} - -PGM_GNUC_INTERNAL -bool -mock_pgm_timer_check ( - pgm_sock_t* const sock - ) -{ - return FALSE; -} - -PGM_GNUC_INTERNAL -pgm_time_t -mock_pgm_timer_expiration ( - pgm_sock_t* const sock - ) -{ - return 100L; -} - -PGM_GNUC_INTERNAL -bool -mock_pgm_timer_dispatch ( - pgm_sock_t* const sock - ) -{ - return TRUE; -} - -/** time module */ -static pgm_time_t mock_pgm_time_now = 0x1; - -static -pgm_time_t -_mock_pgm_time_update_now (void) -{ - return mock_pgm_time_now; -} - -/** libc */ -static -ssize_t -mock_recvmsg ( - int s, - struct msghdr* msg, - int flags - ) -{ - g_assert (NULL != msg); - g_assert (NULL != mock_recvmsg_list); - - g_debug ("mock_recvmsg (s:%d msg:%p flags:%d)", - s, (gpointer)msg, flags); - - struct mock_recvmsg_t* mr = mock_recvmsg_list->data; - struct msghdr* mock_msg = mr->mr_msg; - ssize_t mock_retval = mr->mr_retval; - int mock_errno = mr->mr_errno; - mock_recvmsg_list = g_list_delete_link (mock_recvmsg_list, mock_recvmsg_list); - if (mock_msg) { - g_assert_cmpuint (mock_msg->msg_namelen, <=, msg->msg_namelen); - g_assert_cmpuint (mock_msg->msg_iovlen, <=, msg->msg_iovlen); - g_assert_cmpuint (mock_msg->msg_controllen, <=, msg->msg_controllen); - if (mock_msg->msg_namelen) - memcpy (msg->msg_name, mock_msg->msg_name, mock_msg->msg_namelen); - if (mock_msg->msg_iovlen) { - for (unsigned i = 0; i < mock_msg->msg_iovlen; i++) { - g_assert (mock_msg->msg_iov[i].iov_len <= msg->msg_iov[i].iov_len); - memcpy (msg->msg_iov[i].iov_base, mock_msg->msg_iov[i].iov_base, mock_msg->msg_iov[i].iov_len); - } - } - if (mock_msg->msg_controllen) - memcpy (msg->msg_control, mock_msg->msg_control, mock_msg->msg_controllen); - msg->msg_flags = mock_msg->msg_flags; - } - errno = mock_errno; - return mock_retval; -} - - -/* mock functions for external references */ - -size_t -pgm_pkt_offset ( - const bool can_fragment, - const sa_family_t pgmcc_family /* 0 = disable */ - ) -{ - return 0; -} - - -/* target: - * int - * pgm_recv ( - * pgm_sock_t* sock, - * void* data, - * size_t len, - * int flags, - * size_t* bytes_read, - * pgm_error_t** error - * ) - * - * Most tests default to PGM_IO_STATUS_TIMER_PENDING, PGM_IO_STATUS_WOULD_BLOCK is not expected due - * to peer state engine and SPM broadcasts. - */ - -START_TEST (test_block_pass_001) -{ - pgm_sock_t* sock = generate_sock(); - fail_if (NULL == sock, "generate_sock failed"); - guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; - push_block_event (); - gsize bytes_read; - pgm_error_t* err = NULL; - fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); -} -END_TEST - -/* recv -> on_data */ -START_TEST (test_data_pass_001) -{ - const char source[] = "i am not a string"; - pgm_sock_t* sock = generate_sock(); - fail_if (NULL == sock, "generate_sock failed"); - guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; - gpointer packet; gsize packet_len; - generate_odata (source, sizeof(source), 0 /* sqn */, -1 /* trail */, &packet, &packet_len); - generate_msghdr (packet, packet_len); - push_block_event (); - gsize bytes_read; - pgm_error_t* err = NULL; - fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); - fail_unless (PGM_ODATA == mock_pgm_type, "unexpected PGM packet"); -} -END_TEST - -/* recv -> on_spm */ -START_TEST (test_spm_pass_001) -{ - pgm_sock_t* sock = generate_sock(); - fail_if (NULL == sock, "generate_sock failed"); - guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; - gpointer packet; gsize packet_len; - generate_spm (200 /* spm-sqn */, -1 /* trail */, 0 /* lead */, &packet, &packet_len); - generate_msghdr (packet, packet_len); - push_block_event (); - gsize bytes_read; - pgm_error_t* err = NULL; - fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); - fail_unless (PGM_SPM == mock_pgm_type, "unexpected PGM packet"); -} -END_TEST - -/* recv -> on_nak */ -START_TEST (test_nak_pass_001) -{ - pgm_sock_t* sock = generate_sock(); - fail_if (NULL == sock, "generate_sock failed"); - guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; - gpointer packet; gsize packet_len; - generate_nak (0 /* sqn */, &packet, &packet_len); - generate_msghdr (packet, packet_len); - push_block_event (); - gsize bytes_read; - pgm_error_t* err = NULL; - fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); - fail_unless (PGM_NAK == mock_pgm_type, "unexpected PGM packet"); -} -END_TEST - -/* recv -> on_peer_nak */ -START_TEST (test_peer_nak_pass_001) -{ - pgm_sock_t* sock = generate_sock(); - fail_if (NULL == sock, "generate_sock failed"); - guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; - gpointer packet; gsize packet_len; - generate_peer_nak (0 /* sqn */, &packet, &packet_len); - generate_msghdr (packet, packet_len); - push_block_event (); - gsize bytes_read; - pgm_error_t* err = NULL; - fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); - fail_unless (PGM_NAK == mock_pgm_type, "unexpected PGM packet"); -} -END_TEST - -/* recv -> on_nnak */ -START_TEST (test_nnak_pass_001) -{ - pgm_sock_t* sock = generate_sock(); - fail_if (NULL == sock, "generate_sock failed"); - guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; - gpointer packet; gsize packet_len; - generate_nnak (0 /* sqn */, &packet, &packet_len); - generate_msghdr (packet, packet_len); - push_block_event (); - gsize bytes_read; - pgm_error_t* err = NULL; - fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); - fail_unless (PGM_NNAK == mock_pgm_type, "unexpected PGM packet"); -} -END_TEST - -/* recv -> on_ncf */ -START_TEST (test_ncf_pass_001) -{ - pgm_sock_t* sock = generate_sock(); - fail_if (NULL == sock, "generate_sock failed"); - guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; - gpointer packet; gsize packet_len; - generate_ncf (0 /* sqn */, &packet, &packet_len); - generate_msghdr (packet, packet_len); - push_block_event (); - gsize bytes_read; - pgm_error_t* err = NULL; - fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); - fail_unless (PGM_NCF == mock_pgm_type, "unexpected PGM packet"); -} -END_TEST - -/* recv -> on_spmr */ -START_TEST (test_spmr_pass_001) -{ - pgm_sock_t* sock = generate_sock(); - fail_if (NULL == sock, "generate_sock failed"); - guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; - gpointer packet; gsize packet_len; - generate_spmr (&packet, &packet_len); - generate_msghdr (packet, packet_len); - push_block_event (); - gsize bytes_read; - pgm_error_t* err = NULL; - fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); - fail_unless (PGM_SPMR == mock_pgm_type, "unexpected PGM packet"); -} -END_TEST - -/* recv -> on (peer) spmr */ -START_TEST (test_peer_spmr_pass_001) -{ - pgm_sock_t* sock = generate_sock(); - fail_if (NULL == sock, "generate_sock failed"); - guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; - gpointer packet; gsize packet_len; - generate_peer_spmr (&packet, &packet_len); - generate_msghdr (packet, packet_len); - push_block_event (); - gsize bytes_read; - pgm_error_t* err = NULL; - fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); - fail_unless (PGM_SPMR == mock_pgm_type, "unexpected PGM packet"); -} -END_TEST - -/* recv -> lost data */ -START_TEST (test_lost_pass_001) -{ - pgm_sock_t* sock = generate_sock(); - fail_if (NULL == sock, "generate_sock failed"); - sock->is_reset = TRUE; - const pgm_tsi_t peer_tsi = { { 9, 8, 7, 6, 5, 4 }, g_htons(9000) }; - struct sockaddr_in grp_addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr(TEST_GROUP_ADDR) - }, peer_addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr(TEST_END_ADDR) - }; - pgm_peer_t* peer = mock_pgm_new_peer (sock, &peer_tsi, (struct sockaddr*)&grp_addr, sizeof(grp_addr), (struct sockaddr*)&peer_addr, sizeof(peer_addr), mock_pgm_time_now); - fail_if (NULL == peer, "new_peer failed"); - mock_pgm_peer_set_pending (sock, peer); - push_block_event (); - guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; - gsize bytes_read; - pgm_error_t* err = NULL; - fail_unless (PGM_IO_STATUS_RESET == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); - if (err) { - g_message ("%s", err->message); - pgm_error_free (err); - err = NULL; - } - push_block_event (); - fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); -} -END_TEST - -/* recv -> lost data and abort transport */ -START_TEST (test_abort_on_lost_pass_001) -{ - pgm_sock_t* sock = generate_sock(); - fail_if (NULL == sock, "generate_sock failed"); - sock->is_reset = TRUE; - sock->is_abort_on_reset = TRUE; - const pgm_tsi_t peer_tsi = { { 9, 8, 7, 6, 5, 4 }, g_htons(9000) }; - struct sockaddr_in grp_addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr(TEST_GROUP_ADDR) - }, peer_addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr(TEST_END_ADDR) - }; - pgm_peer_t* peer = mock_pgm_new_peer (sock, &peer_tsi, (struct sockaddr*)&grp_addr, sizeof(grp_addr), (struct sockaddr*)&peer_addr, sizeof(peer_addr), mock_pgm_time_now); - fail_if (NULL == peer, "new_peer failed"); - mock_pgm_peer_set_pending (sock, peer); - push_block_event (); - guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; - gsize bytes_read; - pgm_error_t* err = NULL; - fail_unless (PGM_IO_STATUS_RESET == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); - if (err) { - g_message ("%s", err->message); - pgm_error_free (err); - err = NULL; - } - push_block_event (); - fail_unless (PGM_IO_STATUS_RESET == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); -} -END_TEST - -/* recv -> (spmr) & loss */ -START_TEST (test_then_lost_pass_001) -{ - pgm_sock_t* sock = generate_sock(); - fail_if (NULL == sock, "generate_sock failed"); - mock_reset_on_spmr = TRUE; - gpointer packet; gsize packet_len; - generate_spmr (&packet, &packet_len); - generate_msghdr (packet, packet_len); - const pgm_tsi_t peer_tsi = { { 9, 8, 7, 6, 5, 4 }, g_htons(9000) }; - struct sockaddr_in grp_addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr(TEST_GROUP_ADDR) - }, peer_addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr(TEST_END_ADDR) - }; - mock_peer = mock_pgm_new_peer (sock, &peer_tsi, (struct sockaddr*)&grp_addr, sizeof(grp_addr), (struct sockaddr*)&peer_addr, sizeof(peer_addr), mock_pgm_time_now); - fail_if (NULL == mock_peer, "new_peer failed"); - push_block_event (); - guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; - gsize bytes_read; - pgm_error_t* err = NULL; - fail_unless (PGM_IO_STATUS_RESET == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); - if (err) { - g_message ("%s", err->message); - pgm_error_free (err); - err = NULL; - } - push_block_event (); - fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); -} -END_TEST - -/* recv -> data & loss & abort transport */ -START_TEST (test_then_abort_on_lost_pass_001) -{ - pgm_sock_t* sock = generate_sock(); - fail_if (NULL == sock, "generate_sock failed"); - mock_reset_on_spmr = TRUE; - sock->is_abort_on_reset = TRUE; - gpointer packet; gsize packet_len; - generate_spmr (&packet, &packet_len); - generate_msghdr (packet, packet_len); - const pgm_tsi_t peer_tsi = { { 9, 8, 7, 6, 5, 4 }, g_htons(9000) }; - struct sockaddr_in grp_addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr(TEST_GROUP_ADDR) - }, peer_addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr(TEST_END_ADDR) - }; - mock_peer = mock_pgm_new_peer (sock, &peer_tsi, (struct sockaddr*)&grp_addr, sizeof(grp_addr), (struct sockaddr*)&peer_addr, sizeof(peer_addr), mock_pgm_time_now); - fail_if (NULL == mock_peer, "new_peer failed"); - push_block_event (); - guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; - gsize bytes_read; - pgm_error_t* err = NULL; - fail_unless (PGM_IO_STATUS_RESET == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); - if (err) { - g_message ("%s", err->message); - pgm_error_free (err); - err = NULL; - } - push_block_event (); - fail_unless (PGM_IO_STATUS_RESET == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); -} -END_TEST - -/* new waiting peer -> return data */ -START_TEST (test_on_data_pass_001) -{ - const char source[] = "i am not a string"; - pgm_sock_t* sock = generate_sock(); - fail_if (NULL == sock, "generate_sock failed"); - mock_data_on_spmr = TRUE; - gpointer packet; gsize packet_len; - generate_spmr (&packet, &packet_len); - generate_msghdr (packet, packet_len); - const pgm_tsi_t peer_tsi = { { 9, 8, 7, 6, 5, 4 }, g_htons(9000) }; - struct sockaddr_in grp_addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr(TEST_GROUP_ADDR) - }, peer_addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr(TEST_END_ADDR) - }; - mock_peer = mock_pgm_new_peer (sock, &peer_tsi, (struct sockaddr*)&grp_addr, sizeof(grp_addr), (struct sockaddr*)&peer_addr, sizeof(peer_addr), mock_pgm_time_now); - fail_if (NULL == mock_peer, "new_peer failed"); - struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); - pgm_skb_put (skb, sizeof(source)); - memcpy (skb->data, source, sizeof(source)); - struct pgm_msgv_t* msgv = g_new0 (struct pgm_msgv_t, 1); - msgv->msgv_len = 1; - msgv->msgv_skb[0] = skb; - mock_data_list = g_list_append (mock_data_list, msgv); - push_block_event (); - guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; - gsize bytes_read; - pgm_error_t* err = NULL; - fail_unless (PGM_IO_STATUS_NORMAL == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); - fail_unless (NULL == err, "error raised"); - fail_unless ((gsize)sizeof(source) == bytes_read, "unexpected data length"); - push_block_event (); - fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); -} -END_TEST - -/* zero length data waiting */ -START_TEST (test_on_zero_pass_001) -{ - pgm_sock_t* sock = generate_sock(); - fail_if (NULL == sock, "generate_sock failed"); - mock_data_on_spmr = TRUE; - gpointer packet; gsize packet_len; - generate_spmr (&packet, &packet_len); - generate_msghdr (packet, packet_len); - const pgm_tsi_t peer_tsi = { { 9, 8, 7, 6, 5, 4 }, g_htons(9000) }; - struct sockaddr_in grp_addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr(TEST_GROUP_ADDR) - }, peer_addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr(TEST_END_ADDR) - }; - mock_peer = mock_pgm_new_peer (sock, &peer_tsi, (struct sockaddr*)&grp_addr, sizeof(grp_addr), (struct sockaddr*)&peer_addr, sizeof(peer_addr), mock_pgm_time_now); - fail_if (NULL == mock_peer, "new_peer failed"); - struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); - struct pgm_msgv_t* msgv = g_new0 (struct pgm_msgv_t, 1); - msgv->msgv_len = 1; - msgv->msgv_skb[0] = skb; - mock_data_list = g_list_append (mock_data_list, msgv); - push_block_event (); - guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; - gsize bytes_read; - pgm_error_t* err = NULL; - fail_unless (PGM_IO_STATUS_NORMAL == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); - fail_unless (NULL == err, "error raised"); - fail_unless ((gsize)0 == bytes_read, "unexpected data length"); - push_block_event (); - fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); -} -END_TEST - -/* more than vector length data waiting */ -START_TEST (test_on_many_data_pass_001) -{ - const char* source[] = { - "i am not a string", - "i am not an iguana", - "i am not a peach" - }; - pgm_sock_t* sock = generate_sock(); - fail_if (NULL == sock, "generate_sock failed"); - mock_data_on_spmr = TRUE; - gpointer packet; gsize packet_len; - generate_spmr (&packet, &packet_len); - generate_msghdr (packet, packet_len); - const pgm_tsi_t peer_tsi = { { 9, 8, 7, 6, 5, 4 }, g_htons(9000) }; - struct sockaddr_in grp_addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr(TEST_GROUP_ADDR) - }, peer_addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr(TEST_END_ADDR) - }; - mock_peer = mock_pgm_new_peer (sock, &peer_tsi, (struct sockaddr*)&grp_addr, sizeof(grp_addr), (struct sockaddr*)&peer_addr, sizeof(peer_addr), mock_pgm_time_now); - fail_if (NULL == mock_peer, "new_peer failed"); - struct pgm_sk_buff_t* skb; - struct pgm_msgv_t* msgv; -/* #1 */ - msgv = g_new0 (struct pgm_msgv_t, 1); - skb = pgm_alloc_skb (TEST_MAX_TPDU); - pgm_skb_put (skb, strlen(source[0]) + 1); - memcpy (skb->data, source[0], strlen(source[0])); - msgv->msgv_len = 1; - msgv->msgv_skb[0] = skb; - mock_data_list = g_list_append (mock_data_list, msgv); -/* #2 */ - msgv = g_new0 (struct pgm_msgv_t, 1); - skb = pgm_alloc_skb (TEST_MAX_TPDU); - pgm_skb_put (skb, strlen(source[1]) + 1); - memcpy (skb->data, source[1], strlen(source[1])); - msgv->msgv_len = 1; - msgv->msgv_skb[0] = skb; - mock_data_list = g_list_append (mock_data_list, msgv); -/* #3 */ - msgv = g_new0 (struct pgm_msgv_t, 1); - skb = pgm_alloc_skb (TEST_MAX_TPDU); - pgm_skb_put (skb, strlen(source[2]) + 1); - memcpy (skb->data, source[2], strlen(source[2])); - msgv->msgv_len = 1; - msgv->msgv_skb[0] = skb; - mock_data_list = g_list_append (mock_data_list, msgv); - guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; - gsize bytes_read; - pgm_error_t* err = NULL; - fail_unless (PGM_IO_STATUS_NORMAL == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); - fail_unless (NULL == err, "error raised"); - fail_unless ((gsize)(strlen(source[0]) + 1) == bytes_read, "unexpected data length"); - g_message ("#1 = \"%s\"", buffer); - fail_unless (PGM_IO_STATUS_NORMAL == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); - fail_unless (NULL == err, "error raised"); - fail_unless ((gsize)(strlen(source[1]) + 1) == bytes_read, "unexpected data length"); - g_message ("#2 = \"%s\"", buffer); - fail_unless (PGM_IO_STATUS_NORMAL == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); - fail_unless (NULL == err, "error raised"); - fail_unless ((gsize)(strlen(source[2]) + 1) == bytes_read, "unexpected data length"); - g_message ("#3 = \"%s\"", buffer); - push_block_event (); - fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); -} -END_TEST - -START_TEST (test_recv_fail_001) -{ - guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; - fail_unless (PGM_IO_STATUS_ERROR == pgm_recv (NULL, buffer, sizeof(buffer), 0, NULL, NULL), "recv faied"); -} -END_TEST - -/* target: - * int - * pgm_recvfrom ( - * pgm_sock_t* sock, - * void* data, - * size_t len, - * int flags, - * size_t* bytes_read, - * struct pgm_sockaddr_t* from, - * socklen_t* fromlen, - * pgm_error_t** error - * ) - */ - -START_TEST (test_recvfrom_fail_001) -{ - guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; - struct pgm_sockaddr_t from; - socklen_t fromlen = sizeof(from); - fail_unless (PGM_IO_STATUS_ERROR == pgm_recvfrom (NULL, buffer, sizeof(buffer), 0, NULL, &from, &fromlen, NULL), "recvfrom failed"); -} -END_TEST - -/* target: - * int - * pgm_recvmsg ( - * pgm_sock_t* sock, - * pgm_msgv_t* msgv, - * int flags, - * size_t* bytes_read, - * pgm_error_t** error - * ) - */ - -START_TEST (test_recvmsg_fail_001) -{ - struct pgm_msgv_t msgv; - fail_unless (PGM_IO_STATUS_ERROR == pgm_recvmsg (NULL, &msgv, 0, NULL, NULL), "recvmsg failed"); -} -END_TEST - -/* target: - * int - * pgm_recvmsgv ( - * pgm_sock_t* sock, - * pgm_msgv_t* msgv, - * unsigned msgv_length, - * int flags, - * size_t* bytes_read, - * pgm_error_t** error - * ) - */ - -START_TEST (test_recvmsgv_fail_001) -{ - struct pgm_msgv_t msgv[1]; - fail_unless (PGM_IO_STATUS_ERROR == pgm_recvmsgv (NULL, msgv, G_N_ELEMENTS(msgv), 0, NULL, NULL), "recvmsgv failed"); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_block = tcase_create ("block"); - suite_add_tcase (s, tc_block); - tcase_add_checked_fixture (tc_block, mock_setup, NULL); - tcase_add_test (tc_block, test_block_pass_001); - - TCase* tc_data = tcase_create ("data"); - suite_add_tcase (s, tc_data); - tcase_add_checked_fixture (tc_data, mock_setup, NULL); - tcase_add_test (tc_data, test_data_pass_001); - - TCase* tc_spm = tcase_create ("spm"); - suite_add_tcase (s, tc_spm); - tcase_add_checked_fixture (tc_spm, mock_setup, NULL); - tcase_add_test (tc_spm, test_spm_pass_001); - - TCase* tc_nak = tcase_create ("nak"); - suite_add_tcase (s, tc_nak); - tcase_add_checked_fixture (tc_nak, mock_setup, NULL); - tcase_add_test (tc_nak, test_nak_pass_001); - - TCase* tc_peer_nak = tcase_create ("peer-nak"); - suite_add_tcase (s, tc_peer_nak); - tcase_add_checked_fixture (tc_peer_nak, mock_setup, NULL); - tcase_add_test (tc_peer_nak, test_peer_nak_pass_001); - - TCase* tc_nnak = tcase_create ("nnak"); - suite_add_tcase (s, tc_nnak); - tcase_add_checked_fixture (tc_nnak, mock_setup, NULL); - tcase_add_test (tc_nnak, test_nnak_pass_001); - - TCase* tc_ncf = tcase_create ("ncf"); - suite_add_tcase (s, tc_ncf); - tcase_add_checked_fixture (tc_ncf, mock_setup, NULL); - tcase_add_test (tc_ncf, test_ncf_pass_001); - - TCase* tc_spmr = tcase_create ("spmr"); - suite_add_tcase (s, tc_spmr); - tcase_add_checked_fixture (tc_spmr, mock_setup, NULL); - tcase_add_test (tc_spmr, test_spmr_pass_001); - - TCase* tc_peer_spmr = tcase_create ("peer-spmr"); - suite_add_tcase (s, tc_peer_spmr); - tcase_add_checked_fixture (tc_peer_spmr, mock_setup, NULL); - tcase_add_test (tc_peer_spmr, test_peer_spmr_pass_001); - - TCase* tc_lost = tcase_create ("lost"); - suite_add_tcase (s, tc_lost); - tcase_add_checked_fixture (tc_lost, mock_setup, NULL); - tcase_add_test (tc_lost, test_lost_pass_001); - - TCase* tc_abort_on_lost = tcase_create ("abort-on-lost"); - suite_add_tcase (s, tc_abort_on_lost); - tcase_add_checked_fixture (tc_abort_on_lost, mock_setup, NULL); - tcase_add_test (tc_abort_on_lost, test_abort_on_lost_pass_001); - - TCase* tc_then_lost = tcase_create ("then-lost"); - suite_add_tcase (s, tc_then_lost); - tcase_add_checked_fixture (tc_then_lost, mock_setup, NULL); - tcase_add_test (tc_then_lost, test_then_lost_pass_001); - - TCase* tc_then_abort_on_lost = tcase_create ("then-abort-on-lost"); - suite_add_tcase (s, tc_then_abort_on_lost); - tcase_add_checked_fixture (tc_then_abort_on_lost, mock_setup, NULL); - tcase_add_test (tc_then_abort_on_lost, test_then_abort_on_lost_pass_001); - - TCase* tc_on_data = tcase_create ("on-data"); - suite_add_tcase (s, tc_on_data); - tcase_add_checked_fixture (tc_on_data, mock_setup, NULL); - tcase_add_test (tc_on_data, test_on_data_pass_001); - - TCase* tc_on_zero = tcase_create ("on-zero"); - suite_add_tcase (s, tc_on_zero); - tcase_add_checked_fixture (tc_on_zero, mock_setup, NULL); - tcase_add_test (tc_on_zero, test_on_zero_pass_001); - - TCase* tc_on_many_data = tcase_create ("on-many-data"); - suite_add_tcase (s, tc_on_many_data); - tcase_add_checked_fixture (tc_on_many_data, mock_setup, NULL); - tcase_add_test (tc_on_many_data, test_on_many_data_pass_001); - - TCase* tc_recv = tcase_create ("recv"); - suite_add_tcase (s, tc_recv); - tcase_add_checked_fixture (tc_recv, mock_setup, NULL); - tcase_add_test (tc_recv, test_recv_fail_001); - - TCase* tc_recvfrom = tcase_create ("recvfrom"); - suite_add_tcase (s, tc_recvfrom); - tcase_add_checked_fixture (tc_recvfrom, mock_setup, NULL); - tcase_add_test (tc_recvfrom, test_recvfrom_fail_001); - - TCase* tc_recvmsg = tcase_create ("recvmsg"); - suite_add_tcase (s, tc_recvmsg); - tcase_add_checked_fixture (tc_recvmsg, mock_setup, NULL); - tcase_add_test (tc_recvmsg, test_recvmsg_fail_001); - - TCase* tc_recvmsgv = tcase_create ("recvmsgv"); - suite_add_tcase (s, tc_recvmsgv); - tcase_add_checked_fixture (tc_recvmsgv, mock_setup, NULL); - tcase_add_test (tc_recvmsgv, test_recvmsgv_fail_001); - - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/reed_solomon.c b/3rdparty/openpgm-svn-r1085/pgm/reed_solomon.c deleted file mode 100644 index a9a292c..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/reed_solomon.c +++ /dev/null @@ -1,576 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Reed-Solomon forward error correction based on Vandermonde matrices. - * - * Output is incompatible with BCH style Reed-Solomon encoding. - * - * draft-ietf-rmt-bb-fec-rs-05.txt - * + rfc5052 - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - - -/* Vector GF(2⁸) plus-equals multiplication. - * - * d[] += b • s[] - */ - -static -void -_pgm_gf_vec_addmul ( - pgm_gf8_t* restrict d, - const pgm_gf8_t b, - const pgm_gf8_t* restrict s, - uint16_t len /* length of vectors */ - ) -{ - uint_fast16_t i; - uint_fast16_t count8; - - if (PGM_UNLIKELY(b == 0)) - return; - -#ifdef CONFIG_GALOIS_MUL_LUT - const pgm_gf8_t* gfmul_b = &pgm_gftable[ (uint16_t)b << 8 ]; -#endif - - i = 0; - count8 = len >> 3; /* 8-way unrolls */ - if (count8) - { - while (count8--) { -#ifdef CONFIG_GALOIS_MUL_LUT - d[i ] ^= gfmul_b[ s[i ] ]; - d[i+1] ^= gfmul_b[ s[i+1] ]; - d[i+2] ^= gfmul_b[ s[i+2] ]; - d[i+3] ^= gfmul_b[ s[i+3] ]; - d[i+4] ^= gfmul_b[ s[i+4] ]; - d[i+5] ^= gfmul_b[ s[i+5] ]; - d[i+6] ^= gfmul_b[ s[i+6] ]; - d[i+7] ^= gfmul_b[ s[i+7] ]; -#else - d[i ] ^= gfmul( b, s[i ] ); - d[i+1] ^= gfmul( b, s[i+1] ); - d[i+2] ^= gfmul( b, s[i+2] ); - d[i+3] ^= gfmul( b, s[i+3] ); - d[i+4] ^= gfmul( b, s[i+4] ); - d[i+5] ^= gfmul( b, s[i+5] ); - d[i+6] ^= gfmul( b, s[i+6] ); - d[i+7] ^= gfmul( b, s[i+7] ); -#endif - i += 8; - } - -/* remaining */ - len %= 8; - } - - while (len--) { -#ifdef CONFIG_GALOIS_MUL_LUT - d[i] ^= gfmul_b[ s[i] ]; -#else - d[i] ^= gfmul( b, s[i] ); -#endif - i++; - } -} - -/* Basic matrix multiplication. - * - * C = AB - * n - * c_i,j = ∑ a_i,j × b_r,j = a_i,1 × b_1,j + a_i,2 × b_2,j + ⋯ + a_i,n × b_n,j - * r=1 - */ - -static -void -_pgm_matmul ( - const pgm_gf8_t* restrict a, /* m-by-n */ - const pgm_gf8_t* restrict b, /* n-by-p */ - pgm_gf8_t* restrict c, /* ∴ m-by-p */ - const uint16_t m, - const uint16_t n, - const uint16_t p - ) -{ - for (uint_fast16_t j = 0; j < m; j++) - { - for (uint_fast16_t i = 0; i < p; i++) - { - pgm_gf8_t sum = 0; - - for (uint_fast16_t k = 0; k < n; k++) - { - sum ^= pgm_gfmul ( a[ (j * n) + k ], b[ (k * p) + i ] ); - } - - c[ (j * p) + i ] = sum; - } - } -} - -/* Generic square matrix inversion - */ - -#ifdef CONFIG_XOR_SWAP -/* whilst cute the xor swap is quite slow */ -#define SWAP(a, b) (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b))) -#else -#define SWAP(a, b) do { const pgm_gf8_t _t = (b); (b) = (a); (a) = _t; } while (0) -#endif - -static -void -_pgm_matinv ( - pgm_gf8_t* M, /* is n-by-n */ - const uint8_t n - ) -{ - uint8_t pivot_rows[ n ]; - uint8_t pivot_cols[ n ]; - bool pivots[ n ]; - memset (pivots, 0, sizeof(pivots)); - - pgm_gf8_t identity[ n ]; - memset (identity, 0, sizeof(identity)); - - for (uint_fast8_t i = 0; i < n; i++) - { - uint_fast8_t row = 0, col = 0; - -/* check diagonal for new pivot */ - if (!pivots[ i ] && M[ (i * n) + i ]) - { - row = col = i; - } - else - { - for (uint_fast8_t j = 0; j < n; j++) - { - if (pivots[ j ]) continue; - - for (uint_fast8_t x = 0; x < n; x++) - { - if (!pivots[ x ] && M[ (j * n) + x ]) - { - row = j; - col = x; - goto found; - } - } - } - } - -found: - pivots[ col ] = TRUE; - -/* pivot */ - if (row != col) - { - for (uint_fast8_t x = 0; x < n; x++) - { - pgm_gf8_t *pivot_row = &M[ (row * n) + x ], - *pivot_col = &M[ (col * n) + x ]; - SWAP( *pivot_row, *pivot_col ); - } - } - -/* save location */ - pivot_rows[ i ] = row; - pivot_cols[ i ] = col; - -/* divide row by pivot element */ - if (M[ (col * n) + col ] != 1) - { - const pgm_gf8_t c = M[ (col * n) + col ]; - M[ (col * n) + col ] = 1; - - for (uint_fast8_t x = 0; x < n; x++) - { - M[ (col * n) + x ] = pgm_gfdiv( M[ (col * n) + x ], c ); - } - } - -/* reduce if not an identity row */ - identity[ col ] = 1; - if (memcmp (&M[ (col * n) ], identity, n * sizeof(pgm_gf8_t))) - { - for ( uint_fast8_t x = 0; - x < n; - x++ ) - { - if (x == col) continue; - - const pgm_gf8_t c = M[ (x * n) + col ]; - M[ (x * n) + col ] = 0; - - _pgm_gf_vec_addmul (&M[ x * n ], c, &M[ col * n ], n); - } - } - identity[ col ] = 0; - } - -/* revert all pivots */ - for (int_fast16_t i = n - 1; i >= 0; i--) - { - if (pivot_rows[ i ] != pivot_cols[ i ]) - { - for (uint_fast8_t j = 0; j < n; j++) - { - pgm_gf8_t *pivot_row = &M[ (j * n) + pivot_rows[ i ] ], - *pivot_col = &M[ (j * n) + pivot_cols[ i ] ]; - SWAP( *pivot_row, *pivot_col ); - } - } - } -} - -/* Gauss–Jordan elimination optimised for Vandermonde matrices - * - * matrix = matrix⁻¹ - * - * A Vandermonde matrix exhibits geometric progression in each row: - * - * ⎡ 1 α₁ α₁² ⋯ α₁^^(n-1) ⎤ - * V = ⎢ 1 α₂ α₂² ⋯ α₂^^(n-1) ⎥ - * ⎣ 1 α₃ α₃² ⋯ α₃^^(n-1) ⎦ - * - * First column is actually α_m⁰, second column is α_m¹. - * - * As only the second column is actually unique so optimise from that. - */ - -static -void -_pgm_matinv_vandermonde ( - pgm_gf8_t* V, /* is n-by-n */ - const uint8_t n - ) -{ -/* trivial cases */ - if (n == 1) return; - -/* P_j(α) is polynomial of degree n - 1 defined by - * - * n - * P_j(α) = ∏ (α - α_m) - * m=1 - * - * 1: Work out coefficients. - */ - - pgm_gf8_t P[ n ]; - memset (P, 0, sizeof(P)); - -/* copy across second row, i.e. j = 2 */ - for (uint_fast8_t i = 0; i < n; i++) - { - P[ i ] = V[ (i * n) + 1 ]; - } - - pgm_gf8_t alpha[ n ]; - memset (alpha, 0, sizeof(alpha)); - - alpha[ n - 1 ] = P[ 0 ]; - for (uint_fast8_t i = 1; i < n; i++) - { - for (uint_fast8_t j = (n - i); j < (n - 1); j++) - { - alpha[ j ] ^= pgm_gfmul( P[ i ], alpha[ j + 1 ] ); - } - alpha[ n - 1 ] ^= P[ i ]; - } - -/* 2: Obtain numberators and denominators by synthetic division. - */ - - pgm_gf8_t b[ n ]; - b[ n - 1 ] = 1; - for (uint_fast8_t j = 0; j < n; j++) - { - const pgm_gf8_t xx = P[ j ]; - pgm_gf8_t t = 1; - -/* skip first iteration */ - for (int_fast16_t i = n - 2; i >= 0; i--) - { - b[ i ] = alpha[ i + 1 ] ^ pgm_gfmul( xx, b[ i + 1 ] ); - t = pgm_gfmul( xx, t ) ^ b[ i ]; - } - - for (uint_fast8_t i = 0; i < n; i++) - { - V[ (i * n) + j ] = pgm_gfdiv ( b[ i ], t ); - } - } -} - -/* create the generator matrix of a reed-solomon code. - * - * s GM e - * ⎧ ⎡ s₀ ⎤ ⎡ 1 0 0 ⎤ ⎡ e₀ ⎤ ⎫ - * ⎪ ⎢ ⋮ ⎥ ⎢ 0 1 ⎥ = ⎢ ⋮ ⎥ ⎬ n - * k ⎨ ⎢ ⋮ ⎥ × ⎢ ⋱ ⎥ ⎣e_{n-1}⎦ ⎭ - * ⎪ ⎢ ⋮ ⎥ ⎢ ⋱ ⎥ - * ⎩ ⎣s_{k-1}⎦ ⎣ 0 0 1 ⎦ - * - * e = s × GM - */ - -void -pgm_rs_create ( - pgm_rs_t* rs, - const uint8_t n, - const uint8_t k - ) -{ - pgm_assert (NULL != rs); - pgm_assert (n > 0); - pgm_assert (k > 0); - - rs->n = n; - rs->k = k; - rs->GM = pgm_new0 (pgm_gf8_t, n * k); - rs->RM = pgm_new0 (pgm_gf8_t, k * k); - -/* alpha = root of primitive polynomial of degree m - * ( 1 + x² + x³ + x⁴ + x⁸ ) - * - * V = Vandermonde matrix of k rows and n columns. - * - * Be careful, Harry! - */ -#ifdef CONFIG_PREFER_MALLOC - pgm_gf8_t* V = pgm_new0 (pgm_gf8_t, n * k); -#else - pgm_gf8_t* V = pgm_newa (pgm_gf8_t, n * k); - memset (V, 0, n * k); -#endif - pgm_gf8_t* p = V + k; - V[0] = 1; - for (uint_fast8_t j = 0; j < (n - 1); j++) - { - for (uint_fast8_t i = 0; i < k; i++) - { -/* the {i, j} entry of V_{k,n} is v_{i,j} = α^^(i×j), - * where 0 <= i <= k - 1 and 0 <= j <= n - 1. - */ - *p++ = pgm_gfantilog[ ( i * j ) % PGM_GF_MAX ]; - } - } - -/* This generator matrix would create a Maximum Distance Separable (MDS) - * matrix, a systematic result is required, i.e. original data is left - * unchanged. - * - * GM = V_{k,k}⁻¹ × V_{k,n} - * - * 1: matrix V_{k,k} formed by the first k columns of V_{k,n} - */ - pgm_gf8_t* V_kk = V; - pgm_gf8_t* V_kn = V + (k * k); - -/* 2: invert it - */ - _pgm_matinv_vandermonde (V_kk, k); - -/* 3: multiply by V_{k,n} - */ - _pgm_matmul (V_kn, V_kk, rs->GM + (k * k), n - k, k, k); - -#ifdef CONFIG_PREFER_MALLOC - pgm_free (V); -#endif - -/* 4: set identity matrix for original data - */ - for (uint_fast8_t i = 0; i < k; i++) - { - rs->GM[ (i * k) + i ] = 1; - } -} - -void -pgm_rs_destroy ( - pgm_rs_t* rs - ) -{ - pgm_assert (NULL != rs); - - if (rs->RM) { - pgm_free (rs->RM); - rs->RM = NULL; - } - - if (rs->GM) { - pgm_free (rs->GM); - rs->GM = NULL; - } -} - -/* create a parity packet from a vector of original data packets and - * FEC block packet offset. - */ - -void -pgm_rs_encode ( - pgm_rs_t* restrict rs, - const pgm_gf8_t** restrict src, /* length rs_t::k */ - const uint8_t offset, - pgm_gf8_t* restrict dst, - const uint16_t len - ) -{ - pgm_assert (NULL != rs); - pgm_assert (NULL != src); - pgm_assert (offset >= rs->k && offset < rs->n); /* parity packet */ - pgm_assert (NULL != dst); - pgm_assert (len > 0); - - memset (dst, 0, len); - for (uint_fast8_t i = 0; i < rs->k; i++) - { - const pgm_gf8_t c = rs->GM[ (offset * rs->k) + i ]; - _pgm_gf_vec_addmul (dst, c, src[i], len); - } -} - -/* original data block of packets with missing packet entries replaced - * with on-demand parity packets. - */ - -void -pgm_rs_decode_parity_inline ( - pgm_rs_t* restrict rs, - pgm_gf8_t** restrict block, /* length rs_t::k */ - const uint8_t* restrict offsets, /* offsets within FEC block, 0 < offset < n */ - const uint16_t len /* packet length */ - ) -{ - pgm_assert (NULL != rs); - pgm_assert (NULL != block); - pgm_assert (NULL != offsets); - pgm_assert (len > 0); - -/* create new recovery matrix from generator - */ - for (uint_fast8_t i = 0; i < rs->k; i++) - { - if (offsets[i] < rs->k) { - memset (&rs->RM[ i * rs->k ], 0, rs->k * sizeof(pgm_gf8_t)); - rs->RM[ (i * rs->k) + i ] = 1; - continue; - } - memcpy (&rs->RM[ i * rs->k ], &rs->GM[ offsets[ i ] * rs->k ], rs->k * sizeof(pgm_gf8_t)); - } - -/* invert */ - _pgm_matinv (rs->RM, rs->k); - - pgm_gf8_t* repairs[ rs->k ]; - -/* multiply out, through the length of erasures[] */ - for (uint_fast8_t j = 0; j < rs->k; j++) - { - if (offsets[ j ] < rs->k) - continue; - -#ifdef CONFIG_PREFER_MALLOC - pgm_gf8_t* erasure = repairs[ j ] = pgm_malloc0 (len); -#else - pgm_gf8_t* erasure = repairs[ j ] = pgm_alloca (len); - memset (erasure, 0, len); -#endif - for (uint_fast8_t i = 0; i < rs->k; i++) - { - pgm_gf8_t* src = block[ i ]; - pgm_gf8_t c = rs->RM[ (j * rs->k) + i ]; - _pgm_gf_vec_addmul (erasure, c, src, len); - } - } - -/* move repaired over parity packets */ - for (uint_fast8_t j = 0; j < rs->k; j++) - { - if (offsets[ j ] < rs->k) - continue; - - memcpy (block[ j ], repairs[ j ], len * sizeof(pgm_gf8_t)); -#ifdef CONFIG_PREFER_MALLOC - pgm_free (repairs[ j ]); -#endif - } -} - -/* entire FEC block of original data and parity packets. - * - * erased packet buffers must be zeroed. - */ -void -pgm_rs_decode_parity_appended ( - pgm_rs_t* restrict rs, - pgm_gf8_t** restrict block, /* length rs_t::n, the FEC block */ - const uint8_t* restrict offsets, /* ordered index of packets */ - const uint16_t len /* packet length */ - ) -{ - pgm_assert (NULL != rs); - pgm_assert (NULL != block); - pgm_assert (NULL != offsets); - pgm_assert (len > 0); - -/* create new recovery matrix from generator - */ - for (uint_fast8_t i = 0; i < rs->k; i++) - { - if (offsets[i] < rs->k) { - memset (&rs->RM[ i * rs->k ], 0, rs->k * sizeof(pgm_gf8_t)); - rs->RM[ (i * rs->k) + i ] = 1; - continue; - } - memcpy (&rs->RM[ i * rs->k ], &rs->GM[ offsets[ i ] * rs->k ], rs->k * sizeof(pgm_gf8_t)); - } - -/* invert */ - _pgm_matinv (rs->RM, rs->k); - -/* multiply out, through the length of erasures[] */ - for (uint_fast8_t j = 0; j < rs->k; j++) - { - if (offsets[ j ] < rs->k) - continue; - - uint_fast8_t p = rs->k; - pgm_gf8_t* erasure = block[ j ]; - for (uint_fast8_t i = 0; i < rs->k; i++) - { - pgm_gf8_t* src; - if (offsets[ i ] < rs->k) - src = block[ i ]; - else - src = block[ p++ ]; - const pgm_gf8_t c = rs->RM[ (j * rs->k) + i ]; - _pgm_gf_vec_addmul (erasure, c, src, len); - } - } -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/reed_solomon_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/reed_solomon_unittest.c deleted file mode 100644 index 5e0e75e..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/reed_solomon_unittest.c +++ /dev/null @@ -1,305 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for Reed-Solomon forward error correction based on Vandermonde matrices. - * - * Copyright (c) 2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include -#include - - -/* mock state */ - - -/* mock functions for external references */ - -size_t -pgm_transport_pkt_offset2 ( - const bool can_fragment, - const bool use_pgmcc - ) -{ - return 0; -} - - -#define REED_SOLOMON_DEBUG -#include "reed_solomon.c" - - -/* target: - * void - * pgm_rs_create ( - * pgm_rs_t* rs, - * const uint8_t n, - * const uint8_t k - * ) - */ - -START_TEST (test_create_pass_001) -{ - pgm_rs_t rs; - pgm_rs_create (&rs, 255, 16); -} -END_TEST - -START_TEST (test_create_fail_001) -{ - pgm_rs_create (NULL, 255, 16); - fail ("reached"); -} -END_TEST - -/* target: - * void - * pgm_rs_destroy ( - * pgm_rs_t* rs, - * ) - */ - -START_TEST (test_destroy_pass_001) -{ - pgm_rs_t rs; - pgm_rs_create (&rs, 255, 16); - pgm_rs_destroy (&rs); -} -END_TEST - -START_TEST (test_destroy_fail_001) -{ - pgm_rs_destroy (NULL); - fail ("reached"); -} -END_TEST - -/* target: - * void - * pgm_rs_encode ( - * pgm_rs_t* rs, - * const pgm_gf8_t** src, - * const uint8_t offset, - * pgm_gf8_t* dst, - * const uint16_t len - * ) - */ - -START_TEST (test_encode_pass_001) -{ - const gchar source[] = "i am not a string"; - const guint16 source_len = strlen (source); - pgm_rs_t rs; - const guint8 k = source_len; - const guint8 parity_index = k; - const guint16 packet_len = 100; - pgm_gf8_t* source_packets[k]; - pgm_gf8_t* parity_packet = g_malloc0 (packet_len); - pgm_rs_create (&rs, 255, k); - for (unsigned i = 0; i < k; i++) { - source_packets[i] = g_malloc0 (packet_len); - source_packets[i][0] = source[i]; - g_message ("packet#%2.2d: 0x%02.2x '%c'", i, source[i], source[i]); - } - pgm_rs_encode (&rs, (const pgm_gf8_t**)source_packets, parity_index, parity_packet, packet_len); - g_message ("parity-packet: %02.2x", parity_packet[0]); - pgm_rs_destroy (&rs); -} -END_TEST - -START_TEST (test_encode_fail_001) -{ - pgm_rs_encode (NULL, NULL, 0, NULL, 0); - fail ("reached"); -} -END_TEST - -/* target: - * void - * pgm_rs_decode_parity_inline ( - * pgm_rs_t* rs, - * pgm_gf8_t** block, - * const uint8_t* offsets, - * const uint16_t len - * ) - */ - -START_TEST (test_decode_parity_inline_pass_001) -{ - const gchar source[] = "i am not a string"; - const guint16 source_len = strlen (source); - pgm_rs_t rs; - const guint8 k = source_len; - const guint8 parity_index = k; - const guint16 packet_len = 100; - pgm_gf8_t* source_packets[k]; - pgm_gf8_t* parity_packet = g_malloc0 (packet_len); - pgm_rs_create (&rs, 255, k); - for (unsigned i = 0; i < k; i++) { - source_packets[i] = g_malloc0 (packet_len); - source_packets[i][0] = source[i]; - } - pgm_rs_encode (&rs, (const pgm_gf8_t**)source_packets, parity_index, parity_packet, packet_len); -/* simulate error occuring at index #3 */ - const guint erased_index = 3; - source_packets[erased_index][0] = 'X'; - for (unsigned i = 0; i < k; i++) { - g_message ("damaged-packet#%2.2d: 0x%02.2x '%c'", - i, source_packets[i][0], source_packets[i][0]); - } - guint8 offsets[k]; - for (unsigned i = 0; i < k; i++) - offsets[i] = i; -/* erased offset now points to parity packet at k */ - offsets[erased_index] = parity_index; -/* move parity inline */ - g_free (source_packets[erased_index]); - source_packets[erased_index] = parity_packet; - pgm_rs_decode_parity_inline (&rs, source_packets, offsets, packet_len); - pgm_rs_destroy (&rs); - for (unsigned i = 0; i < k; i++) { - g_message ("repaired-packet#%2.2d: 0x%02.2x '%c'", - i, source_packets[i][0], source_packets[i][0]); - } -} -END_TEST - -START_TEST (test_decode_parity_inline_fail_001) -{ - pgm_rs_decode_parity_inline (NULL, NULL, NULL, 0); - fail ("reached"); -} -END_TEST - -/* target: - * void - * pgm_rs_decode_parity_appended ( - * pgm_rs_t* rs, - * pgm_gf8_t* block, - * const uint8_t* offsets, - * const uint16_t len - * ) - */ - -START_TEST (test_decode_parity_appended_pass_001) -{ - const gchar source[] = "i am not a string"; - const guint16 source_len = strlen (source); - pgm_rs_t rs; - const guint8 k = source_len; - const guint8 parity_index = k; - const guint16 packet_len = 100; - pgm_gf8_t* source_packets[k+1]; /* include 1 appended parity packet */ - pgm_gf8_t* parity_packet = g_malloc0 (packet_len); - pgm_rs_create (&rs, 255, k); - for (unsigned i = 0; i < k; i++) { - source_packets[i] = g_malloc0 (packet_len); - source_packets[i][0] = source[i]; - } - pgm_rs_encode (&rs, (const pgm_gf8_t**)source_packets, parity_index, parity_packet, packet_len); -/* simulate error occuring at index #3 */ - const guint erased_index = 3; - source_packets[erased_index][0] = 'X'; - for (unsigned i = 0; i < k; i++) { - g_message ("damaged-packet#%2.2d: 0x%02.2x '%c'", - i, source_packets[i][0], source_packets[i][0]); - } - guint8 offsets[k]; - for (unsigned i = 0; i < k; i++) - offsets[i] = i; -/* erased offset now points to parity packet at k */ - offsets[erased_index] = parity_index; -/* erase damaged packet */ - memset (source_packets[erased_index], 0, packet_len); -/* append parity to source packet block */ - source_packets[parity_index] = parity_packet; - pgm_rs_decode_parity_appended (&rs, source_packets, offsets, packet_len); - pgm_rs_destroy (&rs); - for (unsigned i = 0; i < k; i++) { - g_message ("repaired-packet#%2.2d: 0x%02.2x '%c'", - i, source_packets[i][0], source_packets[i][0]); - } -} -END_TEST - -START_TEST (test_decode_parity_appended_fail_001) -{ - pgm_rs_decode_parity_appended (NULL, NULL, NULL, 0); - fail ("reached"); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_create = tcase_create ("create"); - suite_add_tcase (s, tc_create); - tcase_add_test (tc_create, test_create_pass_001); - tcase_add_test_raise_signal (tc_create, test_create_fail_001, SIGABRT); - - TCase* tc_destroy = tcase_create ("destroy"); - suite_add_tcase (s, tc_destroy); - tcase_add_test (tc_destroy, test_destroy_pass_001); - tcase_add_test_raise_signal (tc_destroy, test_destroy_fail_001, SIGABRT); - - TCase* tc_encode = tcase_create ("encode"); - suite_add_tcase (s, tc_encode); - tcase_add_test (tc_encode, test_encode_pass_001); - tcase_add_test_raise_signal (tc_encode, test_encode_fail_001, SIGABRT); - - TCase* tc_decode_parity_inline = tcase_create ("decode-parity-inline"); - suite_add_tcase (s, tc_decode_parity_inline); - tcase_add_test (tc_decode_parity_inline, test_decode_parity_inline_pass_001); - tcase_add_test_raise_signal (tc_decode_parity_inline, test_decode_parity_inline_fail_001, SIGABRT); - - TCase* tc_decode_parity_appended = tcase_create ("decode-parity-appended"); - suite_add_tcase (s, tc_decode_parity_appended); - tcase_add_test (tc_decode_parity_appended, test_decode_parity_appended_pass_001); - tcase_add_test_raise_signal (tc_decode_parity_appended, test_decode_parity_appended_fail_001, SIGABRT); - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/rxw.c b/3rdparty/openpgm-svn-r1085/pgm/rxw.c deleted file mode 100644 index ce04ef5..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/rxw.c +++ /dev/null @@ -1,2229 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * A basic receive window: pointer array implementation. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define __STDC_FORMAT_MACROS -#include -#include -#include -#include - - -//#define RXW_DEBUG - -#ifndef RXW_DEBUG -# define PGM_DISABLE_ASSERT -#endif - - -/* testing function: is TSI null - * - * returns TRUE if null, returns FALSE if not null. - */ - -static inline -bool -_pgm_tsi_is_null ( - const void*const tsi - ) -{ - const union { - pgm_tsi_t tsi; - uint32_t l[2]; - } *u = tsi; - -/* pre-conditions */ - pgm_assert (NULL != tsi); - - return (0 == u->l[0] && 0 == u->l[1]); -} - -/* sequence state must be smaller than PGM skbuff control buffer */ -PGM_STATIC_ASSERT(sizeof(struct pgm_rxw_state_t) <= sizeof(((struct pgm_sk_buff_t*)0)->cb)); - -static void _pgm_rxw_define (pgm_rxw_t*const, const uint32_t); -static void _pgm_rxw_update_trail (pgm_rxw_t*const, const uint32_t); -static inline uint32_t _pgm_rxw_update_lead (pgm_rxw_t*const, const uint32_t, const pgm_time_t, const pgm_time_t); -static inline uint32_t _pgm_rxw_tg_sqn (pgm_rxw_t*const, const uint32_t); -static inline uint32_t _pgm_rxw_pkt_sqn (pgm_rxw_t*const, const uint32_t); -static inline bool _pgm_rxw_is_first_of_tg_sqn (pgm_rxw_t*const, const uint32_t); -static inline bool _pgm_rxw_is_last_of_tg_sqn (pgm_rxw_t*const, const uint32_t); -static int _pgm_rxw_insert (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*const restrict); -static int _pgm_rxw_append (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*const restrict, const pgm_time_t); -static int _pgm_rxw_add_placeholder_range (pgm_rxw_t*const, const uint32_t, const pgm_time_t, const pgm_time_t); -static void _pgm_rxw_unlink (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*restrict); -static uint32_t _pgm_rxw_remove_trail (pgm_rxw_t*const); -static void _pgm_rxw_state (pgm_rxw_t*restrict, struct pgm_sk_buff_t*restrict, const int); -static inline void _pgm_rxw_shuffle_parity (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*const restrict); -static inline ssize_t _pgm_rxw_incoming_read (pgm_rxw_t*const restrict, struct pgm_msgv_t**restrict, uint32_t); -static bool _pgm_rxw_is_apdu_complete (pgm_rxw_t*const restrict, const uint32_t); -static inline ssize_t _pgm_rxw_incoming_read_apdu (pgm_rxw_t*const restrict, struct pgm_msgv_t**restrict); -static inline int _pgm_rxw_recovery_update (pgm_rxw_t*const, const uint32_t, const pgm_time_t); -static inline int _pgm_rxw_recovery_append (pgm_rxw_t*const, const pgm_time_t, const pgm_time_t); - - -/* returns the pointer at the given index of the window. - */ - -static -struct pgm_sk_buff_t* -_pgm_rxw_peek ( - const pgm_rxw_t* const window, - const uint32_t sequence - ) -{ -/* pre-conditions */ - pgm_assert (NULL != window); - - if (pgm_rxw_is_empty (window)) - return NULL; - - if (pgm_uint32_gte (sequence, window->trail) && pgm_uint32_lte (sequence, window->lead)) - { - const uint_fast32_t index_ = sequence % pgm_rxw_max_length (window); - struct pgm_sk_buff_t* skb = window->pdata[index_]; -/* availability only guaranteed inside commit window */ - if (pgm_uint32_lt (sequence, window->commit_lead)) { - pgm_assert (NULL != skb); - pgm_assert (pgm_skb_is_valid (skb)); - pgm_assert (!_pgm_tsi_is_null (&skb->tsi)); - } - return skb; - } - - return NULL; -} - -/* sections of the receive window: - * - * | Commit | Incoming | - * |<---------------->|<------------>| - * | | | - * trail commit-lead lead - * - * commit buffers are currently held by the application, the window trail - * cannot be advanced if packets remain in the commit buffer. - * - * incoming buffers are waiting to be passed to the application. - */ - -static inline -uint32_t -_pgm_rxw_commit_length ( - const pgm_rxw_t* const window - ) -{ - pgm_assert (NULL != window); - return window->commit_lead - window->trail; -} - -static inline -bool -_pgm_rxw_commit_is_empty ( - const pgm_rxw_t* const window - ) -{ - pgm_assert (NULL != window); - return (_pgm_rxw_commit_length (window) == 0); -} - -static inline -uint32_t -_pgm_rxw_incoming_length ( - const pgm_rxw_t* const window - ) -{ - pgm_assert (NULL != window); - return ( 1 + window->lead ) - window->commit_lead; -} - -static inline -bool -_pgm_rxw_incoming_is_empty ( - const pgm_rxw_t* const window - ) -{ - pgm_assert (NULL != window); - return (_pgm_rxw_incoming_length (window) == 0); -} - -/* constructor for receive window. zero-length windows are not permitted. - * - * returns pointer to window. - */ - -pgm_rxw_t* -pgm_rxw_create ( - const pgm_tsi_t* tsi, - const uint16_t tpdu_size, - const unsigned sqns, /* transmit window size in sequence numbers */ - const unsigned secs, /* size in seconds */ - const ssize_t max_rte, /* max bandwidth */ - const uint32_t ack_c_p - ) -{ - pgm_rxw_t* window; - -/* pre-conditions */ - pgm_assert (NULL != tsi); - pgm_assert_cmpuint (tpdu_size, >, 0); - if (sqns) { - pgm_assert_cmpuint (sqns, >, 0); - pgm_assert_cmpuint (sqns & PGM_UINT32_SIGN_BIT, ==, 0); - pgm_assert_cmpuint (secs, ==, 0); - pgm_assert_cmpuint (max_rte, ==, 0); - } else { - pgm_assert_cmpuint (secs, >, 0); - pgm_assert_cmpuint (max_rte, >, 0); - } - - pgm_debug ("create (tsi:%s max-tpdu:%" PRIu16 " sqns:%" PRIu32 " secs %u max-rte %zd ack-c_p %" PRIu32 ")", - pgm_tsi_print (tsi), tpdu_size, sqns, secs, max_rte, ack_c_p); - -/* calculate receive window parameters */ - pgm_assert (sqns || (secs && max_rte)); - const unsigned alloc_sqns = sqns ? sqns : ( (secs * max_rte) / tpdu_size ); - window = pgm_malloc0 (sizeof(pgm_rxw_t) + ( alloc_sqns * sizeof(struct pgm_sk_buff_t*) )); - - window->tsi = tsi; - window->max_tpdu = tpdu_size; - -/* empty state: - * - * trail = 0, lead = -1 - * commit_trail = commit_lead = rxw_trail = rxw_trail_init = 0 - */ - window->lead = -1; - window->trail = window->lead + 1; - -/* limit retransmit requests on late session joining */ - window->is_constrained = TRUE; - -/* minimum value of RS::k = 1 */ - window->tg_size = 1; - -/* PGMCC filter weight */ - window->ack_c_p = pgm_fp16 (ack_c_p); - window->bitmap = 0xffffffff; - -/* pointer array */ - window->alloc = alloc_sqns; - -/* post-conditions */ - pgm_assert_cmpuint (pgm_rxw_max_length (window), ==, alloc_sqns); - pgm_assert_cmpuint (pgm_rxw_length (window), ==, 0); - pgm_assert_cmpuint (pgm_rxw_size (window), ==, 0); - pgm_assert (pgm_rxw_is_empty (window)); - pgm_assert (!pgm_rxw_is_full (window)); - - return window; -} - -/* destructor for receive window. must not be called more than once for same window. - */ - -void -pgm_rxw_destroy ( - pgm_rxw_t* const window - ) -{ -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert_cmpuint (window->alloc, >, 0); - - pgm_debug ("destroy (window:%p)", (const void*)window); - -/* contents of window */ - while (!pgm_rxw_is_empty (window)) { - _pgm_rxw_remove_trail (window); - } - -/* window must now be empty */ - pgm_assert_cmpuint (pgm_rxw_length (window), ==, 0); - pgm_assert_cmpuint (pgm_rxw_size (window), ==, 0); - pgm_assert (pgm_rxw_is_empty (window)); - pgm_assert (!pgm_rxw_is_full (window)); - -/* window */ - pgm_free (window); -} - -/* add skb to receive window. window has fixed size and will not grow. - * PGM skbuff data/tail pointers must point to the PGM payload, and hence skb->len - * is allowed to be zero. - * - * if the skb sequence number indicates lost packets placeholders will be defined - * for each missing entry in the window. - * - * side effects: - * - * 1) sequence number is set in skb from PGM header value. - * 2) window may be updated with new skb. - * 3) placeholders may be created for detected lost packets. - * 4) parity skbs may be shuffled to accomodate original data. - * - * returns: - * PGM_RXW_INSERTED - packet filled a waiting placeholder, skb consumed. - * PGM_RXW_APPENDED - packet advanced window lead, skb consumed. - * PGM_RXW_MISSING - missing packets detected whilst window lead was adanced, skb consumed. - * PGM_RXW_DUPLICATE - re-transmission of previously seen packet. - * PGM_RXW_MALFORMED - corrupted or invalid packet. - * PGM_RXW_BOUNDS - packet out of window. - * - * it is an error to try to free the skb after adding to the window. - */ - -int -pgm_rxw_add ( - pgm_rxw_t* const restrict window, - struct pgm_sk_buff_t* const restrict skb, - const pgm_time_t now, - const pgm_time_t nak_rb_expiry /* calculated expiry time for this skb */ - ) -{ - pgm_rxw_state_t* const state = (pgm_rxw_state_t*)&skb->cb; - int status; - -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert (NULL != skb); - pgm_assert_cmpuint (nak_rb_expiry, >, 0); - pgm_assert_cmpuint (pgm_rxw_max_length (window), >, 0); - pgm_assert (pgm_skb_is_valid (skb)); - pgm_assert (((const pgm_list_t*)skb)->next == NULL); - pgm_assert (((const pgm_list_t*)skb)->prev == NULL); - pgm_assert (!_pgm_tsi_is_null (&skb->tsi)); - pgm_assert ((char*)skb->data > (char*)skb->head); - pgm_assert (sizeof(struct pgm_header) + sizeof(struct pgm_data) <= (size_t)((char*)skb->data - (char*)skb->head)); - pgm_assert (skb->len == ((char*)skb->tail - (char*)skb->data)); - - pgm_debug ("add (window:%p skb:%p nak_rb_expiry:%" PGM_TIME_FORMAT ")", - (const void*)window, (const void*)skb, nak_rb_expiry); - - skb->sequence = ntohl (skb->pgm_data->data_sqn); - -/* protocol sanity check: tsdu size */ - if (PGM_UNLIKELY(skb->len != ntohs (skb->pgm_header->pgm_tsdu_length))) - return PGM_RXW_MALFORMED; - -/* protocol sanity check: valid trail pointer wrt. sequence */ - if (PGM_UNLIKELY(skb->sequence - ntohl (skb->pgm_data->data_trail) >= ((UINT32_MAX/2)-1))) - return PGM_RXW_MALFORMED; - -/* verify fragment header for original data, parity packets include a - * parity fragment header - */ - if (!(skb->pgm_header->pgm_options & PGM_OPT_PARITY) && - skb->pgm_opt_fragment) - { -/* protocol sanity check: single fragment APDU */ - if (PGM_UNLIKELY(ntohl (skb->of_apdu_len) == skb->len)) - skb->pgm_opt_fragment = NULL; - -/* protocol sanity check: minimum APDU length */ - if (PGM_UNLIKELY(ntohl (skb->of_apdu_len) < skb->len)) - return PGM_RXW_MALFORMED; - -/* protocol sanity check: sequential ordering */ - if (PGM_UNLIKELY(pgm_uint32_gt (ntohl (skb->of_apdu_first_sqn), skb->sequence))) - return PGM_RXW_MALFORMED; - -/* protocol sanity check: maximum APDU length */ - if (PGM_UNLIKELY(ntohl (skb->of_apdu_len) > PGM_MAX_APDU)) - return PGM_RXW_MALFORMED; - } - -/* first packet of a session defines the window */ - if (PGM_UNLIKELY(!window->is_defined)) - _pgm_rxw_define (window, skb->sequence - 1); /* previous_lead needed for append to occur */ - else - _pgm_rxw_update_trail (window, ntohl (skb->pgm_data->data_trail)); - -/* bounds checking for parity data occurs at the transmission group sequence number */ - if (skb->pgm_header->pgm_options & PGM_OPT_PARITY) - { - if (pgm_uint32_lt (_pgm_rxw_tg_sqn (window, skb->sequence), _pgm_rxw_tg_sqn (window, window->commit_lead))) - return PGM_RXW_DUPLICATE; - - if (pgm_uint32_lt (_pgm_rxw_tg_sqn (window, skb->sequence), _pgm_rxw_tg_sqn (window, window->lead))) { - window->has_event = 1; - return _pgm_rxw_insert (window, skb); - } - - const struct pgm_sk_buff_t* const first_skb = _pgm_rxw_peek (window, _pgm_rxw_tg_sqn (window, skb->sequence)); - const pgm_rxw_state_t* const first_state = (pgm_rxw_state_t*)&first_skb->cb; - - if (_pgm_rxw_tg_sqn (window, skb->sequence) == _pgm_rxw_tg_sqn (window, window->lead)) { - window->has_event = 1; - if (NULL == first_state || first_state->is_contiguous) { - state->is_contiguous = 1; - return _pgm_rxw_append (window, skb, now); - } else - return _pgm_rxw_insert (window, skb); - } - - pgm_assert (NULL != first_state); - status = _pgm_rxw_add_placeholder_range (window, _pgm_rxw_tg_sqn (window, skb->sequence), now, nak_rb_expiry); - } - else - { - if (pgm_uint32_lt (skb->sequence, window->commit_lead)) { - if (pgm_uint32_gte (skb->sequence, window->trail)) - return PGM_RXW_DUPLICATE; - else - return PGM_RXW_BOUNDS; - } - - if (pgm_uint32_lte (skb->sequence, window->lead)) { - window->has_event = 1; - return _pgm_rxw_insert (window, skb); - } - - if (skb->sequence == pgm_rxw_next_lead (window)) { - window->has_event = 1; - if (_pgm_rxw_is_first_of_tg_sqn (window, skb->sequence)) - state->is_contiguous = 1; - return _pgm_rxw_append (window, skb, now); - } - - status = _pgm_rxw_add_placeholder_range (window, skb->sequence, now, nak_rb_expiry); - } - - if (PGM_RXW_APPENDED == status) { - status = _pgm_rxw_append (window, skb, now); - if (PGM_RXW_APPENDED == status) - status = PGM_RXW_MISSING; - } - return status; -} - -/* trail is the next packet to commit upstream, lead is the leading edge - * of the receive window with possible gaps inside, rxw_trail is the transmit - * window trail for retransmit requests. - */ - -/* define window by parameters of first data packet. - */ - -static -void -_pgm_rxw_define ( - pgm_rxw_t* const window, - const uint32_t lead - ) -{ -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert (pgm_rxw_is_empty (window)); - pgm_assert (_pgm_rxw_commit_is_empty (window)); - pgm_assert (_pgm_rxw_incoming_is_empty (window)); - pgm_assert (!window->is_defined); - - window->lead = lead; - window->commit_lead = window->rxw_trail = window->rxw_trail_init = window->trail = window->lead + 1; - window->is_constrained = window->is_defined = TRUE; - -/* post-conditions */ - pgm_assert (pgm_rxw_is_empty (window)); - pgm_assert (_pgm_rxw_commit_is_empty (window)); - pgm_assert (_pgm_rxw_incoming_is_empty (window)); - pgm_assert (window->is_defined); - pgm_assert (window->is_constrained); -} - -/* update window with latest transmitted parameters. - * - * returns count of placeholders added into window, used to start sending naks. - */ - -unsigned -pgm_rxw_update ( - pgm_rxw_t* const window, - const uint32_t txw_lead, - const uint32_t txw_trail, - const pgm_time_t now, - const pgm_time_t nak_rb_expiry /* packet expiration time */ - ) -{ -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert_cmpuint (nak_rb_expiry, >, 0); - - pgm_debug ("pgm_rxw_update (window:%p txw-lead:%" PRIu32 " txw-trail:%" PRIu32 " nak-rb-expiry:%" PGM_TIME_FORMAT ")", - (void*)window, txw_lead, txw_trail, nak_rb_expiry); - - if (PGM_UNLIKELY(!window->is_defined)) { - _pgm_rxw_define (window, txw_lead); - return 0; - } - - _pgm_rxw_update_trail (window, txw_trail); - return _pgm_rxw_update_lead (window, txw_lead, now, nak_rb_expiry); -} - -/* update trailing edge of receive window - */ - -static -void -_pgm_rxw_update_trail ( - pgm_rxw_t* const window, - const uint32_t txw_trail - ) -{ -/* pre-conditions */ - pgm_assert (NULL != window); - -/* advertised trail is less than the current value */ - if (PGM_UNLIKELY(pgm_uint32_lte (txw_trail, window->rxw_trail))) - return; - -/* protocol sanity check: advertised trail jumps too far ahead */ - if (PGM_UNLIKELY(txw_trail - window->rxw_trail > ((UINT32_MAX/2)-1))) - return; - -/* retransmissions requests are constrained on startup until the advertised trail advances - * beyond the first data sequence number. - */ - if (PGM_UNLIKELY(window->is_constrained)) - { - if (pgm_uint32_gt (txw_trail, window->rxw_trail_init)) - window->is_constrained = FALSE; - else - return; - } - - window->rxw_trail = txw_trail; - -/* new value doesn't affect window */ - if (PGM_UNLIKELY(pgm_uint32_lte (window->rxw_trail, window->trail))) - return; - -/* jump remaining sequence numbers if window is empty */ - if (pgm_rxw_is_empty (window)) - { - const uint32_t distance = (int32_t)(window->rxw_trail) - (int32_t)(window->trail); - window->commit_lead = window->trail += distance; - window->lead += distance; - -/* add loss to bitmap */ - if (distance > 32) window->bitmap = 0; - else window->bitmap <<= distance; - -/* update the Exponential Moving Average (EMA) data loss with long jump: - * s_t = α × (p₁ + (1 - α) × p₂ + (1 - α)² × p₃ + ⋯) - * omitting the weight by stopping after k terms, - * = α × ((1 - α)^^k + (1 - α)^^{k+1} +(1 - α)^^{k+1} + ⋯) - * = α × (1 - α)^^k × (1 + (1 - α) + (1 - α)² + ⋯) - * = (1 - α)^^k - */ - window->data_loss = pgm_fp16mul (window->data_loss, pgm_fp16pow (pgm_fp16 (1) - window->ack_c_p, distance)); - - window->cumulative_losses += distance; - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Data loss due to trailing edge update, fragment count %" PRIu32 "."),window->fragment_count); - pgm_assert (pgm_rxw_is_empty (window)); - pgm_assert (_pgm_rxw_commit_is_empty (window)); - pgm_assert (_pgm_rxw_incoming_is_empty (window)); - return; - } - -/* remove all buffers between commit lead and advertised rxw_trail */ - for (uint32_t sequence = window->commit_lead; - pgm_uint32_gt (window->rxw_trail, sequence) && pgm_uint32_gte (window->lead, sequence); - sequence++) - { - struct pgm_sk_buff_t* skb; - pgm_rxw_state_t* state; - - skb = _pgm_rxw_peek (window, sequence); - pgm_assert (NULL != skb); - state = (pgm_rxw_state_t*)&skb->cb; - - switch (state->pkt_state) { - case PGM_PKT_STATE_HAVE_DATA: - case PGM_PKT_STATE_HAVE_PARITY: - case PGM_PKT_STATE_LOST_DATA: - break; - - case PGM_PKT_STATE_ERROR: - pgm_assert_not_reached(); - - default: - pgm_rxw_lost (window, sequence); - break; - } - } - -/* post-conditions: only after flush */ -// pgm_assert (!pgm_rxw_is_full (window)); -} - -/* update FEC parameters - */ - -void -pgm_rxw_update_fec ( - pgm_rxw_t* const window, - const uint8_t rs_k - ) -{ -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert_cmpuint (rs_k, >, 1); - - pgm_debug ("pgm_rxw_update_fec (window:%p rs(k):%u)", - (void*)window, rs_k); - - if (window->is_fec_available) { - if (rs_k == window->rs.k) return; - pgm_rs_destroy (&window->rs); - } else - window->is_fec_available = 1; - pgm_rs_create (&window->rs, PGM_RS_DEFAULT_N, rs_k); - window->tg_sqn_shift = pgm_power2_log2 (rs_k); - window->tg_size = window->rs.k; -} - -/* add one placeholder to leading edge due to detected lost packet. - */ - -static -void -_pgm_rxw_add_placeholder ( - pgm_rxw_t* const window, - const pgm_time_t now, - const pgm_time_t nak_rb_expiry - ) -{ - struct pgm_sk_buff_t* skb; - -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert (!pgm_rxw_is_full (window)); - -/* advance lead */ - window->lead++; - -/* add loss to bitmap */ - window->bitmap <<= 1; - -/* update the Exponential Moving Average (EMA) data loss with loss: - * s_t = α × x_{t-1} + (1 - α) × s_{t-1} - * x_{t-1} = 1 - * ∴ s_t = α + (1 - α) × s_{t-1} - */ - window->data_loss = window->ack_c_p + pgm_fp16mul ((pgm_fp16 (1) - window->ack_c_p), window->data_loss); - - skb = pgm_alloc_skb (window->max_tpdu); - pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; - skb->tstamp = now; - skb->sequence = window->lead; - state->timer_expiry = nak_rb_expiry; - - if (!_pgm_rxw_is_first_of_tg_sqn (window, skb->sequence)) - { - struct pgm_sk_buff_t* first_skb = _pgm_rxw_peek (window, _pgm_rxw_tg_sqn (window, skb->sequence)); - if (first_skb) { - pgm_rxw_state_t* first_state = (pgm_rxw_state_t*)&first_skb->cb; - first_state->is_contiguous = 0; - } - } - -/* add skb to window */ - const uint_fast32_t index_ = skb->sequence % pgm_rxw_max_length (window); - window->pdata[index_] = skb; - - pgm_rxw_state (window, skb, PGM_PKT_STATE_BACK_OFF); - -/* post-conditions */ - pgm_assert_cmpuint (pgm_rxw_length (window), >, 0); - pgm_assert_cmpuint (pgm_rxw_length (window), <=, pgm_rxw_max_length (window)); - pgm_assert_cmpuint (_pgm_rxw_incoming_length (window), >, 0); -} - -/* add a range of placeholders to the window. - */ - -static -int -_pgm_rxw_add_placeholder_range ( - pgm_rxw_t* const window, - const uint32_t sequence, - const pgm_time_t now, - const pgm_time_t nak_rb_expiry - ) -{ -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert (pgm_uint32_gt (sequence, pgm_rxw_lead (window))); - -/* check bounds of commit window */ - const uint32_t new_commit_sqns = ( 1 + sequence ) - window->trail; - if ( !_pgm_rxw_commit_is_empty (window) && - (new_commit_sqns >= pgm_rxw_max_length (window)) ) - { - _pgm_rxw_update_lead (window, sequence, now, nak_rb_expiry); - return PGM_RXW_BOUNDS; /* effectively a slow consumer */ - } - - if (pgm_rxw_is_full (window)) { - pgm_assert (_pgm_rxw_commit_is_empty (window)); - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Receive window full on placeholder sequence.")); - _pgm_rxw_remove_trail (window); - } - -/* if packet is non-contiguous to current leading edge add place holders - * TODO: can be rather inefficient on packet loss looping through dropped sequence numbers - */ - while (pgm_rxw_next_lead (window) != sequence) - { - _pgm_rxw_add_placeholder (window, now, nak_rb_expiry); - if (pgm_rxw_is_full (window)) { - pgm_assert (_pgm_rxw_commit_is_empty (window)); - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Receive window full on placeholder sequence.")); - _pgm_rxw_remove_trail (window); - } - } - -/* post-conditions */ - pgm_assert (!pgm_rxw_is_full (window)); - - return PGM_RXW_APPENDED; -} - -/* update leading edge of receive window. - * - * returns number of place holders added. - */ - -static -unsigned -_pgm_rxw_update_lead ( - pgm_rxw_t* const window, - const uint32_t txw_lead, - const pgm_time_t now, - const pgm_time_t nak_rb_expiry - ) -{ -/* pre-conditions */ - pgm_assert (NULL != window); - -/* advertised lead is less than the current value */ - if (PGM_UNLIKELY(pgm_uint32_lte (txw_lead, window->lead))) - return 0; - - uint32_t lead; - -/* committed packets limit constrain the lead until they are released */ - if (!_pgm_rxw_commit_is_empty (window) && - (txw_lead - window->trail) >= pgm_rxw_max_length (window)) - { - lead = window->trail + pgm_rxw_max_length (window) - 1; - if (lead == window->lead) - return 0; - } - else - lead = txw_lead; - - unsigned lost = 0; - - while (window->lead != lead) - { -/* slow consumer or fast producer */ - if (pgm_rxw_is_full (window)) { - pgm_assert (_pgm_rxw_commit_is_empty (window)); - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Receive window full on window lead advancement.")); - _pgm_rxw_remove_trail (window); - } - _pgm_rxw_add_placeholder (window, now, nak_rb_expiry); - lost++; - } - - return lost; -} - -/* checks whether an APDU is unrecoverable due to lost TPDUs. - */ -static inline -bool -_pgm_rxw_is_apdu_lost ( - pgm_rxw_t* const restrict window, - struct pgm_sk_buff_t* const restrict skb - ) -{ - const pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; - -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert (NULL != skb); - -/* lost is lost */ - if (PGM_PKT_STATE_LOST_DATA == state->pkt_state) - return TRUE; - -/* by definition, a single-TPDU APDU is complete */ - if (!skb->pgm_opt_fragment) - return FALSE; - - const uint32_t apdu_first_sqn = ntohl (skb->of_apdu_first_sqn); - -/* by definition, first fragment indicates APDU is available */ - if (apdu_first_sqn == skb->sequence) - return FALSE; - - const struct pgm_sk_buff_t* const first_skb = _pgm_rxw_peek (window, apdu_first_sqn); -/* first fragment out-of-bounds */ - if (NULL == first_skb) - return TRUE; - - const pgm_rxw_state_t* first_state = (pgm_rxw_state_t*)&first_skb->cb; - if (PGM_PKT_STATE_LOST_DATA == first_state->pkt_state) - return TRUE; - - return FALSE; -} - -/* return the first missing packet sequence in the specified transmission - * group or NULL if not required. - */ - -static inline -struct pgm_sk_buff_t* -_pgm_rxw_find_missing ( - pgm_rxw_t* const window, - const uint32_t tg_sqn /* tg_sqn | pkt_sqn */ - ) -{ - struct pgm_sk_buff_t* skb; - pgm_rxw_state_t* state; - -/* pre-conditions */ - pgm_assert (NULL != window); - - for (uint32_t i = tg_sqn, j = 0; j < window->tg_size; i++, j++) - { - skb = _pgm_rxw_peek (window, i); - pgm_assert (NULL != skb); - state = (pgm_rxw_state_t*)&skb->cb; - switch (state->pkt_state) { - case PGM_PKT_STATE_BACK_OFF: - case PGM_PKT_STATE_WAIT_NCF: - case PGM_PKT_STATE_WAIT_DATA: - case PGM_PKT_STATE_LOST_DATA: - return skb; - - case PGM_PKT_STATE_HAVE_DATA: - case PGM_PKT_STATE_HAVE_PARITY: - break; - - default: pgm_assert_not_reached(); break; - } - } - - return NULL; -} - -/* returns TRUE if skb is a parity packet with packet length not - * matching the transmission group length without the variable-packet-length - * flag set. - */ - -static inline -bool -_pgm_rxw_is_invalid_var_pktlen ( - pgm_rxw_t* const restrict window, - const struct pgm_sk_buff_t* const restrict skb - ) -{ - const struct pgm_sk_buff_t* first_skb; - -/* pre-conditions */ - pgm_assert (NULL != window); - - if (!window->is_fec_available) - return FALSE; - - if (skb->pgm_header->pgm_options & PGM_OPT_VAR_PKTLEN) - return FALSE; - - const uint32_t tg_sqn = _pgm_rxw_tg_sqn (window, skb->sequence); - if (tg_sqn == skb->sequence) - return FALSE; - - first_skb = _pgm_rxw_peek (window, tg_sqn); - if (NULL == first_skb) - return TRUE; /* transmission group unrecoverable */ - - if (first_skb->len == skb->len) - return FALSE; - - return TRUE; -} - -static inline -bool -_pgm_rxw_has_payload_op ( - const struct pgm_sk_buff_t* const skb - ) -{ -/* pre-conditions */ - pgm_assert (NULL != skb); - pgm_assert (NULL != skb->pgm_header); - - return skb->pgm_opt_fragment || skb->pgm_header->pgm_options & PGM_OP_ENCODED; -} - -/* returns TRUE is skb options are invalid when compared to the transmission group - */ - -static inline -bool -_pgm_rxw_is_invalid_payload_op ( - pgm_rxw_t* const restrict window, - const struct pgm_sk_buff_t* const restrict skb - ) -{ - const struct pgm_sk_buff_t* first_skb; - -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert (NULL != skb); - - if (!window->is_fec_available) - return FALSE; - - const uint32_t tg_sqn = _pgm_rxw_tg_sqn (window, skb->sequence); - if (tg_sqn == skb->sequence) - return FALSE; - - first_skb = _pgm_rxw_peek (window, tg_sqn); - if (NULL == first_skb) - return TRUE; /* transmission group unrecoverable */ - - if (_pgm_rxw_has_payload_op (first_skb) == _pgm_rxw_has_payload_op (skb)) - return FALSE; - - return TRUE; -} - -/* insert skb into window range, discard if duplicate. window will have placeholder, - * parity, or data packet already matching sequence. - * - * returns: - * PGM_RXW_INSERTED - packet filled a waiting placeholder, skb consumed. - * PGM_RXW_DUPLICATE - re-transmission of previously seen packet. - * PGM_RXW_MALFORMED - corrupted or invalid packet. - * PGM_RXW_BOUNDS - packet out of window. - */ - -static -int -_pgm_rxw_insert ( - pgm_rxw_t* const restrict window, - struct pgm_sk_buff_t* const restrict new_skb - ) -{ - struct pgm_sk_buff_t* skb; - pgm_rxw_state_t* state; - -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert (NULL != new_skb); - pgm_assert (!_pgm_rxw_incoming_is_empty (window)); - - if (PGM_UNLIKELY(_pgm_rxw_is_invalid_var_pktlen (window, new_skb) || - _pgm_rxw_is_invalid_payload_op (window, new_skb))) - return PGM_RXW_MALFORMED; - - if (new_skb->pgm_header->pgm_options & PGM_OPT_PARITY) - { - skb = _pgm_rxw_find_missing (window, new_skb->sequence); - if (NULL == skb) - return PGM_RXW_DUPLICATE; - state = (pgm_rxw_state_t*)&skb->cb; - } - else - { - skb = _pgm_rxw_peek (window, new_skb->sequence); - pgm_assert (NULL != skb); - state = (pgm_rxw_state_t*)&skb->cb; - - if (state->pkt_state == PGM_PKT_STATE_HAVE_DATA) - return PGM_RXW_DUPLICATE; - } - -/* APDU fragments are already declared lost */ - if (new_skb->pgm_opt_fragment && - _pgm_rxw_is_apdu_lost (window, new_skb)) - { - pgm_rxw_lost (window, skb->sequence); - return PGM_RXW_BOUNDS; - } - -/* verify placeholder state */ - switch (state->pkt_state) { - case PGM_PKT_STATE_BACK_OFF: - case PGM_PKT_STATE_WAIT_NCF: - case PGM_PKT_STATE_WAIT_DATA: - case PGM_PKT_STATE_LOST_DATA: - break; - - case PGM_PKT_STATE_HAVE_PARITY: - _pgm_rxw_shuffle_parity (window, skb); - break; - - default: pgm_assert_not_reached(); break; - } - -/* statistics */ - const pgm_time_t fill_time = new_skb->tstamp - skb->tstamp; - PGM_HISTOGRAM_TIMES("Rx.RepairTime", fill_time); - PGM_HISTOGRAM_COUNTS("Rx.NakTransmits", state->nak_transmit_count); - PGM_HISTOGRAM_COUNTS("Rx.NcfRetries", state->ncf_retry_count); - PGM_HISTOGRAM_COUNTS("Rx.DataRetries", state->data_retry_count); - if (!window->max_fill_time) { - window->max_fill_time = window->min_fill_time = fill_time; - } - else - { - if (fill_time > window->max_fill_time) - window->max_fill_time = fill_time; - else if (fill_time < window->min_fill_time) - window->min_fill_time = fill_time; - - if (!window->max_nak_transmit_count) { - window->max_nak_transmit_count = window->min_nak_transmit_count = state->nak_transmit_count; - } else { - if (state->nak_transmit_count > window->max_nak_transmit_count) - window->max_nak_transmit_count = state->nak_transmit_count; - else if (state->nak_transmit_count < window->min_nak_transmit_count) - window->min_nak_transmit_count = state->nak_transmit_count; - } - } - -/* add packet to bitmap */ - const uint_fast32_t pos = window->lead - new_skb->sequence; - if (pos < 32) { - window->bitmap |= 1 << pos; - } - -/* update the Exponential Moving Average (EMA) data loss with repair data. - * s_t = α × x_{t-1} + (1 - α) × s_{t-1} - * x_{t-1} = 0 - * ∴ s_t = (1 - α) × s_{t-1} - */ - const uint_fast32_t s = pgm_fp16pow (pgm_fp16 (1) - window->ack_c_p, pos); - if (s > window->data_loss) window->data_loss = 0; - else window->data_loss -= s; - -/* replace place holder skb with incoming skb */ - memcpy (new_skb->cb, skb->cb, sizeof(skb->cb)); - pgm_rxw_state_t* rxw_state = (void*)new_skb->cb; - rxw_state->pkt_state = PGM_PKT_STATE_ERROR; - _pgm_rxw_unlink (window, skb); - pgm_free_skb (skb); - const uint_fast32_t index_ = new_skb->sequence % pgm_rxw_max_length (window); - window->pdata[index_] = new_skb; - if (new_skb->pgm_header->pgm_options & PGM_OPT_PARITY) - _pgm_rxw_state (window, new_skb, PGM_PKT_STATE_HAVE_PARITY); - else - _pgm_rxw_state (window, new_skb, PGM_PKT_STATE_HAVE_DATA); - window->size += new_skb->len; - - return PGM_RXW_INSERTED; -} - -/* shuffle parity packet at skb->sequence to any other needed spot. - */ - -static inline -void -_pgm_rxw_shuffle_parity ( - pgm_rxw_t* const restrict window, - struct pgm_sk_buff_t* const restrict skb - ) -{ - uint_fast32_t index_; - -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert (NULL != skb); - - struct pgm_sk_buff_t* restrict missing = _pgm_rxw_find_missing (window, skb->sequence); - if (NULL == missing) - return; - -/* replace place holder skb with parity skb */ - char cb[48]; - _pgm_rxw_unlink (window, missing); - memcpy (cb, skb->cb, sizeof(skb->cb)); - memcpy (skb->cb, missing->cb, sizeof(skb->cb)); - memcpy (missing->cb, cb, sizeof(skb->cb)); - index_ = skb->sequence % pgm_rxw_max_length (window); - window->pdata[index_] = skb; - index_ = missing->sequence % pgm_rxw_max_length (window); - window->pdata[index_] = missing; -} - -/* skb advances the window lead. - * - * returns: - * PGM_RXW_APPENDED - packet advanced window lead, skb consumed. - * PGM_RXW_MALFORMED - corrupted or invalid packet. - * PGM_RXW_BOUNDS - packet out of window. - */ - -static -int -_pgm_rxw_append ( - pgm_rxw_t* const restrict window, - struct pgm_sk_buff_t* const restrict skb, - const pgm_time_t now - ) -{ -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert (NULL != skb); - if (skb->pgm_header->pgm_options & PGM_OPT_PARITY) { - pgm_assert (_pgm_rxw_tg_sqn (window, skb->sequence) == _pgm_rxw_tg_sqn (window, pgm_rxw_lead (window))); - } else { - pgm_assert (skb->sequence == pgm_rxw_next_lead (window)); - } - - if (PGM_UNLIKELY(_pgm_rxw_is_invalid_var_pktlen (window, skb) || - _pgm_rxw_is_invalid_payload_op (window, skb))) - return PGM_RXW_MALFORMED; - - if (pgm_rxw_is_full (window)) { - if (_pgm_rxw_commit_is_empty (window)) { - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Receive window full on new data.")); - _pgm_rxw_remove_trail (window); - } else { - return PGM_RXW_BOUNDS; /* constrained by commit window */ - } - } - -/* advance leading edge */ - window->lead++; - -/* add packet to bitmap */ - window->bitmap = (window->bitmap << 1) | 1; - -/* update the Exponential Moving Average (EMA) data loss with data: - * s_t = α × x_{t-1} + (1 - α) × s_{t-1} - * x_{t-1} = 0 - * ∴ s_t = (1 - α) × s_{t-1} - */ - window->data_loss = pgm_fp16mul (window->data_loss, pgm_fp16 (1) - window->ack_c_p); - -/* APDU fragments are already declared lost */ - if (PGM_UNLIKELY(skb->pgm_opt_fragment && - _pgm_rxw_is_apdu_lost (window, skb))) - { - struct pgm_sk_buff_t* lost_skb = pgm_alloc_skb (window->max_tpdu); - lost_skb->tstamp = now; - lost_skb->sequence = skb->sequence; - -/* add lost-placeholder skb to window */ - const uint_fast32_t index_ = lost_skb->sequence % pgm_rxw_max_length (window); - window->pdata[index_] = lost_skb; - - _pgm_rxw_state (window, lost_skb, PGM_PKT_STATE_LOST_DATA); - return PGM_RXW_BOUNDS; - } - -/* add skb to window */ - if (skb->pgm_header->pgm_options & PGM_OPT_PARITY) - { - const uint_fast32_t index_ = skb->sequence % pgm_rxw_max_length (window); - window->pdata[index_] = skb; - _pgm_rxw_state (window, skb, PGM_PKT_STATE_HAVE_PARITY); - } - else - { - const uint_fast32_t index_ = skb->sequence % pgm_rxw_max_length (window); - window->pdata[index_] = skb; - _pgm_rxw_state (window, skb, PGM_PKT_STATE_HAVE_DATA); - } - -/* statistics */ - window->size += skb->len; - - return PGM_RXW_APPENDED; -} - -/* remove references to all commit packets not in the same transmission group - * as the commit-lead - */ - -void -pgm_rxw_remove_commit ( - pgm_rxw_t* const window - ) -{ -/* pre-conditions */ - pgm_assert (NULL != window); - - const uint32_t tg_sqn_of_commit_lead = _pgm_rxw_tg_sqn (window, window->commit_lead); - - while (!_pgm_rxw_commit_is_empty (window) && - tg_sqn_of_commit_lead != _pgm_rxw_tg_sqn (window, window->trail)) - { - _pgm_rxw_remove_trail (window); - } -} - -/* flush packets but instead of calling on_data append the contiguous data packets - * to the provided scatter/gather vector. - * - * when transmission groups are enabled, packets remain in the windows tagged committed - * until the transmission group has been completely committed. this allows the packet - * data to be used in parity calculations to recover the missing packets. - * - * returns -1 on nothing read, returns length of bytes read, 0 is a valid read length. - * - * PGM skbuffs will have an increased reference count and must be unreferenced by the - * calling application. - */ - -ssize_t -pgm_rxw_readv ( - pgm_rxw_t* const restrict window, - struct pgm_msgv_t** restrict pmsg, /* message array, updated as messages appended */ - const unsigned pmsglen /* number of items in pmsg */ - ) -{ - const struct pgm_msgv_t* msg_end; - struct pgm_sk_buff_t* skb; - pgm_rxw_state_t* state; - ssize_t bytes_read; - -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert (NULL != pmsg); - pgm_assert_cmpuint (pmsglen, >, 0); - - pgm_debug ("readv (window:%p pmsg:%p pmsglen:%u)", - (void*)window, (void*)pmsg, pmsglen); - - msg_end = *pmsg + pmsglen - 1; - - if (_pgm_rxw_incoming_is_empty (window)) - return -1; - - skb = _pgm_rxw_peek (window, window->commit_lead); - pgm_assert (NULL != skb); - - state = (pgm_rxw_state_t*)&skb->cb; - switch (state->pkt_state) { - case PGM_PKT_STATE_HAVE_DATA: - bytes_read = _pgm_rxw_incoming_read (window, pmsg, msg_end - *pmsg + 1); - break; - - case PGM_PKT_STATE_LOST_DATA: -/* do not purge in situ sequence */ - if (_pgm_rxw_commit_is_empty (window)) { - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Removing lost trail from window")); - _pgm_rxw_remove_trail (window); - } else { - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Locking trail at commit window")); - } -/* fall through */ - case PGM_PKT_STATE_BACK_OFF: - case PGM_PKT_STATE_WAIT_NCF: - case PGM_PKT_STATE_WAIT_DATA: - case PGM_PKT_STATE_HAVE_PARITY: - bytes_read = -1; - break; - - case PGM_PKT_STATE_COMMIT_DATA: - case PGM_PKT_STATE_ERROR: - default: - pgm_assert_not_reached(); - break; - } - - return bytes_read; -} - -/* remove lost sequences from the trailing edge of the window. lost sequence - * at lead of commit window invalidates all parity-data packets as any - * transmission group is now unrecoverable. - * - * returns number of sequences purged. - */ - -static -unsigned -_pgm_rxw_remove_trail ( - pgm_rxw_t* const window - ) -{ - struct pgm_sk_buff_t* skb; - -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert (!pgm_rxw_is_empty (window)); - - skb = _pgm_rxw_peek (window, window->trail); - pgm_assert (NULL != skb); - _pgm_rxw_unlink (window, skb); - window->size -= skb->len; -/* remove reference to skb */ - if (PGM_UNLIKELY(pgm_mem_gc_friendly)) { - const uint_fast32_t index_ = skb->sequence % pgm_rxw_max_length (window); - window->pdata[index_] = NULL; - } - pgm_free_skb (skb); - if (window->trail++ == window->commit_lead) { -/* data-loss */ - window->commit_lead++; - window->cumulative_losses++; - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Data loss due to pulled trailing edge, fragment count %" PRIu32 "."),window->fragment_count); - return 1; - } - return 0; -} - -unsigned -pgm_rxw_remove_trail ( - pgm_rxw_t* const window - ) -{ - pgm_debug ("remove_trail (window:%p)", (const void*)window); - return _pgm_rxw_remove_trail (window); -} - -/* read contiguous APDU-grouped sequences from the incoming window. - * - * side effects: - * - * 1) increments statics for window messages and bytes read. - * - * returns count of bytes read. - */ - -static inline -ssize_t -_pgm_rxw_incoming_read ( - pgm_rxw_t* const restrict window, - struct pgm_msgv_t** restrict pmsg, /* message array, updated as messages appended */ - unsigned pmsglen /* number of items in pmsg */ - ) -{ - const struct pgm_msgv_t* msg_end; - struct pgm_sk_buff_t* skb; - -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert (NULL != pmsg); - pgm_assert_cmpuint (pmsglen, >, 0); - pgm_assert (!_pgm_rxw_incoming_is_empty (window)); - - pgm_debug ("_pgm_rxw_incoming_read (window:%p pmsg:%p pmsglen:%u)", - (void*)window, (void*)pmsg, pmsglen); - - msg_end = *pmsg + pmsglen - 1; - ssize_t bytes_read = 0; - size_t data_read = 0; - - do { - skb = _pgm_rxw_peek (window, window->commit_lead); - pgm_assert (NULL != skb); - if (_pgm_rxw_is_apdu_complete (window, - skb->pgm_opt_fragment ? ntohl (skb->of_apdu_first_sqn) : skb->sequence)) - { - bytes_read += _pgm_rxw_incoming_read_apdu (window, pmsg); - data_read ++; - } - else break; - } while (*pmsg <= msg_end && !_pgm_rxw_incoming_is_empty (window)); - - window->bytes_delivered += bytes_read; - window->msgs_delivered += data_read; - return data_read > 0 ? bytes_read : -1; -} - -/* returns TRUE if transmission group is lost. - * - * checking is lightly limited to bounds. - */ - -static inline -bool -_pgm_rxw_is_tg_sqn_lost ( - pgm_rxw_t* const window, - const uint32_t tg_sqn /* transmission group sequence */ - ) -{ -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert_cmpuint (_pgm_rxw_pkt_sqn (window, tg_sqn), ==, 0); - - if (pgm_rxw_is_empty (window)) - return TRUE; - - if (pgm_uint32_lt (tg_sqn, window->trail)) - return TRUE; - - return FALSE; -} - -/* reconstruct missing sequences in a transmission group using embedded parity data. - */ - -static -void -_pgm_rxw_reconstruct ( - pgm_rxw_t* const window, - const uint32_t tg_sqn /* transmission group sequence */ - ) -{ - struct pgm_sk_buff_t* skb; - pgm_rxw_state_t* state; - -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert (1 == window->is_fec_available); - pgm_assert_cmpuint (_pgm_rxw_pkt_sqn (window, tg_sqn), ==, 0); - - skb = _pgm_rxw_peek (window, tg_sqn); - pgm_assert (NULL != skb); - - const bool is_var_pktlen = skb->pgm_header->pgm_options & PGM_OPT_VAR_PKTLEN; - const bool is_op_encoded = skb->pgm_header->pgm_options & PGM_OPT_PRESENT; - const uint16_t parity_length = ntohs (skb->pgm_header->pgm_tsdu_length); - struct pgm_sk_buff_t* tg_skbs[ window->rs.n ]; - pgm_gf8_t* tg_data[ window->rs.n ]; - pgm_gf8_t* tg_opts[ window->rs.n ]; - uint8_t offsets[ window->rs.k ]; - uint8_t rs_h = 0; - - for (uint32_t i = tg_sqn, j = 0; i != (tg_sqn + window->rs.k); i++, j++) - { - skb = _pgm_rxw_peek (window, i); - pgm_assert (NULL != skb); - state = (pgm_rxw_state_t*)&skb->cb; - switch (state->pkt_state) { - case PGM_PKT_STATE_HAVE_DATA: - tg_skbs[ j ] = skb; - tg_data[ j ] = skb->data; - tg_opts[ j ] = (pgm_gf8_t*)skb->pgm_opt_fragment; - offsets[ j ] = j; - break; - - case PGM_PKT_STATE_HAVE_PARITY: - tg_skbs[ window->rs.k + rs_h ] = skb; - tg_data[ window->rs.k + rs_h ] = skb->data; - tg_opts[ window->rs.k + rs_h ] = (pgm_gf8_t*)skb->pgm_opt_fragment; - offsets[ j ] = window->rs.k + rs_h; - ++rs_h; -/* fall through and alloc new skb for reconstructed data */ - case PGM_PKT_STATE_BACK_OFF: - case PGM_PKT_STATE_WAIT_NCF: - case PGM_PKT_STATE_WAIT_DATA: - case PGM_PKT_STATE_LOST_DATA: - skb = pgm_alloc_skb (window->max_tpdu); - pgm_skb_reserve (skb, sizeof(struct pgm_header) + sizeof(struct pgm_data)); - skb->pgm_header = skb->head; - skb->pgm_data = (void*)( skb->pgm_header + 1 ); - if (is_op_encoded) { - const uint16_t opt_total_length = sizeof(struct pgm_opt_length) + - sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_fragment); - pgm_skb_reserve (skb, opt_total_length); - skb->pgm_opt_fragment = (void*)( skb->pgm_data + 1 ); - pgm_skb_put (skb, parity_length); - memset (skb->pgm_opt_fragment, 0, opt_total_length + parity_length); - } else { - pgm_skb_put (skb, parity_length); - memset (skb->data, 0, parity_length); - } - tg_skbs[ j ] = skb; - tg_data[ j ] = skb->data; - tg_opts[ j ] = (void*)skb->pgm_opt_fragment; - break; - - default: pgm_assert_not_reached(); break; - } - - if (!skb->zero_padded) { - memset (skb->tail, 0, parity_length - skb->len); - skb->zero_padded = 1; - } - - } - -/* reconstruct payload */ - pgm_rs_decode_parity_appended (&window->rs, - tg_data, - offsets, - parity_length); - -/* reconstruct opt_fragment option */ - if (is_op_encoded) - pgm_rs_decode_parity_appended (&window->rs, - tg_opts, - offsets, - sizeof(struct pgm_opt_fragment)); - -/* swap parity skbs with reconstructed skbs */ - for (uint_fast8_t i = 0; i < window->rs.k; i++) - { - if (offsets[i] < window->rs.k) - continue; - - struct pgm_sk_buff_t* repair_skb = tg_skbs[i]; - - if (is_var_pktlen) - { - const uint16_t pktlen = *(uint16_t*)( (char*)repair_skb->tail - sizeof(uint16_t)); - if (pktlen > parity_length) { - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Invalid encoded variable packet length in reconstructed packet, dropping entire transmission group.")); - pgm_free_skb (repair_skb); - for (uint_fast8_t j = i; j < window->rs.k; j++) - { - if (offsets[j] < window->rs.k) - continue; - pgm_rxw_lost (window, tg_skbs[offsets[j]]->sequence); - } - break; - } - const uint16_t padding = parity_length - pktlen; - repair_skb->len -= padding; - repair_skb->tail = (char*)repair_skb->tail - padding; - } - -#ifdef PGM_DISABLE_ASSERT - _pgm_rxw_insert (window, repair_skb); -#else - pgm_assert_cmpint (_pgm_rxw_insert (window, repair_skb), ==, PGM_RXW_INSERTED); -#endif - } -} - -/* check every TPDU in an APDU and verify that the data has arrived - * and is available to commit to the application. - * - * if APDU sits in a transmission group that can be reconstructed use parity - * data then the entire group will be decoded and any missing data packets - * replaced by the recovery calculation. - * - * packets with single fragment fragment headers must be normalised as regular - * packets before calling. - * - * APDUs exceeding PGM_MAX_FRAGMENTS or PGM_MAX_APDU length will be discarded. - * - * returns FALSE if APDU is incomplete or longer than max_len sequences. - */ - -static -bool -_pgm_rxw_is_apdu_complete ( - pgm_rxw_t* const window, - const uint32_t first_sequence - ) -{ - struct pgm_sk_buff_t* skb; - pgm_rxw_state_t* state; - -/* pre-conditions */ - pgm_assert (NULL != window); - - pgm_debug ("_pgm_rxw_is_apdu_complete (window:%p first-sequence:%" PRIu32 ")", - (const void*)window, first_sequence); - - skb = _pgm_rxw_peek (window, first_sequence); - if (PGM_UNLIKELY(NULL == skb)) { - return FALSE; - } - - const size_t apdu_size = skb->pgm_opt_fragment ? ntohl (skb->of_apdu_len) : skb->len; - const uint32_t tg_sqn = _pgm_rxw_tg_sqn (window, first_sequence); - uint32_t sequence = first_sequence; - unsigned contiguous_tpdus = 0; - size_t contiguous_size = 0; - bool check_parity = FALSE; - - pgm_assert_cmpuint (apdu_size, >=, skb->len); - -/* protocol sanity check: maximum length */ - if (PGM_UNLIKELY(apdu_size > PGM_MAX_APDU)) { - pgm_rxw_lost (window, first_sequence); - return FALSE; - } - - do { - state = (pgm_rxw_state_t*)&skb->cb; - - if (!check_parity && - PGM_PKT_STATE_HAVE_DATA != state->pkt_state) - { - if (window->is_fec_available && - !_pgm_rxw_is_tg_sqn_lost (window, tg_sqn) ) - { - check_parity = TRUE; -/* pre-seed committed sequence count */ - if (pgm_uint32_lte (tg_sqn, window->commit_lead)) - contiguous_tpdus += window->commit_lead - tg_sqn; - } - else - return FALSE; - } - - if (check_parity) - { - if (PGM_PKT_STATE_HAVE_DATA == state->pkt_state || - PGM_PKT_STATE_HAVE_PARITY == state->pkt_state) - ++contiguous_tpdus; - -/* have sufficient been received for reconstruction */ - if (contiguous_tpdus >= window->tg_size) { - _pgm_rxw_reconstruct (window, tg_sqn); - return _pgm_rxw_is_apdu_complete (window, first_sequence); - } - } - else - { -/* single packet APDU, already complete */ - if (PGM_PKT_STATE_HAVE_DATA == state->pkt_state && - !skb->pgm_opt_fragment) - return TRUE; - -/* protocol sanity check: matching first sequence reference */ - if (PGM_UNLIKELY(ntohl (skb->of_apdu_first_sqn) != first_sequence)) { - pgm_rxw_lost (window, first_sequence); - return FALSE; - } - -/* protocol sanity check: matching apdu length */ - if (PGM_UNLIKELY(ntohl (skb->of_apdu_len) != apdu_size)) { - pgm_rxw_lost (window, first_sequence); - return FALSE; - } - -/* protocol sanity check: maximum number of fragments per apdu */ - if (PGM_UNLIKELY(++contiguous_tpdus > PGM_MAX_FRAGMENTS)) { - pgm_rxw_lost (window, first_sequence); - return FALSE; - } - - contiguous_size += skb->len; - if (apdu_size == contiguous_size) - return TRUE; - else if (PGM_UNLIKELY(apdu_size < contiguous_size)) { - pgm_rxw_lost (window, first_sequence); - return FALSE; - } - } - - skb = _pgm_rxw_peek (window, ++sequence); - } while (skb); - -/* pending */ - return FALSE; -} - -/* read one APDU consisting of one or more TPDUs. target array is guaranteed - * to be big enough to store complete APDU. - */ - -static inline -ssize_t -_pgm_rxw_incoming_read_apdu ( - pgm_rxw_t* const restrict window, - struct pgm_msgv_t** restrict pmsg /* message array, updated as messages appended */ - ) -{ - struct pgm_sk_buff_t *skb; - -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert (NULL != pmsg); - - pgm_debug ("_pgm_rxw_incoming_read_apdu (window:%p pmsg:%p)", - (const void*)window, (const void*)pmsg); - - skb = _pgm_rxw_peek (window, window->commit_lead); - size_t contiguous_len = 0; - const size_t apdu_len = skb->pgm_opt_fragment ? ntohl (skb->of_apdu_len) : skb->len; - unsigned i = 0; - pgm_assert_cmpuint (apdu_len, >=, skb->len); - (*pmsg)->msgv_len = 0; - do { - _pgm_rxw_state (window, skb, PGM_PKT_STATE_COMMIT_DATA); - (*pmsg)->msgv_skb[i++] = skb; - (*pmsg)->msgv_len++; - contiguous_len += skb->len; - window->commit_lead++; - if (apdu_len == contiguous_len) - break; - skb = _pgm_rxw_peek (window, window->commit_lead); - } while (apdu_len > contiguous_len); - - (*pmsg)++; - -/* post-conditions */ - pgm_assert (!_pgm_rxw_commit_is_empty (window)); - -return contiguous_len; -} - -/* returns transmission group sequence (TG_SQN) from sequence (SQN). - */ - -static inline -uint32_t -_pgm_rxw_tg_sqn ( - pgm_rxw_t* const window, - const uint32_t sequence - ) -{ -/* pre-conditions */ - pgm_assert (NULL != window); - - const uint32_t tg_sqn_mask = 0xffffffff << window->tg_sqn_shift; - return sequence & tg_sqn_mask; -} - -/* returns packet number (PKT_SQN) from sequence (SQN). - */ - -static inline -uint32_t -_pgm_rxw_pkt_sqn ( - pgm_rxw_t* const window, - const uint32_t sequence - ) -{ -/* pre-conditions */ - pgm_assert (NULL != window); - - const uint32_t tg_sqn_mask = 0xffffffff << window->tg_sqn_shift; - return sequence & ~tg_sqn_mask; -} - -/* returns TRUE when the sequence is the first of a transmission group. - */ - -static inline -bool -_pgm_rxw_is_first_of_tg_sqn ( - pgm_rxw_t* const window, - const uint32_t sequence - ) -{ -/* pre-conditions */ - pgm_assert (NULL != window); - - return _pgm_rxw_pkt_sqn (window, sequence) == 0; -} - -/* returns TRUE when the sequence is the last of a transmission group - */ - -static inline -bool -_pgm_rxw_is_last_of_tg_sqn ( - pgm_rxw_t* const window, - const uint32_t sequence - ) -{ -/* pre-conditions */ - pgm_assert (NULL != window); - - return _pgm_rxw_pkt_sqn (window, sequence) == window->tg_size - 1; -} - -/* set PGM skbuff to new FSM state. - */ - -static -void -_pgm_rxw_state ( - pgm_rxw_t* const restrict window, - struct pgm_sk_buff_t* const restrict skb, - const int new_pkt_state - ) -{ - pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; - -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert (NULL != skb); - -/* remove current state */ - if (PGM_PKT_STATE_ERROR != state->pkt_state) - _pgm_rxw_unlink (window, skb); - - switch (new_pkt_state) { - case PGM_PKT_STATE_BACK_OFF: - pgm_queue_push_head_link (&window->nak_backoff_queue, (pgm_list_t*)skb); - break; - - case PGM_PKT_STATE_WAIT_NCF: - pgm_queue_push_head_link (&window->wait_ncf_queue, (pgm_list_t*)skb); - break; - - case PGM_PKT_STATE_WAIT_DATA: - pgm_queue_push_head_link (&window->wait_data_queue, (pgm_list_t*)skb); - break; - - case PGM_PKT_STATE_HAVE_DATA: - window->fragment_count++; - pgm_assert_cmpuint (window->fragment_count, <=, pgm_rxw_length (window)); - break; - - case PGM_PKT_STATE_HAVE_PARITY: - window->parity_count++; - pgm_assert_cmpuint (window->parity_count, <=, pgm_rxw_length (window)); - break; - - case PGM_PKT_STATE_COMMIT_DATA: - window->committed_count++; - pgm_assert_cmpuint (window->committed_count, <=, pgm_rxw_length (window)); - break; - - case PGM_PKT_STATE_LOST_DATA: - window->lost_count++; - window->cumulative_losses++; - window->has_event = 1; - pgm_assert_cmpuint (window->lost_count, <=, pgm_rxw_length (window)); - break; - - case PGM_PKT_STATE_ERROR: - break; - - default: pgm_assert_not_reached(); break; - } - - state->pkt_state = new_pkt_state; -} - -void -pgm_rxw_state ( - pgm_rxw_t* const restrict window, - struct pgm_sk_buff_t* const restrict skb, - const int new_pkt_state - ) -{ - pgm_debug ("state (window:%p skb:%p new_pkt_state:%s)", - (const void*)window, (const void*)skb, pgm_pkt_state_string (new_pkt_state)); - _pgm_rxw_state (window, skb, new_pkt_state); -} - -/* remove current state from sequence. - */ - -static -void -_pgm_rxw_unlink ( - pgm_rxw_t* const restrict window, - struct pgm_sk_buff_t* const restrict skb - ) -{ - pgm_queue_t* queue; - -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert (NULL != skb); - - pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; - - switch (state->pkt_state) { - case PGM_PKT_STATE_BACK_OFF: - pgm_assert (!pgm_queue_is_empty (&window->nak_backoff_queue)); - queue = &window->nak_backoff_queue; - goto unlink_queue; - - case PGM_PKT_STATE_WAIT_NCF: - pgm_assert (!pgm_queue_is_empty (&window->wait_ncf_queue)); - queue = &window->wait_ncf_queue; - goto unlink_queue; - - case PGM_PKT_STATE_WAIT_DATA: - pgm_assert (!pgm_queue_is_empty (&window->wait_data_queue)); - queue = &window->wait_data_queue; -unlink_queue: - pgm_queue_unlink (queue, (pgm_list_t*)skb); - break; - - case PGM_PKT_STATE_HAVE_DATA: - pgm_assert_cmpuint (window->fragment_count, >, 0); - window->fragment_count--; - break; - - case PGM_PKT_STATE_HAVE_PARITY: - pgm_assert_cmpuint (window->parity_count, >, 0); - window->parity_count--; - break; - - case PGM_PKT_STATE_COMMIT_DATA: - pgm_assert_cmpuint (window->committed_count, >, 0); - window->committed_count--; - break; - - case PGM_PKT_STATE_LOST_DATA: - pgm_assert_cmpuint (window->lost_count, >, 0); - window->lost_count--; - break; - - case PGM_PKT_STATE_ERROR: - break; - - default: pgm_assert_not_reached(); break; - } - - state->pkt_state = PGM_PKT_STATE_ERROR; - pgm_assert (((pgm_list_t*)skb)->next == NULL); - pgm_assert (((pgm_list_t*)skb)->prev == NULL); -} - -/* returns the pointer at the given index of the window. - */ - -struct pgm_sk_buff_t* -pgm_rxw_peek ( - pgm_rxw_t* const window, - const uint32_t sequence - ) -{ - pgm_debug ("peek (window:%p sequence:%" PRIu32 ")", (void*)window, sequence); - return _pgm_rxw_peek (window, sequence); -} - -/* mark an existing sequence lost due to failed recovery. - */ - -void -pgm_rxw_lost ( - pgm_rxw_t* const window, - const uint32_t sequence - ) -{ - struct pgm_sk_buff_t* skb; - pgm_rxw_state_t* state; - -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert (!pgm_rxw_is_empty (window)); - - pgm_debug ("lost (window:%p sequence:%" PRIu32 ")", - (const void*)window, sequence); - - skb = _pgm_rxw_peek (window, sequence); - pgm_assert (NULL != skb); - - state = (pgm_rxw_state_t*)&skb->cb; - - if (PGM_UNLIKELY(!(state->pkt_state == PGM_PKT_STATE_BACK_OFF || - state->pkt_state == PGM_PKT_STATE_WAIT_NCF || - state->pkt_state == PGM_PKT_STATE_WAIT_DATA || - state->pkt_state == PGM_PKT_STATE_HAVE_DATA || /* fragments */ - state->pkt_state == PGM_PKT_STATE_HAVE_PARITY))) - { - pgm_fatal (_("Unexpected state %s(%u)"), pgm_pkt_state_string (state->pkt_state), state->pkt_state); - pgm_assert_not_reached(); - } - - _pgm_rxw_state (window, skb, PGM_PKT_STATE_LOST_DATA); -} - -/* received a uni/multicast ncf, search for a matching nak & tag or extend window if - * beyond lead - * - * returns: - * PGM_RXW_BOUNDS - sequence is outside of window, or window is undefined. - * PGM_RXW_UPDATED - receiver state updated, waiting for data. - * PGM_RXW_DUPLICATE - data already exists at sequence. - * PGM_RXW_APPENDED - lead is extended with state set waiting for data. - */ - -int -pgm_rxw_confirm ( - pgm_rxw_t* const window, - const uint32_t sequence, - const pgm_time_t now, - const pgm_time_t nak_rdata_expiry, /* pre-calculated expiry times */ - const pgm_time_t nak_rb_expiry - ) -{ -/* pre-conditions */ - pgm_assert (NULL != window); - - pgm_debug ("confirm (window:%p sequence:%" PRIu32 " nak_rdata_expiry:%" PGM_TIME_FORMAT " nak_rb_expiry:%" PGM_TIME_FORMAT ")", - (void*)window, sequence, nak_rdata_expiry, nak_rb_expiry); - -/* NCFs do not define the transmit window */ - if (PGM_UNLIKELY(!window->is_defined)) - return PGM_RXW_BOUNDS; - -/* sequence already committed */ - if (pgm_uint32_lt (sequence, window->commit_lead)) { - if (pgm_uint32_gte (sequence, window->trail)) - return PGM_RXW_DUPLICATE; - else - return PGM_RXW_BOUNDS; - } - - if (pgm_uint32_lte (sequence, window->lead)) - return _pgm_rxw_recovery_update (window, sequence, nak_rdata_expiry); - - if (sequence == window->lead) - return _pgm_rxw_recovery_append (window, now, nak_rdata_expiry); - else { - _pgm_rxw_add_placeholder_range (window, sequence, now, nak_rb_expiry); - return _pgm_rxw_recovery_append (window, now, nak_rdata_expiry); - } -} - -/* update an incoming sequence with state transition to WAIT-DATA. - * - * returns: - * PGM_RXW_UPDATED - receiver state updated, waiting for data. - * PGM_RXW_DUPLICATE - data already exists at sequence. - */ - -static inline -int -_pgm_rxw_recovery_update ( - pgm_rxw_t* const window, - const uint32_t sequence, - const pgm_time_t nak_rdata_expiry /* pre-calculated expiry times */ - ) -{ -/* pre-conditions */ - pgm_assert (NULL != window); - -/* fetch skb from window and bump expiration times */ - struct pgm_sk_buff_t* skb = _pgm_rxw_peek (window, sequence); - pgm_assert (NULL != skb); - pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; - switch (state->pkt_state) { - case PGM_PKT_STATE_BACK_OFF: - case PGM_PKT_STATE_WAIT_NCF: - pgm_rxw_state (window, skb, PGM_PKT_STATE_WAIT_DATA); - -/* fall through */ - case PGM_PKT_STATE_WAIT_DATA: - state->timer_expiry = nak_rdata_expiry; - return PGM_RXW_UPDATED; - - case PGM_PKT_STATE_HAVE_DATA: - case PGM_PKT_STATE_HAVE_PARITY: - case PGM_PKT_STATE_COMMIT_DATA: - case PGM_PKT_STATE_LOST_DATA: - break; - - default: pgm_assert_not_reached(); break; - } - - return PGM_RXW_DUPLICATE; -} - -/* append an skb to the incoming window with WAIT-DATA state. - * - * returns: - * PGM_RXW_APPENDED - lead is extended with state set waiting for data. - * PGM_RXW_BOUNDS - constrained by commit window - */ - -static inline -int -_pgm_rxw_recovery_append ( - pgm_rxw_t* const window, - const pgm_time_t now, - const pgm_time_t nak_rdata_expiry /* pre-calculated expiry times */ - ) -{ - struct pgm_sk_buff_t* skb; - -/* pre-conditions */ - pgm_assert (NULL != window); - - if (pgm_rxw_is_full (window)) { - if (_pgm_rxw_commit_is_empty (window)) { - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Receive window full on confirmed sequence.")); - _pgm_rxw_remove_trail (window); - } else { - return PGM_RXW_BOUNDS; /* constrained by commit window */ - } - } - -/* advance leading edge */ - window->lead++; - -/* add loss to bitmap */ - window->bitmap <<= 1; - -/* update the Exponential Moving Average (EMA) data loss with loss: - * s_t = α × x_{t-1} + (1 - α) × s_{t-1} - * x_{t-1} = 1 - * ∴ s_t = α + (1 - α) × s_{t-1} - */ - window->data_loss = window->ack_c_p + pgm_fp16mul (pgm_fp16 (1) - window->ack_c_p, window->data_loss); - - skb = pgm_alloc_skb (window->max_tpdu); - pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; - skb->tstamp = now; - skb->sequence = window->lead; - state->timer_expiry = nak_rdata_expiry; - - const uint_fast32_t index_ = pgm_rxw_lead (window) % pgm_rxw_max_length (window); - window->pdata[index_] = skb; - _pgm_rxw_state (window, skb, PGM_PKT_STATE_WAIT_DATA); - - return PGM_RXW_APPENDED; -} - -/* dumps window state to stdout - */ - -void -pgm_rxw_dump ( - const pgm_rxw_t* const window - ) -{ - pgm_info ("window = {" - "tsi = {gsi = {identifier = %i.%i.%i.%i.%i.%i}, sport = %" PRIu16 "}, " - "nak_backoff_queue = {head = %p, tail = %p, length = %u}, " - "wait_ncf_queue = {head = %p, tail = %p, length = %u}, " - "wait_data_queue = {head = %p, tail = %p, length = %u}, " - "lost_count = %" PRIu32 ", " - "fragment_count = %" PRIu32 ", " - "parity_count = %" PRIu32 ", " - "committed_count = %" PRIu32 ", " - "max_tpdu = %" PRIu16 ", " - "tg_size = %" PRIu32 ", " - "tg_sqn_shift = %u, " - "lead = %" PRIu32 ", " - "trail = %" PRIu32 ", " - "rxw_trail = %" PRIu32 ", " - "rxw_trail_init = %" PRIu32 ", " - "commit_lead = %" PRIu32 ", " - "is_constrained = %u, " - "is_defined = %u, " - "has_event = %u, " - "is_fec_available = %u, " - "min_fill_time = %" PRIu32 ", " - "max_fill_time = %" PRIu32 ", " - "min_nak_transmit_count = %" PRIu32 ", " - "max_nak_transmit_count = %" PRIu32 ", " - "cumulative_losses = %" PRIu32 ", " - "bytes_delivered = %" PRIu32 ", " - "msgs_delivered = %" PRIu32 ", " - "size = %zu, " - "alloc = %" PRIu32 ", " - "pdata = []" - "}", - window->tsi->gsi.identifier[0], - window->tsi->gsi.identifier[1], - window->tsi->gsi.identifier[2], - window->tsi->gsi.identifier[3], - window->tsi->gsi.identifier[4], - window->tsi->gsi.identifier[5], - ntohs (window->tsi->sport), - (void*)window->nak_backoff_queue.head, - (void*)window->nak_backoff_queue.tail, - window->nak_backoff_queue.length, - (void*)window->wait_ncf_queue.head, - (void*)window->wait_ncf_queue.tail, - window->wait_ncf_queue.length, - (void*)window->wait_data_queue.head, - (void*)window->wait_data_queue.tail, - window->wait_data_queue.length, - window->lost_count, - window->fragment_count, - window->parity_count, - window->committed_count, - window->max_tpdu, - window->tg_size, - window->tg_sqn_shift, - window->lead, - window->trail, - window->rxw_trail, - window->rxw_trail_init, - window->commit_lead, - window->is_constrained, - window->is_defined, - window->has_event, - window->is_fec_available, - window->min_fill_time, - window->max_fill_time, - window->min_nak_transmit_count, - window->max_nak_transmit_count, - window->cumulative_losses, - window->bytes_delivered, - window->msgs_delivered, - window->size, - window->alloc - ); -} - -/* state string helper - */ - -const char* -pgm_pkt_state_string ( - const int pkt_state - ) -{ - const char* c; - - switch (pkt_state) { - case PGM_PKT_STATE_BACK_OFF: c = "PGM_PKT_STATE_BACK_OFF"; break; - case PGM_PKT_STATE_WAIT_NCF: c = "PGM_PKT_STATE_WAIT_NCF"; break; - case PGM_PKT_STATE_WAIT_DATA: c = "PGM_PKT_STATE_WAIT_DATA"; break; - case PGM_PKT_STATE_HAVE_DATA: c = "PGM_PKT_STATE_HAVE_DATA"; break; - case PGM_PKT_STATE_HAVE_PARITY: c = "PGM_PKT_STATE_HAVE_PARITY"; break; - case PGM_PKT_STATE_COMMIT_DATA: c = "PGM_PKT_STATE_COMMIT_DATA"; break; - case PGM_PKT_STATE_LOST_DATA: c = "PGM_PKT_STATE_LOST_DATA"; break; - case PGM_PKT_STATE_ERROR: c = "PGM_PKT_STATE_ERROR"; break; - default: c = "(unknown)"; break; - } - - return c; -} - -const char* -pgm_rxw_returns_string ( - const int rxw_returns - ) -{ - const char* c; - - switch (rxw_returns) { - case PGM_RXW_OK: c = "PGM_RXW_OK"; break; - case PGM_RXW_INSERTED: c = "PGM_RXW_INSERTED"; break; - case PGM_RXW_APPENDED: c = "PGM_RXW_APPENDED"; break; - case PGM_RXW_UPDATED: c = "PGM_RXW_UPDATED"; break; - case PGM_RXW_MISSING: c = "PGM_RXW_MISSING"; break; - case PGM_RXW_DUPLICATE: c = "PGM_RXW_DUPLICATE"; break; - case PGM_RXW_MALFORMED: c = "PGM_RXW_MALFORMED"; break; - case PGM_RXW_BOUNDS: c = "PGM_RXW_BOUNDS"; break; - case PGM_RXW_SLOW_CONSUMER: c = "PGM_RXW_SLOW_CONSUMER"; break; - case PGM_RXW_UNKNOWN: c = "PGM_RXW_UNKNOWN"; break; - default: c = "(unknown)"; break; - } - - return c; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/rxw_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/rxw_unittest.c deleted file mode 100644 index 635c854..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/rxw_unittest.c +++ /dev/null @@ -1,1844 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for receive window. - * - * Copyright (c) 2009-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include - - -/* mock global */ - - -#define pgm_histogram_add mock_pgm_histogram_add -#define pgm_time_now mock_pgm_time_now -#define pgm_rs_create mock_pgm_rs_create -#define pgm_rs_destroy mock_pgm_rs_destroy -#define pgm_rs_decode_parity_appended mock_pgm_rs_decode_parity_appended -#define pgm_histogram_init mock_pgm_histogram_init - -#define RXW_DEBUG -#include "rxw.c" - -#ifdef PGM_DISABLE_ASSERT -# error "PGM_DISABLE_ASSERT set" -#endif - -static pgm_time_t mock_pgm_time_now = 0x1; - - -/* mock functions for external references */ - -size_t -pgm_pkt_offset ( - const bool can_fragment, - const sa_family_t pgmcc_family /* 0 = disable */ - ) -{ - return 0; -} - -/** reed-solomon module */ -void -mock_pgm_rs_create ( - pgm_rs_t* rs, - uint8_t n, - uint8_t k - ) -{ -} - -void -mock_pgm_rs_destroy ( - pgm_rs_t* rs - ) -{ -} - -void -mock_pgm_rs_decode_parity_appended ( - pgm_rs_t* rs, - pgm_gf8_t** block, - const uint8_t* offsets, - uint16_t len - ) -{ -// null -} - -void -mock_pgm_histogram_init ( - pgm_histogram_t* histogram - ) -{ -} - -void -mock_pgm_histogram_add ( - pgm_histogram_t* histogram, - int value - ) -{ -} - - -/* generate valid skb, data pointer pointing to PGM payload - */ -static -struct pgm_sk_buff_t* -generate_valid_skb (void) -{ - const pgm_tsi_t tsi = { { 200, 202, 203, 204, 205, 206 }, 2000 }; - const guint16 tsdu_length = 1000; - const guint16 header_length = sizeof(struct pgm_header) + sizeof(struct pgm_data); - struct pgm_sk_buff_t* skb = pgm_alloc_skb (1500); - memcpy (&skb->tsi, &tsi, sizeof(tsi)); -/* fake but valid socket and timestamp */ - skb->sock = (pgm_sock_t*)0x1; - skb->tstamp = pgm_time_now; -/* header */ - pgm_skb_reserve (skb, header_length); - memset (skb->head, 0, header_length); - skb->pgm_header = (struct pgm_header*)skb->head; - skb->pgm_data = (struct pgm_data*)(skb->pgm_header + 1); - skb->pgm_header->pgm_type = PGM_ODATA; - skb->pgm_header->pgm_tsdu_length = g_htons (tsdu_length); -/* DATA */ - pgm_skb_put (skb, tsdu_length); - return skb; -} - -/* target: - * pgm_rxw_t* - * pgm_rxw_create ( - * const pgm_tsi_t* tsi, - * const uint16_t tpdu_size, - * const unsigned sqns, - * const unsigned secs, - * const ssize_t max_rte, - * const uint32_t ack_c_p - * ) - */ - -/* vanilla sequence count window */ -START_TEST (test_create_pass_001) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - fail_if (NULL == pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p), "create failed"); -} -END_TEST - -/* vanilla time based window */ -START_TEST (test_create_pass_002) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - fail_if (NULL == pgm_rxw_create (&tsi, 1500, 0, 60, 800000, ack_c_p), "create failed"); -} -END_TEST - -/* jumbo frame */ -START_TEST (test_create_pass_003) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - fail_if (NULL == pgm_rxw_create (&tsi, 9000, 0, 60, 800000, ack_c_p), "create failed"); -} -END_TEST - -/* max frame */ -START_TEST (test_create_pass_004) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - fail_if (NULL == pgm_rxw_create (&tsi, UINT16_MAX, 0, 60, 800000, ack_c_p), "create failed"); -} -END_TEST - -/* invalid tsi pointer */ -START_TEST (test_create_fail_001) -{ - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (NULL, 1500, 100, 0, 0, ack_c_p); - fail ("reached"); -} -END_TEST - -/* invalid tpdu size */ -START_TEST (test_create_fail_002) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - fail_if (NULL == pgm_rxw_create (&tsi, 0, 100, 0, 0, ack_c_p), "create failed"); -} -END_TEST - -START_TEST (test_create_fail_003) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 0, 0, 60, 800000, ack_c_p); - fail ("reached"); -} -END_TEST - -/* no specified sequence count or time value */ -START_TEST (test_create_fail_004) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 0, 0, 0, 800000, ack_c_p); - fail ("reached"); -} -END_TEST - -/* no specified rate */ -START_TEST (test_create_fail_005) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 0, 0, 60, 0, ack_c_p); - fail ("reached"); -} -END_TEST - -/* all invalid */ -START_TEST (test_create_fail_006) -{ - pgm_rxw_t* window = pgm_rxw_create (NULL, 0, 0, 0, 0, 0); - fail ("reached"); -} -END_TEST - -/* target: - * void - * pgm_rxw_destroy ( - * pgm_rxw_t* const window - * ) - */ - -START_TEST (test_destroy_pass_001) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - pgm_rxw_destroy (window); -} -END_TEST - -START_TEST (test_destroy_fail_001) -{ - pgm_rxw_destroy (NULL); - fail ("reached"); -} -END_TEST - -/* target: - * int - * pgm_rxw_add ( - * pgm_rxw_t* const window, - * struct pgm_sk_buff_t* const skb, - * const pgm_time_t now, - * const pgm_time_t nak_rb_expiry - * ) - * failures raise assert errors and stop process execution. - */ - -START_TEST (test_add_pass_001) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (0); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - pgm_rxw_destroy (window); -} -END_TEST - -/* missing + inserted */ -START_TEST (test_add_pass_002) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); -/* #1 */ - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (0); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); -/* #2 with jump */ - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (2); - fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not missing"); -/* #3 to fill in gap */ - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (1); - fail_unless (PGM_RXW_INSERTED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not inserted"); - pgm_rxw_destroy (window); -} -END_TEST - -/* duplicate + append */ -START_TEST (test_add_pass_003) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); -/* #1 */ - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (0); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); -/* #2 repeat sequence */ - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (0); - fail_unless (PGM_RXW_DUPLICATE == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not duplicate"); -/* #3 append */ - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (1); - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - pgm_rxw_destroy (window); -} -END_TEST - -/* malformed: tpdu too long */ -START_TEST (test_add_pass_004) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_header->pgm_tsdu_length = g_htons (65535); - skb->pgm_data->data_sqn = g_htonl (0); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_MALFORMED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not malformed"); -} -END_TEST - -/* bounds + append */ -START_TEST (test_add_pass_005) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); -/* #1 */ - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (0); - skb->pgm_data->data_trail = g_htonl (-10); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); -/* #2 jump backwards */ - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (-1); - skb->pgm_data->data_trail = g_htonl (-10); - fail_unless (PGM_RXW_BOUNDS == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not bounds"); -/* #3 append */ - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (1); - skb->pgm_data->data_trail = g_htonl (-10); - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); -/* #4 jump forward */ - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (100 + (UINT32_MAX / 2)); - skb->pgm_data->data_trail = g_htonl (UINT32_MAX / 2); - fail_unless (PGM_RXW_BOUNDS == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not bounds"); -/* #5 append */ - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (2); - skb->pgm_data->data_trail = g_htonl (-10); - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - pgm_rxw_destroy (window); -} -END_TEST - -/* null skb */ -START_TEST (test_add_fail_001) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - int retval = pgm_rxw_add (window, NULL, now, nak_rb_expiry); - fail ("reached"); -} -END_TEST - -/* null window */ -START_TEST (test_add_fail_002) -{ - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (0); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - int retval = pgm_rxw_add (NULL, skb, now, nak_rb_expiry); - fail ("reached"); -} -END_TEST - -/* null skb content */ -START_TEST (test_add_fail_003) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - char buffer[1500]; - memset (buffer, 0, sizeof(buffer)); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - int retval = pgm_rxw_add (window, (struct pgm_sk_buff_t*)buffer, now, nak_rb_expiry); - fail ("reached"); -} -END_TEST - -/* 0 nak_rb_expiry */ -START_TEST (test_add_fail_004) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (0); - const pgm_time_t now = 1; - int retval = pgm_rxw_add (window, skb, now, 0); - fail ("reached"); -} -END_TEST - -/* target: - * struct pgm_sk_buff_t* - * pgm_rxw_peek ( - * pgm_rxw_t* const window, - * const uint32_t sequence - * ) - */ - -START_TEST (test_peek_pass_001) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - fail_unless (NULL == pgm_rxw_peek (window, 0)); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (0); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - fail_unless (skb == pgm_rxw_peek (window, 0), "peek failed"); - fail_unless (NULL == pgm_rxw_peek (window, 1), "peek failed"); - fail_unless (NULL == pgm_rxw_peek (window, -1), "peek failed"); - pgm_rxw_destroy (window); -} -END_TEST - -/* null window */ -START_TEST (test_peek_fail_001) -{ - struct pgm_sk_buff_t* skb = pgm_rxw_peek (NULL, 0); - fail ("reached"); -} -END_TEST - -/** inline function tests **/ -/* pgm_rxw_max_length () - */ -START_TEST (test_max_length_pass_001) -{ - const guint window_length = 100; - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, window_length, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - fail_unless (window_length == pgm_rxw_max_length (window), "max_length failed"); - pgm_rxw_destroy (window); -} -END_TEST - -START_TEST (test_max_length_fail_001) -{ - const unsigned len = pgm_rxw_max_length (NULL); - fail ("reached"); -} -END_TEST - -/* pgm_rxw_length () - */ -START_TEST (test_length_pass_001) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - fail_unless (0 == pgm_rxw_length (window), "length failed"); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (0); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - fail_unless (1 == pgm_rxw_length (window), "length failed"); -/* #2 */ - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (1); - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - fail_unless (2 == pgm_rxw_length (window), "length failed"); - pgm_rxw_destroy (window); -} -END_TEST - -START_TEST (test_length_fail_001) -{ - const uint32_t answer = pgm_rxw_length (NULL); - fail ("reached"); -} -END_TEST - -/* pgm_rxw_size () - */ -START_TEST (test_size_pass_001) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - fail_unless (0 == pgm_rxw_size (window), "size failed"); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (0); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - fail_unless (1000 == pgm_rxw_size (window), "size failed"); -/* #2 */ - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (1); - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - fail_unless (2000 == pgm_rxw_size (window), "size failed"); - pgm_rxw_destroy (window); -} -END_TEST - -START_TEST (test_size_fail_001) -{ - const size_t answer = pgm_rxw_size (NULL); - fail ("reached"); -} -END_TEST - -/* pgm_rxw_is_empty - */ -START_TEST (test_is_empty_pass_001) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - fail_unless (pgm_rxw_is_empty (window), "is_empty failed"); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (0); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - fail_if (pgm_rxw_is_empty (window), "is_empty failed"); - pgm_rxw_destroy (window); -} -END_TEST - -START_TEST (test_is_empty_fail_001) -{ - const bool answer = pgm_rxw_is_empty (NULL); - fail ("reached"); -} -END_TEST - -/* pgm_rxw_is_full - */ -START_TEST (test_is_full_pass_001) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 1, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - fail_if (pgm_rxw_is_full (window), "is_full failed"); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (0); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - fail_unless (pgm_rxw_is_full (window), "is_full failed"); - pgm_rxw_destroy (window); -} -END_TEST - -START_TEST (test_is_full_fail_001) -{ - const bool answer = pgm_rxw_is_full (NULL); - fail ("reached"); -} -END_TEST - -/* pgm_rxw_lead - */ -START_TEST (test_lead_pass_001) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - guint32 lead = pgm_rxw_lead (window); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (0); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - fail_unless (lead + 1 == pgm_rxw_lead (window), "lead failed"); - pgm_rxw_destroy (window); -} -END_TEST - -START_TEST (test_lead_fail_001) -{ - const uint32_t answer = pgm_rxw_lead (NULL); - fail ("reached"); -} -END_TEST - -/* pgm_rxw_next_lead - */ -START_TEST (test_next_lead_pass_001) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - guint32 next_lead = pgm_rxw_next_lead (window); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (0); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - fail_unless (next_lead == pgm_rxw_lead (window), "lead failed"); - pgm_rxw_destroy (window); -} -END_TEST - -START_TEST (test_next_lead_fail_001) -{ - const uint32_t answer = pgm_rxw_next_lead (NULL); - fail ("reached"); -} -END_TEST - -/* target: - * ssize_t - * pgm_rxw_readv ( - * pgm_rxw_t* const window, - * struct pgm_msgv_t** pmsg, - * const unsigned msg_len - * ) - */ - -START_TEST (test_readv_pass_001) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - struct pgm_msgv_t msgv[2], *pmsg; -/* #1 empty */ - pmsg = msgv; - fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); -/* #2 single TPDU-APDU */ - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (0); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - pmsg = msgv; - fail_unless (1000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - pmsg = msgv; - fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); -/* #3,4 two APDUs */ - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (1); - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (2); - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - pmsg = msgv; - fail_unless (2000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); -/* #5,6 skip and repair APDU */ - pmsg = msgv; - fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (4); - fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not missing"); - pmsg = msgv; - fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (3); - fail_unless (PGM_RXW_INSERTED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not inserted"); - pmsg = msgv; - fail_unless (2000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - pmsg = msgv; - fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - pgm_rxw_destroy (window); -} -END_TEST - -/* zero-length */ -START_TEST (test_readv_pass_002) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - struct pgm_msgv_t msgv[2], *pmsg; - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_header->pgm_tsdu_length = g_htons (0); - skb->tail = (guint8*)skb->tail - skb->len; - skb->len = 0; - skb->pgm_data->data_sqn = g_htonl (0); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - pmsg = msgv; - fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - pmsg = msgv; - fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - pgm_rxw_destroy (window); -} -END_TEST - -/* full window */ -START_TEST (test_readv_pass_003) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - struct pgm_msgv_t msgv[1], *pmsg; - struct pgm_sk_buff_t* skb; - for (unsigned i = 0; i < 100; i++) - { - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_header->pgm_tsdu_length = g_htons (0); - skb->tail = (guint8*)skb->tail - skb->len; - skb->len = 0; - skb->pgm_data->data_sqn = g_htonl (i); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - fail_unless ((1 + i) == pgm_rxw_length (window), "length failed"); - } - fail_unless (pgm_rxw_is_full (window), "is_full failed"); - fail_unless (_pgm_rxw_commit_is_empty (window), "commit_is_empty failed"); - for (unsigned i = 0; i < 100; i++) - { - pmsg = msgv; - fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - fail_unless ((1 + i) == _pgm_rxw_commit_length (window), "commit_length failed"); - } - fail_unless (pgm_rxw_length (window) == _pgm_rxw_commit_length (window), "commit_length failed"); - pmsg = msgv; - fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - pgm_rxw_destroy (window); -} -END_TEST - -/* full + 1 window */ -START_TEST (test_readv_pass_004) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - struct pgm_msgv_t msgv[1], *pmsg; - struct pgm_sk_buff_t* skb; - for (unsigned i = 0; i < 101; i++) - { - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_header->pgm_tsdu_length = g_htons (0); - skb->tail = (guint8*)skb->tail - skb->len; - skb->len = 0; - skb->pgm_data->data_sqn = g_htonl (i); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - fail_unless (MIN(100, 1 + i) == pgm_rxw_length (window), "length failed"); - } - fail_unless (pgm_rxw_is_full (window), "is_full failed"); - fail_unless (_pgm_rxw_commit_is_empty (window), "commit_is_empty failed"); - for (unsigned i = 0; i < 100; i++) - { - pmsg = msgv; - fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - fail_unless ((1 + i) == _pgm_rxw_commit_length (window), "commit_length failed"); - } - fail_unless (pgm_rxw_length (window) == _pgm_rxw_commit_length (window), "commit_length failed"); - pmsg = msgv; - fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - pgm_rxw_destroy (window); -} -END_TEST - -/* full - 2 lost last in window */ -START_TEST (test_readv_pass_005) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - struct pgm_msgv_t msgv[1], *pmsg; - struct pgm_sk_buff_t* skb; - for (unsigned i = 0; i < 98; i++) - { - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_header->pgm_tsdu_length = g_htons (0); - skb->tail = (guint8*)skb->tail - skb->len; - skb->len = 0; - skb->pgm_data->data_sqn = g_htonl (i); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - fail_unless ((1 + i) == pgm_rxw_length (window), "length failed"); - } - fail_if (pgm_rxw_is_full (window), "is_full failed"); - fail_unless (_pgm_rxw_commit_is_empty (window), "commit_is_empty failed"); - { - unsigned i = 99; - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_header->pgm_tsdu_length = g_htons (0); - skb->tail = (guint8*)skb->tail - skb->len; - skb->len = 0; - skb->pgm_data->data_sqn = g_htonl (i); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not missing"); - fail_unless ((1 + i) == pgm_rxw_length (window), "length failed"); - } - fail_unless (pgm_rxw_is_full (window)); - fail_unless (_pgm_rxw_commit_is_empty (window)); - for (unsigned i = 0; i < 98; i++) - { - pmsg = msgv; - fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - fail_unless ((1 + i) == _pgm_rxw_commit_length (window), "commit_length failed"); - } - fail_unless (pgm_rxw_length (window) == (2 + _pgm_rxw_commit_length (window)), "commit_length failed"); -/* read end-of-window */ - { - pmsg = msgv; - fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - } - pgm_rxw_destroy (window); -} -END_TEST - -/* add full window, readv 1 skb, add 1 more */ -START_TEST (test_readv_pass_006) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - struct pgm_msgv_t msgv[1], *pmsg; - struct pgm_sk_buff_t* skb; - for (unsigned i = 0; i < 100; i++) - { - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_header->pgm_tsdu_length = g_htons (0); - skb->tail = (guint8*)skb->tail - skb->len; - skb->len = 0; - skb->pgm_data->data_sqn = g_htonl (i); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - fail_unless (MIN(100, 1 + i) == pgm_rxw_length (window), "length failed"); - } - fail_unless (pgm_rxw_is_full (window)); - fail_unless (_pgm_rxw_commit_is_empty (window)); -/* read one skb */ - { - pmsg = msgv; - fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - fail_unless (1 == _pgm_rxw_commit_length (window), "commit_length failed"); - } -/* add one more new skb */ - { - unsigned i = 100; - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_header->pgm_tsdu_length = g_htons (0); - skb->tail = (guint8*)skb->tail - skb->len; - skb->len = 0; - skb->pgm_data->data_sqn = g_htonl (i); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_BOUNDS == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not bounds"); - fail_unless (MIN(100, 1 + i) == pgm_rxw_length (window), "length failed"); - } -/* read off 99 more skbs */ - for (unsigned i = 0; i < 99; i++) - { - pmsg = msgv; - fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - fail_unless ((2 + i) == _pgm_rxw_commit_length (window), "commit_length failed"); - } -/* read end-of-window */ - { - pmsg = msgv; - fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - } - pgm_rxw_destroy (window); -} -END_TEST - -/* NULL window */ -START_TEST (test_readv_fail_001) -{ - struct pgm_msgv_t msgv[1], *pmsg = msgv; - gssize len = pgm_rxw_readv (NULL, &pmsg, G_N_ELEMENTS(msgv)); - fail ("reached"); -} -END_TEST - -/* NULL pmsg */ -START_TEST (test_readv_fail_002) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (0); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - struct pgm_msgv_t msgv[1], *pmsg = msgv; - gssize len = pgm_rxw_readv (window, NULL, G_N_ELEMENTS(msgv)); - fail ("reached"); -} -END_TEST - -/* 0 msg-len */ -START_TEST (test_readv_fail_003) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (0); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - struct pgm_msgv_t msgv[1], *pmsg = msgv; - gssize len = pgm_rxw_readv (window, &pmsg, 0); - fail ("reached"); -} -END_TEST - -/* target: - * - * void - * pgm_rxw_remove_commit ( - * pgm_rxw_t* const window - * ) - */ - -/* full - 2 lost last in window */ -START_TEST (test_remove_commit_pass_001) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - struct pgm_msgv_t msgv[1], *pmsg; - struct pgm_sk_buff_t* skb; - for (unsigned i = 0; i < 98; i++) - { - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_header->pgm_tsdu_length = g_htons (0); - skb->tail = (guint8*)skb->tail - skb->len; - skb->len = 0; - skb->pgm_data->data_sqn = g_htonl (i); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - fail_unless ((1 + i) == pgm_rxw_length (window), "length failed"); - } - fail_if (pgm_rxw_is_full (window)); - fail_unless (_pgm_rxw_commit_is_empty (window)); -/* #98 is missing */ - { - unsigned i = 99; - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_header->pgm_tsdu_length = g_htons (0); - skb->tail = (guint8*)skb->tail - skb->len; - skb->len = 0; - skb->pgm_data->data_sqn = g_htonl (i); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - fail_unless ((1 + i) == pgm_rxw_length (window), "length failed"); - } - fail_unless (pgm_rxw_is_full (window), "is_full failed"); - fail_unless (_pgm_rxw_commit_is_empty (window), "commit_is_empty"); -/* now mark #98 lost */ - pgm_rxw_lost (window, 98); - for (unsigned i = 0; i < 98; i++) - { - pmsg = msgv; - fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - fail_unless ((1 + i) == _pgm_rxw_commit_length (window), "commit_length failed"); - } - fail_unless (100 == pgm_rxw_length (window), "length failed"); - fail_unless ( 98 == _pgm_rxw_commit_length (window), "commit_length failed"); -/* read end-of-window */ - { - pmsg = msgv; - fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - } - fail_unless (100 == pgm_rxw_length (window), "length failed"); - fail_unless ( 98 == _pgm_rxw_commit_length (window), "commit_length failed"); - pgm_rxw_remove_commit (window); -/* read lost skb #98 */ - { - pmsg = msgv; - fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - } - pgm_rxw_remove_commit (window); -/* read valid skb #99 */ - { - pmsg = msgv; - fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - } -/* read end-of-window */ - { - pmsg = msgv; - fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - } - pgm_rxw_destroy (window); -} -END_TEST - -START_TEST (test_remove_commit_fail_001) -{ - pgm_rxw_remove_commit (NULL); - fail ("reached"); -} -END_TEST - -/* target: - * unsigned - * pgm_rxw_remove_trail ( - * pgm_rxw_t* const window - * ) - */ - -START_TEST (test_remove_trail_pass_001) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - struct pgm_msgv_t msgv[2], *pmsg; - fail_unless (0 == pgm_rxw_remove_trail (window), "remove_trail failed"); -/* #1,2 two APDUs */ - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (1); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry)); - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (2); - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry)); - fail_unless (1 == pgm_rxw_remove_trail (window), "remove_trail failed"); - fail_unless (1 == pgm_rxw_length (window), "length failed"); - fail_unless (1000 == pgm_rxw_size (window), "size failed"); - pmsg = msgv; - fail_unless (1000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); - fail_unless (0 == pgm_rxw_remove_trail (window), "remove_trail failed"); - pgm_rxw_destroy (window); -} -END_TEST - -START_TEST (test_remove_trail_fail_001) -{ - guint count = pgm_rxw_remove_trail (NULL); - fail ("reached"); -} -END_TEST - -/* target: - * unsigned - * pgm_rxw_update ( - * pgm_rxw_t* const window, - * const uint32_t txw_trail, - * const uint32_t txw_lead, - * const pgm_time_t now, - * const pgm_time_t nak_rb_expiry - * ) - */ - -START_TEST (test_update_pass_001) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (0 == pgm_rxw_update (window, 100, 99, now, nak_rb_expiry), "update failed"); -/* dupe */ - fail_unless (0 == pgm_rxw_update (window, 100, 99, now, nak_rb_expiry), "update failed"); -/* #1 at 100 */ - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (100); - fail_unless (PGM_RXW_BOUNDS == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not bounds"); -/* #2 at 101 */ - skb->pgm_data->data_sqn = g_htonl (101); - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - struct pgm_msgv_t msgv[1], *pmsg = msgv; - fail_unless (1000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); -/* #3 at 102 */ - fail_unless (1 == pgm_rxw_update (window, 102, 99, now, nak_rb_expiry), "update failed"); - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (102); - fail_unless (PGM_RXW_INSERTED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not inserted"); - pgm_rxw_destroy (window); -} -END_TEST - -START_TEST (test_update_fail_001) -{ - guint count = pgm_rxw_update (NULL, 0, 0, 0, 0); - fail ("reached"); -} -END_TEST - -/* target: - * int - * pgm_rxw_confirm ( - * pgm_rxw_t* const window, - * const uint32_t sequence, - * const pgm_time_t now, - * const pgm_time_t nak_rdata_expiry, - * const pgm_time_t nak_rb_expiry - * ) - */ - -START_TEST (test_confirm_pass_001) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - const pgm_time_t now = 1; - const pgm_time_t nak_rdata_expiry = 2; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_BOUNDS == pgm_rxw_confirm (window, 0, now, nak_rdata_expiry, nak_rb_expiry), "confirm not bounds"); -/* #1 at 100 */ - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (100); - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - fail_unless (1 == pgm_rxw_length (window), "length failed"); - fail_unless (PGM_RXW_BOUNDS == pgm_rxw_confirm (window, 99, now, nak_rdata_expiry, nak_rb_expiry), "confirm not bounds"); - fail_unless (PGM_RXW_DUPLICATE == pgm_rxw_confirm (window, 100, now, nak_rdata_expiry, nak_rb_expiry), "confirm not duplicate"); - fail_unless (PGM_RXW_APPENDED == pgm_rxw_confirm (window, 101, now, nak_rdata_expiry, nak_rb_expiry), "confirm not appended"); - fail_unless (2 == pgm_rxw_length (window)); - fail_unless (PGM_RXW_UPDATED == pgm_rxw_confirm (window, 101, now, nak_rdata_expiry, nak_rb_expiry), "confirm not updated"); -/* #2 at 101 */ - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (101); - fail_unless (PGM_RXW_INSERTED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not inserted"); - struct pgm_msgv_t msgv[2], *pmsg = msgv; - fail_unless (2000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - pgm_rxw_destroy (window); -} -END_TEST - -/* constrained confirm */ -START_TEST (test_confirm_pass_002) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - struct pgm_msgv_t msgv[1], *pmsg; - struct pgm_sk_buff_t* skb; - for (unsigned i = 0; i < 100; i++) - { - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_header->pgm_tsdu_length = g_htons (0); - skb->tail = (guint8*)skb->tail - skb->len; - skb->len = 0; - skb->pgm_data->data_sqn = g_htonl (i); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - fail_unless (MIN(100, 1 + i) == pgm_rxw_length (window), "length failed"); - } - fail_unless (pgm_rxw_is_full (window), "is_full failed"); - fail_unless (_pgm_rxw_commit_is_empty (window), "is_empty failed"); -/* read one skb */ - { - pmsg = msgv; - fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - fail_unless (1 == _pgm_rxw_commit_length (window), "commit_length failed"); - } -/* confirm next sequence */ - const pgm_time_t now = 1; - const pgm_time_t nak_rdata_expiry = 2; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_BOUNDS == pgm_rxw_confirm (window, 100, now, nak_rdata_expiry, nak_rb_expiry), "confirm not bounds"); -/* read off 99 more skbs */ - for (unsigned i = 0; i < 99; i++) - { - pmsg = msgv; - fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - fail_unless ((2 + i) == _pgm_rxw_commit_length (window), "commit_length failed"); - } -/* read end-of-window */ - { - pmsg = msgv; - fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - } - pgm_rxw_destroy (window); -} -END_TEST - -START_TEST (test_confirm_fail_001) -{ - int retval = pgm_rxw_confirm (NULL, 0, 0, 0, 0); - fail ("reached"); -} -END_TEST - -/* target: - * void - * pgm_rxw_lost ( - * pgm_rxw_t* const window, - * const uint32_t sequence - * ) - */ - -START_TEST (test_lost_pass_001) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - const pgm_time_t now = 1; - const pgm_time_t nak_rdata_expiry = 2; - const pgm_time_t nak_rb_expiry = 2; -/* #1 at 100 */ - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (100); - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); - fail_unless (1 == pgm_rxw_length (window), "length failed"); - fail_unless (1000 == pgm_rxw_size (window), "size failed"); - fail_unless (PGM_RXW_APPENDED == pgm_rxw_confirm (window, 101, now, nak_rdata_expiry, nak_rb_expiry), "confirm not appended"); - fail_unless (2 == pgm_rxw_length (window), "length failed"); - fail_unless (1000 == pgm_rxw_size (window), "size failed"); - pgm_rxw_lost (window, 101); - fail_unless (2 == pgm_rxw_length (window), "length failed"); - fail_unless (1000 == pgm_rxw_size (window), "size failed"); -/* #2 at 101 */ - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (101); - fail_unless (PGM_RXW_INSERTED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not inserted"); - fail_unless (2 == pgm_rxw_length (window), "length failed"); - fail_unless (2000 == pgm_rxw_size (window), "size failed"); - pgm_rxw_destroy (window); -} -END_TEST - -START_TEST (test_lost_fail_001) -{ - pgm_rxw_lost (NULL, 0); - fail ("reached"); -} -END_TEST - -/* target: - * void - * pgm_rxw_state ( - * pgm_rxw_t* const window, - * struct pgm_sk_buff_t* skb, - * int new_state - * ) - */ - -START_TEST (test_state_pass_001) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - const pgm_time_t now = 1; - const pgm_time_t nak_rdata_expiry = 2; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (0 == pgm_rxw_update (window, 100, 99, now, nak_rb_expiry), "update failed"); - fail_unless (PGM_RXW_APPENDED == pgm_rxw_confirm (window, 101, now, nak_rdata_expiry, nak_rb_expiry), "confirm not appended"); - struct pgm_sk_buff_t* skb = pgm_rxw_peek (window, 101); - pgm_rxw_state (window, skb, PGM_PKT_STATE_WAIT_NCF); - pgm_rxw_state (window, skb, PGM_PKT_STATE_WAIT_DATA); - pgm_rxw_destroy (window); -} -END_TEST - -START_TEST (test_state_fail_001) -{ - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (0); - pgm_rxw_state (NULL, skb, PGM_PKT_STATE_BACK_OFF); - fail ("reached"); -} -END_TEST - -START_TEST (test_state_fail_002) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - pgm_rxw_state (window, NULL, PGM_PKT_STATE_BACK_OFF); - fail ("reached"); -} -END_TEST - -START_TEST (test_state_fail_003) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (0); - pgm_rxw_state (window, skb, -1); - fail ("reached"); -} -END_TEST - -/* pgm_peer_has_pending - */ - -START_TEST (test_has_pending_pass_001) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window, "create failed"); -/* empty */ - fail_unless (0 == window->has_event, "unexpected event"); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (0); - const pgm_time_t now = 1; - const pgm_time_t nak_rdata_expiry = 2; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); -/* 1 sequence */ - fail_unless (1 == window->has_event, "no event"); - window->has_event = 0; -/* jump */ - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (2); - fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not missing"); - fail_unless (0 == window->has_event, "unexpected event"); -/* loss */ - pgm_rxw_lost (window, 1); - fail_unless (1 == window->has_event, "no event"); - window->has_event = 0; -/* insert */ - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - skb->pgm_data->data_sqn = g_htonl (1); - fail_unless (PGM_RXW_INSERTED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not inserted"); - fail_unless (1 == window->has_event, "no event"); - window->has_event = 0; -/* confirm */ - fail_unless (PGM_RXW_APPENDED == pgm_rxw_confirm (window, 3, now, nak_rdata_expiry, nak_rb_expiry), "confirm not appended"); - fail_unless (0 == window->has_event, "unexpected event"); -/* partial read */ - struct pgm_msgv_t msgv[2], *pmsg = msgv; - fail_unless (2000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - fail_unless (0 == window->has_event, "unexpected event"); -/* finish read */ - pmsg = msgv; - fail_unless (1000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); - fail_unless (0 == window->has_event, "unexpected event"); - pgm_rxw_destroy (window); -} -END_TEST - -static -Suite* -make_basic_test_suite (void) -{ - Suite* s; - - s = suite_create ("basic receive window API"); - - TCase* tc_create = tcase_create ("create"); - suite_add_tcase (s, tc_create); - tcase_add_test (tc_create, test_create_pass_001); - tcase_add_test (tc_create, test_create_pass_002); - tcase_add_test (tc_create, test_create_pass_003); - tcase_add_test (tc_create, test_create_pass_004); - tcase_add_test_raise_signal (tc_create, test_create_fail_001, SIGABRT); - tcase_add_test_raise_signal (tc_create, test_create_fail_002, SIGABRT); - tcase_add_test_raise_signal (tc_create, test_create_fail_003, SIGABRT); - tcase_add_test_raise_signal (tc_create, test_create_fail_004, SIGABRT); - - TCase* tc_destroy = tcase_create ("destroy"); - suite_add_tcase (s, tc_destroy); - tcase_add_test (tc_destroy, test_destroy_pass_001); - tcase_add_test_raise_signal (tc_destroy, test_destroy_fail_001, SIGABRT); - - TCase* tc_add = tcase_create ("add"); - suite_add_tcase (s, tc_add); - tcase_add_test (tc_add, test_add_pass_001); - tcase_add_test (tc_add, test_add_pass_002); - tcase_add_test (tc_add, test_add_pass_003); - tcase_add_test (tc_add, test_add_pass_004); - tcase_add_test (tc_add, test_add_pass_005); - tcase_add_test_raise_signal (tc_add, test_add_fail_001, SIGABRT); - tcase_add_test_raise_signal (tc_add, test_add_fail_002, SIGABRT); - tcase_add_test_raise_signal (tc_add, test_add_fail_003, SIGABRT); - - TCase* tc_peek = tcase_create ("peek"); - suite_add_tcase (s, tc_peek); - tcase_add_test (tc_peek, test_peek_pass_001); - tcase_add_test_raise_signal (tc_peek, test_peek_fail_001, SIGABRT); - - TCase* tc_max_length = tcase_create ("max-length"); - suite_add_tcase (s, tc_max_length); - tcase_add_test (tc_max_length, test_max_length_pass_001); - tcase_add_test_raise_signal (tc_max_length, test_max_length_fail_001, SIGABRT); - - TCase* tc_length = tcase_create ("length"); - suite_add_tcase (s, tc_length); - tcase_add_test (tc_length, test_length_pass_001); - tcase_add_test_raise_signal (tc_length, test_length_fail_001, SIGABRT); - - TCase* tc_size = tcase_create ("size"); - suite_add_tcase (s, tc_size); - tcase_add_test (tc_size, test_size_pass_001); - tcase_add_test_raise_signal (tc_size, test_size_fail_001, SIGABRT); - - TCase* tc_is_empty = tcase_create ("is-empty"); - suite_add_tcase (s, tc_is_empty); - tcase_add_test (tc_is_empty, test_is_empty_pass_001); - tcase_add_test_raise_signal (tc_is_empty, test_is_empty_fail_001, SIGABRT); - - TCase* tc_is_full = tcase_create ("is-full"); - suite_add_tcase (s, tc_is_full); - tcase_add_test (tc_is_full, test_is_full_pass_001); - tcase_add_test_raise_signal (tc_is_full, test_is_full_fail_001, SIGABRT); - - TCase* tc_lead = tcase_create ("lead"); - suite_add_tcase (s, tc_lead); - tcase_add_test (tc_lead, test_lead_pass_001); - tcase_add_test_raise_signal (tc_lead, test_lead_fail_001, SIGABRT); - - TCase* tc_next_lead = tcase_create ("next-lead"); - suite_add_tcase (s, tc_next_lead); - tcase_add_test (tc_next_lead, test_next_lead_pass_001); - tcase_add_test_raise_signal (tc_next_lead, test_next_lead_fail_001, SIGABRT); - - TCase* tc_readv = tcase_create ("readv"); - suite_add_tcase (s, tc_readv); - tcase_add_test (tc_readv, test_readv_pass_001); - tcase_add_test (tc_readv, test_readv_pass_002); - tcase_add_test (tc_readv, test_readv_pass_003); - tcase_add_test (tc_readv, test_readv_pass_004); - tcase_add_test (tc_readv, test_readv_pass_005); - tcase_add_test (tc_readv, test_readv_pass_006); - tcase_add_test_raise_signal (tc_readv, test_readv_fail_001, SIGABRT); - tcase_add_test_raise_signal (tc_readv, test_readv_fail_002, SIGABRT); - tcase_add_test_raise_signal (tc_readv, test_readv_fail_003, SIGABRT); - - TCase* tc_remove_commit = tcase_create ("remove-commit"); - suite_add_tcase (s, tc_remove_commit); - tcase_add_test (tc_remove_commit, test_remove_commit_pass_001); - tcase_add_test_raise_signal (tc_remove_commit, test_remove_commit_fail_001, SIGABRT); - - TCase* tc_remove_trail = tcase_create ("remove-trail"); - TCase* tc_update = tcase_create ("update"); - suite_add_tcase (s, tc_update); - tcase_add_test (tc_update, test_update_pass_001); - tcase_add_test_raise_signal (tc_update, test_update_fail_001, SIGABRT); - - TCase* tc_confirm = tcase_create ("confirm"); - suite_add_tcase (s, tc_confirm); - tcase_add_test (tc_confirm, test_confirm_pass_001); - tcase_add_test (tc_confirm, test_confirm_pass_002); - tcase_add_test_raise_signal (tc_confirm, test_confirm_fail_001, SIGABRT); - - TCase* tc_lost = tcase_create ("lost"); - suite_add_tcase (s, tc_lost); - tcase_add_test (tc_lost, test_lost_pass_001); - tcase_add_test_raise_signal (tc_lost, test_lost_fail_001, SIGABRT); - - TCase* tc_state = tcase_create ("state"); - suite_add_tcase (s, tc_state); - tcase_add_test (tc_state, test_state_pass_001); - tcase_add_test_raise_signal (tc_state, test_state_fail_001, SIGABRT); - - return s; -} - -/* read through lost packet */ -START_TEST (test_readv_pass_007) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window); - struct pgm_msgv_t msgv[1], *pmsg; - struct pgm_sk_buff_t* skb; -/* add #0 */ - { - unsigned i = 0; - skb = generate_valid_skb (); - fail_if (NULL == skb); - skb->pgm_header->pgm_tsdu_length = g_htons (0); - skb->tail = (guint8*)skb->tail - skb->len; - skb->len = 0; - skb->pgm_data->data_sqn = g_htonl (i); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry)); - fail_unless ((1 + i) == pgm_rxw_length (window)); - } -/* add # 2 */ - { - unsigned i = 2; - skb = generate_valid_skb (); - fail_if (NULL == skb); - skb->pgm_header->pgm_tsdu_length = g_htons (0); - skb->tail = (guint8*)skb->tail - skb->len; - skb->len = 0; - skb->pgm_data->data_sqn = g_htonl (i); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry)); - fail_unless ((1 + i) == pgm_rxw_length (window)); - } -/* lose #1 */ - { - pgm_rxw_lost (window, 1); - } - fail_unless (_pgm_rxw_commit_is_empty (window)); -/* read #0 */ - { - pmsg = msgv; - fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); - } -/* end-of-window */ - { - pmsg = msgv; - fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); - } - pgm_rxw_remove_commit (window); -/* read lost skb #1 */ - { - pmsg = msgv; - fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); - } - pgm_rxw_remove_commit (window); -/* read #2 */ - { - pmsg = msgv; - fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); - } -/* end-of-window */ - { - pmsg = msgv; - fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); - } - pgm_rxw_destroy (window); -} -END_TEST - -/* read through loss extended window */ -START_TEST (test_readv_pass_008) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window); - struct pgm_msgv_t msgv[1], *pmsg; - struct pgm_sk_buff_t* skb; -/* add #0 */ - { - unsigned i = 0; - skb = generate_valid_skb (); - fail_if (NULL == skb); - skb->pgm_header->pgm_tsdu_length = g_htons (0); - skb->tail = (guint8*)skb->tail - skb->len; - skb->len = 0; - skb->pgm_data->data_sqn = g_htonl (i); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry)); - fail_unless ((1 + i) == pgm_rxw_length (window)); - } - fail_unless (_pgm_rxw_commit_is_empty (window)); -/* read #0 */ - { - pmsg = msgv; - fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); - } - pgm_rxw_remove_commit (window); -/* end-of-window */ - { - pmsg = msgv; - fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); - } -/* add #100 */ - { - unsigned i = 100; - skb = generate_valid_skb (); - fail_if (NULL == skb); - skb->pgm_header->pgm_tsdu_length = g_htons (0); - skb->tail = (guint8*)skb->tail - skb->len; - skb->len = 0; - skb->pgm_data->data_sqn = g_htonl (i); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry)); - } -/* lose #1-99 */ - { - for (unsigned i = 1; i < 100; i++) - pgm_rxw_lost (window, i); - } -/* read #100 */ - { - int i = 0; - int bytes_read; - pmsg = msgv; - do { - bytes_read = pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)); - pgm_rxw_remove_commit (window); - i++; - if (i > 100) break; - } while (-1 == bytes_read); - fail_unless (100 == i); - } -/* end-of-window */ - { - pmsg = msgv; - fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); - } - pgm_rxw_destroy (window); -} -END_TEST - -/* read through long data-loss */ -START_TEST (test_readv_pass_009) -{ - pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const uint32_t ack_c_p = 500; - pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); - fail_if (NULL == window); - struct pgm_msgv_t msgv[1], *pmsg; - struct pgm_sk_buff_t* skb; -/* add #0 */ - { - unsigned i = 0; - skb = generate_valid_skb (); - fail_if (NULL == skb); - skb->pgm_header->pgm_tsdu_length = g_htons (0); - skb->tail = (guint8*)skb->tail - skb->len; - skb->len = 0; - skb->pgm_data->data_sqn = g_htonl (i); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry)); - fail_unless ((1 + i) == pgm_rxw_length (window)); - } - fail_unless (_pgm_rxw_commit_is_empty (window)); -/* read #0 */ - { - pmsg = msgv; - fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); - } - pgm_rxw_remove_commit (window); -/* end-of-window */ - { - pmsg = msgv; - fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); - } -/* add #2000 */ - { - unsigned i = 2000; - skb = generate_valid_skb (); - fail_if (NULL == skb); - skb->pgm_header->pgm_tsdu_length = g_htons (0); - skb->tail = (guint8*)skb->tail - skb->len; - skb->len = 0; - skb->pgm_data->data_sqn = g_htonl (i); - const pgm_time_t now = 1; - const pgm_time_t nak_rb_expiry = 2; - fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry)); - } -/* lose #1-1999 */ - { - for (unsigned i = 1901; i < 2000; i++) - pgm_rxw_lost (window, i); - } -/* read #2000 */ - { - int i = 0; - int bytes_read; - pmsg = msgv; - do { - bytes_read = pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)); - pgm_rxw_remove_commit (window); - i++; - if (i > 100) break; - } while (-1 == bytes_read); - fail_unless (100 == i); - } -/* end-of-window */ - { - pmsg = msgv; - fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); - } - pgm_rxw_destroy (window); -} -END_TEST - -/* a.k.a. unreliable delivery - */ - -static -Suite* -make_best_effort_test_suite (void) -{ - Suite* s; - - s = suite_create ("Best effort delivery"); - - TCase* tc_readv = tcase_create ("readv"); - suite_add_tcase (s, tc_readv); - tcase_add_test (tc_readv, test_readv_pass_007); - tcase_add_test (tc_readv, test_readv_pass_008); - tcase_add_test (tc_readv, test_readv_pass_009); - - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_basic_test_suite ()); - srunner_add_suite (sr, make_best_effort_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/signal.c b/3rdparty/openpgm-svn-r1085/pgm/signal.c deleted file mode 100644 index 1279a8f..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/signal.c +++ /dev/null @@ -1,176 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Re-entrant safe signal handling. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define _GNU_SOURCE -#include -#include /* _GNU_SOURCE for strsignal() */ -#include -#ifndef G_OS_WIN32 -# include -#else -# include -#endif -#include -#include "pgm/signal.h" - - -//#define SIGNAL_DEBUG - - -/* globals */ - -static pgm_sighandler_t signal_list[NSIG]; -static int signal_pipe[2]; -static GIOChannel* signal_io = NULL; - -static void on_signal (int); -static gboolean on_io_signal (GIOChannel*, GIOCondition, gpointer); -static const char* cond_string (GIOCondition); - - -static -void -set_nonblock ( - const int s, - const gboolean v - ) -{ -#ifndef G_OS_WIN32 - int flags = fcntl (s, F_GETFL); - if (!v) flags &= ~O_NONBLOCK; - else flags |= O_NONBLOCK; - fcntl (s, F_SETFL, flags); -#else - u_long mode = v; - ioctlsocket (s, FIONBIO, &mode); -#endif -} - -/* install signal handler and return unix fd to add to event loop - */ - -gboolean -pgm_signal_install ( - int signum, - pgm_sighandler_t handler, - gpointer user_data - ) -{ - g_debug ("pgm_signal_install (signum:%d handler:%p user_data:%p)", - signum, (const void*)handler, user_data); - - if (NULL == signal_io) - { -#ifdef G_OS_UNIX - if (pipe (signal_pipe)) -#else - if (_pipe (signal_pipe, 4096, _O_BINARY | _O_NOINHERIT)) -#endif - return FALSE; - - set_nonblock (signal_pipe[0], TRUE); - set_nonblock (signal_pipe[1], TRUE); -/* add to evm */ - signal_io = g_io_channel_unix_new (signal_pipe[0]); - g_io_add_watch (signal_io, G_IO_IN, on_io_signal, user_data); - } - - signal_list[signum] = handler; - return (SIG_ERR != signal (signum, on_signal)); -} - -/* process signal from operating system - */ - -static -void -on_signal ( - int signum - ) -{ - g_debug ("on_signal (signum:%d)", signum); - if (write (signal_pipe[1], &signum, sizeof(signum)) != sizeof(signum)) - { -#ifndef G_OS_WIN32 - g_warning ("Unix signal %s (%d) lost", strsignal (signum), signum); -#else - g_warning ("Unix signal (%d) lost", signum); -#endif - } -} - -/* process signal from pipe - */ - -static -gboolean -on_io_signal ( - GIOChannel* source, - GIOCondition cond, - gpointer user_data - ) -{ -/* pre-conditions */ - g_assert (NULL != source); - g_assert (G_IO_IN == cond); - - g_debug ("on_io_signal (source:%p cond:%s user_data:%p)", - (gpointer)source, cond_string (cond), user_data); - - int signum; - const gsize bytes_read = read (g_io_channel_unix_get_fd (source), &signum, sizeof(signum)); - - if (sizeof(signum) == bytes_read) - { - signal_list[signum] (signum, user_data); - } - else - { - g_warning ("Lost data in signal pipe, read %" G_GSIZE_FORMAT " byte%s expected %" G_GSIZE_FORMAT ".", - bytes_read, bytes_read > 1 ? "s" : "", sizeof(signum)); - } - - return TRUE; -} - -static -const char* -cond_string ( - GIOCondition cond - ) -{ - const char* c; - - switch (cond) { - case G_IO_IN: c = "G_IO_IN"; break; - case G_IO_OUT: c = "G_IO_OUT"; break; - case G_IO_PRI: c = "G_IO_PRI"; break; - case G_IO_ERR: c = "G_IO_ERR"; break; - case G_IO_HUP: c = "G_IO_HUP"; break; - case G_IO_NVAL: c = "G_IO_NVAL"; break; - default: c = "(unknown)"; break; - } - - return c; -} - - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/signal_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/signal_unittest.c deleted file mode 100644 index 4784053..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/signal_unittest.c +++ /dev/null @@ -1,115 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for re-entrant safe signal handling. - * - * Copyright (c) 2006-2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include - - -/* mock state */ - -static -void -on_sigusr1 ( - int signum, - gpointer user_data - ) -{ - g_assert (SIGUSR1 == signum); - g_assert (NULL != user_data); - GMainLoop* loop = (GMainLoop*)user_data; - g_debug ("on_sigusr1 (signum:%d)", signum); - g_main_loop_quit (loop); -} - -/* mock functions for external references */ - -#define SIGNAL_DEBUG -#include "signal.c" - - -/* target: - * pgm_sighandler_t - * pgm_signal_install ( - * int signum, - pgm_sighandler_t handler - * ) - */ - -static -gboolean -on_startup ( - gpointer data - ) -{ - g_assert (NULL != data); - const int signum = *(const int*)data; - fail_unless (0 == raise (signum)); - return FALSE; -} - -START_TEST (test_install_pass_001) -{ - const int signum = SIGUSR1; - GMainLoop* loop = g_main_loop_new (NULL, FALSE); - fail_unless (TRUE == pgm_signal_install (signum, on_sigusr1, loop)); - g_timeout_add (0, (GSourceFunc)on_startup, &signum); - g_main_loop_run (loop); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_install = tcase_create ("install"); - suite_add_tcase (s, tc_install); - tcase_add_test (tc_install, test_install_pass_001); - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/skbuff.c b/3rdparty/openpgm-svn-r1085/pgm/skbuff.c deleted file mode 100644 index 5db6ffc..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/skbuff.c +++ /dev/null @@ -1,115 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * PGM socket buffers - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include "pgm/skbuff.h" - - -void -pgm_skb_over_panic ( - const struct pgm_sk_buff_t*const skb, - const uint16_t len - ) -{ - pgm_fatal ("skput:over: %u put:%u", - skb->len, len); - pgm_assert_not_reached(); -} - -void -pgm_skb_under_panic ( - const struct pgm_sk_buff_t*const skb, - const uint16_t len - ) -{ - pgm_fatal ("skput:under: %u put:%u", - skb->len, len); - pgm_assert_not_reached(); -} - -#ifndef SKB_DEBUG -bool -pgm_skb_is_valid ( - PGM_GNUC_UNUSED const struct pgm_sk_buff_t*const skb - ) -{ - return TRUE; -} -#else -bool -pgm_skb_is_valid ( - const struct pgm_sk_buff_t*const skb - ) -{ - pgm_return_val_if_fail (skb, FALSE); -/* link_ */ -/* socket */ - pgm_return_val_if_fail (skb->sock, FALSE); -/* tstamp */ - pgm_return_val_if_fail (skb->tstamp > 0, FALSE); -/* tsi */ -/* sequence can be any value */ -/* cb can be any value */ -/* len can be any value */ -/* zero_padded can be any value */ -/* gpointers */ - pgm_return_val_if_fail (skb->head, FALSE); - pgm_return_val_if_fail ((const char*)skb->head > (const char*)&skb->users, FALSE); - pgm_return_val_if_fail (skb->data, FALSE); - pgm_return_val_if_fail ((const char*)skb->data >= (const char*)skb->head, FALSE); - pgm_return_val_if_fail (skb->tail, FALSE); - pgm_return_val_if_fail ((const char*)skb->tail >= (const char*)skb->data, FALSE); - pgm_return_val_if_fail (skb->len == (char*)skb->tail - (const char*)skb->data, FALSE); - pgm_return_val_if_fail (skb->end, FALSE); - pgm_return_val_if_fail ((const char*)skb->end >= (const char*)skb->tail, FALSE); -/* pgm_header */ - if (skb->pgm_header) { - pgm_return_val_if_fail ((const char*)skb->pgm_header >= (const char*)skb->head, FALSE); - pgm_return_val_if_fail ((const char*)skb->pgm_header + sizeof(struct pgm_header) <= (const char*)skb->tail, FALSE); - pgm_return_val_if_fail (skb->pgm_data, FALSE); - pgm_return_val_if_fail ((const char*)skb->pgm_data >= (const char*)skb->pgm_header + sizeof(struct pgm_header), FALSE); - pgm_return_val_if_fail ((const char*)skb->pgm_data <= (const char*)skb->tail, FALSE); - if (skb->pgm_opt_fragment) { - pgm_return_val_if_fail ((const char*)skb->pgm_opt_fragment > (const char*)skb->pgm_data, FALSE); - pgm_return_val_if_fail ((const char*)skb->pgm_opt_fragment + sizeof(struct pgm_opt_fragment) < (const char*)skb->tail, FALSE); -/* of_apdu_first_sqn can be any value */ -/* of_frag_offset */ - pgm_return_val_if_fail (ntohl (skb->of_frag_offset) < ntohl (skb->of_apdu_len), FALSE); -/* of_apdu_len can be any value */ - } - pgm_return_val_if_fail (PGM_ODATA == skb->pgm_header->pgm_type || PGM_RDATA == skb->pgm_header->pgm_type, FALSE); -/* FEC broken */ - pgm_return_val_if_fail (0 == (skb->pgm_header->pgm_options & PGM_OPT_PARITY), FALSE); - pgm_return_val_if_fail (0 == (skb->pgm_header->pgm_options & PGM_OPT_VAR_PKTLEN), FALSE); - } else { - pgm_return_val_if_fail (NULL == skb->pgm_data, FALSE); - pgm_return_val_if_fail (NULL == skb->pgm_opt_fragment, FALSE); - } -/* truesize */ - pgm_return_val_if_fail (skb->truesize >= sizeof(struct pgm_sk_buff_t*) + skb->len, FALSE); - pgm_return_val_if_fail (skb->truesize == ((const char*)skb->end - (const char*)skb), FALSE); -/* users */ - pgm_return_val_if_fail (pgm_atomic_read32 (&skb->users) > 0, FALSE); - return TRUE; -} -#endif /* SKB_DEBUG */ - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/slist.c b/3rdparty/openpgm-svn-r1085/pgm/slist.c deleted file mode 100644 index 9ba68ea..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/slist.c +++ /dev/null @@ -1,166 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable singly-linked list. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - - -//#define SLIST_DEBUG - -pgm_slist_t* -pgm_slist_append ( - pgm_slist_t* restrict list, - void* restrict data - ) -{ - pgm_slist_t* new_list; - pgm_slist_t* last; - - new_list = pgm_new (pgm_slist_t, 1); - new_list->data = data; - new_list->next = NULL; - - if (list) - { - last = pgm_slist_last (list); - last->next = new_list; - return list; - } - else - return new_list; -} - -pgm_slist_t* -pgm_slist_prepend ( - pgm_slist_t* restrict list, - void* restrict data - ) -{ - pgm_slist_t *new_list; - - new_list = pgm_new (pgm_slist_t, 1); - new_list->data = data; - new_list->next = list; - - return new_list; -} - -pgm_slist_t* -pgm_slist_prepend_link ( - pgm_slist_t* restrict list, - pgm_slist_t* restrict link_ - ) -{ - pgm_slist_t *new_list; - - new_list = link_; - new_list->next = list; - - return new_list; -} - -pgm_slist_t* -pgm_slist_remove ( - pgm_slist_t* restrict list, - const void* restrict data - ) -{ - pgm_slist_t *tmp = list, *prev = NULL; - - while (tmp) - { - if (tmp->data == data) - { - if (prev) - prev->next = tmp->next; - else - list = tmp->next; - pgm_free (tmp); - break; - } - prev = tmp; - tmp = prev->next; - } - - return list; -} - -pgm_slist_t* -pgm_slist_remove_first ( - pgm_slist_t* list - ) -{ - pgm_slist_t *tmp; - - if (PGM_LIKELY (NULL != list)) - { - tmp = list->next; - list->data = NULL; - list->next = NULL; - return tmp; - } - else - return NULL; -} - -void -pgm_slist_free ( - pgm_slist_t* list - ) -{ - while (list) - { - pgm_slist_t* current = list; - list = list->next; - pgm_free (current); - } -} - -pgm_slist_t* -pgm_slist_last ( - pgm_slist_t* list - ) -{ - if (PGM_LIKELY (NULL != list)) - { - while (list->next) - list = list->next; - } - - return list; -} - -unsigned -pgm_slist_length ( - pgm_slist_t* list - ) -{ - unsigned length = 0; - - while (list) - { - length++; - list = list->next; - } - - return length; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/snmp.c b/3rdparty/openpgm-svn-r1085/pgm/snmp.c deleted file mode 100644 index 5673878..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/snmp.c +++ /dev/null @@ -1,222 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * SNMP agent, single session. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include - -#include -#include -#include - -#include "pgm/snmp.h" -#include "impl/pgmMIB.h" - - -/* globals */ - -bool pgm_agentx_subagent = TRUE; -char* pgm_agentx_socket = NULL; -char* pgm_snmp_appname = "PGM"; - -/* locals */ - -#ifndef _WIN32 -static pthread_t snmp_thread; -static void* snmp_routine (void*); -#else -static HANDLE snmp_thread; -static unsigned __stdcall snmp_routine (void*); -#endif -static pgm_notify_t snmp_notify = PGM_NOTIFY_INIT; -static volatile uint32_t snmp_ref_count = 0; - - -/* Calling application needs to redirect SNMP logging before prior to this - * function. - */ - -bool -pgm_snmp_init ( - pgm_error_t** error - ) -{ - if (pgm_atomic_exchange_and_add32 (&snmp_ref_count, 1) > 0) - return TRUE; - - if (pgm_agentx_subagent) - { - pgm_minor (_("Configuring as SNMP AgentX sub-agent.")); - if (pgm_agentx_socket) - { - pgm_minor (_("Using AgentX socket %s."), pgm_agentx_socket); - netsnmp_ds_set_string (NETSNMP_DS_APPLICATION_ID, - NETSNMP_DS_AGENT_X_SOCKET, - pgm_agentx_socket); - } - netsnmp_ds_set_boolean (NETSNMP_DS_APPLICATION_ID, - NETSNMP_DS_AGENT_ROLE, - TRUE); - } - - pgm_minor (_("Initialising SNMP agent.")); - if (0 != init_agent (pgm_snmp_appname)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SNMP, - PGM_ERROR_FAILED, - _("Initialise SNMP agent: see SNMP log for further details.")); - goto err_cleanup; - } - - if (!pgm_mib_init (error)) { - goto err_cleanup; - } - -/* read config and parse mib */ - pgm_minor (_("Initialising SNMP.")); - init_snmp (pgm_snmp_appname); - - if (!pgm_agentx_subagent) - { - pgm_minor (_("Connecting to SNMP master agent.")); - if (0 != init_master_agent ()) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SNMP, - PGM_ERROR_FAILED, - _("Initialise SNMP master agent: see SNMP log for further details.")); - snmp_shutdown (pgm_snmp_appname); - goto err_cleanup; - } - } - -/* create notification channel */ - if (0 != pgm_notify_init (&snmp_notify)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SNMP, - pgm_error_from_errno (errno), - _("Creating SNMP notification channel: %s"), - strerror (errno)); - snmp_shutdown (pgm_snmp_appname); - goto err_cleanup; - } - -/* spawn thread to handle SNMP requests */ -#ifndef _WIN32 - const int status = pthread_create (&snmp_thread, NULL, &snmp_routine, NULL); - if (0 != status) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SNMP, - pgm_error_from_errno (errno), - _("Creating SNMP thread: %s"), - strerror (errno)); - snmp_shutdown (pgm_snmp_appname); - goto err_cleanup; - } -#else - snmp_thread = (HANDLE)_beginthreadex (NULL, 0, &snmp_routine, NULL, 0, NULL); - const int save_errno = errno; - if (0 == snmp_thread) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SNMP, - pgm_error_from_errno (save_errno), - _("Creating SNMP thread: %s"), - strerror (save_errno)); - snmp_shutdown (pgm_snmp_appname); - goto err_cleanup; - } -#endif /* _WIN32 */ - return TRUE; -err_cleanup: - if (pgm_notify_is_valid (&snmp_notify)) { - pgm_notify_destroy (&snmp_notify); - } - pgm_atomic_dec32 (&snmp_ref_count); - return FALSE; -} - -/* Terminate SNMP thread and free resources. - */ - -bool -pgm_snmp_shutdown (void) -{ - pgm_return_val_if_fail (pgm_atomic_read32 (&snmp_ref_count) > 0, FALSE); - - if (pgm_atomic_exchange_and_add32 (&snmp_ref_count, (uint32_t)-1) != 1) - return TRUE; - - pgm_notify_send (&snmp_notify); -#ifndef _WIN32 - pthread_join (snmp_thread, NULL); -#else - CloseHandle (snmp_thread); -#endif - pgm_notify_destroy (&snmp_notify); - snmp_shutdown (pgm_snmp_appname); - return TRUE; -} - -/* Thread routine for processing SNMP requests - */ - -static -#ifndef _WIN32 -void* -#else -unsigned -__stdcall -#endif -snmp_routine ( - PGM_GNUC_UNUSED void* arg - ) -{ - const int notify_fd = pgm_notify_get_fd (&snmp_notify); - - for (;;) - { - int fds = 0, block = 1; - fd_set fdset; - struct timeval timeout; - - FD_ZERO(&fdset); - snmp_select_info (&fds, &fdset, &timeout, &block); - FD_SET(notify_fd, &fdset); - if (notify_fd+1 > fds) - fds = notify_fd+1; - fds = select (fds, &fdset, NULL, NULL, block ? NULL : &timeout); - if (FD_ISSET(notify_fd, &fdset)) - break; - if (fds) - snmp_read (&fdset); - else - snmp_timeout(); - } - -/* cleanup */ -#ifndef _WIN32 - return NULL; -#else - _endthread(); - return 0; -#endif /* WIN32 */ -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/snmp_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/snmp_unittest.c deleted file mode 100644 index 9005a82..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/snmp_unittest.c +++ /dev/null @@ -1,184 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for SNMP. - * - * Copyright (c) 2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include - -#include "pgm/transport.h" - - -/* mock state */ -static const guint mock_pgm_major_version = 0; -static const guint mock_pgm_minor_version = 0; -static const guint mock_pgm_micro_version = 0; -static GStaticRWLock mock_pgm_transport_list_lock = G_STATIC_RW_LOCK_INIT; -static GSList* mock_pgm_transport_list = NULL; - -static -gboolean -mock_pgm_tsi_equal ( - gconstpointer v1, - gconstpointer v2 - ) -{ - return memcmp (v1, v2, sizeof(struct pgm_tsi_t)) == 0; -} - -static -void -mock_pgm_time_since_epoch ( - pgm_time_t* pgm_time_t_time, - time_t* time_t_time - ) -{ - *time_t_time = pgm_to_secs (*pgm_time_t_time + 0); -} - -static -gboolean -mock_pgm_mib_init ( - GError** error - ) -{ - return TRUE; -} - -/* mock functions for external references */ - -#define pgm_major_version mock_pgm_major_version -#define pgm_minor_version mock_pgm_minor_version -#define pgm_micro_version mock_pgm_micro_version -#define pgm_transport_list_lock mock_pgm_transport_list_lock -#define pgm_transport_list mock_pgm_transport_list -#define pgm_tsi_equal mock_pgm_tsi_equal -#define pgm_time_since_epoch mock_pgm_time_since_epoch -#define pgm_mib_init mock_pgm_mib_init - - -#define SNMP_DEBUG -#include "snmp.c" - - -/* target: - * gboolean - * pgm_snmp_init ( - * GError** error - * ) - */ - -START_TEST (test_init_pass_001) -{ - GError* err = NULL; - fail_unless (TRUE == pgm_snmp_init (&err)); - fail_unless (NULL == err); -} -END_TEST - -/* duplicate servers */ -START_TEST (test_init_fail_001) -{ - GError* err = NULL; - fail_unless (TRUE == pgm_snmp_init (&err)); - fail_unless (FALSE == pgm_snmp_init (&err)); -} -END_TEST - -/* target: - * gboolean - * pgm_snmp_shutdown (void) - */ - -START_TEST (test_shutdown_pass_001) -{ - GError* err = NULL; - fail_unless (TRUE == pgm_snmp_init (&err)); - fail_unless (NULL == err); - fail_unless (TRUE == pgm_snmp_shutdown ()); -} -END_TEST - -/* repeatability - */ -START_TEST (test_shutdown_pass_002) -{ - GError* err = NULL; - fail_unless (TRUE == pgm_snmp_init (&err)); - fail_unless (NULL == err); - fail_unless (TRUE == pgm_snmp_shutdown ()); - fail_unless (TRUE == pgm_snmp_init (&err)); - fail_unless (NULL == err); - fail_unless (TRUE == pgm_snmp_shutdown ()); -} -END_TEST - -/* no running server */ -START_TEST (test_shutdown_fail_001) -{ - fail_unless (FALSE == pgm_snmp_shutdown ()); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_init = tcase_create ("init"); - suite_add_tcase (s, tc_init); - tcase_add_test (tc_init, test_init_pass_001); - tcase_add_test (tc_init, test_init_fail_001); - - TCase* tc_shutdown = tcase_create ("shutdown"); - suite_add_tcase (s, tc_shutdown); - tcase_add_test (tc_shutdown, test_shutdown_pass_001); - tcase_add_test (tc_shutdown, test_shutdown_pass_002); - tcase_add_test (tc_shutdown, test_shutdown_fail_001); - - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/sockaddr.c b/3rdparty/openpgm-svn-r1085/pgm/sockaddr.c deleted file mode 100644 index 9b8dcb9..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/sockaddr.c +++ /dev/null @@ -1,1193 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * struct sockaddr functions independent of in or in6. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#ifndef _WIN32 -# include -# include -#endif -#include - - -/* FreeBSD */ -#ifndef IPV6_ADD_MEMBERSHIP -# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP -# define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP -#endif -/* OpenSolaris differences */ -#ifndef MCAST_MSFILTER -# include -#endif -#ifndef SOL_IP -# define SOL_IP IPPROTO_IP -#endif -#ifndef SOL_IPV6 -# define SOL_IPV6 IPPROTO_IPV6 -#endif -#ifndef IP_MAX_MEMBERSHIPS -# define IP_MAX_MEMBERSHIPS 20 -#endif - - -sa_family_t -pgm_sockaddr_family ( - const struct sockaddr* sa - ) -{ - return sa->sa_family; -} - -uint16_t -pgm_sockaddr_port ( - const struct sockaddr* sa - ) -{ - uint16_t sa_port; - switch (sa->sa_family) { - case AF_INET: { - struct sockaddr_in s4; - memcpy (&s4, sa, sizeof(s4)); - sa_port = s4.sin_port; - break; - } - - case AF_INET6: { - struct sockaddr_in6 s6; - memcpy (&s6, sa, sizeof(s6)); - sa_port = s6.sin6_port; - break; - } - - default: - sa_port = 0; - break; - } - return sa_port; -} - -socklen_t -pgm_sockaddr_len ( - const struct sockaddr* sa - ) -{ - socklen_t sa_len; - switch (sa->sa_family) { - case AF_INET: sa_len = sizeof(struct sockaddr_in); break; - case AF_INET6: sa_len = sizeof(struct sockaddr_in6); break; - default: sa_len = 0; break; - } - return sa_len; -} - -socklen_t -pgm_sockaddr_storage_len ( - const struct sockaddr_storage* ss - ) -{ - socklen_t ss_len; - switch (ss->ss_family) { - case AF_INET: ss_len = sizeof(struct sockaddr_in); break; - case AF_INET6: ss_len = sizeof(struct sockaddr_in6); break; - default: ss_len = 0; break; - } - return ss_len; -} - -uint32_t -pgm_sockaddr_scope_id ( - const struct sockaddr* sa - ) -{ - uint32_t scope_id; - if (AF_INET6 == sa->sa_family) { - struct sockaddr_in6 s6; - memcpy (&s6, sa, sizeof(s6)); - scope_id = s6.sin6_scope_id; - } else - scope_id = 0; - return scope_id; -} - -int -pgm_sockaddr_ntop ( - const struct sockaddr* restrict sa, - char* restrict host, - size_t hostlen - ) -{ - return getnameinfo (sa, pgm_sockaddr_len (sa), - host, hostlen, - NULL, 0, - NI_NUMERICHOST); -} - -int -pgm_sockaddr_pton ( - const char* restrict src, - struct sockaddr* restrict dst /* will error on wrong size */ - ) -{ - struct addrinfo hints = { - .ai_family = AF_UNSPEC, - .ai_socktype = SOCK_STREAM, /* not really */ - .ai_protocol = IPPROTO_TCP, /* not really */ - .ai_flags = AI_NUMERICHOST - }, *result = NULL; - const int status = getaddrinfo (src, NULL, &hints, &result); - if (PGM_LIKELY(0 == status)) { - memcpy (dst, result->ai_addr, result->ai_addrlen); - freeaddrinfo (result); - return 1; - } - return 0; -} - -/* returns tri-state value: 1 if sa is multicast, 0 if sa is not multicast, -1 on error - */ - -int -pgm_sockaddr_is_addr_multicast ( - const struct sockaddr* sa - ) -{ - int retval; - - switch (sa->sa_family) { - case AF_INET: { - struct sockaddr_in s4; - memcpy (&s4, sa, sizeof(s4)); - retval = IN_MULTICAST(ntohl( s4.sin_addr.s_addr )); - break; - } - - case AF_INET6: { - struct sockaddr_in6 s6; - memcpy (&s6, sa, sizeof(s6)); - retval = IN6_IS_ADDR_MULTICAST( &s6.sin6_addr ); - break; - } - - default: - retval = -1; - break; - } - return retval; -} - -/* returns 1 if sa is unspecified, 0 if specified. - */ - -int -pgm_sockaddr_is_addr_unspecified ( - const struct sockaddr* sa - ) -{ - int retval; - - switch (sa->sa_family) { - case AF_INET: { - struct sockaddr_in s4; - memcpy (&s4, sa, sizeof(s4)); - retval = (INADDR_ANY == s4.sin_addr.s_addr); - break; - } - - case AF_INET6: { - struct sockaddr_in6 s6; - memcpy (&s6, sa, sizeof(s6)); - retval = IN6_IS_ADDR_UNSPECIFIED( &s6.sin6_addr ); - break; - } - - default: - retval = -1; - break; - } - return retval; -} - -int -pgm_sockaddr_cmp ( - const struct sockaddr* restrict sa1, - const struct sockaddr* restrict sa2 - ) -{ - int retval = 0; - - if (sa1->sa_family != sa2->sa_family) - retval = sa1->sa_family < sa2->sa_family ? -1 : 1; - else { - switch (sa1->sa_family) { - case AF_INET: { - struct sockaddr_in sa1_in, sa2_in; - memcpy (&sa1_in, sa1, sizeof(sa1_in)); - memcpy (&sa2_in, sa2, sizeof(sa2_in)); - if (sa1_in.sin_addr.s_addr != sa2_in.sin_addr.s_addr) - retval = sa1_in.sin_addr.s_addr < sa2_in.sin_addr.s_addr ? -1 : 1; - break; - } - -/* IN6_ARE_ADDR_EQUAL(a,b) only returns true or false */ - case AF_INET6: { - struct sockaddr_in6 sa1_in6, sa2_in6; - memcpy (&sa1_in6, sa1, sizeof(sa1_in6)); - memcpy (&sa2_in6, sa2, sizeof(sa2_in6)); - retval = memcmp (&sa1_in6.sin6_addr, &sa2_in6.sin6_addr, sizeof(struct in6_addr)); - if (0 == retval && sa1_in6.sin6_scope_id != sa2_in6.sin6_scope_id) - retval = sa1_in6.sin6_scope_id < sa2_in6.sin6_scope_id ? -1 : 1; - break; - } - - default: - break; - } - } - return retval; -} - -/* IP header included with data. - * - * If no error occurs, pgm_sockaddr_hdrincl returns zero. Otherwise, a value - * of PGM_SOCKET_ERROR is returned, and a specific error code can be retrieved - * by calling pgm_sock_errno(). - */ - -int -pgm_sockaddr_hdrincl ( - const int s, - const sa_family_t sa_family, - const bool v - ) -{ - int retval = PGM_SOCKET_ERROR; - - switch (sa_family) { - case AF_INET: { -#ifndef _WIN32 -/* Solaris:ip(7P) Mentioned but not detailed. - * - * Linux:ip(7) "A boolean integer flag is zero when it is false, otherwise - * true. If enabled, the user supplies an IP header in front of the user - * data." Mentions only send-side, nothing about receive-side. - * Linux:raw(7) "For receiving the IP header is always included in the packet." - * - * FreeBSD,OS X:IP(4) provided by example "int hincl = 1;" - * - * Stevens: "IP_HDRINCL has datatype int." - */ - const int optval = v ? 1 : 0; -#else - const DWORD optval = v ? 1 : 0; -#endif - retval = setsockopt (s, IPPROTO_IP, IP_HDRINCL, (const char*)&optval, sizeof(optval)); - break; - } - - case AF_INET6: /* method only exists on Win32, just ignore */ - retval = 0; - break; - - default: break; - } - return retval; -} - -/* Return destination IP address. - * - * If no error occurs, pgm_sockaddr_pktinfo returns zero. Otherwise, a value - * of PGM_SOCKET_ERROR is returned, and a specific error code can be retrieved - * by calling pgm_sock_errno(). - */ - -int -pgm_sockaddr_pktinfo ( - const int s, - const sa_family_t sa_family, - const bool v - ) -{ - int retval = PGM_SOCKET_ERROR; -#ifndef _WIN32 -/* Solaris:ip(7P) "The following options take in_pktinfo_t as the parameter" - * Completely different, although ip6(7P) is a little better, "The following - * options are boolean switches controlling the reception of ancillary data" - * - * Linux:ip(7) "A boolean integer flag is zero when it is false, otherwise - * true. The argument is a flag that tells the socket whether the IP_PKTINFO - * message should be passed or not." - * Linux:ipv6(7) Not listed, however IPV6_PKTINFO is with "Argument is a pointer - * to a boolean value in an integer." - * - * Absent from FreeBSD & OS X, suggested replacement IP_RECVDSTADDR. - * OS X:IP6(4) "IPV6_PKTINFO int *" - * - * Stevens: "IP_RECVDSTADDR has datatype int." - */ - const int optval = v ? 1 : 0; -#else - const DWORD optval = v ? 1 : 0; -#endif - - switch (sa_family) { - case AF_INET: -#ifdef IP_RECVDSTADDR - retval = setsockopt (s, IPPROTO_IP, IP_RECVDSTADDR, (const char*)&optval, sizeof(optval)); -#else - retval = setsockopt (s, IPPROTO_IP, IP_PKTINFO, (const char*)&optval, sizeof(optval)); -#endif - break; - - case AF_INET6: -#ifdef IPV6_RECVPKTINFO - retval = setsockopt (s, IPPROTO_IPV6, IPV6_RECVPKTINFO, (const char*)&optval, sizeof(optval)); -#else - retval = setsockopt (s, IPPROTO_IPV6, IPV6_PKTINFO, (const char*)&optval, sizeof(optval)); -#endif - break; - - default: break; - } - return retval; -} - -/* Set IP Router Alert option for all outgoing packets. - * - * If no error occurs, pgm_sockaddr_router_alert returns zero. Otherwise, a - * value of PGM_SOCKET_ERROR is returned, and a specific error code can be - * retrieved by calling pgm_sock_errno(). - */ - -int -pgm_sockaddr_router_alert ( - const int s, - const sa_family_t sa_family, - const bool v - ) -{ - int retval = PGM_SOCKET_ERROR; -#ifdef CONFIG_IP_ROUTER_ALERT -/* Linux:ip(7) "A boolean integer flag is zero when it is false, otherwise - * true. Expects an integer flag." - * Linux:ipv6(7) "Argument is a pointer to an integer." - * - * Sent on special queue to rsvpd on Linux and so best avoided. - */ - const int optval = v ? 1 : 0; - - switch (sa_family) { - case AF_INET: - retval = setsockopt (s, IPPROTO_IP, IP_ROUTER_ALERT, (const char*)&optval, sizeof(optval)); - break; - - case AF_INET6: - retval = setsockopt (s, IPPROTO_IPV6, IPV6_ROUTER_ALERT, (const char*)&optval, sizeof(optval)); - break; - - default: break; - } -#else -# if defined(CONFIG_HAVE_IPOPTION) -/* NB: struct ipoption is not very portable and requires a lot of additional headers */ - const struct ipoption router_alert = { - .ipopt_dst = 0, - .ipopt_list = { PGM_IPOPT_RA, 0x04, 0x00, 0x00 } - }; - const int optlen = v ? sizeof(router_alert) : 0; -# else -/* manually set the IP option */ - const int ipopt_ra = (PGM_IPOPT_RA << 24) | (0x04 << 16); - const int router_alert = htonl (ipopt_ra); - const int optlen = v ? sizeof(router_alert) : 0; -# endif - - switch (sa_family) { - case AF_INET: -/* Linux:ip(7) "The maximum option size for IPv4 is 40 bytes." - */ - retval = setsockopt (s, IPPROTO_IP, IP_OPTIONS, (const char*)&router_alert, optlen); -retval = 0; - break; - - default: break; - } -#endif - return retval; -} - -/* Type-of-service and precedence. - * - * If no error occurs, pgm_sockaddr_tos returns zero. Otherwise, a value of - * PGM_SOCKET_ERROR is returned, and a specific error code can be retrieved by - * calling pgm_sock_errno(). - */ - -int -pgm_sockaddr_tos ( - const int s, - const sa_family_t sa_family, - const int tos - ) -{ - int retval = PGM_SOCKET_ERROR; - - switch (sa_family) { - case AF_INET: { -#ifndef _WIN32 -/* Solaris:ip(7P) "This option takes an integer argument as its input value." - * - * Linux:ip(7) "TOS is a byte." - * - * FreeBSD,OS X:IP(4) provided by example "int tos = IPTOS_LOWDELAY;" - * - * Stevens: "IP_TOS has datatype int." - */ - const int optval = tos; -#else -/* IP_TOS only works on Win32 with system override: - * http://support.microsoft.com/kb/248611 - * TODO: Implement GQoS (IPv4 only), qWAVE QOS is Vista+ only - */ - const DWORD optval = tos; -#endif - retval = setsockopt (s, IPPROTO_IP, IP_TOS, (const char*)&optval, sizeof(optval)); - break; - } - - case AF_INET6: /* TRAFFIC_CLASS not implemented */ - break; - - default: break; - } - return retval; -} - -/* Join multicast group. - * NB: IPV6_JOIN_GROUP == IPV6_ADD_MEMBERSHIP - * - * If no error occurs, pgm_sockaddr_join_group returns zero. Otherwise, a - * value of PGM_SOCKET_ERROR is returned, and a specific error code can be - * retrieved by calling pgm_sock_errno(). - */ - -int -pgm_sockaddr_join_group ( - const int s, - const sa_family_t sa_family, - const struct group_req* gr - ) -{ - int retval = PGM_SOCKET_ERROR; -#ifdef CONFIG_HAVE_MCAST_JOIN -/* Solaris:ip(7P) "The following options take a struct ip_mreq_source as the - * parameter." Presumably with source field zeroed out. - * Solaris:ip6(7P) "Takes a struct group_req as the parameter." - * Different type for each family, however group_req is protocol-independent. - * - * Stevens: "MCAST_JOIN_GROUP has datatype group_req{}." - * - * RFC3678: Argument type struct group_req - */ - const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6; - retval = setsockopt (s, recv_level, MCAST_JOIN_GROUP, gr, sizeof(struct group_req)); -#else - switch (sa_family) { - case AF_INET: { -/* Solaris:ip(7P) Just mentions "Join a multicast group." - * No further details provided. - * - * Linux:ip(7) "Argument is an ip_mreqn structure. For compatibility, the old - * ip_mreq structure (present since Linux 1.2) is still supported." - * - * FreeBSD,OS X:IP(4) provided by example "struct ip_mreq mreq;" - * - * Windows can optionally abuse imt_interface to be 0.0.0. - * - * Stevens: "IP_ADD_MEMBERSHIP has datatype ip_mreq{}." - * - * RFC3678: Argument type struct ip_mreq - */ -#ifdef CONFIG_HAVE_IP_MREQN - struct ip_mreqn mreqn; - struct sockaddr_in ifaddr; - memset (&mreqn, 0, sizeof(mreqn)); - mreqn.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gr->gr_group)->sin_addr.s_addr; - if (!pgm_if_indextoaddr (gr->gr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL)) - return -1; - mreqn.imr_address.s_addr = ifaddr.sin_addr.s_addr; - mreqn.imr_ifindex = gr->gr_interface; - retval = setsockopt (s, SOL_IP, IP_ADD_MEMBERSHIP, (const char*)&mreqn, sizeof(mreqn)); -#else - struct ip_mreq mreq; - struct sockaddr_in ifaddr; - memset (&mreq, 0, sizeof(mreq)); - mreq.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gr->gr_group)->sin_addr.s_addr; - if (!pgm_if_indextoaddr (gr->gr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL)) - return -1; - mreq.imr_interface.s_addr = ifaddr.sin_addr.s_addr; - retval = setsockopt (s, SOL_IP, IP_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)); -#endif /* !CONFIG_HAVE_IP_MREQN */ - break; - } - - case AF_INET6: { -/* Solaris:ip6(7P) "Takes a struct ipv6_mreq as the parameter;" - * - * Linux:ipv6(7) "Argument is a pointer to a struct ipv6_mreq structure." - * - * OS X:IP6(4) "IPV6_JOIN_GROUP struct ipv6_mreq *" - * - * Stevens: "IPV6_JOIN_GROUP has datatype ipv6_mreq{}." - */ - struct ipv6_mreq mreq6; - memset (&mreq6, 0, sizeof(mreq6)); - mreq6.ipv6mr_multiaddr = ((const struct sockaddr_in6*)&gr->gr_group)->sin6_addr; - mreq6.ipv6mr_interface = gr->gr_interface; - retval = setsockopt (s, SOL_IPV6, IPV6_ADD_MEMBERSHIP, (const char*)&mreq6, sizeof(mreq6)); - break; - } - - default: break; - } -#endif /* CONFIG_HAVE_MCAST_JOIN */ - return retval; -} - -/* leave a joined group - */ - -int -pgm_sockaddr_leave_group ( - const int s, - const sa_family_t sa_family, - const struct group_req* gr - ) -{ - int retval = PGM_SOCKET_ERROR; -#ifdef CONFIG_HAVE_MCAST_JOIN - const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6; - retval = setsockopt (s, recv_level, MCAST_LEAVE_GROUP, gr, sizeof(struct group_req)); -#else - switch (sa_family) { - case AF_INET: { -#ifdef CONFIG_HAVE_IP_MREQN - struct ip_mreqn mreqn; - struct sockaddr_in ifaddr; - memset (&mreqn, 0, sizeof(mreqn)); - mreqn.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gr->gr_group)->sin_addr.s_addr; - if (!pgm_if_indextoaddr (gr->gr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL)) - return -1; - mreqn.imr_address.s_addr = ifaddr.sin_addr.s_addr; - mreqn.imr_ifindex = gr->gr_interface; - retval = setsockopt (s, SOL_IP, IP_DROP_MEMBERSHIP, (const char*)&mreqn, sizeof(mreqn)); -#else - struct ip_mreq mreq; - struct sockaddr_in ifaddr; - memset (&mreq, 0, sizeof(mreq)); - mreq.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gr->gr_group)->sin_addr.s_addr; - if (!pgm_if_indextoaddr (gr->gr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL)) - return -1; - mreq.imr_interface.s_addr = ifaddr.sin_addr.s_addr; - retval = setsockopt (s, SOL_IP, IP_DROP_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)); -#endif /* !CONFIG_HAVE_IP_MREQN */ - break; - } - - case AF_INET6: { - struct ipv6_mreq mreq6; - memset (&mreq6, 0, sizeof(mreq6)); - mreq6.ipv6mr_multiaddr = ((const struct sockaddr_in6*)&gr->gr_group)->sin6_addr; - mreq6.ipv6mr_interface = gr->gr_interface; - retval = setsockopt (s, SOL_IPV6, IPV6_DROP_MEMBERSHIP, (const char*)&mreq6, sizeof(mreq6)); - break; - } - - default: break; - } -#endif /* CONFIG_HAVE_MCAST_JOIN */ - return retval; -} - -/* block either at the NIC or kernel, packets from a particular source - */ - -int -pgm_sockaddr_block_source ( - const int s, - const sa_family_t sa_family, - const struct group_source_req* gsr - ) -{ - int retval = PGM_SOCKET_ERROR; -#ifdef CONFIG_HAVE_MCAST_JOIN - const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6; - retval = setsockopt (s, recv_level, MCAST_BLOCK_SOURCE, gsr, sizeof(struct group_source_req)); -#elif defined(IP_BLOCK_SOURCE) - switch (sa_family) { - case AF_INET: { - struct ip_mreq_source mreqs; - struct sockaddr_in ifaddr; - memset (&mreqs, 0, sizeof(mreqs)); - mreqs.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_group)->sin_addr.s_addr; - mreqs.imr_sourceaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_source)->sin_addr.s_addr; - pgm_if_indextoaddr (gsr->gsr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL); - mreqs.imr_interface.s_addr = ifaddr.sin_addr.s_addr; - retval = setsockopt (s, SOL_IP, IP_BLOCK_SOURCE, (const char*)&mreqs, sizeof(mreqs)); - break; - } - - case AF_INET6: -/* No IPv6 API implemented, MCAST_BLOCK_SOURCE should be available instead. - */ - break; - - default: break; - } -#endif /* CONFIG_HAVE_MCAST_JOIN */ - return retval; -} - -/* unblock a blocked multicast source. - */ - -int -pgm_sockaddr_unblock_source ( - const int s, - const sa_family_t sa_family, - const struct group_source_req* gsr - ) -{ - int retval = PGM_SOCKET_ERROR; -#ifdef CONFIG_HAVE_MCAST_JOIN - const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6; - retval = setsockopt (s, recv_level, MCAST_UNBLOCK_SOURCE, gsr, sizeof(struct group_source_req)); -#elif defined(IP_UNBLOCK_SOURCE) - switch (sa_family) { - case AF_INET: { - struct ip_mreq_source mreqs; - struct sockaddr_in ifaddr; - memset (&mreqs, 0, sizeof(mreqs)); - mreqs.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_group)->sin_addr.s_addr; - mreqs.imr_sourceaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_source)->sin_addr.s_addr; - pgm_if_indextoaddr (gsr->gsr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL); - mreqs.imr_interface.s_addr = ifaddr.sin_addr.s_addr; - retval = setsockopt (s, SOL_IP, IP_UNBLOCK_SOURCE, (const char*)&mreqs, sizeof(mreqs)); - break; - } - - case AF_INET6: -/* No IPv6 API implemented, MCAST_UNBLOCK_SOURCE should be available instead. - */ - break; - - default: break; - } -#endif /* CONFIG_HAVE_MCAST_JOIN */ - return retval; -} - -/* Join source-specific multicast. - * NB: Silently reverts to ASM if SSM not supported. - * - * If no error occurs, pgm_sockaddr_join_source_group returns zero. - * Otherwise, a value of PGM_SOCKET_ERROR is returned, and a specific error - * code can be retrieved by calling pgm_sock_errno(). - */ - -int -pgm_sockaddr_join_source_group ( - const int s, - const sa_family_t sa_family, - const struct group_source_req* gsr - ) -{ - int retval = PGM_SOCKET_ERROR; -#ifdef CONFIG_HAVE_MCAST_JOIN -/* Solaris:ip(7P) "The following options take a struct ip_mreq_source as the - * parameter." - * Solaris:ip6(7P) "Takes a struct group_source_req as the parameter." - * Different type for each family, however group_source_req is protocol- - * independent. - * - * Stevens: "MCAST_JOIN_SOURCE_GROUP has datatype group_source_req{}." - * - * RFC3678: Argument type struct group_source_req - */ - const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6; - retval = setsockopt (s, recv_level, MCAST_JOIN_SOURCE_GROUP, gsr, sizeof(struct group_source_req)); -#elif defined(IP_ADD_SOURCE_MEMBERSHIP) - switch (sa_family) { - case AF_INET: { -/* Solaris:ip(7P) "The following options take a struct ip_mreq as the - * parameter." Incorrect literature wrt RFC. - * - * Linux:ip(7) absent. - * - * OS X:IP(4) absent. - * - * Stevens: "IP_ADD_SOURCE_MEMBERSHIP has datatype ip_mreq_source{}." - * - * RFC3678: Argument type struct ip_mreq_source - */ - struct ip_mreq_source mreqs; - struct sockaddr_in ifaddr; - memset (&mreqs, 0, sizeof(mreqs)); - mreqs.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_group)->sin_addr.s_addr; - mreqs.imr_sourceaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_source)->sin_addr.s_addr; - pgm_if_indextoaddr (gsr->gsr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL); - mreqs.imr_interface.s_addr = ifaddr.sin_addr.s_addr; - retval = setsockopt (s, SOL_IP, IP_ADD_SOURCE_MEMBERSHIP, (const char*)&mreqs, sizeof(mreqs)); - break; - } - - case AF_INET6: -/* No IPv6 API implemented, MCAST_JOIN_SOURCE_GROUP should be available instead. - */ - retval = pgm_sockaddr_join_group (s, sa_family, (const struct group_req*)gsr); - break; - - default: break; - } -#else - retval = pgm_sockaddr_join_group (s, sa_family, (const struct group_req*)gsr); -#endif /* CONFIG_HAVE_MCAST_JOIN */ - return retval; -} - -/* drop a SSM source - */ - -int -pgm_sockaddr_leave_source_group ( - const int s, - const sa_family_t sa_family, - const struct group_source_req* gsr - ) -{ - int retval = PGM_SOCKET_ERROR; -#ifdef CONFIG_HAVE_MCAST_JOIN - const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6; - retval = setsockopt (s, recv_level, MCAST_LEAVE_SOURCE_GROUP, gsr, sizeof(struct group_source_req)); -#elif defined(IP_ADD_SOURCE_MEMBERSHIP) - switch (sa_family) { - case AF_INET: { - struct ip_mreq_source mreqs; - struct sockaddr_in ifaddr; - memset (&mreqs, 0, sizeof(mreqs)); - mreqs.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_group)->sin_addr.s_addr; - mreqs.imr_sourceaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_source)->sin_addr.s_addr; - pgm_if_indextoaddr (gsr->gsr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL); - mreqs.imr_interface.s_addr = ifaddr.sin_addr.s_addr; - retval = setsockopt (s, SOL_IP, IP_DROP_SOURCE_MEMBERSHIP, (const char*)&mreqs, sizeof(mreqs)); - break; - } - - case AF_INET6: -/* No IPv6 API implemented, MCAST_LEAVE_SOURCE_GROUP should be available instead. - */ - retval = pgm_sockaddr_leave_group (s, sa_family, (const struct group_req*)gsr); - break; - - default: break; - } -#else - retval = pgm_sockaddr_leave_group (s, sa_family, (const struct group_req*)gsr); -#endif /* CONFIG_HAVE_MCAST_JOIN */ - return retval; -} - -#if defined(MCAST_MSFILTER) || defined(SIOCSMSFILTER) -/* Batch block and unblock sources. - */ - -int -pgm_sockaddr_msfilter ( - const int s, - const sa_family_t sa_family, - const struct group_filter* gf_list - ) -{ - int retval = PGM_SOCKET_ERROR; -#ifdef MCAST_MSFILTER - const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6; - const socklen_t len = GROUP_FILTER_SIZE(gf_list->gf_numsrc); - retval = setsockopt (s, recv_level, MCAST_MSFILTER, (const char*)gf_list, len); -#elif defined(SIOCSMSFILTER) - retval = ioctl (s, SIOCSMSFILTER, (const char*)gf_list); -#endif - return retval; -} -#endif /* MCAST_MSFILTER || SIOCSMSFILTER */ - -/* Specify outgoing interface. - * - * If no error occurs, pgm_sockaddr_multicast_if returns zero. Otherwise, a - * value of PGM_SOCKET_ERROR is returned, and a specific error code can be - * retrieved by calling pgm_sock_errno(). - */ - -int -pgm_sockaddr_multicast_if ( - int s, - const struct sockaddr* address, - unsigned ifindex - ) -{ - int retval = PGM_SOCKET_ERROR; - - switch (address->sa_family) { - case AF_INET: { -/* Solaris:ip(7P) "This option takes a struct in_addr as an argument, and it - * selects that interface for outgoing IP multicast packets." - * - * Linux:ip(7) "Argument is an ip_mreqn or ip_mreq structure similar to - * IP_ADD_MEMBERSHIP." - * - * OS X:IP(4) provided by example "struct in_addr addr;" - * - * Stevens: "IP_MULTICAST_IF has datatype struct in_addr{}." - */ - struct sockaddr_in s4; - memcpy (&s4, address, sizeof(s4)); - retval = setsockopt (s, IPPROTO_IP, IP_MULTICAST_IF, (const char*)&s4.sin_addr, sizeof(s4.sin_addr)); - break; - } - - case AF_INET6: { -#ifndef _WIN32 -/* Solaris:ip6(7P) "This option takes an integer as an argument; the integer - * is the interface index of the selected interface." - * - * Linux:ipv6(7) "The argument is a pointer to an interface index (see - * netdevice(7)) in an integer." - * - * OS X:IP6(4) "IPV6_MULTICAST_IF u_int *" - * - * Stevens: "IPV6_MULTICAST_IF has datatype u_int." - */ - const unsigned int optval = ifindex; -#else - const DWORD optval = ifindex; -#endif - retval = setsockopt (s, IPPROTO_IPV6, IPV6_MULTICAST_IF, (const char*)&optval, sizeof(optval)); - break; - } - - default: break; - } - return retval; -} - -/* Specify multicast loop, other applications on the same host may receive - * outgoing packets. This does not affect unicast packets such as NAKs. - * - * If no error occurs, pgm_sockaddr_multicast_loop returns zero. Otherwise, a - * value of PGM_SOCKET_ERROR is returned, and a specific error code can be - * retrieved by calling pgm_sock_errno(). - */ - -int -pgm_sockaddr_multicast_loop ( - const int s, - const sa_family_t sa_family, - const bool v - ) -{ - int retval = PGM_SOCKET_ERROR; - - switch (sa_family) { - case AF_INET: { -#ifndef _WIN32 -/* Solaris:ip(7P) "Setting the unsigned character argument to 0 causes the - * opposite behavior, meaning that when multiple zones are present, the - * datagrams are delivered to all zones except the sending zone." - * - * Linux:ip(7) "Sets or reads a boolean integer argument" - * - * OS X:IP(4) provided by example "u_char loop;" - * - * Stevens: "IP_MULTICAST_LOOP has datatype u_char." - */ - const unsigned char optval = v ? 1 : 0; -#else - const DWORD optval = v ? 1 : 0; -#endif - retval = setsockopt (s, IPPROTO_IP, IP_MULTICAST_LOOP, (const char*)&optval, sizeof(optval)); - break; - } - - case AF_INET6: { -#ifndef _WIN32 -/* Solaris:ip(7P) "Setting the unsigned character argument to 0 will cause the opposite behavior." - * - * Linux:ipv6(7) "Argument is a pointer to boolean." - * - * OS X:IP6(7) "IPV6_MULTICAST_LOOP u_int *" - * - * Stevens: "IPV6_MULTICAST_LOOP has datatype u_int." - */ - const unsigned int optval = v ? 1 : 0; -#else - const DWORD optval = v ? 1 : 0; -#endif - retval = setsockopt (s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const char*)&optval, sizeof(optval)); - break; - } - - default: break; - } - return retval; -} - -/* Specify TTL or outgoing hop limit. - * NB: Only affects multicast hops, unicast hop-limit is not changed. - * - * If no error occurs, pgm_sockaddr_multicast_hops returns zero. Otherwise, a - * value of PGM_SOCKET_ERROR is returned, and a specific error code can be - * retrieved by calling pgm_sock_errno(). - */ - -int -pgm_sockaddr_multicast_hops ( - const int s, - const sa_family_t sa_family, - const unsigned hops - ) -{ - int retval = PGM_SOCKET_ERROR; - - switch (sa_family) { - case AF_INET: { -#ifndef _WIN32 -/* Solaris:ip(7P) "This option takes an unsigned character as an argument." - * - * Linux:ip(7) "Argument is an integer." - * - * OS X:IP(4) provided by example for SOCK_DGRAM with IP_TTL: "int ttl = 60;", - * or for SOCK_RAW & SOCK_DGRAM with IP_MULTICAST_TTL: "u_char ttl;" - * - * Stevens: "IP_MULTICAST_TTL has datatype u_char." - */ - const unsigned char optval = hops; -#else - const DWORD optval = hops; -#endif - retval = setsockopt (s, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&optval, sizeof(optval)); - break; - } - - case AF_INET6: { -#ifndef _WIN32 -/* Solaris:ip6(7P) "This option takes an integer as an argument." - * - * Linux:ipv6(7) "Argument is a pointer to an integer." - * - * OS X:IP6(7) "IPV6_MULTICAST_HOPS int *" - * - * Stevens: "IPV6_MULTICAST_HOPS has datatype int." - */ - const int optval = hops; -#else - const DWORD optval = hops; -#endif - retval = setsockopt (s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char*)&optval, sizeof(optval)); - break; - } - - default: break; - } - return retval; -} - -void -pgm_sockaddr_nonblocking ( - const int s, - const bool v - ) -{ -#ifndef _WIN32 - int flags = fcntl (s, F_GETFL); - if (!v) flags &= ~O_NONBLOCK; - else flags |= O_NONBLOCK; - fcntl (s, F_SETFL, flags); -#else - u_long mode = v; - ioctlsocket (s, FIONBIO, &mode); -#endif -} - -/* Note that are sockaddr structure is not passed these functions inherently - * cannot support IPv6 Zone Indices and hence are rather limited for the - * link-local scope. - */ -const char* -pgm_inet_ntop ( - int af, - const void* restrict src, - char* restrict dst, - socklen_t size - ) -{ - pgm_assert (AF_INET == af || AF_INET6 == af); - pgm_assert (NULL != src); - pgm_assert (NULL != dst); - pgm_assert (size > 0); - - switch (af) { - case AF_INET: - { - struct sockaddr_in sin; - memset (&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_addr = *(const struct in_addr*)src; - getnameinfo ((struct sockaddr*)&sin, sizeof(sin), - dst, size, - NULL, 0, - NI_NUMERICHOST); - return dst; - } - case AF_INET6: - { - struct sockaddr_in6 sin6; - memset (&sin6, 0, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - sin6.sin6_addr = *(const struct in6_addr*)src; - getnameinfo ((struct sockaddr*)&sin6, sizeof(sin6), - dst, size, - NULL, 0, - NI_NUMERICHOST); - return dst; - } - } - - errno = EAFNOSUPPORT; - return NULL; -} - -int -pgm_inet_pton ( - int af, - const char* restrict src, - void* restrict dst - ) -{ - pgm_assert (AF_INET == af || AF_INET6 == af); - pgm_assert (NULL != src); - pgm_assert (NULL != dst); - - struct addrinfo hints = { - .ai_family = af, - .ai_socktype = SOCK_STREAM, /* not really */ - .ai_protocol = IPPROTO_TCP, /* not really */ - .ai_flags = AI_NUMERICHOST - }, *result = NULL; - - const int e = getaddrinfo (src, NULL, &hints, &result); - if (0 != e) { - return 0; /* error */ - } - - pgm_assert (NULL != result->ai_addr); - pgm_assert (0 != result->ai_addrlen); - - switch (result->ai_addr->sa_family) { - case AF_INET: { - struct sockaddr_in s4; - memcpy (&s4, result->ai_addr, sizeof(s4)); - memcpy (dst, &s4.sin_addr.s_addr, sizeof(struct in_addr)); - break; - } - - case AF_INET6: { - struct sockaddr_in6 s6; - memcpy (&s6, result->ai_addr, sizeof(s6)); - memcpy (dst, &s6.sin6_addr, sizeof(struct in6_addr)); - break; - } - - default: - pgm_assert_not_reached(); - break; - } - - freeaddrinfo (result); - return 1; /* success */ -} - -int -pgm_nla_to_sockaddr ( - const void* restrict nla, - struct sockaddr* restrict sa - ) -{ - uint16_t nla_family; - int retval = 0; - - memcpy (&nla_family, nla, sizeof(nla_family)); - sa->sa_family = ntohs (nla_family); - switch (sa->sa_family) { - case AFI_IP: - sa->sa_family = AF_INET; - ((struct sockaddr_in*)sa)->sin_addr.s_addr = ((const struct in_addr*)((const char*)nla + sizeof(uint32_t)))->s_addr; - break; - - case AFI_IP6: - sa->sa_family = AF_INET6; - memcpy (&((struct sockaddr_in6*)sa)->sin6_addr, (const struct in6_addr*)((const char*)nla + sizeof(uint32_t)), sizeof(struct in6_addr)); - break; - - default: - retval = -EINVAL; - break; - } - - return retval; -} - -int -pgm_sockaddr_to_nla ( - const struct sockaddr* restrict sa, - void* restrict nla - ) -{ - int retval = 0; - - *(uint16_t*)nla = sa->sa_family; - *(uint16_t*)((char*)nla + sizeof(uint16_t)) = 0; /* reserved 16bit space */ - switch (sa->sa_family) { - case AF_INET: - *(uint16_t*)nla = htons (AFI_IP); - ((struct in_addr*)((char*)nla + sizeof(uint32_t)))->s_addr = ((const struct sockaddr_in*)sa)->sin_addr.s_addr; - break; - - case AF_INET6: - *(uint16_t*)nla = htons (AFI_IP6); - memcpy ((struct in6_addr*)((char*)nla + sizeof(uint32_t)), &((const struct sockaddr_in6*)sa)->sin6_addr, sizeof(struct in6_addr)); - break; - - default: - retval = -EINVAL; - break; - } - - return retval; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/socket.c b/3rdparty/openpgm-svn-r1085/pgm/socket.c deleted file mode 100644 index 1338085..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/socket.c +++ /dev/null @@ -1,2046 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * PGM socket: manage incoming & outgoing sockets with ambient SPMs, - * transmit & receive windows. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#ifdef CONFIG_HAVE_POLL -# include -#endif -#ifdef CONFIG_HAVE_EPOLL -# include -#endif -#include -#include -#include -#include -#include -#include -#include - - -//#define SOCK_DEBUG -//#define SOCK_SPM_DEBUG - - -/* global locals */ -pgm_rwlock_t pgm_sock_list_lock; /* list of all sockets for admin interfaces */ -pgm_slist_t* pgm_sock_list = NULL; - - -static const char* pgm_family_string (const int) PGM_GNUC_CONST; -static const char* pgm_sock_type_string (const int) PGM_GNUC_CONST; -static const char* pgm_protocol_string (const int) PGM_GNUC_CONST; - - -size_t -pgm_pkt_offset ( - bool can_fragment, - sa_family_t pgmcc_family /* 0 = disable */ - ) -{ - static const size_t data_size = sizeof(struct pgm_header) + sizeof(struct pgm_data); - size_t pkt_size = data_size; - if (can_fragment || 0 != pgmcc_family) - pkt_size += sizeof(struct pgm_opt_length) + sizeof(struct pgm_opt_header); - if (can_fragment) - pkt_size += sizeof(struct pgm_opt_fragment); - if (AF_INET == pgmcc_family) - pkt_size += sizeof(struct pgm_opt_pgmcc_data); - else if (AF_INET6 == pgmcc_family) - pkt_size += sizeof(struct pgm_opt6_pgmcc_data); - return pkt_size; -} - -/* destroy a pgm_sock object and contents, if last sock also destroy - * associated event loop - * - * outstanding locks: - * 1) pgm_sock_t::lock - * 2) pgm_sock_t::receiver_mutex - * 3) pgm_sock_t::source_mutex - * 4) pgm_sock_t::txw_spinlock - * 5) pgm_sock_t::timer_mutex - * - * If application calls a function on the sock after destroy() it is a - * programmer error: segv likely to occur on unlock. - * - * on success, returns TRUE, on failure returns FALSE. - */ - -bool -pgm_close ( - pgm_sock_t* sock, - bool flush - ) -{ - pgm_return_val_if_fail (sock != NULL, FALSE); - if (!pgm_rwlock_reader_trylock (&sock->lock)) - pgm_return_val_if_reached (FALSE); - pgm_return_val_if_fail (!sock->is_destroyed, FALSE); - pgm_debug ("pgm_sock_destroy (sock:%p flush:%s)", - (const void*)sock, - flush ? "TRUE":"FALSE"); -/* flag existing calls */ - sock->is_destroyed = TRUE; -/* cancel running blocking operations */ - if (PGM_INVALID_SOCKET != sock->recv_sock) { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Closing receive socket.")); - pgm_closesocket (sock->recv_sock); - sock->recv_sock = PGM_INVALID_SOCKET; - } - if (PGM_INVALID_SOCKET != sock->send_sock) { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Closing send socket.")); - pgm_closesocket (sock->send_sock); - sock->send_sock = PGM_INVALID_SOCKET; - } - pgm_rwlock_reader_unlock (&sock->lock); - pgm_debug ("blocking on destroy lock ..."); - pgm_rwlock_writer_lock (&sock->lock); - - pgm_debug ("removing sock from inventory."); - pgm_rwlock_writer_lock (&pgm_sock_list_lock); - pgm_sock_list = pgm_slist_remove (pgm_sock_list, sock); - pgm_rwlock_writer_unlock (&pgm_sock_list_lock); - -/* flush source side by sending heartbeat SPMs */ - if (sock->can_send_data && - sock->is_connected && - flush) - { - pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Flushing PGM source with session finish option broadcast SPMs.")); - if (!pgm_send_spm (sock, PGM_OPT_FIN) || - !pgm_send_spm (sock, PGM_OPT_FIN) || - !pgm_send_spm (sock, PGM_OPT_FIN)) - { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Failed to send flushing SPMs.")); - } - } - - if (sock->peers_hashtable) { - pgm_debug ("destroying peer lookup table."); - pgm_hashtable_destroy (sock->peers_hashtable); - sock->peers_hashtable = NULL; - } - if (sock->peers_list) { - pgm_debug ("destroying peer list."); - do { - pgm_list_t* next = sock->peers_list->next; - pgm_peer_unref ((pgm_peer_t*)sock->peers_list->data); - - sock->peers_list = next; - } while (sock->peers_list); - } - - if (sock->window) { - pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Destroying transmit window.")); - pgm_txw_shutdown (sock->window); - sock->window = NULL; - } - pgm_trace (PGM_LOG_ROLE_RATE_CONTROL,_("Destroying rate control.")); - pgm_rate_destroy (&sock->rate_control); - if (PGM_INVALID_SOCKET != sock->send_with_router_alert_sock) { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Closing send with router alert socket.")); - pgm_closesocket (sock->send_with_router_alert_sock); - sock->send_with_router_alert_sock = PGM_INVALID_SOCKET; - } - if (sock->spm_heartbeat_interval) { - pgm_debug ("freeing SPM heartbeat interval data."); - pgm_free (sock->spm_heartbeat_interval); - sock->spm_heartbeat_interval = NULL; - } - if (sock->rx_buffer) { - pgm_debug ("freeing receive buffer."); - pgm_free_skb (sock->rx_buffer); - sock->rx_buffer = NULL; - } - pgm_debug ("destroying notification channels."); - if (sock->can_send_data) { - if (sock->use_pgmcc) { - pgm_notify_destroy (&sock->ack_notify); - } - pgm_notify_destroy (&sock->rdata_notify); - } - pgm_notify_destroy (&sock->pending_notify); - pgm_debug ("freeing sock locks."); - pgm_rwlock_free (&sock->peers_lock); - pgm_spinlock_free (&sock->txw_spinlock); - pgm_mutex_free (&sock->send_mutex); - pgm_mutex_free (&sock->timer_mutex); - pgm_mutex_free (&sock->source_mutex); - pgm_mutex_free (&sock->receiver_mutex); - pgm_rwlock_writer_unlock (&sock->lock); - pgm_rwlock_free (&sock->lock); - pgm_debug ("freeing sock data."); - pgm_free (sock); - pgm_debug ("finished."); - return TRUE; -} - -/* Create a pgm_sock object. Create sockets that require superuser - * priviledges. If interface ports are specified then UDP encapsulation will - * be used instead of raw protocol. - * - * If send == recv only two sockets need to be created iff ip headers are not - * required (IPv6). - * - * All receiver addresses must be the same family. - * interface and multiaddr must be the same family. - * family cannot be AF_UNSPEC! - * - * returns TRUE on success, or FALSE on error and sets error appropriately. - */ - -#if ( AF_INET != PF_INET ) || ( AF_INET6 != PF_INET6 ) -#error AF_INET and PF_INET are different values, the bananas are jumping in their pyjamas! -#endif - -bool -pgm_socket ( - pgm_sock_t** restrict sock, - const sa_family_t family, /* communications domain */ - const int pgm_sock_type, - const int protocol, - pgm_error_t** restrict error - ) -{ - pgm_sock_t* new_sock; - - pgm_return_val_if_fail (NULL != sock, FALSE); - pgm_return_val_if_fail (AF_INET == family || AF_INET6 == family, FALSE); - pgm_return_val_if_fail (SOCK_SEQPACKET == pgm_sock_type, FALSE); - pgm_return_val_if_fail (IPPROTO_UDP == protocol || IPPROTO_PGM == protocol, FALSE); - - pgm_debug ("socket (sock:%p family:%s sock-type:%s protocol:%s error:%p)", - (const void*)sock, pgm_family_string(family), pgm_sock_type_string(pgm_sock_type), pgm_protocol_string(protocol), (const void*)error); - - new_sock = pgm_new0 (pgm_sock_t, 1); - new_sock->family = family; - new_sock->socket_type = pgm_sock_type; - new_sock->protocol = protocol; - new_sock->can_send_data = TRUE; - new_sock->can_send_nak = TRUE; - new_sock->can_recv_data = TRUE; - new_sock->dport = DEFAULT_DATA_DESTINATION_PORT; - new_sock->tsi.sport = DEFAULT_DATA_SOURCE_PORT; - new_sock->adv_mode = 0; /* advance with time */ - -/* PGMCC */ - new_sock->acker_nla.ss_family = family; - -/* source-side */ - pgm_mutex_init (&new_sock->source_mutex); -/* transmit window */ - pgm_spinlock_init (&new_sock->txw_spinlock); -/* send socket */ - pgm_mutex_init (&new_sock->send_mutex); -/* next timer & spm expiration */ - pgm_mutex_init (&new_sock->timer_mutex); -/* receiver-side */ - pgm_mutex_init (&new_sock->receiver_mutex); -/* peer hash map & list lock */ - pgm_rwlock_init (&new_sock->peers_lock); -/* destroy lock */ - pgm_rwlock_init (&new_sock->lock); - -/* open sockets to implement PGM */ - int socket_type; - if (IPPROTO_UDP == new_sock->protocol) { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Opening UDP encapsulated sockets.")); - socket_type = SOCK_DGRAM; - new_sock->udp_encap_ucast_port = DEFAULT_UDP_ENCAP_UCAST_PORT; - new_sock->udp_encap_mcast_port = DEFAULT_UDP_ENCAP_MCAST_PORT; - } else { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Opening raw sockets.")); - socket_type = SOCK_RAW; - } - - if ((new_sock->recv_sock = socket (new_sock->family, - socket_type, - new_sock->protocol)) == PGM_INVALID_SOCKET) - { - const int save_errno = pgm_sock_errno(); - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - pgm_error_from_sock_errno (save_errno), - _("Creating receive socket: %s"), - pgm_sock_strerror (save_errno)); -#ifndef _WIN32 - if (EPERM == save_errno) { - pgm_error (_("PGM protocol requires CAP_NET_RAW capability, e.g. sudo execcap 'cap_net_raw=ep'")); - } -#endif - goto err_destroy; - } - - if ((new_sock->send_sock = socket (new_sock->family, - socket_type, - new_sock->protocol)) == PGM_INVALID_SOCKET) - { - const int save_errno = pgm_sock_errno(); - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - pgm_error_from_sock_errno (save_errno), - _("Creating send socket: %s"), - pgm_sock_strerror (save_errno)); - goto err_destroy; - } - - if ((new_sock->send_with_router_alert_sock = socket (new_sock->family, - socket_type, - new_sock->protocol)) == PGM_INVALID_SOCKET) - { - const int save_errno = pgm_sock_errno(); - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - pgm_error_from_sock_errno (save_errno), - _("Creating IP Router Alert (RFC 2113) send socket: %s"), - pgm_sock_strerror (save_errno)); - goto err_destroy; - } - - *sock = new_sock; - - pgm_rwlock_writer_lock (&pgm_sock_list_lock); - pgm_sock_list = pgm_slist_append (pgm_sock_list, *sock); - pgm_rwlock_writer_unlock (&pgm_sock_list_lock); - pgm_debug ("PGM socket successfully created."); - return TRUE; - -err_destroy: - if (PGM_INVALID_SOCKET != new_sock->recv_sock) { - if (PGM_SOCKET_ERROR == pgm_closesocket (new_sock->recv_sock)) { - const int save_errno = pgm_sock_errno(); - pgm_warn (_("Close on receive socket failed: %s"), - pgm_sock_strerror (save_errno)); - } - new_sock->recv_sock = PGM_INVALID_SOCKET; - } - if (PGM_INVALID_SOCKET != new_sock->send_sock) { - if (PGM_SOCKET_ERROR == pgm_closesocket (new_sock->send_sock)) { - const int save_errno = pgm_sock_errno(); - pgm_warn (_("Close on send socket failed: %s"), - pgm_sock_strerror (save_errno)); - } - new_sock->send_sock = PGM_INVALID_SOCKET; - } - if (PGM_INVALID_SOCKET != new_sock->send_with_router_alert_sock) { - if (PGM_SOCKET_ERROR == pgm_closesocket (new_sock->send_with_router_alert_sock)) { - const int save_errno = pgm_sock_errno(); - pgm_warn (_("Close on IP Router Alert (RFC 2113) send socket failed: %s"), - pgm_sock_strerror (save_errno)); - } - new_sock->send_with_router_alert_sock = PGM_INVALID_SOCKET; - } - pgm_free (new_sock); - return FALSE; -} - -bool -pgm_getsockopt ( - pgm_sock_t* const restrict sock, - const int optname, - void* restrict optval, - socklen_t* restrict optlen /* required */ - ) -{ - bool status = FALSE; - pgm_return_val_if_fail (sock != NULL, status); - pgm_return_val_if_fail (optval != NULL, status); - pgm_return_val_if_fail (optlen != NULL, status); - if (PGM_UNLIKELY(!pgm_rwlock_reader_trylock (&sock->lock))) - pgm_return_val_if_reached (status); - if (PGM_UNLIKELY(sock->is_destroyed)) { - pgm_rwlock_reader_unlock (&sock->lock); - return status; - } - switch (optname) { -/* maximum TPDU size */ - case PGM_MTU: - if (PGM_UNLIKELY(*optlen != sizeof (int))) - break; - *(int*restrict)optval = sock->max_tpdu; - status = TRUE; - break; - -/* receive socket */ - case PGM_RECV_SOCK: - if (PGM_UNLIKELY(!sock->is_connected)) - break; - if (PGM_UNLIKELY(*optlen != sizeof (int))) - break; - *(int*)optval = sock->recv_sock; - status = TRUE; - break; - -/* repair socket */ - case PGM_REPAIR_SOCK: - if (PGM_UNLIKELY(!sock->is_connected)) - break; - if (PGM_UNLIKELY(*optlen != sizeof (int))) - break; - *(int*)optval = pgm_notify_get_fd (&sock->rdata_notify); - status = TRUE; - break; - -/* pending socket */ - case PGM_PENDING_SOCK: - if (PGM_UNLIKELY(!sock->is_connected)) - break; - if (PGM_UNLIKELY(*optlen != sizeof (int))) - break; - *(int*)optval = pgm_notify_get_fd (&sock->pending_notify); - status = TRUE; - break; - -/* ACK or congestion socket */ - case PGM_ACK_SOCK: - if (PGM_UNLIKELY(!sock->is_connected)) - break; - if (PGM_UNLIKELY(*optlen != sizeof (int))) - break; - if (PGM_UNLIKELY(!sock->use_pgmcc)) - break; - *(int*)optval = pgm_notify_get_fd (&sock->ack_notify); - status = TRUE; - break; - - -/* timeout for pending timer */ - case PGM_TIME_REMAIN: - if (PGM_UNLIKELY(!sock->is_connected)) - break; - if (PGM_UNLIKELY(*optlen != sizeof (struct timeval))) - break; - { - struct timeval* tv = optval; - const pgm_time_t usecs = pgm_timer_expiration (sock); - tv->tv_sec = usecs / 1000000UL; - tv->tv_usec = usecs % 1000000UL; - } - status = TRUE; - break; - -/* timeout for blocking sends */ - case PGM_RATE_REMAIN: - if (PGM_UNLIKELY(!sock->is_connected)) - break; - if (PGM_UNLIKELY(*optlen != sizeof (struct timeval))) - break; - { - struct timeval* tv = optval; - const pgm_time_t usecs = pgm_rate_remaining (&sock->rate_control, sock->blocklen); - tv->tv_sec = usecs / 1000000UL; - tv->tv_usec = usecs % 1000000UL; - } - status = TRUE; - break; - - - } - pgm_rwlock_reader_unlock (&sock->lock); - return status; -} - -bool -pgm_setsockopt ( - pgm_sock_t* const sock, - const int optname, - const void* optval, - const socklen_t optlen - ) -{ - bool status = FALSE; - pgm_return_val_if_fail (sock != NULL, status); - if (PGM_UNLIKELY(!pgm_rwlock_reader_trylock (&sock->lock))) - pgm_return_val_if_reached (status); - if (PGM_UNLIKELY(sock->is_connected || sock->is_destroyed)) { - pgm_rwlock_reader_unlock (&sock->lock); - return status; - } - switch (optname) { - -/* RFC2113 IP Router Alert - */ - case PGM_IP_ROUTER_ALERT: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - { - const bool v = (0 != *(const int*)optval); - if (PGM_SOCKET_ERROR == pgm_sockaddr_router_alert (sock->send_with_router_alert_sock, sock->family, v)) - break; - } - status = TRUE; - break; - -/* IPv4: 68 <= tpdu < 65536 (RFC 2765) - * IPv6: 1280 <= tpdu < 65536 (RFC 2460) - */ - case PGM_MTU: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - if (PGM_UNLIKELY(*(const int*)optval < (sizeof(struct pgm_ip) + sizeof(struct pgm_header)))) - break; - if (PGM_UNLIKELY(*(const int*)optval > UINT16_MAX)) - break; - sock->max_tpdu = *(const int*)optval; - status = TRUE; - break; - -/* 1 = enable multicast loopback. - * 0 = default, to disable. - */ - case PGM_MULTICAST_LOOP: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - { - const bool v = (0 != *(const int*)optval); -#ifndef _WIN32 /* loop on send */ - if (PGM_SOCKET_ERROR == pgm_sockaddr_multicast_loop (sock->send_sock, sock->family, v) || - PGM_SOCKET_ERROR == pgm_sockaddr_multicast_loop (sock->send_with_router_alert_sock, sock->family, v)) - break; -#else /* loop on receive */ - if (PGM_SOCKET_ERROR == pgm_sockaddr_multicast_loop (sock->recv_sock, sock->family, v)) - break; -#endif - } - status = TRUE; - break; - -/* 0 < hops < 256, hops == -1 use kernel default (ignored). - */ - case PGM_MULTICAST_HOPS: -#ifndef CONFIG_TARGET_WINE - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - if (PGM_UNLIKELY(*(const int*)optval <= 0)) - break; - if (PGM_UNLIKELY(*(const int*)optval > UINT8_MAX)) - break; - { - const unsigned hops = *(const int*)optval; - if (PGM_SOCKET_ERROR == pgm_sockaddr_multicast_hops (sock->send_sock, sock->family, hops) || - PGM_SOCKET_ERROR == pgm_sockaddr_multicast_hops (sock->send_with_router_alert_sock, sock->family, hops)) - break; - } -#endif - status = TRUE; - break; - -/* IP Type of Service (ToS) or RFC 3246, differentiated services (DSCP) - */ - case PGM_TOS: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - if (PGM_SOCKET_ERROR == pgm_sockaddr_tos (sock->send_sock, sock->family, *(const int*)optval) || - PGM_SOCKET_ERROR == pgm_sockaddr_tos (sock->send_with_router_alert_sock, sock->family, *(const int*)optval)) - { - pgm_warn (_("ToS/DSCP setting requires CAP_NET_ADMIN or ADMIN capability.")); - break; - } - status = TRUE; - break; - -/* 0 < wmem < wmem_max (user) - * - * operating system and sysctl dependent maximum, minimum on Linux 256 (doubled). - */ - case PGM_SNDBUF: - if (PGM_SOCKET_ERROR == setsockopt (sock->send_sock, SOL_SOCKET, SO_SNDBUF, (const char*)optval, optlen) || - PGM_SOCKET_ERROR == setsockopt (sock->send_with_router_alert_sock, SOL_SOCKET, SO_SNDBUF, (const char*)optval, optlen)) - break; - status = TRUE; - break; - -/* 0 < rmem < rmem_max (user) - * - * minimum on Linux is 2048 (doubled). - */ - case PGM_RCVBUF: - if (PGM_SOCKET_ERROR == setsockopt (sock->recv_sock, SOL_SOCKET, SO_RCVBUF, (const char*)optval, optlen)) - break; - status = TRUE; - break; - -/* periodic ambient broadcast SPM interval in milliseconds. - */ - case PGM_AMBIENT_SPM: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - if (PGM_UNLIKELY(*(const int*)optval <= 0)) - break; - sock->spm_ambient_interval = *(const int*)optval; - status = TRUE; - break; - -/* sequence of heartbeat broadcast SPMS to flush out original - */ - case PGM_HEARTBEAT_SPM: - if (PGM_UNLIKELY(0 != optlen % sizeof (int))) - break; - { - sock->spm_heartbeat_len = optlen / sizeof (int); - sock->spm_heartbeat_interval = pgm_new (unsigned, sock->spm_heartbeat_len + 1); - sock->spm_heartbeat_interval[0] = 0; - for (unsigned i = 0; i < sock->spm_heartbeat_len; i++) - sock->spm_heartbeat_interval[i + 1] = ((const int*)optval)[i]; - } - status = TRUE; - break; - -/* size of transmit window in sequence numbers. - * 0 < txw_sqns < one less than half sequence space - */ - case PGM_TXW_SQNS: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - if (PGM_UNLIKELY(*(const int*)optval <= 0)) - break; - if (PGM_UNLIKELY(*(const int*)optval >= ((UINT32_MAX/2)-1))) - break; - sock->txw_sqns = *(const int*)optval; - status = TRUE; - break; - -/* size of transmit window in seconds. - * 0 < secs < ( txw_sqns / txw_max_rte ) - */ - case PGM_TXW_SECS: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - if (PGM_UNLIKELY(*(const int*)optval <= 0)) - break; - sock->txw_secs = *(const int*)optval; - status = TRUE; - break; - -/* maximum transmit rate. - * 0 < txw_max_rte < interface capacity - * 10mb : 1250000 - * 100mb : 12500000 - * 1gb : 125000000 - */ - case PGM_TXW_MAX_RTE: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - if (PGM_UNLIKELY(*(const int*)optval <= 0)) - break; - sock->txw_max_rte = *(const int*)optval; - status = TRUE; - break; - -/* timeout for peers. - * 0 < 2 * spm_ambient_interval <= peer_expiry - */ - case PGM_PEER_EXPIRY: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - if (PGM_UNLIKELY(*(const int*)optval <= 0)) - break; - sock->peer_expiry = *(const int*)optval; - status = TRUE; - break; - -/* maximum back off range for listening for multicast SPMR. - * 0 < spmr_expiry < spm_ambient_interval - */ - case PGM_SPMR_EXPIRY: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - if (PGM_UNLIKELY(*(const int*)optval <= 0)) - break; - sock->spmr_expiry = *(const int*)optval; - status = TRUE; - break; - -/* size of receive window in sequence numbers. - * 0 < rxw_sqns < one less than half sequence space - */ - case PGM_RXW_SQNS: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - if (PGM_UNLIKELY(*(const int*)optval <= 0)) - break; - if (PGM_UNLIKELY(*(const int*)optval >= ((UINT32_MAX/2)-1))) - break; - sock->rxw_sqns = *(const int*)optval; - status = TRUE; - break; - -/* size of receive window in seconds. - * 0 < secs < ( rxw_sqns / rxw_max_rte ) - */ - case PGM_RXW_SECS: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - if (PGM_UNLIKELY(*(const int*)optval <= 0)) - break; - sock->rxw_secs = *(const int*)optval; - status = TRUE; - break; - -/* maximum receive rate, for determining window size with txw_secs. - * 0 < rxw_max_rte < interface capacity - */ - case PGM_RXW_MAX_RTE: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - if (PGM_UNLIKELY(*(const int*)optval <= 0)) - break; - sock->rxw_max_rte = *(const int*)optval; - status = TRUE; - break; - -/* maximum NAK back-off value nak_rb_ivl in milliseconds. - * 0 < nak_rb_ivl <= nak_bo_ivl - */ - case PGM_NAK_BO_IVL: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - if (PGM_UNLIKELY(*(const int*)optval <= 0)) - break; - sock->nak_bo_ivl = *(const int*)optval; - status = TRUE; - break; - -/* repeat interval prior to re-sending a NAK, in milliseconds. - */ - case PGM_NAK_RPT_IVL: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - if (PGM_UNLIKELY(*(const int*)optval <= 0)) - break; - sock->nak_rpt_ivl = *(const int*)optval; - status = TRUE; - break; - -/* interval waiting for repair data, in milliseconds. - */ - case PGM_NAK_RDATA_IVL: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - if (PGM_UNLIKELY(*(const int*)optval <= 0)) - break; - sock->nak_rdata_ivl = *(const int*)optval; - status = TRUE; - break; - -/* limit for data. - * 0 < nak_data_retries < 256 - */ - case PGM_NAK_DATA_RETRIES: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - if (PGM_UNLIKELY(*(const int*)optval <= 0)) - break; - if (PGM_UNLIKELY(*(const int*)optval > UINT8_MAX)) - break; - sock->nak_data_retries = *(const int*)optval; - status = TRUE; - break; - -/* limit for NAK confirms. - * 0 < nak_ncf_retries < 256 - */ - case PGM_NAK_NCF_RETRIES: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - if (PGM_UNLIKELY(*(const int*)optval <= 0)) - break; - if (PGM_UNLIKELY(*(const int*)optval > UINT8_MAX)) - break; - sock->nak_ncf_retries = *(const int*)optval; - status = TRUE; - break; - -/* Enable FEC for this sock, specifically Reed Solmon encoding RS(n,k), common - * setting is RS(255, 223). - * - * inputs: - * - * n = FEC Block size = [k+1, 255] - * k = original data packets == transmission group size = [2, 4, 8, 16, 32, 64, 128] - * m = symbol size = 8 bits - * - * outputs: - * - * h = 2t = n - k = parity packets - * - * when h > k parity packets can be lost. - */ - case PGM_USE_FEC: - if (PGM_UNLIKELY(optlen != sizeof (struct pgm_fecinfo_t))) - break; - { - const struct pgm_fecinfo_t* fecinfo = optval; - if (PGM_UNLIKELY(0 != (fecinfo->group_size & (fecinfo->group_size - 1)))) - break; - if (PGM_UNLIKELY(fecinfo->group_size < 2 || fecinfo->group_size > 128)) - break; - if (PGM_UNLIKELY(fecinfo->group_size > fecinfo->block_size)) - break; - const uint8_t parity_packets = fecinfo->block_size - fecinfo->group_size; -/* technically could re-send previous packets */ - if (PGM_UNLIKELY(fecinfo->proactive_packets > parity_packets)) - break; -/* check validity of parameters */ - if (PGM_UNLIKELY(fecinfo->group_size > 223 && ((parity_packets * 223.0) / fecinfo->group_size) < 1.0)) - { - pgm_error (_("k/h ratio too low to generate parity data.")); - break; - } - sock->use_proactive_parity = (fecinfo->proactive_packets > 0); - sock->use_ondemand_parity = fecinfo->ondemand_parity_enabled; - sock->use_var_pktlen = fecinfo->var_pktlen_enabled; - sock->rs_n = fecinfo->block_size; - sock->rs_k = fecinfo->group_size; - sock->rs_proactive_h = fecinfo->proactive_packets; - } - status = TRUE; - break; - -/* congestion reporting */ - case PGM_USE_CR: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - if (PGM_UNLIKELY(*(const int*)optval <= 0)) - break; - sock->crqst_ivl = *(const int*)optval; - sock->use_cr = (sock->crqst_ivl > 0); - status = TRUE; - break; - -/* congestion control */ - case PGM_USE_PGMCC: - if (PGM_UNLIKELY(optlen != sizeof (struct pgm_pgmccinfo_t))) - break; - { - const struct pgm_pgmccinfo_t* pgmccinfo = optval; - sock->ack_bo_ivl = pgmccinfo->ack_bo_ivl; - sock->ack_c = pgmccinfo->ack_c; - sock->ack_c_p = pgmccinfo->ack_c_p; - sock->use_pgmcc = (sock->ack_c > 0); - } - status = TRUE; - break; - -/* declare socket only for sending, discard any incoming SPM, ODATA, - * RDATA, etc, packets. - */ - case PGM_SEND_ONLY: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - sock->can_recv_data = (0 == *(const int*)optval); - status = TRUE; - break; - -/* declare socket only for receiving, no transmit window will be created - * and no SPM broadcasts sent. - */ - case PGM_RECV_ONLY: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - sock->can_send_data = (0 == *(const int*)optval); - status = TRUE; - break; - -/* passive receiving socket, i.e. no back channel to source - */ - case PGM_PASSIVE: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - sock->can_send_nak = (0 == *(const int*)optval); - status = TRUE; - break; - -/* on unrecoverable data loss stop socket from further transmission and - * receiving. - */ - case PGM_ABORT_ON_RESET: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - sock->is_abort_on_reset = (0 != *(const int*)optval); - status = TRUE; - break; - -/* default non-blocking operation on send and receive sockets. - */ - case PGM_NOBLOCK: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - sock->is_nonblocking = (0 != *(const int*)optval); - pgm_sockaddr_nonblocking (sock->recv_sock, sock->is_nonblocking); - pgm_sockaddr_nonblocking (sock->send_sock, sock->is_nonblocking); - pgm_sockaddr_nonblocking (sock->send_with_router_alert_sock, sock->is_nonblocking); - status = TRUE; - break; - -/* sending group, singular. - */ - case PGM_SEND_GROUP: - if (PGM_UNLIKELY(optlen != sizeof(struct group_req))) - break; - memcpy (&sock->send_gsr, optval, optlen); - ((struct sockaddr_in*)&sock->send_gsr.gsr_group)->sin_port = htons (sock->udp_encap_mcast_port); - if (PGM_UNLIKELY(sock->family != sock->send_gsr.gsr_group.ss_family)) - break; - if (PGM_SOCKET_ERROR == pgm_sockaddr_multicast_if (sock->send_sock, (const struct sockaddr*)&sock->send_gsr.gsr_group, sock->send_gsr.gsr_interface) || - PGM_SOCKET_ERROR == pgm_sockaddr_multicast_if (sock->send_with_router_alert_sock, (const struct sockaddr*)&sock->send_gsr.gsr_group, sock->send_gsr.gsr_interface)) - break; - status = TRUE; - break; - -/* for any-source applications (ASM), join a new group - */ - case PGM_JOIN_GROUP: - if (PGM_UNLIKELY(optlen != sizeof(struct group_req))) - break; - if (PGM_UNLIKELY(sock->recv_gsr_len >= IP_MAX_MEMBERSHIPS)) - break; - { - const struct group_req* gr = optval; -/* verify not duplicate group/interface pairing */ - for (unsigned i = 0; i < sock->recv_gsr_len; i++) - { - if (pgm_sockaddr_cmp ((const struct sockaddr*)&gr->gr_group, (struct sockaddr*)&sock->recv_gsr[i].gsr_group) == 0 && - pgm_sockaddr_cmp ((const struct sockaddr*)&gr->gr_group, (struct sockaddr*)&sock->recv_gsr[i].gsr_source) == 0 && - (gr->gr_interface == sock->recv_gsr[i].gsr_interface || - 0 == sock->recv_gsr[i].gsr_interface )) - { -#ifdef SOCKET_DEBUG - char s[INET6_ADDRSTRLEN]; - pgm_sockaddr_ntop ((const struct sockaddr*)&gr->gr_group, s, sizeof(s)); - if (sock->recv_gsr[i].gsr_interface) { - pgm_warn(_("Socket has already joined group %s on interface %u"), s, gr->gr_interface); - } else { - pgm_warn(_("Socket has already joined group %s on all interfaces."), s); - } -#endif - break; - } - } - if (PGM_UNLIKELY(sock->family != gr->gr_group.ss_family)) - break; - if (PGM_SOCKET_ERROR == pgm_sockaddr_join_group (sock->recv_sock, sock->family, gr)) - break; - sock->recv_gsr[sock->recv_gsr_len].gsr_interface = gr->gr_interface; - memcpy (&sock->recv_gsr[sock->recv_gsr_len].gsr_group, &gr->gr_group, pgm_sockaddr_len ((const struct sockaddr*)&gr->gr_group)); - memcpy (&sock->recv_gsr[sock->recv_gsr_len].gsr_source, &gr->gr_group, pgm_sockaddr_len ((const struct sockaddr*)&gr->gr_group)); - sock->recv_gsr_len++; - } - status = TRUE; - break; - -/* for any-source applications (ASM), leave a joined group. - */ - case PGM_LEAVE_GROUP: - if (PGM_UNLIKELY(optlen != sizeof(struct group_req))) - break; - if (PGM_UNLIKELY(0 == sock->recv_gsr_len)) - break; - { - const struct group_req* gr = optval; - for (unsigned i = 0; i < sock->recv_gsr_len;) - { - if ((pgm_sockaddr_cmp ((const struct sockaddr*)&gr->gr_group, (struct sockaddr*)&sock->recv_gsr[i].gsr_group) == 0) && -/* drop all matching receiver entries */ - (gr->gr_interface == 0 || -/* drop all sources with matching interface */ - gr->gr_interface == sock->recv_gsr[i].gsr_interface) ) - { - sock->recv_gsr_len--; - if (i < (IP_MAX_MEMBERSHIPS - 1)) - { - memmove (&sock->recv_gsr[i], &sock->recv_gsr[i+1], (sock->recv_gsr_len - i) * sizeof(struct group_source_req)); - continue; - } - } - i++; - } - if (PGM_UNLIKELY(sock->family != gr->gr_group.ss_family)) - break; - if (PGM_SOCKET_ERROR == pgm_sockaddr_leave_group (sock->recv_sock, sock->family, gr)) - break; - } - status = TRUE; - break; - -/* for any-source applications (ASM), turn off a given source - */ - case PGM_BLOCK_SOURCE: - if (PGM_UNLIKELY(optlen != sizeof(struct group_source_req))) - break; - { - const struct group_source_req* gsr = optval; - if (PGM_UNLIKELY(sock->family != gsr->gsr_group.ss_family)) - break; - if (PGM_SOCKET_ERROR == pgm_sockaddr_block_source (sock->recv_sock, sock->family, gsr)) - break; - } - status = TRUE; - break; - -/* for any-source applications (ASM), re-allow a blocked source - */ - case PGM_UNBLOCK_SOURCE: - if (PGM_UNLIKELY(optlen != sizeof(struct group_source_req))) - break; - { - const struct group_source_req* gsr = optval; - if (PGM_UNLIKELY(sock->family != gsr->gsr_group.ss_family)) - break; - if (PGM_SOCKET_ERROR == pgm_sockaddr_unblock_source (sock->recv_sock, sock->family, gsr)) - break; - } - status = TRUE; - break; - -/* for controlled-source applications (SSM), join each group/source pair. - * - * SSM joins are allowed on top of ASM in order to merge a remote source onto the local segment. - */ - case PGM_JOIN_SOURCE_GROUP: - if (PGM_UNLIKELY(optlen != sizeof(struct group_source_req))) - break; - if (PGM_UNLIKELY(sock->recv_gsr_len >= IP_MAX_MEMBERSHIPS)) - break; - { - const struct group_source_req* gsr = optval; -/* verify if existing group/interface pairing */ - for (unsigned i = 0; i < sock->recv_gsr_len; i++) - { - if (pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_group, (struct sockaddr*)&sock->recv_gsr[i].gsr_group) == 0 && - (gsr->gsr_interface == sock->recv_gsr[i].gsr_interface || - 0 == sock->recv_gsr[i].gsr_interface )) - { - if (pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_source, (struct sockaddr*)&sock->recv_gsr[i].gsr_source) == 0) - { -#ifdef SOCKET_DEBUG - char s1[INET6_ADDRSTRLEN], s2[INET6_ADDRSTRLEN]; - pgm_sockaddr_ntop ((const struct sockaddr*)&gsr->gsr_group, s1, sizeof(s1)); - pgm_sockaddr_ntop ((const struct sockaddr*)&gsr->gsr_source, s2, sizeof(s2)); - if (sock->recv_gsr[i].gsr_interface) { - pgm_warn(_("Socket has already joined group %s from source %s on interface %d"), - s1, s2, (unsigned)gsr->gsr_interface); - } else { - pgm_warn(_("Socket has already joined group %s from source %s on all interfaces"), - s1, s2); - } -#endif - break; - } - break; - } - } - if (PGM_UNLIKELY(sock->family != gsr->gsr_group.ss_family)) - break; - if (PGM_UNLIKELY(sock->family != gsr->gsr_source.ss_family)) - break; - if (PGM_SOCKET_ERROR == pgm_sockaddr_join_source_group (sock->recv_sock, sock->family, gsr)) - break; - memcpy (&sock->recv_gsr[sock->recv_gsr_len], gsr, sizeof(struct group_source_req)); - sock->recv_gsr_len++; - } - status = TRUE; - break; - -/* for controlled-source applications (SSM), leave each group/source pair - */ - case PGM_LEAVE_SOURCE_GROUP: - if (PGM_UNLIKELY(optlen != sizeof(struct group_source_req))) - break; - if (PGM_UNLIKELY(0 == sock->recv_gsr_len)) - break; - { - const struct group_source_req* gsr = optval; -/* verify if existing group/interface pairing */ - for (unsigned i = 0; i < sock->recv_gsr_len; i++) - { - if (pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_group, (struct sockaddr*)&sock->recv_gsr[i].gsr_group) == 0 && - pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_source, (struct sockaddr*)&sock->recv_gsr[i].gsr_source) == 0 && - gsr->gsr_interface == sock->recv_gsr[i].gsr_interface) - { - sock->recv_gsr_len--; - if (i < (IP_MAX_MEMBERSHIPS - 1)) - { - memmove (&sock->recv_gsr[i], &sock->recv_gsr[i+1], (sock->recv_gsr_len - i) * sizeof(struct group_source_req)); - break; - } - } - } - if (PGM_UNLIKELY(sock->family != gsr->gsr_group.ss_family)) - break; - if (PGM_UNLIKELY(sock->family != gsr->gsr_source.ss_family)) - break; - if (PGM_SOCKET_ERROR == pgm_sockaddr_leave_source_group (sock->recv_sock, sock->family, gsr)) - break; - } - status = TRUE; - break; - -/* batch block and unblock sources */ - case PGM_MSFILTER: -#if defined(MCAST_MSFILTER) || defined(SIOCSMSFILTER) - if (PGM_UNLIKELY(optlen < sizeof(struct group_filter))) - break; - { - const struct group_filter* gf_list = optval; - if (GROUP_FILTER_SIZE( gf_list->gf_numsrc ) != optlen) - break; - if (PGM_UNLIKELY(sock->family != gf_list->gf_group.ss_family)) - break; -/* check only first */ - if (PGM_UNLIKELY(sock->family != gf_list->gf_slist[0].ss_family)) - break; - if (PGM_SOCKET_ERROR == pgm_sockaddr_msfilter (sock->recv_sock, sock->family, gf_list)) - break; - } - status = TRUE; -#endif - break; - -/* UDP encapsulation ports */ - case PGM_UDP_ENCAP_UCAST_PORT: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - sock->udp_encap_ucast_port = *(const int*)optval; - status = TRUE; - break; - - case PGM_UDP_ENCAP_MCAST_PORT: - if (PGM_UNLIKELY(optlen != sizeof (int))) - break; - sock->udp_encap_mcast_port = *(const int*)optval; - status = TRUE; - break; - - } - - pgm_rwlock_reader_unlock (&sock->lock); - return status; -} - -bool -pgm_bind ( - pgm_sock_t* restrict sock, - const struct pgm_sockaddr_t*const restrict sockaddr, - const socklen_t sockaddrlen, - pgm_error_t** restrict error - ) -{ - struct pgm_interface_req_t null_req; - memset (&null_req, 0, sizeof(null_req)); - return pgm_bind3 (sock, sockaddr, sockaddrlen, &null_req, sizeof(null_req), &null_req, sizeof(null_req), error); -} - -/* bind the sockets to the link layer to start receiving data. - * - * returns TRUE on success, or FALSE on error and sets error appropriately, - */ - -bool -pgm_bind3 ( - pgm_sock_t* restrict sock, - const struct pgm_sockaddr_t* restrict sockaddr, - const socklen_t sockaddrlen, - const struct pgm_interface_req_t* send_req, /* only use gr_interface and gr_group::sin6_scope */ - const socklen_t send_req_len, - const struct pgm_interface_req_t* recv_req, - const socklen_t recv_req_len, - pgm_error_t** restrict error /* maybe NULL */ - ) -{ - pgm_return_val_if_fail (NULL != sock, FALSE); - pgm_return_val_if_fail (NULL != sockaddr, FALSE); - pgm_return_val_if_fail (0 != sockaddrlen, FALSE); - if (sockaddr->sa_addr.sport) pgm_return_val_if_fail (sockaddr->sa_addr.sport != sockaddr->sa_port, FALSE); - pgm_return_val_if_fail (NULL != send_req, FALSE); - pgm_return_val_if_fail (sizeof(struct pgm_interface_req_t) == send_req_len, FALSE); - pgm_return_val_if_fail (NULL != recv_req, FALSE); - pgm_return_val_if_fail (sizeof(struct pgm_interface_req_t) == recv_req_len, FALSE); - - if (!pgm_rwlock_writer_trylock (&sock->lock)) - pgm_return_val_if_reached (FALSE); - if (sock->is_bound || - sock->is_destroyed) - { - pgm_rwlock_writer_unlock (&sock->lock); - pgm_return_val_if_reached (FALSE); - } - -/* sanity checks on state */ - if (sock->max_tpdu < (sizeof(struct pgm_ip) + sizeof(struct pgm_header))) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - PGM_ERROR_FAILED, - _("Invalid maximum TPDU size.")); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - if (sock->can_send_data) { - if (PGM_UNLIKELY(0 == sock->spm_ambient_interval)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - PGM_ERROR_FAILED, - _("SPM ambient interval not configured.")); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - if (PGM_UNLIKELY(0 == sock->spm_heartbeat_len)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - PGM_ERROR_FAILED, - _("SPM heartbeat interval not configured.")); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - if (PGM_UNLIKELY(0 == sock->txw_sqns && 0 == sock->txw_secs)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - PGM_ERROR_FAILED, - _("TXW_SQNS not configured.")); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - if (PGM_UNLIKELY(0 == sock->txw_sqns && 0 == sock->txw_max_rte)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - PGM_ERROR_FAILED, - _("TXW_MAX_RTE not configured.")); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - } - if (sock->can_recv_data) { - if (PGM_UNLIKELY(0 == sock->rxw_sqns && 0 == sock->rxw_secs)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - PGM_ERROR_FAILED, - _("RXW_SQNS not configured.")); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - if (PGM_UNLIKELY(0 == sock->rxw_sqns && 0 == sock->rxw_max_rte)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - PGM_ERROR_FAILED, - _("RXW_MAX_RTE not configured.")); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - if (PGM_UNLIKELY(0 == sock->peer_expiry)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - PGM_ERROR_FAILED, - _("Peer timeout not configured.")); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - if (PGM_UNLIKELY(0 == sock->spmr_expiry)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - PGM_ERROR_FAILED, - _("SPM-Request timeout not configured.")); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - if (PGM_UNLIKELY(0 == sock->nak_bo_ivl)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - PGM_ERROR_FAILED, - _("NAK_BO_IVL not configured.")); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - if (PGM_UNLIKELY(0 == sock->nak_rpt_ivl)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - PGM_ERROR_FAILED, - _("NAK_RPT_IVL not configured.")); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - if (PGM_UNLIKELY(0 == sock->nak_rdata_ivl)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - PGM_ERROR_FAILED, - _("NAK_RDATA_IVL not configured.")); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - if (PGM_UNLIKELY(0 == sock->nak_data_retries)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - PGM_ERROR_FAILED, - _("NAK_DATA_RETRIES not configured.")); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - if (PGM_UNLIKELY(0 == sock->nak_ncf_retries)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - PGM_ERROR_FAILED, - _("NAK_NCF_RETRIES not configured.")); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - } - - pgm_debug ("bind3 (sock:%p sockaddr:%p sockaddrlen:%u send-req:%p send-req-len:%u recv-req:%p recv-req-len:%u error:%p)", - (const void*)sock, (const void*)sockaddr, (unsigned)sockaddrlen, (const void*)send_req, (unsigned)send_req_len, (const void*)recv_req, (unsigned)recv_req_len, (const void*)error); - - memcpy (&sock->tsi, &sockaddr->sa_addr, sizeof(pgm_tsi_t)); - sock->dport = htons (sockaddr->sa_port); - if (sock->tsi.sport) { - sock->tsi.sport = htons (sock->tsi.sport); - } else { - do { - sock->tsi.sport = htons (pgm_random_int_range (0, UINT16_MAX)); - } while (sock->tsi.sport == sock->dport); - } - -/* UDP encapsulation port */ - if (sock->udp_encap_mcast_port) { - ((struct sockaddr_in*)&sock->send_gsr.gsr_group)->sin_port = htons (sock->udp_encap_mcast_port); - } - -/* pseudo-random number generator for back-off intervals */ - pgm_rand_create (&sock->rand_); - -/* PGM Children support of POLLs requires 32-bit random node identifier RAND_NODE_ID */ - if (sock->can_recv_data) { - sock->rand_node_id = pgm_rand_int (&sock->rand_); - } - - if (sock->can_send_data) - { -/* Windows notify call will raise an assertion on error, only Unix versions will return - * a valid error. - */ - if (sock->use_pgmcc && - 0 != pgm_notify_init (&sock->ack_notify)) - { - const int save_errno = errno; - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - pgm_error_from_errno (save_errno), - _("Creating ACK notification channel: %s"), - strerror (save_errno)); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - if (0 != pgm_notify_init (&sock->rdata_notify)) - { - const int save_errno = errno; - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - pgm_error_from_errno (save_errno), - _("Creating RDATA notification channel: %s"), - strerror (save_errno)); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - } - if (0 != pgm_notify_init (&sock->pending_notify)) - { - const int save_errno = errno; - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - pgm_error_from_errno (save_errno), - _("Creating waiting peer notification channel: %s"), - strerror (save_errno)); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - -/* determine IP header size for rate regulation engine & stats */ - sock->iphdr_len = (AF_INET == sock->family) ? sizeof(struct pgm_ip) : sizeof(struct pgm_ip6_hdr); - pgm_trace (PGM_LOG_ROLE_NETWORK,"Assuming IP header size of %zu bytes", sock->iphdr_len); - - if (sock->udp_encap_ucast_port) { - const size_t udphdr_len = sizeof(struct pgm_udphdr); - pgm_trace (PGM_LOG_ROLE_NETWORK,"Assuming UDP header size of %zu bytes", udphdr_len); - sock->iphdr_len += udphdr_len; - } - - const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; - sock->max_tsdu = sock->max_tpdu - sock->iphdr_len - pgm_pkt_offset (FALSE, pgmcc_family); - sock->max_tsdu_fragment = sock->max_tpdu - sock->iphdr_len - pgm_pkt_offset (TRUE, pgmcc_family); - const unsigned max_fragments = sock->txw_sqns ? MIN( PGM_MAX_FRAGMENTS, sock->txw_sqns ) : PGM_MAX_FRAGMENTS; - sock->max_apdu = MIN( PGM_MAX_APDU, max_fragments * sock->max_tsdu_fragment ); - - if (sock->can_send_data) - { - pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Create transmit window.")); - sock->window = sock->txw_sqns ? - pgm_txw_create (&sock->tsi, - 0, /* MAX_TPDU */ - sock->txw_sqns, /* TXW_SQNS */ - 0, /* TXW_SECS */ - 0, /* TXW_MAX_RTE */ - sock->use_ondemand_parity || sock->use_proactive_parity, - sock->rs_n, - sock->rs_k) : - pgm_txw_create (&sock->tsi, - sock->max_tpdu, /* MAX_TPDU */ - 0, /* TXW_SQNS */ - sock->txw_secs, /* TXW_SECS */ - sock->txw_max_rte, /* TXW_MAX_RTE */ - sock->use_ondemand_parity || sock->use_proactive_parity, - sock->rs_n, - sock->rs_k); - pgm_assert (NULL != sock->window); - } - -/* create peer list */ - if (sock->can_recv_data) { - sock->peers_hashtable = pgm_hashtable_new (pgm_tsi_hash, pgm_tsi_equal); - pgm_assert (NULL != sock->peers_hashtable); - } - - if (IPPROTO_UDP == sock->protocol) - { -/* Stevens: "SO_REUSEADDR has datatype int." - */ - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Set socket sharing.")); - const int v = 1; - if (PGM_SOCKET_ERROR == setsockopt (sock->recv_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&v, sizeof(v)) || - PGM_SOCKET_ERROR == setsockopt (sock->send_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&v, sizeof(v)) || - PGM_SOCKET_ERROR == setsockopt (sock->send_with_router_alert_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&v, sizeof(v))) - { - const int save_errno = pgm_sock_errno(); - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - pgm_error_from_sock_errno (save_errno), - _("Enabling reuse of socket local address: %s"), - pgm_sock_strerror (save_errno)); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - -/* request extra packet information to determine destination address on each packet */ -#ifndef CONFIG_TARGET_WINE - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Request socket packet-info.")); - const sa_family_t recv_family = sock->family; - if (PGM_SOCKET_ERROR == pgm_sockaddr_pktinfo (sock->recv_sock, recv_family, TRUE)) - { - const int save_errno = pgm_sock_errno(); - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - pgm_error_from_sock_errno (save_errno), - _("Enabling receipt of ancillary information per incoming packet: %s"), - pgm_sock_strerror (save_errno)); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } -#endif - } - else - { - const sa_family_t recv_family = sock->family; - if (AF_INET == recv_family) - { -/* include IP header only for incoming data, only works for IPv4 */ - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Request IP headers.")); - if (PGM_SOCKET_ERROR == pgm_sockaddr_hdrincl (sock->recv_sock, recv_family, TRUE)) - { - const int save_errno = pgm_sock_errno(); - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - pgm_error_from_sock_errno (save_errno), - _("Enabling IP header in front of user data: %s"), - pgm_sock_strerror (save_errno)); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - } - else - { - pgm_assert (AF_INET6 == recv_family); - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Request socket packet-info.")); - if (PGM_SOCKET_ERROR == pgm_sockaddr_pktinfo (sock->recv_sock, recv_family, TRUE)) - { - const int save_errno = pgm_sock_errno(); - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - pgm_error_from_sock_errno (save_errno), - _("Enabling receipt of control message per incoming datagram: %s"), - pgm_sock_strerror (save_errno)); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - } - } - -/* Bind UDP sockets to interfaces, note multicast on a bound interface is - * fruity on some platforms. Roughly, binding to INADDR_ANY provides all - * data, binding to the multicast group provides only multicast traffic, - * and binding to the interface address provides only unicast traffic. - * - * Multicast routing, IGMP & MLD require a link local address, for IPv4 - * this is provided through MULTICAST_IF and IPv6 through bind, and these - * may be overridden by per packet scopes. - * - * After binding, default interfaces (0.0.0.0) are resolved. - */ -/* TODO: different ports requires a new bound socket */ - - union { - struct sockaddr sa; - struct sockaddr_in s4; - struct sockaddr_in6 s6; - struct sockaddr_storage ss; - } recv_addr, recv_addr2, send_addr, send_with_router_alert_addr; - -#ifdef CONFIG_BIND_INADDR_ANY -/* force default interface for bind-only, source address is still valid for multicast membership. - * effectively same as running getaddrinfo(hints = {ai_flags = AI_PASSIVE}) - */ - if (AF_INET == sock->family) { - memset (&recv_addr.s4, 0, sizeof(struct sockaddr_in)); - recv_addr.s4.sin_family = AF_INET; - recv_addr.s4.sin_addr.s_addr = INADDR_ANY; - } else { - memset (&recv_addr.s6, 0, sizeof(struct sockaddr_in6)); - recv_addr.s6.sin6_family = AF_INET6; - recv_addr.s6.sin6_addr = in6addr_any; - } - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Binding receive socket to INADDR_ANY.")); -#else - if (!pgm_if_indextoaddr (recv_req->ir_interface, - sock->family, - recv_req->ir_scope_id, - &recv_addr.sa, - error)) - { - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Binding receive socket to interface index %u scope %u"), - recv_req->ir_interface, - recv_req->ir_scope_id); - -#endif /* CONFIG_BIND_INADDR_ANY */ - - memcpy (&recv_addr2.sa, &recv_addr.sa, pgm_sockaddr_len (&recv_addr.sa)); - ((struct sockaddr_in*)&recv_addr)->sin_port = htons (sock->udp_encap_mcast_port); - if (PGM_SOCKET_ERROR == bind (sock->recv_sock, - &recv_addr.sa, - pgm_sockaddr_len (&recv_addr.sa))) - { - char addr[INET6_ADDRSTRLEN]; - pgm_sockaddr_ntop ((struct sockaddr*)&recv_addr, addr, sizeof(addr)); - const int save_errno = pgm_sock_errno(); - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - pgm_error_from_sock_errno (save_errno), - _("Binding receive socket to address %s: %s"), - addr, - pgm_sock_strerror (save_errno)); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - - if (PGM_UNLIKELY(pgm_log_mask & PGM_LOG_ROLE_NETWORK)) - { - char s[INET6_ADDRSTRLEN]; - pgm_sockaddr_ntop ((struct sockaddr*)&recv_addr, s, sizeof(s)); - pgm_debug ("bind succeeded on recv_gsr[0] interface %s", s); - } - -/* keep a copy of the original address source to re-use for router alert bind */ - memset (&send_addr, 0, sizeof(send_addr)); - - if (!pgm_if_indextoaddr (send_req->ir_interface, - sock->family, - send_req->ir_scope_id, - (struct sockaddr*)&send_addr, - error)) - { - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - else if (PGM_UNLIKELY(pgm_log_mask & PGM_LOG_ROLE_NETWORK)) - { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Binding send socket to interface index %u scope %u"), - send_req->ir_interface, - send_req->ir_scope_id); - } - - memcpy (&send_with_router_alert_addr, &send_addr, pgm_sockaddr_len ((struct sockaddr*)&send_addr)); - if (PGM_SOCKET_ERROR == bind (sock->send_sock, - (struct sockaddr*)&send_addr, - pgm_sockaddr_len ((struct sockaddr*)&send_addr))) - { - char addr[INET6_ADDRSTRLEN]; - pgm_sockaddr_ntop ((struct sockaddr*)&send_addr, addr, sizeof(addr)); - const int save_errno = pgm_sock_errno(); - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - pgm_error_from_sock_errno (save_errno), - _("Binding send socket to address %s: %s"), - addr, - pgm_sock_strerror (save_errno)); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - -/* resolve bound address if 0.0.0.0 */ - if (AF_INET == send_addr.ss.ss_family) - { - if ((INADDR_ANY == ((struct sockaddr_in*)&send_addr)->sin_addr.s_addr) && - !pgm_if_getnodeaddr (AF_INET, (struct sockaddr*)&send_addr, sizeof(send_addr), error)) - { - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - } - else if ((memcmp (&in6addr_any, &((struct sockaddr_in6*)&send_addr)->sin6_addr, sizeof(in6addr_any)) == 0) && - !pgm_if_getnodeaddr (AF_INET6, (struct sockaddr*)&send_addr, sizeof(send_addr), error)) - { - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - - if (PGM_UNLIKELY(pgm_log_mask & PGM_LOG_ROLE_NETWORK)) - { - char s[INET6_ADDRSTRLEN]; - pgm_sockaddr_ntop ((struct sockaddr*)&send_addr, s, sizeof(s)); - pgm_debug ("bind succeeded on send_gsr interface %s", s); - } - - if (PGM_SOCKET_ERROR == bind (sock->send_with_router_alert_sock, - (struct sockaddr*)&send_with_router_alert_addr, - pgm_sockaddr_len((struct sockaddr*)&send_with_router_alert_addr))) - { - char addr[INET6_ADDRSTRLEN]; - pgm_sockaddr_ntop ((struct sockaddr*)&send_with_router_alert_addr, addr, sizeof(addr)); - const int save_errno = pgm_sock_errno(); - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - pgm_error_from_sock_errno (save_errno), - _("Binding IP Router Alert (RFC 2113) send socket to address %s: %s"), - addr, - pgm_sock_strerror (save_errno)); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - - if (PGM_UNLIKELY(pgm_log_mask & PGM_LOG_ROLE_NETWORK)) - { - char s[INET6_ADDRSTRLEN]; - pgm_sockaddr_ntop ((struct sockaddr*)&send_with_router_alert_addr, s, sizeof(s)); - pgm_debug ("bind (router alert) succeeded on send_gsr interface %s", s); - } - -/* save send side address for broadcasting as source nla */ - memcpy (&sock->send_addr, &send_addr, pgm_sockaddr_len ((struct sockaddr*)&send_addr)); - -/* rx to nak processor notify channel */ - if (sock->can_send_data) - { -/* setup rate control */ - if (sock->txw_max_rte) - { - pgm_trace (PGM_LOG_ROLE_RATE_CONTROL,_("Setting rate regulation to %zd bytes per second."), - sock->txw_max_rte); - - pgm_rate_create (&sock->rate_control, sock->txw_max_rte, sock->iphdr_len, sock->max_tpdu); - sock->is_controlled_spm = TRUE; /* must always be set */ - sock->is_controlled_odata = TRUE; - sock->is_controlled_rdata = TRUE; - } - else - { - sock->is_controlled_spm = FALSE; - sock->is_controlled_odata = FALSE; - sock->is_controlled_rdata = FALSE; - } - } - -/* allocate first incoming packet buffer */ - sock->rx_buffer = pgm_alloc_skb (sock->max_tpdu); - -/* bind complete */ - sock->is_bound = TRUE; - -/* cleanup */ - pgm_rwlock_writer_unlock (&sock->lock); - pgm_debug ("PGM socket successfully bound."); - return TRUE; -} - -bool -pgm_connect ( - pgm_sock_t* restrict sock, - pgm_error_t** restrict error /* maybe NULL */ - ) -{ - pgm_return_val_if_fail (sock != NULL, FALSE); - pgm_return_val_if_fail (sock->recv_gsr_len > 0, FALSE); -#ifdef CONFIG_TARGET_WINE - pgm_return_val_if_fail (sock->recv_gsr_len == 1, FALSE); -#endif - for (unsigned i = 0; i < sock->recv_gsr_len; i++) - { - pgm_return_val_if_fail (sock->recv_gsr[i].gsr_group.ss_family == sock->recv_gsr[0].gsr_group.ss_family, FALSE); - pgm_return_val_if_fail (sock->recv_gsr[i].gsr_group.ss_family == sock->recv_gsr[i].gsr_source.ss_family, FALSE); - } - pgm_return_val_if_fail (sock->send_gsr.gsr_group.ss_family == sock->recv_gsr[0].gsr_group.ss_family, FALSE); -/* shutdown */ - if (PGM_UNLIKELY(!pgm_rwlock_writer_trylock (&sock->lock))) - pgm_return_val_if_reached (FALSE); -/* state */ - if (PGM_UNLIKELY(sock->is_connected || !sock->is_bound || sock->is_destroyed)) { - pgm_rwlock_writer_unlock (&sock->lock); - pgm_return_val_if_reached (FALSE); - } - - pgm_debug ("connect (sock:%p error:%p)", - (const void*)sock, (const void*)error); - -/* rx to nak processor notify channel */ - if (sock->can_send_data) - { -/* announce new sock by sending out SPMs */ - if (!pgm_send_spm (sock, PGM_OPT_SYN) || - !pgm_send_spm (sock, PGM_OPT_SYN) || - !pgm_send_spm (sock, PGM_OPT_SYN)) - { - const int save_errno = pgm_sock_errno(); - pgm_set_error (error, - PGM_ERROR_DOMAIN_SOCKET, - pgm_error_from_sock_errno (save_errno), - _("Sending SPM broadcast: %s"), - pgm_sock_strerror (save_errno)); - pgm_rwlock_writer_unlock (&sock->lock); - return FALSE; - } - - sock->next_poll = sock->next_ambient_spm = pgm_time_update_now() + sock->spm_ambient_interval; - -/* start PGMCC with one token */ - sock->tokens = sock->cwnd_size = pgm_fp8 (1); - -/* slow start threshold */ - sock->ssthresh = pgm_fp8 (4); - -/* ACK timeout, should be greater than first SPM heartbeat interval in order to be scheduled correctly */ - sock->ack_expiry_ivl = pgm_secs (3); - -/* start full history */ - sock->ack_bitmap = 0xffffffff; - } - else - { - pgm_assert (sock->can_recv_data); - sock->next_poll = pgm_time_update_now() + pgm_secs( 30 ); - } - - sock->is_connected = TRUE; - -/* cleanup */ - pgm_rwlock_writer_unlock (&sock->lock); - pgm_debug ("PGM socket successfully connected."); - return TRUE; -} - -/* return local endpoint address - */ - -bool -pgm_getsockname ( - pgm_sock_t* const restrict sock, - struct pgm_sockaddr_t* restrict addr, - socklen_t* restrict addrlen - ) -{ - pgm_assert (NULL != sock); - pgm_assert (NULL != addr); - pgm_assert (NULL != addrlen); - pgm_assert (sizeof(struct pgm_sockaddr_t) == *addrlen); - - if (!sock->is_bound) { - errno = EBADF; - return FALSE; - } - - addr->sa_port = sock->dport; - memcpy (&addr->sa_addr, &sock->tsi, sizeof(pgm_tsi_t)); - return TRUE; -} - -/* add select parameters for the receive socket(s) - * - * returns highest file descriptor used plus one. - */ - -int -pgm_select_info ( - pgm_sock_t* const restrict sock, - fd_set* const restrict readfds, /* blocking recv fds */ - fd_set* const restrict writefds, /* blocking send fds */ - int* const restrict n_fds /* in: max fds, out: max (in:fds, sock:fds) */ - ) -{ - int fds = 0; - - pgm_assert (NULL != sock); - pgm_assert (NULL != n_fds); - - if (!sock->is_bound || sock->is_destroyed) - { - errno = EBADF; - return -1; - } - - const bool is_congested = (sock->use_pgmcc && sock->tokens < pgm_fp8 (1)) ? TRUE : FALSE; - - if (readfds) - { - FD_SET(sock->recv_sock, readfds); - fds = sock->recv_sock + 1; - if (sock->can_send_data) { - const int rdata_fd = pgm_notify_get_fd (&sock->rdata_notify); - FD_SET(rdata_fd, readfds); - fds = MAX(fds, rdata_fd + 1); - if (is_congested) { - const int ack_fd = pgm_notify_get_fd (&sock->ack_notify); - FD_SET(ack_fd, readfds); - fds = MAX(fds, ack_fd + 1); - } - } - const int pending_fd = pgm_notify_get_fd (&sock->pending_notify); - FD_SET(pending_fd, readfds); - fds = MAX(fds, pending_fd + 1); - } - - if (sock->can_send_data && writefds && !is_congested) - { - FD_SET(sock->send_sock, writefds); - fds = MAX(sock->send_sock + 1, fds); - } - - return *n_fds = MAX(fds, *n_fds); -} - -#ifdef CONFIG_HAVE_POLL -/* add poll parameters for the receive socket(s) - * - * returns number of pollfd structures filled. - */ - -int -pgm_poll_info ( - pgm_sock_t* const restrict sock, - struct pollfd* const restrict fds, - int* const restrict n_fds, /* in: #fds, out: used #fds */ - const int events /* POLLIN, POLLOUT */ - ) -{ - pgm_assert (NULL != sock); - pgm_assert (NULL != fds); - pgm_assert (NULL != n_fds); - - if (!sock->is_bound || sock->is_destroyed) - { - errno = EBADF; - return -1; - } - - int moo = 0; - -/* we currently only support one incoming socket */ - if (events & POLLIN) - { - pgm_assert ( (1 + moo) <= *n_fds ); - fds[moo].fd = sock->recv_sock; - fds[moo].events = POLLIN; - moo++; - if (sock->can_send_data) { - pgm_assert ( (1 + moo) <= *n_fds ); - fds[moo].fd = pgm_notify_get_fd (&sock->rdata_notify); - fds[moo].events = POLLIN; - moo++; - } - pgm_assert ( (1 + moo) <= *n_fds ); - fds[moo].fd = pgm_notify_get_fd (&sock->pending_notify); - fds[moo].events = POLLIN; - moo++; - } - -/* ODATA only published on regular socket, no need to poll router-alert sock */ - if (sock->can_send_data && events & POLLOUT) - { - pgm_assert ( (1 + moo) <= *n_fds ); - if (sock->use_pgmcc && sock->tokens < pgm_fp8 (1)) { -/* rx thread poll for ACK */ - fds[moo].fd = pgm_notify_get_fd (&sock->ack_notify); - fds[moo].events = POLLIN; - } else { -/* kernel resource poll */ - fds[moo].fd = sock->send_sock; - fds[moo].events = POLLOUT; - } - moo++; - } - - return *n_fds = moo; -} -#endif /* CONFIG_HAVE_POLL */ - -/* add epoll parameters for the recieve socket(s), events should - * be set to EPOLLIN to wait for incoming events (data), and EPOLLOUT to wait - * for non-blocking write. - * - * returns 0 on success, -1 on failure and sets errno appropriately. - */ -#ifdef CONFIG_HAVE_EPOLL -int -pgm_epoll_ctl ( - pgm_sock_t* const sock, - const int epfd, - const int op, /* EPOLL_CTL_ADD, ... */ - const int events /* EPOLLIN, EPOLLOUT */ - ) -{ - if (!(op == EPOLL_CTL_ADD || op == EPOLL_CTL_MOD)) - { - errno = EINVAL; - return -1; - } - else if (!sock->is_bound || sock->is_destroyed) - { - errno = EBADF; - return -1; - } - - struct epoll_event event; - int retval = 0; - - if (events & EPOLLIN) - { - event.events = events & (EPOLLIN | EPOLLET | EPOLLONESHOT); - event.data.ptr = sock; - retval = epoll_ctl (epfd, op, sock->recv_sock, &event); - if (retval) - goto out; - if (sock->can_send_data) { - retval = epoll_ctl (epfd, op, pgm_notify_get_fd (&sock->rdata_notify), &event); - if (retval) - goto out; - } - retval = epoll_ctl (epfd, op, pgm_notify_get_fd (&sock->pending_notify), &event); - if (retval) - goto out; - - if (events & EPOLLET) - sock->is_edge_triggered_recv = TRUE; - } - - if (sock->can_send_data && events & EPOLLOUT) - { - bool enable_ack_socket = FALSE; - bool enable_send_socket = FALSE; - -/* both sockets need to be added when PGMCC is enabled */ - if (sock->use_pgmcc && EPOLL_CTL_ADD == op) { - enable_ack_socket = enable_send_socket = TRUE; - } else { -/* automagically switch socket when congestion stall occurs */ - if (sock->use_pgmcc && sock->tokens < pgm_fp8 (1)) - enable_ack_socket = TRUE; - else - enable_send_socket = TRUE; - } - - if (enable_ack_socket) - { -/* rx thread poll for ACK */ - event.events = EPOLLIN | (events & (EPOLLONESHOT)); - event.data.ptr = sock; - retval = epoll_ctl (epfd, op, pgm_notify_get_fd (&sock->ack_notify), &event); - } - - if (enable_send_socket) - { -/* kernel resource poll */ - event.events = events & (EPOLLOUT | EPOLLET | EPOLLONESHOT); - event.data.ptr = sock; - retval = epoll_ctl (epfd, op, sock->send_sock, &event); - } - } -out: - return retval; -} -#endif - -static -const char* -pgm_family_string ( - const int family - ) -{ - const char* c; - - switch (family) { - case AF_UNSPEC: c = "AF_UNSPEC"; break; - case AF_INET: c = "AF_INET"; break; - case AF_INET6: c = "AF_INET6"; break; - default: c = "(unknown)"; break; - } - - return c; -} - -static -const char* -pgm_sock_type_string ( - const int sock_type - ) -{ - const char* c; - - switch (sock_type) { - case SOCK_SEQPACKET: c = "SOCK_SEQPACKET"; break; - default: c = "(unknown)"; break; - } - - return c; -} - -static -const char* -pgm_protocol_string ( - const int protocol - ) -{ - const char* c; - - switch (protocol) { - case IPPROTO_UDP: c = "IPPROTO_UDP"; break; - case IPPROTO_PGM: c = "IPPROTO_PGM"; break; - default: c = "(unknown)"; break; - } - - return c; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/socket_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/socket_unittest.c deleted file mode 100644 index 7f79f06..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/socket_unittest.c +++ /dev/null @@ -1,1186 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for PGM socket. - * - * Copyright (c) 2009-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include -#include -#include -#include - - -/* mock state */ - -#define TEST_NETWORK "" -#define TEST_PORT 7500 -#define TEST_MAX_TPDU 1500 -#define TEST_TXW_SQNS 32 -#define TEST_RXW_SQNS 32 -#define TEST_HOPS 16 -#define TEST_SPM_AMBIENT ( pgm_secs(30) ) -#define TEST_SPM_HEARTBEAT_INIT { pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(1300), pgm_secs(7), pgm_secs(16), pgm_secs(25), pgm_secs(30) } -#define TEST_PEER_EXPIRY ( pgm_secs(300) ) -#define TEST_SPMR_EXPIRY ( pgm_msecs(250) ) -#define TEST_NAK_BO_IVL ( pgm_msecs(50) ) -#define TEST_NAK_RPT_IVL ( pgm_secs(2) ) -#define TEST_NAK_RDATA_IVL ( pgm_secs(2) ) -#define TEST_NAK_DATA_RETRIES 5 -#define TEST_NAK_NCF_RETRIES 2 - -#define pgm_ipproto_pgm mock_pgm_ipproto_pgm -#define pgm_peer_unref mock_pgm_peer_unref -#define pgm_on_nak_notify mock_pgm_on_nak_notify -#define pgm_send_spm mock_pgm_send_spm -#define pgm_timer_prepare mock_pgm_timer_prepare -#define pgm_timer_check mock_pgm_timer_check -#define pgm_timer_expiration mock_pgm_timer_expiration -#define pgm_timer_dispatch mock_pgm_timer_dispatch -#define pgm_txw_create mock_pgm_txw_create -#define pgm_txw_shutdown mock_pgm_txw_shutdown -#define pgm_rate_create mock_pgm_rate_create -#define pgm_rate_destroy mock_pgm_rate_destroy -#define pgm_rate_remaining mock_pgm_rate_remaining -#define pgm_rs_create mock_pgm_rs_create -#define pgm_rs_destroy mock_pgm_rs_destroy -#define pgm_time_update_now mock_pgm_time_update_now - -#define SOCK_DEBUG -#include "socket.c" - -int mock_pgm_ipproto_pgm = IPPROTO_PGM; - - -static -void -mock_setup (void) -{ - if (!g_thread_supported ()) g_thread_init (NULL); -} - -static -void -mock_teardown (void) -{ -} - -/* stock create pgm sockaddr structure for calls to pgm_bind() - */ - -static -struct pgm_sockaddr_t* -generate_asm_sockaddr (void) -{ - const pgm_gsi_t gsi = { 200, 202, 203, 204, 205, 206 }; - struct pgm_sockaddr_t* pgmsa = g_new0 (struct pgm_sockaddr_t, 1); - pgmsa->sa_port = 123; - memcpy (&pgmsa->sa_addr.gsi, &gsi, sizeof(gsi)); - return pgmsa; -} - -/* stock create unconnected socket for pgm_setsockopt(), etc. - */ - -static -struct pgm_sock_t* -generate_sock (void) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, g_htons(1000) }; - struct pgm_sock_t* sock = g_new0 (struct pgm_sock_t, 1); - memcpy (&sock->tsi, &tsi, sizeof(pgm_tsi_t)); - ((struct sockaddr*)&sock->send_addr)->sa_family = AF_INET; - ((struct sockaddr_in*)&sock->send_addr)->sin_addr.s_addr = inet_addr ("127.0.0.2"); - ((struct sockaddr*)&sock->send_gsr.gsr_group)->sa_family = AF_INET; - ((struct sockaddr_in*)&sock->send_gsr.gsr_group)->sin_addr.s_addr = inet_addr ("239.192.0.1"); - sock->dport = g_htons(TEST_PORT); - sock->window = g_malloc0 (sizeof(pgm_txw_t)); - sock->txw_sqns = TEST_TXW_SQNS; - sock->max_tpdu = TEST_MAX_TPDU; - sock->max_tsdu = TEST_MAX_TPDU - sizeof(struct pgm_ip) - pgm_pkt_offset (FALSE, FALSE); - sock->max_tsdu_fragment = TEST_MAX_TPDU - sizeof(struct pgm_ip) - pgm_pkt_offset (TRUE, FALSE); - sock->max_apdu = MIN(TEST_TXW_SQNS, PGM_MAX_FRAGMENTS) * sock->max_tsdu_fragment; - sock->iphdr_len = sizeof(struct pgm_ip); - sock->spm_heartbeat_interval = g_malloc0 (sizeof(guint) * (2+2)); - sock->spm_heartbeat_interval[0] = pgm_secs(1); - pgm_spinlock_init (&sock->txw_spinlock); - sock->is_bound = FALSE; - sock->is_connected = FALSE; - sock->is_destroyed = FALSE; - sock->is_reset = FALSE; - return sock; -} - -/** receiver module */ -PGM_GNUC_INTERNAL -void -mock_pgm_peer_unref ( - pgm_peer_t* peer - ) -{ -} - -/** source module */ -static -bool -mock_pgm_on_nak_notify ( - GIOChannel* source, - GIOCondition condition, - gpointer data - ) -{ - return TRUE; -} - -PGM_GNUC_INTERNAL -bool -mock_pgm_send_spm ( - pgm_sock_t* sock, - int flags - ) -{ - return TRUE; -} - -/** timer module */ -PGM_GNUC_INTERNAL -bool -mock_pgm_timer_prepare ( - pgm_sock_t* const sock - ) -{ - return FALSE; -} - -PGM_GNUC_INTERNAL -bool -mock_pgm_timer_check ( - pgm_sock_t* const sock - ) -{ - return FALSE; -} - -PGM_GNUC_INTERNAL -pgm_time_t -mock_pgm_timer_expiration ( - pgm_sock_t* const sock - ) -{ - return 100L; -} - -PGM_GNUC_INTERNAL -bool -mock_pgm_timer_dispatch ( - pgm_sock_t* const sock - ) -{ - return TRUE; -} - -/** transmit window module */ -pgm_txw_t* -mock_pgm_txw_create ( - const pgm_tsi_t* const tsi, - const uint16_t tpdu_size, - const uint32_t sqns, - const unsigned secs, - const ssize_t max_rte, - const bool use_fec, - const uint8_t rs_n, - const uint8_t rs_k - ) -{ - pgm_txw_t* window = g_new0 (pgm_txw_t, 1); - return window; -} - -void -mock_pgm_txw_shutdown ( - pgm_txw_t* const window - ) -{ - g_free (window); -} - -/** rate control module */ -PGM_GNUC_INTERNAL -void -mock_pgm_rate_create ( - pgm_rate_t* bucket, - ssize_t rate_per_sec, - size_t iphdr_len, - uint16_t max_tpdu - ) -{ -} - -PGM_GNUC_INTERNAL -void -mock_pgm_rate_destroy ( - pgm_rate_t* bucket - ) -{ -} - -PGM_GNUC_INTERNAL -pgm_time_t -mock_pgm_rate_remaining ( - pgm_rate_t* bucket, - gsize packetlen - ) -{ - return 0; -} - -/** reed solomon module */ -void -mock_pgm_rs_create ( - pgm_rs_t* rs, - const uint8_t n, - const uint8_t k - ) -{ -} - -void -mock_pgm_rs_destroy ( - pgm_rs_t* rs - ) -{ -} - -/** time module */ -static pgm_time_t _mock_pgm_time_update_now (void); -pgm_time_update_func mock_pgm_time_update_now = _mock_pgm_time_update_now; - -static -pgm_time_t -_mock_pgm_time_update_now (void) -{ - return 0x1; -} - - -/* mock functions for external references */ - - -/* target: - * bool - * pgm_socket ( - * pgm_sock_t** sock, - * const sa_family_t family, - * const int pgm_sock_type, - * const int protocol, - * pgm_error_t** error - * ) - */ - -START_TEST (test_create_pass_001) -{ - pgm_error_t* err = NULL; - pgm_sock_t* sock; -/* only one type currently implemented */ - const int pgm_sock_type = SOCK_SEQPACKET; -/* PGM/IPv4 */ - sock = NULL; - fail_unless (TRUE == pgm_socket (&sock, AF_INET, pgm_sock_type, IPPROTO_PGM, &err), "create failed"); -/* PGM/UDP over IPv4 */ - sock = NULL; - fail_unless (TRUE == pgm_socket (&sock, AF_INET, pgm_sock_type, IPPROTO_UDP, &err), "create failed"); -/* PGM/IPv6 */ - sock = NULL; - fail_unless (TRUE == pgm_socket (&sock, AF_INET6, pgm_sock_type, IPPROTO_PGM, &err), "create failed"); -/* PGM/UDP over IPv6 */ - sock = NULL; - fail_unless (TRUE == pgm_socket (&sock, AF_INET6, pgm_sock_type, IPPROTO_UDP, &err), "create failed"); - fail_unless (NULL == err, "error raised"); -} -END_TEST - -/* NULL socket */ -START_TEST (test_create_fail_002) -{ - pgm_error_t* err = NULL; - fail_unless (FALSE == pgm_socket (NULL, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); -} -END_TEST - -/* invalid protocol family */ -START_TEST (test_create_fail_003) -{ - pgm_error_t* err = NULL; - pgm_sock_t* sock = NULL; - fail_unless (FALSE == pgm_socket (&sock, AF_UNSPEC, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); -} -END_TEST - -/* invalid socket type */ -START_TEST (test_create_fail_004) -{ - pgm_error_t* err = NULL; - pgm_sock_t* sock = NULL; - fail_unless (FALSE == pgm_socket (&sock, AF_INET, SOCK_STREAM, IPPROTO_PGM, &err), "create failed"); - fail_unless (FALSE == pgm_socket (&sock, AF_INET, SOCK_DGRAM, IPPROTO_PGM, &err), "create failed"); -} -END_TEST - -/* invalid protocol */ -START_TEST (test_create_fail_005) -{ - pgm_error_t* err = NULL; - pgm_sock_t* sock = NULL; - fail_unless (FALSE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_TCP, &err), "create failed"); -} -END_TEST - - -/* target: - * bool - * pgm_bind ( - * pgm_sock_t* sock, - * const struct pgm_sockaddr_t* sockaddr, - * const socklen_t sockaddrlen, - * pgm_error_t** error - * ) - */ - -START_TEST (test_bind_pass_001) -{ - pgm_error_t* err = NULL; - pgm_sock_t* sock = NULL; - struct pgm_sockaddr_t* pgmsa = generate_asm_sockaddr (); - fail_if (NULL == pgmsa, "generate_asm_sockaddr failed"); - fail_unless (TRUE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); - fail_unless (NULL == err, "error raised"); - fail_unless (TRUE == pgm_bind (sock, pgmsa, sizeof(*pgmsa), &err), "bind failed"); -} -END_TEST - -/* fail on unset options */ -START_TEST (test_bind_fail_001) -{ - pgm_error_t* err = NULL; - pgm_sock_t* sock = NULL; - struct pgm_sockaddr_t* pgmsa = generate_asm_sockaddr (); - fail_if (NULL == pgmsa, "generate_asm_sockaddr failed"); - fail_unless (TRUE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); - fail_unless (NULL == err, "error raised"); - fail_unless (FALSE == pgm_bind (sock, pgmsa, sizeof(*pgmsa), &err), "bind failed"); -} -END_TEST - -/* invalid parameters */ -START_TEST (test_bind_fail_002) -{ - pgm_error_t* err = NULL; - fail_unless (FALSE == pgm_bind (NULL, NULL, 0, &err), "bind failed"); -} -END_TEST - -/* target: - * bool - * pgm_bind3 ( - * pgm_sock_t* sock, - * const struct pgm_sockaddr_t* sockaddr, - * const socklen_t sockaddrlen, - * const struct pgm_interface_req_t* send_req, - * const socklen_t send_req_len, - * const struct pgm_interface_req_t* recv_req, - * const socklen_t recv_req_len, - * pgm_error_t** error - * ) - */ - -/* fail on unset options */ -START_TEST (test_bind3_fail_001) -{ - pgm_error_t* err = NULL; - pgm_sock_t* sock = NULL; - struct pgm_sockaddr_t* pgmsa = generate_asm_sockaddr (); - fail_if (NULL == pgmsa, "generate_asm_sockaddr failed"); - fail_unless (TRUE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); - fail_unless (NULL == err, "error raised"); - struct pgm_interface_req_t send_req = { .ir_interface = 0, .ir_scope_id = 0 }, - recv_req = { .ir_interface = 0, .ir_scope_id = 0 }; - fail_unless (FALSE == pgm_bind3 (sock, - pgmsa, sizeof(*pgmsa), - &send_req, sizeof(send_req), - &recv_req, sizeof(recv_req), - &err), "bind failed"); -} -END_TEST - -/* invalid parameters */ -START_TEST (test_bind3_fail_002) -{ - pgm_error_t* err = NULL; - fail_unless (FALSE == pgm_bind3 (NULL, NULL, 0, NULL, 0, NULL, 0, &err), "bind failed"); -} -END_TEST - -/* target: - * bool - * pgm_connect ( - * pgm_sock_t* sock, - * pgm_error_t** error - * ) - */ - -START_TEST (test_connect_pass_001) -{ - pgm_error_t* err = NULL; - pgm_sock_t* sock = NULL; - struct pgm_sockaddr_t* pgmsa = generate_asm_sockaddr (); - fail_if (NULL == pgmsa, "generate_asm_sockaddr failed"); - fail_unless (TRUE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); - fail_unless (NULL == err, "error raised"); - fail_unless (TRUE == pgm_bind (sock, pgmsa, sizeof(*pgmsa), &err), "bind failed"); - fail_unless (TRUE == pgm_connect (sock, &err), "connect failed"); -} -END_TEST - -/* invalid parameters */ -START_TEST (test_connect_fail_001) -{ - pgm_error_t* err = NULL; - fail_unless (FALSE == pgm_connect (NULL, &err), "connect failed"); -} -END_TEST - -/* target: - * bool - * pgm_close ( - * pgm_sock_t* sock, - * bool flush - * ) - */ - -/* socket > close */ -START_TEST (test_destroy_pass_001) -{ - pgm_error_t* err = NULL; - pgm_sock_t* sock = NULL; - fail_unless (TRUE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); - fail_unless (TRUE == pgm_close (sock, FALSE), "destroy failed"); -} -END_TEST - -/* socket > bind > close */ -START_TEST (test_destroy_pass_002) -{ - pgm_error_t* err = NULL; - pgm_sock_t* sock = NULL; - struct pgm_sockaddr_t* pgmsa = generate_asm_sockaddr (); - fail_if (NULL == pgmsa, "generate_asm_sockaddr failed"); - fail_unless (TRUE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); - fail_unless (NULL == err, "error raised"); - fail_unless (TRUE == pgm_bind (sock, pgmsa, sizeof(*pgmsa), &err), "bind failed"); - fail_unless (TRUE == pgm_close (sock, FALSE), "destroy failed"); -} -END_TEST - -/* socket > bind > connect > close */ -START_TEST (test_destroy_pass_003) -{ - pgm_error_t* err = NULL; - pgm_sock_t* sock = NULL; - struct pgm_sockaddr_t* pgmsa = generate_asm_sockaddr (); - fail_if (NULL == pgmsa, "generate_asm_sockaddr failed"); - fail_unless (TRUE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); - fail_unless (NULL == err, "error raised"); - fail_unless (TRUE == pgm_bind (sock, pgmsa, sizeof(*pgmsa), &err), "bind failed"); - fail_unless (TRUE == pgm_connect (sock, &err), "connect failed"); - fail_unless (TRUE == pgm_close (sock, FALSE), "destroy failed"); -} -END_TEST - -/* invalid parameters */ -START_TEST (test_destroy_fail_001) -{ - fail_unless (FALSE == pgm_close (NULL, FALSE), "destroy failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_MAX_TPDU, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_max_tpdu_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - const int optname = PGM_MTU; - const int max_tpdu = 1500; - const void* optval = &max_tpdu; - const socklen_t optlen = sizeof(max_tpdu); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_max_tpdu failed"); -} -END_TEST - -/* invalid parameters */ -START_TEST (test_set_max_tpdu_fail_001) -{ - const int optname = PGM_MTU; - const int max_tpdu = 1500; - const void* optval = &max_tpdu; - const socklen_t optlen = sizeof(max_tpdu); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_max_tpdu failed"); -} -END_TEST - -/* too small */ -START_TEST (test_set_max_tpdu_fail_002) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - const int optname = PGM_MTU; - const int max_tpdu = 1; - const void* optval = &max_tpdu; - const socklen_t optlen = sizeof(max_tpdu); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_max_tpdu failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_MULTICAST_LOOP, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_multicast_loop_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - const int optname = PGM_MULTICAST_LOOP; - const int loop_enabled = 1; - const void* optval = &loop_enabled; - const socklen_t optlen = sizeof(loop_enabled); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_multicast_loop failed"); -} -END_TEST - -START_TEST (test_set_multicast_loop_fail_001) -{ - const int optname = PGM_MULTICAST_LOOP; - const int loop_enabled = 1; - const void* optval = &loop_enabled; - const socklen_t optlen = sizeof(loop_enabled); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_multicast_loop failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_MULTICAST_HOPS, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_hops_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - const int optname = PGM_MULTICAST_HOPS; - const int hops = 16; - const void* optval = &hops; - const socklen_t optlen = sizeof(hops); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_hops failed"); -} -END_TEST - -START_TEST (test_set_hops_fail_001) -{ - const int optname = PGM_MULTICAST_HOPS; - const int hops = 16; - const void* optval = &hops; - const socklen_t optlen = sizeof(hops); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_hops failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_SNDBUF, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_sndbuf_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - const int optname = PGM_SNDBUF; - const int bufsize = 131071; /* 128kB */ - const void* optval = &bufsize; - const socklen_t optlen = sizeof(bufsize); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_sndbuf failed"); -} -END_TEST - -START_TEST (test_set_sndbuf_fail_001) -{ - const int optname = PGM_SNDBUF; - const int bufsize = 131071; /* 128kB */ - const void* optval = &bufsize; - const socklen_t optlen = sizeof(bufsize); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_sndbuf failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_RCVBUF, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_rcvbuf_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - const int optname = PGM_RCVBUF; - const int bufsize = 131071; /* 128kB */ - const void* optval = &bufsize; - const socklen_t optlen = sizeof(bufsize); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_rcvbuf failed"); -} -END_TEST - -START_TEST (test_set_rcvbuf_fail_001) -{ - const int optname = PGM_RCVBUF; - const int bufsize = 131071; /* 128kB */ - const void* optval = &bufsize; - const socklen_t optlen = sizeof(bufsize); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_rcvbuf failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_USE_FEC, - * const void* optval, - * const socklen_t optlen = sizeof(struct pgm_fecinfo_t) - * ) - */ - -START_TEST (test_set_fec_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - const int optname = PGM_USE_FEC; - const struct pgm_fecinfo_t fecinfo = { - .ondemand_parity_enabled = TRUE, - .proactive_packets = 16, - .var_pktlen_enabled = TRUE, - .block_size = 255, - .group_size = 239 - }; - const void* optval = &fecinfo; - const socklen_t optlen = sizeof(fecinfo); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_fec failed"); -} -END_TEST - -START_TEST (test_set_fec_fail_001) -{ - const int optname = PGM_USE_FEC; - const struct pgm_fecinfo_t fecinfo = { - .ondemand_parity_enabled = TRUE, - .proactive_packets = 16, - .var_pktlen_enabled = TRUE, - .block_size = 255, - .group_size = 239 - }; - const void* optval = &fecinfo; - const socklen_t optlen = sizeof(fecinfo); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_fec failed"); -} -END_TEST - -/* TODO: invalid Reed-Solomon parameters - */ - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_USE_PGMCC, - * const void* optval, - * const socklen_t optlen = sizeof(struct pgm_pgmccinfo_t) - * ) - */ - -START_TEST (test_set_pgmcc_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - const int optname = PGM_USE_PGMCC; - const struct pgm_pgmccinfo_t pgmccinfo = { - .ack_bo_ivl = pgm_msecs(100), - .ack_c = 123, - .ack_c_p = 456 - }; - const void* optval = &pgmccinfo; - const socklen_t optlen = sizeof(pgmccinfo); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_pgmcc failed"); -} -END_TEST - -START_TEST (test_set_pgmcc_fail_001) -{ - const int optname = PGM_USE_PGMCC; - const struct pgm_pgmccinfo_t pgmccinfo = { - .ack_bo_ivl = pgm_msecs(100), - .ack_c = 123, - .ack_c_p = 456 - }; - const void* optval = &pgmccinfo; - const socklen_t optlen = sizeof(pgmccinfo); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_pgmcc failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_USE_CR, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_cr_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - const int optname = PGM_USE_CR; - const int magic_bunny = 1; - const void* optval = &magic_bunny; - const socklen_t optlen = sizeof(magic_bunny); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_cr failed"); -} -END_TEST - -START_TEST (test_set_cr_fail_001) -{ - const int optname = PGM_USE_CR; - const int magic_bunny = 1; - const void* optval = &magic_bunny; - const socklen_t optlen = sizeof(magic_bunny); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_cr failed"); -} -END_TEST - - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_SEND_ONLY, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_send_only_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - const int optname = PGM_SEND_ONLY; - const int send_only = 1; - const void* optval = &send_only; - const socklen_t optlen = sizeof(send_only); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_send_only failed"); -} -END_TEST - -START_TEST (test_set_send_only_fail_001) -{ - const int optname = PGM_SEND_ONLY; - const int send_only = 1; - const void* optval = &send_only; - const socklen_t optlen = sizeof(send_only); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_send_only failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_RECV_ONLY, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_recv_only_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - const int optname = PGM_RECV_ONLY; - const int recv_only = 1; - const void* optval = &recv_only; - const socklen_t optlen = sizeof(recv_only); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_recv_only failed"); -} -END_TEST - -START_TEST (test_set_recv_only_fail_001) -{ - const int optname = PGM_RECV_ONLY; - const int recv_only = 1; - const void* optval = &recv_only; - const socklen_t optlen = sizeof(recv_only); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_recv_only failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_PASSIVE, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_recv_only_pass_002) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - const int optname = PGM_PASSIVE; - const int passive = 1; - const void* optval = &passive; - const socklen_t optlen = sizeof(passive); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_passive failed"); -} -END_TEST - -START_TEST (test_set_recv_only_fail_002) -{ - const int optname = PGM_PASSIVE; - const int passive = 1; - const void* optval = &passive; - const socklen_t optlen = sizeof(passive); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_passive failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_ABORT_ON_RESET, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_abort_on_reset_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - const int optname = PGM_ABORT_ON_RESET; - const int abort_on_reset= 1; - const void* optval = &abort_on_reset; - const socklen_t optlen = sizeof(abort_on_reset); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_abort_on_reset failed"); -} -END_TEST - -START_TEST (test_set_abort_on_reset_fail_001) -{ - const int optname = PGM_ABORT_ON_RESET; - const int abort_on_reset= 1; - const void* optval = &abort_on_reset; - const socklen_t optlen = sizeof(abort_on_reset); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_abort_on_reset failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_NOBLOCK, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_noblock_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - const int optname = PGM_NOBLOCK; - const int noblock = 1; - const void* optval = &noblock; - const socklen_t optlen = sizeof(noblock); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_noblock failed"); -} -END_TEST - -START_TEST (test_set_noblock_fail_001) -{ - const int optname = PGM_NOBLOCK; - const int noblock = 1; - const void* optval = &noblock; - const socklen_t optlen = sizeof(noblock); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_noblock failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_UDP_ENCAP_UCAST_PORT, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_udp_unicast_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - const int optname = PGM_NOBLOCK; - const int unicast_port = 10001; - const void* optval = &unicast_port; - const socklen_t optlen = sizeof(unicast_port); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_udp_unicast failed"); -} -END_TEST - -START_TEST (test_set_udp_unicast_fail_001) -{ - const int optname = PGM_NOBLOCK; - const int unicast_port = 10001; - const void* optval = &unicast_port; - const socklen_t optlen = sizeof(unicast_port); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_udp_unicast failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_UDP_ENCAP_MCAST_PORT, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_udp_multicast_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - const int optname = PGM_NOBLOCK; - const int multicast_port= 10001; - const void* optval = &multicast_port; - const socklen_t optlen = sizeof(multicast_port); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_udp_multicast failed"); -} -END_TEST - -START_TEST (test_set_udp_multicast_fail_001) -{ - const int optname = PGM_NOBLOCK; - const int multicast_port= 10002; - const void* optval = &multicast_port; - const socklen_t optlen = sizeof(multicast_port); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_udp_multicast failed"); -} -END_TEST - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_create = tcase_create ("create"); - suite_add_tcase (s, tc_create); - tcase_add_checked_fixture (tc_create, mock_setup, mock_teardown); - tcase_add_test (tc_create, test_create_pass_001); - tcase_add_test (tc_create, test_create_fail_002); - tcase_add_test (tc_create, test_create_fail_003); - tcase_add_test (tc_create, test_create_fail_004); - tcase_add_test (tc_create, test_create_fail_005); - - TCase* tc_bind = tcase_create ("bind"); - suite_add_tcase (s, tc_bind); - tcase_add_checked_fixture (tc_bind, mock_setup, mock_teardown); - tcase_add_test (tc_bind, test_bind_fail_001); - tcase_add_test (tc_bind, test_bind_fail_002); - - TCase* tc_connect = tcase_create ("connect"); - suite_add_tcase (s, tc_connect); - tcase_add_checked_fixture (tc_connect, mock_setup, mock_teardown); - tcase_add_test (tc_connect, test_connect_pass_001); - tcase_add_test (tc_connect, test_connect_fail_001); - - TCase* tc_destroy = tcase_create ("destroy"); - suite_add_tcase (s, tc_destroy); - tcase_add_checked_fixture (tc_destroy, mock_setup, mock_teardown); - tcase_add_test (tc_destroy, test_destroy_pass_001); - tcase_add_test (tc_destroy, test_destroy_fail_001); - - TCase* tc_set_max_tpdu = tcase_create ("set-max-tpdu"); - suite_add_tcase (s, tc_set_max_tpdu); - tcase_add_checked_fixture (tc_set_max_tpdu, mock_setup, mock_teardown); - tcase_add_test (tc_set_max_tpdu, test_set_max_tpdu_pass_001); - tcase_add_test (tc_set_max_tpdu, test_set_max_tpdu_fail_001); - - TCase* tc_set_multicast_loop = tcase_create ("set-multicast-loop"); - suite_add_tcase (s, tc_set_multicast_loop); - tcase_add_checked_fixture (tc_set_multicast_loop, mock_setup, mock_teardown); - tcase_add_test (tc_set_multicast_loop, test_set_multicast_loop_pass_001); - tcase_add_test (tc_set_multicast_loop, test_set_multicast_loop_fail_001); - - TCase* tc_set_hops = tcase_create ("set-hops"); - suite_add_tcase (s, tc_set_hops); - tcase_add_checked_fixture (tc_set_hops, mock_setup, mock_teardown); - tcase_add_test (tc_set_hops, test_set_hops_pass_001); - tcase_add_test (tc_set_hops, test_set_hops_fail_001); - - TCase* tc_set_sndbuf = tcase_create ("set-sndbuf"); - suite_add_tcase (s, tc_set_sndbuf); - tcase_add_checked_fixture (tc_set_sndbuf, mock_setup, mock_teardown); - tcase_add_test (tc_set_sndbuf, test_set_sndbuf_pass_001); - tcase_add_test (tc_set_sndbuf, test_set_sndbuf_fail_001); - - TCase* tc_set_rcvbuf = tcase_create ("set-rcvbuf"); - suite_add_tcase (s, tc_set_rcvbuf); - tcase_add_checked_fixture (tc_set_rcvbuf, mock_setup, mock_teardown); - tcase_add_test (tc_set_rcvbuf, test_set_rcvbuf_pass_001); - tcase_add_test (tc_set_rcvbuf, test_set_rcvbuf_fail_001); - - TCase* tc_set_fec = tcase_create ("set-fec"); - suite_add_tcase (s, tc_set_fec); - tcase_add_checked_fixture (tc_set_fec, mock_setup, mock_teardown); - tcase_add_test (tc_set_fec, test_set_fec_pass_001); - tcase_add_test (tc_set_fec, test_set_fec_fail_001); - - TCase* tc_set_pgmcc = tcase_create ("set-pgmcc"); - suite_add_tcase (s, tc_set_pgmcc); - tcase_add_checked_fixture (tc_set_pgmcc, mock_setup, mock_teardown); - tcase_add_test (tc_set_pgmcc, test_set_pgmcc_pass_001); - tcase_add_test (tc_set_pgmcc, test_set_pgmcc_fail_001); - - TCase* tc_set_cr = tcase_create ("set-cr"); - suite_add_tcase (s, tc_set_cr); - tcase_add_checked_fixture (tc_set_cr, mock_setup, mock_teardown); - tcase_add_test (tc_set_cr, test_set_cr_pass_001); - tcase_add_test (tc_set_cr, test_set_cr_fail_001); - - TCase* tc_set_send_only = tcase_create ("set-send-only"); - suite_add_tcase (s, tc_set_send_only); - tcase_add_checked_fixture (tc_set_send_only, mock_setup, mock_teardown); - tcase_add_test (tc_set_send_only, test_set_send_only_pass_001); - tcase_add_test (tc_set_send_only, test_set_send_only_fail_001); - - TCase* tc_set_recv_only = tcase_create ("set-recv-only"); - suite_add_tcase (s, tc_set_recv_only); - tcase_add_checked_fixture (tc_set_recv_only, mock_setup, mock_teardown); - tcase_add_test (tc_set_recv_only, test_set_recv_only_pass_001); - tcase_add_test (tc_set_recv_only, test_set_recv_only_pass_002); - tcase_add_test (tc_set_recv_only, test_set_recv_only_fail_001); - tcase_add_test (tc_set_recv_only, test_set_recv_only_fail_002); - - TCase* tc_set_abort_on_reset = tcase_create ("set-abort-on-reset"); - suite_add_tcase (s, tc_set_abort_on_reset); - tcase_add_checked_fixture (tc_set_abort_on_reset, mock_setup, mock_teardown); - tcase_add_test (tc_set_abort_on_reset, test_set_abort_on_reset_pass_001); - tcase_add_test (tc_set_abort_on_reset, test_set_abort_on_reset_fail_001); - - TCase* tc_set_noblock = tcase_create ("set-non-blocking"); - suite_add_tcase (s, tc_set_noblock); - tcase_add_checked_fixture (tc_set_noblock, mock_setup, mock_teardown); - tcase_add_test (tc_set_noblock, test_set_noblock_pass_001); - tcase_add_test (tc_set_noblock, test_set_noblock_fail_001); - - TCase* tc_set_udp_unicast = tcase_create ("set-udp-encap-ucast-port"); - suite_add_tcase (s, tc_set_udp_unicast); - tcase_add_checked_fixture (tc_set_udp_unicast, mock_setup, mock_teardown); - tcase_add_test (tc_set_udp_unicast, test_set_udp_unicast_pass_001); - tcase_add_test (tc_set_udp_unicast, test_set_udp_unicast_fail_001); - - TCase* tc_set_udp_multicast = tcase_create ("set-udp-encap-mcast-port"); - suite_add_tcase (s, tc_set_udp_multicast); - tcase_add_checked_fixture (tc_set_udp_multicast, mock_setup, mock_teardown); - tcase_add_test (tc_set_udp_multicast, test_set_udp_multicast_pass_001); - tcase_add_test (tc_set_udp_multicast, test_set_udp_multicast_fail_001); - - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/source.c b/3rdparty/openpgm-svn-r1085/pgm/source.c deleted file mode 100644 index 12a61b6..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/source.c +++ /dev/null @@ -1,2339 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * PGM source socket. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define __STDC_FORMAT_MACROS -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -//#define SOURCE_DEBUG - -#ifndef SOURCE_DEBUG -# define PGM_DISABLE_ASSERT -#endif - -#if !defined(ENOBUFS) && defined(WSAENOBUFS) -# define ENOBUFS WSAENOBUFS -#endif - - -/* locals */ -static inline bool peer_is_source (const pgm_peer_t*) PGM_GNUC_CONST; -static inline bool peer_is_peer (const pgm_peer_t*) PGM_GNUC_CONST; -static void reset_heartbeat_spm (pgm_sock_t*const, const pgm_time_t); -static bool send_ncf (pgm_sock_t*const restrict, const struct sockaddr*const restrict, const struct sockaddr*const restrict, const uint32_t, const bool); -static bool send_ncf_list (pgm_sock_t*const restrict, const struct sockaddr*const restrict, const struct sockaddr*restrict, struct pgm_sqn_list_t*const restrict, const bool); -static int send_odata (pgm_sock_t*const restrict, struct pgm_sk_buff_t*const restrict, size_t*restrict); -static int send_odata_copy (pgm_sock_t*const restrict, const void*restrict, const uint16_t, size_t*restrict); -static int send_odatav (pgm_sock_t*const restrict, const struct pgm_iovec*const restrict, const unsigned, size_t*restrict); -static bool send_rdata (pgm_sock_t*const restrict, struct pgm_sk_buff_t*const restrict); - - -static inline -unsigned -_pgm_popcount ( - uint32_t n - ) -{ -#if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) - return __builtin_popcount (n); -#else -/* MIT HAKMEM 169 */ - const uint32_t t = n - ((n >> 1) & 033333333333) - - ((n >> 2) & 011111111111); - return ((t + (t >> 3) & 030707070707)) % 63; -#endif -} - -static inline -bool -peer_is_source ( - const pgm_peer_t* peer - ) -{ - return (NULL == peer); -} - -static inline -bool -peer_is_peer ( - const pgm_peer_t* peer - ) -{ - return (NULL != peer); -} - -static inline -void -reset_spmr_timer ( - pgm_peer_t* const peer - ) -{ - peer->spmr_expiry = 0; -} - -static inline -size_t -source_max_tsdu ( - const pgm_sock_t* sock, - const bool can_fragment - ) -{ - size_t max_tsdu = can_fragment ? sock->max_tsdu_fragment : sock->max_tsdu; - if (sock->use_var_pktlen /* OPT_VAR_PKT_LEN */) - max_tsdu -= sizeof (uint16_t); - return max_tsdu; -} - -/* prototype of function to send pro-active parity NAKs. - */ -static -bool -pgm_schedule_proactive_nak ( - pgm_sock_t* sock, - uint32_t nak_tg_sqn /* transmission group (shifted) */ - ) -{ - pgm_return_val_if_fail (NULL != sock, FALSE); - const bool status = pgm_txw_retransmit_push (sock->window, - nak_tg_sqn | sock->rs_proactive_h, - TRUE /* is_parity */, - sock->tg_sqn_shift); - return status; -} - -/* a deferred request for RDATA, now processing in the timer thread, we check the transmit - * window to see if the packet exists and forward on, maintaining a lock until the queue is - * empty. - * - * returns TRUE on success, returns FALSE if operation would block. - */ - -bool -pgm_on_deferred_nak ( - pgm_sock_t* const sock - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - -/* We can flush queue and block all odata, or process one set, or process each - * sequence number individually. - */ - -/* parity packets are re-numbered across the transmission group with index h, sharing the space - * with the original packets. beyond the transmission group size (k), the PGM option OPT_PARITY_GRP - * provides the extra offset value. - */ - -/* peek from the retransmit queue so we can eliminate duplicate NAKs up until the repair packet - * has been retransmitted. - */ - pgm_spinlock_lock (&sock->txw_spinlock); - struct pgm_sk_buff_t* skb = pgm_txw_retransmit_try_peek (sock->window); - if (skb) { - skb = pgm_skb_get (skb); - pgm_spinlock_unlock (&sock->txw_spinlock); - if (!send_rdata (sock, skb)) { - pgm_free_skb (skb); - pgm_notify_send (&sock->rdata_notify); - return FALSE; - } - pgm_free_skb (skb); -/* now remove sequence number from retransmit queue, re-enabling NAK processing for this sequence number */ - pgm_txw_retransmit_remove_head (sock->window); - } else - pgm_spinlock_unlock (&sock->txw_spinlock); - return TRUE; -} - -/* SPMR indicates if multicast to cancel own SPMR, or unicast to send SPM. - * - * rate limited to 1/IHB_MIN per TSI (13.4). - * - * if SPMR was valid, returns TRUE, if invalid returns FALSE. - */ - -bool -pgm_on_spmr ( - pgm_sock_t* const restrict sock, - pgm_peer_t* const restrict peer, /* maybe NULL if socket is source */ - struct pgm_sk_buff_t* const restrict skb - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != skb); - - pgm_debug ("pgm_on_spmr (sock:%p peer:%p skb:%p)", - (void*)sock, (void*)peer, (void*)skb); - - if (PGM_UNLIKELY(!pgm_verify_spmr (skb))) { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed SPMR rejected.")); - return FALSE; - } - - if (peer_is_source (peer)) { - const bool send_status = pgm_send_spm (sock, 0); - if (PGM_UNLIKELY(!send_status)) { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Failed to send SPM on SPM-Request.")); - } - } else { - pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Suppressing SPMR due to peer multicast SPMR.")); - reset_spmr_timer (peer); - } - return TRUE; -} - -/* Process opt_pgmcc_feedback PGM option that ships attached to ACK or NAK. - * Contents use to elect best ACKer. - * - * returns TRUE if peer is the elected ACKer. - */ - -static -bool -on_opt_pgmcc_feedback ( - pgm_sock_t* const restrict sock, - const struct pgm_sk_buff_t* const restrict skb, - const struct pgm_opt_pgmcc_feedback* restrict opt_pgmcc_feedback - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != skb); - pgm_assert (NULL != opt_pgmcc_feedback); - - const uint32_t opt_tstamp = ntohl (opt_pgmcc_feedback->opt_tstamp); - const uint16_t opt_loss_rate = ntohs (opt_pgmcc_feedback->opt_loss_rate); - - const uint32_t rtt = pgm_to_msecs (skb->tstamp) - opt_tstamp; - const uint64_t peer_loss = rtt * rtt * opt_loss_rate; - - struct sockaddr_storage peer_nla; - pgm_nla_to_sockaddr (&opt_pgmcc_feedback->opt_nla_afi, (struct sockaddr*)&peer_nla); - -/* ACKer elections */ - if (PGM_UNLIKELY(pgm_sockaddr_is_addr_unspecified ((const struct sockaddr*)&sock->acker_nla))) - { - pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Elected first ACKer")); - memcpy (&sock->acker_nla, &peer_nla, pgm_sockaddr_storage_len (&peer_nla)); - } - else if (peer_loss > sock->acker_loss && - 0 != pgm_sockaddr_cmp ((const struct sockaddr*)&peer_nla, (const struct sockaddr*)&sock->acker_nla)) - { - pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Elected new ACKer")); - memcpy (&sock->acker_nla, &peer_nla, pgm_sockaddr_storage_len (&peer_nla)); - } - -/* update ACKer state */ - if (0 == pgm_sockaddr_cmp ((const struct sockaddr*)&peer_nla, (const struct sockaddr*)&sock->acker_nla)) - { - sock->acker_loss = peer_loss; - return TRUE; - } - - return FALSE; -} - -/* NAK requesting RDATA transmission for a sending sock, only valid if - * sequence number(s) still in transmission window. - * - * we can potentially have different IP versions for the NAK packet to the send group. - * - * TODO: fix IPv6 AFIs - * - * take in a NAK and pass off to an asynchronous queue for another thread to process - * - * if NAK is valid, returns TRUE. on error, FALSE is returned. - */ - -bool -pgm_on_nak ( - pgm_sock_t* const restrict sock, - struct pgm_sk_buff_t* const restrict skb - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != skb); - - pgm_debug ("pgm_on_nak (sock:%p skb:%p)", - (const void*)sock, (const void*)skb); - - const bool is_parity = skb->pgm_header->pgm_options & PGM_OPT_PARITY; - if (is_parity) { - sock->cumulative_stats[PGM_PC_SOURCE_PARITY_NAKS_RECEIVED]++; - if (!sock->use_ondemand_parity) { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Parity NAK rejected as on-demand parity is not enabled.")); - sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; - return FALSE; - } - } else - sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED]++; - - if (PGM_UNLIKELY(!pgm_verify_nak (skb))) { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed NAK rejected.")); - sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; - return FALSE; - } - - const struct pgm_nak* nak = (struct pgm_nak*) skb->data; - const struct pgm_nak6* nak6 = (struct pgm_nak6*)skb->data; - -/* NAK_SRC_NLA contains our sock unicast NLA */ - struct sockaddr_storage nak_src_nla; - pgm_nla_to_sockaddr (&nak->nak_src_nla_afi, (struct sockaddr*)&nak_src_nla); - if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&nak_src_nla, (struct sockaddr*)&sock->send_addr) != 0)) - { - char saddr[INET6_ADDRSTRLEN]; - pgm_sockaddr_ntop ((struct sockaddr*)&nak_src_nla, saddr, sizeof(saddr)); - pgm_trace (PGM_LOG_ROLE_NETWORK,_("NAK rejected for unmatched NLA: %s"), saddr); - sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; - return FALSE; - } - -/* NAK_GRP_NLA containers our sock multicast group */ - struct sockaddr_storage nak_grp_nla; - pgm_nla_to_sockaddr ((AF_INET6 == nak_src_nla.ss_family) ? &nak6->nak6_grp_nla_afi : &nak->nak_grp_nla_afi, (struct sockaddr*)&nak_grp_nla); - if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&nak_grp_nla, (struct sockaddr*)&sock->send_gsr.gsr_group) != 0)) - { - char sgroup[INET6_ADDRSTRLEN]; - pgm_sockaddr_ntop ((struct sockaddr*)&nak_src_nla, sgroup, sizeof(sgroup)); - pgm_trace (PGM_LOG_ROLE_NETWORK,_("NAK rejected as targeted for different multicast group: %s"), sgroup); - sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; - return FALSE; - } - -/* create queue object */ - struct pgm_sqn_list_t sqn_list; - sqn_list.sqn[0] = ntohl (nak->nak_sqn); - sqn_list.len = 1; - - pgm_debug ("nak_sqn %" PRIu32, sqn_list.sqn[0]); - -/* check NAK list */ - const uint32_t* nak_list = NULL; - uint_fast8_t nak_list_len = 0; - if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) - { - const struct pgm_opt_length* opt_len = (AF_INET6 == nak_src_nla.ss_family) ? - (const struct pgm_opt_length*)(nak6 + 1) : - (const struct pgm_opt_length*)(nak + 1); - if (PGM_UNLIKELY(opt_len->opt_type != PGM_OPT_LENGTH)) { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed NAK rejected.")); - sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; - return FALSE; - } - if (PGM_UNLIKELY(opt_len->opt_length != sizeof(struct pgm_opt_length))) { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed NAK rejected.")); - sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; - return FALSE; - } -/* TODO: check for > 16 options & past packet end */ - const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; - do { - opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); - if ((opt_header->opt_type & PGM_OPT_MASK) == PGM_OPT_NAK_LIST) { - nak_list = ((const struct pgm_opt_nak_list*)(opt_header + 1))->opt_sqn; - nak_list_len = ( opt_header->opt_length - sizeof(struct pgm_opt_header) - sizeof(uint8_t) ) / sizeof(uint32_t); - break; - } - } while (!(opt_header->opt_type & PGM_OPT_END)); - } - -/* nak list numbers */ - if (PGM_UNLIKELY(nak_list_len > 63)) { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed NAK rejected on too long sequence list.")); - return FALSE; - } - - for (uint_fast8_t i = 0; i < nak_list_len; i++) - { - sqn_list.sqn[sqn_list.len++] = ntohl (*nak_list); - nak_list++; - } - -/* send NAK confirm packet immediately, then defer to timer thread for a.s.a.p - * delivery of the actual RDATA packets. blocking send for NCF is ignored as RDATA - * broadcast will be sent later. - */ - if (nak_list_len) - send_ncf_list (sock, (struct sockaddr*)&nak_src_nla, (struct sockaddr*)&nak_grp_nla, &sqn_list, is_parity); - else - send_ncf (sock, (struct sockaddr*)&nak_src_nla, (struct sockaddr*)&nak_grp_nla, sqn_list.sqn[0], is_parity); - -/* queue retransmit requests */ - for (uint_fast8_t i = 0; i < sqn_list.len; i++) { - const bool push_status = pgm_txw_retransmit_push (sock->window, sqn_list.sqn[i], is_parity, sock->tg_sqn_shift); - if (PGM_UNLIKELY(!push_status)) { - pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Failed to push retransmit request for #%" PRIu32), sqn_list.sqn[i]); - } - } - return TRUE; -} - -/* Null-NAK, or N-NAK propogated by a DLR for hand waving excitement - * - * if NNAK is valid, returns TRUE. on error, FALSE is returned. - */ - -bool -pgm_on_nnak ( - pgm_sock_t* const restrict sock, - struct pgm_sk_buff_t* const restrict skb - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != skb); - - pgm_debug ("pgm_on_nnak (sock:%p skb:%p)", - (void*)sock, (void*)skb); - - sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAK_PACKETS_RECEIVED]++; - - if (PGM_UNLIKELY(!pgm_verify_nnak (skb))) { - sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]++; - return FALSE; - } - - const struct pgm_nak* nnak = (struct pgm_nak*) skb->data; - const struct pgm_nak6* nnak6 = (struct pgm_nak6*)skb->data; - -/* NAK_SRC_NLA contains our sock unicast NLA */ - struct sockaddr_storage nnak_src_nla; - pgm_nla_to_sockaddr (&nnak->nak_src_nla_afi, (struct sockaddr*)&nnak_src_nla); - - if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&nnak_src_nla, (struct sockaddr*)&sock->send_addr) != 0)) - { - sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]++; - return FALSE; - } - -/* NAK_GRP_NLA containers our sock multicast group */ - struct sockaddr_storage nnak_grp_nla; - pgm_nla_to_sockaddr ((AF_INET6 == nnak_src_nla.ss_family) ? &nnak6->nak6_grp_nla_afi : &nnak->nak_grp_nla_afi, (struct sockaddr*)&nnak_grp_nla); - if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&nnak_grp_nla, (struct sockaddr*)&sock->send_gsr.gsr_group) != 0)) - { - sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]++; - return FALSE; - } - -/* check NNAK list */ - uint_fast8_t nnak_list_len = 0; - if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) - { - const struct pgm_opt_length* opt_len = (AF_INET6 == nnak_src_nla.ss_family) ? - (const struct pgm_opt_length*)(nnak6 + 1) : - (const struct pgm_opt_length*)(nnak + 1); - if (PGM_UNLIKELY(opt_len->opt_type != PGM_OPT_LENGTH)) { - sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]++; - return FALSE; - } - if (PGM_UNLIKELY(opt_len->opt_length != sizeof(struct pgm_opt_length))) { - sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]++; - return FALSE; - } -/* TODO: check for > 16 options & past packet end */ - const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; - do { - opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); - if ((opt_header->opt_type & PGM_OPT_MASK) == PGM_OPT_NAK_LIST) { - nnak_list_len = ( opt_header->opt_length - sizeof(struct pgm_opt_header) - sizeof(uint8_t) ) / sizeof(uint32_t); - break; - } - } while (!(opt_header->opt_type & PGM_OPT_END)); - } - - sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAKS_RECEIVED] += 1 + nnak_list_len; - return TRUE; -} - -/* ACK, sent upstream by one selected ACKER for congestion control feedback. - * - * if ACK is valid, returns TRUE. on error, FALSE is returned. - */ - -bool -pgm_on_ack ( - pgm_sock_t* const restrict sock, - struct pgm_sk_buff_t* const restrict skb - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != skb); - - pgm_debug ("pgm_on_ack (sock:%p skb:%p)", - (const void*)sock, (const void*)skb); - - sock->cumulative_stats[PGM_PC_SOURCE_ACK_PACKETS_RECEIVED]++; - - if (PGM_UNLIKELY(!pgm_verify_ack (skb))) { - sock->cumulative_stats[PGM_PC_SOURCE_ACK_ERRORS]++; - return FALSE; - } - - if (!sock->use_pgmcc) - return FALSE; - - const struct pgm_ack* ack = (struct pgm_ack*)skb->data; - bool is_acker = FALSE; - -/* check PGMCC feedback option for new elections */ - if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) - { - const struct pgm_opt_length* opt_len = (const struct pgm_opt_length*)(ack + 1); - if (PGM_UNLIKELY(opt_len->opt_type != PGM_OPT_LENGTH)) { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed ACK rejected.")); - return FALSE; - } - if (PGM_UNLIKELY(opt_len->opt_length != sizeof(struct pgm_opt_length))) { - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed ACK rejected.")); - return FALSE; - } - const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; - do { - opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); - if ((opt_header->opt_type & PGM_OPT_MASK) == PGM_OPT_PGMCC_FEEDBACK) { - const struct pgm_opt_pgmcc_feedback* opt_pgmcc_feedback = (const struct pgm_opt_pgmcc_feedback*)(opt_header + 1); - is_acker = on_opt_pgmcc_feedback (sock, skb, opt_pgmcc_feedback); - break; /* ignore other options */ - } - } while (!(opt_header->opt_type & PGM_OPT_END)); - } - -/* ignore ACKs from other receivers or sessions */ - if (!is_acker) - return TRUE; - -/* reset ACK expiration */ - sock->next_crqst = 0; - -/* count new ACK sequences */ - const uint32_t ack_rx_max = ntohl (ack->ack_rx_max); - const int32_t delta = ack_rx_max - sock->ack_rx_max; -/* ignore older ACKs when multiple active ACKers */ - if (pgm_uint32_gt (ack_rx_max, sock->ack_rx_max)) - sock->ack_rx_max = ack_rx_max; - uint32_t ack_bitmap = ntohl (ack->ack_bitmap); - if (delta > 32) sock->ack_bitmap = 0; /* sequence jump ahead beyond past bitmap */ - else if (delta > 0) sock->ack_bitmap <<= delta; /* immediate sequence */ - else if (delta > -32) ack_bitmap <<= -delta; /* repair sequence scoped by bitmap */ - else ack_bitmap = 0; /* old sequence */ - unsigned new_acks = _pgm_popcount (ack_bitmap & ~sock->ack_bitmap); - sock->ack_bitmap |= ack_bitmap; - - if (0 == new_acks) - return TRUE; - - const bool is_congestion_limited = (sock->tokens < pgm_fp8 (1)); - -/* after loss detection cancel any further manipulation of the window - * until feedback is received for the next transmitted packet. - */ - if (sock->is_congested) - { - if (pgm_uint32_lte (ack_rx_max, sock->suspended_sqn)) - { - pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("PGMCC window token manipulation suspended due to congestion (T:%u W:%u)"), - pgm_fp8tou (sock->tokens), pgm_fp8tou (sock->cwnd_size)); - const uint_fast32_t token_inc = pgm_fp8mul (pgm_fp8 (new_acks), pgm_fp8 (1) + pgm_fp8div (pgm_fp8 (1), sock->cwnd_size)); - sock->tokens = MIN( sock->tokens + token_inc, sock->cwnd_size ); - goto notify_tx; - } - sock->is_congested = FALSE; - } - -/* count outstanding lost sequences */ - const unsigned total_lost = _pgm_popcount (~sock->ack_bitmap); - -/* no detected data loss at ACKer, increase congestion window size */ - if (0 == total_lost) - { - new_acks += sock->acks_after_loss; - sock->acks_after_loss = 0; - uint_fast32_t n = pgm_fp8 (new_acks); - uint_fast32_t token_inc = 0; - -/* slow-start phase, exponential increase to SSTHRESH */ - if (sock->cwnd_size < sock->ssthresh) { - const uint_fast32_t d = MIN( n, sock->ssthresh - sock->cwnd_size ); - n -= d; - token_inc = d + d; - sock->cwnd_size += d; - } - - const uint_fast32_t iw = pgm_fp8div (pgm_fp8 (1), sock->cwnd_size); - -/* linear window increase */ - token_inc += pgm_fp8mul (n, pgm_fp8 (1) + iw); - sock->cwnd_size += pgm_fp8mul (n, iw); - sock->tokens = MIN( sock->tokens + token_inc, sock->cwnd_size ); -// pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("PGMCC++ (T:%u W:%u)"), -// pgm_fp8tou (sock->tokens), pgm_fp8tou (sock->cwnd_size)); - } - else - { -/* Look for an unacknowledged data packet which is followed by at least three - * acknowledged data packets, then the packet is assumed to be lost and PGMCC - * reacts by halving the window. - * - * Common value will be 0xfffffff7. - */ - sock->acks_after_loss += new_acks; - if (sock->acks_after_loss >= 3) - { - sock->acks_after_loss = 0; - sock->suspended_sqn = ack_rx_max; - sock->is_congested = TRUE; - sock->cwnd_size = pgm_fp8div (sock->cwnd_size, pgm_fp8 (2)); - if (sock->cwnd_size > sock->tokens) - sock->tokens = 0; - else - sock->tokens -= sock->cwnd_size; - sock->ack_bitmap = 0xffffffff; - pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("PGMCC congestion, half window size (T:%u W:%u)"), - pgm_fp8tou (sock->tokens), pgm_fp8tou (sock->cwnd_size)); - } - } - -/* token is now available so notify tx thread that transmission time is available */ -notify_tx: - if (is_congestion_limited && - sock->tokens >= pgm_fp8 (1)) - { - pgm_notify_send (&sock->ack_notify); - } - return TRUE; -} - -/* ambient/heartbeat SPM's - * - * heartbeat: ihb_tmr decaying between ihb_min and ihb_max 2x after last packet - * - * on success, TRUE is returned, if operation would block, FALSE is returned. - */ - -bool -pgm_send_spm ( - pgm_sock_t* const sock, - const int flags - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != sock->window); - - pgm_debug ("pgm_send_spm (sock:%p flags:%d)", - (const void*)sock, flags); - - size_t tpdu_length = sizeof(struct pgm_header); - if (AF_INET == sock->send_gsr.gsr_group.ss_family) - tpdu_length += sizeof(struct pgm_spm); - else - tpdu_length += sizeof(struct pgm_spm6); - if (sock->use_proactive_parity || - sock->use_ondemand_parity || - sock->is_pending_crqst || - PGM_OPT_FIN == flags) - { - tpdu_length += sizeof(struct pgm_opt_length); -/* forward error correction */ - if (sock->use_proactive_parity || - sock->use_ondemand_parity) - tpdu_length += sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_parity_prm); -/* congestion report request */ - if (sock->is_pending_crqst) - tpdu_length += sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_crqst); -/* end of session */ - if (PGM_OPT_FIN == flags) - tpdu_length += sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_fin); - } - char buf[ tpdu_length ]; - if (PGM_UNLIKELY(pgm_mem_gc_friendly)) - memset (buf, 0, tpdu_length); - struct pgm_header* header = (struct pgm_header*)buf; - struct pgm_spm* spm = (struct pgm_spm *)(header + 1); - struct pgm_spm6* spm6 = (struct pgm_spm6*)(header + 1); - memcpy (header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); - header->pgm_sport = sock->tsi.sport; - header->pgm_dport = sock->dport; - header->pgm_type = PGM_SPM; - header->pgm_options = 0; - header->pgm_tsdu_length = 0; - -/* SPM */ - spm->spm_sqn = htonl (sock->spm_sqn); - spm->spm_trail = htonl (pgm_txw_trail_atomic (sock->window)); - spm->spm_lead = htonl (pgm_txw_lead_atomic (sock->window)); - spm->spm_reserved = 0; -/* our nla */ - pgm_sockaddr_to_nla ((struct sockaddr*)&sock->send_addr, (char*)&spm->spm_nla_afi); - -/* PGM options */ - if (sock->use_proactive_parity || - sock->use_ondemand_parity || - sock->is_pending_crqst || - PGM_OPT_FIN == flags) - { - struct pgm_opt_length* opt_len; - struct pgm_opt_header *opt_header, *last_opt_header; - uint16_t opt_total_length; - - if (AF_INET == sock->send_gsr.gsr_group.ss_family) - opt_header = (struct pgm_opt_header*)(spm + 1); - else - opt_header = (struct pgm_opt_header*)(spm6 + 1); - header->pgm_options |= PGM_OPT_PRESENT; - opt_len = (struct pgm_opt_length*)opt_header; - opt_len->opt_type = PGM_OPT_LENGTH; - opt_len->opt_length = sizeof(struct pgm_opt_length); - opt_total_length = sizeof(struct pgm_opt_length); - last_opt_header = opt_header = (struct pgm_opt_header*)(opt_len + 1); - -/* OPT_PARITY_PRM */ - if (sock->use_proactive_parity || - sock->use_ondemand_parity) - { - header->pgm_options |= PGM_OPT_NETWORK; - opt_total_length += sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_parity_prm); - opt_header->opt_type = PGM_OPT_PARITY_PRM; - opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_parity_prm); - struct pgm_opt_parity_prm* opt_parity_prm = (struct pgm_opt_parity_prm*)(opt_header + 1); - opt_parity_prm->opt_reserved = (sock->use_proactive_parity ? PGM_PARITY_PRM_PRO : 0) | - (sock->use_ondemand_parity ? PGM_PARITY_PRM_OND : 0); - opt_parity_prm->parity_prm_tgs = htonl (sock->rs_k); - last_opt_header = opt_header; - opt_header = (struct pgm_opt_header*)(opt_parity_prm + 1); - } - -/* OPT_CRQST */ - if (sock->is_pending_crqst) - { - header->pgm_options |= PGM_OPT_NETWORK; - opt_total_length += sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_crqst); - opt_header->opt_type = PGM_OPT_CRQST; - opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_crqst); - struct pgm_opt_crqst* opt_crqst = (struct pgm_opt_crqst*)(opt_header + 1); -/* request receiver worst path report, OPT_CR_RX_WP */ - opt_crqst->opt_reserved = PGM_OPT_CRQST_RXP; - sock->is_pending_crqst = FALSE; - last_opt_header = opt_header; - opt_header = (struct pgm_opt_header*)(opt_crqst + 1); - } - -/* OPT_FIN */ - if (PGM_OPT_FIN == flags) - { - opt_total_length += sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_fin); - opt_header->opt_type = PGM_OPT_FIN; - opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_fin); - struct pgm_opt_fin* opt_fin = (struct pgm_opt_fin*)(opt_header + 1); - opt_fin->opt_reserved = 0; - last_opt_header = opt_header; - opt_header = (struct pgm_opt_header*)(opt_fin + 1); - } - - last_opt_header->opt_type |= PGM_OPT_END; - opt_len->opt_total_length = htons (opt_total_length); - } - -/* checksum optional for SPMs */ - header->pgm_checksum = 0; - header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); - - const ssize_t sent = pgm_sendto (sock, - flags != PGM_OPT_SYN && sock->is_controlled_spm, /* rate limited */ - TRUE, /* with router alert */ - buf, - tpdu_length, - (struct sockaddr*)&sock->send_gsr.gsr_group, - pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); - if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { - sock->blocklen = tpdu_length; - return FALSE; - } -/* advance SPM sequence only on successful transmission */ - sock->spm_sqn++; - pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length); - return TRUE; -} - -/* send a NAK confirm (NCF) message with provided sequence number list. - * - * on success, TRUE is returned, returns FALSE if operation would block. - */ - -static -bool -send_ncf ( - pgm_sock_t* const restrict sock, - const struct sockaddr* const restrict nak_src_nla, - const struct sockaddr* const restrict nak_grp_nla, - const uint32_t sequence, - const bool is_parity /* send parity NCF */ - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != nak_src_nla); - pgm_assert (NULL != nak_grp_nla); - pgm_assert (nak_src_nla->sa_family == nak_grp_nla->sa_family); - -#ifdef SOURCE_DEBUG - char saddr[INET6_ADDRSTRLEN], gaddr[INET6_ADDRSTRLEN]; - pgm_sockaddr_ntop (nak_src_nla, saddr, sizeof(saddr)); - pgm_sockaddr_ntop (nak_grp_nla, gaddr, sizeof(gaddr)); - pgm_debug ("send_ncf (sock:%p nak-src-nla:%s nak-grp-nla:%s sequence:%" PRIu32" is-parity:%s)", - (void*)sock, - saddr, - gaddr, - sequence, - is_parity ? "TRUE": "FALSE" - ); -#endif - - size_t tpdu_length = sizeof(struct pgm_header); - tpdu_length += (AF_INET == nak_src_nla->sa_family) ? sizeof(struct pgm_nak) : sizeof(struct pgm_nak6); - char buf[ tpdu_length ]; - struct pgm_header* header = (struct pgm_header*)buf; - struct pgm_nak* ncf = (struct pgm_nak *)(header + 1); - struct pgm_nak6* ncf6 = (struct pgm_nak6*)(header + 1); - memcpy (header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); - header->pgm_sport = sock->tsi.sport; - header->pgm_dport = sock->dport; - header->pgm_type = PGM_NCF; - header->pgm_options = is_parity ? PGM_OPT_PARITY : 0; - header->pgm_tsdu_length = 0; - -/* NCF */ - ncf->nak_sqn = htonl (sequence); - -/* source nla */ - pgm_sockaddr_to_nla (nak_src_nla, (char*)&ncf->nak_src_nla_afi); - -/* group nla */ - pgm_sockaddr_to_nla (nak_grp_nla, (AF_INET6 == nak_src_nla->sa_family) ? (char*)&ncf6->nak6_grp_nla_afi : (char*)&ncf->nak_grp_nla_afi ); - header->pgm_checksum = 0; - header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); - - const ssize_t sent = pgm_sendto (sock, - FALSE, /* not rate limited */ - TRUE, /* with router alert */ - buf, - tpdu_length, - (struct sockaddr*)&sock->send_gsr.gsr_group, - pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); - if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) - return FALSE; - pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length); - return TRUE; -} - -/* A NCF packet with a OPT_NAK_LIST option extension - * - * on success, TRUE is returned. on error, FALSE is returned. - */ - -static -bool -send_ncf_list ( - pgm_sock_t* const restrict sock, - const struct sockaddr* const restrict nak_src_nla, - const struct sockaddr* const restrict nak_grp_nla, - struct pgm_sqn_list_t* const restrict sqn_list, /* will change to network-order */ - const bool is_parity /* send parity NCF */ - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != nak_src_nla); - pgm_assert (NULL != nak_grp_nla); - pgm_assert (sqn_list->len > 1); - pgm_assert (sqn_list->len <= 63); - pgm_assert (nak_src_nla->sa_family == nak_grp_nla->sa_family); - -#ifdef SOURCE_DEBUG - char saddr[INET6_ADDRSTRLEN], gaddr[INET6_ADDRSTRLEN]; - char list[1024]; - pgm_sockaddr_ntop (nak_src_nla, saddr, sizeof(saddr)); - pgm_sockaddr_ntop (nak_grp_nla, gaddr, sizeof(gaddr)); - sprintf (list, "%" PRIu32, sqn_list->sqn[0]); - for (uint_fast8_t i = 1; i < sqn_list->len; i++) { - char sequence[ 2 + strlen("4294967295") ]; - sprintf (sequence, " %" PRIu32, sqn_list->sqn[i]); - strcat (list, sequence); - } - pgm_debug ("send_ncf_list (sock:%p nak-src-nla:%s nak-grp-nla:%s sqn-list:[%s] is-parity:%s)", - (void*)sock, - saddr, - gaddr, - list, - is_parity ? "TRUE": "FALSE" - ); -#endif - - size_t tpdu_length = sizeof(struct pgm_header) + - sizeof(struct pgm_opt_length) + /* includes header */ - sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + - ( (sqn_list->len-1) * sizeof(uint32_t) ); - tpdu_length += (AF_INET == nak_src_nla->sa_family) ? sizeof(struct pgm_nak) : sizeof(struct pgm_nak6); - char buf[ tpdu_length ]; - struct pgm_header* header = (struct pgm_header*)buf; - struct pgm_nak* ncf = (struct pgm_nak *)(header + 1); - struct pgm_nak6* ncf6 = (struct pgm_nak6*)(header + 1); - memcpy (header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); - header->pgm_sport = sock->tsi.sport; - header->pgm_dport = sock->dport; - header->pgm_type = PGM_NCF; - header->pgm_options = is_parity ? (PGM_OPT_PRESENT | PGM_OPT_NETWORK | PGM_OPT_PARITY) : (PGM_OPT_PRESENT | PGM_OPT_NETWORK); - header->pgm_tsdu_length = 0; -/* NCF */ - ncf->nak_sqn = htonl (sqn_list->sqn[0]); - -/* source nla */ - pgm_sockaddr_to_nla (nak_src_nla, (char*)&ncf->nak_src_nla_afi); - -/* group nla */ - pgm_sockaddr_to_nla (nak_grp_nla, (AF_INET6 == nak_src_nla->sa_family) ? (char*)&ncf6->nak6_grp_nla_afi : (char*)&ncf->nak_grp_nla_afi ); - -/* OPT_NAK_LIST */ - struct pgm_opt_length* opt_len = (AF_INET6 == nak_src_nla->sa_family) ? (struct pgm_opt_length*)(ncf6 + 1) : (struct pgm_opt_length*)(ncf + 1); - opt_len->opt_type = PGM_OPT_LENGTH; - opt_len->opt_length = sizeof(struct pgm_opt_length); - opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + - sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_nak_list) + - ( (sqn_list->len-1) * sizeof(uint32_t) ) ); - struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); - opt_header->opt_type = PGM_OPT_NAK_LIST | PGM_OPT_END; - opt_header->opt_length = sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_nak_list) + - ( (sqn_list->len-1) * sizeof(uint32_t) ); - struct pgm_opt_nak_list* opt_nak_list = (struct pgm_opt_nak_list*)(opt_header + 1); - opt_nak_list->opt_reserved = 0; -/* to network-order */ - for (uint_fast8_t i = 1; i < sqn_list->len; i++) - opt_nak_list->opt_sqn[i-1] = htonl (sqn_list->sqn[i]); - - header->pgm_checksum = 0; - header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); - - const ssize_t sent = pgm_sendto (sock, - FALSE, /* not rate limited */ - TRUE, /* with router alert */ - buf, - tpdu_length, - (struct sockaddr*)&sock->send_gsr.gsr_group, - pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); - if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) - return FALSE; - pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length); - return TRUE; -} - -/* cancel any pending heartbeat SPM and schedule a new one - */ - -static -void -reset_heartbeat_spm ( - pgm_sock_t* sock, - const pgm_time_t now - ) -{ - pgm_mutex_lock (&sock->timer_mutex); - const pgm_time_t next_poll = sock->next_poll; - const pgm_time_t spm_heartbeat_interval = sock->spm_heartbeat_interval[ sock->spm_heartbeat_state = 1 ]; - sock->next_heartbeat_spm = now + spm_heartbeat_interval; - if (pgm_time_after( next_poll, sock->next_heartbeat_spm )) - { - sock->next_poll = sock->next_heartbeat_spm; - if (!sock->is_pending_read) { - pgm_notify_send (&sock->pending_notify); - sock->is_pending_read = TRUE; - } - } - pgm_mutex_unlock (&sock->timer_mutex); -} - -/* state helper for resuming sends - */ -#define STATE(x) (sock->pkt_dontwait_state.x) - -/* send one PGM data packet, transmit window owned memory. - * - * On success, returns PGM_IO_STATUS_NORMAL and the number of data bytes pushed - * into the transmit window and attempted to send to the socket layer is saved - * into bytes_written. On non-blocking sockets, PGM_IO_STATUS_WOULD_BLOCK is - * returned if the send would block. PGM_IO_STATUS_RATE_LIMITED is returned if - * the packet sizes would exceed the current rate limit. - * - * ! always returns successful if data is pushed into the transmit window, even if - * sendto() double fails ¡ we don't want the application to try again as that is the - * reliable socks role. - */ - -static -int -send_odata ( - pgm_sock_t* const restrict sock, - struct pgm_sk_buff_t* const restrict skb, - size_t* restrict bytes_written - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != skb); - pgm_assert (skb->len <= sock->max_tsdu); - - pgm_debug ("send_odata (sock:%p skb:%p bytes-written:%p)", - (void*)sock, (void*)skb, (void*)bytes_written); - - const uint16_t tsdu_length = skb->len; - const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; - const size_t tpdu_length = tsdu_length + pgm_pkt_offset (FALSE, pgmcc_family); - -/* continue if send would block */ - if (sock->is_apdu_eagain) { - STATE(skb)->tstamp = pgm_time_update_now(); - goto retry_send; - } - -/* add PGM header to skbuff */ - STATE(skb) = pgm_skb_get(skb); - STATE(skb)->sock = sock; - STATE(skb)->tstamp = pgm_time_update_now(); - - STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->head; - STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); - memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); - STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; - STATE(skb)->pgm_header->pgm_dport = sock->dport; - STATE(skb)->pgm_header->pgm_type = PGM_ODATA; - STATE(skb)->pgm_header->pgm_options = sock->use_pgmcc ? PGM_OPT_PRESENT : 0; - STATE(skb)->pgm_header->pgm_tsdu_length = htons (tsdu_length); - -/* ODATA */ - STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); - STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); - - STATE(skb)->pgm_header->pgm_checksum = 0; - void* data = STATE(skb)->pgm_data + 1; - if (sock->use_pgmcc) { - struct pgm_opt_length* opt_len = data; - opt_len->opt_type = PGM_OPT_LENGTH; - opt_len->opt_length = sizeof(struct pgm_opt_length); - opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + - sizeof(struct pgm_opt_header) + - ((AF_INET6 == sock->acker_nla.ss_family) ? - sizeof(struct pgm_opt6_pgmcc_data) : - sizeof(struct pgm_opt_pgmcc_data)) ); - struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); - opt_header->opt_type = PGM_OPT_PGMCC_DATA | PGM_OPT_END; - opt_header->opt_length = sizeof(struct pgm_opt_header) + - ((AF_INET6 == sock->acker_nla.ss_family) ? - sizeof(struct pgm_opt6_pgmcc_data) : - sizeof(struct pgm_opt_pgmcc_data)); - struct pgm_opt_pgmcc_data* pgmcc_data = (struct pgm_opt_pgmcc_data*)(opt_header + 1); - struct pgm_opt6_pgmcc_data* pgmcc_data6 = (struct pgm_opt6_pgmcc_data*)(opt_header + 1); - - pgmcc_data->opt_tstamp = htonl (pgm_to_msecs (STATE(skb)->tstamp)); -/* acker nla */ - pgm_sockaddr_to_nla ((struct sockaddr*)&sock->acker_nla, (char*)&pgmcc_data->opt_nla_afi); - if (AF_INET6 == sock->acker_nla.ss_family) - data = (char*)pgmcc_data6 + sizeof(struct pgm_opt6_pgmcc_data); - else - data = (char*)pgmcc_data + sizeof(struct pgm_opt_pgmcc_data); - } - const size_t pgm_header_len = (char*)data - (char*)STATE(skb)->pgm_header; - const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); - STATE(unfolded_odata) = pgm_csum_partial (data, tsdu_length, 0); - STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); - -/* add to transmit window, skb::data set to payload */ - pgm_spinlock_lock (&sock->txw_spinlock); - pgm_txw_add (sock->window, STATE(skb)); - pgm_spinlock_unlock (&sock->txw_spinlock); - -/* the transmit window MUST check the user count to ensure it does not - * attempt to send a repair-data packet based on in transit original data. - */ - - ssize_t sent; -retry_send: - -/* congestion control */ - if (sock->use_pgmcc && - sock->tokens < pgm_fp8 (1)) - { -// pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Token limit reached.")); - sock->is_apdu_eagain = TRUE; - sock->blocklen = tpdu_length; - return PGM_IO_STATUS_CONGESTION; /* peer expiration to re-elect ACKer */ - } - - sent = pgm_sendto (sock, - sock->is_controlled_odata, /* rate limited */ - FALSE, /* regular socket */ - STATE(skb)->head, - tpdu_length, - (struct sockaddr*)&sock->send_gsr.gsr_group, - pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); - if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { - sock->is_apdu_eagain = TRUE; - sock->blocklen = tpdu_length; - if (EAGAIN == errno) { - if (sock->use_pgmcc) - pgm_notify_clear (&sock->ack_notify); - return PGM_IO_STATUS_WOULD_BLOCK; - } - return PGM_IO_STATUS_RATE_LIMITED; - } - -/* save unfolded odata for retransmissions */ - pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); - - sock->is_apdu_eagain = FALSE; - reset_heartbeat_spm (sock, STATE(skb)->tstamp); - if (sock->use_pgmcc) { - sock->tokens -= pgm_fp8 (1); - sock->ack_expiry = STATE(skb)->tstamp + sock->ack_expiry_ivl; - } - - if (PGM_LIKELY((size_t)sent == tpdu_length)) { - sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += tsdu_length; - sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] ++; - pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length + sock->iphdr_len); - } - -/* check for end of transmission group */ - if (sock->use_proactive_parity) { - const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); - const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; - if (!((odata_sqn + 1) & ~tg_sqn_mask)) - pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); - } - -/* remove applications reference to skbuff */ - pgm_free_skb (STATE(skb)); - if (bytes_written) - *bytes_written = tsdu_length; - return PGM_IO_STATUS_NORMAL; -} - -/* send one PGM original data packet, callee owned memory. - * - * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets - * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if - * packet size exceeds the current rate limit. - */ - -static -int -send_odata_copy ( - pgm_sock_t* const restrict sock, - const void* restrict tsdu, - const uint16_t tsdu_length, - size_t* restrict bytes_written - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (tsdu_length <= sock->max_tsdu); - if (PGM_LIKELY(tsdu_length)) pgm_assert (NULL != tsdu); - - pgm_debug ("send_odata_copy (sock:%p tsdu:%p tsdu_length:%u bytes-written:%p)", - (void*)sock, tsdu, tsdu_length, (void*)bytes_written); - - const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; - const size_t tpdu_length = tsdu_length + pgm_pkt_offset (FALSE, pgmcc_family); - -/* continue if blocked mid-apdu, updating timestamp */ - if (sock->is_apdu_eagain) { - STATE(skb)->tstamp = pgm_time_update_now(); - goto retry_send; - } - - STATE(skb) = pgm_alloc_skb (sock->max_tpdu); - STATE(skb)->sock = sock; - STATE(skb)->tstamp = pgm_time_update_now(); - pgm_skb_reserve (STATE(skb), pgm_pkt_offset (FALSE, pgmcc_family)); - pgm_skb_put (STATE(skb), tsdu_length); - - STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->head; - STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); - memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); - STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; - STATE(skb)->pgm_header->pgm_dport = sock->dport; - STATE(skb)->pgm_header->pgm_type = PGM_ODATA; - STATE(skb)->pgm_header->pgm_options = sock->use_pgmcc ? PGM_OPT_PRESENT : 0; - STATE(skb)->pgm_header->pgm_tsdu_length = htons (tsdu_length); - -/* ODATA */ - STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); - STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); - - STATE(skb)->pgm_header->pgm_checksum = 0; - void* data = STATE(skb)->pgm_data + 1; - if (sock->use_pgmcc) { - struct pgm_opt_length* opt_len = data; - opt_len->opt_type = PGM_OPT_LENGTH; - opt_len->opt_length = sizeof(struct pgm_opt_length); - opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + - sizeof(struct pgm_opt_header) + - ((AF_INET6 == sock->acker_nla.ss_family) ? - sizeof(struct pgm_opt6_pgmcc_data) : - sizeof(struct pgm_opt_pgmcc_data)) ); - struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); - opt_header->opt_type = PGM_OPT_PGMCC_DATA | PGM_OPT_END; - opt_header->opt_length = sizeof(struct pgm_opt_header) + - ((AF_INET6 == sock->acker_nla.ss_family) ? - sizeof(struct pgm_opt6_pgmcc_data) : - sizeof(struct pgm_opt_pgmcc_data)); - struct pgm_opt_pgmcc_data* pgmcc_data = (struct pgm_opt_pgmcc_data*)(opt_header + 1); - struct pgm_opt6_pgmcc_data* pgmcc_data6 = (struct pgm_opt6_pgmcc_data*)(opt_header + 1); - - pgmcc_data->opt_reserved = 0; - pgmcc_data->opt_tstamp = htonl (pgm_to_msecs (STATE(skb)->tstamp)); -/* acker nla */ - pgm_sockaddr_to_nla ((struct sockaddr*)&sock->acker_nla, (char*)&pgmcc_data->opt_nla_afi); - data = (char*)opt_header + opt_header->opt_length; - } - const size_t pgm_header_len = (char*)data - (char*)STATE(skb)->pgm_header; - const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); - STATE(unfolded_odata) = pgm_csum_partial_copy (tsdu, data, tsdu_length, 0); - STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); - -/* add to transmit window, skb::data set to payload */ - pgm_spinlock_lock (&sock->txw_spinlock); - pgm_txw_add (sock->window, STATE(skb)); - pgm_spinlock_unlock (&sock->txw_spinlock); - - ssize_t sent; -retry_send: - -/* congestion control */ - if (sock->use_pgmcc && - sock->tokens < pgm_fp8 (1)) - { -// pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Token limit reached.")); - sock->is_apdu_eagain = TRUE; - sock->blocklen = tpdu_length; - return PGM_IO_STATUS_CONGESTION; - } - - sent = pgm_sendto (sock, - sock->is_controlled_odata, /* rate limited */ - FALSE, /* regular socket */ - STATE(skb)->head, - tpdu_length, - (struct sockaddr*)&sock->send_gsr.gsr_group, - pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); - if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { - sock->is_apdu_eagain = TRUE; - sock->blocklen = tpdu_length; - if (EAGAIN == errno) { - if (sock->use_pgmcc) - pgm_notify_clear (&sock->ack_notify); - return PGM_IO_STATUS_WOULD_BLOCK; - } - return PGM_IO_STATUS_RATE_LIMITED; - } - - if (sock->use_pgmcc) { - sock->tokens -= pgm_fp8 (1); - pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("PGMCC tokens-- (T:%u W:%u)"), - pgm_fp8tou (sock->tokens), pgm_fp8tou (sock->cwnd_size)); - sock->ack_expiry = STATE(skb)->tstamp + sock->ack_expiry_ivl; - } - -/* save unfolded odata for retransmissions */ - pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); - - sock->is_apdu_eagain = FALSE; - reset_heartbeat_spm (sock, STATE(skb)->tstamp); - - if (PGM_LIKELY((size_t)sent == tpdu_length)) { - sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += tsdu_length; - sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] ++; - pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length + sock->iphdr_len); - } - -/* check for end of transmission group */ - if (sock->use_proactive_parity) { - const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); - const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; - if (!((odata_sqn + 1) & ~tg_sqn_mask)) - pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); - } - -/* return data payload length sent */ - if (bytes_written) - *bytes_written = tsdu_length; - return PGM_IO_STATUS_NORMAL; -} - -/* send one PGM original data packet, callee owned scatter/gather io vector - * - * ⎢ DATA₀ ⎢ - * ⎢ DATA₁ ⎢ → send_odatav() → ⎢ TSDU₀ ⎢ → libc - * ⎢ ⋮ ⎢ - * - * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets - * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if - * packet size exceeds the current rate limit. - */ - -static -int -send_odatav ( - pgm_sock_t* const restrict sock, - const struct pgm_iovec* const restrict vector, - const unsigned count, /* number of items in vector */ - size_t* restrict bytes_written - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (count <= PGM_MAX_FRAGMENTS); - if (PGM_LIKELY(count)) pgm_assert (NULL != vector); - - pgm_debug ("send_odatav (sock:%p vector:%p count:%u bytes-written:%p)", - (const void*)sock, (const void*)vector, count, (const void*)bytes_written); - - if (PGM_UNLIKELY(0 == count)) - return send_odata_copy (sock, NULL, 0, bytes_written); - -/* continue if blocked on send */ - if (sock->is_apdu_eagain) - goto retry_send; - - STATE(tsdu_length) = 0; - for (unsigned i = 0; i < count; i++) - { -#ifdef TRANSPORT_DEBUG - if (PGM_LIKELY(vector[i].iov_len)) { - pgm_assert( vector[i].iov_base ); - } -#endif - STATE(tsdu_length) += vector[i].iov_len; - } - pgm_return_val_if_fail (STATE(tsdu_length) <= sock->max_tsdu, PGM_IO_STATUS_ERROR); - - STATE(skb) = pgm_alloc_skb (sock->max_tpdu); - STATE(skb)->sock = sock; - STATE(skb)->tstamp = pgm_time_update_now(); - const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; - pgm_skb_reserve (STATE(skb), pgm_pkt_offset (FALSE, pgmcc_family)); - pgm_skb_put (STATE(skb), STATE(tsdu_length)); - - STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->data; - STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); - memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); - STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; - STATE(skb)->pgm_header->pgm_dport = sock->dport; - STATE(skb)->pgm_header->pgm_type = PGM_ODATA; - STATE(skb)->pgm_header->pgm_options = 0; - STATE(skb)->pgm_header->pgm_tsdu_length = htons (STATE(tsdu_length)); - -/* ODATA */ - STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); - STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); - - STATE(skb)->pgm_header->pgm_checksum = 0; - const size_t pgm_header_len = (char*)(STATE(skb)->pgm_data + 1) - (char*)STATE(skb)->pgm_header; - const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); - -/* unroll first iteration to make friendly branch prediction */ - char* dst = (char*)(STATE(skb)->pgm_data + 1); - STATE(unfolded_odata) = pgm_csum_partial_copy ((const char*)vector[0].iov_base, dst, vector[0].iov_len, 0); - -/* iterate over one or more vector elements to perform scatter/gather checksum & copy */ - for (unsigned i = 1; i < count; i++) { - dst += vector[i-1].iov_len; - const uint32_t unfolded_element = pgm_csum_partial_copy ((const char*)vector[i].iov_base, dst, vector[i].iov_len, 0); - STATE(unfolded_odata) = pgm_csum_block_add (STATE(unfolded_odata), unfolded_element, vector[i-1].iov_len); - } - - STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); - -/* add to transmit window, skb::data set to payload */ - pgm_spinlock_lock (&sock->txw_spinlock); - pgm_txw_add (sock->window, STATE(skb)); - pgm_spinlock_unlock (&sock->txw_spinlock); - - ssize_t sent; - size_t tpdu_length; -retry_send: - pgm_assert ((char*)STATE(skb)->tail > (char*)STATE(skb)->head); - tpdu_length = (char*)STATE(skb)->tail - (char*)STATE(skb)->head; - sent = pgm_sendto (sock, - sock->is_controlled_odata, /* rate limited */ - FALSE, /* regular socket */ - STATE(skb)->head, - tpdu_length, - (struct sockaddr*)&sock->send_gsr.gsr_group, - pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); - if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { - sock->is_apdu_eagain = TRUE; - sock->blocklen = tpdu_length; - if (EAGAIN == errno) { - if (sock->use_pgmcc) - pgm_notify_clear (&sock->ack_notify); - return PGM_IO_STATUS_WOULD_BLOCK; - } - return PGM_IO_STATUS_RATE_LIMITED; - } - -/* save unfolded odata for retransmissions */ - pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); - - sock->is_apdu_eagain = FALSE; - reset_heartbeat_spm (sock, STATE(skb)->tstamp); - - if (PGM_LIKELY((size_t)sent == STATE(skb)->len)) { - sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += STATE(tsdu_length); - sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] ++; - pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length + sock->iphdr_len); - } - -/* check for end of transmission group */ - if (sock->use_proactive_parity) { - const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); - const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; - if (!((odata_sqn + 1) & ~tg_sqn_mask)) - pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); - } - -/* return data payload length sent */ - if (bytes_written) - *bytes_written = STATE(tsdu_length); - return PGM_IO_STATUS_NORMAL; -} - -/* send PGM original data, callee owned memory. if larger than maximum TPDU - * size will be fragmented. - * - * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets - * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if - * packet size exceeds the current rate limit. - */ - -static -int -send_apdu ( - pgm_sock_t* const restrict sock, - const void* restrict apdu, - const size_t apdu_length, - size_t* restrict bytes_written - ) -{ - size_t bytes_sent = 0; /* counted at IP layer */ - unsigned packets_sent = 0; /* IP packets */ - size_t data_bytes_sent = 0; - const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; - - pgm_assert (NULL != sock); - pgm_assert (NULL != apdu); - -/* continue if blocked mid-apdu */ - if (sock->is_apdu_eagain) - goto retry_send; - -/* if non-blocking calculate total wire size and check rate limit */ - STATE(is_rate_limited) = FALSE; - if (sock->is_nonblocking && sock->is_controlled_odata) - { - const size_t header_length = pgm_pkt_offset (TRUE, pgmcc_family); - size_t tpdu_length = 0; - size_t offset_ = 0; - do { - const uint16_t tsdu_length = MIN( source_max_tsdu (sock, TRUE), apdu_length - offset_ ); - tpdu_length += sock->iphdr_len + header_length + tsdu_length; - offset_ += tsdu_length; - } while (offset_ < apdu_length); - -/* calculation includes one iphdr length already */ - if (!pgm_rate_check (&sock->rate_control, - tpdu_length - sock->iphdr_len, - sock->is_nonblocking)) - { - sock->blocklen = tpdu_length; - return PGM_IO_STATUS_RATE_LIMITED; - } - STATE(is_rate_limited) = TRUE; - } - - STATE(data_bytes_offset) = 0; - STATE(first_sqn) = pgm_txw_next_lead(sock->window); - - do { -/* retrieve packet storage from transmit window */ - size_t header_length = pgm_pkt_offset (TRUE, pgmcc_family); - STATE(tsdu_length) = MIN( source_max_tsdu (sock, TRUE), apdu_length - STATE(data_bytes_offset) ); - - STATE(skb) = pgm_alloc_skb (sock->max_tpdu); - STATE(skb)->sock = sock; - STATE(skb)->tstamp = pgm_time_update_now(); - pgm_skb_reserve (STATE(skb), header_length); - pgm_skb_put (STATE(skb), STATE(tsdu_length)); - - STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->head; - STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); - memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); - STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; - STATE(skb)->pgm_header->pgm_dport = sock->dport; - STATE(skb)->pgm_header->pgm_type = PGM_ODATA; - STATE(skb)->pgm_header->pgm_options = PGM_OPT_PRESENT; - STATE(skb)->pgm_header->pgm_tsdu_length = htons (STATE(tsdu_length)); - -/* ODATA */ - STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); - STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); - -/* OPT_LENGTH */ - struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(STATE(skb)->pgm_data + 1); - opt_len->opt_type = PGM_OPT_LENGTH; - opt_len->opt_length = sizeof(struct pgm_opt_length); - opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + - sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_fragment) ); -/* OPT_FRAGMENT */ - struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); - opt_header->opt_type = PGM_OPT_FRAGMENT | PGM_OPT_END; - opt_header->opt_length = sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_fragment); - STATE(skb)->pgm_opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); - STATE(skb)->pgm_opt_fragment->opt_reserved = 0; - STATE(skb)->pgm_opt_fragment->opt_sqn = htonl (STATE(first_sqn)); - STATE(skb)->pgm_opt_fragment->opt_frag_off = htonl (STATE(data_bytes_offset)); - STATE(skb)->pgm_opt_fragment->opt_frag_len = htonl (apdu_length); - -/* TODO: the assembly checksum & copy routine is faster than memcpy & pgm_cksum on >= opteron hardware */ - STATE(skb)->pgm_header->pgm_checksum = 0; - const size_t pgm_header_len = (char*)(STATE(skb)->pgm_opt_fragment + 1) - (char*)STATE(skb)->pgm_header; - const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); - STATE(unfolded_odata) = pgm_csum_partial_copy ((const char*)apdu + STATE(data_bytes_offset), STATE(skb)->pgm_opt_fragment + 1, STATE(tsdu_length), 0); - STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); - -/* add to transmit window, skb::data set to payload */ - pgm_spinlock_lock (&sock->txw_spinlock); - pgm_txw_add (sock->window, STATE(skb)); - pgm_spinlock_unlock (&sock->txw_spinlock); - - ssize_t sent; - size_t tpdu_length; -retry_send: - pgm_assert ((char*)STATE(skb)->tail > (char*)STATE(skb)->head); - tpdu_length = (char*)STATE(skb)->tail - (char*)STATE(skb)->head; - sent = pgm_sendto (sock, - !STATE(is_rate_limited), /* rate limit on blocking */ - FALSE, /* regular socket */ - STATE(skb)->head, - tpdu_length, - (struct sockaddr*)&sock->send_gsr.gsr_group, - pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); - if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { - sock->is_apdu_eagain = TRUE; - sock->blocklen = tpdu_length; - goto blocked; - } - -/* save unfolded odata for retransmissions */ - pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); - - if (PGM_LIKELY((size_t)sent == tpdu_length)) { - bytes_sent += tpdu_length + sock->iphdr_len; /* as counted at IP layer */ - packets_sent++; /* IP packets */ - data_bytes_sent += STATE(tsdu_length); - } - - STATE(data_bytes_offset) += STATE(tsdu_length); - -/* check for end of transmission group */ - if (sock->use_proactive_parity) { - const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); - const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; - if (!((odata_sqn + 1) & ~tg_sqn_mask)) - pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); - } - - } while ( STATE(data_bytes_offset) < apdu_length); - pgm_assert( STATE(data_bytes_offset) == apdu_length ); - - sock->is_apdu_eagain = FALSE; - reset_heartbeat_spm (sock, STATE(skb)->tstamp); - - pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); - sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; - sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; - if (bytes_written) - *bytes_written = apdu_length; - return PGM_IO_STATUS_NORMAL; - -blocked: - if (bytes_sent) { - reset_heartbeat_spm (sock, STATE(skb)->tstamp); - pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); - sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; - sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; - } - if (EAGAIN == errno) { - if (sock->use_pgmcc) - pgm_notify_clear (&sock->ack_notify); - return PGM_IO_STATUS_WOULD_BLOCK; - } - return PGM_IO_STATUS_RATE_LIMITED; -} - -/* Send one APDU, whether it fits within one TPDU or more. - * - * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets - * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if - * packet size exceeds the current rate limit. - */ -int -pgm_send ( - pgm_sock_t* const restrict sock, - const void* restrict apdu, - const size_t apdu_length, - size_t* restrict bytes_written - ) -{ - pgm_debug ("pgm_send (sock:%p apdu:%p apdu-length:%zu bytes-written:%p)", - (void*)sock, apdu, apdu_length, (void*)bytes_written); - -/* parameters */ - pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); - if (PGM_LIKELY(apdu_length)) pgm_return_val_if_fail (NULL != apdu, PGM_IO_STATUS_ERROR); - -/* shutdown */ - if (PGM_UNLIKELY(!pgm_rwlock_reader_trylock (&sock->lock))) - pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); - -/* state */ - if (PGM_UNLIKELY(!sock->is_bound || - sock->is_destroyed || - apdu_length > sock->max_apdu)) - { - pgm_rwlock_reader_unlock (&sock->lock); - pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); - } - -/* source */ - pgm_mutex_lock (&sock->source_mutex); - -/* pass on non-fragment calls */ - if (apdu_length <= sock->max_tsdu) - { - const int status = send_odata_copy (sock, apdu, apdu_length, bytes_written); - pgm_mutex_unlock (&sock->source_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - return status; - } - - const int status = send_apdu (sock, apdu, apdu_length, bytes_written); - pgm_mutex_unlock (&sock->source_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - return status; -} - -/* send PGM original data, callee owned scatter/gather IO vector. if larger than maximum TPDU - * size will be fragmented. - * - * is_one_apdu = true: - * - * ⎢ DATA₀ ⎢ - * ⎢ DATA₁ ⎢ → pgm_sendv() → ⎢ ⋯ TSDU₁ TSDU₀ ⎢ → libc - * ⎢ ⋮ ⎢ - * - * is_one_apdu = false: - * - * ⎢ APDU₀ ⎢ ⎢ ⋯ TSDU₁,₀ TSDU₀,₀ ⎢ - * ⎢ APDU₁ ⎢ → pgm_sendv() → ⎢ ⋯ TSDU₁,₁ TSDU₀,₁ ⎢ → libc - * ⎢ ⋮ ⎢ ⎢ ⋮ ⋮ ⎢ - * - * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets - * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if - * packet size exceeds the current rate limit. - */ - -int -pgm_sendv ( - pgm_sock_t* const restrict sock, - const struct pgm_iovec* const restrict vector, - const unsigned count, /* number of items in vector */ - const bool is_one_apdu, /* true = vector = apdu, false = vector::iov_base = apdu */ - size_t* restrict bytes_written - ) -{ - pgm_debug ("pgm_sendv (sock:%p vector:%p count:%u is-one-apdu:%s bytes-written:%p)", - (const void*)sock, - (const void*)vector, - count, - is_one_apdu ? "TRUE" : "FALSE", - (const void*)bytes_written); - - pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); - pgm_return_val_if_fail (count <= PGM_MAX_FRAGMENTS, PGM_IO_STATUS_ERROR); - if (PGM_LIKELY(count)) pgm_return_val_if_fail (NULL != vector, PGM_IO_STATUS_ERROR); - if (PGM_UNLIKELY(!pgm_rwlock_reader_trylock (&sock->lock))) - pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); - if (PGM_UNLIKELY(!sock->is_bound || - sock->is_destroyed)) - { - pgm_rwlock_reader_unlock (&sock->lock); - pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); - } - - pgm_mutex_lock (&sock->source_mutex); - -/* pass on zero length as cannot count vector lengths */ - if (PGM_UNLIKELY(0 == count)) - { - const int status = send_odata_copy (sock, NULL, count, bytes_written); - pgm_mutex_unlock (&sock->source_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - return status; - } - - size_t bytes_sent = 0; - unsigned packets_sent = 0; - size_t data_bytes_sent = 0; - const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; - -/* continue if blocked mid-apdu */ - if (sock->is_apdu_eagain) { - if (is_one_apdu) { - if (STATE(apdu_length) <= sock->max_tsdu) - { - const int status = send_odatav (sock, vector, count, bytes_written); - pgm_mutex_unlock (&sock->source_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - return status; - } - else - goto retry_one_apdu_send; - } else { - goto retry_send; - } - } - -/* calculate (total) APDU length */ - STATE(apdu_length) = 0; - for (unsigned i = 0; i < count; i++) - { -#ifdef TRANSPORT_DEBUG - if (PGM_LIKELY(vector[i].iov_len)) { - pgm_assert( vector[i].iov_base ); - } -#endif - if (!is_one_apdu && - vector[i].iov_len > sock->max_apdu) - { - pgm_mutex_unlock (&sock->source_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); - } - STATE(apdu_length) += vector[i].iov_len; - } - -/* pass on non-fragment calls */ - if (is_one_apdu) { - if (STATE(apdu_length) <= sock->max_tsdu) { - const int status = send_odatav (sock, vector, count, bytes_written); - pgm_mutex_unlock (&sock->source_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - return status; - } else if (STATE(apdu_length) > sock->max_apdu) { - pgm_mutex_unlock (&sock->source_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); - } - } - -/* if non-blocking calculate total wire size and check rate limit */ - STATE(is_rate_limited) = FALSE; - if (sock->is_nonblocking && sock->is_controlled_odata) - { - const size_t header_length = pgm_pkt_offset (TRUE, pgmcc_family); - size_t tpdu_length = 0; - size_t offset_ = 0; - do { - const uint16_t tsdu_length = MIN( source_max_tsdu (sock, TRUE), STATE(apdu_length) - offset_ ); - tpdu_length += sock->iphdr_len + header_length + tsdu_length; - offset_ += tsdu_length; - } while (offset_ < STATE(apdu_length)); - -/* calculation includes one iphdr length already */ - if (!pgm_rate_check (&sock->rate_control, - tpdu_length - sock->iphdr_len, - sock->is_nonblocking)) - { - sock->blocklen = tpdu_length; - pgm_mutex_unlock (&sock->source_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - return PGM_IO_STATUS_RATE_LIMITED; - } - STATE(is_rate_limited) = TRUE; - } - -/* non-fragmented packets can be forwarded onto basic send() */ - if (!is_one_apdu) - { - for (STATE(data_pkt_offset) = 0; STATE(data_pkt_offset) < count; STATE(data_pkt_offset)++) - { - size_t wrote_bytes; - int status; -retry_send: - status = send_apdu (sock, - vector[STATE(data_pkt_offset)].iov_base, - vector[STATE(data_pkt_offset)].iov_len, - &wrote_bytes); - switch (status) { - case PGM_IO_STATUS_NORMAL: - break; - case PGM_IO_STATUS_WOULD_BLOCK: - case PGM_IO_STATUS_RATE_LIMITED: - sock->is_apdu_eagain = TRUE; - pgm_mutex_unlock (&sock->source_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - return status; - case PGM_IO_STATUS_ERROR: - pgm_mutex_unlock (&sock->source_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - return status; - default: - pgm_assert_not_reached(); - } - data_bytes_sent += wrote_bytes; - } - - sock->is_apdu_eagain = FALSE; - if (bytes_written) - *bytes_written = data_bytes_sent; - pgm_mutex_unlock (&sock->source_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - return PGM_IO_STATUS_NORMAL; - } - - STATE(data_bytes_offset) = 0; - STATE(vector_index) = 0; - STATE(vector_offset) = 0; - - STATE(first_sqn) = pgm_txw_next_lead(sock->window); - - do { -/* retrieve packet storage from transmit window */ - size_t header_length = pgm_pkt_offset (TRUE, pgmcc_family); - STATE(tsdu_length) = MIN( source_max_tsdu (sock, TRUE), STATE(apdu_length) - STATE(data_bytes_offset) ); - STATE(skb) = pgm_alloc_skb (sock->max_tpdu); - STATE(skb)->sock = sock; - STATE(skb)->tstamp = pgm_time_update_now(); - pgm_skb_reserve (STATE(skb), header_length); - pgm_skb_put (STATE(skb), STATE(tsdu_length)); - - STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->head; - STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); - memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); - STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; - STATE(skb)->pgm_header->pgm_dport = sock->dport; - STATE(skb)->pgm_header->pgm_type = PGM_ODATA; - STATE(skb)->pgm_header->pgm_options = PGM_OPT_PRESENT; - STATE(skb)->pgm_header->pgm_tsdu_length = htons (STATE(tsdu_length)); - -/* ODATA */ - STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); - STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); - -/* OPT_LENGTH */ - struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(STATE(skb)->pgm_data + 1); - opt_len->opt_type = PGM_OPT_LENGTH; - opt_len->opt_length = sizeof(struct pgm_opt_length); - opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + - sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_fragment) ); -/* OPT_FRAGMENT */ - struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); - opt_header->opt_type = PGM_OPT_FRAGMENT | PGM_OPT_END; - opt_header->opt_length = sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_fragment); - STATE(skb)->pgm_opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); - STATE(skb)->pgm_opt_fragment->opt_reserved = 0; - STATE(skb)->pgm_opt_fragment->opt_sqn = htonl (STATE(first_sqn)); - STATE(skb)->pgm_opt_fragment->opt_frag_off = htonl (STATE(data_bytes_offset)); - STATE(skb)->pgm_opt_fragment->opt_frag_len = htonl (STATE(apdu_length)); - -/* checksum & copy */ - STATE(skb)->pgm_header->pgm_checksum = 0; - const size_t pgm_header_len = (char*)(STATE(skb)->pgm_opt_fragment + 1) - (char*)STATE(skb)->pgm_header; - const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); - -/* iterate over one or more vector elements to perform scatter/gather checksum & copy - * - * STATE(vector_index) - index into application scatter/gather vector - * STATE(vector_offset) - current offset into current vector element - * STATE(unfolded_odata)- checksum accumulator - */ - const char* src = (const char*)vector[STATE(vector_index)].iov_base + STATE(vector_offset); - char* dst = (char*)(STATE(skb)->pgm_opt_fragment + 1); - size_t src_length = vector[STATE(vector_index)].iov_len - STATE(vector_offset); - size_t dst_length = 0; - size_t copy_length = MIN( STATE(tsdu_length), src_length ); - STATE(unfolded_odata) = pgm_csum_partial_copy (src, dst, copy_length, 0); - - for(;;) - { - if (copy_length == src_length) { -/* application packet complete */ - STATE(vector_index)++; - STATE(vector_offset) = 0; - } else { -/* data still remaining */ - STATE(vector_offset) += copy_length; - } - - dst_length += copy_length; - -/* sock packet complete */ - if (dst_length == STATE(tsdu_length)) - break; - - src = (const char*)vector[STATE(vector_index)].iov_base + STATE(vector_offset); - dst += copy_length; - src_length = vector[STATE(vector_index)].iov_len - STATE(vector_offset); - copy_length = MIN( STATE(tsdu_length) - dst_length, src_length ); - const uint32_t unfolded_element = pgm_csum_partial_copy (src, dst, copy_length, 0); - STATE(unfolded_odata) = pgm_csum_block_add (STATE(unfolded_odata), unfolded_element, dst_length); - } - - STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); - -/* add to transmit window, skb::data set to payload */ - pgm_spinlock_lock (&sock->txw_spinlock); - pgm_txw_add (sock->window, STATE(skb)); - pgm_spinlock_unlock (&sock->txw_spinlock); - - ssize_t sent; - size_t tpdu_length; -retry_one_apdu_send: - tpdu_length = (char*)STATE(skb)->tail - (char*)STATE(skb)->head; - sent = pgm_sendto (sock, - !STATE(is_rate_limited), /* rate limited on blocking */ - FALSE, /* regular socket */ - STATE(skb)->head, - tpdu_length, - (struct sockaddr*)&sock->send_gsr.gsr_group, - pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); - if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { - sock->is_apdu_eagain = TRUE; - sock->blocklen = tpdu_length; - goto blocked; - } - -/* save unfolded odata for retransmissions */ - pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); - - if (PGM_LIKELY((size_t)sent == tpdu_length)) { - bytes_sent += tpdu_length + sock->iphdr_len; /* as counted at IP layer */ - packets_sent++; /* IP packets */ - data_bytes_sent += STATE(tsdu_length); - } - - STATE(data_bytes_offset) += STATE(tsdu_length); - -/* check for end of transmission group */ - if (sock->use_proactive_parity) { - const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); - const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; - if (!((odata_sqn + 1) & ~tg_sqn_mask)) - pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); - } - - } while ( STATE(data_bytes_offset) < STATE(apdu_length) ); - pgm_assert( STATE(data_bytes_offset) == STATE(apdu_length) ); - - sock->is_apdu_eagain = FALSE; - reset_heartbeat_spm (sock, STATE(skb)->tstamp); - - pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); - sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; - sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; - if (bytes_written) - *bytes_written = STATE(apdu_length); - pgm_mutex_unlock (&sock->source_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - return PGM_IO_STATUS_NORMAL; - -blocked: - if (bytes_sent) { - reset_heartbeat_spm (sock, STATE(skb)->tstamp); - pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); - sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; - sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; - } - pgm_mutex_unlock (&sock->source_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - if (EAGAIN == errno) { - if (sock->use_pgmcc) - pgm_notify_clear (&sock->ack_notify); - return PGM_IO_STATUS_WOULD_BLOCK; - } - return PGM_IO_STATUS_RATE_LIMITED; -} - -/* send PGM original data, transmit window owned scatter/gather IO vector. - * - * ⎢ TSDU₀ ⎢ - * ⎢ TSDU₁ ⎢ → pgm_send_skbv() → ⎢ ⋯ TSDU₁ TSDU₀ ⎢ → libc - * ⎢ ⋮ ⎢ - * - * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets - * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if - * packet size exceeds the current rate limit. - */ - -int -pgm_send_skbv ( - pgm_sock_t* const restrict sock, - struct pgm_sk_buff_t** const restrict vector, /* array of skb pointers vs. array of skbs */ - const unsigned count, - const bool is_one_apdu, /* true: vector = apdu, false: vector::iov_base = apdu */ - size_t* restrict bytes_written - ) -{ - pgm_debug ("pgm_send_skbv (sock:%p vector:%p count:%u is-one-apdu:%s bytes-written:%p)", - (const void*)sock, - (const void*)vector, - count, - is_one_apdu ? "TRUE" : "FALSE", - (const void*)bytes_written); - - pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); - pgm_return_val_if_fail (count <= PGM_MAX_FRAGMENTS, PGM_IO_STATUS_ERROR); - if (PGM_LIKELY(count)) pgm_return_val_if_fail (NULL != vector, PGM_IO_STATUS_ERROR); - if (PGM_UNLIKELY(!pgm_rwlock_reader_trylock (&sock->lock))) - pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); - if (PGM_UNLIKELY(!sock->is_bound || - sock->is_destroyed)) - { - pgm_rwlock_reader_unlock (&sock->lock); - pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); - } - - pgm_mutex_lock (&sock->source_mutex); - -/* pass on zero length as cannot count vector lengths */ - if (PGM_UNLIKELY(0 == count)) - { - const int status = send_odata_copy (sock, NULL, count, bytes_written); - pgm_mutex_unlock (&sock->source_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - return status; - } - else if (1 == count) - { - const int status = send_odata (sock, vector[0], bytes_written); - pgm_mutex_unlock (&sock->source_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - return status; - } - - size_t bytes_sent = 0; - unsigned packets_sent = 0; - size_t data_bytes_sent = 0; - const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; - -/* continue if blocked mid-apdu */ - if (sock->is_apdu_eagain) - goto retry_send; - - STATE(is_rate_limited) = FALSE; - if (sock->is_nonblocking && sock->is_controlled_odata) - { - size_t total_tpdu_length = 0; - for (unsigned i = 0; i < count; i++) - total_tpdu_length += sock->iphdr_len + pgm_pkt_offset (is_one_apdu, pgmcc_family) + vector[i]->len; - -/* calculation includes one iphdr length already */ - if (!pgm_rate_check (&sock->rate_control, - total_tpdu_length - sock->iphdr_len, - sock->is_nonblocking)) - { - sock->blocklen = total_tpdu_length; - pgm_mutex_unlock (&sock->source_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - return PGM_IO_STATUS_RATE_LIMITED; - } - STATE(is_rate_limited) = TRUE; - } - - if (is_one_apdu) - { - STATE(apdu_length) = 0; - STATE(first_sqn) = pgm_txw_next_lead(sock->window); - for (unsigned i = 0; i < count; i++) - { - if (PGM_UNLIKELY(vector[i]->len > sock->max_tsdu_fragment)) { - pgm_mutex_unlock (&sock->source_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - return PGM_IO_STATUS_ERROR; - } - STATE(apdu_length) += vector[i]->len; - } - if (PGM_UNLIKELY(STATE(apdu_length) > sock->max_apdu)) { - pgm_mutex_unlock (&sock->source_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - return PGM_IO_STATUS_ERROR; - } - } - - for (STATE(vector_index) = 0; STATE(vector_index) < count; STATE(vector_index)++) - { - STATE(tsdu_length) = vector[STATE(vector_index)]->len; - - STATE(skb) = pgm_skb_get(vector[STATE(vector_index)]); - STATE(skb)->sock = sock; - STATE(skb)->tstamp = pgm_time_update_now(); - - STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->head; - STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); - memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); - STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; - STATE(skb)->pgm_header->pgm_dport = sock->dport; - STATE(skb)->pgm_header->pgm_type = PGM_ODATA; - STATE(skb)->pgm_header->pgm_options = is_one_apdu ? PGM_OPT_PRESENT : 0; - STATE(skb)->pgm_header->pgm_tsdu_length = htons (STATE(tsdu_length)); - -/* ODATA */ - STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); - STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); - - if (is_one_apdu) - { -/* OPT_LENGTH */ - struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(STATE(skb)->pgm_data + 1); - opt_len->opt_type = PGM_OPT_LENGTH; - opt_len->opt_length = sizeof(struct pgm_opt_length); - opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + - sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_fragment) ); -/* OPT_FRAGMENT */ - struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); - opt_header->opt_type = PGM_OPT_FRAGMENT | PGM_OPT_END; - opt_header->opt_length = sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_fragment); - STATE(skb)->pgm_opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); - STATE(skb)->pgm_opt_fragment->opt_reserved = 0; - STATE(skb)->pgm_opt_fragment->opt_sqn = htonl (STATE(first_sqn)); - STATE(skb)->pgm_opt_fragment->opt_frag_off = htonl (STATE(data_bytes_offset)); - STATE(skb)->pgm_opt_fragment->opt_frag_len = htonl (STATE(apdu_length)); - - pgm_assert (STATE(skb)->data == (STATE(skb)->pgm_opt_fragment + 1)); - } - else - { - pgm_assert (STATE(skb)->data == (STATE(skb)->pgm_data + 1)); - } - -/* TODO: the assembly checksum & copy routine is faster than memcpy & pgm_cksum on >= opteron hardware */ - STATE(skb)->pgm_header->pgm_checksum = 0; - pgm_assert ((char*)STATE(skb)->data > (char*)STATE(skb)->pgm_header); - const size_t pgm_header_len = (char*)STATE(skb)->data - (char*)STATE(skb)->pgm_header; - const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); - STATE(unfolded_odata) = pgm_csum_partial ((char*)STATE(skb)->data, STATE(tsdu_length), 0); - STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); - -/* add to transmit window, skb::data set to payload */ - pgm_spinlock_lock (&sock->txw_spinlock); - pgm_txw_add (sock->window, STATE(skb)); - pgm_spinlock_unlock (&sock->txw_spinlock); - ssize_t sent; - size_t tpdu_length; -retry_send: - pgm_assert ((char*)STATE(skb)->tail > (char*)STATE(skb)->head); - tpdu_length = (char*)STATE(skb)->tail - (char*)STATE(skb)->head; - sent = pgm_sendto (sock, - !STATE(is_rate_limited), /* rate limited on blocking */ - FALSE, /* regular socket */ - STATE(skb)->head, - tpdu_length, - (struct sockaddr*)&sock->send_gsr.gsr_group, - pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); - if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { - sock->is_apdu_eagain = TRUE; - sock->blocklen = tpdu_length; - goto blocked; - } - -/* save unfolded odata for retransmissions */ - pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); - - if (PGM_LIKELY((size_t)sent == tpdu_length)) { - bytes_sent += tpdu_length + sock->iphdr_len; /* as counted at IP layer */ - packets_sent++; /* IP packets */ - data_bytes_sent += STATE(tsdu_length); - } - - pgm_free_skb (STATE(skb)); - STATE(data_bytes_offset) += STATE(tsdu_length); - -/* check for end of transmission group */ - if (sock->use_proactive_parity) { - const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); - const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; - if (!((odata_sqn + 1) & ~tg_sqn_mask)) - pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); - } - - } -#ifdef TRANSPORT_DEBUG - if (is_one_apdu) - { - pgm_assert( STATE(data_bytes_offset) == STATE(apdu_length) ); - } -#endif - - sock->is_apdu_eagain = FALSE; - reset_heartbeat_spm (sock, STATE(skb)->tstamp); - - pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); - sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; - sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; - if (bytes_written) - *bytes_written = data_bytes_sent; - pgm_mutex_unlock (&sock->source_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - return PGM_IO_STATUS_NORMAL; - -blocked: - if (bytes_sent) { - reset_heartbeat_spm (sock, STATE(skb)->tstamp); - pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); - sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; - sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; - } - pgm_mutex_unlock (&sock->source_mutex); - pgm_rwlock_reader_unlock (&sock->lock); - if (EAGAIN == errno) { - if (sock->use_pgmcc) - pgm_notify_clear (&sock->ack_notify); - return PGM_IO_STATUS_WOULD_BLOCK; - } - return PGM_IO_STATUS_RATE_LIMITED; -} - -/* cleanup resuming send state helper - */ -#undef STATE - -/* send repair packet. - * - * on success, TRUE is returned. on error, FALSE is returned. - */ - -static -bool -send_rdata ( - pgm_sock_t* restrict sock, - struct pgm_sk_buff_t* restrict skb - ) -{ -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (NULL != skb); - pgm_assert ((char*)skb->tail > (char*)skb->head); - - const size_t tpdu_length = (char*)skb->tail - (char*)skb->head; - -/* update previous odata/rdata contents */ - struct pgm_header* header = skb->pgm_header; - struct pgm_data* rdata = skb->pgm_data; - header->pgm_type = PGM_RDATA; -/* RDATA */ - rdata->data_trail = htonl (pgm_txw_trail(sock->window)); - - header->pgm_checksum = 0; - const size_t pgm_header_len = tpdu_length - ntohs(header->pgm_tsdu_length); - uint32_t unfolded_header = pgm_csum_partial (header, pgm_header_len, 0); - uint32_t unfolded_odata = pgm_txw_get_unfolded_checksum (skb); - header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, unfolded_odata, pgm_header_len)); - -/* congestion control */ - if (sock->use_pgmcc && - sock->tokens < pgm_fp8 (1)) - { -// pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Token limit reached.")); - sock->blocklen = tpdu_length; - return FALSE; - } - - const ssize_t sent = pgm_sendto (sock, - sock->is_controlled_rdata, /* rate limited */ - TRUE, /* with router alert */ - header, - tpdu_length, - (struct sockaddr*)&sock->send_gsr.gsr_group, - pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); - - if (sent < 0 && EAGAIN == errno) { - sock->blocklen = tpdu_length; - return FALSE; - } - - const pgm_time_t now = pgm_time_update_now(); - - if (sock->use_pgmcc) { - sock->tokens -= pgm_fp8 (1); - sock->ack_expiry = now + sock->ack_expiry_ivl; - } - -/* re-set spm timer: we are already in the timer thread, no need to prod timers - */ - pgm_mutex_lock (&sock->timer_mutex); - sock->spm_heartbeat_state = 1; - sock->next_heartbeat_spm = now + sock->spm_heartbeat_interval[sock->spm_heartbeat_state++]; - pgm_mutex_unlock (&sock->timer_mutex); - - pgm_txw_inc_retransmit_count (skb); - sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_BYTES_RETRANSMITTED] += ntohs(header->pgm_tsdu_length); - sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED]++; /* impossible to determine APDU count */ - pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length + sock->iphdr_len); - return TRUE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/source_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/source_unittest.c deleted file mode 100644 index 27ffebe..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/source_unittest.c +++ /dev/null @@ -1,1216 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for PGM source transport. - * - * Copyright (c) 2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -/* mock state */ - -#define TEST_NETWORK "" -#define TEST_PORT 7500 -#define TEST_MAX_TPDU 1500 -#define TEST_TXW_SQNS 32 -#define TEST_RXW_SQNS 32 -#define TEST_HOPS 16 -#define TEST_SPM_AMBIENT ( pgm_secs(30) ) -#define TEST_SPM_HEARTBEAT_INIT { pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(1300), pgm_secs(7), pgm_secs(16), pgm_secs(25), pgm_secs(30) } -#define TEST_PEER_EXPIRY ( pgm_secs(300) ) -#define TEST_SPMR_EXPIRY ( pgm_msecs(250) ) -#define TEST_NAK_BO_IVL ( pgm_msecs(50) ) -#define TEST_NAK_RPT_IVL ( pgm_secs(2) ) -#define TEST_NAK_RDATA_IVL ( pgm_secs(2) ) -#define TEST_NAK_DATA_RETRIES 5 -#define TEST_NAK_NCF_RETRIES 2 - -static gboolean mock_is_valid_spmr = TRUE; -static gboolean mock_is_valid_ack = TRUE; -static gboolean mock_is_valid_nak = TRUE; -static gboolean mock_is_valid_nnak = TRUE; - - -#define pgm_txw_get_unfolded_checksum mock_pgm_txw_get_unfolded_checksum -#define pgm_txw_set_unfolded_checksum mock_pgm_txw_set_unfolded_checksum -#define pgm_txw_inc_retransmit_count mock_pgm_txw_inc_retransmit_count -#define pgm_txw_add mock_pgm_txw_add -#define pgm_txw_peek mock_pgm_txw_peek -#define pgm_txw_retransmit_push mock_pgm_txw_retransmit_push -#define pgm_txw_retransmit_try_peek mock_pgm_txw_retransmit_try_peek -#define pgm_txw_retransmit_remove_head mock_pgm_txw_retransmit_remove_head -#define pgm_rs_encode mock_pgm_rs_encode -#define pgm_rate_check mock_pgm_rate_check -#define pgm_verify_spmr mock_pgm_verify_spmr -#define pgm_verify_ack mock_pgm_verify_ack -#define pgm_verify_nak mock_pgm_verify_nak -#define pgm_verify_nnak mock_pgm_verify_nnak -#define pgm_compat_csum_partial mock_pgm_compat_csum_partial -#define pgm_compat_csum_partial_copy mock_pgm_compat_csum_partial_copy -#define pgm_csum_block_add mock_pgm_csum_block_add -#define pgm_csum_fold mock_pgm_csum_fold -#define pgm_sendto mock_pgm_sendto -#define pgm_time_update_now mock_pgm_time_update_now - - -#define SOURCE_DEBUG -#include "source.c" - - -static -void -mock_setup (void) -{ - if (!g_thread_supported ()) g_thread_init (NULL); -} - -static -struct pgm_sock_t* -generate_sock (void) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, g_htons(1000) }; - struct pgm_sock_t* sock = g_new0 (struct pgm_sock_t, 1); - memcpy (&sock->tsi, &tsi, sizeof(pgm_tsi_t)); - ((struct sockaddr*)&sock->send_addr)->sa_family = AF_INET; - ((struct sockaddr_in*)&sock->send_addr)->sin_addr.s_addr = inet_addr ("127.0.0.2"); - ((struct sockaddr*)&sock->send_gsr.gsr_group)->sa_family = AF_INET; - ((struct sockaddr_in*)&sock->send_gsr.gsr_group)->sin_addr.s_addr = inet_addr ("239.192.0.1"); - sock->dport = g_htons(TEST_PORT); - sock->window = g_malloc0 (sizeof(pgm_txw_t)); - sock->txw_sqns = TEST_TXW_SQNS; - sock->max_tpdu = TEST_MAX_TPDU; - sock->max_tsdu = TEST_MAX_TPDU - sizeof(struct pgm_ip) - pgm_pkt_offset (FALSE, FALSE); - sock->max_tsdu_fragment = TEST_MAX_TPDU - sizeof(struct pgm_ip) - pgm_pkt_offset (TRUE, FALSE); - sock->max_apdu = MIN(TEST_TXW_SQNS, PGM_MAX_FRAGMENTS) * sock->max_tsdu_fragment; - sock->iphdr_len = sizeof(struct pgm_ip); - sock->spm_heartbeat_interval = g_malloc0 (sizeof(guint) * (2+2)); - sock->spm_heartbeat_interval[0] = pgm_secs(1); - pgm_spinlock_init (&sock->txw_spinlock); - sock->is_bound = FALSE; - sock->is_destroyed = FALSE; - return sock; -} - -static -struct pgm_sk_buff_t* -generate_skb (void) -{ - const char source[] = "i am not a string"; - struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); - pgm_skb_reserve (skb, pgm_pkt_offset (FALSE, FALSE)); - pgm_skb_put (skb, sizeof(source)); - memcpy (skb->data, source, sizeof(source)); - return skb; -} - -static -struct pgm_sk_buff_t* -generate_fragment_skb (void) -{ - const char source[] = "i am not a string"; - struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); - pgm_skb_reserve (skb, pgm_pkt_offset (TRUE, FALSE)); - pgm_skb_put (skb, sizeof(source)); - memcpy (skb->data, source, sizeof(source)); - return skb; -} - -static -struct pgm_sk_buff_t* -generate_odata (void) -{ - const char source[] = "i am not a string"; - const guint16 header_length = sizeof(struct pgm_header) + sizeof(struct pgm_data); - struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); - pgm_skb_reserve (skb, header_length); - memset (skb->head, 0, header_length); - skb->pgm_header = (struct pgm_header*)skb->head; - skb->pgm_data = (struct pgm_data*)(skb->pgm_header + 1); - skb->pgm_header->pgm_type = PGM_ODATA; - skb->pgm_header->pgm_tsdu_length = g_htons (sizeof(source)); - memcpy (skb->data, source, sizeof(source)); - pgm_skb_put (skb, sizeof(source)); -/* reverse pull */ - skb->len += (guint8*)skb->data - (guint8*)skb->head; - skb->data = skb->head; - return skb; -} - -static -pgm_peer_t* -generate_peer (void) -{ - pgm_peer_t* peer = g_malloc0 (sizeof(pgm_peer_t)); - return peer; -} - -static -struct pgm_sk_buff_t* -generate_spmr (void) -{ - struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); - const guint16 header_length = sizeof(struct pgm_header); - pgm_skb_reserve (skb, header_length); - memset (skb->head, 0, header_length); - skb->pgm_header = (struct pgm_header*)skb->head; - skb->pgm_header->pgm_type = PGM_SPMR; - pgm_skb_put (skb, header_length); - return skb; -} - -static -struct pgm_sk_buff_t* -generate_single_nak (void) -{ - struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); - const guint16 header_length = sizeof(struct pgm_header) + sizeof(struct pgm_nak); - pgm_skb_reserve (skb, sizeof(struct pgm_header)); - memset (skb->head, 0, header_length); - skb->pgm_header = (struct pgm_header*)skb->head; - skb->pgm_header->pgm_type = PGM_NAK; - struct pgm_nak* nak = (struct pgm_nak*)(skb->pgm_header + 1); - struct sockaddr_in nla = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr("127.0.0.2") - }; - pgm_sockaddr_to_nla ((struct sockaddr*)&nla, (char*)&nak->nak_src_nla_afi); - struct sockaddr_in group = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr("239.192.0.1") - }; - pgm_sockaddr_to_nla ((struct sockaddr*)&group, (char*)&nak->nak_grp_nla_afi); - pgm_skb_put (skb, header_length); - return skb; -} - -static -struct pgm_sk_buff_t* -generate_single_nnak (void) -{ - struct pgm_sk_buff_t* skb = generate_single_nak (); - skb->pgm_header->pgm_type = PGM_NNAK; - return skb; -} - -static -struct pgm_sk_buff_t* -generate_parity_nak (void) -{ - struct pgm_sk_buff_t* skb = generate_single_nak (); - skb->pgm_header->pgm_options = PGM_OPT_PARITY; - return skb; -} - -static -struct pgm_sk_buff_t* -generate_nak_list (void) -{ - struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); - const guint16 header_length = sizeof(struct pgm_header) + sizeof(struct pgm_nak) + - sizeof(struct pgm_opt_length) + - sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + - ( 62 * sizeof(guint32) ); - pgm_skb_reserve (skb, sizeof(struct pgm_header)); - memset (skb->head, 0, header_length); - skb->pgm_header = (struct pgm_header*)skb->head; - skb->pgm_header->pgm_type = PGM_NAK; - skb->pgm_header->pgm_options = PGM_OPT_PRESENT | PGM_OPT_NETWORK; - struct pgm_nak *nak = (struct pgm_nak*)(skb->pgm_header + 1); - struct sockaddr_in nla = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr("127.0.0.2") - }; - pgm_sockaddr_to_nla ((struct sockaddr*)&nla, (char*)&nak->nak_src_nla_afi); - struct sockaddr_in group = { - .sin_family = AF_INET, - .sin_addr.s_addr = inet_addr("239.192.0.1") - }; - pgm_sockaddr_to_nla ((struct sockaddr*)&group, (char*)&nak->nak_grp_nla_afi); - struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(nak + 1); - opt_len->opt_type = PGM_OPT_LENGTH; - opt_len->opt_length = sizeof(struct pgm_opt_length); - opt_len->opt_total_length = g_htons ( sizeof(struct pgm_opt_length) + - sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_nak_list) + - ( 62 * sizeof(guint32) ) ); - struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); - opt_header->opt_type = PGM_OPT_NAK_LIST | PGM_OPT_END; - opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + - ( 62 * sizeof(guint32) ); - struct pgm_opt_nak_list* opt_nak_list = (struct pgm_opt_nak_list*)(opt_header + 1); - for (unsigned i = 1; i < 63; i++) { - opt_nak_list->opt_sqn[i-1] = g_htonl (i); - } - pgm_skb_put (skb, header_length); - return skb; -} - -static -struct pgm_sk_buff_t* -generate_parity_nak_list (void) -{ - struct pgm_sk_buff_t* skb = generate_nak_list (); - skb->pgm_header->pgm_options = PGM_OPT_PARITY | PGM_OPT_PRESENT | PGM_OPT_NETWORK; - return skb; -} - -void -mock_pgm_txw_add ( - pgm_txw_t* const window, - struct pgm_sk_buff_t* const skb - ) -{ - g_debug ("mock_pgm_txw_add (window:%p skb:%p)", - (gpointer)window, (gpointer)skb); -} - -struct pgm_sk_buff_t* -mock_pgm_txw_peek ( - const pgm_txw_t* const window, - const uint32_t sequence - ) -{ - g_debug ("mock_pgm_txw_peek (window:%p sequence:%" G_GUINT32_FORMAT ")", - (gpointer)window, sequence); - return NULL; -} - -bool -mock_pgm_txw_retransmit_push ( - pgm_txw_t* const window, - const uint32_t sequence, - const bool is_parity, - const uint8_t tg_sqn_shift - ) -{ - g_debug ("mock_pgm_txw_retransmit_push (window:%p sequence:%" G_GUINT32_FORMAT " is-parity:%s tg-sqn-shift:%d)", - (gpointer)window, - sequence, - is_parity ? "YES" : "NO", - tg_sqn_shift); - return TRUE; -} - -void -mock_pgm_txw_set_unfolded_checksum ( - struct pgm_sk_buff_t*const skb, - const uint32_t csum - ) -{ -} - -uint32_t -pgm_txw_get_unfolded_checksum ( - const struct pgm_sk_buff_t*const skb - ) -{ - return 0; -} - -void -mock_pgm_txw_inc_retransmit_count ( - struct pgm_sk_buff_t*const skb - ) -{ -} - -struct pgm_sk_buff_t* -mock_pgm_txw_retransmit_try_peek ( - pgm_txw_t* const window - ) -{ - g_debug ("mock_pgm_txw_retransmit_try_peek (window:%p)", - (gpointer)window); - return generate_odata (); -} - -void -mock_pgm_txw_retransmit_remove_head ( - pgm_txw_t* const window - ) -{ - g_debug ("mock_pgm_txw_retransmit_remove_head (window:%p)", - (gpointer)window); -} - -void -mock_pgm_rs_encode ( - pgm_rs_t* rs, - const pgm_gf8_t** src, - uint8_t offset, - pgm_gf8_t* dst, - uint16_t len - ) -{ - g_debug ("mock_pgm_rs_encode (rs:%p src:%p offset:%u dst:%p len:%" G_GSIZE_FORMAT ")", - rs, src, offset, dst, len); -} - -PGM_GNUC_INTERNAL -bool -mock_pgm_rate_check ( - pgm_rate_t* bucket, - const size_t data_size, - const bool is_nonblocking - ) -{ - g_debug ("mock_pgm_rate_check (bucket:%p data-size:%u is-nonblocking:%s)", - bucket, data_size, is_nonblocking ? "TRUE" : "FALSE"); - return TRUE; -} - -bool -mock_pgm_verify_spmr ( - const struct pgm_sk_buff_t* const skb - ) -{ - return mock_is_valid_spmr; -} - -bool -mock_pgm_verify_ack ( - const struct pgm_sk_buff_t* const skb - ) -{ - return mock_is_valid_ack; -} - -bool -mock_pgm_verify_nak ( - const struct pgm_sk_buff_t* const skb - ) -{ - return mock_is_valid_nak; -} - -bool -mock_pgm_verify_nnak ( - const struct pgm_sk_buff_t* const skb - ) -{ - return mock_is_valid_nnak; -} - -uint32_t -mock_pgm_compat_csum_partial ( - const void* addr, - uint16_t len, - uint32_t csum - ) -{ - return 0x0; -} - -uint32_t -mock_pgm_compat_csum_partial_copy ( - const void* src, - void* dst, - uint16_t len, - uint32_t csum - ) -{ - return 0x0; -} - -uint32_t -mock_pgm_csum_block_add ( - uint32_t csum, - uint32_t csum2, - uint16_t offset - ) -{ - return 0x0; -} - -uint16_t -mock_pgm_csum_fold ( - uint32_t csum - ) -{ - return 0x0; -} - -PGM_GNUC_INTERNAL -ssize_t -mock_pgm_sendto ( - pgm_sock_t* sock, - bool use_rate_limit, - bool use_router_alert, - const void* buf, - size_t len, - const struct sockaddr* to, - socklen_t tolen - ) -{ - char saddr[INET6_ADDRSTRLEN]; - pgm_sockaddr_ntop (to, saddr, sizeof(saddr)); - g_debug ("mock_pgm_sendto (sock:%p use-rate-limit:%s use-router-alert:%s buf:%p len:%d to:%s tolen:%d)", - (gpointer)sock, - use_rate_limit ? "YES" : "NO", - use_router_alert ? "YES" : "NO", - buf, - len, - saddr, - tolen); - return len; -} - -/** time module */ -static pgm_time_t _mock_pgm_time_update_now (void); -pgm_time_update_func mock_pgm_time_update_now = _mock_pgm_time_update_now; - -static -pgm_time_t -_mock_pgm_time_update_now (void) -{ - return 0x1; -} - -/** socket module */ -size_t -pgm_pkt_offset ( - const bool can_fragment, - const sa_family_t pgmcc_family /* 0 = disable */ - ) -{ - return can_fragment ? ( sizeof(struct pgm_header) - + sizeof(struct pgm_data) - + sizeof(struct pgm_opt_length) - + sizeof(struct pgm_opt_header) - + sizeof(struct pgm_opt_fragment) ) - : ( sizeof(struct pgm_header) + sizeof(struct pgm_data) ); -} - - -/* mock functions for external references */ - - -/* target: - * PGMIOStatus - * pgm_send ( - * pgm_sock_t* sock, - * gconstpointer apdu, - * gsize apdu_length, - * gsize* bytes_written - * ) - */ - -START_TEST (test_send_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - sock->is_bound = TRUE; - const gsize apdu_length = 100; - guint8 buffer[ apdu_length ]; - gsize bytes_written; - fail_unless (PGM_IO_STATUS_NORMAL == pgm_send (sock, buffer, apdu_length, &bytes_written), "send not normal"); - fail_unless ((gssize)apdu_length == bytes_written, "send underrun"); -} -END_TEST - -/* large apdu */ -START_TEST (test_send_pass_002) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - sock->is_bound = TRUE; - const gsize apdu_length = 16000; - guint8 buffer[ apdu_length ]; - gsize bytes_written; - fail_unless (PGM_IO_STATUS_NORMAL == pgm_send (sock, buffer, apdu_length, &bytes_written), "send not normal"); - fail_unless ((gssize)apdu_length == bytes_written, "send underrun"); -} -END_TEST - -START_TEST (test_send_fail_001) -{ - guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; - const gsize apdu_length = 100; - gsize bytes_written; - fail_unless (PGM_IO_STATUS_ERROR == pgm_send (NULL, buffer, apdu_length, &bytes_written), "send not error"); -} -END_TEST - -/* target: - * PGMIOStatus - * pgm_sendv ( - * pgm_sock_t* sock, - * const struct pgmiovec* vector, - * guint count, - * gboolean is_one_apdu, - * gsize* bytes_written - * ) - */ - -START_TEST (test_sendv_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - sock->is_bound = TRUE; - const gsize apdu_length = 100; - guint8 buffer[ apdu_length ]; - struct pgm_iovec vector[] = { { .iov_base = buffer, .iov_len = apdu_length } }; - gsize bytes_written; - fail_unless (PGM_IO_STATUS_NORMAL == pgm_sendv (sock, vector, 1, TRUE, &bytes_written), "send not normal"); - fail_unless ((gssize)apdu_length == bytes_written, "send underrun"); -} -END_TEST - -/* large apdu */ -START_TEST (test_sendv_pass_002) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - sock->is_bound = TRUE; - const gsize apdu_length = 16000; - guint8 buffer[ apdu_length ]; - struct pgm_iovec vector[] = { { .iov_base = buffer, .iov_len = apdu_length } }; - gsize bytes_written; - fail_unless (PGM_IO_STATUS_NORMAL == pgm_sendv (sock, vector, 1, TRUE, &bytes_written), "send not normal"); - fail_unless ((gssize)apdu_length == bytes_written, "send underrun"); -} -END_TEST - -/* multipart apdu */ -START_TEST (test_sendv_pass_003) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - sock->is_bound = TRUE; - const gsize apdu_length = 16000; - guint8 buffer[ apdu_length ]; - struct pgm_iovec vector[ 16 ]; - for (unsigned i = 0; i < G_N_ELEMENTS(vector); i++) { - vector[i].iov_base = &buffer[ (i * apdu_length) / G_N_ELEMENTS(vector) ]; - vector[i].iov_len = apdu_length / G_N_ELEMENTS(vector); - } - gsize bytes_written; - fail_unless (PGM_IO_STATUS_NORMAL == pgm_sendv (sock, vector, G_N_ELEMENTS(vector), TRUE, &bytes_written), "send not normal"); - fail_unless ((gssize)apdu_length == bytes_written, "send underrun"); -} -END_TEST - -/* multiple apdus */ -START_TEST (test_sendv_pass_004) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - sock->is_bound = TRUE; - const gsize apdu_length = 16000; - struct pgm_iovec vector[ 16 ]; - for (unsigned i = 0; i < G_N_ELEMENTS(vector); i++) { - vector[i].iov_base = g_malloc0 (apdu_length); - vector[i].iov_len = apdu_length; - } - gsize bytes_written; - fail_unless (PGM_IO_STATUS_NORMAL == pgm_sendv (sock, vector, G_N_ELEMENTS(vector), FALSE, &bytes_written), "send not normal"); - fail_unless ((gssize)(apdu_length * G_N_ELEMENTS(vector)) == bytes_written, "send underrun"); -} -END_TEST - -START_TEST (test_sendv_fail_001) -{ - guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; - const gsize tsdu_length = 100; - struct pgm_iovec vector[] = { { .iov_base = buffer, .iov_len = tsdu_length } }; - gsize bytes_written; - fail_unless (PGM_IO_STATUS_ERROR == pgm_sendv (NULL, vector, 1, TRUE, &bytes_written), "send not error"); -} -END_TEST - -/* target: - * PGMIOStatus - * pgm_send_skbv ( - * pgm_sock_t* sock, - * struct pgm_sk_buff_t* vector[], - * guint count, - * gboolean is_one_apdu, - * gsize* bytes_written - * ) - */ - -START_TEST (test_send_skbv_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - sock->is_bound = TRUE; - struct pgm_sk_buff_t* skb = NULL; - skb = generate_skb (); - fail_if (NULL == skb, "generate_skb failed"); - gsize apdu_length = (gsize)skb->len; - gsize bytes_written; - fail_unless (PGM_IO_STATUS_NORMAL == pgm_send_skbv (sock, &skb, 1, TRUE, &bytes_written), "send not normal"); - fail_unless (apdu_length == bytes_written, "send underrun"); -} -END_TEST - -/* multipart apdu */ -START_TEST (test_send_skbv_pass_002) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - sock->is_bound = TRUE; - struct pgm_sk_buff_t* skb[16]; - for (unsigned i = 0; i < G_N_ELEMENTS(skb); i++) { - skb[i] = generate_fragment_skb (); - fail_if (NULL == skb[i], "generate_fragment_skb failed"); - } - gsize apdu_length = (gsize)skb[0]->len * G_N_ELEMENTS(skb); - gsize bytes_written; - fail_unless (PGM_IO_STATUS_NORMAL == pgm_send_skbv (sock, skb, G_N_ELEMENTS(skb), TRUE, &bytes_written), "send not normal"); - fail_unless (apdu_length == bytes_written, "send underrun"); -} -END_TEST - -/* multiple apdus */ -START_TEST (test_send_skbv_pass_003) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - sock->is_bound = TRUE; - struct pgm_sk_buff_t* skb[16]; - for (unsigned i = 0; i < G_N_ELEMENTS(skb); i++) { - skb[i] = generate_skb (); - fail_if (NULL == skb[i], "generate_skb failed"); - } - gsize bytes_written; - fail_unless (PGM_IO_STATUS_NORMAL == pgm_send_skbv (sock, skb, G_N_ELEMENTS(skb), FALSE, &bytes_written), "send not normal"); - fail_unless ((gssize)(skb[0]->len * G_N_ELEMENTS(skb)) == bytes_written, "send underrun"); -} -END_TEST - -START_TEST (test_send_skbv_fail_001) -{ - struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU), *skbv[] = { skb }; - fail_if (NULL == skb, "alloc_skb failed"); -/* reserve PGM header */ - pgm_skb_put (skb, pgm_pkt_offset (TRUE, FALSE)); - const gsize tsdu_length = 100; - gsize bytes_written; - fail_unless (PGM_IO_STATUS_ERROR == pgm_send_skbv (NULL, skbv, 1, TRUE, &bytes_written), "send not error"); -} -END_TEST - -/* target: - * gboolean - * pgm_send_spm ( - * pgm_sock_t* sock, - * int flags - * ) - */ - -START_TEST (test_send_spm_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - fail_unless (TRUE == pgm_send_spm (sock, 0), "send_spm failed"); -} -END_TEST - -START_TEST (test_send_spm_fail_001) -{ - pgm_send_spm (NULL, 0); - fail ("reached"); -} -END_TEST - -/* target: - * void - * pgm_on_deferred_nak ( - * pgm_sock_t* sock - * ) - */ - -START_TEST (test_on_deferred_nak_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - pgm_on_deferred_nak (sock); -} -END_TEST - -START_TEST (test_on_deferred_nak_fail_001) -{ - pgm_on_deferred_nak (NULL); - fail ("reached"); -} -END_TEST - -/* target: - * gboolean - * pgm_on_spmr ( - * pgm_sock_t* sock, - * pgm_peer_t* peer, - * struct pgm_sk_buff_t* skb - * ) - */ - -/* peer spmr */ -START_TEST (test_on_spmr_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - pgm_peer_t* peer = generate_peer (); - fail_if (NULL == peer, "generate_peer failed"); - struct pgm_sk_buff_t* skb = generate_spmr (); - fail_if (NULL == skb, "generate_spmr failed"); - skb->sock = sock; - fail_unless (TRUE == pgm_on_spmr (sock, peer, skb), "on_spmr failed"); -} -END_TEST - -/* source spmr */ -START_TEST (test_on_spmr_pass_002) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - struct pgm_sk_buff_t* skb = generate_spmr (); - fail_if (NULL == skb, "generate_spmr failed"); - skb->sock = sock; - fail_unless (TRUE == pgm_on_spmr (sock, NULL, skb), "on_spmr failed"); -} -END_TEST - -/* invalid spmr */ -START_TEST (test_on_spmr_fail_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - pgm_peer_t* peer = generate_peer (); - fail_if (NULL == peer, "generate_peer failed"); - struct pgm_sk_buff_t* skb = generate_spmr (); - fail_if (NULL == skb, "generate_spmr failed"); - skb->sock = sock; - mock_is_valid_spmr = FALSE; - fail_unless (FALSE == pgm_on_spmr (sock, peer, skb), "on_spmr failed"); -} -END_TEST - -START_TEST (test_on_spmr_fail_002) -{ - pgm_on_spmr (NULL, NULL, NULL); - fail ("reached"); -} -END_TEST - -/* target: - * gboolean - * pgm_on_nak ( - * pgm_sock_t* sock, - * struct pgm_sk_buff_t* skb - * ) - */ - -/* single nak */ -START_TEST (test_on_nak_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - struct pgm_sk_buff_t* skb = generate_single_nak (); - fail_if (NULL == skb, "generate_single_nak failed"); - skb->sock = sock; - fail_unless (TRUE == pgm_on_nak (sock, skb), "on_nak failed"); -} -END_TEST - -/* nak list */ -START_TEST (test_on_nak_pass_002) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - struct pgm_sk_buff_t* skb = generate_nak_list (); - fail_if (NULL == skb, "generate_nak_list failed"); - skb->sock = sock; - fail_unless (TRUE == pgm_on_nak (sock, skb), "on_nak failed"); -} -END_TEST - -/* single parity nak */ -START_TEST (test_on_nak_pass_003) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - sock->use_ondemand_parity = TRUE; - struct pgm_sk_buff_t* skb = generate_parity_nak (); - fail_if (NULL == skb, "generate_parity_nak failed"); - skb->sock = sock; - fail_unless (TRUE == pgm_on_nak (sock, skb), "on_nak failed"); -} -END_TEST - -/* parity nak list */ -START_TEST (test_on_nak_pass_004) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - sock->use_ondemand_parity = TRUE; - struct pgm_sk_buff_t* skb = generate_parity_nak_list (); - fail_if (NULL == skb, "generate_parity_nak_list failed"); - skb->sock = sock; - fail_unless (TRUE == pgm_on_nak (sock, skb), "on_nak failed"); -} -END_TEST - -START_TEST (test_on_nak_fail_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - struct pgm_sk_buff_t* skb = generate_single_nak (); - fail_if (NULL == skb, "generate_single_nak failed"); - skb->sock = sock; - mock_is_valid_nak = FALSE; - fail_unless (FALSE == pgm_on_nak (sock, skb), "on_nak failed"); -} -END_TEST - -START_TEST (test_on_nak_fail_002) -{ - pgm_on_nak (NULL, NULL); - fail ("reached"); -} -END_TEST - -/* target: - * gboolean - * pgm_on_nnak ( - * pgm_sock_t* sock, - * struct pgm_sk_buff_t* skb - * ) - */ - -START_TEST (test_on_nnak_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - struct pgm_sk_buff_t* skb = generate_single_nnak (); - fail_if (NULL == skb, "generate_single_nnak failed"); - skb->sock = sock; - fail_unless (TRUE == pgm_on_nnak (sock, skb), "on_nnak failed"); -} -END_TEST - -START_TEST (test_on_nnak_fail_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - struct pgm_sk_buff_t* skb = generate_single_nnak (); - fail_if (NULL == skb, "generate_single_nnak failed"); - skb->sock = sock; - mock_is_valid_nnak = FALSE; - fail_unless (FALSE == pgm_on_nnak (sock, skb), "on_nnak failed"); -} -END_TEST - -START_TEST (test_on_nnak_fail_002) -{ - pgm_on_nnak (NULL, NULL); - fail ("reached"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_AMBIENT_SPM, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_ambient_spm_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - const int optname = PGM_AMBIENT_SPM; - const int ambient_spm = pgm_msecs(1000); - const void* optval = &ambient_spm; - const socklen_t optlen = sizeof(ambient_spm); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_ambient_spm failed"); -} -END_TEST - -START_TEST (test_set_ambient_spm_fail_001) -{ - const int optname = PGM_AMBIENT_SPM; - const int ambient_spm = pgm_msecs(1000); - const void* optval = &ambient_spm; - const socklen_t optlen = sizeof(ambient_spm); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_ambient_spm failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_HEARTBEAT_SPM, - * const void* optval, - * const socklen_t optlen = sizeof(int) * n - * ) - */ - -START_TEST (test_set_heartbeat_spm_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - const int optname = PGM_HEARTBEAT_SPM; - const int intervals[] = { 1, 2, 3, 4, 5 }; - const void* optval = &intervals; - const socklen_t optlen = sizeof(intervals); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_heartbeat_spm failed"); -} -END_TEST - -START_TEST (test_set_heartbeat_spm_fail_001) -{ - const int optname = PGM_HEARTBEAT_SPM; - const int intervals[] = { 1, 2, 3, 4, 5 }; - const void* optval = &intervals; - const socklen_t optlen = sizeof(intervals); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_heartbeat_spm failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_TXW_SQNS, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_txw_sqns_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - const int optname = PGM_TXW_SQNS; - const int txw_sqns = 100; - const void* optval = &txw_sqns; - const socklen_t optlen = sizeof(txw_sqns); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_txw_sqns failed"); -} -END_TEST - -START_TEST (test_set_txw_sqns_fail_001) -{ - const int optname = PGM_TXW_SQNS; - const int txw_sqns = 100; - const void* optval = &txw_sqns; - const socklen_t optlen = sizeof(txw_sqns); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_txw_sqns failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_TXW_SECS, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_txw_secs_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - const int optname = PGM_TXW_SECS; - const int txw_secs = pgm_secs(10); - const void* optval = &txw_secs; - const socklen_t optlen = sizeof(txw_secs); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_txw_secs failed"); -} -END_TEST - -START_TEST (test_set_txw_secs_fail_001) -{ - const int optname = PGM_TXW_SECS; - const int txw_secs = pgm_secs(10); - const void* optval = &txw_secs; - const socklen_t optlen = sizeof(txw_secs); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_txw_secs failed"); -} -END_TEST - -/* target: - * bool - * pgm_setsockopt ( - * pgm_sock_t* const sock, - * const int optname = PGM_TXW_MAX_RTE, - * const void* optval, - * const socklen_t optlen = sizeof(int) - * ) - */ - -START_TEST (test_set_txw_max_rte_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - const int optname = PGM_TXW_MAX_RTE; - const int txw_max_rte = 100*1000; - const void* optval = &txw_max_rte; - const socklen_t optlen = sizeof(txw_max_rte); - fail_unless (TRUE == pgm_setsockopt (sock, optname, optval, optlen), "set_txw_max_rte failed"); -} -END_TEST - -START_TEST (test_set_txw_max_rte_fail_001) -{ - const int optname = PGM_TXW_MAX_RTE; - const int txw_max_rte = 100*1000; - const void* optval = &txw_max_rte; - const socklen_t optlen = sizeof(txw_max_rte); - fail_unless (FALSE == pgm_setsockopt (NULL, optname, optval, optlen), "set_txw_max_rte failed"); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_send = tcase_create ("send"); - suite_add_tcase (s, tc_send); - tcase_add_checked_fixture (tc_send, mock_setup, NULL); - tcase_add_test (tc_send, test_send_pass_001); - tcase_add_test (tc_send, test_send_pass_002); - tcase_add_test (tc_send, test_send_fail_001); - - TCase* tc_sendv = tcase_create ("sendv"); - suite_add_tcase (s, tc_sendv); - tcase_add_checked_fixture (tc_sendv, mock_setup, NULL); - tcase_add_test (tc_sendv, test_sendv_pass_001); - tcase_add_test (tc_sendv, test_sendv_pass_002); - tcase_add_test (tc_sendv, test_sendv_pass_003); - tcase_add_test (tc_sendv, test_sendv_pass_004); - tcase_add_test (tc_sendv, test_sendv_fail_001); - - TCase* tc_send_skbv = tcase_create ("send-skbv"); - suite_add_tcase (s, tc_send_skbv); - tcase_add_checked_fixture (tc_send_skbv, mock_setup, NULL); - tcase_add_test (tc_send_skbv, test_send_skbv_pass_001); - tcase_add_test (tc_send_skbv, test_send_skbv_pass_002); - tcase_add_test (tc_send_skbv, test_send_skbv_fail_001); - - TCase* tc_send_spm = tcase_create ("send-spm"); - suite_add_tcase (s, tc_send_spm); - tcase_add_checked_fixture (tc_send_spm, mock_setup, NULL); - tcase_add_test (tc_send_spm, test_send_spm_pass_001); - tcase_add_test_raise_signal (tc_send_spm, test_send_spm_fail_001, SIGABRT); - - TCase* tc_on_deferred_nak = tcase_create ("on-deferred-nak"); - suite_add_tcase (s, tc_on_deferred_nak); - tcase_add_checked_fixture (tc_on_deferred_nak, mock_setup, NULL); - tcase_add_test (tc_on_deferred_nak, test_on_deferred_nak_pass_001); - tcase_add_test_raise_signal (tc_on_deferred_nak, test_on_deferred_nak_fail_001, SIGABRT); - - TCase* tc_on_spmr = tcase_create ("on-spmr"); - suite_add_tcase (s, tc_on_spmr); - tcase_add_checked_fixture (tc_on_spmr, mock_setup, NULL); - tcase_add_test (tc_on_spmr, test_on_spmr_pass_001); - tcase_add_test (tc_on_spmr, test_on_spmr_pass_002); - tcase_add_test (tc_on_spmr, test_on_spmr_fail_001); - tcase_add_test_raise_signal (tc_on_spmr, test_on_spmr_fail_002, SIGABRT); - - TCase* tc_on_nak = tcase_create ("on-nak"); - suite_add_tcase (s, tc_on_nak); - tcase_add_checked_fixture (tc_on_nak, mock_setup, NULL); - tcase_add_test (tc_on_nak, test_on_nak_pass_001); - tcase_add_test (tc_on_nak, test_on_nak_pass_002); - tcase_add_test (tc_on_nak, test_on_nak_pass_003); - tcase_add_test (tc_on_nak, test_on_nak_pass_004); - tcase_add_test (tc_on_nak, test_on_nak_fail_001); - tcase_add_test_raise_signal (tc_on_nak, test_on_nak_fail_002, SIGABRT); - - TCase* tc_on_nnak = tcase_create ("on-nnak"); - suite_add_tcase (s, tc_on_nnak); - tcase_add_checked_fixture (tc_on_nnak, mock_setup, NULL); - tcase_add_test (tc_on_nnak, test_on_nnak_pass_001); - tcase_add_test (tc_on_nnak, test_on_nnak_fail_001); - tcase_add_test_raise_signal (tc_on_nnak, test_on_nnak_fail_002, SIGABRT); - - TCase* tc_set_ambient_spm = tcase_create ("set-ambient-spm"); - suite_add_tcase (s, tc_set_ambient_spm); - tcase_add_checked_fixture (tc_set_ambient_spm, mock_setup, NULL); - tcase_add_test (tc_set_ambient_spm, test_set_ambient_spm_pass_001); - tcase_add_test (tc_set_ambient_spm, test_set_ambient_spm_fail_001); - - TCase* tc_set_heartbeat_spm = tcase_create ("set-heartbeat-spm"); - suite_add_tcase (s, tc_set_heartbeat_spm); - tcase_add_checked_fixture (tc_set_heartbeat_spm, mock_setup, NULL); - tcase_add_test (tc_set_heartbeat_spm, test_set_heartbeat_spm_pass_001); - tcase_add_test (tc_set_heartbeat_spm, test_set_heartbeat_spm_fail_001); - - TCase* tc_set_txw_sqns = tcase_create ("set-txw-sqns"); - suite_add_tcase (s, tc_set_txw_sqns); - tcase_add_checked_fixture (tc_set_txw_sqns, mock_setup, NULL); - tcase_add_test (tc_set_txw_sqns, test_set_txw_sqns_pass_001); - tcase_add_test (tc_set_txw_sqns, test_set_txw_sqns_fail_001); - - TCase* tc_set_txw_secs = tcase_create ("set-txw-secs"); - suite_add_tcase (s, tc_set_txw_secs); - tcase_add_checked_fixture (tc_set_txw_secs, mock_setup, NULL); - tcase_add_test (tc_set_txw_secs, test_set_txw_secs_pass_001); - tcase_add_test (tc_set_txw_secs, test_set_txw_secs_fail_001); - - TCase* tc_set_txw_max_rte = tcase_create ("set-txw-max-rte"); - suite_add_tcase (s, tc_set_txw_max_rte); - tcase_add_checked_fixture (tc_set_txw_max_rte, mock_setup, NULL); - tcase_add_test (tc_set_txw_max_rte, test_set_txw_max_rte_pass_001); - tcase_add_test (tc_set_txw_max_rte, test_set_txw_max_rte_fail_001); - - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/string.c b/3rdparty/openpgm-svn-r1085/pgm/string.c deleted file mode 100644 index 93458fd..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/string.c +++ /dev/null @@ -1,486 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * portable string manipulation functions. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifdef CONFIG_HAVE_VASPRINTF -# define _GNU_SOURCE -#endif -#include -#include -#include /* _GNU_SOURCE for vasprintf */ -#include -#include - - -//#define STRING_DEBUG - -/* Return copy of string, must be freed with pgm_free(). - */ - -char* -pgm_strdup ( - const char* str - ) -{ - char* new_str; - size_t length; - - if (PGM_LIKELY (NULL != str)) - { - length = strlen (str) + 1; - new_str = malloc (length); - memcpy (new_str, str, length); - } - else - new_str = NULL; - - return new_str; -} - -/* Calculates the maximum space needed to store the output of the sprintf() function. - */ - -int -pgm_printf_string_upper_bound ( - const char* format, - va_list args - ) -{ - char c; - return vsnprintf (&c, 1, format, args) + 1; -} - -/* memory must be freed with free() - */ - -int -pgm_vasprintf ( - char** restrict string, - const char* restrict format, - va_list args - ) -{ - pgm_return_val_if_fail (string != NULL, -1); -#ifdef CONFIG_HAVE_VASPRINTF - const int len = vasprintf (string, format, args); - if (len < 0) - *string = NULL; -#else - va_list args2; - va_copy (args2, args); - *string = malloc (pgm_printf_string_upper_bound (format, args)); -/* NB: must be able to handle NULL args, fails on GCC */ - const int len = vsprintf (*string, format, args); - va_end (args2); -#endif - return len; -} - -char* -pgm_strdup_vprintf ( - const char* format, - va_list args - ) -{ - char *string = NULL; - pgm_vasprintf (&string, format, args); - return string; -} - -static -char* -pgm_stpcpy ( - char* restrict dest, - const char* restrict src - ) -{ - pgm_return_val_if_fail (dest != NULL, NULL); - pgm_return_val_if_fail (src != NULL, NULL); -#ifdef CONFIG_HAVE_STPCPY - return stpcpy (dest, src); -#else - char *d = dest; - const char *s = src; - do { - *d++ = *s; - } while (*s++ != '\0'); - return d - 1; -#endif -} - -char* -pgm_strconcat ( - const char* string1, - ... - ) -{ - size_t l; - va_list args; - char* s; - char* concat; - char* ptr; - - if (!string1) - return NULL; - - l = 1 + strlen (string1); - va_start (args, string1); - s = va_arg (args, char*); - while (s) { - l += strlen (s); - s = va_arg (args, char*); - } - va_end (args); - - concat = malloc (l); - ptr = concat; - - ptr = pgm_stpcpy (ptr, string1); - va_start (args, string1); - s = va_arg (args, char*); - while (s) { - ptr = pgm_stpcpy (ptr, s); - s = va_arg (args, char*); - } - va_end (args); - - return concat; -} - -/* Split a string with delimiter, result must be freed with pgm_strfreev(). - */ - -char** -pgm_strsplit ( - const char* restrict string, - const char* restrict delimiter, - int max_tokens - ) -{ - pgm_slist_t *string_list = NULL, *slist; - char **str_array, *s; - unsigned n = 0; - const char *remainder; - - pgm_return_val_if_fail (string != NULL, NULL); - pgm_return_val_if_fail (delimiter != NULL, NULL); - pgm_return_val_if_fail (delimiter[0] != '\0', NULL); - - if (max_tokens < 1) - max_tokens = INT_MAX; - - remainder = string; - s = strstr (remainder, delimiter); - if (s) - { - const size_t delimiter_len = strlen (delimiter); - - while (--max_tokens && s) - { - const size_t len = s - remainder; - char *new_string = malloc (len + 1); - strncpy (new_string, remainder, len); - new_string[len] = 0; - string_list = pgm_slist_prepend (string_list, new_string); - n++; - remainder = s + delimiter_len; - s = strstr (remainder, delimiter); - } - } - if (*string) - { - n++; - string_list = pgm_slist_prepend (string_list, pgm_strdup (remainder)); - } - - str_array = pgm_new (char*, n + 1); - str_array[n--] = NULL; - for (slist = string_list; slist; slist = slist->next) - str_array[n--] = slist->data; - - pgm_slist_free (string_list); - - return str_array; -} - -/* Free a NULL-terminated array of strings, such as created by pgm_strsplit - */ - -void -pgm_strfreev ( - char** str_array - ) -{ - if (PGM_LIKELY (NULL != str_array)) - { - for (unsigned i = 0; str_array[i] != NULL; i++) - free (str_array[i]); - - pgm_free (str_array); - } -} - -/* resize dynamic string - */ - -static -void -pgm_string_maybe_expand ( - pgm_string_t* string, - size_t len - ) -{ - if ((string->len + len) >= string->allocated_len) - { - string->allocated_len = pgm_nearest_power (1, string->len + len + 1); - string->str = pgm_realloc (string->str, string->allocated_len); - } -} - -/* val may not be a part of string - */ - -static -pgm_string_t* -pgm_string_insert_len ( - pgm_string_t* restrict string, - ssize_t pos, - const char* restrict val, - ssize_t len - ) -{ - pgm_return_val_if_fail (NULL != string, NULL); - pgm_return_val_if_fail (NULL != val, string); - - if (len < 0) - len = strlen (val); - - if (pos < 0) - pos = string->len; - else - pgm_return_val_if_fail ((size_t)pos <= string->len, string); - - pgm_string_maybe_expand (string, len); - - if ((size_t)pos < string->len) - memmove (string->str + pos + len, string->str + pos, string->len - pos); - - if (len == 1) - string->str[pos] = *val; - else - memcpy (string->str + pos, val, len); - string->len += len; - string->str[string->len] = 0; - return string; -} - -static -pgm_string_t* -pgm_string_insert_c ( - pgm_string_t* string, - ssize_t pos, - char c - ) -{ - pgm_return_val_if_fail (NULL != string, NULL); - - if (pos < 0) - pos = string->len; - else - pgm_return_val_if_fail ((size_t)pos <= string->len, string); - - pgm_string_maybe_expand (string, 1); - - if ((size_t)pos < string->len) - memmove (string->str + pos + 1, string->str + pos, string->len - pos); - - string->str[pos] = c; - string->len ++; - string->str[string->len] = '\0'; - return string; -} - -static -pgm_string_t* -pgm_string_append_len ( - pgm_string_t* restrict string, - const char* restrict val, - size_t len - ) -{ - pgm_return_val_if_fail (NULL != string, NULL); - pgm_return_val_if_fail (NULL != val, string); - - return pgm_string_insert_len (string, -1, val, len); -} - -/* create new dynamic string - */ - -static -pgm_string_t* -pgm_string_sized_new ( - size_t init_size - ) -{ - pgm_string_t* string = pgm_new (pgm_string_t, 1); - string->allocated_len = 0; - string->len = 0; - string->str = NULL; - pgm_string_maybe_expand (string, MAX(init_size, 2)); - string->str[0] = '\0'; - return string; -} - -pgm_string_t* -pgm_string_new ( - const char* init - ) -{ - pgm_string_t* string; - - if (NULL == init || '\0' == *init) - string = pgm_string_sized_new (2); - else - { - const size_t len = strlen (init); - string = pgm_string_sized_new (len + 2); - pgm_string_append_len (string, init, len); - } - return string; -} - -/* free dynamic string, optionally just the wrapper object - */ - -char* -pgm_string_free ( - pgm_string_t* string, - bool free_segment - ) -{ - char* segment; - - pgm_return_val_if_fail (NULL != string, NULL); - - if (free_segment) { - pgm_free (string->str); - segment = NULL; - } else - segment = string->str; - - pgm_free (string); - return segment; -} - -static -pgm_string_t* -pgm_string_truncate ( - pgm_string_t* restrict string, - size_t len - ) -{ - pgm_return_val_if_fail (NULL != string, NULL); - - string->len = MIN (len, string->len); - string->str[ string->len ] = '\0'; - - return string; -} - -pgm_string_t* -pgm_string_append ( - pgm_string_t* restrict string, - const char* restrict val - ) -{ - pgm_return_val_if_fail (NULL != string, NULL); - pgm_return_val_if_fail (NULL != val, string); - - return pgm_string_insert_len (string, -1, val, -1); -} - -pgm_string_t* -pgm_string_append_c ( - pgm_string_t* string, - char c - ) -{ - pgm_return_val_if_fail (NULL != string, NULL); - - return pgm_string_insert_c (string, -1, c); -} - -static void pgm_string_append_vprintf (pgm_string_t*restrict, const char*restrict, va_list) PGM_GNUC_PRINTF(2, 0); - -static -void -pgm_string_append_vprintf ( - pgm_string_t* restrict string, - const char* restrict format, - va_list args - ) -{ - char *buf; - int len; - - pgm_return_if_fail (NULL != string); - pgm_return_if_fail (NULL != format); - - len = pgm_vasprintf (&buf, format, args); - if (len >= 0) { - pgm_string_maybe_expand (string, len); - memcpy (string->str + string->len, buf, len + 1); - string->len += len; - free (buf); - } -} - -void -pgm_string_printf ( - pgm_string_t* restrict string, - const char* restrict format, - ... - ) -{ - va_list args; - - pgm_string_truncate (string, 0); - - va_start (args, format); - pgm_string_append_vprintf (string, format, args); - va_end (args); -} - -void -pgm_string_append_printf ( - pgm_string_t* restrict string, - const char* restrict format, - ... - ) -{ - va_list args; - - va_start (args, format); - pgm_string_append_vprintf (string, format, args); - va_end (args); -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/PGM/Test.pm b/3rdparty/openpgm-svn-r1085/pgm/test/PGM/Test.pm deleted file mode 100644 index cb2ee6d..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/PGM/Test.pm +++ /dev/null @@ -1,394 +0,0 @@ -package PGM::Test; - -use strict; -our($VERSION); -use Carp; -use IO::File; -use IPC::Open2; -use Net::SSH qw(sshopen2); -use Sys::Hostname; -use POSIX ":sys_wait_h"; -use JSON; - -$VERSION = '1.00'; - -=head1 NAME - -PGM::Test - PGM test module - -=head1 SYNOPSIS - - $test = PGM::Test->new(); - -=cut - -my $json = new JSON; - -sub new { - my $class = shift; - my $self = {}; - my %params = @_; - - $self->{tag} = exists $params{tag} ? $params{tag} : confess "tag parameter is required"; - $self->{host} = exists $params{host} ? $params{host} : confess "host parameter is required"; - $self->{cmd} = exists $params{cmd} ? $params{cmd} : confess "cmd parameter is required"; - - $self->{in} = IO::File->new(); - $self->{out} = IO::File->new(); - $self->{pid} = undef; - - bless $self, $class; - return $self; -} - -sub connect { - my $self = shift; - my $host = hostname; - - if ($self->{host} =~ /^(localhost|127\.1|127\.0\.0\.1|$host)$/) - { - print "$self->{tag}: opening local connection\n"; - $self->{pid} = open2 ($self->{in}, - $self->{out}, - "uname -a && sudo $self->{cmd}") - or croak "open2 failed $!"; - } - else - { - print "$self->{tag}: opening SSH connection to $self->{host} ...\n"; - $self->{pid} = sshopen2 ($self->{host}, - $self->{in}, - $self->{out}, - "uname -a && sudo $self->{cmd}") - or croak "SSH failed: $!"; - } - - print "$self->{tag}: connected.\n"; - $self->wait_for_ready; -} - -sub disconnect { - my($self,$quiet) = @_; - my $out = $self->{out}; - - print "$self->{tag}: sending quit command ...\n"; - eval { - local($SIG{ALRM}) = sub { die "alarm\n"; }; - alarm 10; - print $out "quit\n"; - while (readline($self->{in})) { - chomp; - print "$self->{tag} [$_]\n" if (!$quiet); - } - alarm 0; - }; - if ($@) { - print "$self->{tag}: alarm raised on quit command.\n"; - } else { - print "$self->{tag}: eof.\n"; - } - - print "$self->{tag}: closing SSH connection ...\n"; - close ($self->{in}); - close ($self->{out}); - print "$self->{tag}: closed.\n"; -} - -sub DESTROY { - my $self = shift; - - if ($self->{pid}) { - print "$self->{tag}: waiting child to terminate ...\n"; - eval { - local($SIG{ALRM}) = sub { die "alarm\n"; }; - alarm 10; - waitpid $self->{pid}, 0; - alarm 0; - }; - if ($@) { - die unless $@ eq "alarm\n"; - local($SIG{CHLD}) = 'IGNORE'; - print "$self->{tag}: killing child ...\n"; - kill 'INT' => $self->{pid}; - print "$self->{tag}: killed.\n"; - } else { - print "$self->{tag}: terminated.\n"; - } - } -} - -sub wait_for_ready { - my $self = shift; - - while (readline($self->{in})) { - chomp; - print "$self->{tag} [$_]\n"; - last if /^READY/; - } -} - -sub wait_for_block { - my $self = shift; - my $fh = $self->{in}; - my $b = ''; - my $state = 0; - - while (<$fh>) { - chomp(); - my $l = $_; - if ($state == 0) { - if ($l =~ /^{$/) { - $state = 1; - } else { - print "$self->{tag} [$l]\n"; - } - } - - if ($state == 1) { - $b .= $l; - - if ($l =~ /^}$/) { - $state = 0; - return $b; - } - } - } -} - -sub wait_for_spm { - my $self = shift; - my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; - my $obj = undef; - - eval { - local $SIG{ALRM} = sub { die "alarm\n"; }; - alarm $timeout; - for (;;) { - my $block = $self->wait_for_block; - $obj = $json->jsonToObj($block); - last if ($obj->{PGM}->{type} =~ /SPM$/); - } - alarm 0; - }; - if ($@) { - die unless $@ eq "alarm\n"; - confess "$self->{tag}: alarm raised waiting for spm.\n"; - } - - return $obj; -} - -sub wait_for_spmr { - my $self = shift; - my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; - my $obj = undef; - - eval { - local $SIG{ALRM} = sub { die "alarm\n"; }; - alarm $timeout; - for (;;) { - my $block = $self->wait_for_block; - $obj = $json->jsonToObj($block); - last if ($obj->{PGM}->{type} =~ /SPMR/); - } - alarm 0; - }; - if ($@) { - die unless $@ eq "alarm\n"; - confess "$self->{tag}: alarm raised waiting for spmr.\n"; - } - - return $obj; -} - -sub die_on_spmr { - my $self = shift; - my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; - my $obj = undef; - - eval { - local $SIG{ALRM} = sub { die "alarm\n"; }; - alarm $timeout; - for (;;) { - my $block = $self->wait_for_block; - $obj = $json->jsonToObj($block); - last if ($obj->{PGM}->{type} =~ /SPMR/); - } - alarm 0; - }; - if ($@) { - die unless $@ eq "alarm\n"; - return $obj; - } - - confess "$self->{tag}: spmr received during blackout.\n"; -} - -# data to {app} -sub wait_for_data { - my $self = shift; - my $fh = $self->{in}; - my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; - my $data = undef; - - eval { - local $SIG{ALRM} = sub { die "alarm\n"; }; - alarm $timeout; - while (<$fh>) { - chomp; - if (/^DATA: (.+)$/) { - $data = $1; - last; - } - print "$self->{tag} [$_]\n"; - } - alarm 0; - }; - if ($@) { - die unless $@ eq "alarm\n"; - confess "$self->{tag}: alarm raised waiting for data.\n"; - } - - return $data; -} - -sub wait_for_odata { - my $self = shift; - my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; - my $obj = undef; - - eval { - local $SIG{ALRM} = sub { die "alarm\n"; }; - alarm $timeout; - for (;;) { - my $block = $self->wait_for_block; - $obj = $json->jsonToObj($block); - last if ($obj->{PGM}->{type} =~ /ODATA/); - } - alarm 0; - }; - if ($@) { - die unless $@ eq "alarm\n"; - confess "$self->{tag}: alarm raised waiting for odata.\n"; - } - - return $obj; -} - -sub wait_for_rdata { - my $self = shift; - my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; - my $obj = undef; - - eval { - local $SIG{ALRM} = sub { die "alarm\n"; }; - alarm $timeout; - for (;;) { - my $block = $self->wait_for_block; - $obj = $json->jsonToObj($block); - last if ($obj->{PGM}->{type} =~ /RDATA/); - } - alarm 0; - }; - if ($@) { - die unless $@ eq "alarm\n"; - confess "$self->{tag}: alarm raised waiting for rdata.\n"; - } - - return $obj; -} - -sub die_on_nak { - my $self = shift; - my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; - my $obj = undef; - - eval { - local $SIG{ALRM} = sub { die "alarm\n"; }; - alarm $timeout; - for (;;) { - my $block = $self->wait_for_block; - $obj = $json->jsonToObj($block); - last if ($obj->{PGM}->{type} =~ /NAK/); - } - alarm 0; - }; - if ($@) { - die unless $@ eq "alarm\n"; - return $obj; - } - - confess "$self->{tag}: nak received during blackout.\n"; -} - -sub wait_for_nak { - my $self = shift; - my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; - my $obj = undef; - - eval { - local $SIG{ALRM} = sub { die "alarm\n"; }; - alarm $timeout; - for (;;) { - my $block = $self->wait_for_block; - $obj = $json->jsonToObj($block); - last if ($obj->{PGM}->{type} =~ /NAK/); - } - alarm 0; - }; - if ($@) { - die unless $@ eq "alarm\n"; - confess "$self->{tag}: alarm raised waiting for nak.\n"; - } - - return $obj; -} - -sub wait_for_ncf { - my $self = shift; - my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; - my $obj = undef; - - eval { - local $SIG{ALRM} = sub { die "alarm\n"; }; - alarm $timeout; - for (;;) { - my $block = $self->wait_for_block; - $obj = $json->jsonToObj($block); - last if ($obj->{PGM}->{type} =~ /NCF/); - } - alarm 0; - }; - if ($@) { - die unless $@ eq "alarm\n"; - confess "$self->{tag}: alarm raised waiting for ncf.\n"; - } - - return $obj; -} - -sub print { - my $self = shift; - my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; - my $out = $self->{out}; - - print "$self->{tag}> @_"; - eval { - local($SIG{ALRM}) = sub { die "alarm\n"; }; - alarm $timeout; - print $out "@_"; - $self->wait_for_ready; - alarm 0; - }; - if ($@) { - die unless $@ eq "alarm\n"; - confess "$self->{tag}: alarm raised.\n"; - } -} - -sub say { - my $self = shift; - $self->print ("@_\n"); -} - -1; diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/SConscript b/3rdparty/openpgm-svn-r1085/pgm/test/SConscript deleted file mode 100644 index 7ca9926..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/SConscript +++ /dev/null @@ -1,15 +0,0 @@ -# -*- mode: python -*- -# OpenPGM build script -# $Id$ - -Import('env') -e = env.Clone(); -e.MergeFlags(env['GLIB_FLAGS']); -e.Append(LIBS = ['libpgm', 'libpgmex']); -e.Append(CCFLAGS = '-DGETTEXT_PACKAGE=\'"pgm"\''); - -e.Program(['monitor.c', 'dump-json.c']) -e.Program(['app.c', 'async.c']) -e.Program(['sim.c', 'dump-json.c', 'async.c']) - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/ambient_spm.pl b/3rdparty/openpgm-svn-r1085/pgm/test/ambient_spm.pl deleted file mode 100755 index dca52a5..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/ambient_spm.pl +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/perl -# ambient_spm.pl -# 5.1.4. Ambient SPMs - -use strict; -use PGM::Test; -use IO::Handle; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -pipe(FROM_PARENT, TO_CHILD) or die "pipe: $!"; -FROM_PARENT->autoflush(1); - -$mon->connect; -$app->connect; - -sub close_ssh { - close FROM_PARENT; close TO_CHILD; - $mon = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -if (my $pid = fork) { -# parent - close FROM_PARENT; - - print "mon: wait for odata ...\n"; - $mon->wait_for_odata; - print "mon: odata received.\n"; - print "mon: wait for spm ...\n"; - $mon->wait_for_spm ({ 'timeout' => 45 }); - print "mon: received spm.\n"; - - print TO_CHILD "die\n"; - - close TO_CHILD; - waitpid($pid,0); -} else { -# child - die "cannot fork: $!" unless defined $pid; - close TO_CHILD; - print "app: loop sending data.\n"; - vec(my $rin, fileno(FROM_PARENT), 1) = 1; - my $rout = undef; - -# hide stdout - open(OLDOUT, ">&STDOUT"); - open(STDOUT, ">/dev/null") or die "Can't redirect stdout: $!"; - -# send every ~50ms - while (! select($rout = $rin, undef, undef, 0.05)) - { - $app->say ("send ao ringo"); - } - -# restore stdout - close(STDOUT) or die "Can't close STDOUT: $!"; - open(STDOUT, ">&OLDOUT") or die "Can't restore stdout: $!"; - close(OLDOUT) or die "Can't close OLDOUT: $!"; - - print "app: loop finished.\n"; - close FROM_PARENT; - exit; -} - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/apdu.pl b/3rdparty/openpgm-svn-r1085/pgm/test/apdu.pl deleted file mode 100755 index f4669c0..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/apdu.pl +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/perl -# apdu.pl -# 6.1. Data Reception - -use strict; -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("set network $config{app}{network}"); -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -$sim->say ("set network $config{sim}{network}"); -$sim->say ("create ao"); -$sim->say ("bind ao"); - -print "sim: publish APDU.\n"; -$sim->say ("send ao ringo x 1000"); - -print "app: wait for data ...\n"; -my $data = $app->wait_for_data; -print "app: received data [$data].\n"; - -my $ref_data = "ringo" x 1000; -die "incoming data corrupt\n" unless ($data == $ref_data); - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/apdu_parity.pl b/3rdparty/openpgm-svn-r1085/pgm/test/apdu_parity.pl deleted file mode 100755 index eb4cf27..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/apdu_parity.pl +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/perl -# apdu_parity.pl -# 6.1. Data Reception - -use strict; -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$sim->connect; -$app->connect; - -sub close_ssh { - $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$app->say ("create ao"); -##$app->say ("set ao FEC RS(255,4)"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -$sim->say ("create ao"); -$sim->say ("set ao FEC RS(255,4)"); -$sim->say ("bind ao"); - -print "sim: publish APDU.\n"; -$sim->say ("send brokn ao ringo x 1200"); - -print "sim: insert parity NAK from app.\n"; - -#print "sim: wait for NAK.\n"; -#my $nak = $sim->wait_for_nak; -#die "Selective NAK received, parityPacket=false\n" unless $nak->{PGM}->{options}->{parityPacket}; -#print "Parity NAK received.\n"; - -print "sim: insert parity RDATA from sim.\n"; - -print "app: wait for data ...\n"; -my $data = $app->wait_for_data; -print "app: received data [$data].\n"; - -my $ref_data = "ringo" x 1200; -die "incoming data corrupt\n" unless ($data == $ref_data); - -print "test completed successfully.\n"; - -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/app.c b/3rdparty/openpgm-svn-r1085/pgm/test/app.c deleted file mode 100644 index 1d8f585..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/app.c +++ /dev/null @@ -1,904 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * PGM conformance test application. - * - * Copyright (c) 2006-2007 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include "async.h" - - -/* typedefs */ - -struct idle_source { - GSource source; - guint64 expiration; -}; - -struct app_session { - char* name; - pgm_transport_t* transport; - pgm_async_t* async; -}; - -/* globals */ -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "app" - -static int g_port = 7500; -static const char* g_network = ";239.192.0.1"; - -static guint g_max_tpdu = 1500; -static guint g_sqns = 100 * 1000; - -static GHashTable* g_sessions = NULL; -static GMainLoop* g_loop = NULL; -static GIOChannel* g_stdin_channel = NULL; - - -static void on_signal (int, gpointer); -static gboolean on_startup (gpointer); -static gboolean on_mark (gpointer); -static void destroy_session (gpointer, gpointer, gpointer); -static int on_data (gpointer, guint, gpointer); -static gboolean on_stdin_data (GIOChannel*, GIOCondition, gpointer); - - -G_GNUC_NORETURN static -void -usage (const char* bin) -{ - fprintf (stderr, "Usage: %s [options]\n", bin); - fprintf (stderr, " -n : Multicast group or unicast IP address\n"); - fprintf (stderr, " -s : IP port\n"); - exit (1); -} - -int -main ( - int argc, - char *argv[] - ) -{ - pgm_error_t* err = NULL; - -/* pre-initialise PGM messages module to add hook for GLib logging */ - pgm_messages_init(); - log_init (); - g_message ("app"); - - if (!pgm_init (&err)) { - g_error ("Unable to start PGM engine: %s", (err && err->message) ? err->message : "(null)"); - pgm_error_free (err); - pgm_messages_shutdown(); - return EXIT_FAILURE; - } - -/* parse program arguments */ - const char* binary_name = strrchr (argv[0], '/'); - int c; - while ((c = getopt (argc, argv, "s:n:h")) != -1) - { - switch (c) { - case 'n': g_network = optarg; break; - case 's': g_port = atoi (optarg); break; - - case 'h': - case '?': - pgm_messages_shutdown(); - usage (binary_name); - } - } - - g_loop = g_main_loop_new (NULL, FALSE); - -/* setup signal handlers */ - signal (SIGSEGV, on_sigsegv); - signal (SIGHUP, SIG_IGN); - pgm_signal_install (SIGINT, on_signal, g_loop); - pgm_signal_install (SIGTERM, on_signal, g_loop); - -/* delayed startup */ - g_message ("scheduling startup."); - g_timeout_add (0, (GSourceFunc)on_startup, NULL); - -/* dispatch loop */ - g_message ("entering main event loop ... "); - g_main_loop_run (g_loop); - - g_message ("event loop terminated, cleaning up."); - -/* cleanup */ - g_main_loop_unref(g_loop); - g_loop = NULL; - - if (g_sessions) { - g_message ("destroying sessions."); - g_hash_table_foreach_remove (g_sessions, (GHRFunc)destroy_session, NULL); - g_hash_table_unref (g_sessions); - g_sessions = NULL; - } - - if (g_stdin_channel) { - puts ("unbinding stdin."); - g_io_channel_unref (g_stdin_channel); - g_stdin_channel = NULL; - } - - g_message ("PGM engine shutdown."); - pgm_shutdown(); - g_message ("finished."); - pgm_messages_shutdown(); - return EXIT_SUCCESS; -} - -static -void -destroy_session ( - gpointer key, /* session name */ - gpointer value, /* transport_session object */ - G_GNUC_UNUSED gpointer user_data - ) -{ - struct app_session* sess = (struct app_session*)value; - - g_message ("destroying transport \"%s\"", (char*)key); - pgm_transport_destroy (sess->transport, TRUE); - sess->transport = NULL; - - if (sess->async) { - g_message ("destroying asynchronous session on \"%s\"", (char*)key); - pgm_async_destroy (sess->async); - sess->async = NULL; - } - - g_free (sess->name); - sess->name = NULL; - g_free (sess); -} - -static -void -on_signal ( - int signum, - gpointer user_data - ) -{ - GMainLoop* loop = (GMainLoop*)user_data; - g_message ("on_signal (signum:%d user-data:%p)", signum, user_data); - g_main_loop_quit (loop); -} - -static -gboolean -on_startup ( - G_GNUC_UNUSED gpointer data - ) -{ - g_message ("startup."); - - g_sessions = g_hash_table_new (g_str_hash, g_str_equal); - -/* add stdin to event manager */ - g_stdin_channel = g_io_channel_unix_new (fileno(stdin)); - printf ("binding stdin with encoding %s.\n", g_io_channel_get_encoding(g_stdin_channel)); - - g_io_add_watch (g_stdin_channel, G_IO_IN | G_IO_PRI, on_stdin_data, NULL); - -/* period timer to indicate some form of life */ -// TODO: Gnome 2.14: replace with g_timeout_add_seconds() - g_timeout_add(10 * 1000, (GSourceFunc)on_mark, NULL); - - puts ("READY"); - fflush (stdout); - return FALSE; -} - -static -int -on_data ( - gpointer data, - G_GNUC_UNUSED guint len, - G_GNUC_UNUSED gpointer user_data - ) -{ - printf ("DATA: %s\n", (char*)data); - fflush (stdout); - return 0; -} - -static -void -session_create ( - char* name - ) -{ - struct pgm_transport_info_t hints = { - .ti_family = AF_INET - }, *res = NULL; - pgm_error_t* err = NULL; - -/* check for duplicate */ - struct app_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess != NULL) { - puts ("FAILED: duplicate session"); - return; - } - -/* create new and fill in bits */ - sess = g_new0(struct app_session, 1); - sess->name = g_memdup (name, strlen(name)+1); - - if (!pgm_if_get_transport_info (g_network, &hints, &res, &err)) { - printf ("FAILED: pgm_if_get_transport_info(): %s\n", (err && err->message) ? err->message : "(null)"); - pgm_error_free (err); - goto err_free; - } - - if (!pgm_gsi_create_from_hostname (&res->ti_gsi, &err)) { - printf ("FAILED: pgm_gsi_create_from_hostname(): %s\n", (err && err->message) ? err->message : "(null)"); - pgm_error_free (err); - pgm_if_free_transport_info (res); - goto err_free; - } - - res->ti_dport = g_port; - res->ti_sport = 0; -printf ("pgm_transport_create (transport:%p res:%p err:%p)\n", (gpointer)sess->transport, (gpointer)res, (gpointer)&err); - if (!pgm_transport_create (&sess->transport, res, &err)) { - printf ("FAILED: pgm_transport_create(): %s\n", (err && err->message) ? err->message : "(null)"); - pgm_error_free (err); - pgm_if_free_transport_info (res); - goto err_free; - } - - pgm_if_free_transport_info (res); - -/* success */ - g_hash_table_insert (g_sessions, sess->name, sess); - printf ("created new session \"%s\"\n", sess->name); - puts ("READY"); - - return; - -err_free: - g_free(sess->name); - g_free(sess); -} - -static -void -session_set_nak_bo_ivl ( - char* name, - guint nak_bo_ivl /* milliseconds */ - ) -{ -/* check that session exists */ - struct app_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess == NULL) { - puts ("FAILED: session not found"); - return; - } - - if (!pgm_transport_set_nak_bo_ivl (sess->transport, pgm_msecs(nak_bo_ivl))) - puts ("FAILED: pgm_transport_set_nak_bo_ivl"); - else - puts ("READY"); -} - -static -void -session_set_nak_rpt_ivl ( - char* name, - guint nak_rpt_ivl /* milliseconds */ - ) -{ -/* check that session exists */ - struct app_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess == NULL) { - puts ("FAILED: session not found"); - return; - } - - if (!pgm_transport_set_nak_rpt_ivl (sess->transport, pgm_msecs(nak_rpt_ivl))) - puts ("FAILED: pgm_transport_set_nak_rpt_ivl"); - else - puts ("READY"); -} - -static -void -session_set_nak_rdata_ivl ( - char* name, - guint nak_rdata_ivl /* milliseconds */ - ) -{ -/* check that session exists */ - struct app_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess == NULL) { - puts ("FAILED: session not found"); - return; - } - - if (!pgm_transport_set_nak_rdata_ivl (sess->transport, pgm_msecs(nak_rdata_ivl))) - puts ("FAILED: pgm_transport_set_nak_rdata_ivl"); - else - puts ("READY"); -} - -static -void -session_set_nak_ncf_retries ( - char* name, - guint nak_ncf_retries - ) -{ -/* check that session exists */ - struct app_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess == NULL) { - puts ("FAILED: session not found"); - return; - } - - if (!pgm_transport_set_nak_ncf_retries (sess->transport, nak_ncf_retries)) - puts ("FAILED pgm_transport_set_nak_ncf_retries"); - else - puts ("READY"); -} - -static -void -session_set_nak_data_retries ( - char* name, - guint nak_data_retries - ) -{ -/* check that session exists */ - struct app_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess == NULL) { - puts ("FAILED: session not found"); - return; - } - - if (!pgm_transport_set_nak_data_retries (sess->transport, nak_data_retries)) - puts ("FAILED: pgm_transport_set_nak_data_retries"); - else - puts ("READY"); -} - -static -void -session_set_txw_max_rte ( - char* name, - guint txw_max_rte - ) -{ -/* check that session exists */ - struct app_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess == NULL) { - puts ("FAILED: session not found"); - return; - } - - if (!pgm_transport_set_txw_max_rte (sess->transport, txw_max_rte)) - puts ("FAILED:pgm_transport_set_txw_max_rte"); - else - puts ("READY"); -} - -static -void -session_set_fec ( - char* name, - guint default_n, - guint default_k - ) -{ -/* check that session exists */ - struct app_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess == NULL) { - puts ("FAILED: session not found"); - return; - } - - if (!pgm_transport_set_fec (sess->transport, - FALSE /* pro-active */, - TRUE /* on-demand */, - TRUE /* varpkt-len */, - default_n, - default_k)) - { - puts ("FAILED: pgm_transport_set_fec"); - } - else - { - puts ("READY"); - } -} - -static -void -session_bind ( - char* name - ) -{ - const guint spm_heartbeat[] = { pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(1300), pgm_secs(7), pgm_secs(16), pgm_secs(25), pgm_secs(30) }; - pgm_error_t* err = NULL; - -/* check that session exists */ - struct app_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess == NULL) { - puts ("FAILED: session not found"); - return; - } - - if (!pgm_transport_set_nonblocking (sess->transport, TRUE)) - puts ("FAILED: pgm_transport_set_nonblocking"); - if (!pgm_transport_set_max_tpdu (sess->transport, g_max_tpdu)) - puts ("FAILED: pgm_transport_set_max_tpdu"); - if (!pgm_transport_set_txw_sqns (sess->transport, g_sqns)) - puts ("FAILED: pgm_transport_set_txw_sqns"); - if (!pgm_transport_set_rxw_sqns (sess->transport, g_sqns)) - puts ("FAILED: pgm_transport_set_rxw_sqns"); - if (!pgm_transport_set_hops (sess->transport, 16)) - puts ("FAILED: pgm_transport_set_hops"); - if (!pgm_transport_set_ambient_spm (sess->transport, pgm_secs(30))) - puts ("FAILED: pgm_transport_set_ambient_spm"); - if (!pgm_transport_set_heartbeat_spm (sess->transport, spm_heartbeat, G_N_ELEMENTS(spm_heartbeat))) - puts ("FAILED: pgm_transport_set_heartbeat_spm"); - if (!pgm_transport_set_peer_expiry (sess->transport, pgm_secs(300))) - puts ("FAILED: pgm_transport_set_peer_expiry"); - if (!pgm_transport_set_spmr_expiry (sess->transport, pgm_msecs(250))) - puts ("FAILED: pgm_transport_set_spmr_expiry"); - if (!sess->transport->nak_bo_ivl && !pgm_transport_set_nak_bo_ivl (sess->transport, pgm_msecs(50))) - puts ("FAILED: pgm_transport_set_nak_bo_ivl"); - if (!sess->transport->nak_rpt_ivl && !pgm_transport_set_nak_rpt_ivl (sess->transport, pgm_secs(2))) - puts ("FAILED: pgm_transport_set_nak_rpt_ivl"); - if (!sess->transport->nak_rdata_ivl && !pgm_transport_set_nak_rdata_ivl (sess->transport, pgm_secs(2))) - puts ("FAILED: pgm_transport_set_nak_rdata_ivl"); - if (!sess->transport->nak_data_retries && !pgm_transport_set_nak_data_retries (sess->transport, 50)) - puts ("FAILED: pgm_transport_set_nak_data_retries"); - if (!sess->transport->nak_ncf_retries && !pgm_transport_set_nak_ncf_retries (sess->transport, 50)) - puts ("FAILED: pgm_transport_set_nak_ncf_retries"); - -printf ("pgm_transport_bind (transport:%p err:%p)\n", (gpointer)sess->transport, (gpointer)&err); - if (!pgm_transport_bind (sess->transport, &err)) { - printf ("FAILED: pgm_transport_bind(): %s\n", (err && err->message) ? err->message : "(null)"); - pgm_error_free (err); - } else - puts ("READY"); -} - -static -void -session_send ( - char* name, - char* string - ) -{ -/* check that session exists */ - struct app_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess == NULL) { - puts ("FAILED: session not found"); - return; - } - -/* send message */ - int status; - gsize stringlen = strlen(string) + 1; - int n_fds = 1; - struct pollfd fds[ n_fds ]; - struct timeval tv; - int timeout; -again: -printf ("pgm_send (transport:%p string:\"%s\" stringlen:%" G_GSIZE_FORMAT " NULL)\n", (gpointer)sess->transport, string, stringlen); - status = pgm_send (sess->transport, string, stringlen, NULL); - switch (status) { - case PGM_IO_STATUS_NORMAL: - puts ("READY"); - break; - case PGM_IO_STATUS_TIMER_PENDING: - pgm_transport_get_timer_pending (sess->transport, &tv); - goto block; - case PGM_IO_STATUS_RATE_LIMITED: - pgm_transport_get_rate_remaining (sess->transport, &tv); -/* fall through */ - case PGM_IO_STATUS_WOULD_BLOCK: -block: - timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); - memset (fds, 0, sizeof(fds)); - pgm_transport_poll_info (sess->transport, fds, &n_fds, POLLOUT); - poll (fds, n_fds, timeout /* ms */); - goto again; - default: - puts ("FAILED: pgm_send()"); - break; - } -} - -static -void -session_listen ( - char* name - ) -{ - GError* err = NULL; - -/* check that session exists */ - struct app_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess == NULL) { - puts ("FAILED: session not found"); - return; - } - -/* listen */ -printf ("pgm_async_create (async:%p transport:%p err:%p)\n", (gpointer)&sess->async, (gpointer)sess->transport, (gpointer)&err); - if (!pgm_async_create (&sess->async, sess->transport, &err)) { - printf ("FAILED: pgm_async_create(): %s", err->message); - g_error_free (err); - return; - } - pgm_async_add_watch (sess->async, on_data, sess); - puts ("READY"); -} - -static -void -session_destroy ( - char* name - ) -{ -/* check that session exists */ - struct app_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess == NULL) { - puts ("FAILED: session not found"); - return; - } - -/* remove from hash table */ - g_hash_table_remove (g_sessions, name); - -/* stop any async thread */ - if (sess->async) { - pgm_async_destroy (sess->async); - sess->async = NULL; - } - - pgm_transport_destroy (sess->transport, TRUE); - sess->transport = NULL; - g_free (sess->name); - sess->name = NULL; - g_free (sess); - - puts ("READY"); -} - -/* process input commands from stdin/fd - */ - -static -gboolean -on_stdin_data ( - GIOChannel* source, - G_GNUC_UNUSED GIOCondition condition, - G_GNUC_UNUSED gpointer data - ) -{ - gchar* str = NULL; - gsize len = 0; - gsize term = 0; - GError* err = NULL; - - g_io_channel_read_line (source, &str, &len, &term, &err); - if (len > 0) { - if (term) str[term] = 0; - -/* quit */ - if (strcmp(str, "quit") == 0) - { - g_main_loop_quit(g_loop); - goto out; - } - - regex_t preg; - regmatch_t pmatch[10]; - -/* create transport */ - const char *re = "^create[[:space:]]+([[:alnum:]]+)$"; - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); - name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; - - session_create (name); - - g_free (name); - regfree (&preg); - goto out; - } - regfree (&preg); - -/* set NAK_BO_IVL */ - re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+NAK_BO_IVL[[:space:]]+([0-9]+)$"; - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); - name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; - - char *p = str + pmatch[2].rm_so; - guint nak_bo_ivl = strtol (p, &p, 10); - - session_set_nak_bo_ivl (name, nak_bo_ivl); - - g_free (name); - regfree (&preg); - goto out; - } - regfree (&preg); - -/* set NAK_RPT_IVL */ - re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+NAK_RPT_IVL[[:space:]]+([0-9]+)$"; - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); - name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; - - char *p = str + pmatch[2].rm_so; - guint nak_rpt_ivl = strtol (p, &p, 10); - - session_set_nak_rpt_ivl (name, nak_rpt_ivl); - - g_free (name); - regfree (&preg); - goto out; - } - regfree (&preg); - -/* set NAK_RDATA_IVL */ - re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+NAK_RDATA_IVL[[:space:]]+([0-9]+)$"; - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); - name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; - - char *p = str + pmatch[2].rm_so; - guint nak_rdata_ivl = strtol (p, &p, 10); - - session_set_nak_rdata_ivl (name, nak_rdata_ivl); - - g_free (name); - regfree (&preg); - goto out; - } - regfree (&preg); - -/* set NAK_NCF_RETRIES */ - re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+NAK_NCF_RETRIES[[:space:]]+([0-9]+)$"; - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); - name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; - - char *p = str + pmatch[2].rm_so; - guint nak_ncf_retries = strtol (p, &p, 10); - - session_set_nak_ncf_retries (name, nak_ncf_retries); - - g_free (name); - regfree (&preg); - goto out; - } - regfree (&preg); - -/* set NAK_DATA_RETRIES */ - re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+NAK_DATA_RETRIES[[:space:]]+([0-9]+)$"; - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); - name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; - - char *p = str + pmatch[2].rm_so; - guint nak_data_retries = strtol (p, &p, 10); - - session_set_nak_data_retries (name, nak_data_retries); - - g_free (name); - regfree (&preg); - goto out; - } - regfree (&preg); - -/* set TXW_MAX_RTE */ - re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+TXW_MAX_RTE[[:space:]]+([0-9]+)$"; - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); - name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; - - char *p = str + pmatch[2].rm_so; - guint txw_max_rte = strtol (p, &p, 10); - - session_set_txw_max_rte (name, txw_max_rte); - - g_free (name); - regfree (&preg); - goto out; - } - regfree (&preg); - -/* enable Reed-Solomon Forward Error Correction */ - re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+FEC[[:space:]]+RS[[:space:]]*\\([[:space:]]*([0-9]+)[[:space:]]*,[[:space:]]*([0-9]+)[[:space:]]*\\)$"; - regcomp (&preg, re, REG_EXTENDED); - - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); - name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; - - char *p = str + pmatch[2].rm_so; - *(str + pmatch[2].rm_eo) = 0; - guint n = strtol (p, &p, 10); - p = str + pmatch[3].rm_so; - *(str + pmatch[3].rm_eo) = 0; - guint k = strtol (p, &p, 10); - session_set_fec (name, n, k); - - g_free (name); - regfree (&preg); - goto out; - } - regfree (&preg); - -/* bind transport */ - re = "^bind[[:space:]]+([[:alnum:]]+)$"; - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); - name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; - - session_bind (name); - - g_free (name); - regfree (&preg); - goto out; - } - regfree (&preg); - -/* send packet */ - re = "^send[[:space:]]+([[:alnum:]]+)[[:space:]]+([[:alnum:]]+)$"; - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); - name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; - - char *string = g_memdup (str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so + 1 ); - string[ pmatch[2].rm_eo - pmatch[2].rm_so ] = 0; - - session_send (name, string); - - g_free (name); - g_free (string); - regfree (&preg); - goto out; - } - regfree (&preg); - -/* listen */ - re = "^listen[[:space:]]+([[:alnum:]]+)$"; - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); - name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; - - session_listen (name); - - g_free (name); - regfree (&preg); - goto out; - } - regfree (&preg); - -/* destroy transport */ - re = "^destroy[[:space:]]+([[:alnum:]]+)$"; - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); - name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; - - session_destroy (name); - - g_free (name); - regfree (&preg); - goto out; - } - regfree (&preg); - -/* set PGM network */ - re = "^set[[:space:]]+network[[:space:]]+([[:print:]]*;[[:print:]]+)$"; - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char* pgm_network = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); - pgm_network[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; - g_network = pgm_network; - puts ("READY"); - - regfree (&preg); - goto out; - } - regfree (&preg); - - printf ("unknown command: %s\n", str); - } - -out: - fflush (stdout); - g_free (str); - return TRUE; -} - -/* idle log notification - */ - -static -gboolean -on_mark ( - G_GNUC_UNUSED gpointer data - ) -{ - g_message ("-- MARK --"); - return TRUE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/async.c b/3rdparty/openpgm-svn-r1085/pgm/test/async.c deleted file mode 100644 index cc2c1ba..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/async.c +++ /dev/null @@ -1,572 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Asynchronous queue for receiving packets in a separate managed thread. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include -#include -#include "async.h" - - -//#define ASYNC_DEBUG - -#ifndef ASYNC_DEBUG -# define g_trace(...) while (0) -#else -#include -# define g_trace(...) g_debug(__VA_ARGS__) -#endif - - -/* globals */ - - -/* global locals */ - -typedef struct pgm_event_t pgm_event_t; - -struct pgm_event_t { - gpointer data; - guint len; -}; - - -/* external: Glib event loop GSource of pgm contiguous data */ -struct pgm_watch_t { - GSource source; - GPollFD pollfd; - pgm_async_t* async; -}; - -typedef struct pgm_watch_t pgm_watch_t; - - -static gboolean pgm_src_prepare (GSource*, gint*); -static gboolean pgm_src_check (GSource*); -static gboolean pgm_src_dispatch (GSource*, GSourceFunc, gpointer); - -static GSourceFuncs g_pgm_watch_funcs = { - .prepare = pgm_src_prepare, - .check = pgm_src_check, - .dispatch = pgm_src_dispatch, - .finalize = NULL, - .closure_callback = NULL -}; - - -static inline gpointer pgm_event_alloc (pgm_async_t* const) G_GNUC_MALLOC; -static PGMAsyncError pgm_async_error_from_errno (const gint); - - -static inline -gpointer -pgm_event_alloc ( - pgm_async_t* const async - ) -{ - g_return_val_if_fail (async != NULL, NULL); - return g_slice_alloc (sizeof(pgm_event_t)); -} - -/* release event memory for custom async queue dispatch handlers - */ - -static inline -void -pgm_event_unref ( - pgm_async_t* const async, - pgm_event_t* const event - ) -{ - g_return_if_fail (async != NULL); - g_return_if_fail (event != NULL); - g_slice_free1 (sizeof(pgm_event_t), event); -} - -/* internal receiver thread, sits in a loop processing incoming packets - */ - -static -gpointer -pgm_receiver_thread ( - gpointer data - ) -{ - g_assert (NULL != data); - - pgm_async_t* async = (pgm_async_t*)data; - g_async_queue_ref (async->commit_queue); - -/* incoming message buffer */ - struct pgm_msgv_t msgv; - gsize bytes_read = 0; - struct timeval tv; - - do { -/* blocking read */ - const int status = pgm_recvmsg (async->transport, &msgv, 0, &bytes_read, NULL); - switch (status) { - case PGM_IO_STATUS_NORMAL: - { -/* queue a copy to receiver */ - pgm_event_t* event = pgm_event_alloc (async); - event->data = bytes_read > 0 ? g_malloc (bytes_read) : NULL; - event->len = bytes_read; - gpointer dst = event->data; - guint i = 0; - while (bytes_read) { - const struct pgm_sk_buff_t* skb = msgv.msgv_skb[i++]; - g_assert (NULL != skb); - g_assert (skb->len > 0); - g_assert (skb->len <= bytes_read); - memcpy (dst, skb->data, skb->len); - dst = (char*)dst + skb->len; - bytes_read -= skb->len; - } -/* prod pipe on edge */ - g_async_queue_lock (async->commit_queue); - g_async_queue_push_unlocked (async->commit_queue, event); - if (g_async_queue_length_unlocked (async->commit_queue) == 1) - pgm_notify_send (&async->commit_notify); - g_async_queue_unlock (async->commit_queue); - break; - } - - case PGM_IO_STATUS_TIMER_PENDING: - { - pgm_transport_get_timer_pending (async->transport, &tv); - goto block; - } - - case PGM_IO_STATUS_RATE_LIMITED: - { - pgm_transport_get_rate_remaining (async->transport, &tv); - } -/* fall through */ - case PGM_IO_STATUS_WOULD_BLOCK: -block: - { -#ifdef CONFIG_HAVE_POLL - const int timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); - int n_fds = 3; - struct pollfd fds[1+n_fds]; - memset (fds, 0, sizeof(fds)); - fds[0].fd = pgm_notify_get_fd (&async->destroy_notify); - fds[0].events = POLLIN; - if (-1 == pgm_transport_poll_info (async->transport, &fds[1], &n_fds, POLLIN)) { - g_trace ("poll_info returned errno=%i",errno); - goto cleanup; - } - const int ready = poll (fds, 1 + n_fds, timeout); -#else /* HAVE_SELECT */ - fd_set readfds; - int fd = pgm_notify_get_fd (&async->destroy_notify), n_fds = 1 + fd; - FD_ZERO(&readfds); - FD_SET(fd, &readfds); - if (-1 == pgm_transport_select_info (async->transport, &readfds, NULL, &n_fds)) { - g_trace ("select_info returned errno=%i",errno); - goto cleanup; - } - const int ready = select (n_fds, &readfds, NULL, NULL, PGM_IO_STATUS_RATE_LIMITED == status ? &tv : NULL); -#endif - if (-1 == ready) { - g_trace ("block returned errno=%i",errno); - goto cleanup; - } -#ifdef CONFIG_HAVE_POLL - if (ready > 0 && fds[0].revents) -#else - if (ready > 0 && FD_ISSET(fd, &readfds)) -#endif - goto cleanup; - break; - } - - case PGM_IO_STATUS_ERROR: - case PGM_IO_STATUS_EOF: - goto cleanup; - - case PGM_IO_STATUS_RESET: - if (async->transport->is_abort_on_reset) - goto cleanup; - break; - -/* TODO: report to user */ - case PGM_IO_STATUS_FIN: - break; - - default: - g_assert_not_reached(); - } - } while (!async->is_destroyed); - -cleanup: - g_async_queue_unref (async->commit_queue); - return NULL; -} - -/* create asynchronous thread handler - * - * on success, 0 is returned. on error, -1 is returned, and errno set appropriately. - * on invalid parameters, -EINVAL is returned. - */ - -gboolean -pgm_async_create ( - pgm_async_t** async, - pgm_transport_t* const transport, - GError** error - ) -{ - pgm_async_t* new_async; - - g_return_val_if_fail (NULL != async, FALSE); - g_return_val_if_fail (NULL != transport, FALSE); - - g_trace ("create (async:%p transport:%p error:%p)", - (gpointer)async, (gpointer)transport, (gpointer)error); - - if (!g_thread_supported()) - g_thread_init (NULL); - - new_async = g_new0 (pgm_async_t, 1); - new_async->transport = transport; - if (0 != pgm_notify_init (&new_async->commit_notify) || - 0 != pgm_notify_init (&new_async->destroy_notify)) - { - g_set_error (error, - PGM_ASYNC_ERROR, - pgm_async_error_from_errno (errno), - _("Creating async notification channels: %s"), - g_strerror (errno)); - g_free (new_async); - return FALSE; - } - new_async->commit_queue = g_async_queue_new(); -/* setup new thread */ - new_async->thread = g_thread_create_full (pgm_receiver_thread, - new_async, - 0, - TRUE, - TRUE, - G_THREAD_PRIORITY_HIGH, - error); - if (NULL == new_async->thread) { - g_async_queue_unref (new_async->commit_queue); - pgm_notify_destroy (&new_async->commit_notify); - g_free (new_async); - return FALSE; - } - -/* return new object */ - *async = new_async; - return TRUE; -} - -/* tell async thread to stop, wait for it to stop, then cleanup. - * - * on success, 0 is returned. if async is invalid, -EINVAL is returned. - */ - -gboolean -pgm_async_destroy ( - pgm_async_t* const async - ) -{ - g_return_val_if_fail (NULL != async, FALSE); - g_return_val_if_fail (!async->is_destroyed, FALSE); - - async->is_destroyed = TRUE; - pgm_notify_send (&async->destroy_notify); - if (async->thread) - g_thread_join (async->thread); - if (async->commit_queue) { - g_async_queue_unref (async->commit_queue); - async->commit_queue = NULL; - } - pgm_notify_destroy (&async->destroy_notify); - pgm_notify_destroy (&async->commit_notify); - g_free (async); - return TRUE; -} - -/* queue to GSource and GMainLoop */ - -GSource* -pgm_async_create_watch ( - pgm_async_t* async - ) -{ - g_return_val_if_fail (async != NULL, NULL); - - GSource *source = g_source_new (&g_pgm_watch_funcs, sizeof(pgm_watch_t)); - pgm_watch_t *watch = (pgm_watch_t*)source; - - watch->async = async; - watch->pollfd.fd = pgm_async_get_fd (async); - watch->pollfd.events = G_IO_IN; - - g_source_add_poll (source, &watch->pollfd); - - return source; -} - -/* pgm transport attaches to the callees context: the default context instead of - * any internal contexts. - */ - -int -pgm_async_add_watch_full ( - pgm_async_t* async, - gint priority, - pgm_eventfn_t function, - gpointer user_data, - GDestroyNotify notify - ) -{ - g_return_val_if_fail (async != NULL, -EINVAL); - g_return_val_if_fail (function != NULL, -EINVAL); - - GSource* source = pgm_async_create_watch (async); - - if (priority != G_PRIORITY_DEFAULT) - g_source_set_priority (source, priority); - - g_source_set_callback (source, (GSourceFunc)function, user_data, notify); - - guint id = g_source_attach (source, NULL); - g_source_unref (source); - - return id; -} - -int -pgm_async_add_watch ( - pgm_async_t* async, - pgm_eventfn_t function, - gpointer user_data - ) -{ - return pgm_async_add_watch_full (async, G_PRIORITY_HIGH, function, user_data, NULL); -} - -/* returns TRUE if source has data ready, i.e. async queue is not empty - * - * called before event loop poll() - */ - -static -gboolean -pgm_src_prepare ( - GSource* source, - gint* timeout - ) -{ - pgm_watch_t* watch = (pgm_watch_t*)source; - -/* infinite timeout */ - *timeout = -1; - - return ( g_async_queue_length(watch->async->commit_queue) > 0 ); -} - -/* called after event loop poll() - * - * return TRUE if ready to dispatch. - */ - -static -gboolean -pgm_src_check ( - GSource* source - ) -{ - pgm_watch_t* watch = (pgm_watch_t*)source; - - return ( g_async_queue_length(watch->async->commit_queue) > 0 ); -} - -/* called when TRUE returned from prepare or check - */ - -static gboolean -pgm_src_dispatch ( - GSource* source, - GSourceFunc callback, - gpointer user_data - ) -{ - g_trace ("pgm_src_dispatch (source:%p callback:() user-data:%p)", - (gpointer)source, user_data); - - const pgm_eventfn_t function = (pgm_eventfn_t)callback; - pgm_watch_t* watch = (pgm_watch_t*)source; - pgm_async_t* async = watch->async; - -/* empty pipe */ - pgm_notify_read (&async->commit_notify); - -/* purge only one message from the asynchronous queue */ - pgm_event_t* event = g_async_queue_try_pop (async->commit_queue); - if (event) - { -/* important that callback occurs out of lock to allow PGM layer to add more messages */ - (*function) (event->data, event->len, user_data); - -/* return memory to receive window */ - if (event->len) g_free (event->data); - pgm_event_unref (async, event); - } - - return TRUE; -} - -/* synchronous reading from the queue. - * - * returns GIOStatus with success, error, again, or eof. - */ - -GIOStatus -pgm_async_recv ( - pgm_async_t* const async, - gpointer data, - const gsize len, - gsize* const bytes_read, - const int flags, /* MSG_DONTWAIT for non-blocking */ - GError** error - ) -{ - g_return_val_if_fail (NULL != async, G_IO_STATUS_ERROR); - if (len) g_return_val_if_fail (NULL != data, G_IO_STATUS_ERROR); - - g_trace ("pgm_async_recv (async:%p data:%p len:%" G_GSIZE_FORMAT" bytes-read:%p flags:%d error:%p)", - (gpointer)async, data, len, (gpointer)bytes_read, flags, (gpointer)error); - - pgm_event_t* event = NULL; - g_async_queue_lock (async->commit_queue); - if (g_async_queue_length_unlocked (async->commit_queue) == 0) - { - g_async_queue_unlock (async->commit_queue); - if (flags & MSG_DONTWAIT || async->is_nonblocking) - return G_IO_STATUS_AGAIN; -#ifdef CONFIG_HAVE_POLL - struct pollfd fds[1]; - int ready; - do { - memset (fds, 0, sizeof(fds)); - fds[0].fd = pgm_notify_get_fd (&async->commit_notify); - fds[0].events = POLLIN; - ready = poll (fds, G_N_ELEMENTS(fds), -1); - if (-1 == ready || async->is_destroyed) /* errno = EINTR */ - return G_IO_STATUS_ERROR; - } while (ready <= 0); -#else - fd_set readfds; - int n_fds, ready, fd = pgm_notify_get_fd (&async->commit_notify); - do { - FD_ZERO(&readfds); - FD_SET(fd, &readfds); - n_fds = fd + 1; - ready = select (n_fds, &readfds, NULL, NULL, NULL); - if (-1 == ready || async->is_destroyed) /* errno = EINTR */ - return G_IO_STATUS_ERROR; - } while (ready <= 0); -#endif - pgm_notify_read (&async->commit_notify); - g_async_queue_lock (async->commit_queue); - } - event = g_async_queue_pop_unlocked (async->commit_queue); - g_async_queue_unlock (async->commit_queue); - -/* pass data back to callee */ - if (event->len > len) { - *bytes_read = len; - memcpy (data, event->data, *bytes_read); - g_set_error (error, - PGM_ASYNC_ERROR, - PGM_ASYNC_ERROR_OVERFLOW, - _("Message too large to be stored in buffer.")); - pgm_event_unref (async, event); - return G_IO_STATUS_ERROR; - } - - if (bytes_read) - *bytes_read = event->len; - memcpy (data, event->data, event->len); - -/* cleanup */ - if (event->len) g_free (event->data); - pgm_event_unref (async, event); - return G_IO_STATUS_NORMAL; -} - -gboolean -pgm_async_set_nonblocking ( - pgm_async_t* const async, - const gboolean nonblocking - ) -{ - g_return_val_if_fail (NULL != async, FALSE); - async->is_nonblocking = nonblocking; - return TRUE; -} - -GQuark -pgm_async_error_quark (void) -{ - return g_quark_from_static_string ("pgm-async-error-quark"); -} - -static -PGMAsyncError -pgm_async_error_from_errno ( - const gint err_no - ) -{ - switch (err_no) { -#ifdef EFAULT - case EFAULT: - return PGM_ASYNC_ERROR_FAULT; - break; -#endif - -#ifdef EMFILE - case EMFILE: - return PGM_ASYNC_ERROR_MFILE; - break; -#endif - -#ifdef ENFILE - case ENFILE: - return PGM_ASYNC_ERROR_NFILE; - break; -#endif - - default : - return PGM_ASYNC_ERROR_FAILED; - break; - } -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/async.h b/3rdparty/openpgm-svn-r1085/pgm/test/async.h deleted file mode 100644 index c2b91a7..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/async.h +++ /dev/null @@ -1,76 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * Asynchronous receive thread helper - * - * Copyright (c) 2006-2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_ASYNC_H__ -#define __PGM_ASYNC_H__ - -#include -#include -#include - - -#define PGM_ASYNC_ERROR pgm_async_error_quark () - -typedef enum -{ - /* Derived from errno */ - PGM_ASYNC_ERROR_FAULT, - PGM_ASYNC_ERROR_MFILE, - PGM_ASYNC_ERROR_NFILE, - PGM_ASYNC_ERROR_OVERFLOW, - PGM_ASYNC_ERROR_FAILED -} PGMAsyncError; - -typedef struct pgm_async_t pgm_async_t; - -struct pgm_async_t { - pgm_transport_t* transport; - GThread* thread; - GAsyncQueue* commit_queue; - pgm_notify_t commit_notify; - pgm_notify_t destroy_notify; - gboolean is_destroyed; - gboolean is_nonblocking; -}; - -typedef int (*pgm_eventfn_t)(gpointer, guint, gpointer); - - -G_BEGIN_DECLS - -int pgm_async_create (pgm_async_t**, pgm_transport_t* const, GError**); -int pgm_async_destroy (pgm_async_t* const); -GIOStatus pgm_async_recv (pgm_async_t* const, gpointer, const gsize, gsize* const, const int, GError**); -gboolean pgm_async_set_nonblocking (pgm_async_t* const, const gboolean); -GSource* pgm_async_create_watch (pgm_async_t* const) G_GNUC_WARN_UNUSED_RESULT; -int pgm_async_add_watch_full (pgm_async_t*, gint, pgm_eventfn_t, gpointer, GDestroyNotify); -int pgm_async_add_watch (pgm_async_t*, pgm_eventfn_t, gpointer); -GQuark pgm_async_error_quark (void); - -static inline int pgm_async_get_fd (pgm_async_t* async) -{ - g_return_val_if_fail (async != NULL, -EINVAL); - return pgm_notify_get_fd (&async->commit_notify); -} - -G_END_DECLS - -#endif /* __PGM_ASYNC_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/dump-json.c b/3rdparty/openpgm-svn-r1085/pgm/test/dump-json.c deleted file mode 100644 index 70a7442..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/dump-json.c +++ /dev/null @@ -1,1292 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * JSON packet dump. - * - * Copyright (c) 2006-2008 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include "dump-json.h" - - -/* globals */ - -#define OPTIONS_TOTAL_LEN(x) *(guint16*)( ((char*)(x)) + sizeof(guint16) ) - - -int verify_ip_header (struct pgm_ip*, guint); -void print_ip_header (struct pgm_ip*); -int verify_pgm_header (struct pgm_header*, guint); -void print_pgm_header (struct pgm_header*); -int verify_spm (struct pgm_header*, char*, guint); -void print_spm (struct pgm_header*, char*); -int verify_poll (struct pgm_header*, char*, guint); -void print_poll (struct pgm_header*, char*); -int verify_polr (struct pgm_header*, char*, guint); -void print_polr (struct pgm_header*, char*); -int verify_odata (struct pgm_header*, char*, guint); -void print_odata (struct pgm_header*, char*); -int verify_rdata (struct pgm_header*, char*, guint); -void print_rdata (struct pgm_header*, char*); -static int generic_verify_nak (const char*, struct pgm_header*, char*, guint); -static void generic_print_nak (const char*, struct pgm_header*, char*); -int verify_nak (struct pgm_header*, char*, guint); -void print_nak (struct pgm_header*, char*); -int verify_nnak (struct pgm_header*, char*, guint); -void print_nnak (struct pgm_header*, char*); -int verify_ncf (struct pgm_header*, char*, guint); -void print_ncf (struct pgm_header*, char*); -int verify_spmr (struct pgm_header*, char*, guint); -void print_spmr (struct pgm_header*, char*); -int verify_options (char*, guint); -void print_options (char*); - - -int -monitor_packet ( - char* data, - guint len - ) -{ - static int count = 0; - - puts ("{"); - printf ("\t\"id\": %i,\n", ++count); - - int retval = 0; - - struct pgm_ip* ip = (struct pgm_ip*)data; - if (verify_ip_header (ip, len) < 0) { - puts ("\t\"valid\": false"); - retval = -1; - goto out; - } - - struct pgm_header* pgm = (struct pgm_header*)(data + (ip->ip_hl * 4)); - guint pgm_len = len - (ip->ip_hl * 4); - if (verify_pgm_header (pgm, pgm_len) < 0) { - puts ("\t\"valid\": false"); - retval = -1; - goto out; - } - - char* pgm_data = (char*)(pgm + 1); - guint pgm_data_len = pgm_len - sizeof(struct pgm_header); - switch (pgm->pgm_type) { - case PGM_SPM: retval = verify_spm (pgm, pgm_data, pgm_data_len); break; - case PGM_POLL: retval = verify_poll (pgm, pgm_data, pgm_data_len); break; - case PGM_POLR: retval = verify_polr (pgm, pgm_data, pgm_data_len); break; - case PGM_ODATA: retval = verify_odata (pgm, pgm_data, pgm_data_len); break; - case PGM_RDATA: retval = verify_rdata (pgm, pgm_data, pgm_data_len); break; - case PGM_NAK: retval = verify_nak (pgm, pgm_data, pgm_data_len); break; - case PGM_NNAK: retval = verify_nnak (pgm, pgm_data, pgm_data_len); break; - case PGM_NCF: retval = verify_ncf (pgm, pgm_data, pgm_data_len); break; - case PGM_SPMR: retval = verify_spmr (pgm, pgm_data, pgm_data_len); break; - } - - if (retval < 0) { - puts ("\t\"valid\": false"); - goto out; - } - -/* packet verified correct */ - puts ("\t\"valid\": true,"); - - print_ip_header (ip); - print_pgm_header (pgm); - - switch (pgm->pgm_type) { - case PGM_SPM: print_spm (pgm, pgm_data); break; - case PGM_POLL: print_poll (pgm, pgm_data); break; - case PGM_POLR: print_polr (pgm, pgm_data); break; - case PGM_ODATA: print_odata (pgm, pgm_data); break; - case PGM_RDATA: print_rdata (pgm, pgm_data); break; - case PGM_NAK: print_nak (pgm, pgm_data); break; - case PGM_NNAK: print_nnak (pgm, pgm_data); break; - case PGM_NCF: print_ncf (pgm, pgm_data); break; - case PGM_SPMR: print_spmr (pgm, pgm_data); break; - } - -out: - puts ("}"); - return retval; -} - - -int -verify_ip_header ( - struct pgm_ip* ip, - guint len - ) -{ -/* minimum size should be IP header plus PGM header */ - if (len < (sizeof(struct pgm_ip) + sizeof(struct pgm_header))) - { - printf ("\t\"message\": \"IP: packet size too small: %i bytes, expecting at least %" G_GSIZE_FORMAT " bytes.\",\n", len, sizeof(struct pgm_header)); - return -1; - } - -/* IP packet header: IPv4 - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * |Version| HL | ToS | Length | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Fragment ID |R|D|M| Fragment Offset | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | TTL | Protocol | Checksum | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Source IP Address | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Destination IP Address | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | IP Options when present ... - * +-+-+-+-+-+-+-+-+-+-+-+-+ ... - * | Data ... - * +-+-+- ... - * - * IPv6: n/a - */ - -/* decode IP header */ - if (ip->ip_v != 4 && ip->ip_v != 6) { /* IP version, 4 or 6 */ - printf ("\t\"message\": \"IP: unknown IP version %i.\",\n", ip->ip_v); - return -1; - } - - guint ip_header_length = ip->ip_hl * 4; /* IP header length in 32bit octets */ - if (ip_header_length < sizeof(struct ip)) { - printf ("\t\"message\": \"IP: bad IP header length %i, should be at least %" G_GSIZE_FORMAT "lu bytes.\",\n", ip_header_length, sizeof(struct ip)); - return -1; - } - -/* ip_len can equal packet_length - ip_header_length in FreeBSD/NetBSD - * Stevens/Fenner/Rudolph, Unix Network Programming Vol.1, p.739 - * - * RFC3828 allows partial packets such that len < packet_length with UDP lite - */ - guint packet_length = g_ntohs(ip->ip_len); /* total packet length */ - if (len < packet_length) { /* redundant: often handled in kernel */ - printf ("\t\"message\": \"IP: truncated IP packet: header reports %i actual length %i bytes.\",\n", (int)len, (int)packet_length); - return -1; - } - -/* TCP Segmentation Offload (TSO) might have zero length here */ - if (packet_length < ip_header_length) { - printf ("\t\"message\": \"IP: header reports %i less than IP header length %i.\",\n", (int)packet_length, (int)ip_header_length); - return -1; - } - -/* packets that fail checksum will generally not be passed upstream except with rfc3828 - */ - int sum = pgm_inet_checksum((char*)ip, ip_header_length, 0); - if (sum != 0) { - int ip_sum = g_ntohs(ip->ip_sum); - printf ("\t\"message\": \"IP: IP header checksum incorrect: 0x%x.\",\n", ip_sum); - return -2; - } - - if (ip->ip_p != IPPROTO_PGM) { - printf ("\t\"message\": \"IP: packet IP protocol not PGM: %i.\",\n", ip->ip_p); - return -1; - } - -/* fragmentation offset, bit 0: 0, bit 1: do-not-fragment, bit 2: more-fragments */ - int offset = g_ntohs(ip->ip_off); - if ((offset & 0x1fff) != 0) { - printf ("\t\"message\": \"IP: fragmented IP packet, ignoring.\",\n"); - return -1; - } - - return 0; -} - -void -print_ip_header ( - struct pgm_ip* ip - ) -{ - puts ("\t\"IP\": {"); - printf ("\t\t\"version\": %i,\n", - ip->ip_v - ); - printf ("\t\t\"headerLength\": %i,\n", - ip->ip_hl - ); - printf ("\t\t\"ToS\": %i,\n", - ip->ip_tos & 0x3 - ); - printf ("\t\t\"length\": %i,\n", - g_ntohs(ip->ip_len) - ); - printf ("\t\t\"fragmentId\": %i,\n", - g_ntohs(ip->ip_id) - ); - printf ("\t\t\"DF\": %s,\n", - (g_ntohs(ip->ip_off) & 0x4000) ? "true" : "false" - ); - printf ("\t\t\"MF\": %s,\n", - (g_ntohs(ip->ip_off) & 0x2000) ? "true" : "false" - ); - printf ("\t\t\"fragmentOffset\": %i,\n", - g_ntohs(ip->ip_off) & 0x1fff - ); - printf ("\t\t\"TTL\": %i,\n", - ip->ip_ttl - ); - printf ("\t\t\"protocol\": %i,\n", - ip->ip_p - ); - printf ("\t\t\"sourceIp\": \"%s\",\n", - inet_ntoa(*(struct in_addr*)&ip->ip_src) - ); - printf ("\t\t\"destinationIp\": \"%s\",\n", - inet_ntoa(*(struct in_addr*)&ip->ip_dst) - ); - puts ("\t\t\"IpOptions\": {"); - puts ("\t\t}"); - puts ("\t},"); -} - -int -verify_pgm_header ( - struct pgm_header* pgm, - guint pgm_len - ) -{ - -/* PGM payload, header looks as follows: - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Source Port | Destination Port | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Type | Options | Checksum | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Global Source ID ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | ... Global Source ID | TSDU Length | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Type specific data ... - * +-+-+-+-+-+-+-+-+-+- ... - */ - if (pgm_len < sizeof(pgm)) { - printf ("\t\"message\": \"PGM: packet size less than PGM header: %i bytes.\",\n", pgm_len); - return -1; - } - - if (pgm->pgm_checksum) - { - int sum = pgm->pgm_checksum; - pgm->pgm_checksum = 0; - int pgm_sum = pgm_csum_fold (pgm_csum_partial ((const char*)pgm, pgm_len, 0)); - pgm->pgm_checksum = sum; - if (pgm_sum != sum) { - printf ("\t\"message\": \"PGM: PGM packet checksum incorrect, packet 0x%x calculated 0x%x.\",\n", sum, pgm_sum); - return -2; - } - } else { - if (pgm->pgm_type != PGM_ODATA && pgm->pgm_type != PGM_RDATA) { - printf ("\t\"message\": \"PGM: No PGM checksum value, mandatory for ODATA/RDATA.\",\n"); - return -1; - } - } - - if ( pgm->pgm_type != PGM_SPM && - pgm->pgm_type != PGM_POLL && - pgm->pgm_type != PGM_POLR && - pgm->pgm_type != PGM_ODATA && - pgm->pgm_type != PGM_RDATA && - pgm->pgm_type != PGM_NAK && - pgm->pgm_type != PGM_NNAK && - pgm->pgm_type != PGM_NCF && - pgm->pgm_type != PGM_SPMR ) - { - printf ("\t\"message\": \"PGM: Not a valid PGM packet type: %i.\",\n", pgm->pgm_type); - return -1; - } - - return 0; -} - -/* note: output trails tsdu length line to allow for comma - */ - -void -print_pgm_header ( - struct pgm_header* pgm - ) -{ - puts ("\t\"PGM\": {"); - printf ("\t\t\"sourcePort\": %i,\n", g_ntohs(pgm->pgm_sport)); - printf ("\t\t\"destinationPort\": %i,\n", g_ntohs(pgm->pgm_dport)); - printf ("\t\t\"type\": \"%s\",\n", pgm_type_string(pgm->pgm_type & 0xf)); - printf ("\t\t\"version\": %i,\n", (pgm->pgm_type & 0xc0) >> 6); - puts ("\t\t\"options\": {"); - printf ("\t\t\t\"networkSignificant\": %s,\n", (pgm->pgm_options & PGM_OPT_NETWORK) ? "true" : "false"); - printf ("\t\t\t\"parityPacket\": %s,\n", (pgm->pgm_options & PGM_OPT_PARITY) ? "true" : "false"); - printf ("\t\t\t\"variableLength\": %s\n", (pgm->pgm_options & PGM_OPT_VAR_PKTLEN) ? "true" : "false"); - puts ("\t\t},"); - printf ("\t\t\"checksum\": %i,\n", pgm->pgm_checksum); - printf ("\t\t\"gsi\": \"%i.%i.%i.%i.%i.%i\",\n", - pgm->pgm_gsi[0], - pgm->pgm_gsi[1], - pgm->pgm_gsi[2], - pgm->pgm_gsi[3], - pgm->pgm_gsi[4], - pgm->pgm_gsi[5]); - printf ("\t\t\"tsduLength\": %i", g_ntohs(pgm->pgm_tsdu_length)); -} - -/* 8.1. Source Path Messages (SPM) - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | SPM's Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Trailing Edge Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Leading Edge Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | NLA AFI | Reserved | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Path NLA ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ - * | Option Extensions when present ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * - * NLA = Network Layer Address - * NLA AFI = NLA Address Family Indicator: rfc 1700 (ADDRESS FAMILY NUMBERS) - * => Path NLA = IP address of last network element - */ - -int -verify_spm ( - struct pgm_header* header, - char* data, - guint len - ) -{ - int retval = 0; - -/* truncated packet */ - if (len < sizeof(struct pgm_spm)) { - printf ("\t\"message\": \"SPM: packet length: %i less than minimum SPM length: %" G_GSIZE_FORMAT "lu bytes.\",\n", len, sizeof(struct pgm_spm)); - retval = -1; - goto out; - } - - struct pgm_spm* spm = (struct pgm_spm*)data; - char* opt_offset = (char*)(spm + 1); - guint opt_len = len - sizeof(spm); - - switch (g_ntohs(spm->spm_nla_afi)) { - case AFI_IP6: - if (len < sizeof(struct pgm_spm6)) { - printf ("\t\"message\": \"SPM: packet length: %i less than minimum IPv6 SPM length: %" G_GSIZE_FORMAT "lu bytes.\",\n", len, sizeof(struct pgm_spm6)); - retval = -1; - goto out; - } - opt_offset += sizeof(struct pgm_spm6) - sizeof(struct pgm_spm); - opt_len -= sizeof(struct pgm_spm6) - sizeof(struct pgm_spm); - - case AFI_IP: - break; - - default: - printf ("\t\"message\": \"SPM: invalid AFI of source NLA: %i.\",\n", g_ntohs(spm->spm_nla_afi)); - retval = -1; - goto out; - } - -/* option extensions */ - if (header->pgm_options & PGM_OPT_PRESENT) - { - retval = verify_options (opt_offset, opt_len); - } - -out: - return retval; -} - -void -print_spm ( - struct pgm_header* header, - char* data - ) -{ - struct pgm_spm* spm = (struct pgm_spm*)data; - struct pgm_spm6* spm6 = (struct pgm_spm6*)data; - char* opt_offset = (char*)(spm + 1); - - puts (","); - printf ("\t\t\"spmSqn\": %i,\n", g_ntohl(spm->spm_sqn)); - printf ("\t\t\"spmTrail\": %i,\n", g_ntohl(spm->spm_trail)); - printf ("\t\t\"spmLead\": %i,\n", g_ntohl(spm->spm_lead)); - printf ("\t\t\"spmNlaAfi\": %i,\n", g_ntohs (spm->spm_nla_afi)); - - char s[INET6_ADDRSTRLEN]; - switch (g_ntohs(spm->spm_nla_afi)) { - case AFI_IP: - inet_ntop ( AF_INET, &spm->spm_nla, s, sizeof (s) ); - break; - - case AFI_IP6: - inet_ntop ( AF_INET6, &spm6->spm6_nla, s, sizeof (s) ); - opt_offset += sizeof(struct pgm_spm6) - sizeof(struct pgm_spm); - break; - } - - printf ("\t\t\"spmNla\": \"%s\"", s); - -/* option extensions */ - if (header->pgm_options & PGM_OPT_PRESENT) - { - puts (","); - print_options (opt_offset); - } - - puts ("\n\t}"); -} - -/* 14.7.1. Poll Request - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | POLL's Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | POLL's Round | POLL's Sub-type | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | NLA AFI | Reserved | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Path NLA ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ - * | POLL's Back-off Interval | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Random String | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Matching Bit-Mask | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Option Extensions when present ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * - * Sent to ODATA multicast group with IP Router Alert option. - */ - -#define PGM_MIN_POLL_SIZE ( sizeof(struct pgm_poll) ) - -int -verify_poll ( - G_GNUC_UNUSED struct pgm_header* header, - G_GNUC_UNUSED char* data, - G_GNUC_UNUSED guint len - ) -{ - return -1; -} - -void -print_poll ( - G_GNUC_UNUSED struct pgm_header* header, - G_GNUC_UNUSED char* data - ) -{ -} - -/* 14.7.2. Poll Response - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | POLR's Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | POLR's Round | reserved | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Option Extensions when present ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - -int -verify_polr ( - G_GNUC_UNUSED struct pgm_header* header, - G_GNUC_UNUSED char* data, - G_GNUC_UNUSED guint len - ) -{ - return -1; -} - -void -print_polr ( - G_GNUC_UNUSED struct pgm_header* header, - G_GNUC_UNUSED char* data - ) -{ -} - -/* 8.2. Data Packet - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Data Packet Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Trailing Edge Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Option Extensions when present ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Data ... - * +-+-+- ... - */ - -int -verify_odata ( - struct pgm_header* header, - char* data, - guint len - ) -{ - int retval = 0; - - if (len < sizeof(struct pgm_data)) { - printf ("\t\"message\": \"ODATA: packet length: %i less than minimum ODATA length: %" G_GSIZE_FORMAT " bytes.\",\n", len, sizeof(struct pgm_data)); - retval = -1; - goto out; - } - - char* tsdu = data + sizeof(struct pgm_data); - guint tsdu_len = len - sizeof(struct pgm_data); - if (header->pgm_options & PGM_OPT_PRESENT) - { - retval = verify_options (tsdu, tsdu_len); - - guint opt_total_len = g_ntohs( OPTIONS_TOTAL_LEN(tsdu) ); - tsdu += opt_total_len; - tsdu_len -= opt_total_len; - } - - if (!retval && g_ntohs(header->pgm_tsdu_length) != tsdu_len) { - printf ("\t\"message\": \"ODATA: TSDU truncated expected %i, found %i bytes.\",\n", g_ntohs(header->pgm_tsdu_length), tsdu_len); - retval = -1; - } -out: - return retval; -} - -void -print_odata ( - struct pgm_header* header, - char* data - ) -{ - struct pgm_data* odata = (struct pgm_data*)data; - char* tsdu = data + sizeof(struct pgm_data); - - puts (","); - printf ("\t\t\"odSqn\": %lu,\n", (gulong)g_ntohl(odata->data_sqn)); - printf ("\t\t\"odTrail\": %lu,\n", (gulong)g_ntohl(odata->data_trail)); - -/* option extensions */ - if (header->pgm_options & PGM_OPT_PRESENT) - { - print_options (tsdu); - tsdu += g_ntohs( OPTIONS_TOTAL_LEN(tsdu) ); - puts (","); - } - -/* data */ - printf ("\t\t\"data\": \""); - char* end = tsdu + g_ntohs (header->pgm_tsdu_length); - while (tsdu < end) { - if (isprint(*tsdu)) - putchar(*tsdu); - else - putchar('.'); - tsdu++; - } - - puts ("\""); - puts ("\t}"); -} - -/* 8.2. Repair Data - */ - -int -verify_rdata ( - struct pgm_header* header, - char* data, - guint len - ) -{ - int retval = 0; - - if (len < sizeof(struct pgm_data)) { - printf ("\t\"message\": \"RDATA: packet length: %i less than minimum RDATA length: %" G_GSIZE_FORMAT " bytes.\",\n", len, sizeof(struct pgm_data)); - retval = -1; - goto out; - } - - char* tsdu = data + sizeof(struct pgm_data); - guint tsdu_len = len - sizeof(struct pgm_data); - if (header->pgm_options & PGM_OPT_PRESENT) - { - retval = verify_options (tsdu, tsdu_len); - - guint opt_total_len = g_ntohs( OPTIONS_TOTAL_LEN(tsdu) ); - tsdu += opt_total_len; - tsdu_len -= opt_total_len; - } - - if (!retval && g_ntohs(header->pgm_tsdu_length) != tsdu_len) { - printf ("\t\"message\": \"RDATA: tsdu truncated expected %i, found %i bytes.\",\n", g_ntohs(header->pgm_tsdu_length), tsdu_len); - retval = -1; - } -out: - return retval; -} - -void -print_rdata ( - struct pgm_header* header, - char* data - ) -{ - struct pgm_data* rdata = (struct pgm_data*)data; - char* tsdu = data + sizeof(struct pgm_data); - - puts (","); - printf ("\t\t\"rdSqn\": %lu,\n", (gulong)g_ntohl(rdata->data_sqn)); - printf ("\t\t\"rdTrail\": %lu,\n", (gulong)g_ntohl(rdata->data_trail)); - -/* option extensions */ - if (header->pgm_options & PGM_OPT_PRESENT) - { - print_options (tsdu); - tsdu += g_ntohs( OPTIONS_TOTAL_LEN(tsdu) ); - puts (","); - } - -/* data */ - printf ("\t\t\"data\": \""); - char* end = tsdu + g_ntohs (header->pgm_tsdu_length); - while (tsdu < end) { - if (isprint(*tsdu)) - putchar(*tsdu); - else - putchar('.'); - tsdu++; - } - - puts ("\""); - puts ("\t}"); -} - -/* 8.3. NAK - * - * Technically the AFI of the source and multicast group can be different - * but that would be very wibbly wobbly. One example is using a local DLR - * with a IPv4 address to reduce NAK cost for recovery on wide IPv6 - * distribution. - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Requested Sequence Number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | NLA AFI | Reserved | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Source NLA ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ - * | NLA AFI | Reserved | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Multicast Group NLA ... | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ - * | Option Extensions when present ... - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... - */ - -int -verify_nak ( - struct pgm_header* header, - char* data, - guint len - ) -{ - return generic_verify_nak ("NAK", header, data, len); -} - -int -verify_ncf ( - struct pgm_header* header, - char* data, - guint len - ) -{ - return generic_verify_nak ("NCF", header, data, len); -} - -int -verify_nnak ( - struct pgm_header* header, - char* data, - guint len - ) -{ - return generic_verify_nak ("NNAK", header, data, len); -} - -static int -generic_verify_nak ( - const char* name, /* upper case */ - G_GNUC_UNUSED struct pgm_header* header, - char* data, - guint len - ) -{ - int retval = 0; - -/* truncated packet */ - if (len < sizeof(struct pgm_nak)) { - printf ("\t\"message\": \"%s: packet length: %i less than minimum %s length: %" G_GSIZE_FORMAT " bytes.\",\n", - name, len, name, sizeof(struct pgm_nak)); - retval = -1; - goto out; - } - - struct pgm_nak* nak = (struct pgm_nak*)data; - int nak_src_nla_afi = g_ntohs (nak->nak_src_nla_afi); - int nak_grp_nla_afi = -1; - -/* check source NLA: unicast address of the ODATA sender */ - switch (nak_src_nla_afi) { - case AFI_IP: - nak_grp_nla_afi = g_ntohs (nak->nak_grp_nla_afi); - break; - - case AFI_IP6: - nak_grp_nla_afi = g_ntohs (((struct pgm_nak6*)nak)->nak6_grp_nla_afi); - break; - - default: - printf ("\t\"message\": \"%s: invalid AFI of source NLA: %i.\",\n", - name, nak_src_nla_afi); - retval = -1; - goto out; - } - -/* check multicast group NLA */ - switch (nak_grp_nla_afi) { - case AFI_IP6: - switch (nak_src_nla_afi) { -/* IPv4 + IPv6 NLA */ - case AFI_IP: - if (len < ( sizeof(struct pgm_nak) + sizeof(struct in6_addr) - sizeof(struct in_addr) )) { - printf ("\t\"message\": \"%s: packet length: %i less than joint IPv4/6 %s length: %" G_GSIZE_FORMAT " bytes.\",\n", - name, len, name, ( sizeof(struct pgm_nak) + sizeof(struct in6_addr) - sizeof(struct in_addr) )); - retval = -1; - } - break; - -/* IPv6 + IPv6 NLA */ - case AFI_IP6: - if (len < sizeof(struct pgm_nak6)) { - printf ("\t\"message\": \"%s: packet length: %i less than IPv6 %s length: %" G_GSIZE_FORMAT " bytes.\",\n", - name, len, name, sizeof(struct pgm_nak6)); - retval = -1; - } - break; - } - break; - - case AFI_IP: - if (nak_src_nla_afi == AFI_IP6) { - if (len < ( sizeof(struct pgm_nak) + sizeof(struct in6_addr) - sizeof(struct in_addr) )) { - printf ("\t\"message\": \"%s: packet length: %i less than joint IPv6/4 %s length: %" G_GSIZE_FORMAT " bytes.\",\n", - name, len, name, ( sizeof(struct pgm_nak) + sizeof(struct in6_addr) - sizeof(struct in_addr) )); - retval = -1; - } - } - break; - - default: - printf ("\t\"message\": \"%s: invalid AFI of group NLA: %i.\",\n", - name, nak_grp_nla_afi); - retval = -1; - break; - } - -out: - return retval; -} - -void -print_nak ( - struct pgm_header* header, - char* data - ) -{ - generic_print_nak ("nak", header, data); -} - -void -print_ncf ( - struct pgm_header* header, - char* data - ) -{ - generic_print_nak ("ncf", header, data); -} - -void -print_nnak ( - struct pgm_header* header, - char* data - ) -{ - generic_print_nak ("nnak", header, data); -} - -static void -generic_print_nak ( - const char* name, /* lower case */ - struct pgm_header* header, - char* data - ) -{ - struct pgm_nak* nak = (struct pgm_nak*)data; - struct pgm_nak6* nak6 = (struct pgm_nak6*)data; - char* opt_offset = (char*)(nak + 1); - - puts (","); - printf ("\t\t\"%sSqn\": %lu,\n", name, (gulong)g_ntohl(nak->nak_sqn)); - - guint16 nak_src_nla_afi = g_ntohs (nak->nak_src_nla_afi); - guint16 nak_grp_nla_afi = 0; - char s[INET6_ADDRSTRLEN]; - - printf ("\t\t\"%sSourceNlaAfi\": %i,\n", name, nak_src_nla_afi); - -/* source nla */ - switch (nak_src_nla_afi) { - case AFI_IP: - inet_ntop ( AF_INET, &nak->nak_src_nla, s, sizeof(s) ); - nak_grp_nla_afi = g_ntohs (nak->nak_grp_nla_afi); - break; - - case AFI_IP6: - inet_ntop ( AF_INET6, &nak6->nak6_src_nla, s, sizeof(s) ); - nak_grp_nla_afi = g_ntohs (nak6->nak6_grp_nla_afi); - opt_offset += sizeof(struct in6_addr) - sizeof(struct in_addr); - break; - } - - printf ("\t\t\"%sSourceNla\": \"%s\",\n", name, s); - printf ("\t\t\"%sGroupNlaAfi\": %i,\n", name, nak_grp_nla_afi); - - switch (nak_grp_nla_afi) { - case AFI_IP6: - switch (nak_src_nla_afi) { -/* IPv4 + IPv6 NLA */ - case AFI_IP: - inet_ntop ( AF_INET6, &nak->nak_grp_nla, s, sizeof(s) ); - break; - -/* IPv6 + IPv6 NLA */ - case AFI_IP6: - inet_ntop ( AF_INET6, &nak6->nak6_grp_nla, s, sizeof(s) ); - break; - } - opt_offset += sizeof(struct in6_addr) - sizeof(struct in_addr); - break; - - case AFI_IP: - switch (nak_src_nla_afi) { -/* IPv4 + IPv4 NLA */ - case AFI_IP: - inet_ntop ( AF_INET, &nak->nak_grp_nla, s, sizeof(s) ); - break; - -/* IPv6 + IPv4 NLA */ - case AFI_IP6: - inet_ntop ( AF_INET, &nak6->nak6_grp_nla, s, sizeof(s) ); - break; - } - break; - } - - printf ("\t\t\"%sGroupNla\": \"%s\"", name, s); - -/* option extensions */ - if (header->pgm_options & PGM_OPT_PRESENT) - { - puts (","); - print_options (opt_offset); - } - - puts ("\n\t}"); -} - - -/* 13.6. SPM Request - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Option Extensions when present ... - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... - */ - -int -verify_spmr ( - struct pgm_header* header, - char* data, - guint len - ) -{ - int retval = 0; - - char* opt_offset = data; - guint opt_len = len; - -/* option extensions */ - if (header->pgm_options & PGM_OPT_PRESENT) - { - retval = verify_options (opt_offset, opt_len); - } - - return retval; -} - -void -print_spmr ( - struct pgm_header* header, - char* data - ) -{ - char* opt_offset = data; - -/* option extensions */ - if (header->pgm_options & PGM_OPT_PRESENT) - { - print_options (opt_offset); - puts (""); - } - - puts ("\t}"); -} - -/* Parse PGM options fields: - * - * assume at least two options, one the mandatory OPT_LENGTH - */ - -#define PGM_MIN_OPT_SIZE ( sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_length) ) - -int -verify_options ( - char* data, - guint len - ) -{ - int retval = 0; - - if (len < PGM_MIN_OPT_SIZE) { - printf ("\t\"message\": \"PGM options: packet size too small for options.\",\n"); - retval = -1; - goto out; - } - -/* OPT_LENGTH first */ - struct pgm_opt_length* opt_len = (struct pgm_opt_length*)data; - if ((opt_len->opt_type & PGM_OPT_MASK) != PGM_OPT_LENGTH) { - printf ("\t\"message\": \"PGM options: first option not OPT_LENGTH.\",\n"); - retval = -1; - goto out; - } - - if (opt_len->opt_length != sizeof(struct pgm_opt_length)) { - printf ("\t\"message\": \"PGM options: OPT_LENGTH incorrect option length: %i, expecting %" G_GSIZE_FORMAT " bytes.\",\n", opt_len->opt_length, sizeof(struct pgm_opt_length)); - retval = -1; - goto out; - } - - if (g_ntohs(opt_len->opt_total_length) < PGM_MIN_OPT_SIZE) { - printf ("\t\"message\": \"PGM options: OPT_LENGTH total length too short: %i bytes.\",\n", g_ntohs(opt_len->opt_total_length)); - retval = -1; - goto out; - } - - if (g_ntohs(opt_len->opt_total_length) > len) { - printf ("\t\"message\": \"PGM options: OPT_LENGTH total length longer than packet allows: %i bytes.\",\n", g_ntohs(opt_len->opt_total_length)); - retval = -1; - goto out; - } - -/* iterate through options (max 16) */ - guint count = 0; - guint total_length = g_ntohs(opt_len->opt_total_length); - - guint opt_counters[256]; - memset (&opt_counters, 0, sizeof(opt_counters)); - - struct pgm_opt_header* opt_header = (struct pgm_opt_header*)data; - for (;;) { - total_length -= opt_header->opt_length; - if (total_length < sizeof(struct pgm_opt_header)) { - printf ("\t\"message\": \"PGM options: option #%i shorter than minimum option size.\",\n", count + 1); - retval = -1; - goto out; - } - opt_header = (struct pgm_opt_header*)( ((char*)opt_header) + opt_header->opt_length ); - if (((int)total_length - (int)opt_header->opt_length) < 0) { - printf ("\t\"message\": \"PGM options: option #%i shorter than embedded size.\",\n", count + 1); - retval = -1; - goto out; - } - - if (opt_counters[opt_header->opt_type]++) { - printf ("\t\"message\": \"PGM options: duplicate option %i.\",\n", opt_header->opt_type); - retval = -1; - goto out; - } - -/* check option types */ - switch (opt_header->opt_type & PGM_OPT_MASK) { - case PGM_OPT_FRAGMENT: - { - if (opt_header->opt_length != sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_fragment)) { - printf ("\t\"message\": \"PGM options: OPT_FRAGMENT incorrect size: %i bytes.\",\n", opt_header->opt_length); - retval = -1; - goto out; - } - - struct pgm_opt_fragment* opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); - if (g_ntohl(opt_fragment->opt_frag_off) > g_ntohl(opt_fragment->opt_frag_len)) { - printf ("\t\"message\": \"PGM options: fragment offset longer than original packet.\",\n"); - retval = -1; - goto out; - } - break; - } - - case PGM_OPT_NAK_LIST: - { - guint list_len = opt_header->opt_length - sizeof(struct pgm_opt_header) - sizeof(guint8); - if (list_len & 1) { - printf ("\t\"message\": \"PGM options: OPT_NAK_LIST invalid odd length: %i bytes.\",\n", opt_header->opt_length); - retval = -1; - goto out; - } - - list_len /= 2; - if (list_len == 0) { - printf ("\t\"message\": \"PGM options: OPT_NAK_LIST empty.\",\n"); - retval = -1; - goto out; - } - - if (list_len > 62) { - printf ("\t\"message\": \"PGM options: OPT_NAK_LIST too long: %i sqns.\",\n", list_len); - retval = -1; - goto out; - } - break; - } - - case PGM_OPT_PARITY_PRM: - { - struct pgm_opt_parity_prm* opt_parity_prm = (struct pgm_opt_parity_prm*)(opt_header + 1); - if ((opt_parity_prm->opt_reserved & PGM_PARITY_PRM_MASK) == 0) { - printf ("\t\"message\": \"PGM options: neither pro-active or on-demand parity set in OPT_PARITY_PRM.\",\n"); - retval = -1; - goto out; - } - guint32 parity_prm_tgs = g_ntohl (opt_parity_prm->parity_prm_tgs); - if (parity_prm_tgs < 2 || parity_prm_tgs > 128) { - printf ("\t\"message\": \"PGM options: transmission group size out of bounds: %i.\",\n", parity_prm_tgs); - retval = -1; - goto out; - } - break; - } - - default: -/* unknown option, skip */ - break; - } -/* end option types */ - - if (opt_header->opt_type & PGM_OPT_END) { - break; - } - - if (count++ == 16) { - printf ("\t\"message\": \"PGM options: more than 16 options found.\",\n"); - retval = -1; - goto out; - } - } - -out: - return retval; -} - -static const char *opx_text[4] = { - "OPX_IGNORE", - "OPX_INVALIDATE", - "OPX_DISCARD", - "OPX_UNKNOWN" -}; - -void -print_options ( - char* data - ) -{ - struct pgm_opt_length* opt_len = (struct pgm_opt_length*)data; - - puts ("\t\t\"pgmOptions\": ["); - puts ("\t\t\t{"); - printf ("\t\t\t\t\"length\": 0x%x,\n", opt_len->opt_length); - puts ("\t\t\t\t\"type\": \"OPT_LENGTH\","); - printf ("\t\t\t\t\"totalLength\": %i\n", g_ntohs (opt_len->opt_total_length)); - printf ("\t\t\t}"); - -/* iterate through options */ - struct pgm_opt_header* opt_header = (struct pgm_opt_header*)data; - do { - opt_header = (struct pgm_opt_header*)( ((char*)opt_header) + opt_header->opt_length ); - - puts (","); - puts ("\t\t\t{"); - printf ("\t\t\t\t\"length\": 0x%x,\n", opt_header->opt_length); - - switch (opt_header->opt_type & PGM_OPT_MASK) { - case PGM_OPT_FRAGMENT: - { - struct pgm_opt_fragment* opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); - printf ("\t\t\t\t\"type\": \"OPT_FRAGMENT%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : ""); - printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false"); - printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]); - printf ("\t\t\t\t\"U-bit\": %s,\n", (opt_fragment->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false"); - printf ("\t\t\t\t\"firstSqn\": %i,\n", g_ntohl(opt_fragment->opt_sqn)); - printf ("\t\t\t\t\"fragmentOffset\": %i,\n", g_ntohl(opt_fragment->opt_frag_off)); - printf ("\t\t\t\t\"originalLength\": %i\n", g_ntohl(opt_fragment->opt_frag_len)); - break; - } - - case PGM_OPT_NAK_LIST: - { - struct pgm_opt_nak_list* opt_nak_list = (struct pgm_opt_nak_list*)(opt_header + 1); - char* end = (char*)opt_header + opt_header->opt_length; - printf ("\t\t\t\t\"type\": \"OPT_NAK_LIST%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : ""); - printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false"); - printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]); - printf ("\t\t\t\t\"U-bit\": %s,\n", (opt_nak_list->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false"); - char sqns[1024] = ""; - guint i = 0; - do { - char sqn[1024]; - sprintf (sqn, "%s%i", (i>0)?", ":"", g_ntohl(opt_nak_list->opt_sqn[i])); - strcat (sqns, sqn); - i++; - } while ((char*)&opt_nak_list->opt_sqn[i] < end); - printf ("\t\t\t\t\"sqn\": [%s]\n", sqns); - break; - } - - case PGM_OPT_PARITY_PRM: - { - struct pgm_opt_parity_prm* opt_parity_prm = (struct pgm_opt_parity_prm*)(opt_header + 1); - printf ("\t\t\t\t\"type\": \"OPT_PARITY_PRM%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : ""); - printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false"); - printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]); - printf ("\t\t\t\t\"U-bit\": %s,\n", (opt_parity_prm->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false"); - printf ("\t\t\t\t\"P-bit\": %s,\n", (opt_parity_prm->opt_reserved & PGM_PARITY_PRM_PRO) ? "true" : "false"); - printf ("\t\t\t\t\"O-bit\": %s,\n", (opt_parity_prm->opt_reserved & PGM_PARITY_PRM_OND) ? "true" : "false"); - printf ("\t\t\t\t\"transmissionGroupSize\": %i\n", g_ntohl(opt_parity_prm->parity_prm_tgs)); - break; - } - - case PGM_OPT_CURR_TGSIZE: - { - struct pgm_opt_curr_tgsize* opt_curr_tgsize = (struct pgm_opt_curr_tgsize*)(opt_header + 1); - printf ("\t\t\t\t\"type\": \"OPT_CURR_TGSIZE%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : ""); - printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false"); - printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]); - printf ("\t\t\t\t\"U-bit\": %s,\n", (opt_curr_tgsize->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false"); - printf ("\t\t\t\t\"actualTransmissionGroupSize\": %i\n", g_ntohl(opt_curr_tgsize->prm_atgsize)); - break; - } - - case PGM_OPT_SYN: - { - struct pgm_opt_syn* opt_syn = (struct pgm_opt_syn*)(opt_header + 1); - printf ("\t\t\t\t\"type\": \"OPT_SYN%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : ""); - printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false"); - printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]); - printf ("\t\t\t\t\"U-bit\": %s\n", (opt_syn->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false"); - break; - } - - case PGM_OPT_FIN: - { - struct pgm_opt_fin* opt_fin = (struct pgm_opt_fin*)(opt_header + 1); - printf ("\t\t\t\t\"type\": \"OPT_FIN%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : ""); - printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false"); - printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]); - printf ("\t\t\t\t\"U-bit\": %s\n", (opt_fin->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false"); - break; - } - - default: - { - guint8 opt_reserved = *(guint8*)(opt_header + 1); - printf ("\t\t\t\t\"type\": \"0x%x%s\",\n", opt_header->opt_type & PGM_OPT_MASK, (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : ""); - printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false"); - printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]); - printf ("\t\t\t\t\"U-bit\": %s\n", (opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false"); - break; - } - } - printf ("\t\t\t}"); - - } while (!(opt_header->opt_type & PGM_OPT_END)); - - printf ("\n\t\t]"); -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/dump-json.h b/3rdparty/openpgm-svn-r1085/pgm/test/dump-json.h deleted file mode 100644 index 6fa0f9d..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/dump-json.h +++ /dev/null @@ -1,33 +0,0 @@ -/* vim:ts=8:sts=4:sw=4:noai:noexpandtab - * - * JSON packet dump. - * - * Copyright (c) 2006-2007 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __PGM_DUMP_JSON_H__ -#define __PGM_DUMP_JSON_H__ - - -G_BEGIN_DECLS - -int monitor_packet (char*, guint); - - -G_END_DECLS - -#endif /* __PGM_DUMP_JSON_H__ */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/heartbeat_spm.pl b/3rdparty/openpgm-svn-r1085/pgm/test/heartbeat_spm.pl deleted file mode 100755 index 60f6266..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/heartbeat_spm.pl +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/perl -# heartbeat_spm.pl -# 5.1.5. Heartbeat SPMs - -use strict; -use Time::HiRes qw( gettimeofday tv_interval ); -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$app->connect; - -sub close_ssh { - $mon = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -print "app: publish test data.\n"; -$app->say ("send ao ringo"); - -print "mon: wait for odata ...\n"; -$mon->wait_for_odata; -my $t0 = [gettimeofday]; - -for (1..4) # look for four consecutive heartbeat SPMs less than 5000ms apart -{ - print "mon: wait for spm ...\n"; - $mon->wait_for_spm ({ 'timeout' => 5 }); - my $tn = [gettimeofday]; - my $elapsed = tv_interval ( $t0, $tn ); - $t0 = $tn; - - print "mon: spm received after $elapsed seconds.\n"; -} - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/monitor.c b/3rdparty/openpgm-svn-r1085/pgm/test/monitor.c deleted file mode 100644 index 6569a55..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/monitor.c +++ /dev/null @@ -1,349 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * PGM link monitor. - * - * Copyright (c) 2006-2007 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include "dump-json.h" - - -/* globals */ - -static const char* g_network = "239.192.0.1"; -static struct in_addr g_filter /* = { 0 } */; - -static GIOChannel* g_io_channel = NULL; -static GIOChannel* g_stdin_channel = NULL; -static GMainLoop* g_loop = NULL; - - -static void on_signal (int, gpointer); -static gboolean on_startup (gpointer); -static gboolean on_mark (gpointer); - -static gboolean on_io_data (GIOChannel*, GIOCondition, gpointer); -static gboolean on_io_error (GIOChannel*, GIOCondition, gpointer); - -static gboolean on_stdin_data (GIOChannel*, GIOCondition, gpointer); - -int -main ( - G_GNUC_UNUSED int argc, - G_GNUC_UNUSED char *argv[] - ) -{ -/* pre-initialise PGM messages module to add hook for GLib logging */ - pgm_messages_init(); - log_init (); - puts ("monitor"); - -/* setup signal handlers */ - signal (SIGSEGV, on_sigsegv); - signal (SIGHUP, SIG_IGN); - pgm_signal_install (SIGINT, on_signal, g_loop); - pgm_signal_install (SIGTERM, on_signal, g_loop); - - g_filter.s_addr = 0; - -/* delayed startup */ - puts ("scheduling startup."); - g_timeout_add(0, (GSourceFunc)on_startup, NULL); - -/* dispatch loop */ - g_loop = g_main_loop_new(NULL, FALSE); - - puts ("entering main event loop ... "); - g_main_loop_run(g_loop); - - puts ("event loop terminated, cleaning up."); - -/* cleanup */ - g_main_loop_unref(g_loop); - g_loop = NULL; - - if (g_io_channel) { - puts ("closing socket."); - - GError *err = NULL; - g_io_channel_shutdown (g_io_channel, FALSE, &err); - g_io_channel = NULL; - } - - if (g_stdin_channel) { - puts ("unbinding stdin."); - g_io_channel_unref (g_stdin_channel); - g_stdin_channel = NULL; - } - - puts ("finished."); - pgm_messages_shutdown(); - return 0; -} - -static void -on_signal ( - int signum, - gpointer user_data - ) -{ - GMainLoop* loop = (GMainLoop*)user_data; - g_message ("on_signal (signum:%d user-data:%p)", signum, user_data); - g_main_loop_quit (loop); -} - -static gboolean -on_startup ( - G_GNUC_UNUSED gpointer data - ) -{ - int e; - - puts ("startup."); - -/* find PGM protocol id */ -// TODO: fix valgrind errors - int ipproto_pgm = IPPROTO_PGM; -#if HAVE_GETPROTOBYNAME_R - char b[1024]; - struct protoent protobuf, *proto; - e = getprotobyname_r("pgm", &protobuf, b, sizeof(b), &proto); - if (e != -1 && proto != NULL) { - if (proto->p_proto != ipproto_pgm) { - printf("Setting PGM protocol number to %i from /etc/protocols.\n"); - ipproto_pgm = proto->p_proto; - } - } -#else - struct protoent *proto = getprotobyname("pgm"); - if (proto != NULL) { - if (proto->p_proto != ipproto_pgm) { - printf("Setting PGM protocol number to %i from /etc/protocols.\n", proto -->p_proto); - ipproto_pgm = proto->p_proto; - } - } -#endif - -/* open socket for snooping */ - puts ("opening raw socket."); - int sock = socket(PF_INET, SOCK_RAW, ipproto_pgm); - if (sock < 0) { - int _e = errno; - perror("on_startup() failed"); - - if (_e == EPERM && 0 != getuid()) { - puts ("PGM protocol requires this program to run as superuser."); - } - g_main_loop_quit(g_loop); - return FALSE; - } - -/* drop out of setuid 0 */ - if (0 == getuid()) { - puts ("dropping superuser privileges."); - setuid((gid_t)65534); - setgid((uid_t)65534); - } - - char _t = 1; - e = setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &_t, sizeof(_t)); - if (e < 0) { - perror("on_startup() failed"); - close(sock); - g_main_loop_quit(g_loop); - return FALSE; - } - -/* buffers */ - int buffer_size = 0; - socklen_t len = 0; - e = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &buffer_size, &len); - if (e == 0) { - printf ("receive buffer set at %i bytes.\n", buffer_size); - } - e = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &buffer_size, &len); - if (e == 0) { - printf ("send buffer set at %i bytes.\n", buffer_size); - } - -/* bind */ - struct sockaddr_in addr; - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_ANY); - - e = bind(sock, (struct sockaddr*)&addr, sizeof(addr)); - if (e < 0) { - perror("on_startup() failed"); - close(sock); - g_main_loop_quit(g_loop); - return FALSE; - } - -/* multicast */ - struct ip_mreq mreq; - memset(&mreq, 0, sizeof(mreq)); - mreq.imr_interface.s_addr = htonl(INADDR_ANY); - printf ("listening on interface %s.\n", inet_ntoa(mreq.imr_interface)); - mreq.imr_multiaddr.s_addr = inet_addr(g_network); - printf ("subscription on multicast address %s.\n", inet_ntoa(mreq.imr_multiaddr)); - e = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); - if (e < 0) { - perror("on_startup() failed"); - close(sock); - g_main_loop_quit(g_loop); - return FALSE; - } - -/* multicast loopback */ -/* multicast ttl */ - -/* add socket to event manager */ - g_io_channel = g_io_channel_unix_new (sock); - printf ("socket opened with encoding %s.\n", g_io_channel_get_encoding(g_io_channel)); - - /* guint event = */ g_io_add_watch (g_io_channel, G_IO_IN | G_IO_PRI, on_io_data, NULL); - /* guint event = */ g_io_add_watch (g_io_channel, G_IO_ERR | G_IO_HUP | G_IO_NVAL, on_io_error, NULL); - -/* add stdin to event manager */ - g_stdin_channel = g_io_channel_unix_new (fileno(stdin)); - printf ("binding stdin with encoding %s.\n", g_io_channel_get_encoding(g_stdin_channel)); - - g_io_add_watch (g_stdin_channel, G_IO_IN | G_IO_PRI, on_stdin_data, NULL); - -/* period timer to indicate some form of life */ -// TODO: Gnome 2.14: replace with g_timeout_add_seconds() - g_timeout_add(10 * 1000, (GSourceFunc)on_mark, NULL); - - puts ("READY"); - fflush (stdout); - return FALSE; -} - -static gboolean -on_mark ( - G_GNUC_UNUSED gpointer data - ) -{ - g_message ("-- MARK --"); - return TRUE; -} - -static gboolean -on_io_data ( - GIOChannel* source, - G_GNUC_UNUSED GIOCondition condition, - G_GNUC_UNUSED gpointer data - ) -{ - char buffer[4096]; - int fd = g_io_channel_unix_get_fd(source); - struct sockaddr_in addr; - socklen_t addr_len = sizeof(addr); - int len = recvfrom(fd, buffer, sizeof(buffer), MSG_DONTWAIT, (struct sockaddr*)&addr, &addr_len); - - if (g_filter.s_addr && g_filter.s_addr != addr.sin_addr.s_addr) { - return TRUE; - } - - printf ("%i bytes received from %s.\n", len, inet_ntoa(addr.sin_addr)); - - monitor_packet (buffer, len); - fflush (stdout); - - return TRUE; -} - -static gboolean -on_io_error ( - GIOChannel* source, - G_GNUC_UNUSED GIOCondition condition, - G_GNUC_UNUSED gpointer data - ) -{ - puts ("on_error."); - - GError *err; - g_io_channel_shutdown (source, FALSE, &err); - -/* remove event */ - return FALSE; -} - -/* process input commands from stdin/fd - */ - -static gboolean -on_stdin_data ( - GIOChannel* source, - G_GNUC_UNUSED GIOCondition condition, - G_GNUC_UNUSED gpointer data - ) -{ - gchar* str = NULL; - gsize len = 0; - gsize term = 0; - GError* err = NULL; - - g_io_channel_read_line (source, &str, &len, &term, &err); - if (len > 0) { - if (term) str[term] = 0; - - if (strcmp(str, "quit") == 0) { - g_main_loop_quit(g_loop); - } else if (strncmp(str, "filter ", strlen("filter ")) == 0) { - unsigned a, b, c, d; - int retval = sscanf(str, "filter %u.%u.%u.%u", &a, &b, &c, &d); - if (retval == 4) { - g_filter.s_addr = (d << 24) | (c << 16) | (b << 8) | a; - puts ("READY"); - } else { - printf ("invalid syntax for filter command."); - } - } else { - printf ("unknown command: %s\n", str); - } - } - - fflush (stdout); - g_free (str); - return TRUE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/nak.pl b/3rdparty/openpgm-svn-r1085/pgm/test/nak.pl deleted file mode 100755 index cb75609..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/nak.pl +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/perl -# nak.pl -# 5.3. Repairs - -use strict; -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$sim->say ("create ao"); -$sim->say ("bind ao"); -print "sim: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); # to process NAK requests - -print "app: publish test data.\n"; -$app->say ("send ao ringo"); -$app->say ("send ao ichigo"); -$app->say ("send ao momo"); - -my $odata = undef; -my $ocnt = 0; -for (1..3) { - print "mon: wait for odata ...\n"; - $odata = $mon->wait_for_odata; - $ocnt++; - print "mon: received $ocnt x odata.\n"; -} - -print "sim: send nak to app.\n"; -$sim->say ("net send nak ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 2"); - -print "mon: wait for rdata ...\n"; -$mon->wait_for_rdata; -print "mon: rdata received.\n"; - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/nak_cancellation.pl b/3rdparty/openpgm-svn-r1085/pgm/test/nak_cancellation.pl deleted file mode 100755 index 6ad36fe..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/nak_cancellation.pl +++ /dev/null @@ -1,161 +0,0 @@ -#!/usr/bin/perl -# nak_cancellation.pl -# 6.3. Data Recovery by Negative Acknowledgment - -use strict; -use IO::Handle; -use JSON; -use Time::HiRes qw( gettimeofday tv_interval ); -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -pipe(FROM_PARENT, TO_CHILD) or die "pipe: $!"; -FROM_PARENT->autoflush(1); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - close FROM_PARENT; close TO_CHILD; - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("set ao NAK_BO_IVL 5000"); # increase to count for test system latency -$app->say ("set ao NAK_RPT_IVL 10000"); -$app->say ("set ao NAK_RDATA_IVL 10000"); -$app->say ("set ao NAK_NCF_RETRIES 15"); -$app->say ("set ao NAK_DATA_RETRIES 10"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -## capture GSI of test spp -$app->say ("send ao nashi"); -print "mon: wait for odata ...\n"; -my $odata = $mon->wait_for_odata; -print "mon: odata received.\n"; - -$sim->say ("create fake ao"); -$sim->say ("bind ao"); - -print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; -$sim->say ("net send spm ao 3200 90001 90000"); - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak ({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "sim: publish ODATA sqn 90,001.\n"; -$sim->say ("net send odata ao 90001 90001 ringo"); - -print "app: wait for data ...\n"; -my $data = $app->wait_for_data; -print "app: data received [$data].\n"; - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak ({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "sim: publish ODATA sqn 90,003.\n"; -$sim->say ("net send odata ao 90003 90001 ichigo"); -my $t0 = [gettimeofday]; - -if (my $pid = fork) { -# parent - close FROM_PARENT; - - sleep 10; - - print "app: wait for data ...\n"; - my $data = $app->wait_for_data({ 'timeout' => 0 }); - print "app: data received [$data].\n"; - - print TO_CHILD "die\n"; - - close TO_CHILD; - waitpid($pid,0); -} else { -# child - die "cannot fork: $!" unless defined $pid; - close TO_CHILD; - - print "sim: loop waiting for NAKs ...\n"; - - my $fh = $sim->{in}; - vec(my $rin, fileno(FROM_PARENT), 1) = 1; - vec($rin, fileno($fh), 1) = 1; - my $rout = undef; - - my $b = ''; - my $state = 0; - my $json = new JSON; - my $io = IO::Handle->new_from_fd( fileno($fh), "r" ); - my $cnt = 0; - while (select($rout = $rin, undef, undef, undef)) - { - last if( vec($rout, fileno(FROM_PARENT), 1) ); - while (defined($_ = $io->getline)) - { - chomp; - my $l = $_; - if ($state == 0) { - if ($l =~ /{$/) { - $state = 1; - } else { - print "sim [$l]\n"; - last; - } - } - - if ($state == 1) { - $b .= $l; - - if ($l =~ /^}$/) { - $state = 0; - - my $obj = $json->jsonToObj($b); - if ($obj->{PGM}->{type} =~ /NAK/) { - $cnt++; - my $elapsed = tv_interval ( $t0, [gettimeofday] ); - print "sim: $cnt x NAK received in $elapsed seconds.\n"; - $sim->say ("net send ncf ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 90002"); - } - -# reset - $b = ''; - last; - } - } - } - last if ($io->eof); - } - - print "sim: loop finished.\n"; - close FROM_PARENT; - exit; -} - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/nak_list.pl b/3rdparty/openpgm-svn-r1085/pgm/test/nak_list.pl deleted file mode 100755 index 838dfd5..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/nak_list.pl +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/perl -# nak_list.pl -# 9.3. NAK List Option - OPT_NAK_LIST - -use strict; -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$sim->say ("create ao"); -$sim->say ("bind ao"); -print "sim: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -print "app: publish test data.\n"; -$app->say ("send ao ringo"); -$app->say ("send ao ichigo"); -$app->say ("send ao momo"); - -my $odata = undef; -my $ocnt = 0; -for (1..3) { - print "mon: wait for odata ...\n"; - $odata = $mon->wait_for_odata; - $ocnt++; - print "mon: received $ocnt x odata.\n"; -} - -print "sim: send nak to app.\n"; -$sim->say ("net send nak ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 0,1,2"); - -my $rcnt = 0; -for (1..3) { - print "mon: wait for rdata ...\n"; - $mon->wait_for_rdata; - $rcnt++; - print "mon: received $rcnt x rdata.\n"; -} - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/nak_parity.pl b/3rdparty/openpgm-svn-r1085/pgm/test/nak_parity.pl deleted file mode 100755 index 50f961b..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/nak_parity.pl +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/perl -# nak_parity.pl -# 5.3. Repairs - -use strict; -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$sim->say ("create ao"); -$sim->say ("set ao FEC RS(255,4)"); -$sim->say ("bind ao"); -print "sim: ready.\n"; - -$app->say ("create ao"); -$app->say ("set ao FEC RS(255,4)"); -$app->say ("bind ao"); -$app->say ("listen ao"); # to process NAK requests - -print "app: publish test data.\n"; -$app->say ("send ao ringo"); -$app->say ("send ao ichigo"); -$app->say ("send ao momo"); -$app->say ("send ao budo"); -$app->say ("send ao nashi"); -$app->say ("send ao anzu"); -$app->say ("send ao kaki"); - -my $odata = undef; -my $ocnt = 0; -for (1..7) { - print "mon: wait for odata ...\n"; - $odata = $mon->wait_for_odata; - $ocnt++; - print "mon: received $ocnt x odata.\n"; -} - -print "sim: send nak to app (transmission group = 0, packet count = 1).\n"; -$sim->say ("net send parity nak ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 1"); - -print "mon: wait for rdata ...\n"; -my $rdata = $mon->wait_for_rdata; -print "mon: rdata received.\n"; - -die "Selective RDATA received, parityPacket=false\n" unless $rdata->{PGM}->{options}->{parityPacket}; -print "Parity RDATA received.\n"; - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/nak_repeat.pl b/3rdparty/openpgm-svn-r1085/pgm/test/nak_repeat.pl deleted file mode 100755 index b811fa8..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/nak_repeat.pl +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/perl -# nak_repeat.pl -# 5.3. Repairs - -use strict; -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$sim->say ("create ao"); -$sim->say ("bind ao"); -print "sim: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); # to process NAK requests - -print "app: publish test data.\n"; -$app->say ("send ao " . ("ringo" x 200)); -$app->say ("send ao " . ("ichigo" x 200)); -$app->say ("send ao " . ("momo" x 200)); - -my $odata = undef; -my $ocnt = 0; -for (1..3) { - print "mon: wait for odata ...\n"; - $odata = $mon->wait_for_odata; - $ocnt++; - print "mon: received $ocnt x odata.\n"; -} - -for (1..1000) { - my $i = $_; - print "sim: $i# send nak to app.\n"; - $sim->say ("net send nak ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 2"); - - print "mon: $i# wait for rdata ...\n"; - $mon->wait_for_rdata; - print "mon: $i# rdata received.\n"; -} - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/ncf.pl b/3rdparty/openpgm-svn-r1085/pgm/test/ncf.pl deleted file mode 100755 index e26145f..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/ncf.pl +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/perl -# ncf.pl -# 5.2. Negative Acknowledgment Confirmation - -use strict; -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$sim->say ("create ao"); -$sim->say ("bind ao"); -print "sim: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -print "app: publish test data.\n"; -$app->say ("send ao ringo"); -$app->say ("send ao ichigo"); -$app->say ("send ao momo"); - -my $odata = undef; -my $ocnt = 0; -for (1..3) { - print "mon: wait for odata ...\n"; - $odata = $mon->wait_for_odata; - $ocnt++; - print "mon: received $ocnt x odata.\n"; -} - -print "sim: send nak to app.\n"; -$sim->say ("net send nak ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 2"); - -print "mon: wait for ncf ...\n"; -$mon->wait_for_ncf; -print "mon: ncf received.\n"; - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/ncf_cancellation.pl b/3rdparty/openpgm-svn-r1085/pgm/test/ncf_cancellation.pl deleted file mode 100755 index ca23b22..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/ncf_cancellation.pl +++ /dev/null @@ -1,147 +0,0 @@ -#!/usr/bin/perl -# ncf_cancellation.pl -# 6.3. Data Recovery by Negative Acknowledgment - -use strict; -use IO::Handle; -use JSON; -use Time::HiRes qw( gettimeofday tv_interval ); -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -pipe(FROM_PARENT, TO_CHILD) or die "pipe: $!"; -FROM_PARENT->autoflush(1); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - close FROM_PARENT; close TO_CHILD; - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -## capture GSI of test spp -$app->say ("send ao nashi"); -print "mon: wait for odata ...\n"; -my $odata = $mon->wait_for_odata; -print "mon: odata received.\n"; - -$sim->say ("create fake ao"); -$sim->say ("bind ao"); - -print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; -$sim->say ("net send spm ao 3200 90001 90000"); - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak ({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "sim: publish ODATA sqn 90,001.\n"; -$sim->say ("net send odata ao 90001 90001 ringo"); - -print "app: wait for data ...\n"; -my $data = $app->wait_for_data; -print "app: data received [$data].\n"; - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak ({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "sim: publish ODATA sqn 90,003.\n"; -$sim->say ("net send odata ao 90003 90001 ichigo"); -my $t0 = [gettimeofday]; - -if (my $pid = fork) { -# parent - close FROM_PARENT; - - print "app: wait for data ...\n"; - my $data = $app->wait_for_data({ 'timeout' => 0 }); - print "app: data received [$data].\n"; - - print TO_CHILD "die\n"; - - close TO_CHILD; - waitpid($pid,0); -} else { -# child - die "cannot fork: $!" unless defined $pid; - close TO_CHILD; - print "sim: loop waiting for NAKs ...\n"; - - my $fh = $sim->{in}; - vec(my $rin, fileno(FROM_PARENT), 1) = 1; - vec($rin, fileno($fh), 1) = 1; - my $rout = undef; - - my $b = ''; - my $state = 0; - my $json = new JSON; - my $io = IO::Handle->new_from_fd( fileno($fh), "r" ); - my $cnt = 0; - while (select($rout = $rin, undef, undef, undef)) - { - last if( vec($rout, fileno(FROM_PARENT), 1) ); - last unless (defined($_ = $io->getline)); - chomp; - my $l = $_; - if ($state == 0) { - if ($l =~ /{$/) { - $state = 1; - } else { - print "sim [$l]\n"; - } - } - - if ($state == 1) { - $b .= $l; - - if ($l =~ /^}$/) { - $state = 0; - - my $obj = $json->jsonToObj($b); - if ($obj->{PGM}->{type} =~ /NAK/) { - $cnt++; - my $elapsed = tv_interval ( $t0, [gettimeofday] ); - print "sim: $cnt x NAK received in $elapsed seconds.\n"; - } - -# reset - $b = ''; - } - } - } - - print "sim: loop finished.\n"; - close FROM_PARENT; - exit; -} - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/ncf_list.pl b/3rdparty/openpgm-svn-r1085/pgm/test/ncf_list.pl deleted file mode 100755 index 55b1d81..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/ncf_list.pl +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/perl -# ncf_list.pl -# 9.3. NAK List Option - OPT_NAK_LIST - -use strict; -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$sim->say ("create ao"); -$sim->say ("bind ao"); -print "sim: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -print "app: publish test data.\n"; -$app->say ("send ao ringo"); -$app->say ("send ao ichigo"); -$app->say ("send ao momo"); - -my $odata = undef; -my $ocnt = 0; -for (1..3) { - print "mon: wait for odata ...\n"; - $odata = $mon->wait_for_odata; - $ocnt++; - print "mon: received $ocnt x odata.\n"; -} - -print "sim: send nak to app.\n"; -$sim->say ("net send nak ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 1,2,3"); - -print "mon: wait for ncf ...\n"; -my $ncf = $mon->wait_for_ncf; -print "mon: ncf received.\n"; -die "ncfSqn != 1\n" unless $ncf->{PGM}->{ncfSqn} == 1; -die "NCF list incorrect\n" unless ( - $ncf->{PGM}->{pgmOptions}[1]->{sqn}[0] == 2 - && $ncf->{PGM}->{pgmOptions}[1]->{sqn}[1] == 3 - ); -print "mon: ncf list correct: $ncf->{PGM}->{ncfSqn} + [$ncf->{PGM}->{pgmOptions}[1]->{sqn}[0], $ncf->{PGM}->{pgmOptions}[1]->{sqn}[1]]\n"; - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/ncf_suppression.pl b/3rdparty/openpgm-svn-r1085/pgm/test/ncf_suppression.pl deleted file mode 100755 index c1d42a8..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/ncf_suppression.pl +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/perl -# ncf_suppression.pl -# 6.3. Data Recovery by Negative Acknowledgment - -use strict; -use Time::HiRes qw( gettimeofday tv_interval ); -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -## capture GSI of test spp -$app->say ("send ao nashi"); -print "mon: wait for odata ...\n"; -my $odata = $mon->wait_for_odata; -print "mon: odata received.\n"; - -$sim->say ("create fake ao"); -$sim->say ("bind ao"); - -print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; -$sim->say ("net send spm ao 3200 90001 90000"); - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak ({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "sim: publish ODATA sqn 90,001.\n"; -$sim->say ("net send odata ao 90001 90001 ringo"); - -print "app: wait for data ...\n"; -my $data = $app->wait_for_data; -print "app: data received [$data].\n"; - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak ({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -## first run through with regular NAK generation to get regular backoff interval -print "sim: publish ODATA sqn 90,003.\n"; -$sim->say ("net send odata ao 90003 90001 ichigo"); -my $t0 = [gettimeofday]; -print "sim: waiting for valid NAK.\n"; -$sim->wait_for_nak; -my $normal_backoff = tv_interval ( $t0, [gettimeofday] ); -print "sim: NAK received in $normal_backoff seconds.\n"; - -## cleanup by publishing repair data -print "sim: publish RDATA sqn 90,002.\n"; -$sim->say ("net send odata ao 90002 90001 momo"); -print "app: wait for data ...\n"; -my $data = $app->wait_for_data; -print "app: data received [$data].\n"; - -## second run with NAK suppression -$t0 = [gettimeofday]; -print "sim: publish ODATA sqn 90,005.\n"; -$sim->say ("net send odata ao 90005 90001 anzu"); -print "sim: publish NCF sqn 90,004.\n"; -$sim->say ("net send ncf ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 90004"); - -print "sim: waiting for valid NAK.\n"; -$sim->wait_for_nak; -my $suppressed_backoff = tv_interval ( $t0, [gettimeofday] ); -print "sim: NAK received in $suppressed_backoff seconds.\n"; - -die "NAK suppression failed.\n" unless ($suppressed_backoff > $normal_backoff); - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/odata.pl b/3rdparty/openpgm-svn-r1085/pgm/test/odata.pl deleted file mode 100755 index 50bcec0..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/odata.pl +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/perl -# odata.pl -# 3.6.2.1. ODATA - Original Data - -use strict; -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$app->connect; - -sub close_ssh { - $mon = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); - -print "app: publish test data.\n"; -$app->say ("send ao ringo"); - -print "mon: wait for odata ...\n"; -$mon->wait_for_odata; -print "mon: received odata.\n"; - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/odata_completion.pl b/3rdparty/openpgm-svn-r1085/pgm/test/odata_completion.pl deleted file mode 100755 index 6b45a47..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/odata_completion.pl +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/perl -# odata_completion.pl -# 6.3. Data Recovery by Negative Acknowledgment - -use strict; -use Time::HiRes qw( gettimeofday tv_interval ); -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -## capture GSI of test spp -$app->say ("send ao nashi"); -print "mon: wait for odata ...\n"; -my $odata = $mon->wait_for_odata; -print "mon: odata received.\n"; - -$sim->say ("create fake ao"); -$sim->say ("bind ao"); - -print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; -$sim->say ("net send spm ao 3200 90001 90000"); - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak ({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "sim: publish ODATA sqn 90,001.\n"; -$sim->say ("net send odata ao 90001 90001 ringo"); - -print "app: wait for data ...\n"; -my $data = $app->wait_for_data; -print "app: data received [$data].\n"; - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak ({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "sim: publish ODATA sqn 90,003.\n"; -$sim->say ("net send odata ao 90003 90001 ichigo"); -print "sim: waiting for valid NAK.\n"; -$sim->wait_for_nak; -print "sim: NAK received.\n"; - -print "sim: publish ODATA sqn 90,002.\n"; -$sim->say ("net send odata ao 90002 90001 momo"); - -for (1..2) -{ - print "app: wait for data ...\n"; - my $data = $app->wait_for_data; - print "app: data received [$data].\n"; -} - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/odata_jump.pl b/3rdparty/openpgm-svn-r1085/pgm/test/odata_jump.pl deleted file mode 100755 index aec4d82..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/odata_jump.pl +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/perl -# odata_jump.pl -# 6.3. Data Recovery by Negative Acknowledgment - -use strict; -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -$sim->say ("create fake ao"); -$sim->say ("bind ao"); - -print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; -$sim->say ("net send spm ao 3200 90001 90000"); - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "sim: publish ODATA sqn 90,001.\n"; -$sim->say ("net send odata ao 90001 90001 ringo"); - -print "app: wait for data ...\n"; -my $data = $app->wait_for_data; -print "app: data received [$data].\n"; - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "sim: publish ODATA sqn 90,003.\n"; -$sim->say ("net send odata ao 90003 90001 ichigo"); - -print "sim: waiting for valid NAK.\n"; -$sim->wait_for_nak; -print "sim: NAK received.\n"; - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/odata_jump_parity.pl b/3rdparty/openpgm-svn-r1085/pgm/test/odata_jump_parity.pl deleted file mode 100755 index 99179cc..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/odata_jump_parity.pl +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/perl -# odata_jump_parity.pl -# 6.3. Data Recovery by Negative Acknowledgment - -use strict; -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -$sim->say ("create fake ao"); -$sim->say ("bind ao"); - -print "sim: publish SPM txw_trail 32,769 txw_lead 32,768 at spm_sqn 3200, advertise on-demand parity, k=4.\n"; -$sim->say ("net send spm ao 3200 32769 32768 on-demand 4"); - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "sim: publish ODATA sqn 32,769.\n"; -$sim->say ("net send odata ao 32769 32769 ringo"); - -print "app: wait for data ...\n"; -my $data = $app->wait_for_data; -print "app: data received [$data].\n"; - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -# force window into next transmission group -print "sim: publish ODATA sqn 32,771.\n"; -$sim->say ("net send odata ao 32771 32769 ichigo"); -print "sim: publish ODATA sqn 32,772.\n"; -$sim->say ("net send odata ao 32772 32769 momo"); -print "sim: publish ODATA sqn 32,773.\n"; -$sim->say ("net send odata ao 32773 32769 yakitori"); -print "sim: publish ODATA sqn 32,774.\n"; -$sim->say ("net send odata ao 32774 32769 sasami"); -print "sim: publish ODATA sqn 32,775.\n"; -$sim->say ("net send odata ao 32775 32769 tebasaki"); - -print "sim: waiting for valid NAK.\n"; -my $nak = $sim->wait_for_nak; -print "sim: NAK received.\n"; - -die "Selective NAK received, parityPacket=false\n" unless $nak->{PGM}->{options}->{parityPacket}; -print "Parity NAK received.\n"; - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/odata_number.pl b/3rdparty/openpgm-svn-r1085/pgm/test/odata_number.pl deleted file mode 100755 index 2d442dc..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/odata_number.pl +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/perl -# odata_number.pl -# 5.1.1. Maximum Cumulative Transmit Rate - -use strict; -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$app->connect; - -sub close_ssh { - $mon = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -print "app: send 1000 data packets ...\n"; -# hide stdout -open(OLDOUT, ">&STDOUT"); -open(STDOUT, ">/dev/null") or die "Can't redirect stdout: $!"; - -for (0..999) -{ - my $i = $_; - $app->say ("send ao $i"); - my $odata = $mon->wait_for_odata; - - die "out of sequence ODATA, received $odata->{PGM}->{odSqn} expected $i\n" unless $odata->{PGM}->{odSqn} == $i; -} - -# restore stdout -close(STDOUT) or die "Can't close STDOUT: $!"; -open(STDOUT, ">&OLDOUT") or die "Can't restore stdout: $!"; -close(OLDOUT) or die "Can't close OLDOUT: $!"; - -print "mon: received 1000 x odata.\n"; - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/odata_rate.pl b/3rdparty/openpgm-svn-r1085/pgm/test/odata_rate.pl deleted file mode 100755 index fb8c7e0..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/odata_rate.pl +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/perl -# odata_number.pl -# 5.1.1. Maximum Cumulative Transmit Rate - -use strict; -use Time::HiRes qw( gettimeofday tv_interval ); -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$app->connect; - -sub close_ssh { - $mon = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("set ao TXW_MAX_RTE 1500"); -$app->say ("bind ao"); - -print "app: send 50 data packets ...\n"; -my $t0 = [gettimeofday]; - -# hide stdout -open(OLDOUT, ">&STDOUT"); -open(STDOUT, ">/dev/null") or die "Can't redirect stdout: $!"; - -my $payload = "ringo" x 100; -my $bytes = 0; -for (1..50) -{ - $app->say ("send ao $payload"); - my $odata = $mon->wait_for_odata; - $bytes += $odata->{IP}->{length}; -} - -close(STDOUT) or die "Can't close STDOUT: $!"; -open(STDOUT, ">&OLDOUT") or die "Can't restore stdout: $!"; -close(OLDOUT) or die "Can't close OLDOUT: $!"; - -my $elapsed = tv_interval ( $t0, [gettimeofday] ); -print "mon: received 50 x odata, $bytes bytes in $elapsed seconds.\n"; - -my $rate = $bytes / $elapsed; -$rate = $bytes if ($rate > $bytes); -print "mon: incoming data rate $rate bps.\n"; - -die "incoming rate exceeds set TXW_MAX_RTE\n" unless $rate < 1650; - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/odata_reception.pl b/3rdparty/openpgm-svn-r1085/pgm/test/odata_reception.pl deleted file mode 100755 index ef95974..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/odata_reception.pl +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/perl -# odata_reception.pl -# 6.1. Data Reception - -use strict; -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -$sim->say ("create fake ao"); -$sim->say ("bind ao"); - -print "sim: publish ODATA sqn 90,000.\n"; -$sim->say ("net send odata ao 90000 90000 ringo"); -print "app: wait for data ...\n"; -my $data = $app->wait_for_data; -print "app: received data [$data].\n"; - -# no NAKs should be generated. -# TODO: test for silence in {mon} - -print "sim: publish ODATA sqn 90,001.\n"; -$sim->say ("net send odata ao 90001 90000 ichigo"); -print "app: wait for data ...\n"; -$data = $app->wait_for_data; -print "app: received data [$data].\n"; - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/on-demand_spm.pl b/3rdparty/openpgm-svn-r1085/pgm/test/on-demand_spm.pl deleted file mode 100755 index eb86cf9..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/on-demand_spm.pl +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/perl -# on-demand_spm.pl -# 5.1.4. Ambient SPMs with on-demand parity flag - -use strict; -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$app->connect; - -sub close_ssh { - $mon = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("set ao FEC RS(255,64)"); -$app->say ("bind ao"); -print "app: ready.\n"; - -print "mon: wait for spm ...\n"; -my $spm = $mon->wait_for_spm; -print "mon: received spm.\n"; - -die "SPM does not contain any PGM options\n" unless $spm->{PGM}->{pgmOptions}; -die "SPM does not contain a PGM_OPT_PARITY_PRM option\n" unless $spm->{PGM}->{pgmOptions}[1]->{type} =~ /OPT_PARITY_PRM/; -print "pro-active parity " . ($spm->{PGM}->{pgmOptions}[1]->{'P-bit'} ? 'enabled' : 'disabled') . ", P-bit " . ($spm->{PGM}->{pgmOptions}[1]->{'P-bit'} ? 'true' : 'false') . "\n"; -die "on-demand parity disabled, O-bit false\n" unless $spm->{PGM}->{pgmOptions}[1]->{'O-bit'}; -print "on-demand parity enabled, O-bit true\n"; - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/outofwindow_ncf.pl b/3rdparty/openpgm-svn-r1085/pgm/test/outofwindow_ncf.pl deleted file mode 100755 index 4849e36..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/outofwindow_ncf.pl +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/perl -# outofwindow_ncf.pl -# 6.3. Data Recovery by Negative Acknowledgment - -use strict; -use Time::HiRes qw( gettimeofday tv_interval ); -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -## capture GSI of test spp -$app->say ("send ao nashi"); -print "mon: wait for odata ...\n"; -my $odata = $mon->wait_for_odata; -print "mon: odata received.\n"; - -$sim->say ("create fake ao"); -$sim->say ("bind ao"); - -print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; -$sim->say ("net send spm ao 3200 90001 90000"); - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak ({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "sim: publish ODATA sqn 90,001.\n"; -$sim->say ("net send odata ao 90001 90001 ringo"); - -print "app: wait for data ...\n"; -my $data = $app->wait_for_data; -print "app: data received [$data].\n"; - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak ({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -## first run through with regular NAK generation to get regular backoff interval -print "sim: publish ODATA sqn 90,003.\n"; -$sim->say ("net send odata ao 90003 90001 ichigo"); -my $t0 = [gettimeofday]; -print "sim: waiting for valid NAK.\n"; -$sim->wait_for_nak; -my $normal_backoff = tv_interval ( $t0, [gettimeofday] ); -print "sim: NAK received in $normal_backoff seconds.\n"; - -## cleanup by publishing repair data -print "sim: publish RDATA sqn 90,002.\n"; -$sim->say ("net send odata ao 90002 90001 momo"); -print "app: wait for data ...\n"; -my $data = $app->wait_for_data; -print "app: data received [$data].\n"; - -## second run with NAK suppression -$t0 = [gettimeofday]; -print "sim: publish ODATA sqn 90,005.\n"; -$sim->say ("net send odata ao 90005 90001 anzu"); -print "sim: publish NCF sqn 90,004.\n"; -$sim->say ("net send ncf ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 90002"); - -print "sim: waiting for valid NAK.\n"; -$sim->wait_for_nak; -my $outofwindow_backoff = tv_interval ( $t0, [gettimeofday] ); -print "sim: NAK received in $outofwindow_backoff seconds.\n"; - -# allow 100ms tolerance -my $fabs = abs( ($outofwindow_backoff - $normal_backoff) * 1000 ); -die "Out-of-window NCF altered back-off interval by $fabs ms.\n" unless ($fabs < 100); - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion.pl b/3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion.pl deleted file mode 100755 index 8a0cc60..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion.pl +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/perl -# rdata_completion.pl -# 6.3. Data Recovery by Negative Acknowledgment - -use strict; -use Time::HiRes qw( gettimeofday tv_interval ); -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -## capture GSI of test spp -$app->say ("send ao nashi"); -print "mon: wait for odata ...\n"; -my $odata = $mon->wait_for_odata; -print "mon: odata received.\n"; - -$sim->say ("create fake ao"); -$sim->say ("bind ao"); - -print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; -$sim->say ("net send spm ao 3200 90001 90000"); - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak ({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "sim: publish ODATA sqn 90,001.\n"; -$sim->say ("net send odata ao 90001 90001 ringo"); - -print "app: wait for data ...\n"; -my $data = $app->wait_for_data; -print "app: data received [$data].\n"; - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak ({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "sim: publish ODATA sqn 90,003.\n"; -$sim->say ("net send odata ao 90003 90001 ichigo"); -print "sim: waiting for valid NAK.\n"; -$sim->wait_for_nak; -print "sim: NAK received.\n"; - -print "sim: publish RDATA sqn 90,002.\n"; -$sim->say ("net send rdata ao 90002 90001 momo"); - -for (1..2) -{ - print "app: wait for data ...\n"; - my $data = $app->wait_for_data; - print "app: data received [$data].\n"; -} - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion_parity.pl b/3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion_parity.pl deleted file mode 100755 index 95a6f7b..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion_parity.pl +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/perl -# rdata_completion_parity.pl -# 6.3. Data Recovery by Negative Acknowledgment - -use strict; -use Time::HiRes qw( gettimeofday tv_interval ); -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("set ao FEC RS(255,4)"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -## capture GSI of test spp -$app->say ("send ao nashi"); -print "mon: wait for odata ...\n"; -my $odata = $mon->wait_for_odata; -print "mon: odata received.\n"; - -$sim->say ("create fake ao"); -$sim->say ("set ao FEC RS(255,4)"); -$sim->say ("bind ao"); - -print "sim: publish SPM txw_trail 32,769 txw_lead 32,768 at spm_sqn 3200, advertise on-demand parity, k=4.\n"; -$sim->say ("net send spm ao 3200 32768 32767 on-demand 4"); - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak ({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "sim: publish ODATA sqn 32,768.\n"; -$sim->say ("net send odata ao 32768 32768 ringo000"); - -print "app: wait for data ...\n"; -my $data = $app->wait_for_data; -print "app: data received [$data].\n"; - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak ({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "sim: publish ODATA sqn 32,770.\n"; -$sim->say ("net send odata ao 32770 32768 momo0000"); -print "sim: publish ODATA sqn 32,771.\n"; -$sim->say ("net send odata ao 32771 32768 yakitori"); -print "sim: publish ODATA sqn 32,772.\n"; -$sim->say ("net send odata ao 32772 32768 sasami00"); -print "sim: publish ODATA sqn 32,773.\n"; -$sim->say ("net send odata ao 32773 32768 tebasaki"); - -print "sim: waiting for valid parity NAK.\n"; -my $nak = $sim->wait_for_nak; -die "Selective NAK received, parityPacket=false\n" unless $nak->{PGM}->{options}->{parityPacket}; -print "sim: Parity NAK received.\n"; - -print "sim: publish parity RDATA, tg_sqn 32,768, pkt_cnt 1 (sqn 32,768).\n"; -$sim->say ("net send parity rdata ao 32768 32768 ringo000 ichigo00 momo0000 yakitori"); - -for (1..5) -{ - print "app: wait for data ...\n"; - my $data = $app->wait_for_data; - print "app: data received [$data].\n"; -} - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion_parity_var_pktlen.pl b/3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion_parity_var_pktlen.pl deleted file mode 100755 index 9011ea1..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion_parity_var_pktlen.pl +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/perl -# rdata_completion_parity_var_pktlen.pl -# 6.3. Data Recovery by Negative Acknowledgment - -use strict; -use Time::HiRes qw( gettimeofday tv_interval ); -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("set ao FEC RS(255,4)"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -## capture GSI of test spp -$app->say ("send ao nashi"); -print "mon: wait for odata ...\n"; -my $odata = $mon->wait_for_odata; -print "mon: odata received.\n"; - -$sim->say ("create fake ao"); -$sim->say ("set ao FEC RS(255,4)"); -$sim->say ("bind ao"); - -print "sim: publish SPM txw_trail 32,769 txw_lead 32,768 at spm_sqn 3200, advertise on-demand parity, k=4.\n"; -$sim->say ("net send spm ao 3200 32768 32767 on-demand 4"); - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak ({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "sim: publish ODATA sqn 32,768.\n"; -$sim->say ("net send odata ao 32768 32768 ringo"); - -print "app: wait for data ...\n"; -my $data = $app->wait_for_data; -print "app: data received [$data].\n"; - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak ({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "sim: publish ODATA sqn 32,770.\n"; -$sim->say ("net send odata ao 32770 32768 momo"); -print "sim: publish ODATA sqn 32,771.\n"; -$sim->say ("net send odata ao 32771 32768 yakitori"); -print "sim: publish ODATA sqn 32,772.\n"; -$sim->say ("net send odata ao 32772 32768 sasami"); -print "sim: publish ODATA sqn 32,773.\n"; -$sim->say ("net send odata ao 32773 32768 tebasaki"); - -print "sim: waiting for valid parity NAK.\n"; -my $nak = $sim->wait_for_nak; -die "Selective NAK received, parityPacket=false\n" unless $nak->{PGM}->{options}->{parityPacket}; -print "sim: Parity NAK received.\n"; - -print "sim: publish parity RDATA, tg_sqn 32,768, pkt_cnt 1 (sqn 32,768).\n"; -$sim->say ("net send parity rdata ao 32768 32768 ringo ichigo momo yakitori"); - -for (1..5) -{ - print "app: wait for data ...\n"; - my $data = $app->wait_for_data; - print "app: data received [$data].\n"; -} - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/rdata_jump.pl b/3rdparty/openpgm-svn-r1085/pgm/test/rdata_jump.pl deleted file mode 100755 index f472b33..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/rdata_jump.pl +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/perl -# rdata_jump.pl -# 6.3. Data Recovery by Negative Acknowledgment - -use strict; -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -$sim->say ("create fake ao"); -$sim->say ("bind ao"); - -print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; -$sim->say ("net send spm ao 3200 90001 90000"); - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "sim: publish ODATA sqn 90,001.\n"; -$sim->say ("net send odata ao 90001 90001 ringo"); - -print "app: wait for data ...\n"; -my $data = $app->wait_for_data; -print "app: data received [$data].\n"; - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "sim: publish RDATA sqn 90,003.\n"; -$sim->say ("net send rdata ao 90003 90001 ichigo"); - -print "sim: waiting for valid NAK.\n"; -$sim->wait_for_nak; -print "sim: NAK received.\n"; - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/rdata_reception.pl b/3rdparty/openpgm-svn-r1085/pgm/test/rdata_reception.pl deleted file mode 100755 index a26bb4e..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/rdata_reception.pl +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/perl -# rdata_reception.pl -# 6.1. Data Reception - -use strict; -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -$sim->say ("create fake ao"); -$sim->say ("bind ao"); - -print "sim: publish RDATA sqn 90,000.\n"; -$sim->say ("net send rdata ao 90000 90000 ringo"); -print "app: wait for data ...\n"; -my $data = $app->wait_for_data; -print "app: received data [$data].\n"; - -# no NAKs should be generated. -# TODO: test for silence in {mon} - -print "sim: publish ODATA sqn 90,001.\n"; -$sim->say ("net send odata ao 90001 90000 ichigo"); -print "app: wait for data ...\n"; -$data = $app->wait_for_data; -print "app: received data [$data].\n"; - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/sim.c b/3rdparty/openpgm-svn-r1085/pgm/test/sim.c deleted file mode 100644 index b0e473b..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/sim.c +++ /dev/null @@ -1,1924 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * PGM conformance endpoint simulator. - * - * Copyright (c) 2006-2008 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include "dump-json.h" -#include "async.h" - - -/* typedefs */ - -struct idle_source { - GSource source; - guint64 expiration; -}; - -struct sim_session { - char* name; - pgm_transport_t* transport; - gboolean is_transport_fake; - GIOChannel* recv_channel; - pgm_async_t* async; -}; - -/* globals */ -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "sim" - -#ifndef SOL_IP -# define SOL_IP IPPROTO_IP -#endif -#ifndef SOL_IPV6 -# define SOL_IPV6 IPPROTO_IPV6 -#endif - - -static int g_port = 7500; -static const char* g_network = ";239.192.0.1"; - -static int g_max_tpdu = 1500; -static int g_sqns = 100 * 1000; - -static GList* g_sessions_list = NULL; -static GHashTable* g_sessions = NULL; -static GMainLoop* g_loop = NULL; -static GIOChannel* g_stdin_channel = NULL; - - -static void on_signal (int, gpointer); -static gboolean on_startup (gpointer); -static gboolean on_mark (gpointer); -static void destroy_session (struct sim_session*); -static int on_data (gpointer, guint, gpointer); -static gboolean on_stdin_data (GIOChannel*, GIOCondition, gpointer); -void generic_net_send_nak (guint8, char*, pgm_tsi_t*, struct pgm_sqn_list_t*); - - -G_GNUC_NORETURN static -void -usage (const char* bin) -{ - fprintf (stderr, "Usage: %s [options]\n", bin); - fprintf (stderr, " -n : Multicast group or unicast IP address\n"); - fprintf (stderr, " -s : IP port\n"); - exit (1); -} - -int -main ( - int argc, - char *argv[] - ) -{ - pgm_error_t* err = NULL; - -/* pre-initialise PGM messages module to add hook for GLib logging */ - pgm_messages_init(); - log_init (); - g_message ("sim"); - - if (!pgm_init (&err)) { - g_error ("Unable to start PGM engine: %s", (err && err->message) ? err->message : "(null)"); - pgm_error_free (err); - pgm_messages_shutdown(); - return EXIT_FAILURE; - } - -/* parse program arguments */ - const char* binary_name = strrchr (argv[0], '/'); - int c; - while ((c = getopt (argc, argv, "s:n:h")) != -1) - { - switch (c) { - case 'n': g_network = optarg; break; - case 's': g_port = atoi (optarg); break; - - case 'h': - case '?': - pgm_messages_shutdown(); - usage (binary_name); - } - } - - g_loop = g_main_loop_new (NULL, FALSE); - -/* setup signal handlers */ - signal (SIGSEGV, on_sigsegv); - signal (SIGHUP, SIG_IGN); - pgm_signal_install (SIGINT, on_signal, g_loop); - pgm_signal_install (SIGTERM, on_signal, g_loop); - -/* delayed startup */ - g_message ("scheduling startup."); - g_timeout_add (0, (GSourceFunc)on_startup, NULL); - -/* dispatch loop */ - g_message ("entering main event loop ... "); - g_main_loop_run (g_loop); - - g_message ("event loop terminated, cleaning up."); - -/* cleanup */ - g_main_loop_unref(g_loop); - g_loop = NULL; - - if (g_sessions) { - g_message ("destroying sessions."); - while (g_sessions_list) { - destroy_session (g_sessions_list->data); - g_sessions_list = g_list_delete_link (g_sessions_list, g_sessions_list); - } - g_hash_table_unref (g_sessions); - g_sessions = NULL; - } - - if (g_stdin_channel) { - puts ("unbinding stdin."); - g_io_channel_unref (g_stdin_channel); - g_stdin_channel = NULL; - } - - g_message ("PGM engine shutdown."); - pgm_shutdown(); - g_message ("finished."); - pgm_messages_shutdown(); - return EXIT_SUCCESS; -} - -static -void -destroy_session ( - struct sim_session* sess - ) -{ - printf ("destroying transport \"%s\"\n", sess->name); - pgm_transport_destroy (sess->transport, TRUE); - sess->transport = NULL; - g_free (sess->name); - sess->name = NULL; - g_free (sess); -} - -static -void -on_signal ( - int signum, - gpointer user_data - ) -{ - GMainLoop* loop = (GMainLoop*)user_data; - g_message ("on_signal (signum:%d user-data:%p)", signum, user_data); - g_main_loop_quit (loop); -} - -static -gboolean -on_startup ( - G_GNUC_UNUSED gpointer data - ) -{ - g_message ("startup."); - - g_sessions = g_hash_table_new (g_str_hash, g_str_equal); - -/* add stdin to event manager */ - g_stdin_channel = g_io_channel_unix_new (fileno(stdin)); - printf ("binding stdin with encoding %s.\n", g_io_channel_get_encoding(g_stdin_channel)); - - g_io_add_watch (g_stdin_channel, G_IO_IN | G_IO_PRI, on_stdin_data, NULL); - -/* period timer to indicate some form of life */ -// TODO: Gnome 2.14: replace with g_timeout_add_seconds() - g_timeout_add(10 * 1000, (GSourceFunc)on_mark, NULL); - - puts ("READY"); - fflush (stdout); - return FALSE; -} - -static -bool -fake_pgm_transport_create ( - pgm_transport_t** transport, - struct pgm_transport_info_t* tinfo, - G_GNUC_UNUSED pgm_error_t** error - ) -{ - pgm_transport_t* new_transport; - - g_return_val_if_fail (NULL != transport, FALSE); - g_return_val_if_fail (NULL != tinfo, FALSE); - if (tinfo->ti_sport) g_return_val_if_fail (tinfo->ti_sport != tinfo->ti_dport, FALSE); - if (tinfo->ti_udp_encap_ucast_port) - g_return_val_if_fail (tinfo->ti_udp_encap_mcast_port, FALSE); - else if (tinfo->ti_udp_encap_mcast_port) - g_return_val_if_fail (tinfo->ti_udp_encap_ucast_port, FALSE); - g_return_val_if_fail (tinfo->ti_recv_addrs_len > 0, FALSE); - g_return_val_if_fail (tinfo->ti_recv_addrs_len <= IP_MAX_MEMBERSHIPS, FALSE); - g_return_val_if_fail (NULL != tinfo->ti_recv_addrs, FALSE); - g_return_val_if_fail (1 == tinfo->ti_send_addrs_len, FALSE); - g_return_val_if_fail (NULL != tinfo->ti_send_addrs, FALSE); - for (unsigned i = 0; i < tinfo->ti_recv_addrs_len; i++) - { - g_return_val_if_fail (tinfo->ti_recv_addrs[i].gsr_group.ss_family == tinfo->ti_recv_addrs[0].gsr_group.ss_family, -FALSE); - g_return_val_if_fail (tinfo->ti_recv_addrs[i].gsr_group.ss_family == tinfo->ti_recv_addrs[i].gsr_source.ss_family, -FALSE); - } - g_return_val_if_fail (tinfo->ti_send_addrs[0].gsr_group.ss_family == tinfo->ti_send_addrs[0].gsr_source.ss_family, -FALSE); - -/* create transport object */ - new_transport = g_new0 (pgm_transport_t, 1); - -/* transport defaults */ - new_transport->can_send_data = TRUE; - new_transport->can_send_nak = FALSE; - new_transport->can_recv_data = TRUE; - - memcpy (&new_transport->tsi.gsi, &tinfo->ti_gsi, sizeof(pgm_gsi_t)); - new_transport->dport = g_htons (tinfo->ti_dport); - if (tinfo->ti_sport) { - new_transport->tsi.sport = tinfo->ti_sport; - } else { - do { - new_transport->tsi.sport = g_htons (g_random_int_range (0, UINT16_MAX)); - } while (new_transport->tsi.sport == new_transport->dport); - } - -/* network data ports */ - new_transport->udp_encap_ucast_port = tinfo->ti_udp_encap_ucast_port; - new_transport->udp_encap_mcast_port = tinfo->ti_udp_encap_mcast_port; - -/* copy network parameters */ - memcpy (&new_transport->send_gsr, &tinfo->ti_send_addrs[0], sizeof(struct group_source_req)); - for (unsigned i = 0; i < tinfo->ti_recv_addrs_len; i++) - { - memcpy (&new_transport->recv_gsr[i], &tinfo->ti_recv_addrs[i], sizeof(struct group_source_req)); - ((struct sockaddr_in*)&new_transport->recv_gsr[i].gsr_group)->sin_port = g_htons (new_transport->udp_encap_mcast_port); - } - new_transport->recv_gsr_len = tinfo->ti_recv_addrs_len; - -/* open sockets to implement PGM */ - int socket_type, protocol; - if (new_transport->udp_encap_ucast_port) { - puts ("opening UDP encapsulated sockets."); - socket_type = SOCK_DGRAM; - protocol = IPPROTO_UDP; - } else { - puts ("opening raw sockets."); - socket_type = SOCK_RAW; - protocol = IPPROTO_PGM; - } - - if ((new_transport->recv_sock = socket (new_transport->recv_gsr[0].gsr_group.ss_family, - socket_type, - protocol)) < 0) - { - if (errno == EPERM && 0 != getuid()) { - g_critical ("PGM protocol requires this program to run as superuser."); - } - goto err_destroy; - } - - if ((new_transport->send_sock = socket (new_transport->send_gsr.gsr_group.ss_family, - socket_type, - protocol)) < 0) - { - goto err_destroy; - } - - if ((new_transport->send_with_router_alert_sock = socket (new_transport->send_gsr.gsr_group.ss_family, - socket_type, - protocol)) < 0) - { - goto err_destroy; - } - - *transport = new_transport; - return TRUE; - -err_destroy: - if (new_transport->recv_sock) { - close(new_transport->recv_sock); - new_transport->recv_sock = 0; - } - if (new_transport->send_sock) { - close(new_transport->send_sock); - new_transport->send_sock = 0; - } - if (new_transport->send_with_router_alert_sock) { - close(new_transport->send_with_router_alert_sock); - new_transport->send_with_router_alert_sock = 0; - } - - g_free (new_transport); - new_transport = NULL; - return FALSE; -} - -static -gboolean -on_io_data ( - GIOChannel* source, - G_GNUC_UNUSED GIOCondition condition, - gpointer data - ) -{ - pgm_transport_t* transport = data; - - struct pgm_sk_buff_t* skb = pgm_alloc_skb (transport->max_tpdu); - int fd = g_io_channel_unix_get_fd(source); - struct sockaddr_storage src_addr; - socklen_t src_addr_len = sizeof(src_addr); - skb->len = recvfrom(fd, skb->head, transport->max_tpdu, MSG_DONTWAIT, (struct sockaddr*)&src_addr, &src_addr_len); - - printf ("%i bytes received from %s.\n", skb->len, inet_ntoa(((struct sockaddr_in*)&src_addr)->sin_addr)); - - monitor_packet (skb->data, skb->len); - fflush (stdout); - -/* parse packet to maintain peer database */ - if (transport->udp_encap_ucast_port) { - if (!pgm_parse_udp_encap (skb, NULL)) - goto out; - } else { - struct sockaddr_storage addr; - if (!pgm_parse_raw (skb, (struct sockaddr*)&addr, NULL)) - goto out; - } - - if (pgm_is_upstream (skb->pgm_header->pgm_type) || - pgm_is_peer (skb->pgm_header->pgm_type)) - goto out; /* ignore */ - -/* downstream = source to receivers */ - if (!pgm_is_downstream (skb->pgm_header->pgm_type)) - goto out; - -/* pgm packet DPORT contains our transport DPORT */ - if (skb->pgm_header->pgm_dport != transport->dport) - goto out; - -/* search for TSI peer context or create a new one */ - pgm_peer_t* sender = pgm_hashtable_lookup (transport->peers_hashtable, &skb->tsi); - if (sender == NULL) - { - printf ("new peer, tsi %s, local nla %s\n", - pgm_tsi_print (&skb->tsi), - inet_ntoa(((struct sockaddr_in*)&src_addr)->sin_addr)); - - pgm_peer_t* peer = g_new0 (pgm_peer_t, 1); - peer->transport = transport; - memcpy (&peer->tsi, &skb->tsi, sizeof(pgm_tsi_t)); - ((struct sockaddr_in*)&peer->nla)->sin_addr.s_addr = INADDR_ANY; - memcpy (&peer->local_nla, &src_addr, src_addr_len); - - pgm_hashtable_insert (transport->peers_hashtable, &peer->tsi, peer); - sender = peer; - } - -/* handle SPMs for advertised NLA */ - if (skb->pgm_header->pgm_type == PGM_SPM) - { - char *pgm_data = (char*)(skb->pgm_header + 1); - struct pgm_spm* spm = (struct pgm_spm*)pgm_data; - guint32 spm_sqn = g_ntohl (spm->spm_sqn); - - if ( pgm_uint32_gte (spm_sqn, sender->spm_sqn) - || ( ((struct sockaddr*)&sender->nla)->sa_family == 0 ) ) - { - pgm_nla_to_sockaddr (&spm->spm_nla_afi, (struct sockaddr*)&sender->nla); - sender->spm_sqn = spm_sqn; - } - } - -out: - return TRUE; -} - -static -bool -fake_pgm_transport_bind ( - pgm_transport_t* transport, - G_GNUC_UNUSED pgm_error_t** error - ) -{ - g_return_val_if_fail (NULL != transport, FALSE); - g_return_val_if_fail (!transport->is_bound, FALSE); - -/* create peer list */ - transport->peers_hashtable = pgm_hashtable_new (pgm_tsi_hash, pgm_tsi_equal); - -/* bind udp unicast sockets to interfaces, note multicast on a bound interface is - * fruity on some platforms so callee should specify any interface. - * - * after binding default interfaces (0.0.0.0) are resolved - */ - struct sockaddr_storage recv_addr; - memset (&recv_addr, 0, sizeof(recv_addr)); - ((struct sockaddr*)&recv_addr)->sa_family = AF_INET; - ((struct sockaddr_in*)&recv_addr)->sin_port = transport->udp_encap_ucast_port; - ((struct sockaddr_in*)&recv_addr)->sin_addr.s_addr = INADDR_ANY; - - int retval = bind (transport->recv_sock, - (struct sockaddr*)&recv_addr, - pgm_sockaddr_len((struct sockaddr*)&recv_addr)); - if (retval < 0) { - goto out; - } - - struct sockaddr_storage send_addr, send_with_router_alert_addr; - memset (&send_addr, 0, sizeof(send_addr)); - if (!pgm_if_indextoaddr (transport->send_gsr.gsr_interface, - transport->send_gsr.gsr_group.ss_family, - pgm_sockaddr_scope_id((struct sockaddr*)&transport->send_gsr.gsr_group), - (struct sockaddr*)&send_addr, - NULL)) - { - goto out; - } - memcpy (&send_with_router_alert_addr, &send_addr, pgm_sockaddr_len((struct sockaddr*)&send_addr)); - retval = bind (transport->send_sock, - (struct sockaddr*)&send_addr, - pgm_sockaddr_len((struct sockaddr*)&send_addr)); - if (retval < 0) - goto out; - -/* resolve bound address if 0.0.0.0 */ - if (((struct sockaddr_in*)&send_addr)->sin_addr.s_addr == INADDR_ANY) - { - if (!pgm_if_getnodeaddr (AF_INET, (struct sockaddr*)&send_addr, sizeof(send_addr), NULL)) - goto out; - } - - retval = bind (transport->send_with_router_alert_sock, - (struct sockaddr*)&send_with_router_alert_addr, - pgm_sockaddr_len((struct sockaddr*)&send_with_router_alert_addr)); - if (retval < 0) - goto out; - - memcpy (&transport->send_addr, &send_addr, pgm_sockaddr_len((struct sockaddr*)&send_addr)); - -/* receiving groups (multiple) */ - for (unsigned i = 0; i < transport->recv_gsr_len; i++) - { - struct group_source_req* p = &transport->recv_gsr[i]; - int optname = (pgm_sockaddr_cmp ((struct sockaddr*)&p->gsr_group, (struct sockaddr*)&p->gsr_source) == 0) - ? MCAST_JOIN_GROUP : MCAST_JOIN_SOURCE_GROUP; - socklen_t plen = MCAST_JOIN_GROUP == optname ? sizeof(struct group_req) : sizeof(struct group_source_req); - retval = setsockopt(transport->recv_sock, SOL_IP, optname, p, plen); - if (retval < 0) - goto out; - } - -/* send group (singular) */ - retval = pgm_sockaddr_multicast_if (transport->send_sock, (struct sockaddr*)&transport->send_addr, transport->send_gsr.gsr_interface); - if (retval < 0) - goto out; - - retval = pgm_sockaddr_multicast_if (transport->send_with_router_alert_sock, (struct sockaddr*)&transport->send_addr, transport->send_gsr.gsr_interface); - if (retval < 0) - goto out; - -/* multicast loopback */ - retval = pgm_sockaddr_multicast_loop (transport->recv_sock, transport->recv_gsr[0].gsr_group.ss_family, FALSE); - if (retval < 0) - goto out; - retval = pgm_sockaddr_multicast_loop (transport->send_sock, transport->send_gsr.gsr_group.ss_family, FALSE); - if (retval < 0) - goto out; - retval = pgm_sockaddr_multicast_loop (transport->send_with_router_alert_sock, transport->send_gsr.gsr_group.ss_family, FALSE); - if (retval < 0) - goto out; - -/* multicast ttl: many crappy network devices go CPU ape with TTL=1, 16 is a popular alternative */ - retval = pgm_sockaddr_multicast_hops (transport->recv_sock, transport->recv_gsr[0].gsr_group.ss_family, transport->hops); - if (retval < 0) - goto out; - retval = pgm_sockaddr_multicast_hops (transport->send_sock, transport->send_gsr.gsr_group.ss_family, transport->hops); - if (retval < 0) - goto out; - retval = pgm_sockaddr_multicast_hops (transport->send_with_router_alert_sock, transport->send_gsr.gsr_group.ss_family, transport->hops); - if (retval < 0) - goto out; - -/* set Expedited Forwarding PHB for network elements, no ECN. - * - * codepoint 101110 (RFC 3246) - */ - int dscp = 0x2e << 2; - retval = pgm_sockaddr_tos (transport->send_sock, transport->send_gsr.gsr_group.ss_family, dscp); - if (retval < 0) - goto out; - retval = pgm_sockaddr_tos (transport->send_with_router_alert_sock, transport->send_gsr.gsr_group.ss_family, dscp); - if (retval < 0) - goto out; - -/* cleanup */ - transport->is_bound = TRUE; - return TRUE; - -out: - return FALSE; -} - -static -bool -fake_pgm_transport_destroy ( - pgm_transport_t* transport, - G_GNUC_UNUSED bool flush - ) -{ - g_return_val_if_fail (transport != NULL, FALSE); - - if (transport->recv_sock) { - puts ("closing receive socket."); - close(transport->recv_sock); - transport->recv_sock = 0; - } - if (transport->send_sock) { - puts ("closing send socket."); - close(transport->send_sock); - transport->send_sock = 0; - } - if (transport->send_with_router_alert_sock) { - puts ("closing send with router alert socket."); - close(transport->send_with_router_alert_sock); - transport->send_with_router_alert_sock = 0; - } - g_free (transport); - return TRUE; -} - -static -void -session_create ( - char* name, - gboolean is_fake - ) -{ - struct pgm_transport_info_t hints = { - .ti_family = AF_INET - }, *res = NULL; - pgm_error_t* err = NULL; - gboolean status; - -/* check for duplicate */ - struct sim_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess != NULL) { - puts ("FAILED: duplicate session"); - return; - } - -/* create new and fill in bits */ - sess = g_new0(struct sim_session, 1); - sess->name = g_memdup (name, strlen(name)+1); - - if (!pgm_if_get_transport_info (g_network, &hints, &res, &err)) { - printf ("FAILED: pgm_if_get_transport_info(): %s\n", (err && err->message) ? err->message : "(null)"); - pgm_error_free (err); - goto err_free; - } - - if (!pgm_gsi_create_from_hostname (&res->ti_gsi, &err)) { - printf ("FAILED: pgm_gsi_create_from_hostname(): %s\n", (err && err->message) ? err->message : "(null)"); - pgm_error_free (err); - pgm_if_free_transport_info (res); - goto err_free; - } - - res->ti_dport = g_port; - res->ti_sport = 0; - if (is_fake) { - sess->is_transport_fake = TRUE; - status = fake_pgm_transport_create (&sess->transport, res, &err); - } else - status = pgm_transport_create (&sess->transport, res, &err); - if (!status) { - printf ("FAILED: pgm_transport_create(): %s\n", (err && err->message) ? err->message : "(null)"); - pgm_error_free (err); - pgm_if_free_transport_info (res); - goto err_free; - } - - pgm_if_free_transport_info (res); - -/* success */ - g_hash_table_insert (g_sessions, sess->name, sess); - g_sessions_list = g_list_prepend (g_sessions_list, sess); - printf ("created new session \"%s\"\n", sess->name); - puts ("READY"); - return; - -err_free: - g_free(sess->name); - g_free(sess); -} - -static -void -session_set_fec ( - char* name, - guint default_n, - guint default_k - ) -{ -/* check that session exists */ - struct sim_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess == NULL) { - puts ("FAILED: session not found"); - return; - } - - pgm_transport_set_fec (sess->transport, FALSE /* pro-active */, TRUE /* on-demand */, TRUE /* varpkt-len */, default_n, default_k); - puts ("READY"); -} - -static -void -session_bind ( - char* name - ) -{ -/* check that session exists */ - struct sim_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess == NULL) { - puts ("FAILED: session not found"); - return; - } - - pgm_transport_set_nonblocking (sess->transport, TRUE); - pgm_sockaddr_nonblocking (sess->transport->send_sock, FALSE); - pgm_transport_set_max_tpdu (sess->transport, g_max_tpdu); - pgm_transport_set_txw_sqns (sess->transport, g_sqns); - pgm_transport_set_rxw_sqns (sess->transport, g_sqns); - pgm_transport_set_hops (sess->transport, 16); - pgm_transport_set_ambient_spm (sess->transport, pgm_secs(30)); - guint spm_heartbeat[] = { pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(1300), pgm_secs(7), pgm_secs(16), pgm_secs(25), pgm_secs(30) }; - pgm_transport_set_heartbeat_spm (sess->transport, spm_heartbeat, G_N_ELEMENTS(spm_heartbeat)); - pgm_transport_set_peer_expiry (sess->transport, pgm_secs(300)); - pgm_transport_set_spmr_expiry (sess->transport, pgm_msecs(250)); - pgm_transport_set_nak_bo_ivl (sess->transport, pgm_msecs(50)); - pgm_transport_set_nak_rpt_ivl (sess->transport, pgm_secs(2)); - pgm_transport_set_nak_rdata_ivl (sess->transport, pgm_secs(2)); - pgm_transport_set_nak_data_retries (sess->transport, 50); - pgm_transport_set_nak_ncf_retries (sess->transport, 50); - - pgm_error_t* err = NULL; - gboolean status; - if (sess->is_transport_fake) - status = fake_pgm_transport_bind (sess->transport, &err); - else - status = pgm_transport_bind (sess->transport, &err); - if (!status) { - printf ("FAILED: pgm_transport_bind(): %s\n", err->message); - pgm_error_free (err); - return; - } - - if (sess->is_transport_fake) - { -/* add receive socket(s) to event manager */ - sess->recv_channel = g_io_channel_unix_new (sess->transport->recv_sock); - - GSource *source; - source = g_io_create_watch (sess->recv_channel, G_IO_IN); - g_source_set_callback (source, (GSourceFunc)on_io_data, sess->transport, NULL); - g_source_attach (source, NULL); - g_source_unref (source); - } - else - { - pgm_async_create (&sess->async, sess->transport, 0); - pgm_async_add_watch (sess->async, on_data, sess); - } - - puts ("READY"); -} - -static inline -gssize -pgm_sendto ( - pgm_transport_t* transport, - gboolean rl, - gboolean ra, - const void* buf, - gsize len, - const struct sockaddr* to, - socklen_t tolen - ) -{ - int sock = ra ? transport->send_with_router_alert_sock : transport->send_sock; - pgm_mutex_lock (&transport->send_mutex); - ssize_t sent = sendto (sock, buf, len, 0, to, tolen); - pgm_mutex_unlock (&transport->send_mutex); - return sent > 0 ? (gssize)len : (gssize)sent; -} - -static -int -pgm_reset_heartbeat_spm (pgm_transport_t* transport) -{ - int retval = 0; - - pgm_mutex_lock (&transport->timer_mutex); - -/* re-set spm timer */ - transport->spm_heartbeat_state = 1; - transport->next_heartbeat_spm = pgm_time_update_now() + transport->spm_heartbeat_interval[transport->spm_heartbeat_state++]; - -/* prod timer thread if sleeping */ - if (pgm_time_after( transport->next_poll, transport->next_heartbeat_spm )) - transport->next_poll = transport->next_heartbeat_spm; - - pgm_mutex_unlock (&transport->timer_mutex); - - return retval; -} - -static inline -int -brokn_send_apdu_unlocked ( - pgm_transport_t* transport, - const gchar* buf, - gsize count, - gsize* bytes_written - ) -{ - guint32 opt_sqn = pgm_txw_next_lead(transport->window); - guint packets = 0; - guint bytes_sent = 0; - guint data_bytes_sent = 0; - - pgm_mutex_lock (&transport->source_mutex); - - do { -/* retrieve packet storage from transmit window */ - int header_length = sizeof(struct pgm_header) + sizeof(struct pgm_data) + - sizeof(struct pgm_opt_length) + /* includes header */ - sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_fragment); - int tsdu_length = MIN(transport->max_tpdu - transport->iphdr_len - header_length, count - data_bytes_sent); - int tpdu_length = header_length + tsdu_length; - - struct pgm_sk_buff_t* skb = pgm_alloc_skb (tsdu_length); - pgm_skb_put (skb, tpdu_length); - - skb->pgm_header = (struct pgm_header*)skb->data; - memcpy (skb->pgm_header->pgm_gsi, &transport->tsi.gsi, sizeof(pgm_gsi_t)); - skb->pgm_header->pgm_sport = transport->tsi.sport; - skb->pgm_header->pgm_dport = transport->dport; - skb->pgm_header->pgm_type = PGM_ODATA; - skb->pgm_header->pgm_options = PGM_OPT_PRESENT; - skb->pgm_header->pgm_tsdu_length = g_htons (tsdu_length); - -/* ODATA */ - skb->pgm_data = (struct pgm_data*)(skb->pgm_header + 1); - skb->pgm_data->data_sqn = g_htonl (pgm_txw_next_lead(transport->window)); - skb->pgm_data->data_trail = g_htonl (pgm_txw_trail(transport->window)); - -/* OPT_LENGTH */ - struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(skb->pgm_data + 1); - opt_len->opt_type = PGM_OPT_LENGTH; - opt_len->opt_length = sizeof(struct pgm_opt_length); - opt_len->opt_total_length = g_htons ( sizeof(struct pgm_opt_length) + - sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_fragment) ); -/* OPT_FRAGMENT */ - struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); - opt_header->opt_type = PGM_OPT_FRAGMENT | PGM_OPT_END; - opt_header->opt_length = sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_fragment); - skb->pgm_opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); - skb->pgm_opt_fragment->opt_reserved = 0; - skb->pgm_opt_fragment->opt_sqn = g_htonl (opt_sqn); - skb->pgm_opt_fragment->opt_frag_off = g_htonl (data_bytes_sent); - skb->pgm_opt_fragment->opt_frag_len = g_htonl (count); - -/* TODO: the assembly checksum & copy routine is faster than memcpy & pgm_cksum on >= opteron hardware */ - skb->pgm_header->pgm_checksum = 0; - - int pgm_header_len = (char*)(skb->pgm_opt_fragment + 1) - (char*)skb->pgm_header; - guint32 unfolded_header = pgm_csum_partial ((const void*)skb->pgm_header, pgm_header_len, 0); - guint32 unfolded_odata = pgm_csum_partial_copy ((const void*)(buf + data_bytes_sent), (void*)(skb->pgm_opt_fragment + 1), tsdu_length, 0); - skb->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, unfolded_odata, pgm_header_len)); - -/* add to transmit window */ - pgm_spinlock_lock (&transport->txw_spinlock); - pgm_txw_add (transport->window, skb); - pgm_spinlock_unlock (&transport->txw_spinlock); - -/* do not send send packet */ - if (packets != 1) - pgm_sendto (transport, - TRUE, - FALSE, - skb->data, - tpdu_length, - (struct sockaddr*)&transport->send_gsr.gsr_group, - pgm_sockaddr_len((struct sockaddr*)&transport->send_gsr.gsr_group)); - -/* save unfolded odata for retransmissions */ - *(guint32*)&skb->cb = unfolded_odata; - - packets++; - bytes_sent += tpdu_length + transport->iphdr_len; - data_bytes_sent += tsdu_length; - - } while (data_bytes_sent < count); - - if (data_bytes_sent > 0 && bytes_written) - *bytes_written = data_bytes_sent; - -/* release txw lock here in order to allow spms to lock mutex */ - pgm_mutex_unlock (&transport->source_mutex); - pgm_reset_heartbeat_spm (transport); - return PGM_IO_STATUS_NORMAL; -} - -static -int -brokn_send ( - pgm_transport_t* transport, - const gchar* data, - gsize len, - gsize* bytes_written - ) -{ - if ( len <= ( transport->max_tpdu - ( sizeof(struct pgm_header) + - sizeof(struct pgm_data) ) ) ) - { - puts ("FAILED: cannot send brokn single TPDU length APDU"); - return PGM_IO_STATUS_ERROR; - } - - return brokn_send_apdu_unlocked (transport, data, len, bytes_written); -} - -static -void -session_send ( - char* name, - char* string, - gboolean is_brokn /* send broken apdu */ - ) -{ -/* check that session exists */ - struct sim_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess == NULL) { - puts ("FAILED: session not found"); - return; - } - -/* send message */ - int status; - gsize stringlen = strlen(string) + 1; - int n_fds = 1; - struct pollfd fds[ n_fds ]; - struct timeval tv; - int timeout; -again: - if (is_brokn) - status = brokn_send (sess->transport, string, stringlen, NULL); - else - status = pgm_send (sess->transport, string, stringlen, NULL); - switch (status) { - case PGM_IO_STATUS_NORMAL: - puts ("READY"); - break; - case PGM_IO_STATUS_TIMER_PENDING: - pgm_transport_get_timer_pending (sess->transport, &tv); - goto block; - case PGM_IO_STATUS_RATE_LIMITED: - pgm_transport_get_rate_remaining (sess->transport, &tv); -/* fall through */ - case PGM_IO_STATUS_WOULD_BLOCK: -block: - timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); - memset (fds, 0, sizeof(fds)); - pgm_transport_poll_info (sess->transport, fds, &n_fds, POLLOUT); - poll (fds, n_fds, timeout /* ms */); - goto again; - default: - puts ("FAILED: pgm_send()"); - break; - } -} - -static -void -session_destroy ( - char* name - ) -{ -/* check that session exists */ - struct sim_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess == NULL) { - puts ("FAILED: session not found"); - return; - } - -/* remove from hash table */ - g_hash_table_remove (g_sessions, name); - -/* close down receive side first to stop new data incoming */ - if (sess->recv_channel) { - puts ("closing receive channel."); - - GError *err = NULL; - g_io_channel_shutdown (sess->recv_channel, TRUE, &err); - - if (err) { - g_warning ("i/o shutdown error %i %s", err->code, err->message); - } - -/* TODO: flush GLib main loop with context specific to the recv channel */ - - sess->recv_channel = NULL; - } - - if (sess->is_transport_fake) - { - fake_pgm_transport_destroy (sess->transport, TRUE); - } - else - { - pgm_transport_destroy (sess->transport, TRUE); - } - sess->transport = NULL; - g_free (sess->name); - sess->name = NULL; - g_free (sess); - - puts ("READY"); -} - -static -void -net_send_data ( - char* name, - guint8 pgm_type, /* PGM_ODATA or PGM_RDATA */ - guint32 data_sqn, - guint32 txw_trail, - char* string - ) -{ -/* check that session exists */ - struct sim_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess == NULL) { - puts ("FAILED: session not found"); - return; - } - - pgm_transport_t* transport = sess->transport; - -/* payload is string including terminating null. */ - int count = strlen(string) + 1; - -/* send */ - int retval = 0; - int tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_data) + count; - - gchar buf[ tpdu_length ]; - - struct pgm_header *header = (struct pgm_header*)buf; - struct pgm_data *data = (struct pgm_data*)(header + 1); - memcpy (header->pgm_gsi, &transport->tsi.gsi, sizeof(pgm_gsi_t)); - header->pgm_sport = transport->tsi.sport; - header->pgm_dport = transport->dport; - header->pgm_type = pgm_type; - header->pgm_options = 0; - header->pgm_tsdu_length = g_htons (count); - -/* O/RDATA */ - data->data_sqn = g_htonl (data_sqn); - data->data_trail = g_htonl (txw_trail); - - memcpy (data + 1, string, count); - - header->pgm_checksum = 0; - header->pgm_checksum = pgm_csum_fold (pgm_csum_partial ((char*)header, tpdu_length, 0)); - - pgm_mutex_lock (&transport->send_mutex); - retval = sendto (transport->send_sock, - header, - tpdu_length, - 0, /* not expecting a reply */ - (struct sockaddr*)&transport->send_gsr.gsr_group, - pgm_sockaddr_len((struct sockaddr*)&transport->send_gsr.gsr_group)); - pgm_mutex_unlock (&transport->send_mutex); - - puts ("READY"); -} - -/* differs to net_send_data in that the string parameters contains every payload - * for the transmission group. this is required to calculate the correct parity - * as the fake transport does not own a transmission window. - * - * all payloads must be the same length unless variable TSDU support is enabled. - */ -static -void -net_send_parity ( - char* name, - guint8 pgm_type, /* PGM_ODATA or PGM_RDATA */ - guint32 data_sqn, - guint32 txw_trail, - char* string - ) -{ -/* check that session exists */ - struct sim_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess == NULL) { - puts ("FAILED: session not found"); - return; - } - - pgm_transport_t* transport = sess->transport; - -/* split string into individual payloads */ - guint16 parity_length = 0; - gchar** src; - src = g_strsplit (string, " ", transport->rs_k); - -/* payload is string including terminating null. */ - parity_length = strlen(*src) + 1; - -/* check length of payload array */ - gboolean is_var_pktlen = FALSE; - guint i; - for (i = 0; src[i]; i++) - { - guint tsdu_length = strlen(src[i]) + 1; - if (tsdu_length != parity_length) { - is_var_pktlen = TRUE; - - if (tsdu_length > parity_length) - parity_length = tsdu_length; - } - } - - if ( i != transport->rs_k ) { - printf ("FAILED: payload array length %u, whilst rs_k is %u.\n", i, transport->rs_k); - return; - } - -/* add padding and append TSDU lengths */ - if (is_var_pktlen) - { - for (i = 0; src[i]; i++) - { - guint tsdu_length = strlen(src[i]) + 1; - gchar* new_string = g_new0 (gchar, parity_length + 2); - strncpy (new_string, src[i], parity_length); - *(guint16*)(new_string + parity_length) = tsdu_length; - g_free (src[i]); - src[i] = new_string; - } - parity_length += 2; - } - -/* calculate FEC block offset */ - guint32 tg_sqn_mask = 0xffffffff << transport->tg_sqn_shift; - guint rs_h = data_sqn & ~tg_sqn_mask; - -/* send */ - int retval = 0; - int tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_data) + parity_length; - - gchar buf[ tpdu_length ]; - - struct pgm_header *header = (struct pgm_header*)buf; - struct pgm_data *data = (struct pgm_data*)(header + 1); - memcpy (header->pgm_gsi, &transport->tsi.gsi, sizeof(pgm_gsi_t)); - header->pgm_sport = transport->tsi.sport; - header->pgm_dport = transport->dport; - header->pgm_type = pgm_type; - header->pgm_options = is_var_pktlen ? (PGM_OPT_PARITY | PGM_OPT_VAR_PKTLEN) : PGM_OPT_PARITY; - header->pgm_tsdu_length = g_htons (parity_length); - -/* O/RDATA */ - data->data_sqn = g_htonl (data_sqn); - data->data_trail = g_htonl (txw_trail); - - memset (data + 1, 0, parity_length); - pgm_rs_t rs; - pgm_rs_create (&rs, transport->rs_n, transport->rs_k); - pgm_rs_encode (&rs, (const pgm_gf8_t**)src, transport->rs_k + rs_h, (pgm_gf8_t*)(data + 1), parity_length); - pgm_rs_destroy (&rs); - - header->pgm_checksum = 0; - header->pgm_checksum = pgm_csum_fold (pgm_csum_partial ((char*)header, tpdu_length, 0)); - - pgm_mutex_lock (&transport->send_mutex); - retval = sendto (transport->send_sock, - header, - tpdu_length, - 0, /* not expecting a reply */ - (struct sockaddr*)&transport->send_gsr.gsr_group, - pgm_sockaddr_len((struct sockaddr*)&transport->send_gsr.gsr_group)); - pgm_mutex_unlock (&transport->send_mutex); - - g_strfreev (src); - src = NULL; - - puts ("READY"); -} - -static -void -net_send_spm ( - char* name, - guint32 spm_sqn, - guint32 txw_trail, - guint32 txw_lead, - gboolean proactive_parity, - gboolean ondemand_parity, - guint k - ) -{ -/* check that session exists */ - struct sim_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess == NULL) { - puts ("FAILED: session not found"); - return; - } - - pgm_transport_t* transport = sess->transport; - -/* send */ - int retval = 0; - int tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_spm); - - if (proactive_parity || ondemand_parity) { - tpdu_length += sizeof(struct pgm_opt_length) + - sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_parity_prm); - } - - gchar buf[ tpdu_length ]; - - struct pgm_header *header = (struct pgm_header*)buf; - struct pgm_spm *spm = (struct pgm_spm*)(header + 1); - memcpy (header->pgm_gsi, &transport->tsi.gsi, sizeof(pgm_gsi_t)); - header->pgm_sport = transport->tsi.sport; - header->pgm_dport = transport->dport; - header->pgm_type = PGM_SPM; - header->pgm_options = (proactive_parity || ondemand_parity) ? (PGM_OPT_PRESENT | PGM_OPT_NETWORK) : 0; - header->pgm_tsdu_length = 0; - -/* SPM */ - spm->spm_sqn = g_htonl (spm_sqn); - spm->spm_trail = g_htonl (txw_trail); - spm->spm_lead = g_htonl (txw_lead); - pgm_sockaddr_to_nla ((struct sockaddr*)&transport->send_addr, (char*)&spm->spm_nla_afi); - - if (proactive_parity || ondemand_parity) { - struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(spm + 1); - opt_len->opt_type = PGM_OPT_LENGTH; - opt_len->opt_length = sizeof(struct pgm_opt_length); - opt_len->opt_total_length = g_htons ( sizeof(struct pgm_opt_length) + - sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_parity_prm) ); - struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); - opt_header->opt_type = PGM_OPT_PARITY_PRM | PGM_OPT_END; - opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_parity_prm); - struct pgm_opt_parity_prm* opt_parity_prm = (struct pgm_opt_parity_prm*)(opt_header + 1); - opt_parity_prm->opt_reserved = (proactive_parity ? PGM_PARITY_PRM_PRO : 0) | - (ondemand_parity ? PGM_PARITY_PRM_OND : 0); - opt_parity_prm->parity_prm_tgs = g_htonl (k); - } - - header->pgm_checksum = 0; - header->pgm_checksum = pgm_csum_fold (pgm_csum_partial ((char*)header, tpdu_length, 0)); - - retval = sendto (transport->send_sock, - header, - tpdu_length, - 0, /* not expecting a reply */ - (struct sockaddr*)&transport->send_gsr.gsr_group, - pgm_sockaddr_len((struct sockaddr*)&transport->send_gsr.gsr_group)); - puts ("READY"); -} - -static -void -net_send_spmr ( - char* name, - pgm_tsi_t* tsi - ) -{ -/* check that session exists */ - struct sim_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess == NULL) { - puts ("FAILED: session not found"); - return; - } - - pgm_transport_t* transport = sess->transport; - -/* check that the peer exists */ - pgm_peer_t* peer = pgm_hashtable_lookup (transport->peers_hashtable, tsi); - struct sockaddr_storage peer_nla; - pgm_gsi_t* peer_gsi; - guint16 peer_sport; - - if (peer == NULL) { -/* ourself */ - if (pgm_tsi_equal (tsi, &transport->tsi)) - { - peer_gsi = &transport->tsi.gsi; - peer_sport = transport->tsi.sport; - } - else - { - printf ("FAILED: peer \"%s\" not found\n", pgm_tsi_print (tsi)); - return; - } - } - else - { - memcpy (&peer_nla, &peer->local_nla, sizeof(struct sockaddr_storage)); - peer_gsi = &peer->tsi.gsi; - peer_sport = peer->tsi.sport; - } - -/* send */ - int retval = 0; - int tpdu_length = sizeof(struct pgm_header); - gchar buf[ tpdu_length ]; - - struct pgm_header *header = (struct pgm_header*)buf; - memcpy (header->pgm_gsi, peer_gsi, sizeof(pgm_gsi_t)); - header->pgm_sport = transport->dport; - header->pgm_dport = peer_sport; - header->pgm_type = PGM_SPMR; - header->pgm_options = 0; - header->pgm_tsdu_length = 0; - header->pgm_checksum = 0; - header->pgm_checksum = pgm_csum_fold (pgm_csum_partial ((char*)header, tpdu_length, 0)); - - pgm_mutex_lock (&transport->send_mutex); -/* TTL 1 */ - pgm_sockaddr_multicast_hops (transport->send_sock, transport->send_gsr.gsr_group.ss_family, 1); - retval = sendto (transport->send_sock, - header, - tpdu_length, - 0, /* not expecting a reply */ - (struct sockaddr*)&transport->send_gsr.gsr_group, - pgm_sockaddr_len((struct sockaddr*)&transport->send_gsr.gsr_group)); -/* default TTL */ - pgm_sockaddr_multicast_hops (transport->send_sock, transport->send_gsr.gsr_group.ss_family, transport->hops); - - if (!pgm_tsi_equal (tsi, &transport->tsi)) - { - retval = sendto (transport->send_sock, - header, - tpdu_length, - 0, /* not expecting a reply */ - (struct sockaddr*)&peer_nla, - pgm_sockaddr_len((struct sockaddr*)&peer_nla)); - } - - pgm_mutex_unlock (&transport->send_mutex); - - puts ("READY"); -} - -/* Send a NAK on a valid transport. A fake transport would need to specify the senders NLA, - * we use the peer list to bypass extracting it from the monitor output. - */ - -static -void -net_send_ncf ( - char* name, - pgm_tsi_t* tsi, - struct pgm_sqn_list_t* sqn_list /* list of sequence numbers */ - ) -{ -/* check that session exists */ - struct sim_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess == NULL) { - puts ("FAILED: session not found"); - return; - } - -/* check that the peer exists */ - pgm_transport_t* transport = sess->transport; - pgm_peer_t* peer = pgm_hashtable_lookup (transport->peers_hashtable, tsi); - if (peer == NULL) { - printf ("FAILED: peer \"%s\" not found\n", pgm_tsi_print (tsi)); - return; - } - -/* check for valid nla */ - if (((struct sockaddr*)&peer->nla)->sa_family == 0 ) { - puts ("FAILED: peer NLA unknown, cannot send NCF."); - return; - } - -/* send */ - int retval = 0; - int tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_nak); - - if (sqn_list->len > 1) { - tpdu_length += sizeof(struct pgm_opt_length) + /* includes header */ - sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + - ( (sqn_list->len-1) * sizeof(guint32) ); - } - - gchar buf[ tpdu_length ]; - - struct pgm_header *header = (struct pgm_header*)buf; - struct pgm_nak *ncf = (struct pgm_nak*)(header + 1); - memcpy (header->pgm_gsi, &transport->tsi.gsi, sizeof(pgm_gsi_t)); - - struct sockaddr_storage peer_nla; - memcpy (&peer_nla, &peer->nla, sizeof(struct sockaddr_storage)); - -/* dport & sport swap over for a nak */ - header->pgm_sport = transport->tsi.sport; - header->pgm_dport = transport->dport; - header->pgm_type = PGM_NCF; - header->pgm_options = (sqn_list->len > 1) ? (PGM_OPT_PRESENT | PGM_OPT_NETWORK) : 0; - header->pgm_tsdu_length = 0; - -/* NCF */ - ncf->nak_sqn = g_htonl (sqn_list->sqn[0]); - -/* source nla */ - pgm_sockaddr_to_nla ((struct sockaddr*)&peer_nla, (char*)&ncf->nak_src_nla_afi); - -/* group nla */ - pgm_sockaddr_to_nla ((struct sockaddr*)&transport->recv_gsr[0].gsr_group, (char*)&ncf->nak_grp_nla_afi); - -/* OPT_NAK_LIST */ - if (sqn_list->len > 1) - { - struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(ncf + 1); - opt_len->opt_type = PGM_OPT_LENGTH; - opt_len->opt_length = sizeof(struct pgm_opt_length); - opt_len->opt_total_length = g_htons ( sizeof(struct pgm_opt_length) + - sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_nak_list) + - ( (sqn_list->len-1) * sizeof(guint32) ) ); - struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); - opt_header->opt_type = PGM_OPT_NAK_LIST | PGM_OPT_END; - opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) - + ( (sqn_list->len-1) * sizeof(guint32) ); - struct pgm_opt_nak_list* opt_nak_list = (struct pgm_opt_nak_list*)(opt_header + 1); - opt_nak_list->opt_reserved = 0; - for (guint i = 1; i < sqn_list->len; i++) { - opt_nak_list->opt_sqn[i-1] = g_htonl (sqn_list->sqn[i]); - } - } - - header->pgm_checksum = 0; - header->pgm_checksum = pgm_csum_fold (pgm_csum_partial ((char*)header, tpdu_length, 0)); - - retval = sendto (transport->send_with_router_alert_sock, - header, - tpdu_length, - 0, /* not expecting a reply */ - (struct sockaddr*)&transport->send_gsr.gsr_group, - pgm_sockaddr_len((struct sockaddr*)&transport->send_gsr.gsr_group)); - - puts ("READY"); -} - -static -void -net_send_nak ( - char* name, - pgm_tsi_t* tsi, - struct pgm_sqn_list_t* sqn_list, /* list of sequence numbers */ - gboolean is_parity /* TRUE = parity, FALSE = selective */ - ) -{ -/* check that session exists */ - struct sim_session* sess = g_hash_table_lookup (g_sessions, name); - if (sess == NULL) { - puts ("FAILED: session not found"); - return; - } - -/* check that the peer exists */ - pgm_transport_t* transport = sess->transport; - pgm_peer_t* peer = pgm_hashtable_lookup (transport->peers_hashtable, tsi); - if (peer == NULL) { - printf ("FAILED: peer \"%s\" not found\n", pgm_tsi_print(tsi)); - return; - } - -/* send */ - int retval = 0; - int tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_nak); - - if (sqn_list->len > 1) { - tpdu_length += sizeof(struct pgm_opt_length) + /* includes header */ - sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + - ( (sqn_list->len-1) * sizeof(guint32) ); - } - - gchar buf[ tpdu_length ]; - - struct pgm_header *header = (struct pgm_header*)buf; - struct pgm_nak *nak = (struct pgm_nak*)(header + 1); - memcpy (header->pgm_gsi, &peer->tsi.gsi, sizeof(pgm_gsi_t)); - - guint16 peer_sport = peer->tsi.sport; - struct sockaddr_storage peer_nla; - memcpy (&peer_nla, &peer->nla, sizeof(struct sockaddr_storage)); - -/* dport & sport swap over for a nak */ - header->pgm_sport = transport->dport; - header->pgm_dport = peer_sport; - header->pgm_type = PGM_NAK; - if (is_parity) { - header->pgm_options = (sqn_list->len > 1) ? (PGM_OPT_PRESENT | PGM_OPT_NETWORK | PGM_OPT_PARITY) - : PGM_OPT_PARITY; - } else { - header->pgm_options = (sqn_list->len > 1) ? (PGM_OPT_PRESENT | PGM_OPT_NETWORK) : 0; - } - header->pgm_tsdu_length = 0; - -/* NAK */ - nak->nak_sqn = g_htonl (sqn_list->sqn[0]); - -/* source nla */ - pgm_sockaddr_to_nla ((struct sockaddr*)&peer_nla, (char*)&nak->nak_src_nla_afi); - -/* group nla */ - pgm_sockaddr_to_nla ((struct sockaddr*)&transport->recv_gsr[0].gsr_group, (char*)&nak->nak_grp_nla_afi); - -/* OPT_NAK_LIST */ - if (sqn_list->len > 1) - { - struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(nak + 1); - opt_len->opt_type = PGM_OPT_LENGTH; - opt_len->opt_length = sizeof(struct pgm_opt_length); - opt_len->opt_total_length = g_htons ( sizeof(struct pgm_opt_length) + - sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_nak_list) + - ( (sqn_list->len-1) * sizeof(guint32) ) ); - struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); - opt_header->opt_type = PGM_OPT_NAK_LIST | PGM_OPT_END; - opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) - + ( (sqn_list->len-1) * sizeof(guint32) ); - struct pgm_opt_nak_list* opt_nak_list = (struct pgm_opt_nak_list*)(opt_header + 1); - opt_nak_list->opt_reserved = 0; - for (guint i = 1; i < sqn_list->len; i++) { - opt_nak_list->opt_sqn[i-1] = g_htonl (sqn_list->sqn[i]); - } - } - - header->pgm_checksum = 0; - header->pgm_checksum = pgm_csum_fold (pgm_csum_partial ((char*)header, tpdu_length, 0)); - - retval = sendto (transport->send_with_router_alert_sock, - header, - tpdu_length, - 0, /* not expecting a reply */ - (struct sockaddr*)&peer_nla, - pgm_sockaddr_len((struct sockaddr*)&peer_nla)); - - puts ("READY"); -} - -static -int -on_data ( - gpointer data, - G_GNUC_UNUSED guint len, - G_GNUC_UNUSED gpointer user_data - ) -{ - printf ("DATA: %s\n", (char*)data); - fflush (stdout); - - return 0; -} - -/* process input commands from stdin/fd - */ - -static -gboolean -on_stdin_data ( - GIOChannel* source, - G_GNUC_UNUSED GIOCondition condition, - G_GNUC_UNUSED gpointer data - ) -{ - gchar* str = NULL; - gsize len = 0; - gsize term = 0; - GError* err = NULL; - - g_io_channel_read_line (source, &str, &len, &term, &err); - if (len > 0) { - if (term) str[term] = 0; - -/* quit */ - if (strcmp(str, "quit") == 0) - { - g_main_loop_quit(g_loop); - goto out; - } - - regex_t preg; - regmatch_t pmatch[10]; - const char *re; - -/* endpoint simulator specific: */ - -/* send odata or rdata */ - re = "^net[[:space:]]+send[[:space:]]+([or])data[[:space:]]+" - "([[:alnum:]]+)[[:space:]]+" /* transport */ - "([0-9]+)[[:space:]]+" /* sequence number */ - "([0-9]+)[[:space:]]+" /* txw_trail */ - "([[:alnum:]]+)$"; /* payload */ - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - guint8 pgm_type = *(str + pmatch[1].rm_so) == 'o' ? PGM_ODATA : PGM_RDATA; - - char *name = g_memdup (str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so + 1 ); - name[ pmatch[2].rm_eo - pmatch[2].rm_so ] = 0; - - char* p = str + pmatch[3].rm_so; - guint32 data_sqn = strtoul (p, &p, 10); - - p = str + pmatch[4].rm_so; - guint txw_trail = strtoul (p, &p, 10); - - char *string = g_memdup (str + pmatch[5].rm_so, pmatch[5].rm_eo - pmatch[5].rm_so + 1 ); - string[ pmatch[5].rm_eo - pmatch[5].rm_so ] = 0; - - net_send_data (name, pgm_type, data_sqn, txw_trail, string); - - g_free (name); - g_free (string); - regfree (&preg); - goto out; - } - regfree (&preg); - -/* send parity odata or rdata */ - re = "^net[[:space:]]+send[[:space:]]+parity[[:space:]]+([or])data[[:space:]]+" - "([[:alnum:]]+)[[:space:]]+" /* transport */ - "([0-9]+)[[:space:]]+" /* sequence number */ - "([0-9]+)[[:space:]]+" /* txw_trail */ - "([a-z0-9 ]+)$"; /* payloads */ - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - guint8 pgm_type = *(str + pmatch[1].rm_so) == 'o' ? PGM_ODATA : PGM_RDATA; - - char *name = g_memdup (str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so + 1 ); - name[ pmatch[2].rm_eo - pmatch[2].rm_so ] = 0; - - char* p = str + pmatch[3].rm_so; - guint32 data_sqn = strtoul (p, &p, 10); - - p = str + pmatch[4].rm_so; - guint txw_trail = strtoul (p, &p, 10); - -/* ideally confirm number of payloads matches sess->transport::rs_k ... */ - char *string = g_memdup (str + pmatch[5].rm_so, pmatch[5].rm_eo - pmatch[5].rm_so + 1 ); - string[ pmatch[5].rm_eo - pmatch[5].rm_so ] = 0; - - net_send_parity (name, pgm_type, data_sqn, txw_trail, string); - - g_free (name); - g_free (string); - regfree (&preg); - goto out; - } - regfree (&preg); - -/* send spm */ - re = "^net[[:space:]]+send[[:space:]]+spm[[:space:]]+" - "([[:alnum:]]+)[[:space:]]+" /* transport */ - "([0-9]+)[[:space:]]+" /* spm sequence number */ - "([0-9]+)[[:space:]]+" /* txw_trail */ - "([0-9]+)" /* txw_lead */ - "([[:space:]]+pro-active)?" /* pro-active parity */ - "([[:space:]]+on-demand)?" /* on-demand parity */ - "([[:space:]]+[0-9]+)?$"; /* transmission group size */ - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); - name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; - - char* p = str + pmatch[2].rm_so; - guint32 spm_sqn = strtoul (p, &p, 10); - - p = str + pmatch[3].rm_so; - guint txw_trail = strtoul (p, &p, 10); - - p = str + pmatch[4].rm_so; - guint txw_lead = strtoul (p, &p, 10); - - gboolean proactive_parity = pmatch[5].rm_eo > pmatch[5].rm_so; - gboolean ondemand_parity = pmatch[6].rm_eo > pmatch[6].rm_so; - - p = str + pmatch[7].rm_so; - guint k = (pmatch[7].rm_eo > pmatch[7].rm_so) ? strtoul (p, &p, 10) : 0; - - net_send_spm (name, spm_sqn, txw_trail, txw_lead, proactive_parity, ondemand_parity, k); - - g_free (name); - regfree (&preg); - goto out; - } - regfree (&preg); - -/* send spmr */ - re = "^net[[:space:]]+send[[:space:]]+spmr[[:space:]]+" - "([[:alnum:]]+)[[:space:]]+" /* transport */ - "([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)$"; /* TSI */ - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); - name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; - - pgm_tsi_t tsi; - char *p = str + pmatch[2].rm_so; - tsi.gsi.identifier[0] = strtol (p, &p, 10); - ++p; - tsi.gsi.identifier[1] = strtol (p, &p, 10); - ++p; - tsi.gsi.identifier[2] = strtol (p, &p, 10); - ++p; - tsi.gsi.identifier[3] = strtol (p, &p, 10); - ++p; - tsi.gsi.identifier[4] = strtol (p, &p, 10); - ++p; - tsi.gsi.identifier[5] = strtol (p, &p, 10); - ++p; - tsi.sport = g_htons ( strtol (p, NULL, 10) ); - - net_send_spmr (name, &tsi); - - g_free (name); - regfree (&preg); - goto out; - } - regfree (&preg); - -/* send nak/ncf */ - re = "^net[[:space:]]+send[[:space:]](parity[[:space:]])?n(ak|cf)[[:space:]]+" - "([[:alnum:]]+)[[:space:]]+" /* transport */ - "([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)[[:space:]]+" /* TSI */ - "([0-9,]+)$"; /* sequence number or list */ - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char *name = g_memdup (str + pmatch[3].rm_so, pmatch[3].rm_eo - pmatch[3].rm_so + 1 ); - name[ pmatch[3].rm_eo - pmatch[3].rm_so ] = 0; - - pgm_tsi_t tsi; - char *p = str + pmatch[4].rm_so; - tsi.gsi.identifier[0] = strtol (p, &p, 10); - ++p; - tsi.gsi.identifier[1] = strtol (p, &p, 10); - ++p; - tsi.gsi.identifier[2] = strtol (p, &p, 10); - ++p; - tsi.gsi.identifier[3] = strtol (p, &p, 10); - ++p; - tsi.gsi.identifier[4] = strtol (p, &p, 10); - ++p; - tsi.gsi.identifier[5] = strtol (p, &p, 10); - ++p; - tsi.sport = g_htons ( strtol (p, NULL, 10) ); - -/* parse list of sequence numbers */ - struct pgm_sqn_list_t sqn_list; - sqn_list.len = 0; - { - char* saveptr = NULL; - for (p = str + pmatch[5].rm_so; ; p = NULL) { - char* token = strtok_r (p, ",", &saveptr); - if (!token) break; - sqn_list.sqn[sqn_list.len++] = strtoul (token, NULL, 10); - } - } - - if ( *(str + pmatch[2].rm_so) == 'a' ) - { - net_send_nak (name, &tsi, &sqn_list, (pmatch[1].rm_eo > pmatch[1].rm_so)); - } - else - { - net_send_ncf (name, &tsi, &sqn_list); - } - - g_free (name); - regfree (&preg); - goto out; - } - regfree (&preg); - -/** same as test application: **/ - -/* create transport */ - re = "^create[[:space:]]+(fake[[:space:]]+)?([[:alnum:]]+)$"; - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char *name = g_memdup (str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so + 1 ); - name[ pmatch[2].rm_eo - pmatch[2].rm_so ] = 0; - - session_create (name, (pmatch[1].rm_eo > pmatch[1].rm_so)); - - g_free (name); - regfree (&preg); - goto out; - } - regfree (&preg); - -/* enable Reed-Solomon Forward Error Correction */ - re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+FEC[[:space:]]+RS[[:space:]]*\\([[:space:]]*([0-9]+)[[:space:]]*,[[:space:]]*([0-9]+)[[:space:]]*\\)$"; - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); - name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; - - char *p = str + pmatch[2].rm_so; - *(str + pmatch[2].rm_eo) = 0; - guint n = strtol (p, &p, 10); - p = str + pmatch[3].rm_so; - *(str + pmatch[3].rm_eo) = 0; - guint k = strtol (p, &p, 10); - session_set_fec (name, n, k); - - g_free (name); - regfree (&preg); - goto out; - } - regfree (&preg); - -/* bind transport */ - re = "^bind[[:space:]]+([[:alnum:]]+)$"; - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); - name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; - - session_bind (name); - - g_free (name); - regfree (&preg); - goto out; - } - regfree (&preg); - -/* send packet */ - re = "^send[[:space:]]+([[:alnum:]]+)[[:space:]]+([[:alnum:]]+)$"; - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); - name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; - - char *string = g_memdup (str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so + 1 ); - string[ pmatch[2].rm_eo - pmatch[2].rm_so ] = 0; - - session_send (name, string, FALSE); - - g_free (name); - g_free (string); - regfree (&preg); - goto out; - } - regfree (&preg); - - re = "^send[[:space:]]+(brokn[[:space:]]+)?([[:alnum:]]+)[[:space:]]+([[:alnum:]]+)[[:space:]]+x[[:space:]]([0-9]+)$"; - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char *name = g_memdup (str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so + 1 ); - name[ pmatch[2].rm_eo - pmatch[2].rm_so ] = 0; - - char* p = str + pmatch[4].rm_so; - int factor = strtol (p, &p, 10); - int src_len = pmatch[3].rm_eo - pmatch[3].rm_so; - char *string = g_malloc ( (factor * src_len) + 1 ); - for (int i = 0; i < factor; i++) - { - memcpy (string + (i * src_len), str + pmatch[3].rm_so, src_len); - } - string[ factor * src_len ] = 0; - - session_send (name, string, (pmatch[1].rm_eo > pmatch[1].rm_so)); - - g_free (name); - g_free (string); - regfree (&preg); - goto out; - } - regfree (&preg); - -/* destroy transport */ - re = "^destroy[[:space:]]+([[:alnum:]]+)$"; - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); - name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; - - session_destroy (name); - - g_free (name); - regfree (&preg); - goto out; - } - regfree (&preg); - -/* set PGM network */ - re = "^set[[:space:]]+network[[:space:]]+([[:print:]]*;[[:print:]]+)$"; - regcomp (&preg, re, REG_EXTENDED); - if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) - { - char *pgm_network = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); - pgm_network[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; - g_network = pgm_network; - puts ("READY"); - - regfree (&preg); - goto out; - } - regfree (&preg); - - printf ("unknown command: %s\n", str); - } - -out: - fflush (stdout); - g_free (str); - return TRUE; -} - -/* idle log notification - */ - -static -gboolean -on_mark ( - G_GNUC_UNUSED gpointer data - ) -{ - g_message ("-- MARK --"); - return TRUE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/spm.pl b/3rdparty/openpgm-svn-r1085/pgm/test/spm.pl deleted file mode 100755 index c92b8fc..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/spm.pl +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/perl -# spm.pl -# 5.1.4. Ambient SPMs - -use strict; -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$app->connect; - -sub close_ssh { - $mon = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -print "app: ready.\n"; - -print "mon: wait for spm ...\n"; -$mon->wait_for_spm; -print "mon: received spm.\n"; - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/spm_jump.pl b/3rdparty/openpgm-svn-r1085/pgm/test/spm_jump.pl deleted file mode 100755 index 9b89720..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/spm_jump.pl +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/perl -# spm_jump.pl -# 6.2. Source Path Messages - -use strict; -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -$sim->say ("create fake ao"); -$sim->say ("bind ao"); - -print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; -$sim->say ("net send spm ao 3200 90001 90000"); - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "sim: publish SPM txw_trail 90,001 txw_lead 90,005 at spm_sqn 20.\n"; -$sim->say ("net send spm ao 20 90001 90005"); - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/spm_jump2.pl b/3rdparty/openpgm-svn-r1085/pgm/test/spm_jump2.pl deleted file mode 100755 index 4ad0562..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/spm_jump2.pl +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/perl -# spm_jump2.pl -# 6.3. Data Recovery by Negative Acknowledgment - -use strict; -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -$sim->say ("create fake ao"); -$sim->say ("bind ao"); - -print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; -$sim->say ("net send spm ao 3200 90001 90000"); - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "sim: publish SPM txw_trail 90,001 txw_lead 90,001 at spm_sqn 3201.\n"; -$sim->say ("net send spm ao 3201 90001 90001"); - -print "sim: waiting for valid NAK.\n"; -$sim->wait_for_nak; -print "sim: NAK received.\n"; - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/spm_reception.pl b/3rdparty/openpgm-svn-r1085/pgm/test/spm_reception.pl deleted file mode 100755 index 63ea43f..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/spm_reception.pl +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/perl -# spm_reception.pl -# 6.1. Data Reception - -use strict; -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -$sim->say ("create fake ao"); -$sim->say ("bind ao"); - -print "sim: publish SPM txw_trail 90,000.\n"; -$sim->say ("net send spm ao 1 90001 90000"); - -# no NAKs should be generated. -print "sim: waiting 2 seconds for erroneous NAKs ...\n"; -$sim->die_on_nak({ 'timeout' => 2 }); -print "sim: no NAKs received.\n"; - -print "sim: publish ODATA sqn 90,001.\n"; -$sim->say ("net send odata ao 90001 90000 ichigo"); -print "app: wait for data ...\n"; -my $data = $app->wait_for_data; -print "app: received data [$data].\n"; - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/spmr.pl b/3rdparty/openpgm-svn-r1085/pgm/test/spmr.pl deleted file mode 100755 index a22f14d..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/spmr.pl +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/perl -# spmr.pl -# 13.3.1. SPM Requests - -use strict; -use Time::HiRes qw( gettimeofday tv_interval ); -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$sim->say ("create fake ao"); -$sim->say ("bind ao"); -print "sim: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -## capture GSI of test spp -$app->say ("send ao nashi"); -print "mon: wait for odata ...\n"; -my $odata = $mon->wait_for_odata; -print "mon: odata received.\n"; -my $t0 = [gettimeofday]; -my $elapsed; - -$mon->disconnect (1); - -## spm hearbeats are going to clear out the data, lets wait for some quiet -print "sim: wait for SPM interval > 5 seconds ...\n"; -do { - $sim->wait_for_spm; - $elapsed = tv_interval ( $t0, [gettimeofday] ); - print "sim: received SPM after $elapsed seconds.\n"; -} while ($elapsed < 5); - -print "sim: request SPM via SPMR.\n"; -$sim->say ("net send spmr ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort}"); -$t0 = [gettimeofday]; - -print "sim: wait for SPM ...\n"; -$sim->wait_for_spm; -$elapsed = tv_interval ( $t0, [gettimeofday] ); -print "sim: SPM received after $elapsed seconds.\n"; -die "SPM interval too large, indicates heartbeat not SPMR induced.\n" unless ($elapsed < 5.0); - -print "test completed successfully.\n"; - -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/spmr_after_spm.pl b/3rdparty/openpgm-svn-r1085/pgm/test/spmr_after_spm.pl deleted file mode 100755 index a1ca5ee..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/spmr_after_spm.pl +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/perl -# spmr.pl -# 13.3.1. SPM Requests - -use strict; -use Time::HiRes qw( gettimeofday tv_interval ); -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -## capture GSI of test spp -$app->say ("send ao nashi"); -print "mon: wait for odata ...\n"; -my $odata = $mon->wait_for_odata; -print "mon: odata received.\n"; -my $t0 = [gettimeofday]; -my $elapsed; - -## spm hearbeats are going to clear out the data, lets wait for some quiet -print "mon: wait for SPM interval > 5 seconds ...\n"; -do { - $mon->wait_for_spm; - $elapsed = tv_interval ( $t0, [gettimeofday] ); - print "mon: received SPM after $elapsed seconds.\n"; -} while ($elapsed < 5); - -$sim->say ("create fake ao"); -$sim->say ("bind ao"); -print "sim: ready.\n"; - -## app needs to send packet for sim to learn of local NLA -$app->say ("send ao budo"); -print "sim: wait for odata ...\n"; -$odata = $sim->wait_for_odata; -print "sim: odata received.\n"; - -print "sim: request SPM via SPMR.\n"; -$sim->say ("net send spmr ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort}"); -$t0 = [gettimeofday]; - -print "sim: wait for SPM ...\n"; -$sim->wait_for_spm; -$elapsed = tv_interval ( $t0, [gettimeofday] ); -print "sim: SPM received after $elapsed seconds.\n"; -die "SPM interval too large, indicates heartbeat not SPMR induced.\n" unless ($elapsed < 5.0); - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/spmr_from_odata.pl b/3rdparty/openpgm-svn-r1085/pgm/test/spmr_from_odata.pl deleted file mode 100755 index 0563b3f..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/spmr_from_odata.pl +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/perl -# spmr_from_odata.pl -# 13.3.1. SPM Requests - -use strict; -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$mon->say ("filter $config{app}{ip}"); -print "mon: ready.\n"; - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -$sim->say ("create fake ao"); -$sim->say ("bind ao"); -print "sim: publish ODATA sqn 90,001.\n"; -$sim->say ("net send odata ao 90001 90001 ringo"); - -my $data = $app->wait_for_data; -print "app: received data [$data].\n"; - -print "mon: wait for SPMR ...\n"; -$mon->wait_for_spmr; -print "mon: received SPMR.\n"; - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/spmr_suppression.pl b/3rdparty/openpgm-svn-r1085/pgm/test/spmr_suppression.pl deleted file mode 100755 index a1df615..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/spmr_suppression.pl +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/perl -# spmr_suppression.pl -# 13.3.1. SPM Requests - -use strict; -use PGM::Test; - -BEGIN { require "test.conf.pl"; } - -$| = 1; - -my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); -my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); -my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); - -$mon->connect; -$sim->connect; -$app->connect; - -sub close_ssh { - $mon = $sim = $app = undef; - print "finished.\n"; -} - -$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; - -$sim->say ("create fake ao"); -$sim->say ("bind ao"); -print "sim: publish ODATA sqn 90,001 to monitor for GSI.\n"; -$sim->say ("net send odata ao 90001 90001 ringo"); - -## capture GSI of test sim (not app!) -my $odata = $mon->wait_for_odata; -$mon->say ("filter $config{app}{ip}"); - -$app->say ("create ao"); -$app->say ("bind ao"); -$app->say ("listen ao"); - -print "sim: re-publish ODATA sqn 90,001 to app.\n"; -$sim->say ("net send odata ao 90001 90001 ringo"); -$sim->say ("net send spmr ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort}"); - -my $data = $app->wait_for_data; -print "app: received data [$data].\n"; - -print "mon: wait for erroneous SPMR ...\n"; -$mon->die_on_spmr({ 'timeout' => 2 }); -print "mon: no SPMR received.\n"; - -print "test completed successfully.\n"; - -$mon->disconnect (1); -$sim->disconnect; -$app->disconnect; -close_ssh; - -# eof diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/sudoers.example b/3rdparty/openpgm-svn-r1085/pgm/test/sudoers.example deleted file mode 100644 index 9212139..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/sudoers.example +++ /dev/null @@ -1,26 +0,0 @@ -# /etc/sudoers -# -# This file MUST be edited with the 'visudo' command as root. -# -# See the man page for details on how to write a sudoers file. -# Host alias specification - -# User alias specification -User_Alias PGM_USER = steve-o - -# Cmnd alias specification -Cmnd_Alias PGM_CONFORMANCE = /miru/projects/openpgm/pgm/ref/debug/test/* - -# Defaults - -Defaults !lecture,tty_tickets,!fqdn - -# User privilege specification -root ALL=(ALL) ALL - -# Members of the admin group may gain root privileges -%admin ALL=(ALL) ALL - - -# PGM testing -PGM_USER ALL = NOPASSWD: PGM_CONFORMANCE diff --git a/3rdparty/openpgm-svn-r1085/pgm/test/test.conf.pl b/3rdparty/openpgm-svn-r1085/pgm/test/test.conf.pl deleted file mode 100644 index 428f49e..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/test/test.conf.pl +++ /dev/null @@ -1,27 +0,0 @@ -# test.conf.pl -use vars qw ( %config ); - -%config = ( - msapp => { - host => 'momo', - ip => '10.6.28.34', - }, - app => { -# host => 'ayaka', ip => '10.6.28.31', -# cmd => '/miru/projects/openpgm/pgm/ref/release-Linux-x86_64/test/app', -# network => 'eth0;239.192.0.1' - host => 'ryoko', ip => '10.6.28.36', - cmd => 'LD_LIBRARY_PATH=/opt/glib-sunstudio/lib:$LD_LIBRARY_PATH /miru/projects/openpgm/pgm/ref/release-SunOS-sun4u-sunstudio/test/app', - network => 'eri0;239.192.0.1' - }, - mon => { - host => 'sora', - cmd => '/miru/projects/openpgm/pgm/ref/release-Linux-x86_64/test/monitor', - network => 'eth0;239.192.0.1' - }, - sim => { - host => 'kiku', - cmd => '/miru/projects/openpgm/pgm/ref/release-Linux-x86_64/test/sim', - network => 'eth0;239.192.0.1' - }, -); diff --git a/3rdparty/openpgm-svn-r1085/pgm/thread.c b/3rdparty/openpgm-svn-r1085/pgm/thread.c deleted file mode 100644 index ad68ca3..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/thread.c +++ /dev/null @@ -1,457 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * mutexes and locks. - * - * Copyright (c) 2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include - -//#define THREAD_DEBUG - - -/* Globals */ - -#if defined(_WIN32) && !defined(CONFIG_HAVE_WIN_COND) -static DWORD cond_event_tls = TLS_OUT_OF_INDEXES; -#endif - -static volatile uint32_t thread_ref_count = 0; - - -#ifndef _WIN32 -# define posix_check_err(err, name) \ - do { \ - const int save_error = (err); \ - if (PGM_UNLIKELY(save_error)) { \ - pgm_error ("file %s: line %d (%s): error '%s' during '%s'", \ - __FILE__, __LINE__, __PRETTY_FUNCTION__, \ - strerror (save_error), name); \ - } \ - } while (0) -# define posix_check_cmd(cmd) posix_check_err ((cmd), #cmd) -#else -# define win32_check_err(err, name) \ - do { \ - const bool save_error = (err); \ - if (PGM_UNLIKELY(!save_error)) { \ - pgm_error ("file %s: line %d (%s): error '%s' during '%s'", \ - __FILE__, __LINE__, __PRETTY_FUNCTION__, \ - pgm_wsastrerror (GetLastError ()), name); \ - } \ - } while (0) -# define win32_check_cmd(cmd) win32_check_err ((cmd), #cmd) -#endif /* !_WIN32 */ - - -/* only needed for Win32 pre-Vista read-write locks - */ -void -pgm_thread_init (void) -{ - if (pgm_atomic_exchange_and_add32 (&thread_ref_count, 1) > 0) - return; - -#if defined(_WIN32) && !defined(CONFIG_HAVE_WIN_COND) - win32_check_cmd (TLS_OUT_OF_INDEXES != (cond_event_tls = TlsAlloc ())); -#endif -} - -void -pgm_thread_shutdown (void) -{ - pgm_return_if_fail (pgm_atomic_read32 (&thread_ref_count) > 0); - - if (pgm_atomic_exchange_and_add32 (&thread_ref_count, (uint32_t)-1) != 1) - return; - -#if defined(_WIN32) && !defined(CONFIG_HAVE_WIN_COND) - TlsFree (cond_event_tls); -#endif -} - -void -pgm_mutex_init ( - pgm_mutex_t* mutex - ) -{ - pgm_assert (NULL != mutex); -#ifndef _WIN32 - posix_check_cmd (pthread_mutex_init (&mutex->pthread_mutex, NULL)); -#else - HANDLE handle; - win32_check_cmd (handle = CreateMutex (NULL, FALSE, NULL)); - mutex->win32_mutex = handle; -#endif /* !_WIN32 */ -} - -bool -pgm_mutex_trylock ( - pgm_mutex_t* mutex - ) -{ - pgm_assert (NULL != mutex); -#ifndef _WIN32 - const int result = pthread_mutex_trylock (&mutex->pthread_mutex); - if (EBUSY == result) - return FALSE; - posix_check_err (result, "pthread_mutex_trylock"); - return TRUE; -#else - DWORD result; - win32_check_cmd (WAIT_FAILED != (result = WaitForSingleObject (mutex->win32_mutex, 0))); - return WAIT_TIMEOUT != result; -#endif /* !_WIN32 */ -} - -void -pgm_mutex_free ( - pgm_mutex_t* mutex - ) -{ - pgm_assert (NULL != mutex); -#ifndef _WIN32 - posix_check_cmd (pthread_mutex_destroy (&mutex->pthread_mutex)); -#else - win32_check_cmd (CloseHandle (mutex->win32_mutex)); -#endif /* !_WIN32 */ -} - -void -pgm_spinlock_init ( - pgm_spinlock_t* spinlock - ) -{ - pgm_assert (NULL != spinlock); -#ifndef _WIN32 - posix_check_cmd (pthread_spin_init (&spinlock->pthread_spinlock, PTHREAD_PROCESS_PRIVATE)); -#else - InitializeCriticalSection (&spinlock->win32_spinlock); -#endif /* !_WIN32 */ -} - -bool -pgm_spinlock_trylock ( - pgm_spinlock_t* spinlock - ) -{ - pgm_assert (NULL != spinlock); -#ifndef _WIN32 - const int result = pthread_spin_trylock (&spinlock->pthread_spinlock); - if (EBUSY == result) - return FALSE; - posix_check_err (result, "pthread_spinlock_trylock"); - return TRUE; -#else - return TryEnterCriticalSection (&spinlock->win32_spinlock); -#endif /* !_WIN32 */ -} - -void -pgm_spinlock_free ( - pgm_spinlock_t* spinlock - ) -{ - pgm_assert (NULL != spinlock); -#ifndef _WIN32 -/* ignore return value */ - pthread_spin_destroy (&spinlock->pthread_spinlock); -#else - DeleteCriticalSection (&spinlock->win32_spinlock); -#endif /* !_WIN32 */ -} - -void -pgm_cond_init ( - pgm_cond_t* cond - ) -{ - pgm_assert (NULL != cond); -#ifndef _WIN32 - posix_check_cmd (pthread_cond_init (&cond->pthread_cond, NULL)); -#elif defined(CONFIG_HAVE_WIN_COND) - InitializeConditionVariable (&cond->win32_cond); -#else - cond->len = 0; - cond->allocated_len = pgm_nearest_power (1, 2 + 1); - cond->phandle = pgm_new (HANDLE, cond->allocated_len); - InitializeCriticalSection (&cond->win32_spinlock); -#endif /* !_WIN32 */ -} - -void -pgm_cond_signal ( - pgm_cond_t* cond - ) -{ - pgm_assert (NULL != cond); -#ifndef _WIN32 - pthread_cond_signal (&cond->pthread_cond); -#elif defined(CONFIG_HAVE_WIN_COND) - WakeConditionVariable (&cond->win32_cond); -#else - EnterCriticalSection (&cond->win32_spinlock); - if (cond->len > 0) { - SetEvent (cond->phandle[ 0 ]); - memmove (&cond->phandle[ 0 ], &cond->phandle[ 1 ], cond->len - 1); - cond->len--; - } - LeaveCriticalSection (&cond->win32_spinlock); -#endif /* !_WIN32 */ -} - -void -pgm_cond_broadcast ( - pgm_cond_t* cond - ) -{ - pgm_assert (NULL != cond); -#ifndef _WIN32 - pthread_cond_broadcast (&cond->pthread_cond); -#elif defined(CONFIG_HAVE_WIN_COND) - WakeAllConditionVariable (&cond->win32_cond); -#else - EnterCriticalSection (&cond->win32_spinlock); - for (unsigned i = 0; i < cond->len; i++) - SetEvent (cond->phandle[ i ]); - cond->len = 0; - LeaveCriticalSection (&cond->win32_spinlock); -#endif /* !_WIN32 */ -} - -#ifndef _WIN32 -void -pgm_cond_wait ( - pgm_cond_t* cond, - pthread_mutex_t* mutex - ) -{ - pgm_assert (NULL != cond); - pgm_assert (NULL != mutex); - pthread_cond_wait (&cond->pthread_cond, mutex); -} -#else -void -pgm_cond_wait ( - pgm_cond_t* cond, - CRITICAL_SECTION* spinlock - ) -{ - pgm_assert (NULL != cond); - pgm_assert (NULL != spinlock); -# if defined(CONFIG_HAVE_WIN_COND) - SleepConditionVariableCS (&cond->win32_cond, spinlock, INFINITE); -# else - DWORD status; - HANDLE event = TlsGetValue (cond_event_tls); - - if (!event) { - win32_check_cmd (event = CreateEvent (0, FALSE, FALSE, NULL)); - TlsSetValue (cond_event_tls, event); - } - - EnterCriticalSection (&cond->win32_spinlock); - pgm_assert (WAIT_TIMEOUT == WaitForSingleObject (event, 0)); - if ((cond->len + 1) > cond->allocated_len) { - cond->allocated_len = pgm_nearest_power (1, cond->len + 1 + 1); - cond->phandle = pgm_realloc (cond->phandle, cond->allocated_len); - } - cond->phandle[ cond->len++ ] = event; - LeaveCriticalSection (&cond->win32_spinlock); - - EnterCriticalSection (spinlock); - win32_check_cmd (WAIT_FAILED != (status = WaitForSingleObject (event, INFINITE))); - LeaveCriticalSection (spinlock); - - if (WAIT_TIMEOUT == status) { - EnterCriticalSection (&cond->win32_spinlock); - for (unsigned i = 0; i < cond->len; i++) { - if (cond->phandle[ i ] == event) { - if (i != cond->len - 1) - memmove (&cond->phandle[ i ], &cond->phandle[ i + 1 ], sizeof(HANDLE) * (cond->len - i - 1)); - cond->len--; - break; - } - } - win32_check_cmd (WAIT_FAILED != (status = WaitForSingleObject (event, 0))); - LeaveCriticalSection (&cond->win32_spinlock); - } -# endif /* !CONFIG_HAVE_WIN_COND */ -} -#endif /* !_WIN32 */ - -void -pgm_cond_free ( - pgm_cond_t* cond - ) -{ - pgm_assert (NULL != cond); -#ifndef _WIN32 - posix_check_cmd (pthread_cond_destroy (&cond->pthread_cond)); -#elif defined(CONFIG_HAVE_WIN_COND) - /* nop */ -#else - DeleteCriticalSection (&cond->win32_spinlock); - pgm_free (cond->phandle); -#endif /* !_WIN32 */ -} - -void -pgm_rwlock_init ( - pgm_rwlock_t* rwlock - ) -{ - pgm_assert (NULL != rwlock); -#ifdef CONFIG_HAVE_WIN_SRW_LOCK - InitializeSRWLock (&rwlock->win32_lock); -#elif !defined(_WIN32) - posix_check_cmd (pthread_rwlock_init (&rwlock->pthread_rwlock, NULL)); -#else - InitializeCriticalSection (&rwlock->win32_spinlock); - pgm_cond_init (&rwlock->read_cond); - pgm_cond_init (&rwlock->write_cond); - rwlock->read_counter = 0; - rwlock->have_writer = FALSE; - rwlock->want_to_read = 0; - rwlock->want_to_write = 0; -#endif /* !CONFIG_HAVE_WIN_SRW_LOCK */ -} - -void -pgm_rwlock_free ( - pgm_rwlock_t* rwlock - ) -{ - pgm_assert (NULL != rwlock); -#ifdef CONFIG_HAVE_WIN_SRW_LOCK - /* nop */ -#elif !defined(_WIN32) - pthread_rwlock_destroy (&rwlock->pthread_rwlock); -#else - pgm_cond_free (&rwlock->read_cond); - pgm_cond_free (&rwlock->write_cond); - DeleteCriticalSection (&rwlock->win32_spinlock); -#endif /* !CONFIG_HAVE_WIN_SRW_LOCK */ -} - -#if !defined(CONFIG_HAVE_WIN_SRW_LOCK) && defined(_WIN32) -static inline -void -_pgm_rwlock_signal ( - pgm_rwlock_t* rwlock - ) -{ - pgm_assert (NULL != rwlock); - if (rwlock->want_to_write) - pgm_cond_signal (&rwlock->write_cond); - else if (rwlock->want_to_read) - pgm_cond_broadcast (&rwlock->read_cond); -} - -void -pgm_rwlock_reader_lock ( - pgm_rwlock_t* rwlock - ) -{ - pgm_assert (NULL != rwlock); - EnterCriticalSection (&rwlock->win32_spinlock); - rwlock->want_to_read++; - while (rwlock->have_writer || rwlock->want_to_write) - pgm_cond_wait (&rwlock->read_cond, &rwlock->win32_spinlock); - rwlock->want_to_read--; - rwlock->read_counter++; - LeaveCriticalSection (&rwlock->win32_spinlock); -} - -bool -pgm_rwlock_reader_trylock ( - pgm_rwlock_t* rwlock - ) -{ - pgm_assert (NULL != rwlock); - bool status; - EnterCriticalSection (&rwlock->win32_spinlock); - if (!rwlock->have_writer && !rwlock->want_to_write) { - rwlock->read_counter++; - status = TRUE; - } else - status = FALSE; - LeaveCriticalSection (&rwlock->win32_spinlock); - return status; -} - -void -pgm_rwlock_reader_unlock( - pgm_rwlock_t* rwlock - ) -{ - pgm_assert (NULL != rwlock); - EnterCriticalSection (&rwlock->win32_spinlock); - rwlock->read_counter--; - if (rwlock->read_counter == 0) - _pgm_rwlock_signal (rwlock); - LeaveCriticalSection (&rwlock->win32_spinlock); -} - -void -pgm_rwlock_writer_lock ( - pgm_rwlock_t* rwlock - ) -{ - pgm_assert (NULL != rwlock); - EnterCriticalSection (&rwlock->win32_spinlock); - rwlock->want_to_write++; - while (rwlock->have_writer || rwlock->read_counter) - pgm_cond_wait (&rwlock->write_cond, &rwlock->win32_spinlock); - rwlock->want_to_write--; - rwlock->have_writer = TRUE; - LeaveCriticalSection (&rwlock->win32_spinlock); -} - -bool -pgm_rwlock_writer_trylock ( - pgm_rwlock_t* rwlock - ) -{ - pgm_assert (NULL != rwlock); - bool status; - EnterCriticalSection (&rwlock->win32_spinlock); - if (!rwlock->have_writer && !rwlock->read_counter) { - rwlock->have_writer = TRUE; - status = TRUE; - } else - status = FALSE; - LeaveCriticalSection (&rwlock->win32_spinlock); - return status; -} - -void -pgm_rwlock_writer_unlock ( - pgm_rwlock_t* rwlock - ) -{ - pgm_assert (NULL != rwlock); - EnterCriticalSection (&rwlock->win32_spinlock); - rwlock->have_writer = FALSE; - _pgm_rwlock_signal (rwlock); - LeaveCriticalSection (&rwlock->win32_spinlock); -} -#endif /* !_WIN32 && !CONFIG_HAVE_WIN_SRW_LOCK */ - - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/time.c b/3rdparty/openpgm-svn-r1085/pgm/time.c deleted file mode 100644 index 9b8eeaa..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/time.c +++ /dev/null @@ -1,770 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * high resolution timers. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define __STDC_FORMAT_MACROS -#include -#include -#include -#ifdef _WIN32 -# define WIN32_LEAN_AND_MEAN -# include "windows.h" -#endif -#include -#include - -//#define TIME_DEBUG - - -/* globals */ - -pgm_time_update_func pgm_time_update_now PGM_GNUC_READ_MOSTLY; -pgm_time_since_epoch_func pgm_time_since_epoch PGM_GNUC_READ_MOSTLY; - - -/* locals */ - -#define msecs_to_secs(t) ( (t) / 1000 ) -#define usecs_to_secs(t) ( (t) / 1000000UL ) -#define nsecs_to_secs(t) ( (t) / 1000000000UL ) -#define secs_to_msecs(t) ( (pgm_time_t)(t) * 1000 ) -#define secs_to_usecs(t) ( (pgm_time_t)(t) * 1000000UL ) -#define secs_to_nsecs(t) ( (pgm_time_t)(t) * 1000000000UL ) -#define msecs_to_usecs(t) ( (pgm_time_t)(t) * 1000 ) -#define msecs_to_nsecs(t) ( (pgm_time_t)(t) * 1000000UL ) -#define usecs_to_msecs(t) ( (t) / 1000 ) -#define usecs_to_nsecs(t) ( (pgm_time_t)(t) * 1000 ) -#define nsecs_to_msecs(t) ( (t) / 1000000UL ) -#define nsecs_to_usecs(t) ( (t) / 1000 ) -#define fsecs_to_nsecs(t) ( (t) / 1000000UL ) -#define fsecs_to_usecs(t) ( (t) / 1000000000UL ) - -static volatile uint32_t time_ref_count = 0; -static pgm_time_t rel_offset PGM_GNUC_READ_MOSTLY = 0; - -static void pgm_time_conv (const pgm_time_t*const restrict, time_t*restrict); -static void pgm_time_conv_from_reset (const pgm_time_t*const restrict, time_t*restrict); - -#if defined(CONFIG_HAVE_CLOCK_GETTIME) -# include -static pgm_time_t pgm_clock_update (void); -#endif -#ifdef CONFIG_HAVE_FTIME -# include -# ifdef _WIN32 -# define ftime _ftime -# endif -static pgm_time_t pgm_ftime_update (void); -#endif -#ifdef CONFIG_HAVE_GETTIMEOFDAY -# include -static pgm_time_t pgm_gettimeofday_update (void); -#endif -#ifdef CONFIG_HAVE_HPET -# include -# include -# include -# include -# include -# define HPET_MMAP_SIZE 0x400 -# define HPET_GENERAL_CAPS_REGISTER 0x00 -# define HPET_COUNTER_CLK_PERIOD 0x004 -# define HPET_MAIN_COUNTER_REGISTER 0x0f0 -# define HPET_COUNT_SIZE_CAP (1 << 13) -/* HPET counter size maybe 64-bit or 32-bit */ -# if defined(__x86_64__) -typedef uint64_t hpet_counter_t; -# else -typedef uint32_t hpet_counter_t; -# endif -static int hpet_fd PGM_GNUC_READ_MOSTLY = -1; -static char* hpet_ptr PGM_GNUC_READ_MOSTLY; -static uint64_t hpet_offset = 0; -static uint64_t hpet_wrap PGM_GNUC_READ_MOSTLY; -static hpet_counter_t hpet_last = 0; - -# define HPET_NS_SCALE 22 -# define HPET_US_SCALE 34 -static uint_fast32_t hpet_ns_mul PGM_GNUC_READ_MOSTLY = 0; -static uint_fast32_t hpet_us_mul PGM_GNUC_READ_MOSTLY = 0; - -static inline -void -set_hpet_mul ( - const uint32_t hpet_period - ) -{ - hpet_ns_mul = fsecs_to_nsecs((uint64_t)hpet_period << HPET_NS_SCALE); - hpet_us_mul = fsecs_to_usecs((uint64_t)hpet_period << HPET_US_SCALE); -} - -static inline -uint64_t -hpet_to_ns ( - const uint64_t hpet - ) -{ - return (hpet * hpet_ns_mul) >> HPET_NS_SCALE; -} - -static inline -uint64_t -hpet_to_us ( - const uint64_t hpet - ) -{ - return (hpet * hpet_us_mul) >> HPET_US_SCALE; -} - -static bool pgm_hpet_init (pgm_error_t**); -static bool pgm_hpet_shutdown (void); -static pgm_time_t pgm_hpet_update (void); -#endif -#ifdef CONFIG_HAVE_RTC -# include -# include -# include -# include -# include -# include -static int rtc_fd PGM_GNUC_READ_MOSTLY = -1; -static int rtc_frequency PGM_GNUC_READ_MOSTLY = 8192; -static pgm_time_t rtc_count = 0; -static bool pgm_rtc_init (pgm_error_t**); -static bool pgm_rtc_shutdown (void); -static pgm_time_t pgm_rtc_update (void); -#endif -#ifdef CONFIG_HAVE_TSC -# include -# include -# define TSC_NS_SCALE 10 /* 2^10, carefully chosen */ -# define TSC_US_SCALE 20 -static uint_fast32_t tsc_mhz PGM_GNUC_READ_MOSTLY = 0; -static uint_fast32_t tsc_ns_mul PGM_GNUC_READ_MOSTLY = 0; -static uint_fast32_t tsc_us_mul PGM_GNUC_READ_MOSTLY = 0; - -static inline -void -set_tsc_mul ( - const unsigned khz - ) -{ - tsc_ns_mul = (1000000 << TSC_NS_SCALE) / khz; - tsc_us_mul = (1000 << TSC_US_SCALE) / khz; -} - -static inline -uint64_t -tsc_to_ns ( - const uint64_t tsc - ) -{ - return (tsc * tsc_ns_mul) >> TSC_NS_SCALE; -} - -static inline -uint64_t -ns_to_tsc ( - const uint64_t ns - ) -{ - return (ns << TSC_NS_SCALE) / tsc_ns_mul; -} - -static inline -uint64_t -tsc_to_us ( - const uint64_t tsc - ) -{ - return (tsc * tsc_us_mul) >> TSC_US_SCALE; -} - -static inline -uint64_t -us_to_tsc ( - const uint64_t us - ) -{ - return (us << TSC_US_SCALE) / tsc_us_mul; -} - -# ifndef _WIN32 -static bool pgm_tsc_init (pgm_error_t**); -# endif -static pgm_time_t pgm_tsc_update (void); -#endif - - -/* initialize time system. - * - * returns TRUE on success, returns FALSE on error such as being unable to open - * the RTC device, an unstable TSC, or system already initialized. - */ - -bool -pgm_time_init ( - pgm_error_t** error - ) -{ - if (pgm_atomic_exchange_and_add32 (&time_ref_count, 1) > 0) - return TRUE; - -/* current time */ - const char *cfg = getenv ("PGM_TIMER"); - if (cfg == NULL) { -#ifdef CONFIG_HAVE_TSC - cfg = "TSC"; -#else - cfg = "GTOD"; -#endif - } - - pgm_time_since_epoch = pgm_time_conv; - - switch (cfg[0]) { -#ifdef CONFIG_HAVE_FTIME - case 'F': - pgm_minor (_("Using ftime() timer.")); - pgm_time_update_now = pgm_ftime_update; - break; -#endif -#ifdef CONFIG_HAVE_CLOCK_GETTIME - case 'C': - pgm_minor (_("Using clock_gettime() timer.")); - pgm_time_update_now = pgm_clock_update; - break; -#endif -#ifdef CONFIG_HAVE_RTC - case 'R': - pgm_minor (_("Using /dev/rtc timer.")); - pgm_time_update_now = pgm_rtc_update; - pgm_time_since_epoch = pgm_time_conv_from_reset; - break; -#endif -#ifdef CONFIG_HAVE_TSC -# ifdef _WIN32 - default: -# endif - case 'T': - pgm_minor (_("Using TSC timer.")); - pgm_time_update_now = pgm_tsc_update; - pgm_time_since_epoch = pgm_time_conv_from_reset; - break; -#endif -#ifdef CONFIG_HAVE_HPET - case 'H': - pgm_minor (_("Using HPET timer.")); - pgm_time_update_now = pgm_hpet_update; - pgm_time_since_epoch = pgm_time_conv_from_reset; - break; -#endif - -#ifdef CONFIG_HAVE_GETTIMEOFDAY -# ifndef _WIN32 - default: -# endif - case 'G': - pgm_minor (_("Using gettimeofday() timer.")); - pgm_time_update_now = pgm_gettimeofday_update; - break; -#endif - } - -#ifdef CONFIG_HAVE_RTC - if (pgm_time_update_now == pgm_rtc_update) - { - pgm_error_t* sub_error = NULL; - if (!pgm_rtc_init (&sub_error)) { - pgm_propagate_error (error, sub_error); - goto err_cleanup; - } - } -#endif -#ifdef CONFIG_HAVE_TSC - if (pgm_time_update_now == pgm_tsc_update) - { -#ifdef CONFIG_HAVE_PROC -/* attempt to parse clock ticks from kernel - */ - FILE* fp = fopen ("/proc/cpuinfo", "r"); - char buffer[1024]; - if (fp) - { - while (!feof(fp) && fgets (buffer, sizeof(buffer), fp)) - { - if (strstr (buffer, "cpu MHz")) - { - const char *p = strchr (buffer, ':'); - if (p) tsc_mhz = atoi (p + 1); - break; - } - } - fclose (fp); - } -#elif defined(_WIN32) - uint64_t frequency; - if (QueryPerformanceFrequency ((LARGE_INTEGER*)&frequency)) - { - tsc_mhz = frequency / 1000; - } -#endif /* !_WIN32 */ - -/* e.g. export RDTSC_FREQUENCY=3200.000000 - * - * Value can be used to override kernel tick rate as well as internal calibration - */ - const char *env_mhz = getenv ("RDTSC_FREQUENCY"); - if (env_mhz) - tsc_mhz = atoi (env_mhz); - -#ifndef _WIN32 -/* calibrate */ - if (0 >= tsc_mhz) { - pgm_error_t* sub_error = NULL; - if (!pgm_tsc_init (&sub_error)) { - pgm_propagate_error (error, sub_error); - goto err_cleanup; - } - } -#endif - set_tsc_mul (tsc_mhz * 1000); - } -#endif /* CONFIG_HAVE_TSC */ - -#ifdef CONFIG_HAVE_HPET - if (pgm_time_update_now == pgm_hpet_update) - { - pgm_error_t* sub_error = NULL; - if (!pgm_hpet_init (&sub_error)) { - pgm_propagate_error (error, sub_error); - goto err_cleanup; - } - } -#endif - - pgm_time_update_now(); - -/* calculate relative time offset */ -#if defined(CONFIG_HAVE_RTC) || defined(CONFIG_HAVE_TSC) - if ( 0 -# ifdef CONFIG_HAVE_RTC - || pgm_time_update_now == pgm_rtc_update -# endif -# ifdef CONFIG_HAVE_TSC - || pgm_time_update_now == pgm_tsc_update -# endif - ) - { -# if defined( CONFIG_HAVE_GETTIMEOFDAY ) - rel_offset = pgm_gettimeofday_update() - pgm_time_update_now(); -# elif defined( CONFIG_HAVE_FTIME ) - rel_offset = pgm_ftime_update() - pgm_time_update_now(); -# else -# error "gettimeofday() or ftime() required to calculate counter offset" -# endif - } -#else - rel_offset = 0; -#endif - - return TRUE; - -err_cleanup: - pgm_atomic_dec32 (&time_ref_count); - return FALSE; -} - -/* returns TRUE if shutdown succeeded, returns FALSE on error. - */ - -bool -pgm_time_shutdown (void) -{ - pgm_return_val_if_fail (pgm_atomic_read32 (&time_ref_count) > 0, FALSE); - - if (pgm_atomic_exchange_and_add32 (&time_ref_count, (uint32_t)-1) != 1) - return TRUE; - - bool success = TRUE; -#ifdef CONFIG_HAVE_RTC - if (pgm_time_update_now == pgm_rtc_update) - success = pgm_rtc_shutdown (); -#endif -#ifdef CONFIG_HAVE_HPET - if (pgm_time_update_now == pgm_hpet_update) - success = pgm_hpet_shutdown (); -#endif - return success; -} - -#ifdef CONFIG_HAVE_GETTIMEOFDAY -static -pgm_time_t -pgm_gettimeofday_update (void) -{ - struct timeval gettimeofday_now; - static pgm_time_t last = 0; - gettimeofday (&gettimeofday_now, NULL); - const pgm_time_t now = secs_to_usecs (gettimeofday_now.tv_sec) + gettimeofday_now.tv_usec; - if (PGM_UNLIKELY(now < last)) - return last; - else - return last = now; -} -#endif /* CONFIG_HAVE_GETTIMEOFDAY */ - -#ifdef CONFIG_HAVE_CLOCK_GETTIME -static -pgm_time_t -pgm_clock_update (void) -{ - struct timespec clock_now; - static pgm_time_t last = 0; - clock_gettime (CLOCK_MONOTONIC, &clock_now); - const pgm_time_t now = secs_to_usecs (clock_now.tv_sec) + nsecs_to_usecs (clock_now.tv_nsec); - if (PGM_UNLIKELY(now < last)) - return last; - else - return last = now; -} -#endif /* CONFIG_HAVE_CLOCK_GETTIME */ - -#ifdef CONFIG_HAVE_FTIME -static -pgm_time_t -pgm_ftime_update (void) -{ - struct timeb ftime_now; - static pgm_time_t last = 0; - ftime (&ftime_now); - const pgm_time_t now = secs_to_usecs (ftime_now.time) + msecs_to_usecs (ftime_now.millitm); - if (PGM_UNLIKELY(now < last)) - return last; - else - return last = now; -} -#endif /* CONFIG_HAVE_FTIME */ - -#ifdef CONFIG_HAVE_RTC -/* Old PC/AT-Compatible driver: /dev/rtc - * - * Not so speedy 8192 Hz timer, thats 122us resolution. - * - * WARNING: time is relative to start of timer. - * WARNING: only one process is allowed to access the RTC. - */ - -static -bool -pgm_rtc_init ( - pgm_error_t** error - ) -{ - pgm_return_val_if_fail (rtc_fd == -1, FALSE); - - rtc_fd = open ("/dev/rtc", O_RDONLY); - if (-1 == rtc_fd) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_TIME, - PGM_ERROR_FAILED, - _("Cannot open /dev/rtc for reading: %s"), - strerror(errno)); - return FALSE; - } - if (-1 == ioctl (rtc_fd, RTC_IRQP_SET, rtc_frequency)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_TIME, - PGM_ERROR_FAILED, - _("Cannot set RTC frequency to %i Hz: %s"), - rtc_frequency, - strerror(errno)); - return FALSE; - } - if (-1 == ioctl (rtc_fd, RTC_PIE_ON, 0)) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_TIME, - PGM_ERROR_FAILED, - _("Cannot enable periodic interrupt (PIE) on RTC: %s"), - strerror(errno)); - return FALSE; - } - return TRUE; -} - -/* returns TRUE on success even if RTC device cannot be closed or had an IO error, - * returns FALSE if the RTC file descriptor is not set. - */ - -static -bool -pgm_rtc_shutdown (void) -{ - pgm_return_val_if_fail (rtc_fd, FALSE); - pgm_warn_if_fail (0 == close (rtc_fd)); - rtc_fd = -1; - return TRUE; -} - -/* RTC only indicates passed ticks therefore is by definition monotonic, we do not - * need to check the difference with respect to the last value. - */ - -static -pgm_time_t -pgm_rtc_update (void) -{ - uint32_t data; - -/* returned value contains interrupt type and count of interrupts since last read */ - pgm_warn_if_fail (sizeof(data) == read (rtc_fd, &data, sizeof(data))); - rtc_count += data >> 8; - return rtc_count * 1000000UL / rtc_frequency; -} -#endif /* CONFIG_HAVE_RTC */ - -#ifdef CONFIG_HAVE_TSC -/* read time stamp counter (TSC), count of ticks from processor reset. - * - * NB: On Windows this will usually be HPET or PIC timer interpolated with TSC. - */ - -static inline -pgm_time_t -rdtsc (void) -{ -# ifndef _WIN32 - uint32_t lo, hi; - -/* We cannot use "=A", since this would use %rax on x86_64 */ - asm volatile ("rdtsc" : "=a" (lo), "=d" (hi)); - - return (pgm_time_t)hi << 32 | lo; -# else - uint64_t counter; - QueryPerformanceCounter ((LARGE_INTEGER*)&counter); - return (pgm_time_t)counter; -# endif -} - -# ifndef _WIN32 -/* determine ratio of ticks to nano-seconds, use /dev/rtc for high accuracy - * millisecond timer and convert. - * - * WARNING: time is relative to start of timer. - */ - -static -bool -pgm_tsc_init ( - PGM_GNUC_UNUSED pgm_error_t** error - ) -{ -# ifdef CONFIG_HAVE_PROC -/* Test for constant TSC from kernel - */ - FILE* fp = fopen ("/proc/cpuinfo", "r"); - char buffer[1024], *flags = NULL; - if (fp) - { - while (!feof(fp) && fgets (buffer, sizeof(buffer), fp)) - { - if (strstr (buffer, "flags")) - { - flags = strchr (buffer, ':'); - break; - } - } - fclose (fp); - } - if (!flags || !strstr (flags, " tsc")) { - pgm_warn (_("Linux kernel reports no Time Stamp Counter (TSC).")); -/* force both to stable clocks even though one might be OK */ - pgm_time_update_now = pgm_gettimeofday_update; - return TRUE; - } - if (!strstr (flags, " constant_tsc")) { - pgm_warn (_("Linux kernel reports non-constant Time Stamp Counter (TSC).")); -/* force both to stable clocks even though one might be OK */ - pgm_time_update_now = pgm_gettimeofday_update; - return TRUE; - } -# endif /* CONFIG_HAVE_PROC */ - pgm_time_t start, stop; - const pgm_time_t calibration_usec = secs_to_usecs (4); - - pgm_info (_("Running a benchmark to measure system clock frequency...")); - - struct timespec req = { - .tv_sec = 4, - .tv_nsec = 0 - }; - start = rdtsc(); - while (-1 == nanosleep (&req, &req) && EINTR == errno); - stop = rdtsc(); - - if (stop < start) - { - pgm_warn (_("Finished RDTSC test. Unstable TSC detected. The benchmark resulted in a " - "non-monotonic time response rendering the TSC unsuitable for high resolution " - "timing. To prevent the start delay from this benchmark and use a stable clock " - "source set the environment variable PGM_TIMER to GTOD.")); -/* force both to stable clocks even though one might be OK */ - pgm_time_update_now = pgm_gettimeofday_update; - return TRUE; - } - -/* TODO: this math needs to be scaled to reduce rounding errors */ - const pgm_time_t tsc_diff = stop - start; - if (tsc_diff > calibration_usec) { -/* cpu > 1 Ghz */ - tsc_mhz = tsc_diff / calibration_usec; - } else { -/* cpu < 1 Ghz */ - tsc_mhz = -( calibration_usec / tsc_diff ); - } - - pgm_info (_("Finished RDTSC test. To prevent the startup delay from this benchmark, " - "set the environment variable RDTSC_FREQUENCY to %" PRIuFAST32 " on this " - "system. This value is dependent upon the CPU clock speed and " - "architecture and should be determined separately for each server."), - tsc_mhz); - return TRUE; -} -# endif - -/* TSC is monotonic on the same core but we do neither force the same core or save the count - * for each core as if the counter is unstable system wide another timing mechanism should be - * used, preferably HPET on x86/AMD64 or gettimeofday() on SPARC. - */ - -static -pgm_time_t -pgm_tsc_update (void) -{ - static pgm_time_t last = 0; - const pgm_time_t now = tsc_to_us (rdtsc()); - if (PGM_UNLIKELY(now < last)) - return last; - else - return last = now; -} -#endif - -#ifdef CONFIG_HAVE_HPET -/* High Precision Event Timer (HPET) created as a system wide stable high resolution timer - * to replace dependency on core specific counters (TSC). - * - * NB: Only available on x86/AMD64 hardware post 2007 - */ - -static -bool -pgm_hpet_init ( - pgm_error_t** error - ) -{ - pgm_return_val_if_fail (hpet_fd == -1, FALSE); - - hpet_fd = open("/dev/hpet", O_RDONLY); - if (hpet_fd < 0) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_TIME, - PGM_ERROR_FAILED, - _("Cannot open /dev/hpet for reading: %s"), - strerror(errno)); - return FALSE; - } - - hpet_ptr = mmap(NULL, HPET_MMAP_SIZE, PROT_READ, MAP_SHARED, hpet_fd, 0); - if (MAP_FAILED == hpet_ptr) { - pgm_set_error (error, - PGM_ERROR_DOMAIN_TIME, - PGM_ERROR_FAILED, - _("Error mapping HPET device: %s"), - strerror(errno)); - close (hpet_fd); - hpet_fd = -1; - return FALSE; - } - -/* HPET counter tick period is in femto-seconds, a value of 0 is not permitted, - * the value must be <= 0x05f5e100 or 100ns. - */ - const uint32_t hpet_period = *((uint32_t*)(hpet_ptr + HPET_COUNTER_CLK_PERIOD)); - set_hpet_mul (hpet_period); -#if defined( __x86_64__ ) || defined( __amd64 ) - const uint32_t hpet_caps = *((uint32_t*)(hpet_ptr + HPET_GENERAL_CAPS_REGISTER)); - hpet_wrap = hpet_caps & HPET_COUNT_SIZE_CAP ? 0 : (1ULL << 32); -#else - hpet_wrap = 1ULL << 32; -#endif - - return TRUE; -} - -static -bool -pgm_hpet_shutdown (void) -{ - pgm_return_val_if_fail (hpet_fd, FALSE); - pgm_warn_if_fail (0 == close (hpet_fd)); - hpet_fd = -1; - return TRUE; -} - -static -pgm_time_t -pgm_hpet_update (void) -{ - const hpet_counter_t hpet_count = *((hpet_counter_t*)(hpet_ptr + HPET_MAIN_COUNTER_REGISTER)); -/* 32-bit HPET counters wrap after ~4 minutes */ - if (PGM_UNLIKELY(hpet_count < hpet_last)) - hpet_offset += hpet_wrap; - hpet_last = hpet_count; - return hpet_to_us (hpet_offset + hpet_count); -} -#endif /* CONFIG_HAVE_HPET */ - -/* convert from pgm_time_t to time_t with pgm_time_t in microseconds since the epoch. - */ -static -void -pgm_time_conv ( - const pgm_time_t* const restrict pgm_time_t_time, - time_t* restrict time_t_time - ) -{ - *time_t_time = pgm_to_secs (*pgm_time_t_time); -} - -/* convert from pgm_time_t to time_t with pgm_time_t in microseconds since the core started. - */ -static -void -pgm_time_conv_from_reset ( - const pgm_time_t* const restrict pgm_time_t_time, - time_t* restrict time_t_time - ) -{ - *time_t_time = pgm_to_secs (*pgm_time_t_time + rel_offset); -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/time_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/time_unittest.c deleted file mode 100644 index fd28572..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/time_unittest.c +++ /dev/null @@ -1,188 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for high resolution timers. - * - * Copyright (c) 2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include -#include - - -/* mock state */ - - -/* mock functions for external references */ - -size_t -pgm_transport_pkt_offset2 ( - const bool can_fragment, - const bool use_pgmcc - ) -{ - return 0; -} - - -#include "time.c" - - -/* target: - * boolean - * pgm_time_init (pgm_error_t** error) - */ - -/* time initialisation uses reference counting */ - -START_TEST (test_init_pass_001) -{ - fail_unless (TRUE == pgm_time_init (NULL), "init #1 failed"); - fail_unless (TRUE == pgm_time_init (NULL), "init #2 failed"); -} -END_TEST - -/* target: - * bool - * pgm_time_shutdown (void) - */ - -START_TEST (test_shutdown_pass_001) -{ - fail_unless (TRUE == pgm_time_init (NULL), "init failed"); - fail_unless (TRUE == pgm_time_shutdown (), "shutdown #1 failed"); - fail_unless (FALSE == pgm_time_shutdown (), "shutdown #2 failed"); -} -END_TEST - -START_TEST (test_shutdown_pass_002) -{ - fail_unless (TRUE == pgm_time_init (NULL), "init #1 failed"); - fail_unless (TRUE == pgm_time_init (NULL), "init #2 failed"); - fail_unless (TRUE == pgm_time_shutdown (), "shutdown #1 failed"); - fail_unless (TRUE == pgm_time_shutdown (), "shutdown #2 failed"); - fail_unless (FALSE == pgm_time_shutdown (), "shutdown #3 failed"); -} -END_TEST - -/* target: - * pgm_time_t - * pgm_time_update_now (void) - */ - -START_TEST (test_update_now_pass_001) -{ - pgm_time_t tstamps[11]; - fail_unless (TRUE == pgm_time_init (NULL), "init failed"); - const pgm_time_t start_time = pgm_time_update_now (); - for (unsigned i = 1; i <= 10; i++) - { - tstamps[i] = pgm_time_update_now(); - } - g_message ("start-time: %" PGM_TIME_FORMAT, start_time); - for (unsigned i = 1; i <= 10; i++) - { - const pgm_time_t check_time = tstamps[i]; - const gint64 elapsed_time = check_time - start_time; - -/* must be monotonic */ - fail_unless (G_LIKELY(check_time >= start_time), "non-monotonic"); - - g_message ("check-point-%2.2u: %" PGM_TIME_FORMAT " (%+" G_GINT64_FORMAT "us)", - i, check_time, pgm_to_usecs(elapsed_time)); - } - fail_unless (TRUE == pgm_time_shutdown (), "shutdown failed"); -} -END_TEST - -/* target: - * void - * pgm_time_since_epoch ( - * pgm_time_t* pgm_time, - * time_t* epoch_time - * ) - */ - -START_TEST (test_since_epoch_pass_001) -{ - char stime[1024]; - time_t t; - struct tm* tmp; - fail_unless (TRUE == pgm_time_init (NULL), "init failed"); - pgm_time_t pgm_now = pgm_time_update_now (); - pgm_time_since_epoch (&pgm_now, &t); - tmp = localtime (&t); - fail_unless (NULL != tmp, "localtime failed"); - fail_unless (0 != strftime (stime, sizeof(stime), "%X", tmp), "strftime failed"); - g_message ("pgm-time:%" PGM_TIME_FORMAT " = %s", - pgm_now, stime); - fail_unless (TRUE == pgm_time_shutdown (), "shutdown failed"); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_init = tcase_create ("init"); - suite_add_tcase (s, tc_init); - tcase_add_test (tc_init, test_init_pass_001); - - TCase* tc_shutdown = tcase_create ("shutdown"); - suite_add_tcase (s, tc_shutdown); - tcase_add_test (tc_shutdown, test_shutdown_pass_001); - tcase_add_test (tc_shutdown, test_shutdown_pass_002); - - TCase* tc_update_now = tcase_create ("update-now"); - suite_add_tcase (s, tc_update_now); - tcase_add_test (tc_update_now, test_update_now_pass_001); - - TCase* tc_since_epoch = tcase_create ("since-epoch"); - suite_add_tcase (s, tc_since_epoch); - tcase_add_test (tc_since_epoch, test_since_epoch_pass_001); - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/timer.c b/3rdparty/openpgm-svn-r1085/pgm/timer.c deleted file mode 100644 index 3bee14c..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/timer.c +++ /dev/null @@ -1,223 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * PGM timer thread. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define __STDC_FORMAT_MACROS -#include -#include -#include -#include -#include -#include - - -//#define TIMER_DEBUG - - -/* determine which timer fires next: spm (ihb_tmr), nak_rb_ivl, nak_rpt_ivl, or nak_rdata_ivl - * and check whether its already due. - * - * called in sock creation so locks unrequired. - */ - -bool -pgm_timer_prepare ( - pgm_sock_t* const sock - ) -{ - int32_t msec; - -/* pre-conditions */ - pgm_assert (NULL != sock); - pgm_assert (sock->can_send_data || sock->can_recv_data); - - pgm_time_t now = pgm_time_update_now(); - pgm_time_t expiration; - - if (sock->can_send_data) - expiration = sock->next_ambient_spm; - else - expiration = now + sock->peer_expiry; - - sock->next_poll = expiration; - -/* advance time again to adjust for processing time out of the event loop, this - * could cause further timers to expire even before checking for new wire data. - */ - msec = pgm_to_msecs ((int64_t)expiration - (int64_t)now); - if (msec < 0) - msec = 0; - else - msec = MIN (INT32_MAX, msec); - pgm_trace (PGM_LOG_ROLE_NETWORK,_("Next expiration in %" PRIi32 "ms"), msec); - return (msec == 0); -} - -bool -pgm_timer_check ( - pgm_sock_t* const sock - ) -{ - const pgm_time_t now = pgm_time_update_now(); - bool expired; - -/* pre-conditions */ - pgm_assert (NULL != sock); - - pgm_timer_lock (sock); - expired = pgm_time_after_eq (now, sock->next_poll); - pgm_timer_unlock (sock); - return expired; -} - -/* return next timer expiration in microseconds (μs) - */ - -pgm_time_t -pgm_timer_expiration ( - pgm_sock_t* const sock - ) -{ - const pgm_time_t now = pgm_time_update_now(); - pgm_time_t expiration; - -/* pre-conditions */ - pgm_assert (NULL != sock); - - pgm_timer_lock (sock); - expiration = pgm_time_after (sock->next_poll, now) ? pgm_to_usecs (sock->next_poll - now) : 0; - pgm_timer_unlock (sock); - return expiration; -} - -/* call all timers, assume that time_now has been updated by either pgm_timer_prepare - * or pgm_timer_check and no other method calls here. - * - * returns TRUE on success, returns FALSE on blocked send-in-receive operation. - */ - -bool -pgm_timer_dispatch ( - pgm_sock_t* const sock - ) -{ - const pgm_time_t now = pgm_time_update_now(); - pgm_time_t next_expiration = 0; - -/* pre-conditions */ - pgm_assert (NULL != sock); - - pgm_debug ("pgm_timer_dispatch (sock:%p)", (const void*)sock); - -/* find which timers have expired and call each */ - if (sock->can_recv_data) - { - if (!pgm_check_peer_state (sock, now)) - return FALSE; - next_expiration = pgm_min_receiver_expiry (now + sock->peer_expiry, sock); - } - - if (sock->can_send_data) - { -/* reset congestion control on ACK timeout */ - if (sock->use_pgmcc && - sock->tokens < pgm_fp8 (1) && - 0 != sock->ack_expiry) - { - if (pgm_time_after_eq (now, sock->ack_expiry)) - { -#ifdef DEBUG_PGMCC -char nows[1024]; -time_t t = time (NULL); -struct tm* tmp = localtime (&t); -strftime (nows, sizeof(nows), "%Y-%m-%d %H:%M:%S", tmp); -printf ("ACK timeout, T:%u W:%u\n", pgm_fp8tou(sock->tokens), pgm_fp8tou(sock->cwnd_size)); -#endif - sock->tokens = sock->cwnd_size = pgm_fp8 (1); - sock->ack_bitmap = 0xffffffff; - sock->ack_expiry = 0; - -/* notify blocking tx thread that transmission time is now available */ - pgm_notify_send (&sock->ack_notify); - } - next_expiration = next_expiration > 0 ? MIN(next_expiration, sock->ack_expiry) : sock->ack_expiry; - } - -/* SPM broadcast */ - pgm_mutex_lock (&sock->timer_mutex); - const unsigned spm_heartbeat_state = sock->spm_heartbeat_state; - const pgm_time_t next_heartbeat_spm = sock->next_heartbeat_spm; - pgm_mutex_unlock (&sock->timer_mutex); - -/* no lock needed on ambient */ - const pgm_time_t next_ambient_spm = sock->next_ambient_spm; - pgm_time_t next_spm = spm_heartbeat_state ? MIN(next_heartbeat_spm, next_ambient_spm) : next_ambient_spm; - - if (pgm_time_after_eq (now, next_spm) && - !pgm_send_spm (sock, 0)) - return FALSE; - -/* ambient timing not so important so base next event off current time */ - if (pgm_time_after_eq (now, next_ambient_spm)) - { - sock->next_ambient_spm = now + sock->spm_ambient_interval; - next_spm = spm_heartbeat_state ? MIN(next_heartbeat_spm, sock->next_ambient_spm) : sock->next_ambient_spm; - } - -/* heartbeat timing is often high resolution so base times to last event */ - if (spm_heartbeat_state && pgm_time_after_eq (now, next_heartbeat_spm)) - { - unsigned new_heartbeat_state = spm_heartbeat_state; - pgm_time_t new_heartbeat_spm = next_heartbeat_spm; - do { - new_heartbeat_spm += sock->spm_heartbeat_interval[new_heartbeat_state++]; - if (new_heartbeat_state == sock->spm_heartbeat_len) { - new_heartbeat_state = 0; - new_heartbeat_spm = now + sock->spm_ambient_interval; - break; - } - } while (pgm_time_after_eq (now, new_heartbeat_spm)); -/* check for reset heartbeat */ - pgm_mutex_lock (&sock->timer_mutex); - if (next_heartbeat_spm == sock->next_heartbeat_spm) { - sock->spm_heartbeat_state = new_heartbeat_state; - sock->next_heartbeat_spm = new_heartbeat_spm; - next_spm = MIN(sock->next_ambient_spm, new_heartbeat_spm); - } else - next_spm = MIN(sock->next_ambient_spm, sock->next_heartbeat_spm); - sock->next_poll = next_expiration > 0 ? MIN(next_expiration, next_spm) : next_spm; - pgm_mutex_unlock (&sock->timer_mutex); - return TRUE; - } - - next_expiration = next_expiration > 0 ? MIN(next_expiration, next_spm) : next_spm; - -/* check for reset */ - pgm_mutex_lock (&sock->timer_mutex); - sock->next_poll = sock->next_poll > now ? MIN(sock->next_poll, next_expiration) : next_expiration; - pgm_mutex_unlock (&sock->timer_mutex); - } - else - sock->next_poll = next_expiration; - - return TRUE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/timer_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/timer_unittest.c deleted file mode 100644 index 2e48802..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/timer_unittest.c +++ /dev/null @@ -1,355 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for PGM timer thread. - * - * Copyright (c) 2009-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include - - -/* mock state */ - - -#define g_main_context_new mock_g_main_context_new -#define g_main_context_unref mock_g_main_context_unref -#define g_main_loop_new mock_g_main_loop_new -#define g_main_loop_run mock_g_main_loop_run -#define g_main_loop_unref mock_g_main_loop_unref -#define g_source_new mock_g_source_new -#define g_source_set_priority mock_g_source_set_priority -#define g_source_attach mock_g_source_attach -#define g_source_unref mock_g_source_unref -#define pgm_time_now mock_pgm_time_now -#define pgm_time_update_now mock_pgm_time_update_now -#define pgm_min_receiver_expiry mock_pgm_min_receiver_expiry -#define pgm_check_peer_state mock_pgm_check_peer_state -#define pgm_send_spm mock_pgm_send_spm - - -#define TIMER_DEBUG -#include "timer.c" - -static pgm_time_t _mock_pgm_time_update_now(void); -pgm_time_update_func mock_pgm_time_update_now = _mock_pgm_time_update_now; -static pgm_time_t mock_pgm_time_now = 0x1; - - -static -pgm_sock_t* -generate_sock (void) -{ - pgm_sock_t* sock = g_new0 (pgm_sock_t, 1); - return sock; -} - - -/* mock functions for external references */ - -size_t -pgm_pkt_offset ( - const bool can_fragment, - const sa_family_t pgmcc_family /* 0 = disable */ - ) -{ - return 0; -} - -/** GLib */ -static -GMainContext* -mock_g_main_context_new (void) -{ - GMainContext* context = g_malloc0 (sizeof(gpointer)); - return context; -} - -static -GMainLoop* -mock_g_main_loop_new ( - GMainContext* context, - gboolean is_running - ) -{ - g_assert (NULL != context); - GMainLoop* loop = g_malloc0 (sizeof(gpointer)); - return loop; -} - -static -void -mock_g_main_loop_run ( - GMainLoop* loop - ) -{ - g_assert (NULL != loop); -} - -static -void -mock_g_main_loop_unref ( - GMainLoop* loop - ) -{ - g_assert (NULL != loop); - g_free (loop); -} - -static -void -mock_g_main_context_unref ( - GMainContext* context - ) -{ - g_assert (NULL != context); - g_free (context); -} - -static -GSource* -mock_g_source_new ( - GSourceFuncs* source_funcs, - guint struct_size - ) -{ - g_assert (struct_size > 0); - GSource* source = g_malloc0 (struct_size); - return source; -} - -static -void -mock_g_source_set_priority ( - GSource* source, - gint priority - ) -{ - g_assert (NULL != source); -} - -static -guint -mock_g_source_attach ( - GSource* source, - GMainContext* context - ) -{ - g_assert (NULL != source); - return 1; -} - -static -void -mock_g_source_unref ( - GSource* source - ) -{ - g_assert (NULL != source); - g_free (source); -} - -/** time module */ -static -pgm_time_t -_mock_pgm_time_update_now (void) -{ - return mock_pgm_time_now; -} - -/** receiver module */ -PGM_GNUC_INTERNAL -pgm_time_t -mock_pgm_min_receiver_expiry ( - pgm_time_t expiration, - pgm_sock_t* sock - ) -{ - g_assert (NULL != sock); - return 0x1; -} - -PGM_GNUC_INTERNAL -bool -mock_pgm_check_peer_state ( - pgm_sock_t* sock, - pgm_time_t now - ) -{ - g_assert (NULL != sock); - return TRUE; -} - -/** source module */ -PGM_GNUC_INTERNAL -bool -mock_pgm_send_spm ( - pgm_sock_t* sock, - int flags - ) -{ - g_assert (NULL != sock); - return TRUE; -} - - -/* target: - * bool - * pgm_timer_prepare ( - * pgm_sock_t* sock - * ) - */ - -START_TEST (test_prepare_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - sock->can_send_data = TRUE; - sock->next_ambient_spm = mock_pgm_time_now + pgm_secs(10); - fail_unless (FALSE == pgm_timer_prepare (sock), "prepare failed"); -} -END_TEST - -START_TEST (test_prepare_fail_001) -{ - gboolean expired = pgm_timer_prepare (NULL); - fail ("reached"); -} -END_TEST - -/* target: - * bool - * pgm_timer_check ( - * pgm_sock_t* sock - * ) - */ - -START_TEST (test_check_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - fail_unless (TRUE == pgm_timer_check (sock), "check failed"); -} -END_TEST - -START_TEST (test_check_fail_001) -{ - gboolean expired = pgm_timer_check (NULL); - fail ("reached"); -} -END_TEST - -/* target: - * pgm_time_t - * pgm_timer_expiration ( - * pgm_sock_t* sock - * ) - */ - -START_TEST (test_expiration_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - sock->next_poll = mock_pgm_time_now + pgm_secs(300); - fail_unless (pgm_secs(300) == pgm_timer_expiration (sock), "expiration failed"); -} -END_TEST - -START_TEST (test_expiration_fail_001) -{ - long expiration = pgm_timer_expiration (NULL); - fail ("reached"); -} -END_TEST - -/* target: - * void - * pgm_timer_dispatch ( - * pgm_sock_t* sock - * ) - */ - -START_TEST (test_dispatch_pass_001) -{ - pgm_sock_t* sock = generate_sock (); - fail_if (NULL == sock, "generate_sock failed"); - pgm_timer_dispatch (sock); -} -END_TEST - -START_TEST (test_dispatch_fail_001) -{ - pgm_timer_dispatch (NULL); - fail ("reached"); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_prepare = tcase_create ("prepare"); - suite_add_tcase (s, tc_prepare); - tcase_add_test (tc_prepare, test_prepare_pass_001); - tcase_add_test_raise_signal (tc_prepare, test_prepare_fail_001, SIGABRT); - - TCase* tc_check = tcase_create ("check"); - suite_add_tcase (s, tc_check); - tcase_add_test (tc_check, test_check_pass_001); - tcase_add_test_raise_signal (tc_check, test_check_fail_001, SIGABRT); - - TCase* tc_expiration = tcase_create ("expiration"); - suite_add_tcase (s, tc_expiration); - tcase_add_test (tc_expiration, test_expiration_pass_001); - tcase_add_test_raise_signal (tc_expiration, test_expiration_fail_001, SIGABRT); - - TCase* tc_dispatch = tcase_create ("dispatch"); - suite_add_tcase (s, tc_dispatch); - tcase_add_test (tc_dispatch, test_dispatch_pass_001); - tcase_add_test_raise_signal (tc_dispatch, test_dispatch_fail_001, SIGABRT); - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/token and leaky bucket.txt b/3rdparty/openpgm-svn-r1085/pgm/token and leaky bucket.txt deleted file mode 100644 index 3efb9c7..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/token and leaky bucket.txt +++ /dev/null @@ -1,12 +0,0 @@ -leaky bucket: - - if (BUCKET->rate_limit > BUCKET->rate_per_sec) - BUCKET->rate_limit = BUCKET->rate_per_sec; - - -token bucket: - - guint bucket_limit; - - if (BUCKET->rate_limit > BUCKET->bucket_limit) - BUCKET->rate_limit = BUCKET->bucket_limit; diff --git a/3rdparty/openpgm-svn-r1085/pgm/tsi.c b/3rdparty/openpgm-svn-r1085/pgm/tsi.c deleted file mode 100644 index 5f9ae85..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/tsi.c +++ /dev/null @@ -1,119 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * transport session ID helper functions. - * - * Copyright (c) 2006-2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include - - -//#define TSI_DEBUG - - -/* locals */ - - -/* re-entrant form of pgm_tsi_print() - * - * returns number of bytes written to buffer on success, returns -1 on - * invalid parameters. - */ - -int -pgm_tsi_print_r ( - const pgm_tsi_t* restrict tsi, - char* restrict buf, - size_t bufsize - ) -{ - pgm_return_val_if_fail (NULL != tsi, -1); - pgm_return_val_if_fail (NULL != buf, -1); - pgm_return_val_if_fail (bufsize > 0, -1); - - const uint8_t* gsi = (const uint8_t*)tsi; - const uint16_t source_port = tsi->sport; - - return snprintf (buf, bufsize, "%i.%i.%i.%i.%i.%i.%i", - gsi[0], gsi[1], gsi[2], gsi[3], gsi[4], gsi[5], ntohs (source_port)); -} - -/* transform TSI to ASCII string form. - * - * on success, returns pointer to ASCII string. on error, returns NULL. - */ - -char* -pgm_tsi_print ( - const pgm_tsi_t* tsi - ) -{ - pgm_return_val_if_fail (tsi != NULL, NULL); - - static char buf[PGM_TSISTRLEN]; - pgm_tsi_print_r (tsi, buf, sizeof(buf)); - return buf; -} - -/* create hash value of TSI for use with GLib hash tables. - * - * on success, returns a hash value corresponding to the TSI. on error, fails - * on assert. - */ - -pgm_hash_t -pgm_tsi_hash ( - const void* p - ) -{ - const union { - pgm_tsi_t tsi; - uint32_t l[2]; - } *u = p; - -/* pre-conditions */ - pgm_assert (NULL != p); - - return u->l[0] ^ u->l[1]; -} - -/* compare two transport session identifier TSI values. - * - * returns TRUE if they are equal, FALSE if they are not. - */ - -bool -pgm_tsi_equal ( - const void* restrict p1, - const void* restrict p2 - ) -{ - const union { - pgm_tsi_t tsi; - uint32_t l[2]; - uint64_t ll; - } *restrict u1 = p1, *restrict u2 = p2; - -/* pre-conditions */ - pgm_assert (NULL != p1); - pgm_assert (NULL != p2); - - return (u1->l[0] == u2->l[0] && u1->l[1] == u2->l[1]); -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/tsi_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/tsi_unittest.c deleted file mode 100644 index fddff25..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/tsi_unittest.c +++ /dev/null @@ -1,185 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for transport session ID helper functions. - * - * Copyright (c) 2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include -#include -#include - - -/* mock state */ - -/* mock functions for external references */ - -size_t -pgm_transport_pkt_offset2 ( - const bool can_fragment, - const bool use_pgmcc - ) -{ - return 0; -} - - -#define TSI_DEBUG -#include "tsi.c" - - -/* target: - * gchar* - * pgm_tsi_print ( - * const pgm_tsi_t* tsi - * ) - */ - -START_TEST (test_print_pass_001) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - fail_if (NULL == pgm_tsi_print (&tsi), "print failed"); -} -END_TEST - -START_TEST (test_print_pass_002) -{ - fail_unless (NULL == pgm_tsi_print (NULL), "print failed"); -} -END_TEST - -/* target: - * int - * pgm_tsi_print_r ( - * const pgm_tsi_t* tsi, - * char* buf, - * gsize bufsize - * ) - */ - -START_TEST (test_print_r_pass_001) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - char buf[PGM_TSISTRLEN]; - fail_unless (pgm_tsi_print_r (&tsi, buf, sizeof(buf)) > 0, "print_r failed"); -} -END_TEST - -START_TEST (test_print_r_pass_002) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - char buf[PGM_TSISTRLEN]; - fail_unless (pgm_tsi_print_r (NULL, buf, sizeof(buf)) == -1, "print_r failed"); - fail_unless (pgm_tsi_print_r (&tsi, NULL, sizeof(buf)) == -1, "print_r failed"); - fail_unless (pgm_tsi_print_r (&tsi, buf, 0) == -1, "print_r failed"); -} -END_TEST - -/* target: - * gboolean - * pgm_tsi_equal ( - * gconstpointer tsi1, - * gconstpointer tsi2 - * ) - */ - -START_TEST (test_equal_pass_001) -{ - const pgm_tsi_t tsi1 = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const pgm_tsi_t tsi2 = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - fail_unless (pgm_tsi_equal (&tsi1, &tsi2), "equal failed"); -} -END_TEST - -START_TEST (test_equal_pass_002) -{ - const pgm_tsi_t tsi1 = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const pgm_tsi_t tsi2 = { { 9, 8, 7, 6, 5, 4 }, 2000 }; - fail_if (pgm_tsi_equal (&tsi1, &tsi2), "equal failed"); -} -END_TEST - -START_TEST (test_equal_fail_001) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - gboolean retval = pgm_tsi_equal (NULL, &tsi); - fail ("reached"); -} -END_TEST - -START_TEST (test_equal_fail_002) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - gboolean retval = pgm_tsi_equal (&tsi, NULL); - fail ("reached"); -} -END_TEST - - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_print = tcase_create ("print"); - suite_add_tcase (s, tc_print); - tcase_add_test (tc_print, test_print_pass_001); - tcase_add_test (tc_print, test_print_pass_002); - - TCase* tc_print_r = tcase_create ("print-r"); - suite_add_tcase (s, tc_print_r); - tcase_add_test (tc_print_r, test_print_r_pass_001); - tcase_add_test (tc_print_r, test_print_r_pass_002); - - TCase* tc_equal = tcase_create ("equal"); - suite_add_tcase (s, tc_equal); - tcase_add_test (tc_equal, test_equal_pass_001); - tcase_add_test (tc_equal, test_equal_pass_002); - tcase_add_test_raise_signal (tc_equal, test_equal_fail_001, SIGABRT); - tcase_add_test_raise_signal (tc_equal, test_equal_fail_002, SIGABRT); - - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/txw.c b/3rdparty/openpgm-svn-r1085/pgm/txw.c deleted file mode 100644 index e2487ae..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/txw.c +++ /dev/null @@ -1,763 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * A basic transmit window: pointer array implementation. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define __STDC_FORMAT_MACROS -#include -#include -#include -#include - - -//#define TXW_DEBUG - -#ifndef TXW_DEBUG -# define PGM_DISABLE_ASSERT -#endif - - -/* testing function: is TSI null - * - * returns TRUE if null, returns FALSE if not null. - */ - -static inline -bool -pgm_tsi_is_null ( - const void*const tsi - ) -{ - const union { - pgm_tsi_t tsi; - uint32_t l[2]; - } *u = tsi; - -/* pre-conditions */ - pgm_assert (NULL != tsi); - - return (0 == u->l[0] && 0 == u->l[1]); -} - -/* returns the pointer at the given index of the window. responsibility - * is with the caller to verify a single user ownership. - */ - -static inline -struct pgm_sk_buff_t* -_pgm_txw_peek ( - const pgm_txw_t*const window, - const uint32_t sequence - ) -{ - struct pgm_sk_buff_t* skb; - -/* pre-conditions */ - pgm_assert (NULL != window); - - if (pgm_txw_is_empty (window)) - return NULL; - - if (pgm_uint32_gte (sequence, window->trail) && pgm_uint32_lte (sequence, window->lead)) - { - const uint_fast32_t index_ = sequence % pgm_txw_max_length (window); - skb = window->pdata[index_]; - pgm_assert (NULL != skb); - pgm_assert (pgm_skb_is_valid (skb)); - pgm_assert (pgm_tsi_is_null (&skb->tsi)); - } - else - skb = NULL; - - return skb; -} - -/* testing function: can a request be peeked from the retransmit queue. - * - * returns TRUE if request is available, returns FALSE if not available. - */ - -static inline -bool -pgm_txw_retransmit_can_peek ( - pgm_txw_t*const window - ) -{ - pgm_return_val_if_fail (NULL != window, FALSE); - return (NULL != pgm_txw_retransmit_try_peek (window)); -} - -/* sequence state must be smaller than PGM skbuff control buffer */ -PGM_STATIC_ASSERT(sizeof(struct pgm_txw_state_t) <= sizeof(((struct pgm_sk_buff_t*)0)->cb)); - -uint32_t -pgm_txw_get_unfolded_checksum ( - const struct pgm_sk_buff_t*const skb - ) -{ - const pgm_txw_state_t*const state = (const pgm_txw_state_t*const)&skb->cb; - return state->unfolded_checksum; -} - -void -pgm_txw_set_unfolded_checksum ( - struct pgm_sk_buff_t*const skb, - const uint32_t csum - ) -{ - pgm_txw_state_t* state = (pgm_txw_state_t*)&skb->cb; - state->unfolded_checksum = csum; -} - -void -pgm_txw_inc_retransmit_count ( - struct pgm_sk_buff_t*const skb - ) -{ - pgm_txw_state_t*const state = (pgm_txw_state_t*const)&skb->cb; - state->retransmit_count++; -} - -bool -pgm_txw_retransmit_is_empty ( - const pgm_txw_t*const window - ) -{ - pgm_assert (NULL != window); - return pgm_queue_is_empty (&window->retransmit_queue); -} - - -/* globals */ - -static void pgm_txw_remove_tail (pgm_txw_t*const); -static bool pgm_txw_retransmit_push_parity (pgm_txw_t*const, const uint32_t, const uint8_t); -static bool pgm_txw_retransmit_push_selective (pgm_txw_t*const, const uint32_t); - - -/* constructor for transmit window. zero-length windows are not permitted. - * - * returns pointer to window. - */ - -pgm_txw_t* -pgm_txw_create ( - const pgm_tsi_t*const tsi, - const uint16_t tpdu_size, - const uint32_t sqns, /* transmit window size in sequence numbers */ - const unsigned secs, /* size in seconds */ - const ssize_t max_rte, /* max bandwidth */ - const bool use_fec, - const uint8_t rs_n, - const uint8_t rs_k - ) -{ - pgm_txw_t* window; - -/* pre-conditions */ - pgm_assert (NULL != tsi); - if (sqns) { - pgm_assert_cmpuint (tpdu_size, ==, 0); - pgm_assert_cmpuint (sqns, >, 0); - pgm_assert_cmpuint (sqns & PGM_UINT32_SIGN_BIT, ==, 0); - pgm_assert_cmpuint (secs, ==, 0); - pgm_assert_cmpuint (max_rte, ==, 0); - } else { - pgm_assert_cmpuint (tpdu_size, >, 0); - pgm_assert_cmpuint (secs, >, 0); - pgm_assert_cmpuint (max_rte, >, 0); - } - if (use_fec) { - pgm_assert_cmpuint (rs_n, >, 0); - pgm_assert_cmpuint (rs_k, >, 0); - } - - pgm_debug ("create (tsi:%s max-tpdu:%" PRIu16 " sqns:%" PRIu32 " secs %u max-rte %zd use-fec:%s rs(n):%u rs(k):%u)", - pgm_tsi_print (tsi), - tpdu_size, sqns, secs, max_rte, - use_fec ? "YES" : "NO", - rs_n, rs_k); - -/* calculate transmit window parameters */ - pgm_assert (sqns || (tpdu_size && secs && max_rte)); - const unsigned alloc_sqns = sqns ? sqns : ( (secs * max_rte) / tpdu_size ); - window = pgm_malloc0 (sizeof(pgm_txw_t) + ( alloc_sqns * sizeof(struct pgm_sk_buff_t*) )); - window->tsi = tsi; - -/* empty state for transmission group boundaries to align. - * - * trail = 0, lead = -1 - */ - window->lead = -1; - window->trail = window->lead + 1; - -/* reed-solomon forward error correction */ - if (use_fec) { - window->parity_buffer = pgm_alloc_skb (tpdu_size); - window->tg_sqn_shift = pgm_power2_log2 (rs_k); - pgm_rs_create (&window->rs, rs_n, rs_k); - window->is_fec_enabled = 1; - } - -/* pointer array */ - window->alloc = alloc_sqns; - -/* post-conditions */ - pgm_assert_cmpuint (pgm_txw_max_length (window), ==, alloc_sqns); - pgm_assert_cmpuint (pgm_txw_length (window), ==, 0); - pgm_assert_cmpuint (pgm_txw_size (window), ==, 0); - pgm_assert (pgm_txw_is_empty (window)); - pgm_assert (!pgm_txw_is_full (window)); - pgm_assert (!pgm_txw_retransmit_can_peek (window)); - - return window; -} - -/* destructor for transmit window. must not be called more than once for same window. - */ - -void -pgm_txw_shutdown ( - pgm_txw_t*const window - ) -{ -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert_cmpuint (window->alloc, >, 0); - - pgm_debug ("shutdown (window:%p)", (const void*)window); - -/* contents of window */ - while (!pgm_txw_is_empty (window)) { - pgm_txw_remove_tail (window); - } - -/* window must now be empty */ - pgm_assert_cmpuint (pgm_txw_length (window), ==, 0); - pgm_assert_cmpuint (pgm_txw_size (window), ==, 0); - pgm_assert (pgm_txw_is_empty (window)); - pgm_assert (!pgm_txw_is_full (window)); - -/* retransmit queue must be empty */ - pgm_assert (!pgm_txw_retransmit_can_peek (window)); - -/* free reed-solomon state */ - if (window->is_fec_enabled) { - pgm_free_skb (window->parity_buffer); - pgm_rs_destroy (&window->rs); - } - -/* window */ - pgm_free (window); -} - -/* add skb to transmit window, taking ownership. window does not grow. - * PGM skbuff data/tail pointers must point to the PGM payload, and hence skb->len - * is allowed to be zero. - * - * side effects: - * - * 1) sequence number is set in skb. - * 2) window is updated with new skb. - * - * no return value. fatal error raised on invalid parameters. if window is full then - * an entry is dropped to fulfil the request. - * - * it is an error to try to free the skb after adding to the window. - */ - -void -pgm_txw_add ( - pgm_txw_t* const restrict window, - struct pgm_sk_buff_t* const restrict skb /* cannot be NULL */ - ) -{ -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert (NULL != skb); - pgm_assert_cmpuint (pgm_txw_max_length (window), >, 0); - pgm_assert (pgm_skb_is_valid (skb)); - pgm_assert (((const pgm_list_t*)skb)->next == NULL); - pgm_assert (((const pgm_list_t*)skb)->prev == NULL); - pgm_assert (pgm_tsi_is_null (&skb->tsi)); - pgm_assert ((char*)skb->data > (char*)skb->head); - pgm_assert ((sizeof(struct pgm_header) + sizeof(struct pgm_data)) <= (size_t)((char*)skb->data - (char*)skb->head)); - - pgm_debug ("add (window:%p skb:%p)", (const char*)window, (const char*)skb); - - if (pgm_txw_is_full (window)) - { -/* transmit window advancement scheme dependent action here */ - pgm_txw_remove_tail (window); - } - -/* generate new sequence number */ - pgm_atomic_inc32 (&window->lead); - skb->sequence = window->lead; - -/* add skb to window */ - const uint_fast32_t index_ = skb->sequence % pgm_txw_max_length (window); - window->pdata[index_] = skb; - -/* statistics */ - window->size += skb->len; - -/* post-conditions */ - pgm_assert_cmpuint (pgm_txw_length (window), >, 0); - pgm_assert_cmpuint (pgm_txw_length (window), <=, pgm_txw_max_length (window)); -} - -/* peek an entry from the window for retransmission. - * - * returns pointer to skbuff on success, returns NULL on invalid parameters. - */ - -struct pgm_sk_buff_t* -pgm_txw_peek ( - const pgm_txw_t*const window, - const uint32_t sequence - ) -{ - pgm_debug ("peek (window:%p sequence:%" PRIu32 ")", - (const void*)window, sequence); - return _pgm_txw_peek (window, sequence); -} - -/* remove an entry from the trailing edge of the transmit window. - */ - -static -void -pgm_txw_remove_tail ( - pgm_txw_t* const window - ) -{ - struct pgm_sk_buff_t* skb; - pgm_txw_state_t* state; - - pgm_debug ("pgm_txw_remove_tail (window:%p)", (const void*)window); - -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert (!pgm_txw_is_empty (window)); - - skb = _pgm_txw_peek (window, pgm_txw_trail (window)); - pgm_assert (NULL != skb); - pgm_assert (pgm_skb_is_valid (skb)); - pgm_assert (pgm_tsi_is_null (&skb->tsi)); - - state = (pgm_txw_state_t*)&skb->cb; - if (state->waiting_retransmit) { - pgm_queue_unlink (&window->retransmit_queue, (pgm_list_t*)skb); - state->waiting_retransmit = 0; - } - -/* statistics */ - window->size -= skb->len; - if (state->retransmit_count > 0) { - PGM_HISTOGRAM_COUNTS("Tx.RetransmitCount", state->retransmit_count); - } - if (state->nak_elimination_count > 0) { - PGM_HISTOGRAM_COUNTS("Tx.NakEliminationCount", state->nak_elimination_count); - } - -/* remove reference to skb */ - if (PGM_UNLIKELY(pgm_mem_gc_friendly)) { - const uint_fast32_t index_ = skb->sequence % pgm_txw_max_length (window); - window->pdata[index_] = NULL; - } - pgm_free_skb (skb); - -/* advance trailing pointer */ - pgm_atomic_inc32 (&window->trail); - -/* post-conditions */ - pgm_assert (!pgm_txw_is_full (window)); -} - -/* Try to add a sequence number to the retransmit queue, ignore if - * already there or no longer in the transmit window. - * - * For parity NAKs, we deal on the transmission group sequence number - * rather than the packet sequence number. To simplify managment we - * use the leading window packet to store the details of the entire - * transmisison group. Parity NAKs are ignored if the packet count is - * less than or equal to the count already queued for retransmission. - * - * returns FALSE if request was eliminated, returns TRUE if request was - * added to queue. - */ - -bool -pgm_txw_retransmit_push ( - pgm_txw_t* const window, - const uint32_t sequence, - const bool is_parity, /* parity NAK ⇒ sequence_number = transmission group | packet count */ - const uint8_t tg_sqn_shift - ) -{ -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert_cmpuint (tg_sqn_shift, <, 8 * sizeof(uint32_t)); - - pgm_debug ("retransmit_push (window:%p sequence:%" PRIu32 " is_parity:%s tg_sqn_shift:%u)", - (const void*)window, sequence, is_parity ? "TRUE" : "FALSE", tg_sqn_shift); - -/* early elimination */ - if (pgm_txw_is_empty (window)) - return FALSE; - - if (is_parity) - { - return pgm_txw_retransmit_push_parity (window, sequence, tg_sqn_shift); - } - else - { - return pgm_txw_retransmit_push_selective (window, sequence); - } -} - -static -bool -pgm_txw_retransmit_push_parity ( - pgm_txw_t* const window, - const uint32_t sequence, - const uint8_t tg_sqn_shift - ) -{ - struct pgm_sk_buff_t* skb; - pgm_txw_state_t* state; - -/* pre-conditions */ - pgm_assert (NULL != window); - pgm_assert_cmpuint (tg_sqn_shift, <, 8 * sizeof(uint32_t)); - - const uint32_t tg_sqn_mask = 0xffffffff << tg_sqn_shift; - const uint32_t nak_tg_sqn = sequence & tg_sqn_mask; /* left unshifted */ - const uint32_t nak_pkt_cnt = sequence & ~tg_sqn_mask; - skb = _pgm_txw_peek (window, nak_tg_sqn); - - if (NULL == skb) { - pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Transmission group lead #%" PRIu32 " not in window."), nak_tg_sqn); - return FALSE; - } - - pgm_assert (pgm_skb_is_valid (skb)); - pgm_assert (pgm_tsi_is_null (&skb->tsi)); - state = (pgm_txw_state_t*)&skb->cb; - -/* check if request can be eliminated */ - if (state->waiting_retransmit) - { - pgm_assert (NULL != ((const pgm_list_t*)skb)->next); - pgm_assert (NULL != ((const pgm_list_t*)skb)->prev); - if (state->pkt_cnt_requested < nak_pkt_cnt) { -/* more parity packets requested than currently scheduled, simply bump up the count */ - state->pkt_cnt_requested = nak_pkt_cnt; - } - state->nak_elimination_count++; - return FALSE; - } - else - { - pgm_assert (((const pgm_list_t*)skb)->next == NULL); - pgm_assert (((const pgm_list_t*)skb)->prev == NULL); - } - -/* new request */ - state->pkt_cnt_requested++; - pgm_queue_push_head_link (&window->retransmit_queue, (pgm_list_t*)skb); - pgm_assert (!pgm_queue_is_empty (&window->retransmit_queue)); - state->waiting_retransmit = 1; - return TRUE; -} - -static -bool -pgm_txw_retransmit_push_selective ( - pgm_txw_t* const window, - const uint32_t sequence - ) -{ - struct pgm_sk_buff_t* skb; - pgm_txw_state_t* state; - -/* pre-conditions */ - pgm_assert (NULL != window); - - skb = _pgm_txw_peek (window, sequence); - if (NULL == skb) { - pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Requested packet #%" PRIu32 " not in window."), sequence); - return FALSE; - } - - pgm_assert (pgm_skb_is_valid (skb)); - pgm_assert (pgm_tsi_is_null (&skb->tsi)); - state = (pgm_txw_state_t*)&skb->cb; - -/* check if request can be eliminated */ - if (state->waiting_retransmit) { - pgm_assert (!pgm_queue_is_empty (&window->retransmit_queue)); - state->nak_elimination_count++; - return FALSE; - } - - pgm_assert (((const pgm_list_t*)skb)->next == NULL); - pgm_assert (((const pgm_list_t*)skb)->prev == NULL); - -/* new request */ - pgm_queue_push_head_link (&window->retransmit_queue, (pgm_list_t*)skb); - pgm_assert (!pgm_queue_is_empty (&window->retransmit_queue)); - state->waiting_retransmit = 1; - return TRUE; -} - -/* try to peek a request from the retransmit queue - * - * return pointer of first skb in queue, or return NULL if the queue is empty. - */ - -struct pgm_sk_buff_t* -pgm_txw_retransmit_try_peek ( - pgm_txw_t* const window - ) -{ -/* pre-conditions */ - pgm_assert (NULL != window); - - pgm_debug ("retransmit_try_peek (window:%p)", (const void*)window); - -/* no lock required to detect presence of a request */ - pgm_list_t* tail_link = pgm_queue_peek_tail_link (&window->retransmit_queue); - if (PGM_UNLIKELY(NULL == tail_link)) { - pgm_debug ("retransmit queue empty on peek."); - return NULL; - } - - struct pgm_sk_buff_t* skb = (struct pgm_sk_buff_t*)tail_link; - pgm_assert (pgm_skb_is_valid (skb)); - pgm_txw_state_t* state = (pgm_txw_state_t*)&skb->cb; - - if (!state->waiting_retransmit) { - pgm_assert (((const pgm_list_t*)skb)->next == NULL); - pgm_assert (((const pgm_list_t*)skb)->prev == NULL); - } -/* packet payload still in transit */ - if (PGM_UNLIKELY(1 != pgm_atomic_read32 (&skb->users))) { - pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Retransmit sqn #%" PRIu32 " is still in transit in transmit thread."), skb->sequence); - return NULL; - } - if (!state->pkt_cnt_requested) { - return skb; - } - -/* generate parity packet to satisify request */ - const uint8_t rs_h = state->pkt_cnt_sent % (window->rs.n - window->rs.k); - const uint32_t tg_sqn_mask = 0xffffffff << window->tg_sqn_shift; - const uint32_t tg_sqn = skb->sequence & tg_sqn_mask; - bool is_var_pktlen = FALSE; - bool is_op_encoded = FALSE; - uint16_t parity_length = 0; - const pgm_gf8_t* src[ window->rs.k ]; - for (uint_fast8_t i = 0; i < window->rs.k; i++) - { - const struct pgm_sk_buff_t* odata_skb = pgm_txw_peek (window, tg_sqn + i); - const uint16_t odata_tsdu_length = ntohs (odata_skb->pgm_header->pgm_tsdu_length); - if (!parity_length) - { - parity_length = odata_tsdu_length; - } - else if (odata_tsdu_length != parity_length) - { - is_var_pktlen = TRUE; - if (odata_tsdu_length > parity_length) - parity_length = odata_tsdu_length; - } - - src[i] = odata_skb->data; - if (odata_skb->pgm_header->pgm_options & PGM_OPT_PRESENT) { - is_op_encoded = TRUE; - } - } - -/* construct basic PGM header to be completed by send_rdata() */ - skb = window->parity_buffer; - skb->data = skb->tail = skb->head = skb + 1; - -/* space for PGM header */ - pgm_skb_put (skb, sizeof(struct pgm_header)); - - skb->pgm_header = skb->data; - skb->pgm_data = (void*)( skb->pgm_header + 1 ); - memcpy (skb->pgm_header->pgm_gsi, &window->tsi->gsi, sizeof(pgm_gsi_t)); - skb->pgm_header->pgm_options = PGM_OPT_PARITY; - -/* append actual TSDU length if variable length packets, zero pad as necessary. - */ - if (is_var_pktlen) - { - skb->pgm_header->pgm_options |= PGM_OPT_VAR_PKTLEN; - - for (uint_fast8_t i = 0; i < window->rs.k; i++) - { - struct pgm_sk_buff_t* odata_skb = pgm_txw_peek (window, tg_sqn + i); - const uint16_t odata_tsdu_length = ntohs (odata_skb->pgm_header->pgm_tsdu_length); - - pgm_assert (odata_tsdu_length == odata_skb->len); - pgm_assert (parity_length >= odata_tsdu_length); - - if (!odata_skb->zero_padded) { - memset (odata_skb->tail, 0, parity_length - odata_tsdu_length); - *(uint16_t*)((char*)odata_skb->data + parity_length) = odata_tsdu_length; - odata_skb->zero_padded = 1; - } - } - parity_length += 2; - } - - skb->pgm_header->pgm_tsdu_length = htons (parity_length); - -/* space for DATA */ - pgm_skb_put (skb, sizeof(struct pgm_data) + parity_length); - - skb->pgm_data->data_sqn = htonl ( tg_sqn | rs_h ); - - void* data_bytes = skb->pgm_data + 1; - -/* encode every option separately, currently only one applies: opt_fragment - */ - if (is_op_encoded) - { - skb->pgm_header->pgm_options |= PGM_OPT_PRESENT; - - struct pgm_opt_fragment null_opt_fragment; - const pgm_gf8_t* opt_src[ window->rs.k ]; - memset (&null_opt_fragment, 0, sizeof(null_opt_fragment)); - *(uint8_t*)&null_opt_fragment |= PGM_OP_ENCODED_NULL; - for (uint_fast8_t i = 0; i < window->rs.k; i++) - { - const struct pgm_sk_buff_t* odata_skb = pgm_txw_peek (window, tg_sqn + i); - - if (odata_skb->pgm_opt_fragment) - { - pgm_assert (odata_skb->pgm_header->pgm_options & PGM_OPT_PRESENT); -/* skip three bytes of header */ - opt_src[i] = (pgm_gf8_t*)((char*)odata_skb->pgm_opt_fragment + sizeof (struct pgm_opt_header)); - } - else - { - opt_src[i] = (pgm_gf8_t*)&null_opt_fragment; - } - } - -/* add options to this rdata packet */ - const uint16_t opt_total_length = sizeof(struct pgm_opt_length) + - sizeof(struct pgm_opt_header) + - sizeof(struct pgm_opt_fragment); - -/* add space for PGM options */ - pgm_skb_put (skb, opt_total_length); - - struct pgm_opt_length* opt_len = data_bytes; - opt_len->opt_type = PGM_OPT_LENGTH; - opt_len->opt_length = sizeof(struct pgm_opt_length); - opt_len->opt_total_length = htons ( opt_total_length ); - struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); - opt_header->opt_type = PGM_OPT_FRAGMENT | PGM_OPT_END; - opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_fragment); - opt_header->opt_reserved = PGM_OP_ENCODED; - struct pgm_opt_fragment* opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); - -/* The cast below is the correct way to handle the problem. - * The (void *) cast is to avoid a GCC warning like: - * - * "warning: dereferencing type-punned pointer will break strict-aliasing rules" - */ - pgm_rs_encode (&window->rs, - opt_src, - window->rs.k + rs_h, - (pgm_gf8_t*)((char*)opt_fragment + sizeof(struct pgm_opt_header)), - sizeof(struct pgm_opt_fragment) - sizeof(struct pgm_opt_header)); - - data_bytes = opt_fragment + 1; - } - -/* encode payload */ - pgm_rs_encode (&window->rs, - src, - window->rs.k + rs_h, - data_bytes, - parity_length); - -/* calculate partial checksum */ - const uint16_t tsdu_length = ntohs (skb->pgm_header->pgm_tsdu_length); - state->unfolded_checksum = pgm_csum_partial ((char*)skb->tail - tsdu_length, tsdu_length, 0); - return skb; -} - -/* remove head entry from retransmit queue, will fail on assertion if queue is empty. - */ - -void -pgm_txw_retransmit_remove_head ( - pgm_txw_t* const window - ) -{ - struct pgm_sk_buff_t* skb; - pgm_txw_state_t* state; - -/* pre-conditions */ - pgm_assert (NULL != window); - - pgm_debug ("retransmit_remove_head (window:%p)", - (const void*)window); - -/* tail link is valid without lock */ - pgm_list_t* tail_link = pgm_queue_peek_tail_link (&window->retransmit_queue); - -/* link must be valid for pop */ - pgm_assert (NULL != tail_link); - - skb = (struct pgm_sk_buff_t*)tail_link; - pgm_assert (pgm_skb_is_valid (skb)); - pgm_assert (pgm_tsi_is_null (&skb->tsi)); - state = (pgm_txw_state_t*)&skb->cb; - if (!state->waiting_retransmit) - { - pgm_assert (((const pgm_list_t*)skb)->next == NULL); - pgm_assert (((const pgm_list_t*)skb)->prev == NULL); - } - if (state->pkt_cnt_requested) - { - state->pkt_cnt_sent++; - -/* remove if all requested parity packets have been sent */ - if (state->pkt_cnt_sent == state->pkt_cnt_requested) { - pgm_queue_pop_tail_link (&window->retransmit_queue); - state->waiting_retransmit = 0; - } - } - else /* selective request */ - { - pgm_queue_pop_tail_link (&window->retransmit_queue); - state->waiting_retransmit = 0; - } -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/txw_unittest.c b/3rdparty/openpgm-svn-r1085/pgm/txw_unittest.c deleted file mode 100644 index bec079b..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/txw_unittest.c +++ /dev/null @@ -1,743 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * unit tests for transmit window. - * - * Copyright (c) 2009 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include - - -/* mock global */ - -#define pgm_histogram_add mock_pgm_histogram_add -#define pgm_rs_create mock_pgm_rs_create -#define pgm_rs_destroy mock_pgm_rs_destroy -#define pgm_rs_encode mock_pgm_rs_encode -#define pgm_compat_csum_partial mock_pgm_compat_csum_partial -#define pgm_histogram_init mock_pgm_histogram_init - -#define TXW_DEBUG -#include "txw.c" - - -/** reed-solomon module */ -void -mock_pgm_rs_create ( - pgm_rs_t* rs, - uint8_t n, - uint8_t k - ) -{ -} - -void -mock_pgm_rs_destroy ( - pgm_rs_t* rs - ) -{ -} - -void -mock_pgm_rs_encode( - pgm_rs_t* rs, - const pgm_gf8_t** src, - const uint8_t offset, - pgm_gf8_t* dst, - const uint16_t len - ) -{ -} - -/** checksum module */ -uint32_t -mock_pgm_compat_csum_partial ( - const void* addr, - uint16_t len, - uint32_t csum - ) -{ - return 0x0; -} - -void -mock_pgm_histogram_init ( - pgm_histogram_t* histogram - ) -{ -} - -void -mock_pgm_histogram_add ( - pgm_histogram_t* histogram, - int value - ) -{ -} - - -/* mock functions for external references */ - -size_t -pgm_pkt_offset ( - const bool can_fragment, - const sa_family_t pgmcc_family /* 0 = disable */ - ) -{ - return 0; -} - - -/* generate valid skb, data pointer pointing to PGM payload - */ -static -struct pgm_sk_buff_t* -generate_valid_skb (void) -{ - const guint16 tsdu_length = 1000; - const guint16 header_length = sizeof(struct pgm_header) + sizeof(struct pgm_data); - struct pgm_sk_buff_t* skb = pgm_alloc_skb (1500); -/* fake but valid transport and timestamp */ - skb->sock = (pgm_sock_t*)0x1; - skb->tstamp = 1; -/* header */ - pgm_skb_reserve (skb, header_length); - memset (skb->head, 0, header_length); - skb->pgm_header = (struct pgm_header*)skb->head; - skb->pgm_data = (struct pgm_data*)(skb->pgm_header + 1); - skb->pgm_header->pgm_type = PGM_ODATA; - skb->pgm_header->pgm_tsdu_length = g_htons (tsdu_length); -/* DATA */ - pgm_skb_put (skb, tsdu_length); - return skb; -} - -/* target: - * pgm_txw_t* - * pgm_txw_create ( - * const pgm_tsi_t* const tsi, - * const guint16 tpdu_size, - * const guint32 sqns, - * const guint secs, - * const guint max_rte, - * const gboolean use_fec, - * const guint rs_n, - * const guint rs_k - * ) - */ - -/* vanilla sequence count window */ -START_TEST (test_create_pass_001) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - fail_if (NULL == pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0), "create failed"); -} -END_TEST - -/* vanilla time based window */ -START_TEST (test_create_pass_002) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - fail_if (NULL == pgm_txw_create (&tsi, 1500, 0, 60, 800000, FALSE, 0, 0), "create failed"); -} -END_TEST - -/* jumbo frame */ -START_TEST (test_create_pass_003) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - fail_if (NULL == pgm_txw_create (&tsi, 9000, 0, 60, 800000, FALSE, 0, 0), "create failed"); -} -END_TEST - -/* max frame */ -START_TEST (test_create_pass_004) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - fail_if (NULL == pgm_txw_create (&tsi, UINT16_MAX, 0, 60, 800000, FALSE, 0, 0), "create failed"); -} -END_TEST - -/* invalid tpdu size */ -START_TEST (test_create_fail_001) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const pgm_txw_t* window = pgm_txw_create (&tsi, 0, 0, 60, 800000, FALSE, 0, 0); - fail ("reached"); -} -END_TEST - -/* no specified sequence count or time value */ -START_TEST (test_create_fail_002) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const pgm_txw_t* window = pgm_txw_create (&tsi, 0, 0, 0, 800000, FALSE, 0, 0); - fail ("reached"); -} -END_TEST - -/* no specified rate */ -START_TEST (test_create_fail_003) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const pgm_txw_t* window = pgm_txw_create (&tsi, 0, 0, 60, 0, FALSE, 0, 0); - fail ("reached"); -} -END_TEST - -/* all invalid */ -START_TEST (test_create_fail_004) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - const pgm_txw_t* window = pgm_txw_create (NULL, 0, 0, 0, 0, FALSE, 0, 0); - fail ("reached"); -} -END_TEST - -/* target: - * void - * pgm_txw_shutdown ( - * pgm_txw_t* const window - * ) - */ - -START_TEST (test_shutdown_pass_001) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); - fail_if (NULL == window, "create failed"); - pgm_txw_shutdown (window); -} -END_TEST - -START_TEST (test_shutdown_fail_001) -{ - pgm_txw_shutdown (NULL); - fail ("reached"); -} -END_TEST - -/* target: - * void - * pgm_txw_add ( - * pgm_txw_t* const window, - * struct pgm_sk_buff_t* const skb - * ) - * failures raise assert errors and stop process execution. - */ - -START_TEST (test_add_pass_001) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); - fail_if (NULL == window, "create failed"); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - pgm_txw_add (window, skb); - pgm_txw_shutdown (window); -} -END_TEST - -/* null skb */ -START_TEST (test_add_fail_001) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); - fail_if (NULL == window, "create failed"); - pgm_txw_add (window, NULL); - fail ("reached"); -} -END_TEST - -/* null window */ -START_TEST (test_add_fail_002) -{ - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - pgm_txw_add (NULL, skb); - fail ("reached"); -} -END_TEST - -/* null skb content */ -START_TEST (test_add_fail_003) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); - fail_if (NULL == window, "create failed"); - char buffer[1500]; - memset (buffer, 0, sizeof(buffer)); - pgm_txw_add (window, (struct pgm_sk_buff_t*)buffer); - fail ("reached"); -} -END_TEST - -/* target: - * struct pgm_sk_buff_t* - * pgm_txw_peek ( - * pgm_txw_t* const window, - * const guint32 sequence - * ) - */ - -START_TEST (test_peek_pass_001) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); - fail_if (NULL == window, "create failed"); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - pgm_txw_add (window, skb); - fail_unless (skb == pgm_txw_peek (window, window->trail), "peek failed"); - pgm_txw_shutdown (window); -} -END_TEST - -/* null window */ -START_TEST (test_peek_fail_001) -{ - const struct pgm_sk_buff_t* skb = pgm_txw_peek (NULL, 0); - fail ("reached"); -} -END_TEST - -/* empty window */ -START_TEST (test_peek_fail_002) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); - fail_if (NULL == window, "create failed"); - fail_unless (NULL == pgm_txw_peek (window, window->trail), "peek failed"); - pgm_txw_shutdown (window); -} -END_TEST - -/** inline function tests **/ -/* pgm_txw_max_length () - */ -START_TEST (test_max_length_pass_001) -{ - const guint window_length = 100; - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - pgm_txw_t* window = pgm_txw_create (&tsi, 0, window_length, 0, 0, FALSE, 0, 0); - fail_if (NULL == window, "create failed"); - fail_unless (window_length == pgm_txw_max_length (window), "max_length failed"); - pgm_txw_shutdown (window); -} -END_TEST - -START_TEST (test_max_length_fail_001) -{ - const size_t answer = pgm_txw_max_length (NULL); - fail ("reached"); -} -END_TEST - -/* pgm_txw_length () - */ -START_TEST (test_length_pass_001) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); - fail_if (NULL == window, "create failed"); - fail_unless (0 == pgm_txw_length (window), "length failed"); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - pgm_txw_add (window, skb); - fail_unless (1 == pgm_txw_length (window), "length failed"); - pgm_txw_shutdown (window); -} -END_TEST - -START_TEST (test_length_fail_001) -{ - const uint32_t answer = pgm_txw_length (NULL); - fail ("reached"); -} -END_TEST - -/* pgm_txw_size () - */ -START_TEST (test_size_pass_001) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); - fail_if (NULL == window, "create failed"); - fail_unless (0 == pgm_txw_size (window), "size failed"); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - pgm_txw_add (window, skb); - fail_unless (1000 == pgm_txw_size (window), "size failed"); - pgm_txw_shutdown (window); -} -END_TEST - -START_TEST (test_size_fail_001) -{ - const size_t answer = pgm_txw_size (NULL); - fail ("reached"); -} -END_TEST - -/* pgm_txw_is_empty - */ -START_TEST (test_is_empty_pass_001) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); - fail_if (NULL == window, "create failed"); - fail_unless (pgm_txw_is_empty (window), "is_empty failed"); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - pgm_txw_add (window, skb); - fail_if (pgm_txw_is_empty (window), "is_empty failed"); - pgm_txw_shutdown (window); -} -END_TEST - -START_TEST (test_is_empty_fail_001) -{ - const bool answer = pgm_txw_is_empty (NULL); - fail ("reached"); -} -END_TEST - -/* pgm_txw_is_full - */ -START_TEST (test_is_full_pass_001) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - pgm_txw_t* window = pgm_txw_create (&tsi, 0, 1, 0, 0, FALSE, 0, 0); - fail_if (NULL == window, "create failed"); - fail_if (pgm_txw_is_full (window), "is_full failed"); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - pgm_txw_add (window, skb); - fail_unless (pgm_txw_is_full (window), "is_full failed"); - pgm_txw_shutdown (window); -} -END_TEST - -START_TEST (test_is_full_fail_001) -{ - const bool answer = pgm_txw_is_full (NULL); - fail ("reached"); -} -END_TEST - -/* pgm_txw_lead - */ -START_TEST (test_lead_pass_001) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); - fail_if (NULL == window, "create failed"); - guint32 lead = pgm_txw_lead (window); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - pgm_txw_add (window, skb); - fail_unless (lead + 1 == pgm_txw_lead (window), "lead failed"); - pgm_txw_shutdown (window); -} -END_TEST - -START_TEST (test_lead_fail_001) -{ - const uint32_t answer = pgm_txw_lead (NULL); - fail ("reached"); -} -END_TEST - -/* pgm_txw_next_lead - */ -START_TEST (test_next_lead_pass_001) -{ - const guint window_length = 100; - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - pgm_txw_t* window = pgm_txw_create (&tsi, 0, window_length, 0, 0, FALSE, 0, 0); - fail_if (NULL == window, "create failed"); - guint32 next_lead = pgm_txw_next_lead (window); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - pgm_txw_add (window, skb); - fail_unless (next_lead == pgm_txw_lead (window), "lead failed"); - pgm_txw_shutdown (window); -} -END_TEST - -START_TEST (test_next_lead_fail_001) -{ - const uint32_t answer = pgm_txw_next_lead (NULL); - fail ("reached"); -} -END_TEST - -/* pgm_txw_trail - */ -START_TEST (test_trail_pass_001) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - pgm_txw_t* window = pgm_txw_create (&tsi, 0, 1, 0, 0, FALSE, 0, 0); - fail_if (NULL == window, "create failed"); -/* does not advance with adding skb */ - guint32 trail = pgm_txw_trail (window); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - pgm_txw_add (window, skb); - fail_unless (trail == pgm_txw_trail (window), "trail failed"); -/* does advance when filling up window */ - skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - pgm_txw_add (window, skb); - fail_if (trail == pgm_txw_trail (window), "trail failed"); - pgm_txw_shutdown (window); -} -END_TEST - -START_TEST (test_trail_fail_001) -{ - const uint32_t answer = pgm_txw_trail (NULL); - fail ("reached"); -} -END_TEST - -/* target: - * bool - * pgm_txw_retransmit_push ( - * pgm_txw_t* const window, - * const uint32_t sequence, - * const bool is_parity, - * const uint8_t tg_sqn_shift - * ) - */ - -START_TEST (test_retransmit_push_pass_001) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); - fail_if (NULL == window, "create failed"); -/* empty window invalidates all requests */ - fail_unless (FALSE == pgm_txw_retransmit_push (window, window->trail, FALSE, 0), "retransmit_push failed"); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - pgm_txw_add (window, skb); -/* first request */ - fail_unless (TRUE == pgm_txw_retransmit_push (window, window->trail, FALSE, 0), "retransmit_push failed"); -/* second request eliminated */ - fail_unless (FALSE == pgm_txw_retransmit_push (window, window->trail, FALSE, 0), "retransmit_push failed"); - pgm_txw_shutdown (window); -} -END_TEST - -START_TEST (test_retransmit_push_fail_001) -{ - const bool answer = pgm_txw_retransmit_push (NULL, 0, FALSE, 0); - fail ("reached"); -} -END_TEST - -/* target: - * struct pgm_sk_buff_t* - * pgm_txw_retransmit_try_peek ( - * pgm_txw_t* const window - * ) - */ - -START_TEST (test_retransmit_try_peek_pass_001) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); - fail_if (NULL == window, "create failed"); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - pgm_txw_add (window, skb); - fail_unless (1 == pgm_txw_retransmit_push (window, window->trail, FALSE, 0), "retransmit_push failed"); - fail_unless (NULL != pgm_txw_retransmit_try_peek (window), "retransmit_try_peek failed"); - pgm_txw_shutdown (window); -} -END_TEST - -/* null window */ -START_TEST (test_retransmit_try_peek_fail_001) -{ - const struct pgm_sk_buff_t* skb = pgm_txw_retransmit_try_peek (NULL); - fail ("reached"); -} -END_TEST - -/* target: - * void - * pgm_txw_retransmit_remove_head ( - * pgm_txw_t* const window - * ) - */ - -START_TEST (test_retransmit_remove_head_pass_001) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); - fail_if (NULL == window, "create failed"); - struct pgm_sk_buff_t* skb = generate_valid_skb (); - fail_if (NULL == skb, "generate_valid_skb failed"); - pgm_txw_add (window, skb); - fail_unless (1 == pgm_txw_retransmit_push (window, window->trail, FALSE, 0), "retransmit_push failed"); - fail_unless (NULL != pgm_txw_retransmit_try_peek (window), "retransmit_try_peek failed"); - pgm_txw_retransmit_remove_head (window); - pgm_txw_shutdown (window); -} -END_TEST - -/* null window */ -START_TEST (test_retransmit_remove_head_fail_001) -{ - pgm_txw_retransmit_remove_head (NULL); - fail ("reached"); -} -END_TEST - -/* empty retransmit queue */ -START_TEST (test_retransmit_remove_head_fail_002) -{ - const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; - pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); - fail_if (NULL == window, "create failed"); - pgm_txw_retransmit_remove_head (window); - fail ("reached"); -} -END_TEST - -static -Suite* -make_test_suite (void) -{ - Suite* s; - - s = suite_create (__FILE__); - - TCase* tc_create = tcase_create ("create"); - suite_add_tcase (s, tc_create); - tcase_add_test (tc_create, test_create_pass_001); - tcase_add_test (tc_create, test_create_pass_002); - tcase_add_test (tc_create, test_create_pass_003); - tcase_add_test (tc_create, test_create_pass_004); - tcase_add_test_raise_signal (tc_create, test_create_fail_001, SIGABRT); - tcase_add_test_raise_signal (tc_create, test_create_fail_002, SIGABRT); - tcase_add_test_raise_signal (tc_create, test_create_fail_003, SIGABRT); - tcase_add_test_raise_signal (tc_create, test_create_fail_004, SIGABRT); - - TCase* tc_shutdown = tcase_create ("shutdown"); - suite_add_tcase (s, tc_shutdown); - tcase_add_test (tc_shutdown, test_shutdown_pass_001); - tcase_add_test_raise_signal (tc_shutdown, test_shutdown_fail_001, SIGABRT); - - TCase* tc_add = tcase_create ("add"); - suite_add_tcase (s, tc_add); - tcase_add_test (tc_add, test_add_pass_001); - tcase_add_test_raise_signal (tc_add, test_add_fail_001, SIGABRT); - tcase_add_test_raise_signal (tc_add, test_add_fail_002, SIGABRT); - tcase_add_test_raise_signal (tc_add, test_add_fail_003, SIGABRT); - - TCase* tc_peek = tcase_create ("peek"); - suite_add_tcase (s, tc_peek); - tcase_add_test (tc_peek, test_peek_pass_001); - tcase_add_test_raise_signal (tc_peek, test_peek_fail_001, SIGABRT); -/* logical not fatal errors */ - tcase_add_test (tc_peek, test_peek_fail_002); - - TCase* tc_max_length = tcase_create ("max-length"); - suite_add_tcase (s, tc_max_length); - tcase_add_test (tc_max_length, test_max_length_pass_001); - tcase_add_test_raise_signal (tc_max_length, test_max_length_fail_001, SIGABRT); - - TCase* tc_length = tcase_create ("length"); - suite_add_tcase (s, tc_length); - tcase_add_test (tc_length, test_length_pass_001); - tcase_add_test_raise_signal (tc_length, test_length_fail_001, SIGABRT); - - TCase* tc_size = tcase_create ("size"); - suite_add_tcase (s, tc_size); - tcase_add_test (tc_size, test_size_pass_001); - tcase_add_test_raise_signal (tc_size, test_size_fail_001, SIGABRT); - - TCase* tc_is_empty = tcase_create ("is-empty"); - suite_add_tcase (s, tc_is_empty); - tcase_add_test (tc_is_empty, test_is_empty_pass_001); - tcase_add_test_raise_signal (tc_is_empty, test_is_empty_fail_001, SIGABRT); - TCase* tc_is_full = tcase_create ("is-full"); - suite_add_tcase (s, tc_is_full); - tcase_add_test (tc_is_full, test_is_full_pass_001); - tcase_add_test_raise_signal (tc_is_full, test_is_full_fail_001, SIGABRT); - - TCase* tc_lead = tcase_create ("lead"); - suite_add_tcase (s, tc_lead); - tcase_add_test (tc_lead, test_lead_pass_001); - tcase_add_test_raise_signal (tc_lead, test_lead_fail_001, SIGABRT); - - TCase* tc_next_lead = tcase_create ("next-lead"); - suite_add_tcase (s, tc_next_lead); - tcase_add_test (tc_next_lead, test_next_lead_pass_001); - tcase_add_test_raise_signal (tc_next_lead, test_next_lead_fail_001, SIGABRT); - - TCase* tc_trail = tcase_create ("trail"); - suite_add_tcase (s, tc_trail); - tcase_add_test (tc_trail, test_trail_pass_001); - tcase_add_test_raise_signal (tc_trail, test_trail_fail_001, SIGABRT); - - TCase* tc_retransmit_push = tcase_create ("retransmit-push"); - suite_add_tcase (s, tc_retransmit_push); - tcase_add_test (tc_retransmit_push, test_retransmit_push_pass_001); - tcase_add_test_raise_signal (tc_retransmit_push, test_retransmit_push_fail_001, SIGABRT); - - TCase* tc_retransmit_try_peek = tcase_create ("retransmit-try-peek"); - suite_add_tcase (s, tc_retransmit_try_peek); - tcase_add_test (tc_retransmit_try_peek, test_retransmit_try_peek_pass_001); - tcase_add_test_raise_signal (tc_retransmit_try_peek, test_retransmit_try_peek_fail_001, SIGABRT); - - TCase* tc_retransmit_remove_head = tcase_create ("retransmit-remove-head"); - suite_add_tcase (s, tc_retransmit_remove_head); - tcase_add_test (tc_retransmit_remove_head, test_retransmit_remove_head_pass_001); - tcase_add_test_raise_signal (tc_retransmit_remove_head, test_retransmit_remove_head_fail_001, SIGABRT); - tcase_add_test_raise_signal (tc_retransmit_remove_head, test_retransmit_remove_head_fail_002, SIGABRT); - - return s; -} - -static -Suite* -make_master_suite (void) -{ - Suite* s = suite_create ("Master"); - return s; -} - -int -main (void) -{ - SRunner* sr = srunner_create (make_master_suite ()); - srunner_add_suite (sr, make_test_suite ()); - srunner_run_all (sr, CK_ENV); - int number_failed = srunner_ntests_failed (sr); - srunner_free (sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1085/pgm/valgrind.supp b/3rdparty/openpgm-svn-r1085/pgm/valgrind.supp deleted file mode 100644 index ea74d2d..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/valgrind.supp +++ /dev/null @@ -1,147 +0,0 @@ -##----------------------------------------------------------------------## -## Suppressions to run OpenPGM - -{ - miru-glib-hack-1 - Memcheck:Leak - fun:memalign - fun:posix_memalign - obj:/usr/lib/libglib-2.0.so* - fun:g_slice_alloc -} - -{ - miru-glib-hack-2 - Memcheck:Leak - fun:calloc - fun:g_malloc0 - obj:/usr/lib/libglib-2.0.so* - fun:g_slice_alloc -} - -{ - miru-glib-hack-2b - Memcheck:Leak - fun:malloc - fun:g_malloc - obj:/usr/lib/libglib-2.0.so* - fun:g_slice_alloc -} - -{ - miru-glib-hack-3 - Memcheck:Leak - fun:malloc - fun:realloc - fun:g_realloc - obj:/usr/lib/libglib-2.0.so* - fun:g_ptr_array_add - fun:g_main_context_check - obj:/usr/lib/libglib-2.0.so* - fun:g_main_loop_run -} - -{ - miru-glib-hack-4 - Memcheck:Leak - fun:realloc - fun:g_realloc - obj:/usr/lib/libglib-2.0.so* - fun:g_array_set_size - fun:g_static_private_set - fun:g_get_language_names -} - -{ - miru-glib-hack-5 - Memcheck:Leak - fun:malloc - fun:g_malloc - fun:g_slice_alloc - fun:g_array_sized_new - fun:g_static_private_set - fun:g_get_charset - fun:g_log_default_handler - fun:g_logv - fun:g_log -} - -{ - miru-glib-hack-6 - Memcheck:Leak - fun:malloc - fun:g_malloc - fun:g_log_set_handler -} - -{ - miru-glib-hack-7 - Memcheck:Leak - fun:calloc - fun:g_malloc0 - fun:g_thread_self - fun:g_thread_init_glib -} - - - -## Annoying libc errors - -{ - miru-libc-hack-1 - Memcheck:Addr8 - obj:/lib/ld-2.*.so - obj:/lib/ld-2.*.so - obj:/lib/libc-2.*.so - obj:/lib/ld-2.*.so - fun:__libc_dlopen_mode - fun:__nss_lookup_function - obj:/lib/libc-2.*.so - fun:getprotobyname_r -} - -{ - miru-libc-hack-1b - Memcheck:Addr8 - obj:/lib/ld-2.*.so - obj:/lib/ld-2.*.so - obj:/lib/ld-2.*.so - obj:/lib/ld-2.*.so - obj:/lib/ld-2.*.so - obj:/lib/ld-2.*.so - obj:/lib/ld-2.*.so - obj:/lib/libc-2.*.so - obj:/lib/ld-2.*.so - fun:__libc_dlopen_mode - fun:__nss_lookup_function - obj:/lib/libc-2.*.so - fun:getprotobyname_r -} - -{ - miru-libc-hack-2 - Memcheck:Cond - obj:/lib/ld-2.*.so - obj:/lib/ld-2.*.so - obj:/lib/libc-2.*.so - obj:/lib/ld-2.*.so - fun:__libc_dlsym - fun:__nss_lookup_function - obj:/lib/libc-2.*.so - fun:getaddrinfo -} - -{ - miru-libc-hack-3 - Memcheck:Addr8 - obj:/lib/ld-2.*.so - obj:/lib/ld-2.*.so - obj:/lib/ld-2.*.so - obj:/lib/ld-2.*.so - obj:/lib/libc-2.*.so - obj:/lib/ld-2.*.so - fun:__libc_dlsym - fun:__nss_lookup_function - obj:/lib/libc-2.*.so - fun:getaddrinfo -} diff --git a/3rdparty/openpgm-svn-r1085/pgm/version_generator.py b/3rdparty/openpgm-svn-r1085/pgm/version_generator.py deleted file mode 100755 index 0db781b..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/version_generator.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/python - -import os -import platform -import time - -build_date = time.strftime ("%Y-%m-%d") -build_time = time.strftime ("%H:%M:%S") -build_rev = os.popen('svnversion -n .').read(); - -print """ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * OpenPGM version. - * - * Copyright (c) 2006-2010 Miru Limited. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include - - -/* globals */ - -const unsigned pgm_major_version = 5; -const unsigned pgm_minor_version = 0; -const unsigned pgm_micro_version = 70; -const char* pgm_build_date = "%s"; -const char* pgm_build_time = "%s"; -const char* pgm_build_system = "%s"; -const char* pgm_build_machine = "%s"; -const char* pgm_build_revision = "%s"; - - -/* eof */ -"""%(build_date, build_time, platform.system(), platform.machine(), build_rev) - -# end of file diff --git a/3rdparty/openpgm-svn-r1085/pgm/win/mingw32-runtime_3.13-1openpgm3.diff b/3rdparty/openpgm-svn-r1085/pgm/win/mingw32-runtime_3.13-1openpgm3.diff deleted file mode 100644 index 5b860a1..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/win/mingw32-runtime_3.13-1openpgm3.diff +++ /dev/null @@ -1,136 +0,0 @@ -diff -urN include-original/mswsock.h include/mswsock.h ---- include-original/mswsock.h 2009-08-21 22:41:22.000000000 +0800 -+++ include/mswsock.h 2010-01-21 17:31:14.662159471 +0800 -@@ -83,23 +83,19 @@ - } WSAMSG, *PWSAMSG, *LPWSAMSG; - - --/* According to MSDN docs, the WSAMSG.Control buffer starts with a -- cmsghdr header of the following form. See also RFC 2292. */ -- --typedef struct wsacmsghdr { -- UINT cmsg_len; -- INT cmsg_level; -- INT cmsg_type; -- /* followed by UCHAR cmsg_data[]; */ --} WSACMSGHDR; -- --/* TODO: Standard Posix.1g macros as per RFC 2292, with WSA_uglification. */ --#if 0 --#define WSA_CMSG_FIRSTHDR(mhdr) --#define WSA_CMSG_NXTHDR(mhdr, cmsg) --#define WSA_CMSG_SPACE(length) --#define WSA_CMSG_LEN(length) --#endif -+ typedef struct _WSACMSGHDR { -+ SIZE_T cmsg_len; -+ INT cmsg_level; -+ INT cmsg_type; -+ } WSACMSGHDR,*PWSACMSGHDR,*LPWSACMSGHDR; -+ -+#define WSA_CMSGHDR_ALIGN(length) (((length) + TYPE_ALIGNMENT(WSACMSGHDR)-1) & (~(TYPE_ALIGNMENT(WSACMSGHDR)-1))) -+#define WSA_CMSGDATA_ALIGN(length) (((length) + MAX_NATURAL_ALIGNMENT-1) & (~(MAX_NATURAL_ALIGNMENT-1))) -+#define WSA_CMSG_FIRSTHDR(msg) (((msg)->Control.len >= sizeof(WSACMSGHDR)) ? (LPWSACMSGHDR)(msg)->Control.buf : (LPWSACMSGHDR)NULL) -+#define WSA_CMSG_NXTHDR(msg,cmsg) ((!(cmsg)) ? WSA_CMSG_FIRSTHDR(msg) : ((((u_char *)(cmsg) + WSA_CMSGHDR_ALIGN((cmsg)->cmsg_len) + sizeof(WSACMSGHDR)) > (u_char *)((msg)->Control.buf) + (msg)->Control.len) ? (LPWSACMSGHDR)NULL : (LPWSACMSGHDR)((u_char *)(cmsg) + WSA_CMSGHDR_ALIGN((cmsg)->cmsg_len)))) -+#define WSA_CMSG_DATA(cmsg) ((u_char *)(cmsg) + WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR))) -+#define WSA_CMSG_SPACE(length) (WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR) + WSA_CMSGHDR_ALIGN(length))) -+#define WSA_CMSG_LEN(length) (WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR)) + length) -+ -+typedef INT (WINAPI * LPFN_WSARECVMSG)(SOCKET, LPWSAMSG, LPDWORD, LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE); -+ - BOOL PASCAL DisconnectEx(SOCKET,LPOVERLAPPED,DWORD,DWORD); - int PASCAL WSARecvMsg(SOCKET,LPWSAMSG,LPDWORD,LPWSAOVERLAPPED,LPWSAOVERLAPPED_COMPLETION_ROUTINE); - -diff -urN include-original/ws2tcpip.h include/ws2tcpip.h ---- include-original/ws2tcpip.h 2009-08-21 22:41:42.000000000 +0800 -+++ include/ws2tcpip.h 2009-08-21 22:42:15.000000000 +0800 -@@ -78,6 +78,18 @@ - - #define UDP_NOCHECKSUM 1 - -+/* RFC 3768 */ -+#define MCAST_JOIN_GROUP 41 -+#define MCAST_LEAVE_GROUP 42 -+#define MCAST_BLOCK_SOURCE 43 -+#define MCAST_UNBLOCK_SOURCE 44 -+#define MCAST_JOIN_SOURCE_GROUP 45 -+#define MCAST_LEAVE_SOURCE_GROUP 46 -+#define MCAST_MSFILTER 47 -+ -+#define MCAST_EXCLUDE 0 -+#define MCAST_INCLUDE 1 -+ - /* INTERFACE_INFO iiFlags */ - #define IFF_UP 1 - #define IFF_BROADCAST 2 -@@ -104,6 +116,7 @@ - #define AI_PASSIVE 1 - #define AI_CANONNAME 2 - #define AI_NUMERICHOST 4 -+#define AI_ADDRCONFIG 0x20 - - /* getaddrinfo error codes */ - #define EAI_AGAIN WSATRY_AGAIN -@@ -132,6 +145,25 @@ - struct in_addr imr_interface; - }; - -+struct group_req { -+ u_long gr_interface; -+ struct sockaddr_storage gr_group; -+}; -+ -+struct group_source_req { -+ u_long gsr_interface; -+ struct sockaddr_storage gsr_group; -+ struct sockaddr_storage gsr_source; -+}; -+ -+struct group_filter { -+ u_long gf_interface; -+ struct sockaddr_storage gf_group; -+ u_long gf_fmode; -+ u_long gf_numsrc; -+ struct in_addr gf_slist[1]; -+}; -+ - struct ip_msfilter { - struct in_addr imsf_multiaddr; - struct in_addr imsf_interface; -@@ -356,6 +388,13 @@ - sockaddr_gen iiNetmask; - } INTERFACE_INFO, *LPINTERFACE_INFO; - -+typedef struct _INTERFACE_INFO_EX { -+ u_long iiFlags; -+ SOCKET_ADDRESS iiAddress; -+ SOCKET_ADDRESS iiBroadcastAddress; -+ SOCKET_ADDRESS iiNetmask; -+} INTERFACE_INFO_EX, *_LPINTERFACE_INFO_EX; -+ - /* - The definition above can cause problems on NT4,prior to sp4. - To workaround, include the following struct and typedef and ---- include-original/winnt.h 2009-08-21 22:41:42.000000000 +0800 -+++ include/winnt.h 2010-01-21 17:33:56.366162880 +0800 -@@ -43,6 +43,20 @@ - #define UNALIGNED - #endif - -+#ifdef _WIN64 -+#define MAX_NATURAL_ALIGNMENT sizeof(ULONGLONG) -+#define MEMORY_ALLOCATION_ALIGNMENT 16 -+#else -+#define MAX_NATURAL_ALIGNMENT sizeof(DWORD) -+#define MEMORY_ALLOCATION_ALIGNMENT 8 -+#endif -+ -+#ifdef __cplusplus -+#define TYPE_ALIGNMENT(t) __alignof__ (t) -+#else -+#define TYPE_ALIGNMENT(t) FIELD_OFFSET(struct { char x; t test; },test) -+#endif -+ - #ifndef DECLSPEC_ALIGN - #ifdef __GNUC__ - #define DECLSPEC_ALIGN(x) __attribute__((aligned(x))) diff --git a/3rdparty/openpgm-svn-r1085/pgm/win/mingw32-runtime_3.15.2-0openpgm1.diff b/3rdparty/openpgm-svn-r1085/pgm/win/mingw32-runtime_3.15.2-0openpgm1.diff deleted file mode 100644 index b6e3d11..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/win/mingw32-runtime_3.15.2-0openpgm1.diff +++ /dev/null @@ -1,135 +0,0 @@ -diff -urN include-original/mswsock.h include/mswsock.h ---- include-original/mswsock.h 2009-06-30 16:32:31.000000000 +0800 -+++ include/mswsock.h 2010-03-23 20:34:12.000000000 +0800 -@@ -83,23 +83,20 @@ - } WSAMSG, *PWSAMSG, *LPWSAMSG; - - --/* According to MSDN docs, the WSAMSG.Control buffer starts with a -- cmsghdr header of the following form. See also RFC 2292. */ -- --typedef struct wsacmsghdr { -- UINT cmsg_len; -- INT cmsg_level; -- INT cmsg_type; -- /* followed by UCHAR cmsg_data[]; */ --} WSACMSGHDR; -- --/* TODO: Standard Posix.1g macros as per RFC 2292, with WSA_uglification. */ --#if 0 --#define WSA_CMSG_FIRSTHDR(mhdr) --#define WSA_CMSG_NXTHDR(mhdr, cmsg) --#define WSA_CMSG_SPACE(length) --#define WSA_CMSG_LEN(length) --#endif -+typedef struct _WSACMSGHDR { -+ SIZE_T cmsg_len; -+ INT cmsg_level; -+ INT cmsg_type; -+} WSACMSGHDR,*PWSACMSGHDR,*LPWSACMSGHDR; -+ -+#define WSA_CMSGHDR_ALIGN(length) (((length) + TYPE_ALIGNMENT(WSACMSGHDR)-1) & (~(TYPE_ALIGNMENT(WSACMSGHDR)-1))) -+#define WSA_CMSGDATA_ALIGN(length) (((length) + MAX_NATURAL_ALIGNMENT-1) & (~(MAX_NATURAL_ALIGNMENT-1))) -+#define WSA_CMSG_FIRSTHDR(msg) (((msg)->Control.len >= sizeof(WSACMSGHDR)) ? (LPWSACMSGHDR)(msg)->Control.buf : (LPWSACMSGHDR)NULL) -+#define WSA_CMSG_NXTHDR(msg,cmsg) ((!(cmsg)) ? WSA_CMSG_FIRSTHDR(msg) : ((((u_char *)(cmsg) + WSA_CMSGHDR_ALIGN((cmsg)->cmsg_len) + sizeof(WSACMSGHDR)) > (u_char *)((msg)->Control.buf) + (msg)->Control.len) ? (LPWSACMSGHDR)NULL : (LPWSACMSGHDR)((u_char *)(cmsg) + WSA_CMSGHDR_ALIGN((cmsg)->cmsg_len)))) -+#define WSA_CMSG_DATA(cmsg) ((u_char *)(cmsg) + WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR))) -+#define WSA_CMSG_SPACE(length) (WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR) + WSA_CMSGHDR_ALIGN(length))) -+#define WSA_CMSG_LEN(length) (WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR)) + length) -+typedef INT (WINAPI * LPFN_WSARECVMSG)(SOCKET, LPWSAMSG, LPDWORD, LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE); - - BOOL PASCAL DisconnectEx(SOCKET,LPOVERLAPPED,DWORD,DWORD); - int PASCAL WSARecvMsg(SOCKET,LPWSAMSG,LPDWORD,LPWSAOVERLAPPED,LPWSAOVERLAPPED_COMPLETION_ROUTINE); -diff -urN include-original/winnt.h include/winnt.h ---- include-original/winnt.h 2009-06-30 16:32:32.000000000 +0800 -+++ include/winnt.h 2010-03-23 20:36:29.000000000 +0800 -@@ -43,6 +43,20 @@ - #define UNALIGNED - #endif - -+#ifdef _WIN64 -+#define MAX_NATURAL_ALIGNMENT sizeof(ULONGLONG) -+#define MEMORY_ALLOCATION_ALIGNMENT 16 -+#else -+#define MAX_NATURAL_ALIGNMENT sizeof(DWORD) -+#define MEMORY_ALLOCATION_ALIGNMENT 8 -+#endif -+ -+#ifdef __cplusplus -+#define TYPE_ALIGNMENT(t) __alignof__ (t) -+#else -+#define TYPE_ALIGNMENT(t) FIELD_OFFSET(struct { char x; t test; },test) -+#endif -+ - #ifndef DECLSPEC_ALIGN - #ifdef __GNUC__ - #define DECLSPEC_ALIGN(x) __attribute__((aligned(x))) -diff -urN include-original/ws2tcpip.h include/ws2tcpip.h ---- include-original/ws2tcpip.h 2009-06-30 16:32:32.000000000 +0800 -+++ include/ws2tcpip.h 2010-03-23 20:35:59.000000000 +0800 -@@ -78,6 +78,18 @@ - - #define UDP_NOCHECKSUM 1 - -+/* RFC 3768 */ -+#define MCAST_JOIN_GROUP 41 -+#define MCAST_LEAVE_GROUP 42 -+#define MCAST_BLOCK_SOURCE 43 -+#define MCAST_UNBLOCK_SOURCE 44 -+#define MCAST_JOIN_SOURCE_GROUP 45 -+#define MCAST_LEAVE_SOURCE_GROUP 46 -+#define MCAST_MSFILTER 47 -+ -+#define MCAST_EXCLUDE 0 -+#define MCAST_INCLUDE 1 -+ - /* INTERFACE_INFO iiFlags */ - #define IFF_UP 1 - #define IFF_BROADCAST 2 -@@ -104,6 +116,7 @@ - #define AI_PASSIVE 1 - #define AI_CANONNAME 2 - #define AI_NUMERICHOST 4 -+#define AI_ADDRCONFIG 0x20 - - /* getaddrinfo error codes */ - #define EAI_AGAIN WSATRY_AGAIN -@@ -132,6 +145,25 @@ - struct in_addr imr_interface; - }; - -+struct group_req { -+ u_long gr_interface; -+ struct sockaddr_storage gr_group; -+}; -+ -+struct group_source_req { -+ u_long gsr_interface; -+ struct sockaddr_storage gsr_group; -+ struct sockaddr_storage gsr_source; -+}; -+ -+struct group_filter { -+ u_long gf_interface; -+ struct sockaddr_storage gf_group; -+ u_long gf_fmode; -+ u_long gf_numsrc; -+ struct in_addr gf_slist[1]; -+}; -+ - struct ip_msfilter { - struct in_addr imsf_multiaddr; - struct in_addr imsf_interface; -@@ -356,6 +388,13 @@ - sockaddr_gen iiNetmask; - } INTERFACE_INFO, *LPINTERFACE_INFO; - -+typedef struct _INTERFACE_INFO_EX { -+ u_long iiFlags; -+ SOCKET_ADDRESS iiAddress; -+ SOCKET_ADDRESS iiBroadcastAddress; -+ SOCKET_ADDRESS iiNetmask; -+} INTERFACE_INFO_EX, *_LPINTERFACE_INFO_EX; -+ - /* - The definition above can cause problems on NT4,prior to sp4. - To workaround, include the following struct and typedef and diff --git a/3rdparty/openpgm-svn-r1085/pgm/win64/mingw-w64-bin_x86-64-linux_4.4.1-1openpgm1.diff b/3rdparty/openpgm-svn-r1085/pgm/win64/mingw-w64-bin_x86-64-linux_4.4.1-1openpgm1.diff deleted file mode 100644 index 237bcb3..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/win64/mingw-w64-bin_x86-64-linux_4.4.1-1openpgm1.diff +++ /dev/null @@ -1,53 +0,0 @@ -diff -urN include-original/./ws2tcpip.h x86_64-w64-mingw32/include/./ws2tcpip.h ---- include-original/./ws2tcpip.h 2009-09-10 13:36:49.000000000 +0800 -+++ x86_64-w64-mingw32/include/./ws2tcpip.h 2010-01-21 14:59:13.000000000 +0800 -@@ -12,6 +12,25 @@ - - #include - -+struct group_req { -+ u_long gr_interface; -+ struct sockaddr_storage gr_group; -+}; -+ -+struct group_source_req { -+ u_long gsr_interface; -+ struct sockaddr_storage gsr_group; -+ struct sockaddr_storage gsr_source; -+}; -+ -+struct group_filter { -+ u_long gf_interface; -+ struct sockaddr_storage gf_group; -+ u_long gf_fmode; -+ u_long gf_numsrc; -+ struct in_addr gf_slist[1]; -+}; -+ - struct ip_msfilter { - struct in_addr imsf_multiaddr; - struct in_addr imsf_interface; -@@ -22,6 +41,15 @@ - - #define IP_MSFILTER_SIZE(numsrc) (sizeof(struct ip_msfilter)-sizeof(struct in_addr) + (numsrc)*sizeof(struct in_addr)) - -+/* RFC 3768 */ -+#define MCAST_JOIN_GROUP 41 -+#define MCAST_LEAVE_GROUP 42 -+#define MCAST_BLOCK_SOURCE 43 -+#define MCAST_UNBLOCK_SOURCE 44 -+#define MCAST_JOIN_SOURCE_GROUP 45 -+#define MCAST_LEAVE_SOURCE_GROUP 46 -+#define MCAST_MSFILTER 47 -+ - #define MCAST_INCLUDE 0 - #define MCAST_EXCLUDE 1 - -@@ -277,6 +305,7 @@ - #define AI_PASSIVE 0x1 - #define AI_CANONNAME 0x2 - #define AI_NUMERICHOST 0x4 -+#define AI_ADDRCONFIG 0x20 - - #ifdef __cplusplus - extern "C" { diff --git a/3rdparty/openpgm-svn-r1085/pgm/wsastrerror.c b/3rdparty/openpgm-svn-r1085/pgm/wsastrerror.c deleted file mode 100644 index 2e21449..0000000 --- a/3rdparty/openpgm-svn-r1085/pgm/wsastrerror.c +++ /dev/null @@ -1,372 +0,0 @@ -/* vim:ts=8:sts=8:sw=4:noai:noexpandtab - * - * Winsock Error strings. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include - -#ifdef _WIN32 -# include - - -char* -pgm_wsastrerror ( - const int wsa_errno - ) -{ - switch (wsa_errno) { -#ifdef WSA_INVALID_HANDLE - case WSA_INVALID_HANDLE: return _("Specified event object handle is invalid."); -#endif -#ifdef WSA_NOT_ENOUGH_MEMORY - case WSA_NOT_ENOUGH_MEMORY: return _("Insufficient memory available."); -#endif -#ifdef WSA_INVALID_PARAMETER - case WSA_INVALID_PARAMETER: return _("One or more parameters are invalid."); -#endif -#ifdef WSA_OPERATION_ABORTED - case WSA_OPERATION_ABORTED: return _("Overlapped operation aborted."); -#endif -#ifdef WSA_IO_INCOMPLETE - case WSA_IO_INCOMPLETE: return _("Overlapped I/O event object not in signaled state."); -#endif -#ifdef WSA_IO_PENDING - case WSA_IO_PENDING: return _("Overlapped operations will complete later."); -#endif -#ifdef WSAEINTR - case WSAEINTR: return _("Interrupted function call."); -#endif -#ifdef WSAEBADF - case WSAEBADF: return _("File handle is not valid."); -#endif -#ifdef WSAEACCES - case WSAEACCES: return _("Permission denied."); -#endif -#ifdef WSAEFAULT - case WSAEFAULT: return _("Bad address."); -#endif -#ifdef WSAEINVAL - case WSAEINVAL: return _("Invalid argument."); -#endif -#ifdef WSAEMFILE - case WSAEMFILE: return _("Too many open files."); -#endif -#ifdef WSAEWOULDBLOCK - case WSAEWOULDBLOCK: return _("Resource temporarily unavailable."); -#endif -#ifdef WSAEINPROGRESS - case WSAEINPROGRESS: return _("Operation now in progress."); -#endif -#ifdef WSAEALREADY - case WSAEALREADY: return _("Operation already in progress."); -#endif -#ifdef WSAENOTSOCK - case WSAENOTSOCK: return _("Socket operation on nonsocket."); -#endif -#ifdef WSAEDESTADDRREQ - case WSAEDESTADDRREQ: return _("Destination address required."); -#endif -#ifdef WSAEMSGSIZE - case WSAEMSGSIZE: return _("Message too long."); -#endif -#ifdef WSAEPROTOTYPE - case WSAEPROTOTYPE: return _("Protocol wrong type for socket."); -#endif -#ifdef WSAENOPROTOOPT - case WSAENOPROTOOPT: return _("Bad protocol option."); -#endif -#ifdef WSAEPROTONOSUPPORT - case WSAEPROTONOSUPPORT: return _("Protocol not supported."); -#endif -#ifdef WSAESOCKTNOSUPPORT - case WSAESOCKTNOSUPPORT: return _("Socket type not supported."); -#endif -#ifdef WSAEOPNOTSUPP - case WSAEOPNOTSUPP: return _("Operation not supported."); -#endif -#ifdef WSAEPFNOSUPPORT - case WSAEPFNOSUPPORT: return _("Protocol family not supported."); -#endif -#ifdef WSAEAFNOSUPPORT - case WSAEAFNOSUPPORT: return _("Address family not supported by protocol family."); -#endif -#ifdef WSAEADDRINUSE - case WSAEADDRINUSE: return _("Address already in use."); -#endif -#ifdef WSAEADDRNOTAVAIL - case WSAEADDRNOTAVAIL: return _("Cannot assign requested address."); -#endif -#ifdef WSAENETDOWN - case WSAENETDOWN: return _("Network is down."); -#endif -#ifdef WSAENETUNREACH - case WSAENETUNREACH: return _("Network is unreachable."); -#endif -#ifdef WSAENETRESET - case WSAENETRESET: return _("Network dropped connection on reset."); -#endif -#ifdef WSAECONNABORTED - case WSAECONNABORTED: return _("Software caused connection abort."); -#endif -#ifdef WSAECONNRESET - case WSAECONNRESET: return _("Connection reset by peer."); -#endif -#ifdef WSAENOBUFS - case WSAENOBUFS: return _("No buffer space available."); -#endif -#ifdef WSAEISCONN - case WSAEISCONN: return _("Socket is already connected."); -#endif -#ifdef WSAENOTCONN - case WSAENOTCONN: return _("Socket is not connected."); -#endif -#ifdef WSAESHUTDOWN - case WSAESHUTDOWN: return _("Cannot send after socket shutdown."); -#endif -#ifdef WSAETOOMANYREFS - case WSAETOOMANYREFS: return _("Too many references."); -#endif -#ifdef WSAETIMEDOUT - case WSAETIMEDOUT: return _("Connection timed out."); -#endif -#ifdef WSAECONNREFUSED - case WSAECONNREFUSED: return _("Connection refused."); -#endif -#ifdef WSAELOOP - case WSAELOOP: return _("Cannot translate name."); -#endif -#ifdef WSAENAMETOOLONG - case WSAENAMETOOLONG: return _("Name too long."); -#endif -#ifdef WSAEHOSTDOWN - case WSAEHOSTDOWN: return _("Host is down."); -#endif -#ifdef WSAEHOSTUNREACH - case WSAEHOSTUNREACH: return _("No route to host."); -#endif -#ifdef WSAENOTEMPTY - case WSAENOTEMPTY: return _("Directory not empty."); -#endif -#ifdef WSAEPROCLIM - case WSAEPROCLIM: return _("Too many processes."); -#endif -#ifdef WSAEUSERS - case WSAEUSERS: return _("User quota exceeded."); -#endif -#ifdef WSAEDQUOT - case WSAEDQUOT: return _("Disk quota exceeded."); -#endif -#ifdef WSAESTALE - case WSAESTALE: return _("Stale file handle reference."); -#endif -#ifdef WSAEREMOTE - case WSAEREMOTE: return _("Item is remote."); -#endif -#ifdef WSASYSNOTREADY - case WSASYSNOTREADY: return _("Network subsystem is unavailable."); -#endif -#ifdef WSAVERNOTSUPPORTED - case WSAVERNOTSUPPORTED: return _("Winsock.dll version out of range."); -#endif -#ifdef WSANOTINITIALISED - case WSANOTINITIALISED: return _("Successful WSAStartup not yet performed."); -#endif -#ifdef WSAEDISCON - case WSAEDISCON: return _("Graceful shutdown in progress."); -#endif -#ifdef WSAENOMORE - case WSAENOMORE: return _("No more results."); -#endif -#ifdef WSAECANCELLED - case WSAECANCELLED: return _("Call has been canceled."); -#endif -#ifdef WSAEINVALIDPROCTABLE - case WSAEINVALIDPROCTABLE: return _("Procedure call table is invalid."); -#endif -#ifdef WSAEINVALIDPROVIDER - case WSAEINVALIDPROVIDER: return _("Service provider is invalid."); -#endif -#ifdef WSAEPROVIDERFAILEDINIT - case WSAEPROVIDERFAILEDINIT: return _("Service provider failed to initialize."); -#endif -#ifdef WSASYSCALLFAILURE - case WSASYSCALLFAILURE: return _("System call failure."); -#endif -#ifdef WSASERVICE_NOT_FOUND - case WSASERVICE_NOT_FOUND: return _("Service not found."); -#endif -#ifdef WSATYPE_NOT_FOUND - case WSATYPE_NOT_FOUND: return _("Class type not found."); -#endif -#ifdef WSA_E_NO_MORE - case WSA_E_NO_MORE: return _("No more results."); -#endif -#ifdef WSA_E_CANCELLED - case WSA_E_CANCELLED: return _("Call was canceled."); -#endif -#ifdef WSAEREFUSED - case WSAEREFUSED: return _("Database query was refused."); -#endif -#ifdef WSAHOST_NOT_FOUND - case WSAHOST_NOT_FOUND: return _("Host not found."); -#endif -#ifdef WSATRY_AGAIN - case WSATRY_AGAIN: return _("Nonauthoritative host not found."); -#endif -#ifdef WSANO_RECOVERY - case WSANO_RECOVERY: return _("This is a nonrecoverable error."); -#endif -#ifdef WSANO_DATA - case WSANO_DATA: return _("Valid name, no data record of requested type."); -#endif -#ifdef WSA_QOS_RECEIVERS - case WSA_QOS_RECEIVERS: return _("QOS receivers."); -#endif -#ifdef WSA_QOS_SENDERS - case WSA_QOS_SENDERS: return _("QOS senders."); -#endif -#ifdef WSA_QOS_NO_SENDERS - case WSA_QOS_NO_SENDERS: return _("No QOS senders."); -#endif -#ifdef WSA_QOS_NO_RECEIVERS - case WSA_QOS_NO_RECEIVERS: return _("QOS no receivers."); -#endif -#ifdef WSA_QOS_REQUEST_CONFIRMED - case WSA_QOS_REQUEST_CONFIRMED: return _("QOS request confirmed."); -#endif -#ifdef WSA_QOS_ADMISSION_FAILURE - case WSA_QOS_ADMISSION_FAILURE: return _("QOS admission error."); -#endif -#ifdef WSA_QOS_POLICY_FAILURE - case WSA_QOS_POLICY_FAILURE: return _("QOS policy failure."); -#endif -#ifdef WSA_QOS_BAD_STYLE - case WSA_QOS_BAD_STYLE: return _("QOS bad style."); -#endif -#ifdef WSA_QOS_BAD_OBJECT - case WSA_QOS_BAD_OBJECT: return _("QOS bad object."); -#endif -#ifdef WSA_QOS_TRAFFIC_CTRL_ERROR - case WSA_QOS_TRAFFIC_CTRL_ERROR: return _("QOS traffic control error."); -#endif -#ifdef WSA_QOS_GENERIC_ERROR - case WSA_QOS_GENERIC_ERROR: return _("QOS generic error."); -#endif -#ifdef WSA_QOS_ESERVICETYPE - case WSA_QOS_ESERVICETYPE: return _("QOS service type error."); -#endif -#ifdef WSA_QOS_EFLOWSPEC - case WSA_QOS_EFLOWSPEC: return _("QOS flowspec error."); -#endif -#ifdef WSA_QOS_EPROVSPECBUF - case WSA_QOS_EPROVSPECBUF: return _("Invalid QOS provider buffer."); -#endif -#ifdef WSA_QOS_EFILTERSTYLE - case WSA_QOS_EFILTERSTYLE: return _("Invalid QOS filter style."); -#endif -#ifdef WSA_QOS_EFILTERTYPE - case WSA_QOS_EFILTERTYPE: return _("Invalid QOS filter type."); -#endif -#ifdef WSA_QOS_EFILTERCOUNT - case WSA_QOS_EFILTERCOUNT: return _("Incorrect QOS filter count."); -#endif -#ifdef WSA_QOS_EOBJLENGTH - case WSA_QOS_EOBJLENGTH: return _("Invalid QOS object length."); -#endif -#ifdef WSA_QOS_EFLOWCOUNT - case WSA_QOS_EFLOWCOUNT: return _("Incorrect QOS flow count."); -#endif -#ifdef WSA_QOS_EUNKOWNPSOBJ - case WSA_QOS_EUNKOWNPSOBJ: return _("Unrecognized QOS object."); -#endif -#ifdef WSA_QOS_EPOLICYOBJ - case WSA_QOS_EPOLICYOBJ: return _("Invalid QOS policy object."); -#endif -#ifdef WSA_QOS_EFLOWDESC - case WSA_QOS_EFLOWDESC: return _("Invalid QOS flow descriptor."); -#endif -#ifdef WSA_QOS_EPSFLOWSPEC - case WSA_QOS_EPSFLOWSPEC: return _("Invalid QOS provider-specific flowspec."); -#endif -#ifdef WSA_QOS_EPSFILTERSPEC - case WSA_QOS_EPSFILTERSPEC: return _("Invalid QOS provider-specific filterspec."); -#endif -#ifdef WSA_QOS_ESDMODEOBJ - case WSA_QOS_ESDMODEOBJ: return _("Invalid QOS shape discard mode object."); -#endif -#ifdef WSA_QOS_ESHAPERATEOBJ - case WSA_QOS_ESHAPERATEOBJ: return _("Invalid QOS shaping rate object."); -#endif -#ifdef WSA_QOS_RESERVED_PETYPE - case WSA_QOS_RESERVED_PETYPE: return _("Reserved policy QOS element type."); -#endif - default: return _("Unknown."); - } -} - -char* -pgm_adapter_strerror ( - const int adapter_errno - ) -{ - switch (adapter_errno) { -#ifdef ERROR_ADDRESS_NOT_ASSOCIATED - case ERROR_ADDRESS_NOT_ASSOCIATED: return _("DHCP lease information was available."); -#endif -#ifdef ERROR_BUFFER_OVERFLOW - case ERROR_BUFFER_OVERFLOW: return _("The buffer to receive the adapter information is too small."); -#endif -#ifdef ERROR_INVALID_DATA - case ERROR_INVALID_DATA: return _("Invalid adapter information was retrieved."); -#endif -#ifdef ERROR_INVALID_PARAMETER - case ERROR_INVALID_PARAMETER: return _("One of the parameters is invalid."); -#endif -#ifdef ERROR_NOT_ENOUGH_MEMORY - case ERROR_NOT_ENOUGH_MEMORY: return _("Insufficient memory resources are available to complete the operation."); -#endif -#ifdef ERROR_NO_DATA - case ERROR_NO_DATA: return _("No adapter information exists for the local computer."); -#endif -#ifdef ERROR_NOT_SUPPORTED - case ERROR_NOT_SUPPORTED: return _("The GetAdaptersInfo function is not supported by the operating system running on the local computer.."); -#endif - default: return _("Other."); - } -} - -char* -pgm_win_strerror ( - char* buf, - size_t buflen, - const int win_errno - ) -{ - const DWORD nSize = buflen; - FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, - NULL, /* source */ - win_errno, /* message id */ - MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), /* language id */ - (LPTSTR)buf, - buflen, - NULL); /* arguments */ - return buf; -} -#endif /* _WIN32 */ - -/* eof */ diff --git a/3rdparty/openpgm-svn-r1135-0001-sigsegv-in-txw.patch b/3rdparty/openpgm-svn-r1135-0001-sigsegv-in-txw.patch new file mode 100644 index 0000000..af6d4fd --- /dev/null +++ b/3rdparty/openpgm-svn-r1135-0001-sigsegv-in-txw.patch @@ -0,0 +1,28 @@ +Problem: OpenPGM generates a SIGSEGV upon accessing peer->sock->use_pgmcc. + This is because the code assumes that the queue list entry comes first + in the definition of struct pgm_peer_t, which it does not (anymore?). + +diff -Naur openpgm-r1135-pristine/pgm/receiver.c openpgm-svn-r1135/pgm/receiver.c +--- openpgm-r1135-pristine/pgm/receiver.c 2010-09-06 20:41:52.000000000 +0200 ++++ openpgm-svn-r1135/pgm/receiver.c 2010-09-24 12:40:07.000000000 +0200 +@@ -71,7 +71,9 @@ + pgm_assert (NULL != window); + pgm_assert (NULL != window->ack_backoff_queue.tail); + +- const struct pgm_peer_t* peer = (const struct pgm_peer_t*)window->ack_backoff_queue.tail; ++ const struct pgm_peer_t* peer = (const struct pgm_peer_t*)window->ack_backoff_queue.tail->data; ++ pgm_assert (NULL != peer); ++ + pgm_assert (peer->sock->use_pgmcc); + return peer->ack_rb_expiry; + } +@@ -416,6 +418,9 @@ + sock->ack_c_p); + peer->spmr_expiry = now + sock->spmr_expiry; + ++/* Prepare ack_link */ ++ peer->ack_link.data = peer; ++ + /* add peer to hash table and linked list */ + pgm_rwlock_writer_lock (&sock->peers_lock); + pgm_peer_t* entry = _pgm_peer_ref (peer); diff --git a/3rdparty/openpgm-svn-r1135-0002-correct-checksum-calculation.patch b/3rdparty/openpgm-svn-r1135-0002-correct-checksum-calculation.patch new file mode 100644 index 0000000..b78dc3c --- /dev/null +++ b/3rdparty/openpgm-svn-r1135-0002-correct-checksum-calculation.patch @@ -0,0 +1,17 @@ +Problem: OpenPGM may calculate incorrect checksums when sending RDATA packets + if no ODATA packet has been sent before. When and why this + happens exactly is unknown. +Solution: Always force recomputation of checksums until a more suitable + solution is found. + +--- openpgm-r1135-pristine/pgm/source.c 2010-09-09 03:24:47.000000000 +0200 ++++ openpgm-svn-r1135/pgm/source.c 2010-09-30 18:32:04.000000000 +0200 +@@ -2295,7 +2295,7 @@ + header->pgm_checksum = 0; + const size_t pgm_header_len = tpdu_length - ntohs(header->pgm_tsdu_length); + uint32_t unfolded_header = pgm_csum_partial (header, pgm_header_len, 0); +- uint32_t unfolded_odata = pgm_txw_get_unfolded_checksum (skb); ++ uint32_t unfolded_odata = pgm_csum_partial (skb->data, ntohs(header->pgm_tsdu_length), 0); + header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, unfolded_odata, pgm_header_len)); + + /* congestion control */ diff --git a/3rdparty/openpgm-svn-r1135-0003-fix-rdata-congestion-control.patch b/3rdparty/openpgm-svn-r1135-0003-fix-rdata-congestion-control.patch new file mode 100644 index 0000000..c6e721d --- /dev/null +++ b/3rdparty/openpgm-svn-r1135-0003-fix-rdata-congestion-control.patch @@ -0,0 +1,42 @@ +Problem: When enough lost data accumulates, RDATA packets are sent at + a massively reduced rate and block the congestion control + window for subsequent RDATA packets. This results in a + transmission rate of close to zero for up to 15 seconds in + experiments. +Solution: Allow sending of RDATA packets even when congestion control + would normally disallow it, but still consume a token if + there is one left. + +diff -Naur openpgm-r1135-pristine/pgm/source.c openpgm-svn-r1135/pgm/source.c +--- openpgm-r1135-pristine/pgm/source.c 2010-09-09 03:24:47.000000000 +0200 ++++ openpgm-svn-r1135/pgm/source.c 2010-09-24 17:40:12.000000000 +0200 +@@ -2299,13 +2299,13 @@ + header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, unfolded_odata, pgm_header_len)); + + /* congestion control */ +- if (sock->use_pgmcc && +- sock->tokens < pgm_fp8 (1)) +- { ++// if (sock->use_pgmcc && ++// sock->tokens < pgm_fp8 (1)) ++// { + // pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Token limit reached.")); +- sock->blocklen = tpdu_length; +- return FALSE; +- } ++// sock->blocklen = tpdu_length; ++// return FALSE; ++// } + + const ssize_t sent = pgm_sendto (sock, + sock->is_controlled_rdata, /* rate limited */ +@@ -2323,7 +2323,8 @@ + const pgm_time_t now = pgm_time_update_now(); + + if (sock->use_pgmcc) { +- sock->tokens -= pgm_fp8 (1); ++ if (sock->tokens >= pgm_fp8 (1)) ++ sock->tokens -= pgm_fp8 (1); + sock->ack_expiry = now + sock->ack_expiry_ivl; + } + diff --git a/3rdparty/openpgm-svn-r1135/doc/draft-ietf-rmt-bb-pgmcc-03.txt b/3rdparty/openpgm-svn-r1135/doc/draft-ietf-rmt-bb-pgmcc-03.txt new file mode 100644 index 0000000..6f1869c --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/doc/draft-ietf-rmt-bb-pgmcc-03.txt @@ -0,0 +1,1226 @@ + +Internet Engineering Task Force RMT WG +INTERNET-DRAFT Luigi Rizzo/U. Pisa +draft-ietf-rmt-bb-pgmcc-03.txt Gianluca Iannaccone/Intel + Lorenzo Vicisano/Cisco + Mark Handley/UCL + 12 July 2004 + Expires: January 2005 + + + PGMCC single rate multicast congestion control: + Protocol Specification + + + +Status of this Document + +This document is an Internet-Draft and is in full conformance with all +provisions of Section 10 of RFC2026. + +Internet-Drafts are working documents of the Internet Engineering Task +Force (IETF), its areas, and its working groups. Note that other groups +may also distribute working documents as Internet-Drafts. + +Internet-Drafts are valid for a maximum of six months and may be +updated, replaced, or obsoleted by other documents at any time. It is +inappropriate to use Internet-Drafts as reference material or to cite +them other than as a "work in progress". + +The list of current Internet-Drafts can be accessed at +http://www.ietf.org/ietf/1id-abstracts.txt + +To view the list Internet-Draft Shadow Directories, see +http://www.ietf.org/shadow.html. + +This document is a product of the IETF RMT WG. Comments should be +addressed to the authors, or the WG's mailing list at rmt@lbl.gov. + + + Abstract + + + This document describes PGMCC, a single rate multicast + congestion control scheme which is TCP-friendly and achieves + scalability, stability and fast response to variations in + network conditions. PGMCC is suitable for both non-reliable + + + +Rizzo/Iannaccone/Vicisano/Handley [Page 1] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + + and reliable data transfers. It is mainly designed for NAK- + based multicast protocols, and uses a window-based, TCP-like + control loop using positive ACKs between one representative of + the receiver group (the ACKER) and the sender. The ACKER is + selected dynamically and may change over time. + + PGMCC is made of two components: a window-based control loop, + which closely mimics TCP behavior, and a fast and low-overhead + procedure to select (and track changes of) the ACKER. The + scheme is robust to measurement errors, and supports fast + response to changes in the receiver set and/or network + conditions. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Rizzo/Iannaccone/Vicisano/Handley [Page 2] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + + Table of Contents + + + 1. Introduction. . . . . . . . . . . . . . . . . . . . . . 4 + 1.1. Terminology. . . . . . . . . . . . . . . . . . . . . 4 + 2. Protocol Overview . . . . . . . . . . . . . . . . . . . 4 + 2.1. Packet Contents. . . . . . . . . . . . . . . . . . . 6 + 2.1.1. Data Packets. . . . . . . . . . . . . . . . . . . 6 + 2.1.2. Feedback Packets. . . . . . . . . . . . . . . . . 7 + 2.1.3. Field sizes and formats . . . . . . . . . . . . . 8 + 2.2. Window-based controller. . . . . . . . . . . . . . . 9 + 2.3. Acker Selection. . . . . . . . . . . . . . . . . . . 11 + 2.3.1. Initial Acker election. . . . . . . . . . . . . . 11 + 2.3.2. Acker dropouts. . . . . . . . . . . . . . . . . . 12 + 2.4. TCP Throughput Equation. . . . . . . . . . . . . . . 12 + 2.5. RTT measurement. . . . . . . . . . . . . . . . . . . 13 + 2.5.1. Explicit Timestamp. . . . . . . . . . . . . . . . 13 + 2.5.2. Implicit timestamp. . . . . . . . . . . . . . . . 13 + 2.5.3. Sequence numbers. . . . . . . . . . . . . . . . . 14 + 2.5.4. Recommendations . . . . . . . . . . . . . . . . . 15 + 2.6. Loss rate measurement. . . . . . . . . . . . . . . . 15 + 2.7. Timeouts . . . . . . . . . . . . . . . . . . . . . . 16 + 2.8. Interaction with feedback suppression + schemes . . . . . . . . . . . . . . . . . . . . . . . . . 16 + 2.9. Interaction with ECN . . . . . . . . . . . . . . . . 17 + 3. Procedures - Sender . . . . . . . . . . . . . . . . . . 17 + 4. Procedures -- Receiver. . . . . . . . . . . . . . . . . 18 + 5. Security Considerations . . . . . . . . . . . . . . . . 19 + 6. Authors' Addresses. . . . . . . . . . . . . . . . . . . 20 + 7. Acknowledgments . . . . . . . . . . . . . . . . . . . . 20 + 8. Full Copyright Statement. . . . . . . . . . . . . . . . 21 + + + + + + + + + + + + + + + + + + + + +Rizzo/Iannaccone/Vicisano/Handley [Page 3] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +1. Introduction + +This document describes PGMCC, a single rate multicast congestion +control scheme which is TCP-friendly and achieves scalability, stability +and fast response to variations in network conditions. + +PGMCC is designed for multicast sessions with one sender and one or more +receivers, and is a good match for transport protocols using negative +acknowledgements (NAKs) to collect feedback from the receivers. The +congestion control scheme implemented by PGMCC closely mimics the +congestion-control behavior of TCP, as it uses a window-based control +loop which is run between the sender and a selected receiver called the +ACKER. The role of the ACKER is to provide timely feedback in the same +way as a TCP receiver; additionally, the ACKER is selected dynamically +among the receivers as the one which would experience the lowest +throughput if separate TCP sessions were run between the sender and each +of the receivers. + +Scalability in PGMCC comes from the use of negative acknowledgements +(NAKs) for collecting feedback from receivers other than the ACKER. As +a consequence, the usual techniques for NAK suppression and aggregation +can be used to reduce the amount of feedback to the source and improve +the scalability of the scheme. + +PGMCC is designed to completely decouple congestion control from data +integrity. As a consequence, the scheme can work with both reliable data +transfer and unreliable communication protocols such as those used for +video or audio streaming. + +While designed with multicast in mind, PGMCC can be equally used as a +replacement for TCP for unicast sessions which require a lower degree of +reliability than what TCP offers. + + +1.1. Terminology + +In this document, the key words "MUST", "MUST NOT", "REQUIRED", "SHALL", +"SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" are to be interpreted as described in RFC 2119 and indicate +requirement levels for compliant PGMCC implementations. + + +2. Protocol Overview + +PGMCC is based on two separate but complementary mechanisms: + + o A window-based control loop which closely emulates TCP congestion + control. + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2. [Page 4] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + + The window-based control loop is simply an adaptation of the TCP + congestion control scheme to transport protocols where missing + (because of network errors or congestion) data packets are not + necessarily retransmitted, and so the congestion control scheme + cannot rely on cumulative acknowledgements. In PGMCC, the + ``congestion window'' is simulated using a token-based scheme which + permits congestion control to be decoupled from retransmission + state. One of the receivers in the group operates as the ACKER, i.e. + the node in charge of sending positive acknowledgements back to the + source and thus controlling the rate of the transfer. + + + o A procedure to select the ACKER. + The purpose of this procedure is to make sure that, in presence of + multiple receivers, the ACKER is dynamically selected to be the + receiver which would have the lowest throughput if separate TCP + sessions were run between the sender and each receiver. + For the acker selection mechanism, PGMCC uses a throughput equation + to determine the expected throughput for a given receiver as a + function of the loss rate and round-trip time. Unlike other schemes + [2], the TCP throughput equation is not used to determine the actual + sending rate, which is completely controlled by the window-based + control loop. + + +In principle, PGMCC's congestion control mechanism works as follows: + + + o Receivers measure the loss rate and feed this information back to + the sender, either in ACK or NAK messages. + + + o The sender also uses these feedback messages to measure the round- + trip time (RTT) to each receiver. + + + o The loss rate and RTT are then fed into PGMCC's throughput equation, + to determine the expected throughput to that receiver. + + + o The sender then selects as the acker the receiver with the lowest + expected throughput, as computed by the equation. + +The dynamics of the acker selection mechanism are sensitive to how the +measurements are performed and applied. In the rest of this document we +suggest specific mechanisms to perform and apply these measurements. +Other mechanisms are possible, but it is important to understand how the +interactions between mechanisms affect the dynamics of PGMCC. + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2. [Page 5] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +2.1. Packet Contents + +Before specifying the sender and receiver functionality, we describe the +information required by PGMCC to perform its tasks. This information is +carried in the data packets sent by the sender, and in the feedback +packets sent by the receiver. As PGMCC will be used along with some +transport protocol, the actual data and feedback packets will contain +further information for use by the protocol itself. For this reason, we +do not specify packet formats, as these depend on the details of the +transport protocol used. + +Note that the requirements of the transport protocol in terms of packet +generation may differ from those of PGMCC. As an example, most NAK-based +reliable multicast protocols do not use positive acknowledgements, but +PGMCC requires ACKs for clocking out data packets; unreliable transport +protocols might have no interest in generating NAKs for data integrity +purposes, yet PGMCC depends on NAKs reaching the data sender in order to +elect the ACKER. + + +Implementors may decide to insert PGMCC-related information in already +existing protocol packets whenever possible, but in cases such as the +ones described in the previous paragraph, it might be necessary to +define and generate new packets exclusively for congestion control +purposes. As an example, in a prototype implementation of PGMCC on top +of the PGM protocol [7], some of the information used by PGMCC is +already present in the original protocol packets, and PGMCC-specific +information is carried as PGM options in ODATA and NAK packets. However, +a new packet type has been defined for ACKs, which are generated +according to the rules defined in this document. + + +2.1.1. Data Packets + +Each data packet sent by the data sender contains the following +information: + + + o A SEQUENCE NUMBER. This number is incremented by one for each data + packet transmitted. The field must be sufficiently large that it + does not wrap causing two different packets with the same sequence + number to be in the receiver's recent packet history at the same + time. + + + o A TIMESTAMP (or equivalent information, see Section 2.5) indicating + when the packet with this sequence number has been sent. There is + no requirement for synchronized clocks between the sender and the + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2.1.1. [Page 6] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + + receivers. The timestamp is used to measure network round-trip + times, so needs sufficient resolution for this task. A resolution + of 1ms would be adequate. + + + o The ACKER IDENTITY, i.e. the identity of the receiver in charge of + sending an acknowledgement for this data packet. The ACKER is + elected as a result of the process described in Section 2.3. + A special value is used to indicate that no ACKER is designated for + this packet -- this can happen at the beginning of a session or when + the current ACKER leaves the group. Receivers interpret this value + as a request to elect a new acker. + + +2.1.2. Feedback Packets + +There are two types of feedback packets used by PGMCC: ACK packets and +NAK packets. +ACK packets are generated by the current ACKER, and are used to detect +loss or successful delivery of packets, and to regulate the throughput +accordingly. ACK packets also contain information used to determine the +TCP-equivalent throughput for the ACKER. +NAK packets are sent by any receiver who experiences loss. They contain +information used to determine the TCP-equivalent throughput for that +receiver. In an actual protocol instantiation (such as PGM [7]), NAK +packets might also be used by the protocol to request the retransmission +of specific packets, and indicate the identity of the packet being +requested. + +Both ACK and NAK packets are sent by data receivers, and contain the +following information: + + + o The TIMESTAMP (or equivalent information) derived from the most + recently received data packet according to one of the techniques + described in Section 2.5. + This value is used by the sender to measure the RTT to the receiver + who generated this feedback packet. + + + o ``p'', the receiver's current estimate of the LOSS RATE. The loss + rate is measured by receivers as described in Section 2.6 + +In addition to the above, ACK packets (sent by the acker designated in +the corresponding data packets) must also contain the following +information: + + + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2.1.2. [Page 7] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + + o RX_MAX, the highest sequence number among received data packets + (taking care to deal with sequence number wrapping correctly). + + o ACK_BITMAP, a bitmap indicating the receive status of the latest N + (typically N=32) data packets with sequence numbers RX_MAX-(N-1) to + RX_MAX. + + +This information is used by the sender to record which packets have been +received or lost, and manipulate the transmit window accordingly. Note +that each ACK packet contains information about multiple packets, and +this increases the robustness of the scheme to loss of ACK packets. +This is necessary because ACKs are not sent reliably (unlike TCP's ACKs, +which are cumulative). + + +2.1.3. Field sizes and formats + +The following sizes and formats are suggested for the various fields +used by PGMCC and transmitted over the network: + + + o SEQUENCE NUMBERS + 32 bit, unsigned, network order. + + + o TIMESTAMPS + 32 bit, unsigned, network order. A resolution of 1ms or better is + desirable. + + + o ACKER IDENTITY + Same size and format of a network layer address (e.g. 32 bit for + IPv4). Note though that using an IP address for the Acker Identify + will cause problems with NAT traversal. Transport protocol + designers might examine the SSRC mechanism used by RTP [6] as an + alternative form of node identifier that could be used as Acker + Identity. + + + o LOSS RATE (``p'') + 16-bit unsigned integer, in network format, with 0 indicating no + loss and 2^16-1 indicating 100% loss. + + + o ACK BITMAP + 32-bit, in network format, with least significant bit indicating + receive status of packet RX_MAX. + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2.1.3. [Page 8] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +2.2. Window-based controller + +In a window-based congestion control scheme such as TCP, the +``congestion window'' represents, among other things, the maximum amount +of packets in flight at any time, which in turn controls the throughput +of the session. The sender keeps track of the actual number of packets +in flight, basing on its transmissions and the reception of +acknowledgements. + +The sender may dynamically change the size of the window, according to +the congestion control scheme being used. In TCP, and PGMCC, an +``Additive Increase Multiplicative Decrease'' (AIMD) scheme is used: in +absence of loss, the window is increased by some fixed amount (typically +one packet) per round trip time (RTT), whereas upon loss the window is +reduced to a fraction of its original value (typically halved) in each +RTT in which a loss event is experienced. + +In PGMCC the window is managed using a token-based mechanism, controlled +by two variables: + + o A ``Window Size'', W, which describes the current window size in + packets. + + o A ``Token Count'', T, which indicates the number of packets that can + be transmitted. T is bounded above by W. It is decremented every + time a packet is transmitted, and incremented every time an ACK is + received, according to the rules below. + +Note that these two variables need to hold non-integer data. Typically +a fixed point representation with at least 16 bits for both integer and +fractional parts would be acceptable for implementation purposes. + +The information contained in each ACK is used to determine how many new +packets are acknowledged by that ACK, and whether there are +unacknowledged packets which were not reported in previous ACKs. The +sender also schedules a timeout to react in case no ACKs are received. + +The sender behaves as follows: + + + o INITIALIZATION + At startup, or after a timeout, both W and T are set to 1. + + + o ACK RECEPTION, NO LOSS DETECTED + If the incoming ACK reports new acknowledged packets, and no loss + (as defined in the next paragraph) is detected, then the window is + inflated by one packet per RTT. + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2.2. [Page 9] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + + NOTE: during the slow-start phase, TCP opens the window + exponentially up to the SSTHRESH value, which is computed by TCP + according to the dynamics of the session and updated upon losses. + + We do recommend that PGMCC uses a similar strategy, but using a + fixed, small value for SSTHRESH (e.g. 4 packets). In fact, due to + the dynamicity of the ACKER, which might change on every single + packet, it is hard to compute a reliable estimate of the SSTHRESH + without keeping state for multiple receivers, and the benefits are + small in any event. + + In summary, the reaction to ACK reception on no loss modifies T and + W as follows (here, N is the number of new packets acknowledged by + the incoming ACK): + + if (W < SSTHRESH) then + D = min(N, SSTHRESH - W) // use the first D acks for + exp.opening + N = N - D // and the remaining ones for + linear opening + T = T + 2*D + W = W + D + endif + // do linear window opening with the remaining acks + T = T + N * ( 1 + 1/W ) + W = W + N/W + + + o PACKET TRANSMISSION + One token is consumed for each packet transmitted: + + T = T - 1 + + + o ACK RECEPTION, LOSS DETECTED + If the incoming ACK reports an unacknowledged data packet which is + followed by at least 3 acknowledged data packets, then the packet is + assumed to be lost and PGMCC reacts by halving the window, in the + same way as TCP after 3 duplicate acknowledgements. This is + achieved by modifying T and W as follows: + + T = T - W/2 , W = W/2 + + to simulate the multiplicative decrease. + Additionally, all window manipulation is suspended for the + subsequent RTT. This is achieved by recording the current transmit + sequence number, and canceling any further manipulation of the + window until feedback is received for the next transmitted packet, + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2.2. [Page 10] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + + or until a timeout occurs. + + + + +2.3. Acker Selection + +The ACKER selection process in PGMCC aims at locating the receiver which +would have the lowest throughput if each receiver were using a separate +TCP connection to transfer data. + +Because the steady-state throughput of a TCP connection can be +characterized in a reasonably accurate way in terms of its loss rate and +round trip time [3], the throughput for each receiver can be estimated +by using these two parameters. + +Whenever an ACK or NAK packet from any of the receivers reaches it, the +sender is able to compute the expected throughput T_i for that receiver +by using the equation shown in Section 2.4, with the round trip time RTT +and loss rate p and measured as described in Sections 2.5 and 2.6, +respectively. At any given time, the sender stores the expected +throughput for the current ACKER, T_acker. This value is updated every +time an ACK or NAK from the current ACKER is received (note that, after +a new ACKER is selected, the sender will typically receive ACKs from the +old ACKER for one RTT, and the feedback from different ACKERs might be +interleaved if the paths leading to them have different round trip +times). + +Whenever an ACK or NAK is received from another node i (a previous ACKER +or some other receiver), the expected throughput T_i for that node is +computed, and compared with T_acker. Node i is selected as the new +acker if + + T_i < C * T_acker + +where the constant C between 0 and 1 provides some hysteresis and avoids +too frequent oscillations in the choice of the ACKER. A suggested value +for C is 0.75. + +Note that, from an implementation point of view (see Section 2.4), it is +more convenient to compute T_i ^(-2), so the above equation must be +modified accordingly. + + +2.3.1. Initial Acker election + +Upon reception of a data packet reporting that no acker is currently +selected, receivers generate a dummy NAK report which is used to elect + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2.3.1. [Page 11] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +the initial acker. The NAK is sent with the usual feedback suppression +mechanism dictated by the transport protocol (possibly with shorter time +constants) to avoid feedback implosion, and the sender will select the +source of the first incoming NAK as the new ACKER. + + +2.3.2. Acker dropouts + + +If the ACKER decides to disconnect from the session, it can cause the +session to stop. To avoid this, it is recommended that an ACKER deciding +to leave the session informs the sender by sending an ACK packet (or a +duplicate) carrying an "ACKER_LEAVING" option. The reception of this +packet by the sender will in turn trigger an initial acker election +phase. + + + +2.4. TCP Throughput Equation + +Any realistic equation of TCP throughput as a function of loss event +rate and RTT should be suitable for use in PGMCC. Unlike other schemes +[2] where the throughput equation directly controls the transmit rate, +in PGMCC the equation is used only for acker selection purposes, and the +throughput values are only compared among themselves. As a consequence, +we can use the following equation, derived from the one presented in [3] +by setting RTO = 4 * RTT (as it is common practice): + + M = 1/T = RTT_i * sqrt(p) * (1 + 9*p * (1 + 32*(p)^2)) + + +where + + M = 1/T is proportional to the inverse of the throughput for the + receiver under consideration; + + RTT is the round trip time for the receiver under consideration; + + p is the loss rate for the receiver under consideration, between 0 + and 1.0; + +and multiplying constants are omitted. + +The above equation is accurate on a wide range of loss rates, and also +covers situations where retransmission timeouts have a significant +impact on the throughput of the protocol. + +Note that when p=0, the equation yields 1/T = M = 0. This does not + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2.4. [Page 12] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +constitute a problem as we can still compare the M values computed for +different receivers to determine the acker. Also note that it is easier +to compute M^2 instead of M, because the former does not require the use +of sqrt(). + +In future, different throughput equations may be substituted for this +equation. The requirement is that the throughput equation be a +reasonable approximation of the sending rate of TCP for conformant TCP +congestion control. + +The parameters p and RTT need to be measured or calculated by a PGMCC +implementation. The measurement of RTT is specified in Section 2.5; the +measurement of p is specified in Section 2.6. + +2.5. RTT measurement + +In PGMCC, the RTT is measured by the sender making use of the timestamp +(or equivalent information) echoed back by each receiver in feedback +messages. Three procedures are possible to measure the RTT, as follows. +In no case is it required to have clock synchronization between sender +and receivers. + + +2.5.1. Explicit Timestamp + +This first technique relies on the transmission of a timestamp TS_j with +each data packet j. +The receiver will record the most recently received timestamp, and will +echo it back to the source when generating an ACK or a NAK. If the +feedback is delayed, the time elapsed between the reception of the +timestamp and the generation of the feedback should be added to the +echoed timestamp. +The sender computes the RTT by subtracting the received timestamp from +the current value of the clock. + +The resolution of the timestamp value should be good enough for +reasonable precision measurement of typical network round trip times. If +receivers need to apply correction for delayed feedback, it is necessary +that receivers know the resolution of the timestamp clock. A suggested +value is 1ms. + + +2.5.2. Implicit timestamp + +With this technique, the sender will record a timestamp TS_j for each +transmitted data packet j, but the timestamp will not be transmitted +with the packet itself. +The receiver will record the most recently received sequence number, and + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2.5.2. [Page 13] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +will echo it back to the source when generating an ACK or a NAK. +The sender computes the RTT by looking up the timestamp associated with +the sequence number received in the feedback packet, and subtracting it +from the current clock value. + +If the feedback from the receiver is delayed, as it is commonly the case +for NAKs, the receiver can compute, and send back to the source, a +correction term corresponding to the time elapsed between the reception +of the timestamp and the generation of the feedback. The correction term +will then be subtracted by the sender in order to obtain the correct +estimate of the RTT. + +This RTT measurement technique is equivalent to the previous one, but it +saves some space in data packets as the timestamp does not need to be +sent explicitly. Feedback packets might become larger if the correction +value is transmitted explicitly; but in many cases, the sequence number +will already be present for other reasons (e.g. ACK packets), and +wherever space is a concern the sequence number and the correction term +can be packed in a single 32-bit word without loss of precision. + + +2.5.3. Sequence numbers + +This technique is the least precise, but it does not rely on the +presence of a high resolution clock on the nodes. +The sender will not compute any timestamp, and just send data packets +with their sequence number j. +The receiver will record the most recently received sequence number, and +will echo it back to the source when generating an ACK or a NAK. +The sender computes the RTT as the difference between the most recently +sent sequence number and the sequence number received from the ACK or +NAK packet. + +Note that in this case the RTT is not measured in seconds, but in +"sequence numbers", which are monotonically, but not uniformly, +increasing with time. The two measurements are equivalent if the sender +transmits at a constant rate. When the data rate changes over time (as +it is normally happens, given that PGMCC controls the actual data rate), +then the "measured" RTT values grow with the actual transmit rate. This +can influence the correctness of the results when comparing two +measurement done over different and only partially overlapping time (and +sequence number) intervals where the transmit rate incurs a significant +change. + + + + + + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2.5.3. [Page 14] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +2.5.4. Recommendations + +Whenever possible, the measurement of the RTT should be carried out +using either explicit or implicit timestamps, and by keeping track of +the "correction term" (the delay between data reception and feedback +generation). + +If the receiver does not have a clock with a suitable resolution, the +correction term might not be present (or be inaccurate). In this case +the timestamps received by the sender on NAK packets might be in error, +in the worst case, by as much as the packet interarrival time. This +error will normally not be present on ACK packets, which are sent +immediately. A suitable correction should be applied by the sender in +order to avoid systematic errors. + +The measurement based on sequence numbers is less accurate, but also +less sensitive to errors due to the lack of the correction term. In +fact, the measurement error induced by the lack of the correction term +can be at most one unit. This suggests that, when the correction term +is not available, measurements based on sequence numbers should be +favoured. Simulations have shown that the acker selection mechanism +performs moderately better when the RTT measurement is based on +timestamps, but performance is reasonably good also with measurements +based on sequence numbers. + + + +2.6. Loss rate measurement + + +The loss measurement in PGMCC is entirely performed by receivers. The +measurement results do not directly influence the transmit rate, but are +only used for comparison purposes. As a consequence, the scheme is +reasonably robust to different measurement techniques, as long as they +are not influenced too strongly by single loss events. + +The main method suggested for loss measurement is Exponentially Weighted +Moving Average (EWMA), which is formally equivalent to a single-pole +digital low pass filter applied to a binary signal x_i, where x_i = 1 if +packet i is lost, x_i = 0 if packet i is successfully received. + +The loss rate p_i upon reception or detection of loss of packet i is +computed as + + + p_i = c_p * p_{i-1} + (1 - c_p ) * p_i + where the constant c_p between 0 and 1 is related to the bandpass of +the filter. Experiments have shown good performance with c = 500/65536, + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2.6. [Page 15] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +and computations performed with fixed point arithmetic and 16 fractional +digits. + +As an alternative to EWMA, the technique used in TFRC [2] can be +adopted. Simulations have shown a moderate improvement in the acker +selection mechanism by measuring loss using the TFRC loss estimator, +which is however slightly more expensive to compute than the EWMA loss +estimator in presence of packet reordering. + + +2.7. Timeouts + + +When a packet is transmitted, the sender schedules a timeout to prevent +stalls upon loss of ACKs or disconnection of the ACKER. In TCP, which +has a similar problem, the timeout value is computed by accumulating +statistics (SRTT and RTTVAR) on RTT samples, starting from a default +initial value (3s) when no RTT samples are available. + +PGMCC can use a similar scheme to compute the timeouts, remembering that +upon ACKER changes (which may be very frequent), the computation of SRTT +and RTTVAR must be restarted from the beginning, unless the sender +decides to keep state for at least a small number of recent ackers. + +Because the ACKER can leave the group without notifying the sender, +after a number of successive timeouts the sender MUST force the election +of a new ACKER. We recommend this new election to be performed after +two successive timeouts. + + +2.8. Interaction with feedback suppression schemes + + +Several schemes are used by NAK-based multicast protocols to reduce the +amount of feedback directed toward the source and make the protocol +scale with large populations of receivers. Such schemes typically rely +on randomly delaying NAK generation, and suppressing pending NAKs when +an equivalent NAK or a retransmission is heard; or, intermediate nodes +such as routers can implement some form of feedback aggregation and +filtering. + +Such schemes might prevent NAKs from potential ACKER candidates from +reaching the source. This filtering might impact the speed at which +PGMCC selects the correct ACKER, though initial experience from +simulations seem to suggest that PGMCC behavior is not severely affected +by NAK suppression schemes. + + + + + +Rizzo/Iannaccone/Vicisano/Handley Section 2.8. [Page 16] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +2.9. Interaction with ECN + + +PGMCC can use ECN notifications in much the same way as actual losses, +and use such notifications to control the throughput of the session. + +At the receiver, ECN-marked data packets can be considered as lost +packets for the purpose of loss rate computation and ACK/NAK generation. +If the ACKER sends an ACK for ECN-marked packets, that ACK MUST report +that the packet being acknowledged that was ECN marked. Similarly the +ACKER must indicate in the ACK packet's received packets bitmap that the +packet was ECN-marked, or that the packet was lost. + +We note that to support use of the ECN nonce, the ACK packet's received +packets bitmap would require two bits per packet being reported. + + +3. Procedures - Sender + +The following pseudo-code specifies the complete behavior of the sender +in PGMCC. + + +initialization: + T = 1 ; W = 1 ; /* initialize window and number of tokens */ + RETRY = 0 ; /* number of consecutive timeouts so far */ + < initialize p, RTT for acker to default values > + ACKER = NO_ACKER; /* no acker is known */ + < initialize sequence numbers > + QUEUED = 0; /* packets waiting to be transmitted */ + +on transmission request: + send_packet() ; + +on timeout expiration : + T = 1 ; W = 1 ; /* initialize window and number of tokens */ + if (RETRY < RETRY_MAX) + RETRY = RETRY + 1 + else + ACKER = NO_ACKER ; /* old acker is not valid anymore */ + send_packet() ; + + + + + + + + + + +Rizzo/Iannaccone/Vicisano/Handley Section 3. [Page 17] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +on ACK/NAK reception from receiver I : + < compute p and RTT for source of this ACK, see Sec. 2.5 and 2.6 > + RETRY = 0 ; + if (ACKER == NO_ACKER) { /* select current as acker is no other known */ + ACKER = I ; + T = T + 1 ; + } + if (ACKER != I) + < select acker according to Sec. 2.3 > ; + else { + + if (packet_type == ACK) { + < update_window according to Sec.2.2 > + send_packet ; + if (ack_pending) + update_timeout ; + } +} + +send_packet() { + if (QUEUED > 0 && T >= 1) { + < transmit one packet > + T = T - 1 ; + QUEUED = QUEUED - 1 ; + } + if ( ) + +} + + + +4. Procedures -- Receiver + +The following pseudo-code specifies the complete behavior of the +receiver in PGMCC. + +A receiver only transmits an ACK packet when it receives a data packet +for which the receiver is designated as the ACKER by the data packet +itself. A receiver can transmit a NAK packet after it has detected that +a data packet is missing and a suitable delay has passed, as dictated by +the feedback suppression rules of the protocol in use. + +The data packet contains acknowledgement status about the most recent 32 +sequence numbers known to the receiver. + + + + + + + +Rizzo/Iannaccone/Vicisano/Handley Section 4. [Page 18] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +on initialization/session setup: + < initialize state variables and ACK bitmap > + +on DATA packet reception: + < update p measurement according to Sec.2.6 > + < record timestamp and packet reception time > + if (ACKER == this_node) { + < send an immediate ACK > + } + if ( ) + < schedule a timeout for NAK transmission > + +on NAK reception: + < suppress any pending NAK transmission for the sequence + number indicated in the NAK > + +on timeout: + if ( < there are missing and unacknowledged packets > ) { + < send a NAK for one or more of the missing packets > + < mark such packets as acknowledged > + if ( ) + < schedule a timeout for NAK transmission > + } + + +5. Security Considerations + +PGMCC is not a transport protocol in its own right, but a congestion +control mechanism that is intended to be used in conjunction with a +transport protocol. Therefore security primarily needs to be considered +in the context of a specific transport protocol and its authentication +mechanisms. + +Congestion control mechanisms can potentially be exploited to create +denial of service. This may occur through spoofed feedback. Thus any +transport protocol that uses PGMCC should take care to ensure that +feedback is only accepted from the receiver of the data. The precise +mechanism to achieve this will however depend on the transport protocol +itself. + +In addition, congestion control mechanisms may potentially be +manipulated by a greedy receiver that wishes to receive more than its +fair share of network bandwidth. A receiver might do this by first +reporting inflated loss and RTT samples, in order to get selected as the +ACKER, and then generating ACK at the desired rate (including possibly +claiming to have received packets that in fact were lost due to +congestion). Possible defenses against such a receiver could be based +on the sender verifying the correctness of the loss and RTT samples + + + +Rizzo/Iannaccone/Vicisano/Handley Section 5. [Page 19] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +supplied by the receiver. A PGMCC sender SHOULD compare the receiver +reports on loss rate and RTT with the information derived directly from +the incoming stream of ACKs. In case of discrepancy of the reports, a +PGMCC sender SHOULD mark the current acker as ineligible and initiate a +new acker election. The decision on how large that discrepancy should be +before initiating a new acker election is left to the implementation. + +Also, the sender MAY include some form of nonce that the receiver must +feed back to the sender to prove receipt. However, the details of such a +nonce would depend on the transport protocol, and in particular on +whether the transport protocol is reliable or unreliable. + + +6. Authors' Addresses + + Luigi Rizzo + luigi@iet.unipi.it + Dip. Ing. dell'Informazione, + Univ. di Pisa + via Diotisalvi 2, 56122 Pisa, Italy + + Gianluca Iannaccone + gianluca.iannaccone@intel.com + Intel Research + 15 JJ Thomson Avenue, Cambridge CB3 0FD, UK + + Lorenzo Vicisano + lorenzo@cisco.com + cisco Systems, Inc. + 170 West Tasman Dr., + San Jose, CA, USA, 95134 + + Mark Handley + m.handley@cs.ucl.ac.uk + University College London, + Gower Street, London WC1E 6BT, UK + + +7. Acknowledgments + +We would like to acknowledge feedback and discussions on equation-based +congestion control with a wide range of people, including members of the +Reliable Multicast Research Group, the Reliable Multicast Transport +Working Group, and the End-to-End Research Group. + + + + + + + +Rizzo/Iannaccone/Vicisano/Handley Section 7. [Page 20] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +[1] Bradner, S., Key words for use in RFCs to Indicate Requirement +Levels (IETF RFC 2119) http://www.rfc-editor.org/rfc/rfc2119.txt + +[2] Floyd, S., Handley, M., Padhye, J., Widmer, J., "Equation-Based +Congestion Control for Unicast Applications", ACM SIGCOMM 2000, +Stockholm, Aug. 2000 + +[3] Padhye, J. and Firoiu, V. and Towsley, D. and Kurose, J., "Modeling +TCP Throughput: A Simple Model and its Empirical Validation", Proc ACM +SIGCOMM 1998. + +[4] Mankin, A., Romanow, A., Brander, S., Paxson, V., "IETF Criteria for +Evaluating Reliable Multicast Transport and Application Protocols," +RFC2357, June 1998. + +[5] Rizzo, L., "pgmcc: a TCP-friendly single-rate multicast congestion +control scheme", ACM SIGCOMM 2000, Stockholm, Aug.2000 + +[6] Schulzrinne, H., Casner, S., Frederick, R., Jacobson, V., "RTP: A +Transport Protocol for Real-Time Applications", RFC 1889, Jan 1996. + +[7] Speakman, T., Crowcroft, J., Gemmell, J., Farinacci, D. , Lin, S., +Leshchiner, D., Luby, M., Montgomery, T. , Rizzo, L., Tweedly, A., +Bhaskar, N., Edmonstone, R., Sumanasekera, R., Vicisano, L., PGM +Reliable Transport Protocol Specification, RFC 3208, December 2001. +rfc3208.txt also available at ftp://ftp.rfc-editor.org/in- +notes/rfc3208.txt + + + + +8. Full Copyright Statement + +Copyright (C) The Internet Society (2000). All Rights Reserved. + +This document and translations of it may be copied and furnished to +others, and derivative works that comment on or otherwise explain it or +assist in its implementation may be prepared, copied, published and +distributed, in whole or in part, without restriction of any kind, +provided that the above copyright notice and this paragraph are included +on all such copies and derivative works. However, this document itself +may not be modified in any way, such as by removing the copyright notice +or references to the Internet Society or other Internet organizations, +except as needed for the purpose of developing Internet standards in +which case the procedures for copyrights defined in the Internet +languages other than English. + + + + + +Rizzo/Iannaccone/Vicisano/Handley Section 8. [Page 21] + +INTERNET-DRAFT Expires: January 2005 July 2004 + + +The limited permissions granted above are perpetual and will not be +revoked by the Internet Society or its successors or assigns. + +This document and the information contained herein is provided on an "AS +IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK +FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT +INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR +FITNESS FOR A PARTICULAR PURPOSE." + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Rizzo/Iannaccone/Vicisano/Handley Section 8. [Page 22] diff --git a/3rdparty/openpgm-svn-r1135/doc/rfc3208.txt b/3rdparty/openpgm-svn-r1135/doc/rfc3208.txt new file mode 100644 index 0000000..fb82c26 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/doc/rfc3208.txt @@ -0,0 +1,6219 @@ + + + + + + +Network Working Group T. Speakman +Request for Comments: 3208 Cisco Systems +Category: Experimental J. Crowcroft + UCL + J. Gemmell + Microsoft + D. Farinacci + Procket Networks + S. Lin + Juniper Networks + D. Leshchiner + TIBCO Software + M. Luby + Digital Fountain + T. Montgomery + Talarian Corporation + L. Rizzo + University of Pisa + A. Tweedly + N. Bhaskar + R. Edmonstone + R. Sumanasekera + L. Vicisano + Cisco Systems + December 2001 + + + PGM Reliable Transport Protocol Specification + +Status of this Memo + + This memo defines an Experimental Protocol for the Internet + community. It does not specify an Internet standard of any kind. + Discussion and suggestions for improvement are requested. + Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2001). All Rights Reserved. + +Abstract + + Pragmatic General Multicast (PGM) is a reliable multicast transport + protocol for applications that require ordered or unordered, + duplicate-free, multicast data delivery from multiple sources to + multiple receivers. PGM guarantees that a receiver in the group + either receives all data packets from transmissions and repairs, or + is able to detect unrecoverable data packet loss. PGM is + + + +Speakman, et. al. Experimental [Page 1] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + specifically intended as a workable solution for multicast + applications with basic reliability requirements. Its central design + goal is simplicity of operation with due regard for scalability and + network efficiency. + +Table of Contents + + 1. Introduction and Overview .................................. 3 + 2. Architectural Description .................................. 9 + 3. Terms and Concepts ......................................... 12 + 4. Procedures - General ....................................... 18 + 5. Procedures - Sources ....................................... 19 + 6. Procedures - Receivers ..................................... 22 + 7. Procedures - Network Elements .............................. 27 + 8. Packet Formats ............................................. 31 + 9. Options .................................................... 40 + 10. Security Considerations .................................... 56 + 11. Appendix A - Forward Error Correction ...................... 58 + 12. Appendix B - Support for Congestion Control ................ 72 + 13. Appendix C - SPM Requests .................................. 79 + 14. Appendix D - Poll Mechanism ................................ 82 + 15. Appendix E - Implosion Prevention .......................... 92 + 16. Appendix F - Transmit Window Example ....................... 98 + 17 Appendix G - Applicability Statement ....................... 103 + 18. Abbreviations .............................................. 105 + 19. Acknowledgments ............................................ 106 + 20. References ................................................. 106 + 21. Authors' Addresses.......................................... 108 + 22. Full Copyright Statement ................................... 111 + +Nota Bene: + + The publication of this specification is intended to freeze the + definition of PGM in the interest of fostering both ongoing and + prospective experimentation with the protocol. The intent of that + experimentation is to provide experience with the implementation and + deployment of a reliable multicast protocol of this class so as to be + able to feed that experience back into the longer-term + standardization process underway in the Reliable Multicast Transport + Working Group of the IETF. Appendix G provides more specific detail + on the scope and status of some of this experimentation. Reports of + experiments include [16-23]. Additional results and new + experimentation are encouraged. + + + + + + + + +Speakman, et. al. Experimental [Page 2] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +1. Introduction and Overview + + A variety of reliable protocols have been proposed for multicast data + delivery, each with an emphasis on particular types of applications, + network characteristics, or definitions of reliability ([1], [2], + [3], [4]). In this tradition, Pragmatic General Multicast (PGM) is a + reliable transport protocol for applications that require ordered or + unordered, duplicate-free, multicast data delivery from multiple + sources to multiple receivers. + + PGM is specifically intended as a workable solution for multicast + applications with basic reliability requirements rather than as a + comprehensive solution for multicast applications with sophisticated + ordering, agreement, and robustness requirements. Its central design + goal is simplicity of operation with due regard for scalability and + network efficiency. + + PGM has no notion of group membership. It simply provides reliable + multicast data delivery within a transmit window advanced by a source + according to a purely local strategy. Reliable delivery is provided + within a source's transmit window from the time a receiver joins the + group until it departs. PGM guarantees that a receiver in the group + either receives all data packets from transmissions and repairs, or + is able to detect unrecoverable data packet loss. PGM supports any + number of sources within a multicast group, each fully identified by + a globally unique Transport Session Identifier (TSI), but since these + sources/sessions operate entirely independently of each other, this + specification is phrased in terms of a single source and extends + without modification to multiple sources. + + More specifically, PGM is not intended for use with applications that + depend either upon acknowledged delivery to a known group of + recipients, or upon total ordering amongst multiple sources. + + Rather, PGM is best suited to those applications in which members may + join and leave at any time, and that are either insensitive to + unrecoverable data packet loss or are prepared to resort to + application recovery in the event. Through its optional extensions, + PGM provides specific mechanisms to support applications as disparate + as stock and news updates, data conferencing, low-delay real-time + video transfer, and bulk data transfer. + + In the following text, transport-layer originators of PGM data + packets are referred to as sources, transport-layer consumers of PGM + data packets are referred to as receivers, and network-layer entities + in the intervening network are referred to as network elements. + + + + + +Speakman, et. al. Experimental [Page 3] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Unless otherwise specified, the term "repair" will be used to + indicate both the actual retransmission of a copy of a missing packet + or the transmission of an FEC repair packet. + +Terminology + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in RFC 2119 [14] and + indicate requirement levels for compliant PGM implementations. + +1.1. Summary of Operation + + PGM runs over a datagram multicast protocol such as IP multicast [5]. + In the normal course of data transfer, a source multicasts sequenced + data packets (ODATA), and receivers unicast selective negative + acknowledgments (NAKs) for data packets detected to be missing from + the expected sequence. Network elements forward NAKs PGM-hop-by- + PGM-hop to the source, and confirm each hop by multicasting a NAK + confirmation (NCF) in response on the interface on which the NAK was + received. Repairs (RDATA) may be provided either by the source + itself or by a Designated Local Repairer (DLR) in response to a NAK. + + Since NAKs provide the sole mechanism for reliability, PGM is + particularly sensitive to their loss. To minimize NAK loss, PGM + defines a network-layer hop-by-hop procedure for reliable NAK + forwarding. + + Upon detection of a missing data packet, a receiver repeatedly + unicasts a NAK to the last-hop PGM network element on the + distribution tree from the source. A receiver repeats this NAK until + it receives a NAK confirmation (NCF) multicast to the group from that + PGM network element. That network element responds with an NCF to + the first occurrence of the NAK and any further retransmissions of + that same NAK from any receiver. In turn, the network element + repeatedly forwards the NAK to the upstream PGM network element on + the reverse of the distribution path from the source of the original + data packet until it also receives an NCF from that network element. + Finally, the source itself receives and confirms the NAK by + multicasting an NCF to the group. + + While NCFs are multicast to the group, they are not propagated by PGM + network elements since they act as hop-by-hop confirmations. + + + + + + + + +Speakman, et. al. Experimental [Page 4] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + To avoid NAK implosion, PGM specifies procedures for subnet-based NAK + suppression amongst receivers and NAK elimination within network + elements. The usual result is the propagation of just one copy of a + given NAK along the reverse of the distribution path from any network + with directly connected receivers to a source. + + The net effect is that unicast NAKs return from a receiver to a + source on the reverse of the path on which ODATA was forwarded, that + is, on the reverse of the distribution tree from the source. More + specifically, they return through exactly the same sequence of PGM + network elements through which ODATA was forwarded, but in reverse. + The reasons for handling NAKs this way will become clear in the + discussion of constraining repairs, but first it's necessary to + describe the mechanisms for establishing the requisite source path + state in PGM network elements. + + To establish source path state in PGM network elements, the basic + data transfer operation is augmented by Source Path Messages (SPMs) + from a source, periodically interleaved with ODATA. SPMs function + primarily to establish source path state for a given TSI in all PGM + network elements on the distribution tree from the source. PGM + network elements use this information to address returning unicast + NAKs directly to the upstream PGM network element toward the source, + and thereby insure that NAKs return from a receiver to a source on + the reverse of the distribution path for the TSI. + + SPMs are sent by a source at a rate that serves to maintain up-to- + date PGM neighbor information. In addition, SPMs complement the role + of DATA packets in provoking further NAKs from receivers, and + maintaining receive window state in the receivers. + + As a further efficiency, PGM specifies procedures for the constraint + of repairs by network elements so that they reach only those network + segments containing group members that did not receive the original + transmission. As NAKs traverse the reverse of the ODATA path + (upward), they establish repair state in the network elements which + is used in turn to constrain the (downward) forwarding of the + corresponding RDATA. + + Besides procedures for the source to provide repairs, PGM also + specifies options and procedures that permit designated local + repairers (DLRs) to announce their availability and to redirect + repair requests (NAKs) to themselves rather than to the original + source. In addition to these conventional procedures for loss + recovery through selective ARQ, Appendix A specifies Forward Error + Correction (FEC) procedures for sources to provide and receivers to + request general error correcting parity packets rather than selective + retransmissions. + + + +Speakman, et. al. Experimental [Page 5] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Finally, since PGM operates without regular return traffic from + receivers, conventional feedback mechanisms for transport flow and + congestion control cannot be applied. Appendix B specifies a TCP- + friendly, NE-based solution for PGM congestion control, and cites a + reference to a TCP-friendly, end-to-end solution for PGM congestion + control. + + In its basic operation, PGM relies on a purely rate-limited + transmission strategy in the source to bound the bandwidth consumed + by PGM transport sessions and to define the transmit window + maintained by the source. + + PGM defines four basic packet types: three that flow downstream + (SPMs, DATA, NCFs), and one that flows upstream (NAKs). + +1.2. Design Goals and Constraints + + PGM has been designed to serve that broad range of multicast + applications that have relatively simple reliability requirements, + and to do so in a way that realizes the much advertised but often + unrealized network efficiencies of multicast data transfer. The + usual impediments to realizing these efficiencies are the implosion + of negative and positive acknowledgments from receivers to sources, + repair latency from the source, and the propagation of repairs to + disinterested receivers. + +1.2.1. Reliability. + + Reliable data delivery across an unreliable network is conventionally + achieved through an end-to-end protocol in which a source (implicitly + or explicitly) solicits receipt confirmation from a receiver, and the + receiver responds positively or negatively. While the frequency of + negative acknowledgments is a function of the reliability of the + network and the receiver's resources (and so, potentially quite low), + the frequency of positive acknowledgments is fixed at at least the + rate at which the transmit window is advanced, and usually more + often. + + Negative acknowledgments primarily determine repairs and reliability. + Positive acknowledgments primarily determine transmit buffer + management. + + When these principles are extended without modification to multicast + protocols, the result, at least for positive acknowledgments, is a + burden of positive acknowledgments transmitted to the source that + quickly threatens to overwhelm it as the number of receivers grows. + More succinctly, ACK implosion keeps ACK-based reliable multicast + protocols from scaling well. + + + +Speakman, et. al. Experimental [Page 6] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + One of the goals of PGM is to get as strong a definition of + reliability as possible from as simple a protocol as possible. ACK + implosion can be addressed in a variety of effective but complicated + ways, most of which require re-transmit capability from other than + the original source. + + An alternative is to dispense with positive acknowledgments + altogether, and to resort to other strategies for buffer management + while retaining negative acknowledgments for repairs and reliability. + The approach taken in PGM is to retain negative acknowledgments, but + to dispense with positive acknowledgments and resort instead to + timeouts at the source to manage transmit resources. + + The definition of reliability with PGM is a direct consequence of + this design decision. PGM guarantees that a receiver either receives + all data packets from transmissions and repairs, or is able to detect + unrecoverable data packet loss. + + PGM includes strategies for repeatedly provoking NAKs from receivers, + and for adding reliability to the NAKs themselves. By reinforcing + the NAK mechanism, PGM minimizes the probability that a receiver will + detect a missing data packet so late that the packet is unavailable + for repair either from the source or from a designated local repairer + (DLR). Without ACKs and knowledge of group membership, however, PGM + cannot eliminate this possibility. + +1.2.2. Group Membership + + A second consequence of eliminating ACKs is that knowledge of group + membership is neither required nor provided by the protocol. + Although a source may receive some PGM packets (NAKs for instance) + from some receivers, the identity of the receivers does not figure in + the processing of those packets. Group membership MAY change during + the course of a PGM transport session without the knowledge of or + consequence to the source or the remaining receivers. + +1.2.3. Efficiency + + While PGM avoids the implosion of positive acknowledgments simply by + dispensing with ACKs, the implosion of negative acknowledgments is + addressed directly. + + Receivers observe a random back-off prior to generating a NAK during + which interval the NAK is suppressed (i.e. it is not sent, but the + receiver acts as if it had sent it) by the receiver upon receipt of a + matching NCF. In addition, PGM network elements eliminate duplicate + NAKs received on different interfaces on the same network element. + + + + +Speakman, et. al. Experimental [Page 7] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + The combination of these two strategies usually results in the source + receiving just a single NAK for any given lost data packet. + + Whether a repair is provided from a DLR or the original source, it is + important to constrain that repair to only those network segments + containing members that negatively acknowledged the original + transmission rather than propagating it throughout the group. PGM + specifies procedures for network elements to use the pattern of NAKs + to define a sub-tree within the group upon which to forward the + corresponding repair so that it reaches only those receivers that + missed it in the first place. + +1.2.4. Simplicity + + PGM is designed to achieve the greatest improvement in reliability + (as compared to the usual UDP) with the least complexity. As a + result, PGM does NOT address conference control, global ordering + amongst multiple sources in the group, nor recovery from network + partitions. + +1.2.5. Operability + + PGM is designed to function, albeit with less efficiency, even when + some or all of the network elements in the multicast tree have no + knowledge of PGM. To that end, all PGM data packets can be + conventionally multicast routed by non-PGM network elements with no + loss of functionality, but with some inefficiency in the propagation + of RDATA and NCFs. + + In addition, since NAKs are unicast to the last-hop PGM network + element and NCFs are multicast to the group, NAK/NCF operation is + also consistent across non-PGM network elements. Note that for NAK + suppression to be most effective, receivers should always have a PGM + network element as a first hop network element between themselves and + every path to every PGM source. If receivers are several hops + removed from the first PGM network element, the efficacy of NAK + suppression may degrade. + +1.3. Options + + In addition to the basic data transfer operation described above, PGM + specifies several end-to-end options to address specific application + requirements. PGM specifies options to support fragmentation, late + joining, redirection, Forward Error Correction (FEC), reachability, + and session synchronization/termination/reset. Options MAY be + appended to PGM data packet headers only by their original + transmitters. While they MAY be interpreted by network elements, + options are neither added nor removed by network elements. + + + +Speakman, et. al. Experimental [Page 8] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + All options are receiver-significant (i.e., they must be interpreted + by receivers). Some options are also network-significant (i.e., they + must be interpreted by network elements). + + Fragmentation MAY be used in conjunction with data packets to allow a + transport-layer entity at the source to break up application-layer + data packets into multiple PGM data packets to conform with the + maximum transmission unit (MTU) supported by the network layer. + + Late joining allows a source to indicate whether or not receivers may + request all available repairs when they initially join a particular + transport session. + + Redirection MAY be used in conjunction with Poll Responses to allow a + DLR to respond to normal NCFs or POLLs with a redirecting POLR + advertising its own address as an alternative re-transmitter to the + original source. + + FEC techniques MAY be applied by receivers to use source-provided + parity packets rather than selective retransmissions to effect loss + recovery. + +2. Architectural Description + + As an end-to-end transport protocol, PGM specifies packet formats and + procedures for sources to transmit and for receivers to receive data. + To enhance the efficiency of this data transfer, PGM also specifies + packet formats and procedures for network elements to improve the + reliability of NAKs and to constrain the propagation of repairs. The + division of these functions is described in this section and expanded + in detail in the next section. + +2.1. Source Functions + + Data Transmission + + Sources multicast ODATA packets to the group within the + transmit window at a given transmit rate. + + Source Path State + + Sources multicast SPMs to the group, interleaved with ODATA if + present, to establish source path state in PGM network + elements. + + + + + + + +Speakman, et. al. Experimental [Page 9] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + NAK Reliability + + Sources multicast NCFs to the group in response to any NAKs + they receive. + + Repairs + + Sources multicast RDATA packets to the group in response to + NAKs received for data packets within the transmit window. + + Transmit Window Advance + + Sources MAY advance the trailing edge of the window according + to one of a number of strategies. Implementations MAY support + automatic adjustments such as keeping the window at a fixed + size in bytes, a fixed number of packets or a fixed real time + duration. In addition, they MAY optionally delay window + advancement based on NAK-silence for a certain period. Some + possible strategies are outlined later in this document. + +2.2. Receiver Functions + + Source Path State + + Receivers use SPMs to determine the last-hop PGM network + element for a given TSI to which to direct their NAKs. + + Data Reception + + Receivers receive ODATA within the transmit window and + eliminate any duplicates. + + Repair Requests + + Receivers unicast NAKs to the last-hop PGM network element (and + MAY optionally multicast a NAK with TTL of 1 to the local + group) for data packets within the receive window detected to + be missing from the expected sequence. A receiver MUST + repeatedly transmit a given NAK until it receives a matching + NCF. + + NAK Suppression + + Receivers suppress NAKs for which a matching NCF or NAK is + received during the NAK transmit back-off interval. + + + + + + +Speakman, et. al. Experimental [Page 10] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Receive Window Advance + + Receivers immediately advance their receive windows upon + receipt of any PGM data packet or SPM within the transmit + window that advances the receive window. + +2.3. Network Element Functions + + Network elements forward ODATA without intervention. + + Source Path State + + Network elements intercept SPMs and use them to establish + source path state for the corresponding TSI before multicast + forwarding them in the usual way. + + NAK Reliability + + Network elements multicast NCFs to the group in response to any + NAK they receive. For each NAK received, network elements + create repair state recording the transport session identifier, + the sequence number of the NAK, and the input interface on + which the NAK was received. + + Constrained NAK Forwarding + + Network elements repeatedly unicast forward only the first copy + of any NAK they receive to the upstream PGM network element on + the distribution path for the TSI until they receive an NCF in + response. In addition, they MAY optionally multicast this NAK + upstream with TTL of 1. + + Nota Bene: Once confirmed by an NCF, network elements discard NAK + packets; NAKs are NOT retained in network elements beyond this + forwarding operation, but state about the reception of them is + stored. + + NAK Elimination + + Network elements discard exact duplicates of any NAK for which + they already have repair state (i.e., that has been forwarded + either by themselves or a neighboring PGM network element), and + respond with a matching NCF. + + + + + + + + +Speakman, et. al. Experimental [Page 11] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Constrained RDATA Forwarding + + Network elements use NAKs to maintain repair state consisting + of a list of interfaces upon which a given NAK was received, + and they forward the corresponding RDATA only on these + interfaces. + + NAK Anticipation + + If a network element hears an upstream NCF (i.e., on the + upstream interface for the distribution tree for the TSI), it + establishes repair state without outgoing interfaces in + anticipation of responding to and eliminating duplicates of the + NAK that may arrive from downstream. + +3. Terms and Concepts + + Before proceeding from the preceding overview to the detail in the + subsequent Procedures, this section presents some concepts and + definitions that make that detail more intelligible. + +3.1. Transport Session Identifiers + + Every PGM packet is identified by a: + + TSI transport session identifier + + TSIs MUST be globally unique, and only one source at a time may act + as the source for a transport session. (Note that repairers do not + change the TSI in any RDATA they transmit). TSIs are composed of the + concatenation of a globally unique source identifier (GSI) and a + source-assigned data-source port. + + Since all PGM packets originated by receivers are in response to PGM + packets originated by a source, receivers simply echo the TSI heard + from the source in any corresponding packets they originate. + + Since all PGM packets originated by network elements are in response + to PGM packets originated by a receiver, network elements simply echo + the TSI heard from the receiver in any corresponding packets they + originate. + +3.2. Sequence Numbers + + PGM uses a circular sequence number space from 0 through ((2**32) - + 1) to identify and order ODATA packets. Sources MUST number ODATA + packets in unit increments in the order in which the corresponding + application data is submitted for transmission. Within a transmit or + + + +Speakman, et. al. Experimental [Page 12] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + receive window (defined below), a sequence number x is "less" or + "older" than sequence number y if it numbers an ODATA packet + preceding ODATA packet y, and a sequence number y is "greater" or + "more recent" than sequence number x if it numbers an ODATA packet + subsequent to ODATA packet x. + +3.3. Transmit Window + + The description of the operation of PGM rests fundamentally on the + definition of the source-maintained transmit window. This definition + in turn is derived directly from the amount of transmitted data (in + seconds) a source retains for repair (TXW_SECS), and the maximum + transmit rate (in bytes/second) maintained by a source to regulate + its bandwidth utilization (TXW_MAX_RTE). + + In terms of sequence numbers, the transmit window is the range of + sequence numbers consumed by the source for sequentially numbering + and transmitting the most recent TXW_SECS of ODATA packets. The + trailing (or left) edge of the transmit window (TXW_TRAIL) is defined + as the sequence number of the oldest data packet available for repair + from a source. The leading (or right) edge of the transmit window + (TXW_LEAD) is defined as the sequence number of the most recent data + packet a source has transmitted. + + The size of the transmit window in sequence numbers (TXW_SQNS) (i.e., + the difference between the leading and trailing edges plus one) MUST + be no greater than half the PGM sequence number space less one. + + When TXW_TRAIL is equal to TXW_LEAD, the transmit window size is one. + When TXW_TRAIL is equal to TXW_LEAD plus one, the transmit window + size is empty. + +3.4. Receive Window + + The receive window at the receivers is determined entirely by PGM + packets from the source. That is, a receiver simply obeys what the + source tells it in terms of window state and advancement. + + For a given transport session identified by a TSI, a receiver + maintains: + + RXW_TRAIL the sequence number defining the trailing edge of the + receive window, the sequence number (known from data + packets and SPMs) of the oldest data packet available + for repair from the source + + + + + + +Speakman, et. al. Experimental [Page 13] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + RXW_LEAD the sequence number defining the leading edge of the + receive window, the greatest sequence number of any + received data packet within the transmit window + + The receive window is the range of sequence numbers a receiver is + expected to use to identify receivable ODATA. + + A data packet is described as being "in" the receive window if its + sequence number is in the receive window. + + The receive window is advanced by the receiver when it receives an + SPM or ODATA packet within the transmit window that increments + RXW_TRAIL. Receivers also advance their receive windows upon receipt + of any PGM data packet within the receive window that advances the + receive window. + +3.5. Source Path State + + To establish the repair state required to constrain RDATA, it's + essential that NAKs return from a receiver to a source on the reverse + of the distribution tree from the source. That is, they must return + through the same sequence of PGM network elements through which the + ODATA was forwarded, but in reverse. There are two reasons for this, + the less obvious one being by far the more important. + + The first and obvious reason is that RDATA is forwarded on the same + path as ODATA and so repair state must be established on this path if + it is to constrain the propagation of RDATA. + + The second and less obvious reason is that in the absence of repair + state, PGM network elements do NOT forward RDATA, so the default + behavior is to discard repairs. If repair state is not properly + established for interfaces on which ODATA went missing, then + receivers on those interfaces will continue to NAK for lost data and + ultimately experience unrecoverable data loss. + + The principle function of SPMs is to provide the source path state + required for PGM network elements to forward NAKs from one PGM + network element to the next on the reverse of the distribution tree + for the TSI, establishing repair state each step of the way. This + source path state is simply the address of the upstream PGM network + element on the reverse of the distribution tree for the TSI. That + upstream PGM network element may be more than one subnet hop away. + SPMs establish the identity of the upstream PGM network element on + the distribution tree for each TSI in each group in each PGM network + element, a sort of virtual PGM topology. So although NAKs are + unicast addressed, they are NOT unicast routed by PGM network + elements in the conventional sense. Instead PGM network elements use + + + +Speakman, et. al. Experimental [Page 14] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + the source path state established by SPMs to direct NAKs PGM-hop-by- + PGM-hop toward the source. The idea is to constrain NAKs to the pure + PGM topology spanning the more heterogeneous underlying topology of + both PGM and non-PGM network elements. + + The result is repair state in every PGM network element between the + receiver and the source so that the corresponding RDATA is never + discarded by a PGM network element for lack of repair state. + + SPMs also maintain transmit window state in receivers by advertising + the trailing and leading edges of the transmit window (SPM_TRAIL and + SPM_LEAD). In the absence of data, SPMs MAY be used to close the + transmit window in time by advancing the transmit window until + SPM_TRAIL is equal to SPM_LEAD plus one. + +3.6. Packet Contents + + This section just provides enough short-hand to make the Procedures + intelligible. For the full details of packet contents, please refer + to Packet Formats below. + +3.6.1. Source Path Messages + +3.6.1.1. SPMs + + SPMs are transmitted by sources to establish source-path state in PGM + network elements, and to provide transmit-window state in receivers. + + SPMs are multicast to the group and contain: + + SPM_TSI the source-assigned TSI for the session to which the + SPM corresponds + + SPM_SQN a sequence number assigned sequentially by the source + in unit increments and scoped by SPM_TSI + + Nota Bene: this is an entirely separate sequence than is used to + number ODATA and RDATA. + + SPM_TRAIL the sequence number defining the trailing edge of the + source's transmit window (TXW_TRAIL) + + SPM_LEAD the sequence number defining the leading edge of the + source's transmit window (TXW_LEAD) + + SPM_PATH the network-layer address (NLA) of the interface on + the PGM network element on which the SPM is forwarded + + + + +Speakman, et. al. Experimental [Page 15] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +3.6.2. Data Packets + +3.6.2.1. ODATA - Original Data + + ODATA packets are transmitted by sources to send application data to + receivers. + + ODATA packets are multicast to the group and contain: + + OD_TSI the globally unique source-assigned TSI + + OD_TRAIL the sequence number defining the trailing edge of the + source's transmit window (TXW_TRAIL) + + OD_TRAIL makes the protocol more robust in the face of + lost SPMs. By including the trailing edge of the + transmit window on every data packet, receivers that + have missed any SPMs that advanced the transmit window + can still detect the case, recover the application, + and potentially re-synchronize to the transport + session. + + OD_SQN a sequence number assigned sequentially by the source + in unit increments and scoped by OD_TSI + +3.6.2.2. RDATA - Repair Data + + RDATA packets are repair packets transmitted by sources or DLRs in + response to NAKs. + + RDATA packets are multicast to the group and contain: + + RD_TSI OD_TSI of the ODATA packet for which this is a repair + + RD_TRAIL the sequence number defining the trailing edge of the + source's transmit window (TXW_TRAIL). This is updated + to the most current value when the repair is sent, so + it is not necessarily the same as OD_TRAIL of the + ODATA packet for which this is a repair + + RD_SQN OD_SQN of the ODATA packet for which this is a repair + +3.6.3. Negative Acknowledgments + +3.6.3.1. NAKs - Negative Acknowledgments + + NAKs are transmitted by receivers to request repairs for missing data + packets. + + + +Speakman, et. al. Experimental [Page 16] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + NAKs are unicast (PGM-hop-by-PGM-hop) to the source and contain: + + NAK_TSI OD_TSI of the ODATA packet for which a repair is + requested + + NAK_SQN OD_SQN of the ODATA packet for which a repair is + requested + + NAK_SRC the unicast NLA of the original source of the missing + ODATA. + + NAK_GRP the multicast group NLA + +3.6.3.2. NNAKs - Null Negative Acknowledgments + + NNAKs are transmitted by a DLR that receives NAKs redirected to it by + either receivers or network elements to provide flow-control feed- + back to a source. + + NNAKs are unicast (PGM-hop-by-PGM-hop) to the source and contain: + + NNAK_TSI NAK_TSI of the corresponding re-directed NAK. + + NNAK_SQN NAK_SQN of the corresponding re-directed NAK. + + NNAK_SRC NAK_SRC of the corresponding re-directed NAK. + + NNAK_GRP NAK_GRP of the corresponding re-directed NAK. + +3.6.4. Negative Acknowledgment Confirmations + +3.6.4.1. NCFs - NAK confirmations + + NCFs are transmitted by network elements and sources in response to + NAKs. + + NCFs are multicast to the group and contain: + + NCF_TSI NAK_TSI of the NAK being confirmed + + NCF_SQN NAK_SQN of the NAK being confirmed + + NCF_SRC NAK_SRC of the NAK being confirmed + + NCF_GRP NAK_GRP of the NAK being confirmed + + + + + + +Speakman, et. al. Experimental [Page 17] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +3.6.5. Option Encodings + + OPT_LENGTH 0x00 - Option's Length + + OPT_FRAGMENT 0x01 - Fragmentation + + OPT_NAK_LIST 0x02 - List of NAK entries + + OPT_JOIN 0x03 - Late Joining + + OPT_REDIRECT 0x07 - Redirect + + OPT_SYN 0x0D - Synchronization + + OPT_FIN 0x0E - Session Fin receivers, conventional + feedbackish + + OPT_RST 0x0F - Session Reset + + OPT_PARITY_PRM 0x08 - Forward Error Correction Parameters + + OPT_PARITY_GRP 0x09 - Forward Error Correction Group Number + + OPT_CURR_TGSIZE 0x0A - Forward Error Correction Group Size + + OPT_CR 0x10 - Congestion Report + + OPT_CRQST 0x11 - Congestion Report Request + + OPT_NAK_BO_IVL 0x04 - NAK Back-Off Interval + + OPT_NAK_BO_RNG 0x05 - NAK Back-Off Range + + OPT_NBR_UNREACH 0x0B - Neighbor Unreachable + + OPT_PATH_NLA 0x0C - Path NLA + + OPT_INVALID 0x7F - Option invalidated + +4. Procedures - General + + Since SPMs, NCFs, and RDATA must be treated conditionally by PGM + network elements, they must be distinguished from other packets in + the chosen multicast network protocol if PGM network elements are to + extract them from the usual switching path. + + + + + + +Speakman, et. al. Experimental [Page 18] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + The most obvious way for network elements to achieve this is to + examine every packet in the network for the PGM transport protocol + and packet types. However, the overhead of this approach is costly + for high-performance, multi-protocol network elements. An + alternative, and a requirement for PGM over IP multicast, is that + SPMs, NCFs, and RDATA MUST be transmitted with the IP Router Alert + Option [6]. This option gives network elements a network-layer + indication that a packet should be extracted from IP switching for + more detailed processing. + +5. Procedures - Sources + +5.1. Data Transmission + + Since PGM relies on a purely rate-limited transmission strategy in + the source to bound the bandwidth consumed by PGM transport sessions, + an assortment of techniques is assembled here to make that strategy + as conservative and robust as possible. These techniques are the + minimum REQUIRED of a PGM source. + +5.1.1. Maximum Cumulative Transmit Rate + + A source MUST number ODATA packets in the order in which they are + submitted for transmission by the application. A source MUST + transmit ODATA packets in sequence and only within the transmit + window beginning with TXW_TRAIL at no greater a rate than + TXW_MAX_RTE. + + TXW_MAX_RTE is typically the maximum cumulative transmit rate of SPM, + ODATA, and RDATA. Different transmission strategies MAY define + TXW_MAX_RTE as appropriate for the implementation. + +5.1.2. Transmit Rate Regulation + + To regulate its transmit rate, a source MUST use a token bucket + scheme or any other traffic management scheme that yields equivalent + behavior. A token bucket [7] is characterized by a continually + sustainable data rate (the token rate) and the extent to which the + data rate may exceed the token rate for short periods of time (the + token bucket size). Over any arbitrarily chosen interval, the number + of bytes the source may transmit MUST NOT exceed the token bucket + size plus the product of the token rate and the chosen interval. + + In addition, a source MUST bound the maximum rate at which successive + packets may be transmitted using a leaky bucket scheme drained at a + maximum transmit rate, or equivalent mechanism. + + + + + +Speakman, et. al. Experimental [Page 19] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +5.1.3. Outgoing Packet Ordering + + To preserve the logic of PGM's transmit window, a source MUST + strictly prioritize sending of pending NCFs first, pending SPMs + second, and only send ODATA or RDATA when no NCFs or SPMs are + pending. The priority of RDATA versus ODATA is application + dependent. The sender MAY implement weighted bandwidth sharing + between RDATA and ODATA. Note that strict prioritization of RDATA + over ODATA may stall progress of ODATA if there are receivers that + keep generating NAKs so as to always have RDATA pending (e.g. a + steady stream of late joiners with OPT_JOIN). Strictly prioritizing + ODATA over RDATA may lead to a larger portion of receivers getting + unrecoverable losses. + +5.1.4. Ambient SPMs + + Interleaved with ODATA and RDATA, a source MUST transmit SPMs at a + rate at least sufficient to maintain current source path state in PGM + network elements. Note that source path state in network elements + does not track underlying changes in the distribution tree from a + source until an SPM traverses the altered distribution tree. The + consequence is that NAKs may go unconfirmed both at receivers and + amongst network elements while changes in the underlying distribution + tree take place. + +5.1.5. Heartbeat SPMs + + In the absence of data to transmit, a source SHOULD transmit SPMs at + a decaying rate in order to assist early detection of lost data, to + maintain current source path state in PGM network elements, and to + maintain current receive window state in the receivers. + + In this scheme [8], a source maintains an inter-heartbeat timer + IHB_TMR which times the interval between the most recent packet + (ODATA, RDATA, or SPM) transmission and the next heartbeat + transmission. IHB_TMR is initialized to a minimum interval IHB_MIN + after the transmission of any data packet. If IHB_TMR expires, the + source transmits a heartbeat SPM and initializes IHB_TMR to double + its previous value. The transmission of consecutive heartbeat SPMs + doubles IHB each time up to a maximum interval IHB_MAX. The + transmission of any data packet initializes IHB_TMR to IHB_MIN once + again. The effect is to provoke prompt detection of missing packets + in the absence of data to transmit, and to do so with minimal + bandwidth overhead. + + + + + + + +Speakman, et. al. Experimental [Page 20] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +5.1.6. Ambient and Heartbeat SPMs + + Ambient and heartbeat SPMs are described as driven by separate timers + in this specification to highlight their contrasting functions. + Ambient SPMs are driven by a count-down timer that expires regularly + while heartbeat SPMs are driven by a count-down timer that keeps + being reset by data, and the interval of which changes once it begins + to expire. The ambient SPM timer is just counting down in real-time + while the heartbeat timer is measuring the inter-data-packet + interval. + + In the presence of data, no heartbeat SPMs will be transmitted since + the transmission of data keeps setting the IHB_TMR back to its + initial value. At the same time however, ambient SPMs MUST be + interleaved into the data as a matter of course, not necessarily as a + heartbeat mechanism. This ambient transmission of SPMs is REQUIRED + to keep the distribution tree information in the network current and + to allow new receivers to synchronize with the session. + + An implementation SHOULD de-couple ambient and heartbeat SPM timers + sufficiently to permit them to be configured independently of each + other. + +5.2. Negative Acknowledgment Confirmation + + A source MUST immediately multicast an NCF in response to any NAK it + receives. The NCF is REQUIRED since the alternative of responding + immediately with RDATA would not allow other PGM network elements on + the same subnet to do NAK anticipation, nor would it allow DLRs on + the same subnet to provide repairs. A source SHOULD be able to + detect a NAK storm and adopt countermeasure to protect the network + against a denial of service. A possible countermeasure is to send + the first NCF immediately in response to a NAK and then delay the + generation of further NCFs (for identical NAKs) by a small interval, + so that identical NCFs are rate-limited, without affecting the + ability to suppress NAKs. + +5.3. Repairs + + After multicasting an NCF in response to a NAK, a source MUST then + multicast RDATA (while respecting TXW_MAX_RTE) in response to any NAK + it receives for data packets within the transmit window. + + In the interest of increasing the efficiency of a particular RDATA + packet, a source MAY delay RDATA transmission to accommodate the + arrival of NAKs from the whole loss neighborhood. This delay SHOULD + not exceed twice the greatest propagation delay in the loss + neighborhood. + + + +Speakman, et. al. Experimental [Page 21] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +6. Procedures - Receivers + +6.1. Data Reception + + Initial data reception + + A receiver SHOULD initiate data reception beginning with the first + data packet it receives within the advertised transmit window. This + packet's sequence number (ODATA_SQN) temporarily defines the trailing + edge of the transmit window from the receiver's perspective. That + is, it is assigned to RXW_TRAIL_INIT within the receiver, and until + the trailing edge sequence number advertised in subsequent packets + (SPMs or ODATA or RDATA) increments past RXW_TRAIL_INIT, the receiver + MUST only request repairs for sequence numbers subsequent to + RXW_TRAIL_INIT. Thereafter, it MAY request repairs anywhere in the + transmit window. This temporary restriction on repair requests + prevents receivers from requesting a potentially large amount of + history when they first begin to receive a given PGM transport + session. + + Note that the JOIN option, discussed later, MAY be used to provide a + different value for RXW_TRAIL_INIT. + + Receiving and discarding data packets + + Within a given transport session, a receiver MUST accept any ODATA or + RDATA packets within the receive window. A receiver MUST discard any + data packet that duplicates one already received in the transmit + window. A receiver MUST discard any data packet outside of the + receive window. + + Contiguous data + + Contiguous data is comprised of those data packets within the receive + window that have been received and are in the range from RXW_TRAIL up + to (but not including) the first missing sequence number in the + receive window. The most recently received data packet of contiguous + data defines the leading edge of contiguous data. + + As its default mode of operation, a receiver MUST deliver only + contiguous data packets to the application, and it MUST do so in the + order defined by those data packets' sequence numbers. This provides + applications with a reliable ordered data flow. + + + + + + + + +Speakman, et. al. Experimental [Page 22] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Non contiguous data + + PGM receiver implementations MAY optionally provide a mode of + operation in which data is delivered to an application in the order + received. However, the implementation MUST only deliver complete + application protocol data units (APDUs) to the application. That is, + APDUs that have been fragmented into different TPDUs MUST be + reassembled before delivery to the application. + +6.2. Source Path Messages + + Receivers MUST receive and sequence SPMs for any TSI they are + receiving. An SPM is in sequence if its sequence number is greater + than that of the most recent in-sequence SPM and within half the PGM + number space. Out-of-sequence SPMs MUST be discarded. + + For each TSI, receivers MUST use the most recent SPM to determine the + NLA of the upstream PGM network element for use in NAK addressing. A + receiver MUST NOT initiate repair requests until it has received at + least one SPM for the corresponding TSI. + + Since SPMs require per-hop processing, it is likely that they will be + forwarded at a slower rate than data, and that they will arrive out + of sync with the data stream. In this case, the window information + that the SPMs carry will be out of date. Receivers SHOULD expect + this to be the case and SHOULD detect it by comparing the packet lead + and trail values with the values the receivers have stored for lead + and trail. If the SPM packet values are less, they SHOULD be + ignored, but the rest of the packet SHOULD be processed as normal. + +6.3. Data Recovery by Negative Acknowledgment + + Detecting missing data packets + + Receivers MUST detect gaps in the expected data sequence in the + following manners: + + by comparing the sequence number on the most recently received + ODATA or RDATA packet with the leading edge of contiguous data + + by comparing SPM_LEAD of the most recently received SPM with the + leading edge of contiguous data + + In both cases, if the receiver has not received all intervening data + packets, it MAY initiate selective NAK generation for each missing + sequence number. + + + + + +Speakman, et. al. Experimental [Page 23] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + In addition, a receiver may detect a single missing data packet by + receiving an NCF or multicast NAK for a data packet within the + transmit window which it has not received. In this case it MAY + initiate selective NAK generation for the said sequence number. + + In all cases, receivers SHOULD temper the initiation of NAK + generation to account for simple mis-ordering introduced by the + network. A possible mechanism to achieve this is to assume loss only + after the reception of N packets with sequence numbers higher than + those of the (assumed) lost packets. A possible value for N is 2. + This method SHOULD be complemented with a timeout based mechanism + that handles the loss of the last packet before a pause in the + transmission of the data stream. The leading edge field in SPMs + SHOULD also be taken into account in the loss detection algorithm. + + Generating NAKs + + NAK generation follows the detection of a missing data packet and is + the cycle of: + + waiting for a random period of time (NAK_RB_IVL) while listening + for matching NCFs or NAKs + + transmitting a NAK if a matching NCF or NAK is not heard + + waiting a period (NAK_RPT_IVL) for a matching NCF and recommencing + NAK generation if the matching NCF is not received + + waiting a period (NAK_RDATA_IVL) for data and recommencing NAK + generation if the matching data is not received + + The entire generation process can be summarized by the following + state machine: + + + + + + + + + + + + + + + + + + +Speakman, et. al. Experimental [Page 24] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + | + | detect missing tpdu + | - clear data retry count + | - clear NCF retry count + V + matching NCF |--------------------------| + <---------------| BACK-OFF_STATE | <---------------------- + | | start timer(NAK_RB_IVL) | ^ ^ + | | | | | + | |--------------------------| | | + | matching | | timer expires | | + | NAK | | - send NAK | | + | | | | | + | V V | | + | |--------------------------| | | + | | WAIT_NCF_STATE | | | + | matching NCF | start timer(NAK_RPT_IVL) | | | + |<--------------| |------------> | + | |--------------------------| timer expires | + | | | ^ - increment NCF | + | NAK_NCF_RETRIES | | | retry count | + | exceeded | | | | + | V ----------- | + | Cancelation matching NAK | + | - restart timer(NAK_RPT_IVL) | + | | + | | + V |--------------------------| | + --------------->| WAIT_DATA_STATE |-----------------------> + |start timer(NAK_RDATA_IVL)| timer expires + | | - increment data + |--------------------------| retry count + | | ^ + NAK_DATA_RETRIES | | | + exceeded | | | + | ----------- + | matching NCF or NAK + V - restart timer(NAK_RDATA_IVL) + Cancellation + + In any state, receipt of matching RDATA or ODATA completes data + recovery and successful exit from the state machine. State + transition stops any running timers. + + In any state, if the trailing edge of the window moves beyond the + sequence number, data recovery for that sequence number terminates. + + + + + +Speakman, et. al. Experimental [Page 25] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + During NAK_RB_IVL a NAK is said to be pending. When awaiting data or + an NCF, a NAK is said to be outstanding. + + Backing off NAK transmission + + Before transmitting a NAK, a receiver MUST wait some interval + NAK_RB_IVL chosen randomly over some time period NAK_BO_IVL. During + this period, receipt of a matching NAK or a matching NCF will suspend + NAK generation. NAK_RB_IVL is counted down from the time a missing + data packet is detected. + + A value for NAK_BO_IVL learned from OPT_NAK_BO_IVL (see 16.4.1 below) + MUST NOT be used by a receiver (i.e., the receiver MUST NOT NAK) + unless either NAK_BO_IVL_SQN is zero, or the receiver has seen + POLL_RND == 0 for POLL_SQN =< NAK_BO_IVL_SQN within half the sequence + number space. + + When a parity NAK (Appendix A, FEC) is being generated, the back-off + interval SHOULD be inversely biased with respect to the number of + parity packets requested. This way NAKs requesting larger numbers of + parity packets are likely to be sent first and thus suppress other + NAKs. A NAK for a given transmission group suppresses another NAK + for the same transmission group only if it is requesting an equal or + larger number of parity packets. + + When a receiver has to transmit a sequence of NAKs, it SHOULD + transmit the NAKs in order from oldest to most recent. + + Suspending NAK generation + + Suspending NAK generation just means waiting for either NAK_RB_IVL, + NAK_RPT_IVL or NAK_RDATA_IVL to pass. A receiver MUST suspend NAK + generation if a duplicate of the NAK is already pending from this + receiver or the NAK is already outstanding from this or another + receiver. + + NAK suppression + + A receiver MUST suppress NAK generation and wait at least + NAK_RDATA_IVL before recommencing NAK generation if it hears a + matching NCF or NAK during NAK_RB_IVL. A matching NCF must match + NCF_TSI with NAK_TSI, and NCF_SQN with NAK_SQN. + + Transmitting a NAK + + Upon expiry of NAK_RB_IVL, a receiver MUST unicast a NAK to the + upstream PGM network element for the TSI specifying the transport + session identifier and missing sequence number. In addition, it MAY + + + +Speakman, et. al. Experimental [Page 26] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + multicast a NAK with TTL of 1 to the group, if the PGM parent is not + directly connected. It also records both the address of the source + of the corresponding ODATA and the address of the group in the NAK + header. + + It MUST repeat the NAK at a rate governed by NAK_RPT_IVL up to + NAK_NCF_RETRIES times while waiting for a matching NCF. It MUST then + wait NAK_RDATA_IVL before recommencing NAK generation. If it hears a + matching NCF or NAK during NAK_RDATA_IVL, it MUST wait anew for + NAK_RDATA_IVL before recommencing NAK generation (i.e. matching NCFs + and NAKs restart NAK_RDATA_IVL). + + Completion of NAK generation + + NAK generation is complete only upon the receipt of the matching + RDATA (or even ODATA) packet at any time during NAK generation. + + Cancellation of NAK generation + + NAK generation is cancelled upon the advancing of the receive window + so as to exclude the matching sequence number of a pending or + outstanding NAK, or NAK_DATA_RETRIES / NAK_NCF_RETRIES being + exceeded. Cancellation of NAK generation indicates unrecoverable + data loss. + + Receiving NCFs and multicast NAKs + + A receiver MUST discard any NCFs or NAKs it hears for data packets + outside the transmit window or for data packets it has received. + Otherwise they are treated as appropriate for the current repair + state. + +7. Procedures - Network Elements + +7.1. Source Path State + + Upon receipt of an in-sequence SPM, a network element records the + Source Path Address SPM_PATH with the multicast routing information + for the TSI. If the receiving network element is on the same subnet + as the forwarding network element, this address will be the same as + the address of the immediately upstream network element on the + distribution tree for the TSI. If, however, non-PGM network elements + intervene between the forwarding and the receiving network elements, + this address will be the address of the first PGM network element + across the intervening network elements. + + + + + + +Speakman, et. al. Experimental [Page 27] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + The network element then forwards the SPM on each outgoing interface + for that TSI. As it does so, it encodes the network address of the + outgoing interface in SPM_PATH in each copy of the SPM it forwards. + +7.2. NAK Confirmation + + Network elements MUST immediately transmit an NCF in response to any + unicast NAK they receive. The NCF MUST be multicast to the group on + the interface on which the NAK was received. + + Nota Bene: In order to avoid creating multicast routing state for + PGM network elements across non-PGM-capable clouds, the network- + header source address of NCFs transmitted by network elements MUST + be set to the ODATA source's NLA, not the network element's NLA as + might be expected. + + Network elements should be able to detect a NAK storm and adopt + counter-measure to protect the network against a denial of service. + A possible countermeasure is to send the first NCF immediately in + response to a NAK and then delay the generation of further NCFs (for + identical NAKs) by a small interval, so that identical NCFs are + rate-limited, without affecting the ability to suppress NAKs. + + Simultaneously, network elements MUST establish repair state for the + NAK if such state does not already exist, and add the interface on + which the NAK was received to the corresponding repair interface list + if the interface is not already listed. + +7.3. Constrained NAK Forwarding + + The NAK forwarding procedures for network elements are quite similar + to those for receivers, but three important differences should be + noted. + + First, network elements do NOT back off before forwarding a NAK + (i.e., there is no NAK_BO_IVL) since the resulting delay of the NAK + would compound with each hop. Note that NAK arrivals will be + randomized by the receivers from which they originate, and this + factor in conjunction with NAK anticipation and elimination will + combine to forestall NAK storms on subnets with a dense network + element population. + + Second, network elements do NOT retry confirmed NAKs if RDATA is not + seen; they simply discard the repair state and rely on receivers to + re-request the repair. This approach keeps the repair state in the + network elements relatively ephemeral and responsive to underlying + routing changes. + + + + +Speakman, et. al. Experimental [Page 28] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Third, note that ODATA does NOT cancel NAK forwarding in network + elements since it is switched by network elements without transport- + layer intervention. + + Nota Bene: Once confirmed by an NCF, network elements discard NAK + packets; they are NOT retained in network elements beyond this + forwarding operation. + + NAK forwarding requires that a network element listen to NCFs for the + same transport session. NAK forwarding also requires that a network + element observe two time out intervals for any given NAK (i.e., per + NAK_TSI and NAK_SQN): NAK_RPT_IVL and NAK_RDATA_IVL. + + The NAK repeat interval NAK_RPT_IVL, limits the length of time for + which a network element will repeat a NAK while waiting for a + corresponding NCF. NAK_RPT_IVL is counted down from the transmission + of a NAK. Expiry of NAK_RPT_IVL cancels NAK forwarding (due to + missing NCF). + + The NAK RDATA interval NAK_RDATA_IVL, limits the length of time for + which a network element will wait for the corresponding RDATA. + NAK_RDATA_IVL is counted down from the time a matching NCF is + received. Expiry of NAK_RDATA_IVL causes the network element to + discard the corresponding repair state (due to missing RDATA). + + During NAK_RPT_IVL, a NAK is said to be pending. During + NAK_RDATA_IVL, a NAK is said to be outstanding. + + A Network element MUST forward NAKs only to the upstream PGM network + element for the TSI. + + A network element MUST repeat a NAK at a rate of NAK_RPT_RTE for an + interval of NAK_RPT_IVL until it receives a matching NCF. A matching + NCF must match NCF_TSI with NAK_TSI, and NCF_SQN with NAK_SQN. + + Upon reception of the corresponding NCF, network elements MUST wait + at least NAK_RDATA_IVL for the corresponding RDATA. Receipt of the + corresponding RDATA at any time during NAK forwarding cancels NAK + forwarding and tears down the corresponding repair state in the + network element. + +7.4. NAK elimination + + Two NAKs duplicate each other if they bear the same NAK_TSI and + NAK_SQN. Network elements MUST discard all duplicates of a NAK that + is pending. + + + + + +Speakman, et. al. Experimental [Page 29] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Once a NAK is outstanding, network elements MUST discard all + duplicates of that NAK for NAK_ELIM_IVL. Upon expiry of + NAK_ELIM_IVL, network elements MUST suspend NAK elimination for that + TSI/SQN until the first duplicate of that NAK is seen after the + expiry of NAK_ELIM_IVL. This duplicate MUST be forwarded in the + usual manner. Once this duplicate NAK is outstanding, network + elements MUST once again discard all duplicates of that NAK for + NAK_ELIM_IVL, and so on. NAK_RDATA_IVL MUST be reset each time a NAK + for the corresponding TSI/SQN is confirmed (i.e., each time + NAK_ELIM_IVL is reset). NAK_ELIM_IVL MUST be some small fraction of + NAK_RDATA_IVL. + + NAK_ELIM_IVL acts to balance implosion prevention against repair + state liveness. That is, it results in the elimination of all but at + most one NAK per NAK_ELIM_IVL thereby allowing repeated NAKs to keep + the repair state alive in the PGM network elements. + +7.5. NAK Anticipation + + An unsolicited NCF is one that is received by a network element when + the network element has no corresponding pending or outstanding NAK. + Network elements MUST process unsolicited NCFs differently depending + on the interface on which they are received. + + If the interface on which an NCF is received is the same interface + the network element would use to reach the upstream PGM network + element, the network element simply establishes repair state for + NCF_TSI and NCF_SQN without adding the interface to the repair + interface list, and discards the NCF. If the repair state already + exists, the network element restarts the NAK_RDATA_IVL and + NAK_ELIM_IVL timers and discards the NCF. + + If the interface on which an NCF is received is not the same + interface the network element would use to reach the upstream PGM + network element, the network element does not establish repair state + and just discards the NCF. + + Anticipated NAKs permit the elimination of any subsequent matching + NAKs from downstream. Upon establishing anticipated repair state, + network elements MUST eliminate subsequent NAKs only for a period of + NAK_ELIM_IVL. Upon expiry of NAK_ELIM_IVL, network elements MUST + suspend NAK elimination for that TSI/SQN until the first duplicate of + that NAK is seen after the expiry of NAK_ELIM_IVL. This duplicate + MUST be forwarded in the usual manner. Once this duplicate NAK is + outstanding, network elements MUST once again discard all duplicates + of that NAK for NAK_ELIM_IVL, and so on. NAK_RDATA_IVL MUST be reset + + + + + +Speakman, et. al. Experimental [Page 30] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + each time a NAK for the corresponding TSI/SQN is confirmed (i.e., + each time NAK_ELIM_IVL is reset). NAK_ELIM_IVL must be some small + fraction of NAK_RDATA_IVL. + +7.6. NAK Shedding + + Network elements MAY implement local procedures for withholding NAK + confirmations for receivers detected to be reporting excessive loss. + The result of these procedures would ultimately be unrecoverable data + loss in the receiver. + +7.7. Addressing NAKs + + A PGM network element uses the source and group addresses (NLAs) + contained in the transport header to find the state for the + corresponding TSI, looks up the corresponding upstream PGM network + element's address, uses it to re-address the (unicast) NAK, and + unicasts it on the upstream interface for the distribution tree for + the TSI. + +7.8. Constrained RDATA Forwarding + + Network elements MUST maintain repair state for each interface on + which a given NAK is received at least once. Network elements MUST + then use this list of interfaces to constrain the forwarding of the + corresponding RDATA packet only to those interfaces in the list. An + RDATA packet corresponds to a NAK if it matches NAK_TSI and NAK_SQN. + + Network elements MUST maintain this repair state only until either + the corresponding RDATA is received and forwarded, or NAK_RDATA_IVL + passes after forwarding the most recent instance of a given NAK. + Thereafter, the corresponding repair state MUST be discarded. + + Network elements SHOULD discard and not forward RDATA packets for + which they have no repair state. Note that the consequence of this + procedure is that, while it constrains repairs to the interested + subset of the network, loss of repair state precipitates further NAKs + from neglected receivers. + +8. Packet Formats + + All of the packet formats described in this section are transport- + layer headers that MUST immediately follow the network-layer header + in the packet. Only data packet headers (ODATA and RDATA) may be + followed in the packet by application data. For each packet type, + the network-header source and destination addresses are specified in + + + + + +Speakman, et. al. Experimental [Page 31] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + addition to the format and contents of the transport layer header. + Recall from General Procedures that, for PGM over IP multicast, SPMs, + NCFs, and RDATA MUST also bear the IP Router Alert Option. + + For PGM over IP, the IP protocol number is 113. + + In all packets the descriptions of Data-Source Port, Data-Destination + Port, Type, Options, Checksum, Global Source ID (GSI), and Transport + Service Data Unit (TSDU) Length are: + + Data-Source Port: + + A random port number generated by the source. This port number + MUST be unique within the source. Source Port together with + Global Source ID forms the TSI. + + Data-Destination Port: + + A globally well-known port number assigned to the given PGM + application. + + Type: + + The high-order two bits of the Type field encode a version + number, 0x0 in this instance. The low-order nibble of the type + field encodes the specific packet type. The intervening two + bits (the low-order two bits of the high-order nibble) are + reserved and MUST be zero. + + Within the low-order nibble of the Type field: + + values in the range 0x0 through 0x3 represent SPM-like + packets (i.e., session-specific, sourced by a source, + periodic), + + values in the range 0x4 through 0x7 represent DATA-like + packets (i.e., data and repairs), + + values in the range 0x8 through 0xB represent NAK-like + packets (i.e., hop-by-hop reliable NAK forwarding + procedures), + + and values in the range 0xC through 0xF represent SPMR-like + packets (i.e., session-specific, sourced by a receiver, + asynchronous). + + + + + + +Speakman, et. al. Experimental [Page 32] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Options: + + This field encodes binary indications of the presence and + significance of any options. It also directly encodes some + options. + + bit 0 set => One or more Option Extensions are present + + bit 1 set => One or more Options are network-significant + + Note that this bit is clear when OPT_FRAGMENT and/or + OPT_JOIN are the only options present. + + bit 6 set => Packet is a parity packet for a transmission group + of variable sized packets (OPT_VAR_PKTLEN). Only present when + OPT_PARITY is also present. + + bit 7 set => Packet is a parity packet (OPT_PARITY) + + Bits are numbered here from left (0 = MSB) to right (7 = LSB). + + All the other options (option extensions) are encoded in + extensions to the PGM header. + + Checksum: + + This field is the usual 1's complement of the 1's complement + sum of the entire PGM packet including header. + + The checksum does not include a network-layer pseudo header for + compatibility with network address translation. If the + computed checksum is zero, it is transmitted as all ones. A + value of zero in this field means the transmitter generated no + checksum. + + Note that if any entity between a source and a receiver + modifies the PGM header for any reason, it MUST either + recompute the checksum or clear it. The checksum is mandatory + on data packets (ODATA and RDATA). + + Global Source ID: + + A globally unique source identifier. This ID MUST NOT change + throughout the duration of the transport session. A + RECOMMENDED identifier is the low-order 48 bits of the MD5 [9] + signature of the DNS name of the source. Global Source ID + together with Data-Source Port forms the TSI. + + + + +Speakman, et. al. Experimental [Page 33] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + TSDU Length: + + The length in octets of the transport data unit exclusive of + the transport header. + + Note that those who require the TPDU length must obtain it from + sum of the transport header length (TH) and the TSDU length. + TH length is the sum of the size of the particular PGM packet + header (type_specific_size) plus the length of any options that + might be present. + + Address Family Indicators (AFIs) are as specified in [10]. + +8.1. Source Path Messages + + SPMs are sent by a source to establish source path state in network + elements and to provide transmit window state to receivers. + + The network-header source address of an SPM is the unicast NLA of the + entity that originates the SPM. + + The network-header destination address of an SPM is a multicast group + NLA. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Source Port | Destination Port | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Options | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Global Source ID ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ... Global Source ID | TSDU Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SPM's Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Trailing Edge Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Leading Edge Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NLA AFI | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Path NLA ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + | Option Extensions when present ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + + +Speakman, et. al. Experimental [Page 34] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Source Port: + + SPM_SPORT + + Data-Source Port, together with SPM_GSI forms SPM_TSI + + Destination Port: + + SPM_DPORT + + Data-Destination Port + + Type: + + SPM_TYPE = 0x00 + + Global Source ID: + + SPM_GSI + + Together with SPM_SPORT forms SPM_TSI + + SPM's Sequence Number + + SPM_SQN + + The sequence number assigned to the SPM by the source. + + Trailing Edge Sequence Number: + + SPM_TRAIL + + The sequence number defining the current trailing edge of the + source's transmit window (TXW_TRAIL). + + Leading Edge Sequence Number: + + SPM_LEAD + + The sequence number defining the current leading edge of the + source's transmit window (TXW_LEAD). + + If SPM_TRAIL == 0 and SPM_LEAD == 0x80000000, this indicates that + no window information is present in the packet. + + + + + + + +Speakman, et. al. Experimental [Page 35] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Path NLA: + + SPM_PATH + + The NLA of the interface on the network element on which this SPM + was forwarded. Initialized by a source to the source's NLA, + rewritten by each PGM network element upon forwarding. + +8.2. Data Packets + + Data packets carry application data from a source or a repairer to + receivers. + + ODATA: + + Original data packets transmitted by a source. + + RDATA: + + Repairs transmitted by a source or by a designated local + repairer (DLR) in response to a NAK. + + The network-header source address of a data packet is the unicast NLA + of the entity that originates the data packet. + + The network-header destination address of a data packet is a + multicast group NLA. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Source Port | Destination Port | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Options | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Global Source ID ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ... Global Source ID | TSDU Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Data Packet Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Trailing Edge Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Option Extensions when present ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Data ... + +-+-+- ... + + + + +Speakman, et. al. Experimental [Page 36] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Source Port: + + OD_SPORT, RD_SPORT + + Data-Source Port, together with Global Source ID forms: + + OD_TSI, RD_TSI + + Destination Port: + + OD_DPORT, RD_DPORT + + Data-Destination Port + + Type: + + OD_TYPE = 0x04 RD_TYPE = 0x05 + + Global Source ID: + + OD_GSI, RD_GSI + + Together with Source Port forms: + + OD_TSI, RD_TSI + + Data Packet Sequence Number: + + OD_SQN, RD_SQN + + The sequence number originally assigned to the ODATA packet by the + source. + + Trailing Edge Sequence Number: + + OD_TRAIL, RD_TRAIL + + The sequence number defining the current trailing edge of the + source's transmit window (TXW_TRAIL). In RDATA, this MAY not be + the same as OD_TRAIL of the ODATA packet for which it is a repair. + + Data: + + Application data. + + + + + + + +Speakman, et. al. Experimental [Page 37] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +8.3. Negative Acknowledgments and Confirmations + + NAK: + + Negative Acknowledgments are sent by receivers to request the + repair of an ODATA packet detected to be missing from the + expected sequence. + + N-NAK: + + Null Negative Acknowledgments are sent by DLRs to provide flow + control feedback to the source of ODATA for which the DLR has + provided the corresponding RDATA. + + The network-header source address of a NAK is the unicast NLA of the + entity that originates the NAK. The network-header source address of + NAK is rewritten by each PGM network element with its own. + + The network-header destination address of a NAK is initialized by the + originator of the NAK (a receiver) to the unicast NLA of the upstream + PGM network element known from SPMs. The network-header destination + address of a NAK is rewritten by each PGM network element with the + unicast NLA of the upstream PGM network element to which this NAK is + forwarded. On the final hop, the network-header destination address + of a NAK is rewritten by the PGM network element with the unicast NLA + of the original source or the unicast NLA of a DLR. + + NCF: + + NAK Confirmations are sent by network elements and sources to + confirm the receipt of a NAK. + + The network-header source address of an NCF is the ODATA source's + NLA, not the network element's NLA as might be expected. + + The network-header destination address of an NCF is a multicast group + NLA. + + Note that in NAKs and N-NAKs, unlike the other packets, the field + SPORT contains the Data-Destination port and the field DPORT contains + the Data-Source port. As a general rule, the content of SPORT/DPORT + is determined by the direction of the flow: in packets which travel + down-stream SPORT is the port number chosen in the data source + (Data-Source Port) and DPORT is the data destination port number + (Data-Destination Port). The opposite holds for packets which travel + upstream. This makes DPORT the protocol endpoint in the recipient + host, regardless of the direction of the packet. + + + + +Speakman, et. al. Experimental [Page 38] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Source Port | Destination Port | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Options | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Global Source ID ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ... Global Source ID | TSDU Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Requested Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NLA AFI | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Source NLA ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + | NLA AFI | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Multicast Group NLA ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + | Option Extensions when present ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... + + Source Port: + + NAK_SPORT, NNAK_SPORT + + Data-Destination Port + + NCF_SPORT + + Data-Source Port, together with Global Source ID forms NCF_TSI + + Destination Port: + + NAK_DPORT, NNAK_DPORT + + Data-Source Port, together with Global Source ID forms: + + NAK_TSI, NNAK_TSI + + NCF_DPORT + + Data-Destination Port + + + + + + +Speakman, et. al. Experimental [Page 39] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Type: + + NAK_TYPE = 0x08 NNAK_TYPE = 0x09 + + NCF_TYPE = 0x0A + + Global Source ID: + + NAK_GSI, NNAK_GSI, NCF_GSI + + Together with Data-Source Port forms + + NAK_TSI, NNAK_TSI, NCF_TSI + + Requested Sequence Number: + + NAK_SQN, NNAK_SQN + + NAK_SQN is the sequence number of the ODATA packet for which a + repair is requested. + + NNAK_SQN is the sequence number of the RDATA packet for which a + repair has been provided by a DLR. + + NCF_SQN + + NCF_SQN is NAK_SQN from the NAK being confirmed. + + Source NLA: + + NAK_SRC, NNAK_SRC, NCF_SRC + + The unicast NLA of the original source of the missing ODATA. + + Multicast Group NLA: + + NAK_GRP, NNAK_GRP, NCF_GRP + + The multicast group NLA. NCFs MAY bear OPT_REDIRECT and/or + OPT_NAK_LIST + +9. Options + + PGM specifies several end-to-end options to address specific + application requirements. PGM specifies options to support + fragmentation, late joining, and redirection. + + + + + +Speakman, et. al. Experimental [Page 40] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Options MAY be appended to PGM data packet headers only by their + original transmitters. While they MAY be interpreted by network + elements, options are neither added nor removed by network elements. + + Options are all in the TLV style, or Type, Length, Value. The Type + field is contained in the first byte, where bit 0 is the OPT_END bit, + followed by 7 bits of type. The OPT_END bit MUST be set in the last + option in the option list, whichever that might be. The Length field + is the total length of the option in bytes, and directly follows the + Type field. Following the Length field are 5 reserved bits, the + OP_ENCODED flag, the 2 Option Extensibility bits OPX and the + OP_ENCODED_NULL flag. Last are 7 bits designated for option specific + information which may be defined on a per-option basis. If not + defined for a particular option, they MUST be set to 0. + + The Option Extensibility bits dictate the desired treatment of an + option if it is unknown to the network element processing it. + + Nota Bene: Only network elements pay any attention to these bits. + + The OPX bits are defined as follows: + + 00 - Ignore the option + + 01 - Invalidate the option by changing the type to OPT_INVALID + = 0x7F + + 10 - Discard the packet + + 11 - Unsupported, and reserved for future use + + Some options present in data packet (ODATA and RDATA) are strictly + associated with the packet content (PGM payload), OPT_FRAGMENT being + an example. These options must be preserved even when the data + packet that would normally contain them is not received, but its the + payload is recovered though the use of FEC. PGM specifies a + mechanism to accomplish this that uses the F (OP_ENCODED) and U + (OP_ENCODED_NULL) bits in the option common header. OP_ENCODED and + OP_ENCODED_NULL MUST be normally set to zero except when the option + is used in FEC packets to preserve original options. See Appendix A + for details. + + There is a limit of 16 options per packet. + + + + + + + + +Speakman, et. al. Experimental [Page 41] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + General Option Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U|Opt. Specific| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Option Value ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+...+-+-+ + +9.1. Option extension length - OPT_LENGTH + + When option extensions are appended to the standard PGM header, the + extensions MUST be preceded by an option extension length field + specifying the total length of all option extensions. + + In addition, the presence of the options MUST be encoded in the + Options field of the standard PGM header before the Checksum is + computed. + + All network-significant options MUST be appended before any + exclusively receiver-significant options. + + To provide an indication of the end of option extensions, OPT_END + (0x80) MUST be set in the Option Type field of the trailing option + extension. + +9.1.1. OPT_LENGTH - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Option Type | Option Length | Total length of all options | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x00 + + Option Length = 4 octets + + Total length of all options + + The total length in octets of all option extensions including + OPT_LENGTH. + + OPT_LENGTH is NOT network-significant. + + + + + + +Speakman, et. al. Experimental [Page 42] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.2. Fragmentation Option - OPT_FRAGMENT + + Fragmentation allows transport-layer entities at a source to break up + application protocol data units (APDUs) into multiple PGM data + packets (TPDUs) to conform with the MTU supported by the network + layer. The fragmentation option MAY be applied to ODATA and RDATA + packets only. + + Architecturally, the accumulation of TSDUs into APDUs is applied to + TPDUs that have already been received, duplicate eliminated, and + contiguously sequenced by the receiver. Thus APDUs MAY be + reassembled across increments of the transmit window. + +9.2.1. OPT_FRAGMENT - Packet Extension Contents + + OPT_FRAG_OFF the offset of the fragment from the beginning of the + APDU + + OPT_FRAG_LEN the total length of the original APDU + +9.2.2. OPT_FRAGMENT - Procedures - Sources + + A source fragments APDUs into a contiguous series of fragments no + larger than the MTU supported by the network layer. A source + sequentially and uniquely assigns OD_SQNs to these fragments in the + order in which they occur in the APDU. A source then sets + OPT_FRAG_OFF to the value of the offset of the fragment in the + original APDU (where the first byte of the APDU is at offset 0, and + OPT_FRAG_OFF numbers the first byte in the fragment), and set + OPT_FRAG_LEN to the value of the total length of the original APDU. + +9.2.3. OPT_FRAGMENT - Procedures - Receivers + + Receivers detect and accumulate fragmented packets until they have + received an entire contiguous sequence of packets comprising an APDU. + This sequence begins with the fragment bearing OPT_FRAG_OFF of 0, and + terminates with the fragment whose length added to its OPT_FRAG_OFF + is OPT_FRAG_LEN. + + + + + + + + + + + + + +Speakman, et. al. Experimental [Page 43] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.2.4. OPT_FRAGMENT - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | First Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Offset | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x01 + + Option Length = 12 octets + + First Sequence Number + + Sequence Number of the PGM DATA/RDATA packet containing the first + fragment of the APDU. + + Offset + + The byte offset of the fragment from the beginning of the APDU + (OPT_FRAG_OFF). + + Length + + The total length of the original APDU (OPT_FRAG_LEN). + + OPT_FRAGMENT is NOT network-significant. + +9.3. NAK List Option - OPT_NAK_LIST + + The NAK List option MAY be used in conjunction with NAKs to allow + receivers to request transmission for more than one sequence number + with a single NAK packet. The option is limited to 62 listed NAK + entries. The NAK list MUST be unique and duplicate free. It MUST be + ordered, and MUST consist of either a list of selective or a list of + parity NAKs. In general, network elements, sources and receivers + must process a NAK list as if they had received individual NAKs for + each sequence number in the list. The procedures for each are + outlined in detail earlier in this document. Clarifications and + differences are detailed here. + + + + + +Speakman, et. al. Experimental [Page 44] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.3.1. OPT_NAK_LIST - Packet Extensions Contents + + A list of sequence numbers for which retransmission is requested. + +9.3.2. OPT_NAK_LIST - Procedures - Receivers + + Receivers MAY append the NAK List option to a NAK to indicate that + they wish retransmission of a number of RDATA. + + Receivers SHOULD proceed to back off NAK transmission in a manner + consistent with the procedures outlined for single sequence number + NAKs. Note that the repair of each separate sequence number will be + completed upon receipt of a separate RDATA packet. + + Reception of an NCF or multicast NAK containing the NAK List option + suspends generation of NAKs for all sequence numbers within the NAK + list, as well as the sequence number within the NAK header. + +9.3.3. OPT_NAK_LIST - Procedures - Network Elements + + Network elements MUST immediately respond to a NAK with an identical + NCF containing the same NAK list as the NAK itself. + + Network elements MUST forward a NAK containing a NAK List option if + any one sequence number specified by the NAK (including that in the + main NAK header) is not currently outstanding. That is, it MUST + forward the NAK, if any one sequence number does not have an + elimination timer running for it. The NAK must be forwarded intact. + + Network elements MUST eliminate a NAK containing the NAK list option + only if all sequence numbers specified by the NAK (including that in + the main NAK header) are outstanding. That is, they are all running + an elimination timer. + + Upon receipt of an unsolicited NCF containing the NAK list option, a + network element MUST anticipate data for every sequence number + specified by the NAK as if it had received an NCF for every sequence + number specified by the NAK. + +9.3.4. OPT_NAK_LIST - Procedures - Sources + + A source MUST immediately respond to a NAK with an identical NCF + containing the same NAK list as the NAK itself. + + It MUST then multicast RDATA (while respecting TXW_MAX_RTE) for every + requested sequence number. + + + + + +Speakman, et. al. Experimental [Page 45] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.3.5. OPT_NAK_LIST - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Requested Sequence Number 1 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ..... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Requested Sequence Number N | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x02 + + Option Length = 4 + (4 * number of SQNs) octets + + Requested Sequence Number + + A list of up to 62 additional sequence numbers to which the NAK + applies. + + OPT_NAK_LIST is network-significant. + +9.4. Late Joining Option - OPT_JOIN + + Late joining allows a source to bound the amount of repair history + receivers may request when they initially join a particular transport + session. + + This option indicates that receivers that join a transport session in + progress MAY request repair of all data as far back as the given + minimum sequence number from the time they join the transport + session. The default is for receivers to receive data only from the + first packet they receive and onward. + +9.4.1. OPT_JOIN - Packet Extensions Contents + + OPT_JOIN_MIN the minimum sequence number for repair + +9.4.2. OPT_JOIN - Procedures - Receivers + + If a PGM packet (ODATA, RDATA, or SPM) bears OPT_JOIN, a receiver MAY + initialize the trailing edge of the receive window (RXW_TRAIL_INIT) + to the given Minimum Sequence Number and proceeds with normal data + reception. + + + + +Speakman, et. al. Experimental [Page 46] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.4.3. OPT_JOIN - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Minimum Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + Option Type = 0x03 + + Option Length = 8 octets + + Minimum Sequence Number + + The minimum sequence number defining the initial trailing edge of + the receive window for a late joining receiver. + + OPT_JOIN is NOT network-significant. + +9.5. Redirect Option - OPT_REDIRECT + + Redirection MAY be used by a designated local repairer (DLR) to + advertise its own address as an alternative to the original source, + for requesting repairs. + + These procedures allow a PGM Network Element to use a DLR that is one + PGM hop from it either upstream or downstream in the multicast + distribution tree. The former are referred to as upstream DLRs. The + latter are referred to as off-tree DLRs. Off-Tree because even + though they are downstream of the point of loss, they might not lie + on the subtree affected by the loss. + + A DLR MUST receive any PGM sessions for which it wishes to provide + retransmissions. A DLR SHOULD respond to NCFs or POLLs sourced by + its PGM parent with a redirecting POLR response packet containing an + OPT_REDIRECT which provides its own network layer address. + Recipients of redirecting POLRs MAY then direct NAKs for subsequent + ODATA sequence numbers to the DLR rather than to the original source. + In addition, DLRs that receive redirected NAKs for which they have + RDATA MUST send a NULL NAK to provide flow control to the original + source without also provoking a repair from that source. + + + + + + + +Speakman, et. al. Experimental [Page 47] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.5.1. OPT_REDIRECT - Packet Extensions Contents + + OPT_REDIR_NLA the DLR's own unicast network-layer address to which + recipients of the redirecting POLR MAY direct + subsequent NAKs for the corresponding TSI. + +9.5.2. OPT_REDIRECT - Procedures - DLRs + + A DLR MUST receive any PGM sessions for which it wishes to provide a + source of repairs. In addition to acting as an ordinary PGM + receiver, a DLR MAY then respond to NCFs or relevant POLLs sourced by + parent network elements (or even by the source itself) by sending a + POLR containing an OPT_REDIRECT providing its own network-layer + address. + + If a DLR can provide FEC repairs it MUST denote this by setting + OPT_PARITY in the PGM header of its POLR response. + +9.5.2.1. Upstream DLRs + + If the NCF completes NAK transmission initiated by the DLR itself, + the DLR MUST NOT send a redirecting POLR. + + When a DLR receives an NCF from its upstream PGM parent, it SHOULD + send a redirecting POLR, multicast to the group. The DLR SHOULD + record that it is acting as an upstream DLR for the said session. + Note that this POLR MUST have both the data source's source address + and the router alert option in its network header. + + An upstream DLR MUST act as an ordinary PGM source in responding to + any NAK it receives (i.e., directed to it). That is, it SHOULD + respond first with a normal NCF and then RDATA as usual. In + addition, an upstream DLR that receives redirected NAKs for which it + has RDATA MUST send a NULL NAK to provide flow control to the + original source. If it cannot provide the RDATA it forwards the NAK + to the upstream PGM neighbor as usual. + + Nota Bene: In order to propagate on exactly the same distribution + tree as ODATA, RDATA and POLR packets transmitted by DLRs MUST + bear the ODATA source's NLA as the network-header source address, + not the DLR's NLA as might be expected. + + + + + + + + + + +Speakman, et. al. Experimental [Page 48] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.5.2.2. Off-Tree DLRs + + A DLR that receives a POLL with sub-type PGM_POLL_DLR MUST respond + with a unicast redirecting POLR if it provides the appropriate + service. The DLR SHOULD respond using the rules outlined for polling + in Appendix D of this text. If the DLR responds, it SHOULD record + that it is acting as an off-tree DLR for the said session. + + An off-tree DLR acts in a special way in responding to any NAK it + receives (i.e., directed to it). It MUST respond to a NAK directed + to it from its parent by unicasting an NCF and RDATA to its parent. + The parent will then forward the RDATA down the distribution tree. + The DLR uses its own and the parent's NLA addresses in the network + header for the source and destination respectively. The unicast NCF + and RDATA packets SHOULD not have the router alert option. In all + other ways the RDATA header should be "as if" the packet had come + from the source. + + Again, an off-tree DLR that receives redirected NAKs for which it has + RDATA MUST originate a NULL NAK to provide flow control to the + original source. It MUST originate the NULL NAK before originating + the RDATA. This must be done to reduce the state held in the network + element. + + If it cannot provide the RDATA for a given NAK, an off-tree DLR + SHOULD confirm the NAK with a unicast NCF as normal, then immediately + send a NAK for the said data packet back to its parent. + +9.5.2.3. Simultaneous Upstream and Off-Tree DLR operation + + Note that it is possible for a DLR to provide service to its parent + and to downstream network elements simultaneously. A downstream loss + coupled with a loss for the same data on some other part of the + distribution tree served by its parent could cause this. In this + case it may provide both upstream and off-tree functionality + simultaneously. + + Note that a DLR differentiates between NAKs from an NE downstream or + from its parent by comparing the network-header source address of the + NAK with it's upstream PGM parent's NLA. The DLR knows the parent's + NLA from the session's SPM messages. + + + + + + + + + + +Speakman, et. al. Experimental [Page 49] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.5.3. OPT_REDIRECT - Procedures - Network Elements + +9.5.3.1. Discovering DLRs + + When a PGM router receives notification of a loss via a NAK, it + SHOULD first try to use a known DLR to recover the loss. If such a + DLR is not known it SHOULD initiate DLR discovery. DLR discovery may + occur in two ways. If there are upstream DLRs, the NAK transmitted + by this router to its PGM parent will trigger their discovery, via a + redirecting POLR. Also, a network element SHOULD initiate a search + for off-tree DLRs using the PGM polling mechanism, and the sub-type + PGM_POLL_DLR. + + If a DLR can provide FEC repairs it will denote this by setting + OPT_PARITY in the PGM header of its POLR response. A network element + SHOULD only direct parity NAKs to a DLR that can provide FEC repairs. + +9.5.3.2. Redirected Repair + + When it can, a network element SHOULD use upstream DLRs. + + Upon receiving a redirecting POLR, network elements SHOULD record the + redirecting information for the TSI, and SHOULD redirect subsequent + NAKs for the same TSI to the network address provided in the + redirecting POLR rather than to the PGM neighbor known via the SPMs. + Note, however, that a redirecting POLR is NOT regarded as matching + the NAK that provoked it, so it does not complete the transmission of + that NAK. Only a normal matching NCF can complete the transmission + of a NAK. + + For subsequent NAKs, if the network element has recorded redirection + information for the corresponding TSI, it MAY change the destination + network address of those NAKs and attempt to transmit them to the + DLR. No NAK for a specific SQN SHOULD be sent to an off-tree DLR if + a NAK for the SQN has been seen on the interface associated with the + DLR. Instead the NAK SHOULD be forwarded upstream. Subsequent NAKs + for different SQNs MAY be forwarded to the said DLR (again assuming + no NAK for them has been seen on the interface to the DLR). + + If a corresponding NCF is not received from the DLR within + NAK_RPT_IVL, the network element MUST discard the redirecting + information for the TSI and re-attempt to forward the NAK towards the + PGM upstream neighbor. + + + + + + + + +Speakman, et. al. Experimental [Page 50] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + If a NAK is received from the DLR for a requested SQN, the network + element MUST discard the redirecting information for the SQN and re- + attempt to forward the NAK towards the PGM upstream neighbor. The + network element MAY still direct NAKs for different SQNs to the DLR. + + RDATA and NCFs from upstream DLRs will flow down the distribution + tree. However, RDATA and NCFs from off-tree DLRs will be unicast to + the network element. The network element will terminate the NCF, but + MUST put the source's NLA and the group address into the network + header and MUST add router alert before forwarding the RDATA packet + to the distribution subtree. + + NULL NAKs from an off-tree DLR for an RDATA packet requested from + that off-tree DLR MUST always be forwarded upstream. The network + element can assume that these will arrive before the matching RDATA. + Other NULL NAKs are forwarded only if matching repair state has not + already been created. Network elements MUST NOT confirm or retry + NULL NAKs and they MUST NOT add the receiving interface to the repair + state. If a NULL NAK is used to initially create repair state, this + fact must be recorded so that any subsequent non-NULL NAK will not be + eliminated, but rather will be forwarded to provoke an actual repair. + State created by a NULL NAK exists only for NAK_ELIM_IVL. + +9.5.4. OPT_REDIRECT - Procedures - Receivers + + These procedures are intended to be applied in instances where a + receiver's first hop router on the reverse path to the source is not + a PGM Network Element. So, receivers MUST ignore a redirecting POLR + from a DLR on the same IP subnet that the receiver resides on, since + this is likely to suffer identical loss to the receiver and so be + useless. Therefore, these procedures are entirely OPTIONAL. A + receiver MAY choose to ignore all redirecting POLRs since in cases + where its first hop router on the reverse path is PGM capable, it + would ignore them anyway. Also, note that receivers will never learn + of off-tree DLRs. + + Upon receiving a redirecting POLR, receivers SHOULD record the + redirecting information for the TSI, and MAY redirect subsequent NAKs + for the same TSI to the network address provided in the redirecting + POLR rather than to the PGM neighbor for the corresponding ODATA for + which the receiver is requesting repair. Note, however, that a + redirecting POLR is NOT regarded as matching the NAK that provoked + it, so it does not complete the transmission of that NAK. Only a + normal matching NCF can complete the transmission of a NAK. + + For subsequent NAKs, if the receiver has recorded redirection + information for the corresponding TSI, it MAY change the destination + network address of those NAKs and attempt to transmit them to the + + + +Speakman, et. al. Experimental [Page 51] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + DLR. If a corresponding NCF is not received within NAK_RPT_IVL, the + receiver MUST discard the redirecting information for the TSI and + re-attempt to forward the NAK to the PGM neighbor for the original + source of the missing ODATA. + +9.5.5. OPT_REDIRECT - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NLA AFI | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | DLR's NLA ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + + Option Type = 0x07 + + Option Length = 4 + NLA length + + DLR's NLA + + The DLR's own unicast network address to which recipients of the + redirecting POLR may direct subsequent NAKs. + + OPT_REDIRECT is network-significant. + +9.6. OPT_SYN - Synchronization Option + + The SYN option indicates the starting data packet for a session. It + must only appear in ODATA or RDATA packets. + + The SYN option MAY be used to provide a useful abstraction to + applications that can simplify application design by providing stream + start notification. It MAY also be used to let a late joiner to a + session know that it is indeed late (i.e. it would not see the SYN + option). + +9.6.1. OPT_SYN - Procedures - Receivers + + Procedures for receivers are implementation dependent. A receiver + MAY use the SYN to provide its applications with abstractions of the + data stream. + + + + + + + +Speakman, et. al. Experimental [Page 52] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.6.2. OPT_SYN - Procedures - Sources + + Sources MAY include OPT_SYN in the first data for a session. That + is, they MAY include the option in: + + the first ODATA sent on a session by a PGM source + + any RDATA sent as a result of loss of this ODATA packet + + all FEC packets for the first transmission group; in this case it + is interpreted as the first packet having the SYN + +9.6.3. OPT_SYN - Procedures - DLRs + + In an identical manner to sources, DLRs MUST provide OPT_SYN in + any retransmitted data that is at the start of a session. + +9.6.4. OPT_SYN - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x0D + + Option Length = 4 + + OPT_SYN is NOT network-significant. + +9.7. OPT_FIN - Session Finish Option + + This FIN option indicates the last data packet for a session and + an orderly close down. + + The FIN option MAY be used to provide an abstraction to + applications that can simplify application design by providing + stream end notification. + + This option MAY be present in the last data packet or transmission + group for a session. The FIN PGM option MUST appear in every SPM + sent after the last ODATA for a session. The SPM_LEAD sequence + number in an SPM with the FIN option indicates the last known data + successfully transmitted for the session. + + + + + + +Speakman, et. al. Experimental [Page 53] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.7.1. OPT_FIN - Procedures - Receivers + + A receiver SHOULD use receipt of a FIN to let it know that it can + tear down its data structures for the said session once a suitable + time period has expired (TXW_SECS). It MAY still try to solicit + retransmissions within the existing transmit window. + + Other than this, procedures for receivers are implementation + dependent. A receiver MAY use the FIN to provide its applications + with abstractions of the data stream and to inform its + applications that the session is ending. + + 9.7.2. OPT_FIN - Procedures - Sources + + Sources MUST include OPT_FIN in every SPM sent after it has been + determined that the application has closed gracefully. If a + source is aware at the time of transmission that it is ending a + session the source MAY include OPT_FIN in, + + the last ODATA + + any associated RDATAs for the last data + + FEC packets for the last transmission group; in this case it is + interpreted as the last packet having the FIN + + When a source detects that it needs to send an OPT_FIN it SHOULD + immediately send it. This is done either by appending it to the last + data packet or transmission group or by immediately sending an SPM + and resetting the SPM heartbeat timer (i.e. it does not wait for a + timer to expire before sending the SPM). After sending an OPT_FIN, + the session SHOULD not close and stop sending SPMs until after a time + period equal to TXW_SECS. + +9.7.3. OPT_FIN - Procedures - DLRs + + In an identical manner to sources, DLRs MUST provide OPT_FIN in any + retransmitted data that is at the end of a session. + + + + + + + + + + + + + +Speakman, et. al. Experimental [Page 54] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.7.4. OPT_FIN - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x0E + + Option Length = 4 + + OPT_FIN is NOT network-significant. + +9.8. OPT_RST - Session Reset Option + + The RST option MAY appear in every SPM sent after an unrecoverable + error is identified by the source. This acts to notify the receivers + that the session is being aborted. This option MAY appear only in + SPMs. The SPM_LEAD sequence number in an SPM with the RST option + indicates the last known data successfully transmitted for the + session. + +9.8.1. OPT_RST - Procedures - Receivers + + Receivers SHOULD treat the reception of OPT_RST in an SPM as an abort + of the session. + + A receiver that receives an SPM with an OPT_RST with the N bit set + SHOULD not send any more NAKs for the said session towards the + source. If the N bit (see 9.8.5) is not set, the receiver MAY + continue to try to solicit retransmit data within the current + transmit window. + +9.8.2. OPT_RST - Procedures - Sources + + Sources SHOULD include OPT_RST in every SPM sent after it has been + determined that an unrecoverable error condition has occurred. The N + bit of the OPT_RST SHOULD only be sent if the source has determined + that it cannot process NAKs for the session. The cause of the + OPT_RST is set to an implementation specific value. If the error + code is unknown, then the value of 0x00 is used. When a source + detects that it needs to send an OPT_RST it SHOULD immediately send + it. This is done by immediately sending an SPM and resetting the SPM + heartbeat timer (i.e. it does not wait for a timer to expire before + sending the SPM). After sending an OPT_RST, the session SHOULD not + close and stop sending SPMs until after a time period equal to + TXW_SECS. + + + +Speakman, et. al. Experimental [Page 55] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +9.8.3. OPT_RST - Procedures - DLRs + + None. + +9.8.4. OPT_RST - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U|N|Error Code | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x0F + + Option Length = 4 + + N bit + + The N bit is set to 1 to indicate that NAKs for previous ODATA + will go unanswered from the source. The application will tell the + source to turn this bit on or off. + + Error Code + + The 6 bit error code field is used to forward an error code down + to the receivers from the source. + + The value of 0x00 indicates an unknown reset reason. Any other + value indicates the application purposely aborted and gave a + reason (the error code value) that may have meaning to the end + receiver application. These values are entirely application + dependent. + + OPT_RST is NOT network-significant. + +10. Security Considerations + + In addition to the usual problems of end-to-end authentication, PGM + is vulnerable to a number of security risks that are specific to the + mechanisms it uses to establish source path state, to establish + repair state, to forward NAKs, to identify DLRs, and to distribute + repairs. These mechanisms expose PGM network elements themselves to + security risks since network elements not only switch but also + interpret SPMs, NAKs, NCFs, and RDATA, all of which may legitimately + be transmitted by PGM sources, receivers, and DLRs. Short of full + authentication of all neighboring sources, receivers, DLRs, and + network elements, the protocol is not impervious to abuse. + + + + +Speakman, et. al. Experimental [Page 56] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + So putting aside the problems of rogue PGM network elements for the + moment, there are enough potential security risks to network elements + associated with sources, receivers, and DLRs alone. These risks + include denial of service through the exhausting of both CPU + bandwidth and memory, as well as loss of (repair) data connectivity + through the muddling of repair state. + + False SPMs may cause PGM network elements to mis-direct NAKs intended + for the legitimate source with the result that the requested RDATA + would not be forthcoming. + + False NAKs may cause PGM network elements to establish spurious + repair state that will expire only upon time-out and could lead to + memory exhaustion in the meantime. + + False NCFs may cause PGM network elements to suspend NAK forwarding + prematurely (or to mis-direct NAKs in the case of redirecting POLRs) + resulting eventually in loss of RDATA. + + False RDATA may cause PGM network elements to tear down legitimate + repair state resulting eventually in loss of legitimate RDATA. + + The development of precautions for network elements to protect + themselves against incidental or unsophisticated versions of these + attacks is work outside of this spec and includes: + + Damping of jitter in the value of either the network-header source + address of SPMs or the path NLA in SPMs. While the network-header + source address is expected to change seldom, the path NLA is + expected to change occasionally as a consequence of changes in + underlying multicast routing information. + + The extension of NAK shedding procedures to control the volume, not + just the rate, of confirmed NAKs. In either case, these procedures + assist network elements in surviving NAK attacks at the expense of + maintaining service. More efficiently, network elements may use the + knowledge of TSIs and their associated transmit windows gleaned from + SPMs to control the proliferation of repair state. + + A three-way handshake between network elements and DLRs that would + permit a network element to ascertain with greater confidence that an + alleged DLR is identified by the alleged network-header source + address, and is PGM conversant. + + + + + + + + +Speakman, et. al. Experimental [Page 57] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +11. Appendix A - Forward Error Correction + +11.1. Introduction + + The following procedures incorporate packet-level Reed Solomon + Erasure correcting techniques as described in [11] and [12] into PGM. + This approach to Forward Error Correction (FEC) is based upon the + computation of h parity packets from k data packets for a total of n + packets such that a receiver can reconstruct the k data packets out + of any k of the n packets. The original k data packets are referred + to as the Transmission Group, and the total n packets as the FEC + Block. + + These procedures permit any combination of pro-active FEC or on- + demand FEC with conventional ARQ (selective retransmission) within a + given TSI to provide any flavor of layered or integrated FEC. The + two approaches can be used by the same or different receivers in a + single transport session without conflict. Once provided by a + source, the actual use of FEC or selective retransmission for loss + recovery in the session is entirely at the discretion of the + receivers. Note however that receivers SHOULD NOT ask for selective + retransmissions when FEC is available, nevertheless sources MUST + provide selective retransmissions in response to selective NAKs from + the leading partial transmission group (i.e. the most recent + transmission group, which is not yet full). For any group that is + full, the source SHOULD provide FEC on demand in response to a + selective NAK. + + Pro-active FEC refers to the technique of computing parity packets at + transmission time and transmitting them as a matter of course + following the data packets. Pro-active FEC is RECOMMENDED for + providing loss recovery over simplex or asymmetric multicast channels + over which returning repair requests is either impossible or costly. + It provides increased reliability at the expense of bandwidth. + + On-demand FEC refers to the technique of computing parity packets at + repair time and transmitting them only upon demand (i.e., receiver- + based loss detection and repair request). On-demand FEC is + RECOMMENDED for providing loss recovery of uncorrelated loss in very + large receiver populations in which the probability of any single + packet being lost is substantial. It provides equivalent reliability + to selective NAKs (ARQ) at no more and typically less expense of + bandwidth. + + Selective NAKs are NAKs that request the retransmission of specific + packets by sequence number corresponding to the sequence number of + any data packets detected to be missing from the expected sequence + (conventional ARQ). Selective NAKs can be used for recovering losses + + + +Speakman, et. al. Experimental [Page 58] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + occurring in leading partial transmission groups, i.e. in the most + recent transmission group, which is not yet full. The RECOMMENDED + way of handling partial transmission groups, however, is for the data + source to use variable-size transmission groups (see below). + + Parity NAKs are NAKs that request the transmission of a specific + number of parity packets by count corresponding to the count of the + number of data packets detected to be missing from a group of k data + packets (on-demand FEC). + + The objective of these procedures is to incorporate these FEC + techniques into PGM so that: + + sources MAY provide parity packets either pro-actively or on- + demand, interchangeably within the same TSI, + + receivers MAY use either selective or parity NAKs interchangeably + within the same TSI (however, in a session where on-demand parity + is available receivers SHOULD only use parity NAKs). + + network elements maintain repair state based on either selective + or parity NAKs in the same data structure, altering only search, + RDATA constraint, and deletion algorithms in either case, + + and only OPTION additions to the basic packet formats are + REQUIRED. + +11.2. Overview + + Advertising FEC parameters in the transport session + + Sources add OPT_PARITY_PRM to SPMs to provide session-specific + parameters such as the number of packets (TGSIZE == k) in a + transmission group. This option lets receivers know how many packets + there are in a transmission group, and it lets network elements sort + repair state by transmission group number. This option includes an + indication of whether pro-active and/or on-demand parity is available + from the source. + + Distinguishing parity packets from data packets + + Sources send pro-active parity packets as ODATA (NEs do not forward + RDATA unless a repair state is present) and on-demand parity packets + as RDATA. A source MUST add OPT_PARITY to the ODATA/RDATA packet + header of parity packets to permit network elements and receivers to + distinguish them from data packets. + + + + + +Speakman, et. al. Experimental [Page 59] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Data and parity packet numbering + + Parity packets MUST be calculated over a fixed number k of data + packets known as the Transmission Group. Grouping of packets into + transmission groups effectively partitions a packet sequence number + into a high-order portion (TG_SQN) specifying the transmission group + (TG), and a low-order portion (PKT_SQN) specifying the packet number + (PKT-NUM in the range 0 through k-1) within that group. From an + implementation point of view, it's handy if k, the TG size, is a + power of 2. If so, then TG_SQN and PKT_SQN can be mapped side-by- + side into the 32 bit SQN. log2(TGSIZE) is then the size in bits of + PKT_SQN. + + This mapping does not reduce the effective sequence number space + since parity packets marked with OPT_PARITY allow the sequence space + (PKT_SQN) to be completely reused in order to number the h parity + packets, as long as h is not greater than k. + + In the case where h is greater than k, a source MUST add + OPT_PARITY_GRP to any parity packet numbered j greater than k-1, + specifying the number m of the group of k parity packets to which the + packet belongs, where m is just the quotient from the integer + division of j by k. Correspondingly, PKT-NUM for such parity packets + is just j modulo k. In other words, when a source needs to generate + more parity packets than there were original data packets (perhaps + because of a particularly lossy line such that a receiver lost not + only the original data but some of the parity RDATA as well), use the + OPT_PARITY_GRP option in order to number and identify the + transmission group of the extra packets that would exceed the normal + sequential number space. + + Note that parity NAKs (and consequently their corresponding parity + NCFs) MUST also contain the OPT_PARITY flag in the options field of + the fixed header, and that in these packets, PKT_SQN MUST contain + PKT_CNT, the number of missing packets, rather than PKT_NUM, the SQN + of a specific missing packet. More on all this later. + + Variable Transmission Group Size + + The transmission group size advertised in the OPT_PARITY_PRM option + on SPMs MUST be a power of 2 and constant for the duration of the + session. However, the actual transmission group size used MAY not be + constant for the duration of the session, and MAY not be a power of + 2. When a TG size different from the one advertised in + OPT_PARITY_PRM is used, the TG size advertised in OPT_PARITY_PRM MUST + be interpreted as specifying the maximum effective size of the TG. + + + + + +Speakman, et. al. Experimental [Page 60] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + When the actual TG size is not a power of 2 or is smaller than the + max TG size, there will be sparse utilization of the sequence number + space since some of the sequence numbers that would have been + consumed in numbering a maximum sized TG will not be assigned to + packets in the smaller TG. The start of the next transmission group + will always begin on the boundary of the maximum TG size as though + each of the sequence numbers had been utilized. + + When the source decides to use a smaller group size than that + advertised in OPT_PARITY_PRM, it appends OPT_CURR_TGSIZE to the last + data packet (ODATA) in the truncated transmission group. This lets + the receiver know that it should not expect any more packets in this + transmission group, and that it may start requesting repairs for any + missing packets. If the last data packet itself went missing, the + receiver will detect the end of the group when it receives a parity + packet for the group, an SPM with SPM_LEAD equal to OD_SQN of the + last data packet, or the first packet of the next group, whichever + comes first. In addition, any parity packet from this TG will also + carry the OPT_CURR_TGSIZE option as will any SPM sent with SPM_LEAD + equal to OD_SQN of the last data packet. + + Variable TSDU length + + If a non constant TSDU length is used within a given transmission + group, the size of parity packets in the corresponding FEC block MUST + be equal to the size of the largest original data packet in the + block. Parity packets MUST be computed by padding the original + packets with zeros up to the size of the largest data packet. Note + that original data packets are transmitted without padding. + + Receivers using a combination of original packets and FEC packets to + rebuild missing packets MUST pad the original packets in the same way + as the source does. The receiver MUST then feed the padded original + packets plus the parity packets to the FEC decoder. The decoder + produces the original packets padded with zeros up to the size of the + largest original packet in the group. In order for the receiver to + eliminate the padding on the reconstructed data packets, the original + size of the packet MUST be known, and this is accomplished as + follows: + + The source, along with the packet payloads, encodes the TSDU + length and appends the 2-byte encoded length to the padded FEC + packets. + + Receivers pad the original packets that they received to the + largest original packet size and then append the TSDU length to + the padded packets. They then pass them and the FEC packets to + the FEC decoder. + + + +Speakman, et. al. Experimental [Page 61] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + The decoder produces padded original packets with their original + TSDU length appended. Receivers MUST now use this length to get + rid of the padding. + + A source that transmits variable size packets MUST take into account + the fact that FEC packets will have a size equal to the maximum size + of the original packets plus the size of the length field (2 bytes). + + If a fixed packet size is used within a transmission group, the + encoded length is not appended to the parity packets. The presence + of the fixed header option flag OPT_VAR_PKTLEN in parity packets + allows receivers to distinguish between transmission groups with + variable sized packets and fixed-size ones, and behave accordingly. + + Payload-specific options + + Some options present in data packet (ODATA and RDATA) are strictly + associated with the packet content (PGM payload), OPT_FRAGMENT being + an example. These options must be preserved even when the data + packet that would normally contain them is not received, but its the + payload is recovered though the use of FEC. + + To achieve this, PGM encodes the content of these options in special + options that are inserted in parity packets. Two flags present in + the the option common-header are used for this process: bit F + (OP_ENCODED) and bit U (OP_ENCODED_NULL). + + Whenever at least one of the original packets of a TG contains a + payload-specific option of a given type, the source MUST include an + encoded version of that option type in all the parity packets it + transmits. The encoded option is computed by applying FEC encoding + to the whole option with the exception of the first three bytes of + the option common-header (E, Option Type, Option Length, OP_ENCODED + and OPX fields). The type, length and OPX of the encoded option are + the same as the type, length and OPX in the original options. + OP_ENCODED is set to 1 (all original option have OP_ENCODED = 0). + + The encoding is performed using the same process that is used to + compute the payload of the parity packet. i.e. the FEC encoder is fed + with one copy of that option type for each original packet in the TG. + If one (or more) original packet of the TG does not contain that + option type, an all zeroes option is used for the encoding process. + To be able to distinguish this "dummy" option from valid options with + all-zeroes payload, OP_ENCODED_NULL is used. OP_ENCODED_NULL is set + to 0 in all the original options, but the value of 1 is used in the + encoding process if the option did not exist in the original packet. + On the receiver side, all option with OP_ENCODED_NULL equal to 1 are + discarded after decoding. + + + +Speakman, et. al. Experimental [Page 62] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + When a receiver recovers a missing packet using FEC repair packets, + it MUST also recover payload-specific options, if any. The presence + of these can be unequivocally detected through the presence of + encoded options in parity packets (encoded options have OP_ENCODED + set to 1). Receivers apply FEC-recovery to encoded options and + possibly original options, as they do to recover packet payloads. + The FEC decoding is applied to the whole option with the exception of + the first three bytes of the option common-header (E, Option Type, + Option Length, OP_ENCODED and OPX fields). Each decoded option is + associated with the relative payload, unless OP_ENCODED_NULL turns + out to be 1, in which case the decoded option is discarded. + + The decoding MUST be performed using the 1st occurrence of a given + option type in original/parity packets. If one or more original + packets do not contain that option type, an option of the same type + with zero value must be used. This option MUST have OP_ENCODED_NULL + equal to 1. + +11.3. Packet Contents + + This section just provides enough short-hand to make the Procedures + intelligible. For the full details of packet contents, please refer + to Packet Formats below. + + OPT_PARITY indicated in pro-active (ODATA) and on-demand + (RDATA) parity packets to distinguish them from + data packets. This option is directly encoded in + the "Option" field of the fixed PGM header + + OPT_VAR_PKTLEN MAY be present in pro-active (ODATA) and on-demand + (RDATA) parity packets to indicate that the + corresponding transmission group is composed of + variable size data packets. This option is + directly encoded in the "Option" field of the fixed + PGM header + + OPT_PARITY_PRM appended by sources to SPMs to specify session- + specific parameters such as the transmission group + size and the availability of pro-active and/or on- + demand parity from the source + + OPT_PARITY_GRP the number of the group (greater than 0) of h + parity packets to which the parity packet belongs + when more than k parity packets are provided by the + source + + + + + + +Speakman, et. al. Experimental [Page 63] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + OPT_CURR_TGSIZE appended by sources to the last data packet and any + parity packets in a variable sized transmission + group to indicate to the receiver the actual size + of a transmission group. May also be appended to + certain SPMs + +11.3.1. Parity NAKs + + NAK_TG_SQN the high-order portion of NAK_SQN specifying the + transmission group for which parity packets are + requested + + NAK_PKT_CNT the low-order portion of NAK_SQN specifying the + number of missing data packets for which parity + packets are requested + + Nota Bene: NAK_PKT_CNT (and NCF_PKT_CNT) are 0-based counters, + meaning that NAK_PKT_CNT = 0 means that 1 FEC RDATA is being + requested, and in general NAK_PKT_CNT = k - 1 means that k FEC + RDATA are being requested. + +11.3.2. Parity NCFs + + NCF_TG_SQN the high-order portion of NCF_SQN specifying the + transmission group for which parity packets were + requested + + NCF_PKT_CNT the low-order portion of NCF_SQN specifying the + number of missing data packets for which parity + packets were requested + + Nota Bene: NCF_PKT_CNT (and NAK_PKT_CNT) are 0-based counters, + meaning that NAK_PKT_CNT = 0 means that 1 FEC RDATA is being + requested, and in general NAK_PKT_CNT = k - 1 means that k FEC + RDATA are being requested. + +11.3.3. On-demand Parity + + RDATA_TG_SQN the high-order portion of RDATA_SQN specifying the + transmission group to which the parity packet + belongs + + RDATA_PKT_SQN the low-order portion of RDATA_SQN specifying the + parity packet sequence number within the + transmission group + + + + + + +Speakman, et. al. Experimental [Page 64] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +11.3.4. Pro-active Parity + + ODATA_TG_SQN the high-order portion of ODATA_SQN specifying the + transmission group to which the parity packet + belongs + + ODATA_PKT_SQN the low-order portion of ODATA_SQN specifying the + parity packet sequence number within the + transmission group + +11.4. Procedures - Sources + + If a source elects to provide parity for a given transport session, + it MUST first provide the transmission group size PARITY_PRM_TGS in + the OPT_PARITY_PRM option of its SPMs. This becomes the maximum + effective transmission group size in the event that the source elects + to send smaller size transmission groups. If a source elects to + provide proactive parity for a given transport session, it MUST set + PARITY_PRM_PRO in the OPT_PARITY_PRM option of its SPMs. If a source + elects to provide on-demand parity for a given transport session, it + MUST set PARITY_PRM_OND in the OPT_PARITY_PRM option of its SPMs. + + A source MUST send any pro-active parity packets for a given + transmission group only after it has first sent all of the + corresponding k data packets in that group. Pro-active parity + packets MUST be sent as ODATA with OPT_PARITY in the fixed header. + + If a source elects to provide on-demand parity, it MUST respond to a + parity NAK for a transmission group with a parity NCF. The source + MUST complete the transmission of the k original data packets and the + proactive parity packets, possibly scheduled, before starting the + transmission of on-demand parity packets. Subsequently, the source + MUST send the number of parity packets requested by that parity NAK. + On-demand parity packets MUST be sent as RDATA with OPT_PARITY in the + fixed header. Previously transmitted pro-active parity packets + cannot be reused as on-demand parity packets, these MUST be computed + with new, previously unused, indexes. + + In either case, the source MUST provide selective retransmissions + only in response to selective NAKs from the leading partial + transmission group. For any group that is full, the source SHOULD + provide FEC on demand in response to a selective retransmission + request. + + In the absence of data to transmit, a source SHOULD prematurely + terminate the current transmission group by including OPT_CURR_TGSIZE + to the last data packet or to any proactive parity packets provided. + + + + +Speakman, et. al. Experimental [Page 65] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + If the last data packet has already been transmitted and there is no + provision for sending proactive parity packets, an SPM with + OPT_CURR_TGSIZE SHOULD be sent. + + A source consolidates requests for on-demand parity in the same + transmission group according to the following procedures. If the + number of pending (i.e., unsent) parity packets from a previous + request for on-demand parity packets is equal to or greater than + NAK_PKT_CNT in a subsequent NAK, that subsequent NAK MUST be + confirmed but MAY otherwise be ignored. If the number of pending + (i.e., unsent) parity packets from a previous request for on-demand + parity packets is less than NAK_PKT_CNT in a subsequent NAK, that + subsequent NAK MUST be confirmed but the source need only increase + the number of pending parity packets to NAK_PKT_CNT. + + When a source provides parity packets relative to a transmission + group with variable sized packets, it MUST compute parity packets by + padding the smaller original packets with zeroes out to the size of + the largest of the original packets. The source MUST also append the + encoded TSDU lengths at the end of any padding or directly to the end + of the largest packet, and add the OPT_VAR_PKTLEN option as specified + in the overview description. + + When a source provides variable sized transmission groups, it SHOULD + append the OPT_CURR_TGSIZE option to the last data packet in the + shortened group, and it MUST append the OPT_CURR_TGSIZE option to any + parity packets it sends within that group. In case the the last data + packet is sent before a determination has been made to shorten the + group and there is no provision for sending proactive parity packets, + an SPM with OPT_CURR_TGSIZE SHOULD be sent. The source MUST also add + OPT_CURR_TGSIZE to any SPM that it sends with SPM_LEAD equal to + OD_SQN of the last data packet. + + A receiver MUST NAK for the entire number of packets missing based on + the maximum TG size, even if it already knows that the actual TG size + is smaller. The source MUST take this into account and compute the + number of packets effectively needed as the difference between + NAK_PKT_CNT and an offset computed as the difference between the max + TG size and the effective TG size. + +11.5. Procedures - Receivers + + If a receiver elects to make use of parity packets for loss recovery, + it MUST first learn the transmission group size PARITY_PRM_TGS from + OPT_PARITY_PRM in the SPMs for the TSI. The transmission group size + is used by a receiver to determine the sequence number boundaries + between transmission groups. + + + + +Speakman, et. al. Experimental [Page 66] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Thereafter, if PARITY_PRM_PRO is also set in the SPMs for the TSI, a + receiver SHOULD use any pro-active parity packets it receives for + loss recovery, and if PARITY_PRM_OND is also set in the SPMs for the + TSI, it MAY solicit on-demand parity packets upon loss detection. If + PARITY_PRM_OND is set, a receiver MUST NOT send selective NAKs, + except in partial transmission groups if the source does not use the + variable transmission-group size option. Parity packets are ODATA + (pro-active) or RDATA (on-demand) packets distinguished by OPT_PARITY + which lets receivers know that ODATA/RDATA_TG_SQN identifies the + group of PARITY_PRM_TGS packets to which the parity may be applied + for loss recovery in the corresponding transmission group, and that + ODATA/RDATA_PKT_SQN is being reused to number the parity packets + within that group. Receivers order parity packets and eliminate + duplicates within a transmission group based on ODATA/RDATA_PKT_SQN + and on OPT_PARITY_GRP if present. + + To solicit on-demand parity packets, a receiver MUST send parity NAKs + upon loss detection. For the purposes of soliciting on-demand + parity, loss detection occurs at transmission group boundaries, i.e. + upon receipt of the last data packet in a transmission group, upon + receipt of any data packet in any subsequent transmission group, or + upon receipt of any parity packet in the current or a subsequent + transmission group. + + A parity NAK is simply a NAK with OPT_PARITY and NAK_PKT_CNT set to + the count of the number of packets detected to be missing from the + transmission group specified by NAK_TG_SQN. Note that this + constrains the receiver to request no more parity packets than there + are data packets in the transmission group. + + A receiver SHOULD bias the value of NAK_BO_IVL for parity NAKs + inversely proportional to NAK_PKT_CNT so that NAKs for larger losses + are likely to be scheduled ahead of NAKs for smaller losses in the + same receiver population. + + A confirming NCF for a parity NAK is a parity NCF with NCF_PKT_CNT + equal to or greater than that specified by the parity NAK. + + A receiver's NAK_RDATA_IVL timer is not cancelled until all requested + parity packets have been received. + + In the absence of data (detected from SPMs bearing SPM_LEAD equal to + RXW_LEAD) on non-transmission-group boundaries, receivers MAY resort + to selective NAKs for any missing packets in that partial + transmission group. + + + + + + +Speakman, et. al. Experimental [Page 67] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + When a receiver handles parity packets belonging to a transmission + group with variable sized packets, (detected from the presence of the + OPT_VAR_PKTLEN option in the parity packets), it MUST decode them as + specified in the overview description and use the decoded TSDU length + to get rid of the padding in the decoded packet. + + If the source was using a variable sized transmission group via the + OPT_CURR_TGSIZE, the receiver might learn this before having + requested (and received) any retransmission. The above happens if it + sees OPT_CURR_TGSIZE in the last data packet of the TG, in any + proactive parity packet or in a SPM. If the receivers learns this + and determines that it has missed one or more packets in the + shortened transmission group, it MAY then NAK for them without + waiting for the start of the next transmission group. Otherwise it + will start NAKing at the start of the next transmission group. + + In both cases, the receiver MUST NAK for the number of packets + missing assuming that the size of the transmission group is the + maximum effective transmission group. In other words, the receivers + cannot exploit the fact that it might already know that the + transmission group was smaller but MUST always NAK for the number of + packets it believes are missing, plus the number of packets required + to bring the total packets up to the maximum effective transmission + group size. + + After the first parity packet has been delivered to the receiver, the + actual TG size is known to him, either because already known or + because discovered via OPT_CURR_TGSIZE contained in the parity + packet. Hence the receiver can decode the whole group as soon as the + minimum number of parity packets needed is received. + +11.6. Procedures - Network Elements + + Pro-active parity packets (ODATA with OPT_PARITY) are switched by + network elements without transport-layer intervention. + + On-demand parity packets (RDATA with OPT_PARITY) necessitate modified + request, confirmation and repair constraint procedures for network + elements. In the context of these procedures, repair state is + maintained per NAK_TSI and NAK_TG_SQN, and in addition to recording + the interfaces on which corresponding NAKs have been received, + records the largest value of NAK_PKT_CNT seen in corresponding NAKs + on each interface. This value is referred to as the known packet + count. The largest of the known packet counts recorded for any + interface in the repair state for the transmit group or carried by an + NCF is referred to as the largest known packet count. + + + + + +Speakman, et. al. Experimental [Page 68] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Upon receipt of a parity NAK, a network element responds with the + corresponding parity NCF. The corresponding parity NCF is just an + NCF formed in the usual way (i.e., a multicast copy of the NAK with + the packet type changed), but with the addition of OPT_PARITY and + with NCF_PKT_CNT set to the larger of NAK_PKT_CNT and the known + packet count for the receiving interface. The network element then + creates repair state in the usual way with the following + modifications. + + If repair state for the receiving interface does not exist, the + network element MUST create it and additionally record NAK_PKT_CNT + from the parity NAK as the known packet count for the receiving + interface. + + If repair state for the receiving interface already exists, the + network element MUST eliminate the NAK only if NAK_ELIM_IVL has not + expired and NAK_PKT_CNT is equal to or less than the largest known + packet count. If NAK_PKT_CNT is greater than the known packet count + for the receiving interface, the network element MUST update the + latter with the larger NAK_PKT_CNT. + + Upon either adding a new interface or updating the known packet count + for an existing interface, the network element MUST determine if + NAK_PKT_CNT is greater than the largest known packet count. If so or + if NAK_ELIM_IVL has expired, the network element MUST forward the + parity NAK in the usual way with a value of NAK_PKT_CNT equal to the + largest known packet count. + + Upon receipt of an on-demand parity packet, a network element MUST + locate existing repair state for the corresponding RDATA_TSI and + RDATA_TG_SQN. If no such repair state exists, the network element + MUST discard the RDATA as usual. + + If corresponding repair state exists, the largest known packet count + MUST be decremented by one, then the network element MUST forward the + RDATA on all interfaces in the existing repair state, and decrement + the known packet count by one for each. Any interfaces whose known + packet count is thereby reduced to zero MUST be deleted from the + repair state. If the number of interfaces is thereby reduced to + zero, the repair state itself MUST be deleted. + + Upon reception of a parity NCF, network elements MUST cancel pending + NAK retransmission only if NCF_PKT_CNT is greater or equal to the + largest known packet count. Network elements MUST use parity NCFs to + anticipate NAKs in the usual way with the addition of recording + NCF_PKT_CNT from the parity NCF as the largest known packet count + with the anticipated state so that any subsequent NAKs received with + NAK_PKT_CNT equal to or less than NCF_PKT_CNT will be eliminated, and + + + +Speakman, et. al. Experimental [Page 69] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + any with NAK_PKT_CNT greater than NCF_PKT_CNT will be forwarded. + Network elements which receive a parity NCF with NCF_PKT_CNT larger + than the largest known packet count MUST also use it to anticipate + NAKs, increasing the largest known packet count to reflect + NCF_PKT_CNT (partial anticipation). + + Parity NNAKs follow the usual elimination procedures with the + exception that NNAKs are eliminated only if existing NAK state has a + NAK_PKT_CNT greater than NNAK_PKT_CNT. + + Network elements must take extra precaution when the source is using + a variable sized transmission group. Network elements learn that the + source is using a TG size smaller than the maximum from + OPT_CURR_TGSIZE in parity RDATAs or in SPMs. When this happens, they + compute a TG size offset as the difference between the maximum TG + size and the actual TG size advertised by OPT_CURR_TGSIZE. Upon + reception of parity RDATA, the TG size offset is used to update the + repair state as follows: + + Any interface whose known packet count is reduced to the TG size + offset is deleted from the repair state. + + This replaces the normal rule for deleting interfaces that applies + when the TG size is equal to the maximum TG size. + +11.7. Procedures - DLRs + + A DLR with the ability to provide FEC repairs MUST indicate this by + setting the OPT_PARITY bit in the redirecting POLR. It MUST then + process any redirected FEC NAKs in the usual way. + +11.8. Packet Formats + +11.8.1. OPT_PARITY_PRM - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| |P O| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Transmission Group Size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x08 + + Option Length = 8 octets + + P-bit (PARITY_PRM_PRO) + + + +Speakman, et. al. Experimental [Page 70] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Indicates when set that the source is providing pro-active parity + packets. + + O-bit (PARITY_PRM_OND) + + Indicates when set that the source is providing on-demand parity + packets. + + At least one of PARITY_PRM_PRO and PARITY_PRM_OND MUST be set. + + Transmission Group Size (PARITY_PRM_TGS) + + The number of data packets in the transmission group over which + the parity packets are calculated. If a variable transmission + group size is being used, then this becomes the maximum effective + transmission group size across the session. + + OPT_PARITY_PRM MAY be appended only to SPMs. + + OPT_PARITY_PRM is network-significant. + +11.8.2. OPT_PARITY_GRP - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Parity Group Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x09 + + Option Length = 8 octets + + Parity Group Number (PRM_GROUP) + + The number of the group of k parity packets amongst the h parity + packets within the transmission group to which the parity packet + belongs, where the first k parity packets are in group zero. + PRM_GROUP MUST NOT be zero. + + OPT_PARITY_GRP MAY be appended only to parity packets. + + OPT_PARITY_GRP is NOT network-significant. + + + + + + +Speakman, et. al. Experimental [Page 71] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +11.8.3. OPT_CURR_TGSIZE - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Actual Transmission Group Size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x0A + + Option Length = 8 octets + + Actual Transmission Group Size (PRM_ATGSIZE) + + The actual number of data packets in this transmission group. + This MUST be less than or equal to the maximum transmission group + size PARITY_PRM_TGS in OPT_PARITY_PRM. + + OPT_CURR_TGSIZE MAY be appended to data and parity packets (ODATA or + RDATA) and to SPMs. + + OPT_CURR_TGSIZE is network-significant except when appended to ODATA. + +12. Appendix B - Support for Congestion Control + +12.1. Introduction + + A source MUST implement strategies for congestion avoidance, aimed at + providing overall network stability, fairness among competing PGM + flows, and some degree of fairness towards coexisting TCP flows [13]. + In order to do this, the source must be provided with feedback on the + status of the network in terms of traffic load. This appendix + specifies NE procedures that provide such feedback to the source in a + scalable way. (An alternative TCP-friendly scheme for congestion + control that does not require NE support can be found in [16]). + + The procedures specified in this section enable the collection and + selective forwarding of three types of feedback to the source: + + o Worst link load as measured in network elements. + + o Worst end-to-end path load as measured in network elements. + + o Worst end-to-end path load as reported by receivers. + + + + + +Speakman, et. al. Experimental [Page 72] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + This specification defines in detail NE procedures, receivers + procedures and packet formats. It also defines basic procedures in + receivers for generating congestion reports. This specification does + not define the procedures used by PGM sources to adapt their + transmission rates in response of congestion reports. Those + procedures depend upon the specific congestion control scheme. + + PGM defines a header option that PGM receivers may append to NAKs + (OPT_CR). OPT_CR carries congestion reports in NAKs that propagate + upstream towards the source. + + During the process of hop-by-hop reverse NAK forwarding, NEs examine + OPT_CR and possibly modify its contents prior to forwarding the NAK + upstream. Forwarding CRs also has the side effect of creating + congestion report state in the NE. The presence of OPT_CR and its + contents also influences the normal NAK suppression rules. Both the + modification performed on the congestion report and the additional + suppression rules depend on the content of the congestion report and + on the congestion report state recorded in the NE as detailed below. + + OPT_CR contains the following fields: + + OPT_CR_NE_WL Reports the load in the worst link as detected though + NE internal measurements + + OPT_CR_NE_WP Reports the load in the worst end-to-end path as + detected though NE internal measurements + + OPT_CR_RX_WP Reports the load in the worst end-to-end path as + detected by receivers + + A load report is either a packet drop rate (as measured at an NE's + interfaces) or a packet loss rate (as measured in receivers). Its + value is linearly encoded in the range 0-0xFFFF, where 0xFFFF + represents a 100% loss/drop rate. Receivers that send a NAK bearing + OPT_CR determine which of the three report fields are being reported. + + OPT_CR also contains the following fields: + + OPT_CR_NEL A bit indicating that OPT_CR_NE_WL is being reported. + + OPT_CR_NEP A bit indicating that OPT_CR_NE_WP is being reported. + + OPT_CR_RXP A bit indicating that OPT_CR_RX_WP is being reported. + + + + + + + +Speakman, et. al. Experimental [Page 73] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + OPT_CR_LEAD A SQN in the ODATA space that serves as a temporal + reference for the load report values. This is + initialized by receivers with the leading edge of the + transmit window as known at the moment of transmitting + the NAK. This value MAY be advanced in NEs that + modify the content of OPT_CR. + + OPT_CR_RCVR The identity of the receiver that generated the worst + OPT_CR_RX_WP. + + The complete format of the option is specified later. + +12.2. NE-Based Worst Link Report + + To permit network elements to report worst link, receivers append + OPT_CR to a NAK with bit OPT_CR_NEL set and OPT_CR_NE_WL set to zero. + NEs receiving NAKs that contain OPT_CR_NE_WL process the option and + update per-TSI state related to it as described below. The ultimate + result of the NEs' actions ensures that when a NAK leaves a sub-tree, + OPT_CR_NE_WL contains a congestion report that reflects the load of + the worst link in that sub-tree. To achieve this, NEs rewrite + OPT_CR_NE_WL with the worst value among the loads measured on the + local (outgoing) links for the session and the congestion reports + received from those links. + + Note that the mechanism described in this sub-section does not permit + the monitoring of the load on (outgoing) links at non-PGM-capable + multicast routers. For this reason, NE-Based Worst Link Reports + SHOULD be used in pure PGM topologies only. Otherwise, this + mechanism might fail in detecting congestion. To overcome this + limitation PGM sources MAY use a heuristic that combines NE-Based + Worst Link Reports and Receiver-Based Reports. + +12.3. NE-Based Worst Path Report + + To permit network elements to report a worst path, receivers append + OPT_CR to a NAK with bit OPT_CR_NEP set and OPT_CR_NE_WP set to zero. + The processing of this field is similar to that of OPT_CR_NE_WL with + the difference that, on the reception of a NAK, the value of + OPT_CR_NE_WP is adjusted with the load measured on the interface on + which the NAK was received according to the following formula: + + OPT_CR_NE_WP = if_load + OPT_CR_NE_WP * (100% - if_loss_rate) + + The worst among the adjusted OPT_CR_NE_WP is then written in the + outgoing NAK. This results in a hop-by-hop accumulation of link loss + rates into a path loss rate. + + + + +Speakman, et. al. Experimental [Page 74] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + As with OPT_CR_NE_WL, the congestion report in OPT_CR_NE_WP may be + invalid if the multicast distribution tree includes non-PGM-capable + routers. + +12.4. Receiver-Based Worst Report + + To report a packet loss rate, receivers append OPT_CR to a NAK with + bit OPT_CR_RXP set and OPT_CR_RX_WP set to the packet loss rate. NEs + receiving NAKs that contain OPT_CR_RX_WP process the option and + update per-TSI state related to it as described below. The ultimate + result of the NEs' actions ensures that when a NAK leaves a sub-tree, + OPT_CR_RX_WP contains a congestion report that reflects the load of + the worst receiver in that sub-tree. To achieve this, NEs rewrite + OTP_CR_RE_WP with the worst value among the congestion reports + received on its outgoing links for the session. In addition to this, + OPT_CR_RCVR MUST contain the NLA of the receiver that originally + measured the value of OTP_CR_RE_WP being forwarded. + +12.5. Procedures - Receivers + + To enable the generation of any type of congestion report, receivers + MUST insert OPT_CR in each NAK they generate and provide the + corresponding field (OPT_CR_NE_WL, OPT_CR_NE_WP, OPT_CR_RX_WP). The + specific fields to be reported will be advertised to receivers in + OPT_CRQST on the session's SPMs. Receivers MUST provide only those + options requested in OPT_CRQST. + + Receivers MUST initialize OPT_CR_NE_WL and OPT_CR_NE_WP to 0 and they + MUST initialize OPT_CR_RCVR to their NLA. At the moment of sending + the NAK, they MUST also initialize OPT_CR_LEAD to the leading edge of + the transmission window. + + Additionally, if a receiver generates a NAK with OPT_CR with + OPT_CR_RX_WP, it MUST initialize OPT_CR_RX_WP to the proper value, + internally computed. + +12.6. Procedures - Network Elements + + Network elements start processing each OPT_CR by selecting a + reference SQN in the ODATA space. The reference SQN selected is the + highest SQN known to the NE. Usually this is OPT_CR_LEAD contained + in the NAK received. + + They use the selected SQN to age the value of load measurement as + follows: + + o locally measured load values (e.g. interface loads) are + considered up-to-date + + + +Speakman, et. al. Experimental [Page 75] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + o load values carried in OPT_CR are considered up-to-date and are + not aged so as to be independent of variance in round-trip + times from the network element to the receivers + + o old load values recorded in the NE are exponentially aged + according to the difference between the selected reference SQN + and the reference SQN associated with the old load value. + + The exponential aging is computed so that a recorded value gets + scaled down by a factor exp(-1/2) each time the expected inter-NAK + time elapses. Hence the aging formula must include the current loss + rate as follows: + + aged_loss_rate = loss_rate * exp( - SQN_difference * loss_rate / + 2) + + Note that the quantity 1/loss_rate is the expected SQN_lag between + two NAKs, hence the formula above can also be read as: + + aged_loss_rate = loss_rate * exp( - 1/2 * SQN_difference / + SQN_lag) + + which equates to (loss_rate * exp(-1/2)) when the SQN_difference is + equal to expected SQN_lag between two NAKs. + + All the subsequent computations refer to the aged load values. + + Network elements process OPT_CR by handling the three possible types + of congestion reports independently. + + For each congestion report in an incoming NAK, a new value is + computed to be used in the outgoing NAK: + + o The new value for OPT_CR_NE_WL is the maximum of the load + measured on the outgoing interfaces for the session, the value + of OPT_CR_NE_WL of the incoming NAK, and the value previously + sent upstream (recorded in the NE). All these values are as + obtained after the aging process. + + o The new value for OPT_CR_NE_WP is the maximum of the value + previously sent upstream (after aging) and the value of + OPT_CR_NE_WP in the incoming NAK adjusted with the load on the + interface upon which the NAK was received (as described above). + + o The new value for OPT_CR_RX_WP is the maximum of the value + previously sent upstream (after aging) and the value of + OPT_CR_RX_WP in the incoming NAK. + + + + +Speakman, et. al. Experimental [Page 76] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + o If OPT_CR_RX_WP was selected from the incoming NAK, the new + value for OPT_CR_RCVR is the one in the incoming NAK. + Otherwise it is the value previously sent upstream. + + o The new value for OPT_CR_LEAD is the reference SQN selected for + the aging procedure. + +12.6.1. Overriding Normal Suppression Rules + + Normal suppression rules hold to determine if a NAK should be + forwarded upstream or not. However if any of the outgoing congestion + reports has changed by more than 5% relative to the one previously + sent upstream, this new NAK is not suppressed. + +12.6.2. Link Load Measurement + + PGM routers monitor the load on all their outgoing links and record + it in the form of per-interface loss rate statistics. "load" MUST be + interpreted as the percentage of the packets meant to be forwarded on + the interface that were dropped. Load statistics refer to the + aggregate traffic on the links and not to PGM traffic only. + + This document does not specify the algorithm to be used to collect + such statistics, but requires that such algorithm provide both + accuracy and responsiveness in the measurement process. As far as + accuracy is concerned, the expected measurement error SHOULD be + upper-limited (e.g. less than than 10%). As far as responsiveness is + concerned, the measured load SHOULD converge to the actual value in a + limited time (e.g. converge to 90% of the actual value in less than + 200 milliseconds), when the instantaneous offered load changes. + Whenever both requirements cannot be met at the same time, accuracy + SHOULD be traded for responsiveness. + + + + + + + + + + + + + + + + + + + +Speakman, et. al. Experimental [Page 77] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +12.7. Packet Formats + +12.7.1. OPT_CR - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| L P R| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Congestion Report Reference SQN | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NE Worst Link | NE Worst Path | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Rcvr Worst Path | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NLA AFI | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Worst Receiver's NLA ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + + Option Type = 0x10 + + Option Length = 20 octets + NLA length + + L OPT_CR_NEL bit : set indicates OPT_CR_NE_WL is being reported + + P OPT_CR_NEP bit : set indicates OPT_CR_NE_WP is being reported + + R OPT_CR_RXP bit : set indicates OPT_CR_RX_WP is being reported + + Congestion Report Reference SQN (OPT_CR_LEAD). + + A SQN in the ODATA space that serves as a temporal reference point + for the load report values. + + NE Worst Link (OPT_CR_NE_WL). + + Reports the load in the worst link as detected though NE internal + measurements + + NE Worst Path (OPT_CR_NE_WP). + + Reports the load in the worst end-to-end path as detected though + NE internal measurements + + + + + + + +Speakman, et. al. Experimental [Page 78] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Rcvr Worst Path (OPT_CR_RX_WP). + + Reports the load in the worst end-to-end path as detected by + receivers + + Worst Receiver's NLA (OPT_CR_RCVR). + + The unicast address of the receiver that generated the worst + OPT_CR_RX_WP. + + OPT_CR MAY be appended only to NAKs. + + OPT-CR is network-significant. + +12.7.2. OPT_CRQST - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| L P R| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x11 + + Option Length = 4 octets + + L OPT_CRQST_NEL bit : set indicates OPT_CR_NE_WL is being + requested + + P OPT_CRQST_NEP bit : set indicates OPT_CR_NE_WP is being + requested + + R OPT_CRQST_RXP bit : set indicates OPT_CR_RX_WP is being + requested + + OPT_CRQST MAY be appended only to SPMs. + + OPT-CRQST is network-significant. + +13. Appendix C - SPM Requests + +13.1. Introduction + + SPM Requests (SPMRs) MAY be used to solicit an SPM from a source in a + non-implosive way. The typical application is for late-joining + receivers to solicit SPMs directly from a source in order to be able + to NAK for missing packets without having to wait for a regularly + scheduled SPM from that source. + + + +Speakman, et. al. Experimental [Page 79] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +13.2. Overview + + Allowing for SPMR implosion protection procedures, a receiver MAY + unicast an SPMR to a source to solicit the most current session, + window, and path state from that source any time after the receiver + has joined the group. A receiver may learn the TSI and source to + which to direct the SPMR from any other PGM packet it receives in the + group, or by any other means such as from local configuration or + directory services. The receiver MUST use the usual SPM procedures + to glean the unicast address to which it should direct its NAKs from + the solicited SPM. + +13.3. Packet Contents + + This section just provides enough short-hand to make the Procedures + intelligible. For the full details of packet contents, please refer + to Packet Formats below. + +13.3.1. SPM Requests + + SPMRs are transmitted by receivers to solicit SPMs from a source. + + SPMs are unicast to a source and contain: + + SPMR_TSI the source-assigned TSI for the session to which the + SPMR corresponds + +13.4. Procedures - Sources + + A source MUST respond immediately to an SPMR with the corresponding + SPM rate limited to once per IHB_MIN per TSI. The corresponding SPM + matches SPM_TSI to SPMR_TSI and SPM_DPORT to SPMR_DPORT. + +13.5. Procedures - Receivers + + To moderate the potentially implosive behavior of SPMRs at least on a + densely populated subnet, receivers MUST use the following back-off + and suppression procedure based on multicasting the SPMR with a TTL + of 1 ahead of and in addition to unicasting the SPMR to the source. + The role of the multicast SPMR is to suppress the transmission of + identical SPMRs from the subnet. + + More specifically, before unicasting a given SPMR, receivers MUST + choose a random delay on SPMR_BO_IVL (~250 msecs) during which they + listen for a multicast of an identical SPMR. If a receiver does not + see a matching multicast SPMR within its chosen random interval, it + MUST first multicast its own SPMR to the group with a TTL of 1 before + then unicasting its own SPMR to the source. If a receiver does see a + + + +Speakman, et. al. Experimental [Page 80] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + matching multicast SPMR within its chosen random interval, it MUST + refrain from unicasting its SPMR and wait instead for the + corresponding SPM. + + In addition, receipt of the corresponding SPM within this random + interval SHOULD cancel transmission of an SPMR. + + In either case, the receiver MUST wait at least SPMR_SPM_IVL before + attempting to repeat the SPMR by choosing another delay on + SPMR_BO_IVL and repeating the procedure above. + + The corresponding SPMR matches SPMR_TSI to SPMR_TSI and SPMR_DPORT to + SPMR_DPORT. The corresponding SPM matches SPM_TSI to SPMR_TSI and + SPM_DPORT to SPMR_DPORT. + +13.6. SPM Requests + + SPMR: + + SPM Requests are sent by receivers to request the immediate + transmission of an SPM for the given TSI from a source. + + The network-header source address of an SPMR is the unicast NLA of + the entity that originates the SPMR. + + The network-header destination address of an SPMR is the unicast NLA + of the source from which the corresponding SPM is requested. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Source Port | Destination Port | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Options | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Global Source ID ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ... Global Source ID | TSDU Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Option Extensions when present ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... + + Source Port: + + SPMR_SPORT + + Data-Destination Port + + + + +Speakman, et. al. Experimental [Page 81] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Destination Port: + + SPMR_DPORT + + Data-Source Port, together with Global Source ID forms SPMR_TSI + + Type: + + SPMR_TYPE = 0x0C + + Global Source ID: + + SPMR_GSI + + Together with Source Port forms + + SPMR_TSI + +14. Appendix D - Poll Mechanism + +14.1. Introduction + + These procedures provide PGM network elements and sources with the + ability to poll their downstream PGM neighbors to solicit replies + in an implosion-controlled way. + + Both general polls and specific polls are possible. The former + provide a PGM (parent) node with a way to check if there are any + PGM (children) nodes connected to it, both network elements and + receivers, and to estimate their number. The latter may be used + by PGM parent nodes to search for nodes with specific properties + among its PGM children. An example of application for this is DLR + discovery. + + Polling is implemented using two additional PGM packets: + + POLL a Poll Request that PGM parent nodes multicast to the group to + perform the poll. Similarly to NCFs, POLL packets stop at the + first PGM node they reach, as they are not forwarded by PGM + network elements. + + POLR a Poll Response that PGM children nodes (either network elements + or receivers) use to reply to a Poll Request by addressing it + to the NLA of the interface from which the triggering POLL was + sent. + + + + + + +Speakman, et. al. Experimental [Page 82] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + The polling mechanism dictates that PGM children nodes that receive a + POLL packet reply to it only if certain conditions are satisfied and + ignore the POLL otherwise. Two types of condition are possible: a + random condition that defines a probability of replying for the + polled child, and a deterministic condition. Both the random + condition and the deterministic condition are controlled by the + polling PGM parent node by specifying the probability of replying and + defining the deterministic condition(s) respectively. Random-only + poll, deterministic-only poll or a combination of the two are + possible. + + The random condition in polls allows the prevention of implosion of + replies by controlling their number. Given a probability of replying + P and assuming that each receiver makes an independent decision, the + number of expected replies to a poll is P*N where N is the number of + PGM children relative to the polling PGM parent. The polling node + can control the number of expected replies by specifying P in the + POLL packet. + +14.2. Packet Contents + + This section just provides enough short-hand to make the Procedures + intelligible. For the full details of packet contents, please refer + to Packet Formats below. + +14.2.1. POLL (Poll Request) + + POLL_SQN a sequence number assigned sequentially by the polling + parent in unit increments and scoped by POLL_PATH and + the TSI of the session. + + POLL_ROUND a poll round sequence number. Multiple poll rounds + are possible within a POLL_SQN. + + POLL_S_TYPE the sub-type of the poll request + + POLL_PATH the network-layer address (NLA) of the interface on + the PGM network element or source on which the POLL is + transmitted + + POLL_BO_IVL the back-off interval that MUST be used to compute the + random back-off time to wait before sending the + response to a poll. POLL_BO_IVL is expressed in + microseconds. + + POLL_RAND a random string used to implement the randomness in + replying + + + + +Speakman, et. al. Experimental [Page 83] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + POLL_MASK a bit-mask used to determine the probability of random + replies + + Poll request MAY also contain options which specify deterministic + conditions for the reply. No options are currently defined. + +14.2.2. POLR (Poll Response) + + POLR_SQN POLL_SQN of the poll request for which this is a reply + + POLR_ROUND POLL_ROUND of the poll request for which this is a + reply + + Poll response MAY also contain options. No options are currently + defined. + +14.3. Procedures - General + +14.3.1. General Polls + + General Polls may be used to check for and count PGM children that + are 1 PGM hop downstream of an interface of a given node. They have + POLL_S_TYPE equal to PGM_POLL_GENERAL. PGM children that receive a + general poll decide whether to reply to it only based on the random + condition present in the POLL. + + To prevent response implosion, PGM parents that initiate a general + poll SHOULD establish the probability of replying to the poll, P, so + that the expected number of replies is contained. The expected + number of replies is N * P, where N is the number of children. To be + able to compute this number, PGM parents SHOULD already have a rough + estimate of the number of children. If they do not have a recent + estimate of this number, they SHOULD send the first poll with a very + low probability of replying and increase it in subsequent polls in + order to get the desired number of replies. + + To prevent poll-response implosion caused by a sudden increase in the + children population occurring between two consecutive polls with + increasing probability of replying, PGM parents SHOULD use poll + rounds. Poll rounds allow PGM parents to "freeze" the size of the + children population when they start a poll and to maintain it + constant across multiple polls (with the same POLL_SQN but different + POLL_ROUND). This works by allowing PGM children to respond to a + poll only if its POLL_ROUND is zero or if they have previously + received a poll with the same POLL_SQN and POLL_ROUND equal to zero. + + + + + + +Speakman, et. al. Experimental [Page 84] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + In addition to this PGM children MUST observe a random back-off in + replying to a poll. This spreads out the replies in time and allows + a PGM parent to abort the poll if too many replies are being + received. To abort an ongoing poll a PGM parent MUST initiate + another poll with different POLL_SQN. PGM children that receive a + POLL MUST cancel any pending reply for POLLs with POLL_SQN different + from the one of the last POLL received. + + For a given poll with probability of replying P, a PGM parent + estimates the number of children as M / P, where M is the number of + responses received. PGM parents SHOULD keep polling periodically and + use some average of the result of recent polls as their estimate for + the number of children. + +14.3.2. Specific Polls + + Specific polls provide a way to search for PGM children that comply + to specific requisites. As an example specific poll could be used to + search for down-stream DLRs. A specific poll is characterized by a + POLL_S_TYPE different from PGM_POLL_GENERAL. PGM children decide + whether to reply to a specific poll or not based on the POLL_S_TYPE, + on the random condition and on options possibly present in the POLL. + The way options should be interpreted is defined by POLL_S_TYPE. The + random condition MUST be interpreted as an additional condition to be + satisfied. To disable the random condition PGM parents MUST specify + a probability of replying P equal to 1. + + PGM children MUST ignore a POLL packet if they do not understand + POLL_S_TYPE. Some specific POLL_S_TYPE may also require that the + children ignore a POLL if they do not fully understand all the PGM + options present in the packet. + +14.4. Procedures - PGM Parents (Sources or Network Elements) + + A PGM parent (source or network element), that wants to poll the + first PGM-hop children connected to one of its outgoing interfaces + MUST send a POLL packet on that interface with: + + POLL_SQN equal to POLL_SQN of the last POLL sent incremented by + one. If poll rounds are used, this must be equal to + POLL_SQN of the last group of rounds incremented by + one. + + POLL_ROUND The round of the poll. If the poll has a single + round, this must be zero. If the poll has multiple + rounds, this must be one plus the last POLL_ROUND for + the same POLL_SQN, or zero if this is the first round + within this POLL_SQN. + + + +Speakman, et. al. Experimental [Page 85] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + POLL_S_TYPE the type of the poll. For general poll use + PGM_POLL_GENERAL + + POLL_PATH set to the NLA of the outgoing interface + + POLL_BO_IVL set to the wanted reply back-off interval. As far as + the choice of this is concerned, using NAK_BO_IVL is + usually a conservative option, however a smaller value + MAY be used, if the number of expected replies can be + determined with a good confidence or if a + conservatively low probability of reply (P) is being + used (see POLL_MASK next). When the number of + expected replies is unknown, a large POLL_BO_IVL + SHOULD be used, so that the poll can be effectively + aborted if the number of replies being received is too + large. + + POLL_RAND MUST be a random string re-computed each time a new + poll is sent on a given interface + + POLL_MASK determines the probability of replying, P, according + to the relationship P = 1 / ( 2 ^ B ), where B is the + number of bits set in POLL_MASK [15]. If this is a + deterministic poll, B MUST be 0, i.e. POLL_MASK MUST + be a all-zeroes bit-mask. + + Nota Bene: POLLs transmitted by network elements MUST bear the + ODATA source's network-header source address, not the network + element's NLA. POLLs MUST also be transmitted with the IP + + Router Alert Option [6], to be allow PGM network element to + intercept them. + + A PGM parent that has started a poll by sending a POLL packet SHOULD + wait at least POLL_BO_IVL before starting another poll. During this + interval it SHOULD collect all the valid response (the one with + POLR_SQN and POLR_ROUND matching with the outstanding poll) and + process them at the end of the collection interval. + + A PGM parent SHOULD observe the rules mentioned in the description of + general procedures, to prevent implosion of response. These rules + should in general be observed both for generic polls and specific + polls. The latter however can be performed using deterministic poll + (with no implosion prevention) if the expected number of replies is + known to be small. A PGM parent that issue a generic poll with the + intent of estimating the children population size SHOULD use poll + rounds to "freeze" the children that are involved in the measure + process. This allows the sender to "open the door wider" across + + + +Speakman, et. al. Experimental [Page 86] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + subsequent rounds (by increasing the probability of response), + without fear of being flooded by late joiners. Note the use of + rounds has the drawback of introducing additional delay in the + estimate of the population size, as the estimate obtained at the end + of a round-group refers to the condition present at the time of the + first round. + + A PGM parent that has started a poll SHOULD monitor the number of + replies during the collection phase. If this become too large, the + PGM parent SHOULD abort the poll by immediately starting a new poll + (different POLL_SQN) and specifying a very low probability of + replying. + + + When polling is being used to estimate the receiver population for + the purpose of calculating NAK_BO_IVL, OPT_NAK_BO_IVL (see 16.4.1 + below) MUST be appended to SPMs, MAY be appended to NCFs and POLLs, + and in all cases MUST have NAK_BO_IVL_SQN set to POLL_SQN of the most + recent complete round of polls, and MUST bear that round's + corresponding derived value of NAK_BAK_IVL. In this way, + OPT_NAK_BO_IVL provides a current value for NAK_BO_IVL at the same + time as information is being gathered for the calculation of a future + value of NAK_BO_IVL. + +14.5. Procedures - PGM Children (Receivers or Network Elements) + + PGM receivers and network elements MUST compute a 32-bit random node + identifier (RAND_NODE_ID) at startup time. When a PGM child + (receiver or network element) receives a POLL it MUST use its + RAND_NODE_ID to match POLL_RAND of incoming POLLs. The match is + limited to the bits specified by POLL_MASK. If the incoming POLL + contain a POLL_MASK made of all zeroes, the match is successful + despite the content of POLL_RAND (deterministic reply). If the match + fails, then the receiver or network element MUST discard the POLL + without any further action, otherwise it MUST check the fields + POLL_ROUND, POLL_S_TYPE and any PGM option included in the POLL to + determine whether it SHOULD reply to the poll. + + If POLL_ROUND is non-zero and the PGM receiver has not received a + previous poll with the same POLL_SQN and a zero POLL_ROUND, it MUST + discard the poll without further action. + + If POLL_S_TYPE is equal to PGM_POLL_GENERAL, the PGM child MUST + schedule a reply to the POLL despite the presence of PGM options on + the POLL packet. + + + + + + +Speakman, et. al. Experimental [Page 87] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + If POLL_S_TYPE is different from PGM_POLL_GENERAL, the decision on + whether a reply should be scheduled depends on the actual type and on + the options possibly present in the POLL. + + If POLL_S_TYPE is unknown to the recipient of the POLL, it MUST NOT + reply and ignore the poll. Currently the only POLL_S_TYPE defined + are PGM_POLL_GENERAL and PGM_POLL_DLR. + + If a PGM receiver or network element has decided to reply to a POLL, + it MUST schedule the transmission of a single POLR at a random time + in the future. The random delay is chosen in the interval [0, + POLL_BO_IVL]. POLL_BO_IVL is the one contained in the POLL received. + When this timer expires, it MUST send a POLR using POLL_PATH of the + received POLL as destination address. POLR_SQN MUST be equal to + POLL_SQN and POLR_ROUND must be equal to POLL_ROUND. The POLR MAY + contain PGM options according to the semantic of POLL_S_TYPE or the + semantic of PGM options possibly present in the POLL. If POLL_S_TYPE + is PGM_POLL_GENERAL no option is REQUIRED. + + A PGM receiver or network element MUST cancel any pending + transmission of POLRs if a new POLL is received with POLL_SQN + different from POLR_SQN of the poll that scheduled POLRs. + +14.6. Constant Definition + + The POLL_S_TYPE values currently defined are: + + PGM_POLL_GENERAL 0 + + PGM_POLL_DLR 1 + +14.7. Packet Formats + + The packet formats described in this section are transport-layer + headers that MUST immediately follow the network-layer header in the + packet. + + The descriptions of Data-Source Port, Data-Destination Port, Options, + Checksum, Global Source ID (GSI), and TSDU Length are those provided + in Section 8. + +14.7.1. Poll Request + + POLL are sent by PGM parents (sources or network elements) to + initiate a poll among their first PGM-hop children. + + + + + + +Speakman, et. al. Experimental [Page 88] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + POLLs are sent to the ODATA multicast group. The network-header + source address of a POLL is the ODATA source's NLA. POLL MUST be + transmitted with the IP Router Alert Option. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Source Port | Destination Port | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Options | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Global Source ID ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ... Global Source ID | TSDU Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | POLL's Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | POLL's Round | POLL's Sub-type | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NLA AFI | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Path NLA ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + | POLL's Back-off Interval | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Random String | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Matching Bit-Mask | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Option Extensions when present ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Source Port: + + POLL_SPORT + + Data-Source Port, together with POLL_GSI forms POLL_TSI + + Destination Port: + + POLL_DPORT + + Data-Destination Port + + Type: + + POLL_TYPE = 0x01 + + + + +Speakman, et. al. Experimental [Page 89] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Global Source ID: + + POLL_GSI + + Together with POLL_SPORT forms POLL_TSI + + POLL's Sequence Number + + POLL_SQN + + The sequence number assigned to the POLL by the originator. + + POLL's Round + + POLL_ROUND + + The round number within the poll sequence number. + + POLL's Sub-type + + POLL_S_TYPE + + The sub-type of the poll request. + + Path NLA: + + POLL_PATH + + The NLA of the interface on the source or network element on which + this POLL was forwarded. + + POLL's Back-off Interval + + POLL_BO_IVL + + The back-off interval used to compute a random back-off for the + reply, expressed in microseconds. + + Random String + + POLL_RAND + + A random string used to implement the random condition in + replying. + + + + + + + +Speakman, et. al. Experimental [Page 90] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Matching Bit-Mask + + POLL_MASK + + A bit-mask used to determine the probability of random replies. + +14.7.2. Poll Response + + POLR are sent by PGM children (receivers or network elements) to + reply to a POLL. + + The network-header source address of a POLR is the unicast NLA of the + entity that originates the POLR. The network-header destination + address of a POLR is initialized by the originator of the POLL to the + unicast NLA of the upstream PGM element (source or network element) + known from the POLL that triggered the POLR. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Source Port | Destination Port | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Options | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Global Source ID ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ... Global Source ID | TSDU Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | POLR's Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | POLR's Round | reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Option Extensions when present ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Source Port: + + POLR_SPORT + + Data-Destination Port + + Destination Port: + + POLR_DPORT + + Data-Source Port, together with Global Source ID forms POLR_TSI + + + + + +Speakman, et. al. Experimental [Page 91] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Type: + + POLR_TYPE = 0x02 + + Global Source ID: + + POLR_GSI + + Together with POLR_DPORT forms POLR_TSI + + POLR's Sequence Number + + POLR_SQN + + The sequence number (POLL_SQN) of the POLL packet for which this + is a reply. + + POLR's Round + + POLR_ROUND + + The round number (POLL_ROUND) of the POLL packet for which this is + a reply. + +15. Appendix E - Implosion Prevention + +15.1. Introduction + + These procedures are intended to prevent NAK implosion and to limit + its extent in case of the loss of all or part of the suppressing + multicast distribution tree. They also provide a means to adaptively + tune the NAK back-off interval, NAK_BO_IVL. + + The PGM virtual topology is established and refreshed by SPMs. + Between one SPM and the next, PGM nodes may have an out-of-date view + of the PGM topology due to multicast routing changes, flapping, or a + link/router failure. If any of the above happens relative to a PGM + parent node, a potential NAK implosion problem arises because the + parent node is unable to suppress the generation of duplicate NAKs as + it cannot reach its children using NCFs. The procedures described + below introduce an alternative way of performing suppression in this + case. They also attempt to prevent implosion by adaptively tuning + NAK_BO_IVL. + + + + + + + + +Speakman, et. al. Experimental [Page 92] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +15.2. Tuning NAK_BO_IVL + + Sources and network elements continuously monitor the number of + duplicated NAKs received and use this observation to tune the NAK + back-off interval (NAK_BO_IVL) for the first PGM-hop receivers + connected to them. Receivers learn the current value of NAK_BO_IVL + through OPT_NAK_BO_IVL appended to NCFs or SPMs. + +15.2.1. Procedures - Sources and Network Elements + + For each TSI, sources and network elements advertise the value of + NAK_BO_IVL that their first PGM-hop receivers should use. They + advertise a separate value on all the outgoing interfaces for the TSI + and keep track of the last values advertised. + + For each interface and TSI, sources and network elements count the + number of NAKs received for a specific repair state (i.e., per + sequence number per TSI) from the time the interface was first added + to the repair state list until the time the repair state is + discarded. Then they use this number to tune the current value of + NAK_BO_IVL as follows: + + Increase the current value NAK_BO_IVL when the first duplicate NAK + is received for a given SQN on a particular interface. + + Decrease the value of NAK_BO_IVL if no duplicate NAKs are received on + a particular interface for the last NAK_PROBE_NUM measurements where + each measurement corresponds to the creation of a new repair state. + + An upper and lower limit are defined for the possible value of + NAK_BO_IVL at any time. These are NAK_BO_IVL_MAX and NAK_BO_IVL_MIN + respectively. The initial value that should be used as a starting + point to tune NAK_BO_IVL is NAK_BO_IVL_DEFAULT. The policies + RECOMMENDED for increasing and decreasing NAK_BO_IVL are multiplying + by two and dividing by two respectively. + + Sources and network elements advertise the current value of + NAK_BO_IVL through the OPT_NAK_BO_IVL that they append to NCFs. They + MAY also append OPT_NAK_BO_IVL to outgoing SPMs. + + In order to avoid forwarding the NAK_BO_IVL advertised by the parent, + network elements must be able to recognize OPT_NAK_BO_IVL. Network + elements that receive SPMs containing OPT_NAK_BO_IVL MUST either + remove the option or over-write its content (NAK_BO_IVL) with the + current value of NAK_BO_IVL for the outgoing interface(s), before + forwarding the SPMs. + + + + + +Speakman, et. al. Experimental [Page 93] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Sources MAY advertise the value of NAK_BO_IVL_MAX and NAK_BO_IVL_MIN + to the session by appending a OPT_NAK_BO_RNG to SPMs. + +15.2.2. Procedures - Receivers + + Receivers learn the value of NAK_BO_IVL to use through the option + OPT_NAK_BO_IVL, when this is present in NCFs or SPMs. A value for + NAK_BO_IVL learned from OPT_NAK_BO_IVL MUST NOT be used by a receiver + unless either NAK_BO_IVL_SQN is zero, or the receiver has seen + POLL_RND == 0 for POLL_SQN =< NAK_BO_IVL_SQN within half the sequence + number space. The initial value of NAK_BO_IVL is set to + NAK_BO_IVL_DEFAULT. + + Receivers that receive an SPM containing OPT_NAK_BO_RNG MUST use its + content to set the local values of NAK_BO_IVL_MAX and NAK_BO_IVL_MIN. + +15.2.3. Adjusting NAK_BO_IVL in the absence of NAKs + + Monitoring the number of duplicate NAKs provides a means to track + indirectly the change in the size of first PGM-hop receiver + population and adjust NAK_BO_IVL accordingly. Note that the number + of duplicate NAKs for a given SQN is related to the number of first + PGM-hop children that scheduled (or forwarded) a NAK and not to the + absolute number of first PGM-hop children. This mechanism, however, + does not work in the absence of packet loss, hence a large number of + duplicate NAKs is possible after a period without NAKs, if many new + receivers have joined the session in the meanwhile. To address this + issue, PGM Sources and network elements SHOULD periodically poll the + number of first PGM-hop children using the "general poll" procedures + described in Appendix D. If the result of the polls shows that the + population size has increased significantly during a period without + NAKs, they SHOULD increase NAK_BO_IVL as a safety measure. + +15.3. Containing Implosion in the Presence of Network Failures + +15.3.1. Detecting Network Failures + + In some cases PGM (parent) network elements can promptly detect the + loss of all or part of the suppressing multicast distribution tree + (due to network failures or route changes) by checking their + multicast connectivity, when they receive NAKs. In some other cases + this is not possible as the connectivity problem might occur at some + other non-PGM node downstream or might take time to reflect in the + multicast routing table. To address these latter cases, PGM uses a + simple heuristic: a failure is assumed for a TSI when the count of + duplicated NAKs received for a repair state reaches the value + DUP_NAK_MAX in one of the interfaces. + + + + +Speakman, et. al. Experimental [Page 94] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +15.3.2. Containing Implosion + + When a PGM source or network element detects or assumes a failure for + which it looses multicast connectivity to down-stream PGM agents + (either receivers or other network elements), it sends unicast NCFs + to them in response to NAKs. Downstream PGM network elements which + receive unicast NCFs and have multicast connectivity to the multicast + session send special SPMs to prevent further NAKs until a regular SPM + sent by the source refreshes the PGM tree. + + Procedures - Sources and Network Elements + + PGM sources or network elements which detect or assume a failure that + prevents them from reaching down-stream PGM agents through multicast + NCFs revert to confirming NAKs through unicast NCFs for a given TSI + on a given interface. If the PGM agent is the source itself, than it + MUST generate an SPM for the TSI, in addition to sending the unicast + NCF. + + Network elements MUST keep using unicast NCFs until they receive a + regular SPM from the source. + + When a unicast NCF is sent for the reasons described above, it MUST + contain the OPT_NBR_UNREACH option and the OPT_PATH_NLA option. + OPT_NBR_UNREACH indicates that the sender is unable to use multicast + to reach downstream PGM agents. OPT_PATH_NLA carries the network + layer address of the NCF sender, namely the NLA of the interface + leading to the unreachable subtree. + + When a PGM network element receives an NCF containing the + OPT_NBR_UNREACH option, it MUST ignore it if OPT_PATH_NLA specifies + an upstream neighbour different from the one currently known to be + the upstream neighbor for the TSI. Assuming the network element + matches the OPT_PATH_NLA of the upstream neighbour address, it MUST + stop forwarding NAKs for the TSI until it receives a regular SPM for + the TSI. In addition, it MUST also generate a special SPM to prevent + downstream receivers from sending more NAKs. This special SPM MUST + contain the OPT_NBR_UNREACH option and SHOULD have a SPM_SQN equal to + SPM_SQN of the last regular SPM forwarded. The OPT_NBR_UNREACH + option invalidates the windowing information in SPMs (SPM_TRAIL and + SPM_LEAD). The PGM network element that adds the OPT_NBR_UNREACH + option SHOULD invalidate the windowing information by setting + SPM_TRAIL to 0 and SPM_LEAD to 0x80000000. + + PGM network elements which receive an SPM containing the + OPT_NBR_UNREACH option and whose SPM_PATH matches the currently known + PGM parent, MUST forward them in the normal way and MUST stop + + + + +Speakman, et. al. Experimental [Page 95] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + forwarding NAKs for the TSI until they receive a regular SPM for the + TSI. If the SPM_PATH does not match the currently known PGM parent, + the SPM containing the OPT_NBR_UNREACH option MUST be ignored. + + Procedures - Receivers + + PGM receivers which receive either an NCF or an SPM containing the + OPT_NBR_UNREACH option MUST stop sending NAKs until a regular SPM is + received for the TSI. + + On reception of a unicast NCF containing the OPT_NBR_UNREACH option + receivers MUST generate a multicast copy of the packet with TTL set + to one on the RPF interface for the data source. This will prevent + other receivers in the same subnet from generating NAKs. + + Receivers MUST ignore windowing information in SPMs which contain the + OPT_NBR_UNREACH option. + + Receivers MUST ignore NCFs containing the OPT_NBR_UNREACH option if + the OPT_PATH_NLA specifies a neighbour different than the one + currently know to be the PGM parent neighbour. Similarly receivers + MUST ignore SPMs containing the OPT_NBR_UNREACH option if SPM_PATH + does not match the current PGM parent. + +15.4. Packet Formats + +15.4.1. OPT_NAK_BO_IVL - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NAK Back-Off Interval | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NAK Back-Off Interval SQN | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x04 + + NAK Back-Off Interval + + The value of NAK-generation Back-Off Interval in microseconds. + + + + + + + + +Speakman, et. al. Experimental [Page 96] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + NAK Back-Off Interval Sequence Number + + The POLL_SQN to which this value of NAK_BO_IVL corresponds. Zero + is reserved and means NAK_BO_IVL is NOT being determined through + polling (see Appendix D) and may be used immediately. Otherwise, + NAK_BO_IVL MUST NOT be used unless the receiver has also seen + POLL_ROUND = 0 for POLL_SQN =< NAK_BO_IVL_SQN within half the + sequence number space. + + OPT_NAK_BO_IVL MAY be appended to NCFs, SPMs, or POLLs. + + OPT_NAK_BO_IVL is network-significant. + +15.4.2. OPT_NAK_BO_RNG - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Maximum NAK Back-Off Interval | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Minimum NAK Back-Off Interval | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x05 + + Maximum NAK Back-Off Interval + + The maximum value of NAK-generation Back-Off Interval in + microseconds. + + Minimum NAK Back-Off Interval + + The minimum value of NAK-generation Back-Off Interval in + microseconds. + + OPT_NAK_BO_RNG MAY be appended to SPMs. + + OPT_NAK_BO_RNG is network-significant. + +15.4.3. OPT_NBR_UNREACH - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + +Speakman, et. al. Experimental [Page 97] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Option Type = 0x0B + + When present in SPMs, it invalidates the windowing information. + + OPT_NBR_UNREACH MAY be appended to SPMs and NCFs. + + OPT_NBR_UNREACH is network-significant. + +15.4.4. OPT_PATH_NLA - Packet Extension Format + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E| Option Type | Option Length |Reserved |F|OPX|U| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Path NLA | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Option Type = 0x0C + + Path NLA + + The NLA of the interface on the originating PGM network element + that it uses to send multicast SPMs to the recipient of the packet + containing this option. + + OPT_PATH_NLA MAY be appended to NCFs. + + OPT_PATH_NLA is network-significant. + +16. Appendix F - Transmit Window Example + + Nota Bene: The concept of and all references to the increment + window (TXW_INC) and the window increment (TXW_ADV_SECS) + throughout this document are for illustrative purposes only. They + provide the shorthand with which to describe the concept of + advancing the transmit window without also implying any particular + implementation or policy of advancement. + + The size of the transmit window in seconds is simply TXW_SECS. The + size of the transmit window in bytes (TXW_BYTES) is (TXW_MAX_RTE * + TXW_SECS). The size of the transmit window in sequence numbers + (TXW_SQNS) is (TXW_BYTES / bytes-per-packet). + + The fraction of the transmit window size (in seconds of data) by + which the transmit window is advanced (TXW_ADV_SECS) is called the + window increment. The trailing (oldest) such fraction of the + transmit window itself is called the increment window. + + + +Speakman, et. al. Experimental [Page 98] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + In terms of sequence numbers, the increment window is the range of + sequence numbers that will be the first to be expired from the + transmit window. The trailing (or left) edge of the increment window + is just TXW_TRAIL, the trailing (or left) edge of the transmit + window. The leading (or right) edge of the increment window + (TXW_INC) is defined as one less than the sequence number of the + first data packet transmitted by the source TXW_ADV_SECS after + transmitting TXW_TRAIL. + + A data packet is described as being "in" the transmit or increment + window, respectively, if its sequence number is in the range defined + by the transmit or increment window, respectively. + + The transmit window is advanced across the increment window by the + source when it increments TXW_TRAIL to TXW_INC. When the transmit + window is advanced across the increment window, the increment window + is emptied (i.e., TXW_TRAIL is momentarily equal to TXW_INC), begins + to refill immediately as transmission proceeds, is full again + TXW_ADV_SECS later (i.e., TXW_TRAIL is separated from TXW_INC by + TXW_ADV_SECS of data), at which point the transmit window is advanced + again, and so on. + +16.1. Advancing across the Increment Window + + In anticipation of advancing the transmit window, the source starts a + timer TXW_ADV_IVL_TMR which runs for time period TXW_ADV_IVL. + TXW_ADV_IVL has a value in the range (0, TXW_ADV_SECS). The value + MAY be configurable or MAY be determined statically by the strategy + used for advancing the transmit window. + + When TXW_ADV_IVL_TMR is running, a source MAY reset TXW_ADV_IVL_TMR + if NAKs are received for packets in the increment window. In + addition, a source MAY transmit RDATA in the increment window with + priority over other data within the transmit window. + + When TXW_ADV_IVL_TMR expires, a source SHOULD advance the trailing + edge of the transmit window from TXW_TRAIL to TXW_INC. + + Once the transmit window is advanced across the increment window, + SPM_TRAIL, OD_TRAIL and RD_TRAIL are set to the new value of + TXW_TRAIL in all subsequent transmitted packets, until the next + window advancement. + + PGM does not constrain the strategies that a source may use for + advancing the transmit window. The source MAY implement any scheme + or number of schemes. Three suggested strategies are outlined here. + + + + + +Speakman, et. al. Experimental [Page 99] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Consider the following example: + + Assuming a constant transmit rate of 128kbps and a constant data + packet size of 1500 bytes, if a source maintains the past 30 + seconds of data for repair and increments its transmit window in 5 + second increments, then + + TXW_MAX_RTE = 16kBps + TXW_ADV_SECS = 5 seconds, + TXW_SECS = 35 seconds, + TXW_BYTES = 560kB, + TXW_SQNS = 383 (rounded up), + + and the size of the increment window in sequence numbers + (TXW_MAX_RTE * TXW_ADV_SECS / 1500) = 54 (rounded down). + + Continuing this example, the following is a diagram of the transmit + window and the increment window therein in terms of sequence numbers. + + + TXW_TRAIL TXW_LEAD + | | + | | + |--|--------------- Transmit Window -------------|----| + v | | v + v v + n-1 | n | n+1 | ... | n+53 | n+54 | ... | n+381 | n+382 | n+383 + ^ + ^ | ^ + |--- Increment Window|---| + | + | + TXW_INC + + So the values of the sequence numbers defining these windows are: + + TXW_TRAIL = n + TXW_INC = n+53 + TXW_LEAD = n+382 + + Nota Bene: In this example the window sizes in terms of sequence + numbers can be determined only because of the assumption of a + constant data packet size of 1500 bytes. When the data packet + sizes are variable, more or fewer sequence numbers MAY be consumed + transmitting the same amount (TXW_BYTES) of data. + + So, for a given transport session identified by a TSI, a source + maintains: + + + +Speakman, et. al. Experimental [Page 100] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + TXW_MAX_RTE a maximum transmit rate in kBytes per second, the + cumulative transmit rate of some combination of SPMs, + ODATA, and RDATA depending on the transmit window + advancement strategy + + TXW_TRAIL the sequence number defining the trailing edge of the + transmit window, the sequence number of the oldest + data packet available for repair + + TXW_LEAD the sequence number defining the leading edge of the + transmit window, the sequence number of the most + recently transmitted ODATA packet + + TXW_INC the sequence number defining the leading edge of the + increment window, the sequence number of the most + recently transmitted data packet amongst those that + will expire upon the next increment of the transmit + window + + PGM does not constrain the strategies that a source may use for + advancing the transmit window. A source MAY implement any scheme or + number of schemes. This is possible because a PGM receiver must obey + the window provided by the source in its packets. Three strategies + are suggested within this document. + + In the first, called "Advance with Time", the transmit window + maintains the last TXW_SECS of data in real-time, regardless of + whether any data was sent in that real time period or not. The + actual number of bytes maintained at any instant in time will vary + between 0 and TXW_BYTES, depending on traffic during the last + TXW_SECS. In this case, TXW_MAX_RTE is the cumulative transmit rate + of SPMs and ODATA. + + In the second, called "Advance with Data", the transmit window + maintains the last TXW_BYTES bytes of data for repair. That is, it + maintains the theoretical maximum amount of data that could be + transmitted in the time period TXW_SECS, regardless of when they were + transmitted. In this case, TXW_MAX_RTE is the cumulative transmit + rate of SPMs, ODATA, and RDATA. + + The third strategy leaves control of the window in the hands of the + application. The API provided by a source implementation for this, + could allow the application to control the window in terms of APDUs + and to manually step the window. This gives a form of Application + Level Framing (ALF). In this case, TXW_MAX_RTE is the cumulative + transmit rate of SPMs, ODATA, and RDATA. + + + + + +Speakman, et. al. Experimental [Page 101] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +16.2. Advancing with Data + + In the first strategy, TXW_MAX_RTE is calculated from SPMs and both + ODATA and RDATA, and NAKs reset TXW_ADV_IVL_TMR. In this mode of + operation the transmit window maintains the last TXW_BYTES bytes of + data for repair. That is, it maintains the theoretical maximum + amount of data that could be transmitted in the time period TXW_SECS. + This means that the following timers are not treated as real-time + timers, instead they are "data driven". That is, they expire when + the amount of data that could be sent in the time period they define + is sent. They are the SPM ambient time interval, TXW_ADV_SECS, + TXW_SECS, TXW_ADV_IVL, TXW_ADV_IVL_TMR and the join interval. Note + that the SPM heartbeat timers still run in real-time. + + While TXW_ADV_IVL_TMR is running, a source uses the receipt of a NAK + for ODATA within the increment window to reset timer TXW_ADV_IVL_TMR + to TXW_ADV_IVL so that transmit window advancement is delayed until + no NAKs for data in the increment window are seen for TXW_ADV_IVL + seconds. If the transmit window should fill in the meantime, further + transmissions would be suspended until the transmit window can be + advanced. + + A source MUST advance the transmit window across the increment window + only upon expiry of TXW_ADV_IVL_TMR. + + This mode of operation is intended for non-real-time, messaging + applications based on the receipt of complete data at the expense of + delay. + +16.3. Advancing with Time + + This strategy advances the transmit window in real-time. In this + mode of operation, TXW_MAX_RTE is calculated from SPMs and ODATA only + to maintain a constant data throughput rate by consuming extra + bandwidth for repairs. TXW_ADV_IVL has the value 0 which advances + the transmit window without regard for whether NAKs for data in the + increment window are still being received. + + In this mode of operation, all timers are treated as real-time + timers. + + This mode of operation is intended for real-time, streaming + applications based on the receipt of timely data at the expense of + completeness. + + + + + + + +Speakman, et. al. Experimental [Page 102] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +16.4. Advancing under explicit application control + + Some applications may wish more explicit control of the transmit + window than that provided by the advance with data / time strategies + above. An implementation MAY provide this mode of operation and + allow an application to explicitly control the window in terms of + APDUs. + +17. Appendix G - Applicability Statement + + As stated in the introduction, PGM has been designed with a specific + class of applications in mind in recognition of the fact that a + general solution for reliable multicast has proven elusive. The + applicability of PGM is narrowed further, and perhaps more + significantly, by the prototypical nature of at least four of the + transport elements the protocol incorporates. These are congestion + control, router assist, local retransmission, and a programmatic API + for reliable multicast protocols of this class. At the same time as + standardization efforts address each of these elements individually, + this publication is intended to foster experimentation with these + elements in general, and to inform that standardization process with + results from practise. + + This section briefly describes some of the experimental aspects of + PGM and makes non-normative references to some examples of current + practise based upon them. + + At least 3 different approaches to congestion control can be explored + with PGM: a receiver-feedback based approach, a router-assist based + approach, and layer-coding based approach. The first is supported by + the negative acknowledgement mechanism in PGM augmented by an + application-layer acknowledgement mechanism. The second is supported + by the router exception processing mechanism in PGM. The third is + supported by the FEC mechanisms in PGM. An example of a receiver- + feedback based approach is provided in [16], and a proposal for a + router-assist based approach was proposed in [17]. Open issues for + the researchers include how do each of these approaches behave in the + presence of multiple competing sessions of the same discipline or of + different disciplines, TCP most notably; how do each of them behave + over a particular range of topologies, and over a particular range of + loads; and how do each of them scale as a function of the size of the + receiver population. + + Router assist has applications not just to implosion control and + retransmit constraint as described in this specification, but also to + congestion control as described above, and more generally to any + feature which may be enhanced by access to per-network-element state + and processing. The full range of these features is as yet + + + +Speakman, et. al. Experimental [Page 103] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + unexplored, but a general mechanism for providing router assist in a + transport-protocol independent way (GRA) is a topic of active + research [18]. That effort has been primarily informed by the router + assist component of PGM, and implementation and deployment experience + with PGM will continue to be fed back into the specification and + eventual standardization of GRA. Open questions facing the + researchers ([19], [20], [21]) include how router-based state scales + relative to the feature benefit obtained, how system-wide factors + (such as throughput and retransmit latency) vary relative to the + scale and topology of deployed router assistance, and how incremental + deployment considerations affect the tractability of router-assist + based features. Router assist may have additional implications in + the area of congestion control to the extent that it may be applied + in multi-group layered coding schemes to increase the granularity and + reduce the latency of receiver based congestion control. + + GRA itself explicitly incorporates elements of active networking, and + to the extent that the router assist component of PGM is reflected in + GRA, experimentation with the narrowly defined network-element + functionality of PGM will provide some of the first real world + experience with this promising if controversial technology. + + Local retransmission is not a new idea in general in reliable + multicast, but the specific approach taken in PGM of locating re- + transmitters on the distribution tree for the session, diverting + repair requests from network elements to the re-transmitters, and + then propagating repairs downward from the repair point on the + distribution tree raises interesting questions concerning where to + locate re-transmitters in a given topology, and how network elements + locate those re-transmitters and evaluate their efficiency relative + to other available sources of retransmissions, most notably the + source itself. This particular aspect of PGM, while fully specified, + has only been implemented on the network element side, and awaits a + host-side implementation before questions like these can be + addressed. + + PGM presents the opportunity to develop a programming API for + reliable multicast applications that reflects both those + applications' service requirements as well as the services provided + by PGM in support of those applications that may usefully be made + visible above the transport interface. At least a couple of host- + side implementations of PGM and a concomitant API have been developed + for research purposes ([22], [23]), and are available as open source + explicitly for the kind of experimentation described in this section. + + Perhaps the broadest experiment that PGM can enable in a community of + researchers using a reasonable scale experimental transport protocol + is simply in the definition, implementation, and deployment of IP + + + +Speakman, et. al. Experimental [Page 104] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + multicast applications for which the reliability provided by PGM is a + significant enabler. Experience with such applications will not just + illuminate the value of reliable multicast, but will also provoke + practical examination of and responses to the attendant policy issues + (such as peering, billing, access control, firewalls, NATs, etc.), + and, if successful, will ultimately encourage more wide spread + deployment of IP multicast itself. + +18. Abbreviations + + ACK Acknowledgment + AFI Address Family Indicator + ALF Application Level Framing + APDU Application Protocol Data Unit + ARQ Automatic Repeat reQuest + DLR Designated Local Repairer + GSI Globally Unique Source Identifier + FEC Forward Error Correction + MD5 Message-Digest Algorithm + MTU Maximum Transmission Unit + NAK Negative Acknowledgment + NCF NAK Confirmation + NLA Network Layer Address + NNAK Null Negative Acknowledgment + ODATA Original Data + POLL Poll Request + POLR Poll Response + RDATA Repair Data + RSN Receive State Notification + SPM Source Path Message + SPMR SPM Request + TG Transmission Group + TGSIZE Transmission Group Size + TPDU Transport Protocol Data Unit + TSDU Transport Service Data Unit + TSI Transport Session Identifier + TSN Transmit State Notification + + + + + + + + + + + + + + +Speakman, et. al. Experimental [Page 105] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +19. Acknowledgements + + The design and specification of PGM has been substantially influenced + by reviews and revisions provided by several people who took the time + to read and critique this document. These include, in alphabetical + order: + + Bob Albrightson + Joel Bion + Mark Bowles + Steve Deering + Tugrul Firatli + Dan Harkins + Dima Khoury + Gerard Newman + Dave Oran + Denny Page + Ken Pillay + Chetan Rai + Yakov Rekhter + Dave Rossetti + Paul Stirpe + Brian Whetten + Kyle York + +20. References + + [1] B. Whetten, T. Montgomery, S. Kaplan, "A High Performance + Totally Ordered Multicast Protocol", in "Theory and Practice in + Distributed Systems", Springer Verlag LCNS938, 1994. + + [2] S. Floyd, V. Jacobson, C. Liu, S. McCanne, L. Zhang, "A + Reliable Multicast Framework for Light-weight Sessions and + Application Level Framing", ACM Transactions on Networking, + November 1996. + + [3] J. C. Lin, S. Paul, "RMTP: A Reliable Multicast Transport + Protocol", ACM SIGCOMM August 1996. + + [4] Miller, K., Robertson, K., Tweedly, A. and M. White, "Multicast + File Transfer Protocol (MFTP) Specification", Work In Progress. + + [5] Deering, S., "Host Extensions for IP Multicasting", STD 5, RFC + 1112, August 1989. + + [6] Katz, D., "IP Router Alert Option", RFC 2113, February 1997. + + [7] C. Partridge, "Gigabit Networking", Addison Wesley 1994. + + + +Speakman, et. al. Experimental [Page 106] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + [8] H. W. Holbrook, S. K. Singhal, D. R. Cheriton, "Log-Based + Receiver-Reliable Multicast for Distributed Interactive + Simulation", ACM SIGCOMM 1995. + + [9] Rivest, R., "The MD5 Message-Digest Algorithm", RFC 1321, April + 1992. + + [10] Reynolds, J. and J. Postel, "Assigned Numbers", STD 2, RFC + 1700, October 1994. + + [11] J. Nonnenmacher, E. Biersack, D. Towsley, "Parity-Based Loss + Recovery for Reliable Multicast Transmission", ACM SIGCOMM + September 1997. + + [12] L. Rizzo, "Effective Erasure Codes for Reliable Computer + Communication Protocols", Computer Communication Review, April + 1997. + + [13] V. Jacobson, "Congestion Avoidance and Control", ACM SIGCOMM + August 1988. + + [14] Bradner, S., "Key words for use in RFCs to Indicate Requirement + Levels", BCP, 14, RFC 2119, March 1997. + + [15] J. Bolot, T. Turletti, I. Wakeman, "Scalable Feedback Control + for Multicast Video Distribution in the Internet", Proc. + ACM/Sigcomm 94, pp. 58-67. + + [16] L. Rizzo, "pgmcc: A TCP-friendly Single-Rate Multicast + Congestion Control Scheme", Proc. of ACM SIGCOMM August 2000. + + [17] M. Luby, L. Vicisano, T. Speakman. "Heterogeneous multicast + congestion control based on router packet filtering", RMT + working group, June 1999, Pisa, Italy. + + [18] Cain, B., Speakman, T. and D. Towsley, "Generic Router Assist + (GRA) Building Block, Motivation and Architecture", Work In + Progress. + + [19] C. Papadopoulos, and E. Laliotis,"Incremental Deployment of a + Router-assisted Reliable Multicast Scheme,", Proc. of Networked + Group Communications (NGC2000), Stanford University, Palo Alto, + CA. November 2000. + + + + + + + + +Speakman, et. al. Experimental [Page 107] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + [20] C. Papadopoulos, "RAIMS: an Architecture for Router-Assisted + Internet Multicast Services." Presented at ETH, Zurich, + Switzerland, October 23 2000. + + [21] J. Chesterfield, A. Diana, A. Greenhalgh, M. Lad, and M. Lim, + "A BSD Router Implementation of PGM", + http://www.cs.ucl.ac.uk/external/m.lad/rpgm/ + + [22] L. Rizzo, "A PGM Host Implementation for FreeBSD", + http://www.iet.unipi.it/~luigi/pgm.html + + [23] M. Psaltaki, R. Araujo, G. Aldabbagh, P. Kouniakis, and A. + Giannopoulos, "Pragmatic General Multicast (PGM) host + implementation for FreeBSD.", + http://www.cs.ucl.ac.uk/research/darpa/pgm/PGM_FINAL.html + +21. Authors' Addresses + + Tony Speakman + EMail: speakman@cisco.com + + Dino Farinacci + Procket Networks + 3850 North First Street + San Jose, CA 95134 + USA + EMail: dino@procket.com + + Steven Lin + Juniper Networks + 1194 N. Mathilda Ave. + Sunnyvale, CA 94086 + USA + EMail: steven@juniper.net + + Alex Tweedly + EMail: agt@cisco.com + + Nidhi Bhaskar + EMail: nbhaskar@cisco.com + + Richard Edmonstone + EMail: redmonst@cisco.com + + Rajitha Sumanasekera + EMail: rajitha@cisco.com + + + + + +Speakman, et. al. Experimental [Page 108] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Lorenzo Vicisano + Cisco Systems, Inc. + 170 West Tasman Drive, + San Jose, CA 95134 + USA + EMail: lorenzo@cisco.com + + Jon Crowcroft + Department of Computer Science + University College London + Gower Street + London WC1E 6BT + UK + EMail: j.crowcroft@cs.ucl.ac.uk + + Jim Gemmell + Microsoft Bay Area Research Center + 301 Howard Street, #830 + San Francisco, CA 94105 + USA + EMail: jgemmell@microsoft.com + + Dan Leshchiner + Tibco Software + 3165 Porter Dr. + Palo Alto, CA 94304 + USA + EMail: dleshc@tibco.com + + Michael Luby + Digital Fountain, Inc. + 39141 Civic Center Drive + Fremont CA 94538 + USA + EMail: luby@digitalfountain.com + + Todd L. Montgomery + Talarian Corporation + 124 Sherman Ave. + Morgantown, WV 26501 + USA + EMail: todd@talarian.com + + + + + + + + + +Speakman, et. al. Experimental [Page 109] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + + Luigi Rizzo + Dip. di Ing. dell'Informazione + Universita` di Pisa + via Diotisalvi 2 + 56126 Pisa + Italy + EMail: luigi@iet.unipi.it + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Speakman, et. al. Experimental [Page 110] + +RFC 3208 PGM Reliable Transport Protocol December 2001 + + +22. Full Copyright Statement + + Copyright (C) The Internet Society (2001). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Speakman, et. al. Experimental [Page 111] + diff --git a/3rdparty/openpgm-svn-r1135/pgm/COPYING b/3rdparty/openpgm-svn-r1135/pgm/COPYING new file mode 100644 index 0000000..5ab7695 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/3rdparty/openpgm-svn-r1135/pgm/INSTALL b/3rdparty/openpgm-svn-r1135/pgm/INSTALL new file mode 100644 index 0000000..3ba60e5 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/INSTALL @@ -0,0 +1,4 @@ +For building instructions, see: + + http://code.google.com/p/openpgm/wiki/OpenPgm3CReferenceBuildLibrary + diff --git a/3rdparty/openpgm-svn-r1135/pgm/LICENSE b/3rdparty/openpgm-svn-r1135/pgm/LICENSE new file mode 100644 index 0000000..fabbeef --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/LICENSE @@ -0,0 +1,19 @@ +Most of OpenPGM is licensed under the terms of the GNU Lesser Public License, +the LGPL, with a special exception: + + As a special exception, the copyright holders of this library give + you permission to link this library with independent modules to + produce an executable, regardless of the license terms of these + independent modules, and to copy and distribute the resulting + executable under terms of your choice, provided that you also meet, + for each linked independent module, the terms and conditions of the + license of that module. An independent module is a module which is + not derived from or based on this library. If you modify this + library, you must extend this exception to your version of the + library. + +See the file COPYING for details of the LGPL. + +Hence you should treat the libraries libpgm, libpgmsnmp, and libpgmhttp of +OpenPGM as being LGPL licensed, with the special exception. + diff --git a/3rdparty/openpgm-svn-r1135/pgm/README b/3rdparty/openpgm-svn-r1135/pgm/README new file mode 100644 index 0000000..ece7aa1 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/README @@ -0,0 +1,7 @@ +OpenPGM is a library implementing the PGM reliable multicast network protocol. + +For more information about OpenPGM, see: + + http://openpgm.googlecode.com/ + + diff --git a/3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgm b/3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgm new file mode 100644 index 0000000..2aaa3ce --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgm @@ -0,0 +1,171 @@ +# -*- mode: python -*- +# OpenPGM build script +# $Id$ + +import os; +Import('env') + +src = Split(""" + thread.c + mem.c + string.c + list.c + slist.c + queue.c + hashtable.c + messages.c + error.c + math.c + packet_parse.c + packet_test.c + sockaddr.c + time.c + if.c + getifaddrs.c + getnodeaddr.c + indextoaddr.c + indextoname.c + nametoindex.c + inet_network.c + md5.c + rand.c + gsi.c + tsi.c + txw.c + rxw.c + skbuff.c + socket.c + source.c + receiver.c + recv.c + engine.c + timer.c + net.c + rate_control.c + checksum.c + reed_solomon.c + galois_tables.c + wsastrerror.c + histogram.c +""") + +e = env.Clone(); +e.Append(CCFLAGS = '-DGETTEXT_PACKAGE=\'"pgm"\''); + +# Galois tables +e.Command ('galois_tables.c', 'galois_generator.pl', "perl $SOURCE > $TARGET"); + +# Version stamping +e.Command ('version.c', 'version_generator.py', "python $SOURCE > $TARGET"); +e.Depends ('version.c', src); +src += ['version.c']; + +e.StaticLibrary('libpgm', src); +e.StaticSharedLibrary('libpgm-pic', src); + +#----------------------------------------------------------------------------- +# unit testing + +if env['WITH_CHECK'] == 'true': + te = e.Clone(); + te.MergeFlags(env['GLIB_FLAGS']); + newCCFLAGS = []; + for flag in te['CCFLAGS']: + if ("-W" != flag[:2]) and ("-pedantic" != flag[:9]): + newCCFLAGS.append(flag); + te['CCFLAGS'] = newCCFLAGS; + te.ParseConfig ('pkg-config --cflags --libs check'); +# log dependencies + tlog = [ e.Object('messages.c'), + e.Object('thread.c'), + e.Object('galois_tables.c'), + e.Object('mem.c'), + e.Object('histogram.c'), + e.Object('string.c'), + e.Object('slist.c') + ]; +# framework + te.Program (['atomic_unittest.c']); + te.Program (['checksum_unittest.c'] + tlog); + te.Program (['error_unittest.c'] + tlog); + te.Program (['md5_unittest.c'] + tlog); + te.Program (['getifaddrs_unittest.c', + e.Object('error.c'), + e.Object('sockaddr.c'), + e.Object('list.c')] + tlog); + te.Program (['getnodeaddr_unittest.c', + e.Object('error.c'), + e.Object('sockaddr.c')] + tlog); + te.Program (['indextoaddr_unittest.c', + e.Object('error.c'), + e.Object('sockaddr.c')] + tlog); + te.Program (['inet_network_unittest.c', + e.Object('sockaddr.c')] + tlog); + te.Program (['rate_control_unittest.c'] + tlog); + te.Program (['reed_solomon_unittest.c'] + tlog); + te.Program (['time_unittest.c', + e.Object('error.c')] + tlog); +# collate + tframework = [ e.Object('checksum.c'), + e.Object('error.c'), + e.Object('galois_tables.c'), + e.Object('getifaddrs.c'), + e.Object('getnodeaddr.c'), + e.Object('hashtable.c'), + e.Object('histogram.c'), + e.Object('indextoaddr.c'), + e.Object('indextoname.c'), + e.Object('inet_network.c'), + e.Object('list.c'), + e.Object('math.c'), + e.Object('md5.c'), + e.Object('mem.c'), + e.Object('messages.c'), + e.Object('nametoindex.c'), + e.Object('queue.c'), + e.Object('rand.c'), + e.Object('rate_control.c'), + e.Object('reed_solomon.c'), + e.Object('slist.c'), + e.Object('sockaddr.c'), + e.Object('string.c'), + e.Object('thread.c'), + e.Object('time.c'), + e.Object('wsastrerror.c') + ]; +# library + te.Program (['txw_unittest.c', + e.Object('tsi.c'), + e.Object('skbuff.c')] + tframework); + te.Program (['rxw_unittest.c', + e.Object('tsi.c'), + e.Object('skbuff.c')] + tframework); + te.Program (['engine_unittest.c', + e.Object('version.c')] + tframework); + te.Program (['gsi_unittest.c', + e.Object('if.c')] + tframework); + te.Program (['tsi_unittest.c'] + tframework); + te.Program (['if_unittest.c'] + tframework); + te.Program (['socket_unittest.c', + e.Object('if.c'), + e.Object('tsi.c')] + tframework); + te.Program (['source_unittest.c', + e.Object('skbuff.c')] + tframework); + te.Program (['receiver_unittest.c', + e.Object('tsi.c')] + tframework); + te.Program (['recv_unittest.c', + e.Object('tsi.c'), + e.Object('gsi.c'), + e.Object('skbuff.c')] + tframework); + te.Program (['net_unittest.c'] + tframework); + te.Program (['timer_unittest.c'] + tframework); + te.Program (['packet_parse_unittest.c'] + tframework); + te.Program (['packet_test_unittest.c'] + tframework); + te.Program (['ip_unittest.c', + e.Object('if.c')] + tframework); +# performance tests + te.Program (['checksum_perftest.c', + e.Object('time.c'), + e.Object('error.c')] + tlog); + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgm89 b/3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgm89 new file mode 100644 index 0000000..5092157 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgm89 @@ -0,0 +1,83 @@ +# -*- mode: python -*- +# OpenPGM build script +# $Id$ + +import os; +import string; +Import('env') + +src = Split(""" + thread.c + mem.c + string.c + list.c + slist.c + queue.c + hashtable.c + messages.c + error.c + math.c + packet_parse.c + packet_test.c + sockaddr.c + time.c + if.c + getifaddrs.c + getnodeaddr.c + indextoaddr.c + indextoname.c + nametoindex.c + inet_network.c + md5.c + rand.c + gsi.c + tsi.c + txw.c + rxw.c + skbuff.c + socket.c + source.c + receiver.c + recv.c + engine.c + timer.c + net.c + rate_control.c + checksum.c + reed_solomon.c + galois_tables.c + wsastrerror.c + histogram.c +""") + +e = env.Clone(); +e.Append(CCFLAGS = '-DGETTEXT_PACKAGE=\'"pgm"\''); + +# Galois tables +e.Command ('galois_tables.c', 'galois_generator.pl', "perl $SOURCE > $TARGET"); + +# Version stamping +e.Command ('version.c', 'version_generator.py', "python $SOURCE > $TARGET"); +e.Depends ('version.c', src); +src += ['version.c']; + +# C89 degrading +c89source = Builder(action = 'cp $SOURCE $TARGET && if test -f "${SOURCE}.c89.patch"; then patch -i ${SOURCE}.c89.patch $TARGET; fi', + suffix = '.c89.c', + src_suffix = '.c', + single_source = 1); +e.Append(BUILDERS = {'C89Source' : c89source}) + +c89src = [] +for c99file in src: + c89file = c99file.replace('.c', '.c89.c'); + c89src += [ c89file ]; + patchFile = c99file + '.c89.patch'; + if os.path.exists (patchFile): + e.Depends (c89file, patchFile); + e.C89Source(c99file); + +e.StaticLibrary('libpgm89', c89src); +e.StaticSharedLibrary('libpgm89-pic', c89src); + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgmex b/3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgmex new file mode 100644 index 0000000..b508a04 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgmex @@ -0,0 +1,18 @@ +# -*- mode: python -*- +# OpenPGM build script +# $Id$ + +Import('env') + +e = env.Clone(); +e.MergeFlags(env['GLIB_FLAGS']); + +src = Split(""" + log.c + backtrace.c + signal.c +""") + +e.StaticLibrary('libpgmex', src); + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgmhttp b/3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgmhttp new file mode 100644 index 0000000..9824b3c --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgmhttp @@ -0,0 +1,53 @@ +# -*- mode: python -*- +# OpenPGM build script +# $Id$ + +Import('env') + +e = env.Clone() +e.Append(CCFLAGS = '-DGETTEXT_PACKAGE=\'"pgm-http"\''); + +# add binary tree to include path to find embedded htdocs +e.Append(CPPPATH = ['.']); + +src = Split(""" + http.c +""") + +htdocs = Split(""" + htdocs/404.html + htdocs/base.css + htdocs/robots.txt + htdocs/xhtml10_strict.doctype +""") + +pgmhttp = e.StaticLibrary('libpgmhttp', src); +pgmhttppic = e.StaticSharedLibrary('libpgmhttp-pic', src); + +# embed htdocs into C headers +embed_htdoc = Builder(action = './htdocs/convert_to_macro.pl $SOURCE > $TARGET') +e.Append(BUILDERS = {'EmbedHtdoc' : embed_htdoc}) + +for htdoc in htdocs: + embedded = htdoc + '.h' + e.EmbedHtdoc(embedded, htdoc) + +#----------------------------------------------------------------------------- +# unit testing + +if env['WITH_CHECK'] == 'true': + te = e.Clone(); +# add new suffix so we can re-use libpgm objects + te['SHOBJSUFFIX'] = '.http' + te['SHOBJSUFFIX']; + te['OBJSUFFIX'] = '.http' + te['OBJSUFFIX']; + + newCCFLAGS = []; + for flag in te['CCFLAGS']: + if ("-W" != flag[:2]) and ("-pedantic" != flag[:9]): + newCCFLAGS.append(flag); + te['CCFLAGS'] = newCCFLAGS; + te.ParseConfig ('pkg-config --cflags --libs check'); + te.Program (['http_unittest.c', te.Object('sockaddr.c'), te.Object('getifaddrs.c')]); + + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgmsnmp b/3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgmsnmp new file mode 100644 index 0000000..8e35aab --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/SConscript.libpgmsnmp @@ -0,0 +1,35 @@ +# -*- mode: python -*- +# OpenPGM build script +# $Id$ + +Import('env') + +e = env.Clone() +e.Append(CCFLAGS = '-DGETTEXT_PACKAGE=\'"pgm-snmp"\''); + +e.MergeFlags(e['SNMP_FLAGS']); + +src = Split(""" + snmp.c + pgmMIB.c +""") + +e.StaticLibrary('libpgmsnmp', src); +e.StaticSharedLibrary('libpgmsnmp-pic', src); + +#----------------------------------------------------------------------------- +# unit testing + +if env['WITH_CHECK'] == 'true': + te = e.Clone(); + newCCFLAGS = []; + for flag in te['CCFLAGS']: + if ("-W" != flag[:2]) and ("-pedantic" != flag[:9]): + newCCFLAGS.append(flag); + te['CCFLAGS'] = newCCFLAGS; + te.ParseConfig ('pkg-config --cflags --libs check'); + te.Program ('snmp_unittest.c'); + te.Program (['pgmMIB_unittest.c', e.Object('snmp.c')]); + + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/SConstruct b/3rdparty/openpgm-svn-r1135/pgm/SConstruct new file mode 100644 index 0000000..1187ef9 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/SConstruct @@ -0,0 +1,345 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 1, 0 ) +SConsignFile('scons.signatures' + '-' + platform.system() + '-' + platform.machine()); + +vars = Variables() +vars.AddVariables ( + EnumVariable ('BUILD', 'build environment', 'debug', + allowed_values=('release', 'debug', 'profile')), + EnumVariable ('BRANCH', 'branch prediction', 'none', + allowed_values=('none', 'profile', 'seed')), + EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', + allowed_values=('true', 'false')), + EnumVariable ('COVERAGE', 'test coverage', 'none', + allowed_values=('none', 'full')), + EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CHECK', 'Check test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_TEST', 'Network test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CC', 'C++ examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', + allowed_values=('true', 'false')), +) + +#----------------------------------------------------------------------------- +# Dependencies + +env = Environment(); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment( + variables = vars, + ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', + '-Wunsafe-loop-optimizations', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', + '-pedantic', +# C99 + '-std=gnu99', + '-D_XOPEN_SOURCE=600', + '-D_BSD_SOURCE', +# re-entrant libc + '-D_REENTRANT', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R', + '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header + '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system + '-DCONFIG_HAVE_PROC', +# example: crash handling + '-DCONFIG_HAVE_BACKTRACE', +# timing + '-DCONFIG_HAVE_PSELECT', + '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', + '-DCONFIG_HAVE_HPET', +# event handling + '-DCONFIG_HAVE_POLL', + '-DCONFIG_HAVE_EPOLL', +# interface enumeration + '-DCONFIG_HAVE_GETIFADDRS', + '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg +# '-DCONFIG_HAVE_WSACMSGHDR', +# multicast + '-DCONFIG_HAVE_MCAST_JOIN', + '-DCONFIG_HAVE_IP_MREQN', +# sprintf + '-DCONFIG_HAVE_SPRINTF_GROUPING', + '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE', +# GNU getopt + '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ '-pipe' + ], + LIBS = [ +# histogram math + 'm', +# clock_gettime() + 'rt' + ] +) + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Coverage analysis +if env['COVERAGE'] == 'full': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(CCFLAGS = '-ftest-coverage') + env.Append(LINKFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-lgcov') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = '-O2') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = '-gdb') + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = '-pg') + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +if env['WITH_SNMP'] == 'true': + env['SNMP_FLAGS'] = env.ParseFlags('!net-snmp-config --agent-libs'); + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); +# backup = context.env.Clone().Dictionary(); + lastASFLAGS = context.env.get('ASFLAGS', ''); + lastCCFLAGS = context.env.get('CCFLAGS', ''); + lastCFLAGS = context.env.get('CFLAGS', ''); + lastCPPDEFINES = context.env.get('CPPDEFINES', ''); + lastCPPFLAGS = context.env.get('CPPFLAGS', ''); + lastCPPPATH = context.env.get('CPPPATH', ''); + lastLIBPATH = context.env.get('LIBPATH', ''); + lastLIBS = context.env.get('LIBS', ''); + lastLINKFLAGS = context.env.get('LINKFLAGS', ''); + lastRPATH = context.env.get('RPATH', ''); + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); +# context.env.Replace(**backup); + context.env.Replace(ASFLAGS = lastASFLAGS, + CCFLAGS = lastCCFLAGS, + CFLAGS = lastCFLAGS, + CPPDEFINES = lastCPPDEFINES, + CPPFLAGS = lastCPPFLAGS, + CPPPATH = lastCPPPATH, + LIBPATH = lastLIBPATH, + LIBS = lastLIBS, + LINKFLAGS = lastLINKFLAGS, + RPATH = lastRPATH); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +def CheckEventFD(context): + context.Message('Checking eventfd...'); + result = context.TryLink(""" +#include +int main(int argc, char**argv) +{ + eventfd(0,0); + return 0; +} +""", '.c') + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck, + 'CheckEventFD': CheckEventFD +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +if conf.CheckEventFD(): + print 'Enabling kernel eventfd notification mechanism.'; + conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/SConstruct.097 b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.097 new file mode 100644 index 0000000..12e058a --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.097 @@ -0,0 +1,332 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 0, 97 ) +SConsignFile('scons.signatures'+ '-' + platform.system() + '-' + platform.machine()); + +opt = Options(None, ARGUMENTS) +opt.AddOptions ( + (EnumOption ('BUILD', 'build environment', 'debug', ('release', 'debug', 'profile'))), + (EnumOption ('BRANCH', 'branch prediction', 'none', ('none', 'profile', 'seed'))), + (EnumOption ('WITH_GETTEXT', 'l10n support via libintl', 'false', ('true', 'false'))), + (EnumOption ('WITH_GLIB', 'Build GLib dependent modules', 'false', ('true', 'false'))), + (EnumOption ('COVERAGE', 'test coverage', 'none', ('none', 'full'))), + (EnumOption ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', ('true', 'false'))), + (EnumOption ('WITH_HTTP', 'HTTP administration', 'false', ('true', 'false'))), + (EnumOption ('WITH_SNMP', 'SNMP administration', 'false', ('true', 'false'))), + (EnumOption ('WITH_CHECK', 'Check test system', 'false', ('true', 'false'))), + (EnumOption ('WITH_TEST', 'Network test system', 'false', ('true', 'false'))), + (EnumOption ('WITH_CC', 'C++ Examples', 'true', ('true', 'false'))), + (EnumOption ('WITH_EXAMPLES', 'Examples', 'true', ('true', 'false'))), + (EnumOption ('WITH_NCURSES', 'NCURSES examples', 'false', ('true', 'false'))), + (EnumOption ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', ('true', 'false'))), +) + +#----------------------------------------------------------------------------- +# Dependencies + +env = Environment(); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment(ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', + '-Wunsafe-loop-optimizations', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', + '-pedantic', +# C99 + '-std=gnu99', + '-D_XOPEN_SOURCE=600', + '-D_BSD_SOURCE', +# re-entrant libc + '-D_REENTRANT', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R', + '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header + '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system + '-DCONFIG_HAVE_PROC', +# example: crash handling + '-DCONFIG_HAVE_BACKTRACE', +# timing + '-DCONFIG_HAVE_PSELECT', + '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', +# event handling + '-DCONFIG_HAVE_POLL', + '-DCONFIG_HAVE_EPOLL', +# interface enumeration + '-DCONFIG_HAVE_GETIFADDRS', + '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg +# '-DCONFIG_HAVE_WSACMSGHDR', +# multicast + '-DCONFIG_HAVE_MCAST_JOIN', + '-DCONFIG_HAVE_IP_MREQN', +# sprintf + '-DCONFIG_HAVE_SPRINTF_GROUPING', + '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE', +# GNU getopt + '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ '-pipe' + ], + LIBS = [ +# histogram math + 'm', +# clock_gettime() + 'rt' + ], + PROTOBUF_CCFLAGS = '-I/miru/projects/protobuf/protobuf-2.1.0/src', + PROTOBUF_LIBS = '/miru/projects/protobuf/protobuf-2.1.0/src/.libs/libprotobuf.a', + PROTOBUF_PROTOC = '/miru/projects/protobuf/protobuf-2.1.0/src/protoc' +) +opt.Update (env) + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Coverage analysis +if env['COVERAGE'] == 'full': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(CCFLAGS = '-ftest-coverage') + env.Append(LINKFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-lgcov') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = '-O2') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = '-gdb') + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O0','-pg'], LINKFLAGS = '-pg') + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +if env['WITH_SNMP'] == 'true': + env['SNMP_FLAGS'] = env.ParseFlags('!net-snmp-config --cflags --agent-libs'); + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); +# backup = context.env.Clone().Dictionary(); + lastASFLAGS = context.env.get('ASFLAGS', ''); + lastCCFLAGS = context.env.get('CCFLAGS', ''); + lastCFLAGS = context.env.get('CFLAGS', ''); + lastCPPDEFINES = context.env.get('CPPDEFINES', ''); + lastCPPFLAGS = context.env.get('CPPFLAGS', ''); + lastCPPPATH = context.env.get('CPPPATH', ''); + lastLIBPATH = context.env.get('LIBPATH', ''); + lastLIBS = context.env.get('LIBS', ''); + lastLINKFLAGS = context.env.get('LINKFLAGS', ''); + lastRPATH = context.env.get('RPATH', ''); + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); +# context.env.Replace(**backup); + context.env.Replace(ASFLAGS = lastASFLAGS, + CCFLAGS = lastCCFLAGS, + CFLAGS = lastCFLAGS, + CPPDEFINES = lastCPPDEFINES, + CPPFLAGS = lastCPPFLAGS, + CPPPATH = lastCPPPATH, + LIBPATH = lastLIBPATH, + LIBS = lastLIBS, + LINKFLAGS = lastLINKFLAGS, + RPATH = lastRPATH); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +def CheckEventFD(context): + context.Message('Checking eventfd...'); + result = context.TryLink(""" +#include +int main(int argc, char**argv) +{ + eventfd(0,0); + return 0; +} +""", '.c') + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck, + 'CheckEventFD': CheckEventFD +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +if conf.CheckEventFD(): + print 'Enabling kernel eventfd notification mechanism.'; + conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/SConstruct.097.intelc b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.097.intelc new file mode 100644 index 0000000..337432a --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.097.intelc @@ -0,0 +1,387 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 0, 97 ) +SConsignFile('scons.signatures'+ '-' + platform.system() + '-' + platform.machine() + '-intelc'); + +opt = Options(None, ARGUMENTS) +opt.AddOptions ( + (EnumOption ('BUILD', 'build environment', 'debug', ('release', 'debug', 'profile'))), + (EnumOption ('BRANCH', 'branch prediction', 'none', ('none', 'profile', 'seed'))), + (EnumOption ('WITH_GETTEXT', 'l10n support via libintl', 'false', ('true', 'false'))), + (EnumOption ('WITH_GLIB', 'Build GLib dependent modules', 'false', ('true', 'false'))), + (EnumOption ('COVERAGE', 'test coverage', 'none', ('none', 'full'))), + (EnumOption ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', ('true', 'false'))), + (EnumOption ('WITH_HTTP', 'HTTP administration', 'false', ('true', 'false'))), + (EnumOption ('WITH_SNMP', 'SNMP administration', 'false', ('true', 'false'))), + (EnumOption ('WITH_CHECK', 'Check test system', 'false', ('true', 'false'))), + (EnumOption ('WITH_TEST', 'Network test system', 'false', ('true', 'false'))), + (EnumOption ('WITH_CC', 'C++ Examples', 'true', ('true', 'false'))), + (EnumOption ('WITH_EXAMPLES', 'Examples', 'true', ('true', 'false'))), + (EnumOption ('WITH_NCURSES', 'NCURSES examples', 'false', ('true', 'false'))), + (EnumOption ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', ('true', 'false'))), +) + +#----------------------------------------------------------------------------- +# Dependencies + +def force_intelc(env): + env.Tool('intelc', version='11.1', topdir='/opt/intel/Compiler/11.1/064/bin/intel64'); + +env = Environment(); +force_intelc(env); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment(ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', +# '-Wextra', +# '-Wfloat-equal', + '-Wshadow', +# '-Wunsafe-loop-optimizations', + '-Wpointer-arith', +# '-Wbad-function-cast', +# '-Wcast-qual', +# '-Wcast-align', + '-Wwrite-strings', +# '-Waggregate-return', + '-Wstrict-prototypes', +# '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', +# '-Wmissing-noreturn', +# '-Wmissing-format-attribute', +# '-Wredundant-decls', +# '-Wnested-externs', +# Verbose inlining reports +# '-Winline', + +# 175: subscript out of range +# - Ignored for broken compiler C99 support +# e.g. tbuf[ sizeof(tbuf) ] = '\0'; + '-wd175', + +# 177: function was declared but never referenced +# - Ignored as side effect of portability pre-processor blocks. + '-wd177', + +# 181: argument is incompatible with corresponding format string conversion +# - Ignored as pre-processor fails to parse variadic printf style macros. + '-wd181', + +# 191: type qualifier is meaningless on cast type +# - Ignored as cast required for portability +# e.g. pgm_txw_state_t*const state = (pgm_txw_state_t*const)&skb->cb; + '-wd191', + +# 269: invalid format string conversion +# - Ignored with failure to display PRIu32 with (volatile uint32_t) + '-wd269', + +# 556: cannot assign to an entity of type "void *" +# - Ignored, it's a C++2003 error, valid in C99 aside of const down-converting. +# e.g. const char *s = ""; void *p = s; + '-wd556', + +# 589: transfer of control bypasses initialization +# - Ignored, it's a C++2003 error, perfectly valid in C99. + '-wd589', + +# 593: variable was set but never used +# '-wd593', + +# 981: operands are evaluated in unspecified order +# - Ignored as pedantic warning against possible side effects. +# e.g. printf ("%s.%s > ", +# pgm_gethostbyaddr((const struct in_addr*)&ip->ip_src), +# pgm_udpport_string(pgm_header->pgm_sport)); + '-wd981', + +# 2259: non-pointer conversion from "/type a/" to "/type b/" may lose significant bits +# - Ignored for really pedantic compiler temporary value conversions, +# e.g. uint16_t j = 1, i = ~j; + '-wd2259', + +# '-pedantic', +# C99 + '-std=c99', + '-D_XOPEN_SOURCE=600', + '-D_BSD_SOURCE', + '-gcc-version=420', +# re-entrant libc + '-D_REENTRANT', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R', + '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header + '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system + '-DCONFIG_HAVE_PROC', +# example: crash handling + '-DCONFIG_HAVE_BACKTRACE', +# timing + '-DCONFIG_HAVE_PSELECT', +# ioctl RTC_IRQP_SET: undefined reference to `__invalid_size_argument_for_IOC' +# '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', +# event handling + '-DCONFIG_HAVE_POLL', + '-DCONFIG_HAVE_EPOLL', +# interface enumeration + '-DCONFIG_HAVE_GETIFADDRS', + '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg +# '-DCONFIG_HAVE_WSACMSGHDR', +# multicast + '-DCONFIG_HAVE_MCAST_JOIN', + '-DCONFIG_HAVE_IP_MREQN', +# sprintf + '-DCONFIG_HAVE_SPRINTF_GROUPING', + '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE', +# GNU getopt + '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ '-pipe' + ], + LIBS = [ +# histogram math + 'm', +# clock_gettime() + 'rt' + ], + PROTOBUF_CCFLAGS = '-I/miru/projects/protobuf/protobuf-2.1.0/src', + PROTOBUF_LIBS = '/miru/projects/protobuf/protobuf-2.1.0/src/.libs/libprotobuf.a', + PROTOBUF_PROTOC = '/miru/projects/protobuf/protobuf-2.1.0/src/protoc' +) +opt.Update (env) +force_intelc(env); + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Coverage analysis +if env['COVERAGE'] == 'full': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(CCFLAGS = '-ftest-coverage') + env.Append(LINKFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-lgcov') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = '-O2') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = '-ggdb') + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O0','-pg'], LINKFLAGS = '-pg') + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +if env['WITH_SNMP'] == 'true': + env['SNMP_FLAGS'] = env.ParseFlags('!net-snmp-config --cflags --agent-libs'); + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); +# backup = context.env.Clone().Dictionary(); + lastASFLAGS = context.env.get('ASFLAGS', ''); + lastCCFLAGS = context.env.get('CCFLAGS', ''); + lastCFLAGS = context.env.get('CFLAGS', ''); + lastCPPDEFINES = context.env.get('CPPDEFINES', ''); + lastCPPFLAGS = context.env.get('CPPFLAGS', ''); + lastCPPPATH = context.env.get('CPPPATH', ''); + lastLIBPATH = context.env.get('LIBPATH', ''); + lastLIBS = context.env.get('LIBS', ''); + lastLINKFLAGS = context.env.get('LINKFLAGS', ''); + lastRPATH = context.env.get('RPATH', ''); + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); +# context.env.Replace(**backup); + context.env.Replace(ASFLAGS = lastASFLAGS, + CCFLAGS = lastCCFLAGS, + CFLAGS = lastCFLAGS, + CPPDEFINES = lastCPPDEFINES, + CPPFLAGS = lastCPPFLAGS, + CPPPATH = lastCPPPATH, + LIBPATH = lastLIBPATH, + LIBS = lastLIBS, + LINKFLAGS = lastLINKFLAGS, + RPATH = lastRPATH); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +def CheckEventFD(context): + context.Message('Checking eventfd...'); + result = context.TryLink(""" +#include +int main(int argc, char**argv) +{ + eventfd(0,0); + return 0; +} +""", '.c') + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck, + 'CheckEventFD': CheckEventFD +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +if conf.CheckEventFD(): + print 'Enabling kernel eventfd notification mechanism.'; + conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '-intelc/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/SConstruct.097.mingw64 b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.097.mingw64 new file mode 100644 index 0000000..a3ffec9 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.097.mingw64 @@ -0,0 +1,338 @@ +# -*- coding: utf-8 mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 0, 97 ) +SConsignFile('scons.signatures'+ '-Win64-' + platform.machine()); + +opt = Options(None, ARGUMENTS) +opt.AddOptions ( + (EnumOption ('BUILD', 'build environment', 'debug', ('release', 'debug', 'profile'))), + (EnumOption ('BRANCH', 'branch prediction', 'none', ('none', 'profile', 'seed'))), + (EnumOption ('WITH_GETTEXT', 'l10n support via libintl', 'false', ('true', 'false'))), + (EnumOption ('WITH_GLIB', 'Build GLib dependent modules', 'false', ('true', 'false'))), + (EnumOption ('COVERAGE', 'test coverage', 'none', ('none', 'full'))), + (EnumOption ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', ('true', 'false'))), + (EnumOption ('WITH_HTTP', 'HTTP administration', 'false', ('true', 'false'))), + (EnumOption ('WITH_SNMP', 'SNMP administration', 'false', ('true', 'false'))), + (EnumOption ('WITH_CHECK', 'Check test system', 'false', ('true', 'false'))), + (EnumOption ('WITH_TEST', 'Network test system', 'false', ('true', 'false'))), +# C++ broken scope + (EnumOption ('WITH_CC', 'C++ examples', 'false', ('true', 'false'))), + (EnumOption ('WITH_EXAMPLES', 'Examples', 'true', ('true', 'false'))), + (EnumOption ('WITH_NCURSES', 'NCURSES examples', 'false', ('true', 'false'))), + (EnumOption ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', ('true', 'false'))), +) + +#----------------------------------------------------------------------------- +# Dependencies + +def force_mingw(env): + env.PrependENVPath('PATH', '/opt/mingw/bin'); + env.Tool('crossmingw64', toolpath=['.']); + +env = Environment(); +force_mingw(env); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('PKG_CONFIG_PATH=win64/lib/pkgconfig pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('PKG_CONFIG_PATH=win64/lib/pkgconfig pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment(ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', + '-Wunsafe-loop-optimizations', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', + '-pedantic', + '-std=gnu99', +# ¡C89, not C99! +# '-std=gnu89', + '-D_BSD_SOURCE', + '-D_WIN32_WINNT=0x0501', +# re-entrant libc + '-D_REENTRANT', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header +# '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system +# '-DCONFIG_HAVE_PROC', +# example: crash handling +# '-DCONFIG_HAVE_BACKTRACE', +# timing +# '-DCONFIG_HAVE_PSELECT', +# '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', +# event handling +# '-DCONFIG_HAVE_POLL', +# '-DCONFIG_HAVE_EPOLL', +# interface enumeration +# '-DCONFIG_HAVE_GETIFADDRS', +# '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg + '-DCONFIG_HAVE_WSACMSGHDR', +# multicast +# '-DCONFIG_HAVE_MCAST_JOIN', +# '-DCONFIG_HAVE_IP_MREQN', +# sprintf +# '-DCONFIG_HAVE_SPRINTF_GROUPING', +# '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE' +# GNU getopt +# '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ '-pipe' + ], + LIBS = [ + 'iphlpapi.lib', + 'ws2_32.lib' + ] +) +opt.Update (env) +force_mingw(env); + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Coverage analysis +if env['COVERAGE'] == 'full': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(CCFLAGS = '-ftest-coverage') + env.Append(LINKFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-lgcov') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = '-O2') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = '-gdb') + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O0','-pg'], LINKFLAGS = '-pg') + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!PKG_CONFIG_PATH=win64/lib/pkgconfig pkg-config --cflags --libs glib-2.0 gthread-2.0'); + env.MergeFlags('-Iwin/include -Lwin64/lib'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +env['SNMP_FLAGS'] = { + 'CCFLAGS' : [], + 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ], +}; + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); +# backup = context.env.Clone().Dictionary(); + lastASFLAGS = context.env.get('ASFLAGS', ''); + lastCCFLAGS = context.env.get('CCFLAGS', ''); + lastCFLAGS = context.env.get('CFLAGS', ''); + lastCPPDEFINES = context.env.get('CPPDEFINES', ''); + lastCPPFLAGS = context.env.get('CPPFLAGS', ''); + lastCPPPATH = context.env.get('CPPPATH', ''); + lastLIBPATH = context.env.get('LIBPATH', ''); + lastLIBS = context.env.get('LIBS', ''); + lastLINKFLAGS = context.env.get('LINKFLAGS', ''); + lastRPATH = context.env.get('RPATH', ''); + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); +# context.env.Replace(**backup); + context.env.Replace(ASFLAGS = lastASFLAGS, + CCFLAGS = lastCCFLAGS, + CFLAGS = lastCFLAGS, + CPPDEFINES = lastCPPDEFINES, + CPPFLAGS = lastCPPFLAGS, + CPPPATH = lastCPPPATH, + LIBPATH = lastLIBPATH, + LIBS = lastLIBS, + LINKFLAGS = lastLINKFLAGS, + RPATH = lastRPATH); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('PKG_CONFIG_PATH=win64/lib/pkgconfig pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +def CheckEventFD(context): + context.Message('Checking eventfd...'); + result = context.TryLink(""" +#include +int main(int argc, char**argv) +{ + eventfd(0,0); + return 0; +} +""", '.c') + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck, + 'CheckEventFD': CheckEventFD +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +if conf.CheckEventFD(): + print 'Enabling kernel eventfd notification mechanism.'; + conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-Win64-' + platform.machine() + '/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm89'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript89'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/SConstruct.097.sunstudio b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.097.sunstudio new file mode 100644 index 0000000..86323d5 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.097.sunstudio @@ -0,0 +1,332 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 0, 97 ) +SConsignFile('scons.signatures'+ '-' + platform.system() + '-' + platform.machine() + '-sunstudio'); + +opt = Options(None, ARGUMENTS) +opt.AddOptions ( + (EnumOption ('BUILD', 'build environment', 'debug', ('release', 'debug', 'profile'))), + (EnumOption ('BRANCH', 'branch prediction', 'none', ('none', 'profile', 'seed'))), + (EnumOption ('WITH_GETTEXT', 'l10n support via libintl', 'false', ('true', 'false'))), + (EnumOption ('WITH_GLIB', 'Build GLib dependent modules', 'false', ('true', 'false'))), + (EnumOption ('COVERAGE', 'test coverage', 'none', ('none', 'full'))), + (EnumOption ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', ('true', 'false'))), + (EnumOption ('WITH_HTTP', 'HTTP administration', 'false', ('true', 'false'))), + (EnumOption ('WITH_SNMP', 'SNMP administration', 'false', ('true', 'false'))), + (EnumOption ('WITH_CHECK', 'Check test system', 'false', ('true', 'false'))), + (EnumOption ('WITH_TEST', 'Network test system', 'false', ('true', 'false'))), + (EnumOption ('WITH_CC', 'C++ Examples', 'true', ('true', 'false'))), + (EnumOption ('WITH_EXAMPLES', 'Examples', 'true', ('true', 'false'))), + (EnumOption ('WITH_NCURSES', 'NCURSES examples', 'false', ('true', 'false'))), + (EnumOption ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', ('true', 'false'))), +) + +#----------------------------------------------------------------------------- +# Dependencies + +def force_sunstudio(env): + env.PrependENVPath('PATH', '/opt/sun/sunstudio12.1/bin'); + env.Tool('sunc++'); + env.Tool('suncc'); + env.Tool('sunlink'); + env.Tool('sunar'); + +env = Environment(); +force_sunstudio(env); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment(ENV = os.environ, + CCFLAGS = [ '-v', +# C99 + '-xc99=all', + '-D_XOPEN_SOURCE=600', + '-D__EXTENSIONS__', + '-DBSD_COMP', + '-D_BSD_SOURCE', +# re-entrant libc + '-D_REENTRANT', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R', + '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header + '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system + '-DCONFIG_HAVE_PROC', +# example: crash handling + '-DCONFIG_HAVE_BACKTRACE', +# timing + '-DCONFIG_HAVE_PSELECT', + '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', +# event handling + '-DCONFIG_HAVE_POLL', + '-DCONFIG_HAVE_EPOLL', +# interface enumeration + '-DCONFIG_HAVE_GETIFADDRS', + '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg +# '-DCONFIG_HAVE_WSACMSGHDR', +# multicast + '-DCONFIG_HAVE_MCAST_JOIN', + '-DCONFIG_HAVE_IP_MREQN', +# sprintf + '-DCONFIG_HAVE_SPRINTF_GROUPING', + '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE', +# GNU getopt + '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ + ], + LIBS = [ +# histogram math + 'm', +# clock_gettime() + 'rt' + ], + PROTOBUF_CCFLAGS = '-I/miru/projects/protobuf/protobuf-2.1.0/src', + PROTOBUF_LIBS = '/miru/projects/protobuf/protobuf-2.1.0/src/.libs/libprotobuf.a', + PROTOBUF_PROTOC = '/miru/projects/protobuf/protobuf-2.1.0/src/protoc' +) +opt.Update (env) +force_sunstudio(env); + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Coverage analysis +if env['COVERAGE'] == 'full': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(CCFLAGS = '-ftest-coverage') + env.Append(LINKFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-lgcov') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = '-O2') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG','-g'], LINKFLAGS = '-g') + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O0','-pg'], LINKFLAGS = '-pg') + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +def list_remove(list, target): + newlist = []; + for item in str(list).split(' '): + if item != target: + newlist.append(item); + return newlist; + +# managed environment for libpgmsnmp, libpgmhttp +if env['WITH_SNMP'] == 'true': + env['SNMP_FLAGS'] = env.ParseFlags('!net-snmp-config --cflags --agent-libs'); +# strip out GCC only flags + ccflags = env['SNMP_FLAGS'].get('CCFLAGS', ''); + env['SNMP_FLAGS']['CCFLAGS'] = list_remove(ccflags, '-Wall'); + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); +# backup = context.env.Clone().Dictionary(); + lastASFLAGS = context.env.get('ASFLAGS', ''); + lastCCFLAGS = context.env.get('CCFLAGS', ''); + lastCFLAGS = context.env.get('CFLAGS', ''); + lastCPPDEFINES = context.env.get('CPPDEFINES', ''); + lastCPPFLAGS = context.env.get('CPPFLAGS', ''); + lastCPPPATH = context.env.get('CPPPATH', ''); + lastLIBPATH = context.env.get('LIBPATH', ''); + lastLIBS = context.env.get('LIBS', ''); + lastLINKFLAGS = context.env.get('LINKFLAGS', ''); + lastRPATH = context.env.get('RPATH', ''); + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); +# context.env.Replace(**backup); + context.env.Replace(ASFLAGS = lastASFLAGS, + CCFLAGS = lastCCFLAGS, + CFLAGS = lastCFLAGS, + CPPDEFINES = lastCPPDEFINES, + CPPFLAGS = lastCPPFLAGS, + CPPPATH = lastCPPPATH, + LIBPATH = lastLIBPATH, + LIBS = lastLIBS, + LINKFLAGS = lastLINKFLAGS, + RPATH = lastRPATH); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +def CheckEventFD(context): + context.Message('Checking eventfd...'); + result = context.TryLink(""" +#include +int main(int argc, char**argv) +{ + eventfd(0,0); + return 0; +} +""", '.c') + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck, + 'CheckEventFD': CheckEventFD +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +if conf.CheckEventFD(): + print 'Enabling kernel eventfd notification mechanism.'; + conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '-sunstudio/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/SConstruct.Debian4 b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.Debian4 new file mode 100644 index 0000000..952013e --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.Debian4 @@ -0,0 +1,281 @@ +# -*- mode: python -*- +# OpenPGM build script + +import os +import time +import sys + +EnsureSConsVersion( 0, 97 ) +SConsignFile('scons.signatures'); + +opt = Options(None, ARGUMENTS) +opt.AddOptions ( + (EnumOption ('BUILD', 'build environment', 'debug', ('release', 'debug', 'profile'))), + (EnumOption ('BRANCH', 'branch prediction', 'none', ('none', 'profile', 'seed'))), + (EnumOption ('COVERAGE', 'test coverage', 'none', ('none', 'full'))), + (EnumOption ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', ('true', 'false'))), + (EnumOption ('WITH_HTTP', 'HTTP administration', 'true', ('true', 'false'))), + (EnumOption ('WITH_SNMP', 'SNMP administration', 'true', ('true', 'false'))), + (EnumOption ('WITH_CHECK', 'Check test system', 'false', ('true', 'false'))), + (EnumOption ('WITH_TEST', 'Network test system', 'false', ('true', 'false'))), + (EnumOption ('WITH_EXAMPLES', 'Examples', 'true', ('true', 'false'))), + (EnumOption ('WITH_NCURSES', 'NCURSES examples', 'false', ('true', 'false'))), + (EnumOption ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', ('true', 'false'))), + (EnumOption ('WITH_PLUS', 'libpgmplus GPL library', 'false', ('true', 'false'))), +) + +#----------------------------------------------------------------------------- +# Dependencies + +env = Environment(); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' + Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' + Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' + Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment(ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', + '-Wunsafe-loop-optimizations', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', + '-pedantic', + '-std=gnu99', + '--param', 'max-inline-insns-single=600', + '-D_REENTRANT', + '-D_GNU_SOURCE', + '-D__need_IOV_MAX', + '-DCONFIG_16BIT_CHECKSUM', + '-DCONFIG_HAVE_PROC', + '-DCONFIG_HAVE_BACKTRACE', + '-DCONFIG_HAVE_PSELECT', + '-DCONFIG_HAVE_POLL', + '-DCONFIG_HAVE_EPOLL', + '-DCONFIG_HAVE_CLOCK_GETTIME', + '-DCONFIG_HAVE_CLOCK_NANOSLEEP', + '-DCONFIG_HAVE_NANOSLEEP', + '-DCONFIG_HAVE_USLEEP', + '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', + '-DCONFIG_HAVE_IFR_NETMASK', + '-DCONFIG_HAVE_GETIFADDRS', + '-DCONFIG_HAVE_GETHOSTBYNAME2', + '-DCONFIG_HAVE_GETPROTOBYNAME_R', + '-DCONFIG_HAVE_MCAST_JOIN', + '-DCONFIG_HAVE_IP_MREQN', + '-DCONFIG_HAVE_DSO_VISIBILITY', + '-DCONFIG_BIND_INADDR_ANY', + '-DCONFIG_GALOIS_MUL_LUT', + ], + LINKFLAGS = [ '-pipe', + '-lm' + ] +) +opt.Update (env) + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Coverage analysis +if env['COVERAGE'] == 'full': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(CCFLAGS = '-ftest-coverage') + env.Append(LINKFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-lgcov') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = '-O2') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = '-ggdb', LINKFLAGS = '-gdb') + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = '-pg') + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +env.ParseConfig('pkg-config --cflags --libs glib-2.0 gthread-2.0'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +env['SNMP_FLAGS'] = { + 'CCFLAGS' : [], + 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ], +}; + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); + lastLIBS = context.env['LIBS']; + lastCCFLAGS= context.env['CCFLAGS']; + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); + context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +def CheckEventFD(context): + context.Message('Checking eventfd...'); + result = context.TryLink(""" +#include +int main(int argc, char**argv) +{ + eventfd(0,0); + return 0; +} +""", '.c') + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck, + 'CheckEventFD': CheckEventFD +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Enabling extra Red Hat dependencies for Net-SNMP.'; + conf.env['SNMP_FLAGS']['LIBS'].append(['librpm', 'libsensors', 'libdl', 'libwrap']); + lastLIBS = conf.env['LIBS']; + conf.env.ParseConfig('perl -MExtUtils::Embed -e ldopts'); + conf.env['SNMP_FLAGS']['LIBS'].append(conf.env['LIBS']); + conf.env.Replace(LIBS = lastLIBS); + if not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +if conf.CheckEventFD(): + print 'Enabling kernel eventfd notification mechanism.'; + conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); + +env = conf.Finish(); + +# DSO visibility flags +if '-DCONFIG_HAVE_DSO_VISIBILITY' in env['CCFLAGS']: + env.Append(CCFLAGS = '-DPGM_GNUC_INTERNAL=G_GNUC_INTERNAL') +else: + env.Append(CCFLAGS = '-DPGM_GNUC_INTERNAL=') + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +SConscript(ref_node + 'SConscript.libpgm'); +SConscript(ref_node + 'SConscript.libpgmex'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/SConstruct.FreeBSD80 b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.FreeBSD80 new file mode 100644 index 0000000..62b49f7 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.FreeBSD80 @@ -0,0 +1,351 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 1, 0 ) +SConsignFile('scons.signatures' + '-' + platform.system() + '-' + platform.machine()); + +vars = Variables() +vars.AddVariables ( + EnumVariable ('BUILD', 'build environment', 'debug', + allowed_values=('release', 'debug', 'profile')), + EnumVariable ('BRANCH', 'branch prediction', 'none', + allowed_values=('none', 'profile', 'seed')), + EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', + allowed_values=('true', 'false')), + EnumVariable ('COVERAGE', 'test coverage', 'none', + allowed_values=('none', 'full')), + EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CHECK', 'Check test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_TEST', 'Network test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CC', 'C++ examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', + allowed_values=('true', 'false')), +) + +#----------------------------------------------------------------------------- +# Dependencies + +env = Environment(); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment( + variables = vars, + ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', + '-Wunsafe-loop-optimizations', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', + '-pedantic', +# C99 + '-std=gnu99', +# re-entrant libc + '-D_REENTRANT', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header +# '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system +# '-DCONFIG_HAVE_PROC', +# example: crash handling +# '-DCONFIG_HAVE_BACKTRACE', +# timing + '-DCONFIG_HAVE_PSELECT', +# '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', +# '-DCONFIG_HAVE_HPET', +# event handling + '-DCONFIG_HAVE_POLL', +# '-DCONFIG_HAVE_EPOLL', +# interface enumeration + '-DCONFIG_HAVE_GETIFADDRS', + '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg +# '-DCONFIG_HAVE_WSACMSGHDR', +# multicast +# '-DCONFIG_HAVE_MCAST_JOIN', + '-DCONFIG_HAVE_IP_MREQN', +# sprintf + '-DCONFIG_HAVE_SPRINTF_GROUPING', + '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD + '-DCONFIG_HOST_ORDER_IP_LEN', + '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE', +# GNU getopt + '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ '-pipe' + ], + LIBS = [ +# histogram math + 'm', +# clock_gettime() + 'rt', +# ftime() + 'compat', +# POSIX threads + 'pthread' + ] +) + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Coverage analysis +if env['COVERAGE'] == 'full': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(CCFLAGS = '-ftest-coverage') + env.Append(LINKFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-lgcov') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = '-O2') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = '-gdb') + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = '-pg') + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +if env['WITH_SNMP'] == 'true': + env['SNMP_FLAGS'] = env.ParseFlags('!net-snmp-config --cflags --agent-libs'); + +def restore_env(env, backup): + for var in backup.keys(): + env[var] = backup[var]; + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); +# backup = context.env.Clone().Dictionary(); + lastASFLAGS = context.env.get('ASFLAGS', ''); + lastCCFLAGS = context.env.get('CCFLAGS', ''); + lastCFLAGS = context.env.get('CFLAGS', ''); + lastCPPDEFINES = context.env.get('CPPDEFINES', ''); + lastCPPFLAGS = context.env.get('CPPFLAGS', ''); + lastCPPPATH = context.env.get('CPPPATH', ''); + lastLIBPATH = context.env.get('LIBPATH', ''); + lastLIBS = context.env.get('LIBS', ''); + lastLINKFLAGS = context.env.get('LINKFLAGS', ''); + lastRPATH = context.env.get('RPATH', ''); + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); +# context.env.Replace(**backup); + context.env.Replace(ASFLAGS = lastASFLAGS, + CCFLAGS = lastCCFLAGS, + CFLAGS = lastCFLAGS, + CPPDEFINES = lastCPPDEFINES, + CPPFLAGS = lastCPPFLAGS, + CPPPATH = lastCPPPATH, + LIBPATH = lastLIBPATH, + LIBS = lastLIBS, + LINKFLAGS = lastLINKFLAGS, + RPATH = lastRPATH); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +def CheckEventFD(context): + context.Message('Checking eventfd...'); + result = context.TryLink(""" +#include +int main(int argc, char**argv) +{ + eventfd(0,0); + return 0; +} +""", '.c') + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck, + 'CheckEventFD': CheckEventFD +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +if conf.CheckEventFD(): + print 'Enabling kernel eventfd notification mechanism.'; + conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/SConstruct.OpenSolaris b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.OpenSolaris new file mode 100644 index 0000000..c9833ed --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.OpenSolaris @@ -0,0 +1,310 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 1, 0 ) +SConsignFile('scons.signatures'+ '-OpenSolaris-' + platform.machine() + '-gcc'); + +vars = Variables() +vars.AddVariables ( + EnumVariable ('BUILD', 'build environment', 'debug', + allowed_values=('release', 'debug', 'profile')), + EnumVariable ('BRANCH', 'branch prediction', 'none', + allowed_values=('none', 'profile', 'seed')), + EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CHECK', 'Check test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_TEST', 'Network test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CC', 'C++ examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', + allowed_values=('true', 'false')), +) + +#----------------------------------------------------------------------------- +# Dependencies + +env = Environment(); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment( + variables = vars, + ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', +# '-Wunsafe-loop-optimizations', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', + '-pedantic', +# C99 + '-std=gnu99', + '-D_XOPEN_SOURCE=600', + '-D__EXTENSIONS__', + '-DBSD_COMP', + '-D_BSD_SOURCE', +# re-entrant libc + '-D_REENTRANT', + '-DCONFIG_HAVE_GETPROTOBYNAME_R', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header + '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system +# '-DCONFIG_HAVE_PROC', +# example: crash handling +# '-DCONFIG_HAVE_BACKTRACE', +# timing + '-DCONFIG_HAVE_PSELECT', +# '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', +# '-DCONFIG_HAVE_HPET', +# event handling + '-DCONFIG_HAVE_POLL', +# '-DCONFIG_HAVE_EPOLL', +# interface enumeration +# '-DCONFIG_HAVE_GETIFADDRS', +# '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg +# '-DCONFIG_HAVE_WSACMSGHDR', +# multicast + '-DCONFIG_HAVE_MCAST_JOIN', +# '-DCONFIG_HAVE_IP_MREQN', +# sprintf + '-DCONFIG_HAVE_SPRINTF_GROUPING', +# '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE', +# GNU getopt + '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ '-pipe' + ], + LIBS = [ +# histogram math + 'm', +# clock_gettime() + 'rt', +# Solaris sockets + 'resolv', + 'socket', + 'nsl' + ] +) + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = ['-O2'], LINKFLAGS = []) + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG', '-ggdb'], LINKFLAGS = ['-gdb']) + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = ['-pg']) + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +env['SNMP_FLAGS'] = { + 'CCFLAGS' : [], + 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp', + 'ssl', 'kstat', 'wrap', 'adm' ], + 'CPPPATH' : ['/opt/cws/include'], + 'LIBPATH' : ['/opt/csw/lib'], +}; + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); + lastLIBS = context.env['LIBS']; + lastCCFLAGS= context.env['CCFLAGS']; + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); + context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-OpenSolaris-' + platform.machine() + '-gcc/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/SConstruct.RHEL4 b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.RHEL4 new file mode 100644 index 0000000..1637ea5 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.RHEL4 @@ -0,0 +1,279 @@ +# -*- mode: python -*- +# OpenPGM build script + +import os +import time +import sys + +EnsureSConsVersion( 0, 97 ) +SConsignFile('scons.signatures'); + +opt = Options(None, ARGUMENTS) +opt.AddOptions ( + (EnumOption ('BUILD', 'build environment', 'debug', ('release', 'debug', 'profile'))), + (EnumOption ('BRANCH', 'branch prediction', 'none', ('none', 'profile', 'seed'))), + (EnumOption ('COVERAGE', 'test coverage', 'none', ('none', 'full'))), + (EnumOption ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', ('true', 'false'))), + (EnumOption ('WITH_HTTP', 'HTTP administration', 'true', ('true', 'false'))), + (EnumOption ('WITH_SNMP', 'SNMP administration', 'true', ('true', 'false'))), + (EnumOption ('WITH_CHECK', 'Check test system', 'false', ('true', 'false'))), + (EnumOption ('WITH_TEST', 'Network test system', 'false', ('true', 'false'))), + (EnumOption ('WITH_EXAMPLES', 'Examples', 'true', ('true', 'false'))), + (EnumOption ('WITH_NCURSES', 'NCURSES examples', 'false', ('true', 'false'))), + (EnumOption ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', ('true', 'false'))), + (EnumOption ('WITH_PLUS', 'libpgmplus GPL library', 'false', ('true', 'false'))), +) + +#----------------------------------------------------------------------------- +# Dependencies + +env = Environment(); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('PKG_CONFIG_PATH=/usr/evolution28/lib/pkgconfig:/usr/lib/pkconfig pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('PKG_CONFIG_PATH=/usr/evolution28/lib/pkgconfig:/usr/lib/pkconfig pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' + Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' + Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' + Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment(ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', + '-pedantic', + '-std=gnu99', + '--param', 'max-inline-insns-single=600', + '-D_REENTRANT', + '-D_GNU_SOURCE', + '-D__need_IOV_MAX', + '-DCONFIG_16BIT_CHECKSUM', + '-DCONFIG_HAVE_PROC', + '-DCONFIG_HAVE_BACKTRACE', + '-DCONFIG_HAVE_PSELECT', + '-DCONFIG_HAVE_POLL', + '-DCONFIG_HAVE_EPOLL', + '-DCONFIG_HAVE_CLOCK_GETTIME', + '-DCONFIG_HAVE_CLOCK_NANOSLEEP', + '-DCONFIG_HAVE_NANOSLEEP', + '-DCONFIG_HAVE_USLEEP', + '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', + '-DCONFIG_HAVE_GETIFADDRS', + '-DCONFIG_HAVE_GETHOSTBYNAME2', + '-DCONFIG_HAVE_GETPROTOBYNAME_R', + '-DCONFIG_HAVE_MCAST_JOIN', + '-DCONFIG_HAVE_IP_MREQN', + '-DCONFIG_HAVE_DSO_VISIBILITY', + '-DCONFIG_BIND_INADDR_ANY', + '-DCONFIG_GALOIS_MUL_LUT', + ], + LINKFLAGS = [ '-pipe', + '-lm' + ] +) +opt.Update (env) + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Coverage analysis +if env['COVERAGE'] == 'full': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(CCFLAGS = '-ftest-coverage') + env.Append(LINKFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-lgcov') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = '-O2') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = '-ggdb', LINKFLAGS = '-gdb') + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = '-pg') + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +env.ParseConfig('PKG_CONFIG_PATH=/usr/evolution28/lib/pkgconfig:/usr/lib/pkconfig pkg-config --cflags --libs glib-2.0 gthread-2.0'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +env['SNMP_FLAGS'] = { + 'CCFLAGS' : [], + 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ], +}; + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); + lastLIBS = context.env['LIBS']; + lastCCFLAGS= context.env['CCFLAGS']; + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); + context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +def CheckEventFD(context): + context.Message('Checking eventfd...'); + result = context.TryLink(""" +#include +int main(int argc, char**argv) +{ + eventfd(0,0); + return 0; +} +""", '.c') + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck, + 'CheckEventFD': CheckEventFD +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Enabling extra Red Hat dependencies for Net-SNMP.'; + conf.env['SNMP_FLAGS']['LIBS'].append(['librpm', 'libsensors', 'libdl', 'libwrap']); + lastLIBS = conf.env['LIBS']; + conf.env.ParseConfig('perl -MExtUtils::Embed -e ldopts'); + conf.env['SNMP_FLAGS']['LIBS'].append(conf.env['LIBS']); + conf.env.Replace(LIBS = lastLIBS); + if not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +if conf.CheckEventFD(): + print 'Enabling kernel eventfd notification mechanism.'; + conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); + +env = conf.Finish(); + +# DSO visibility flags +if '-DCONFIG_HAVE_DSO_VISIBILITY' in env['CCFLAGS']: + env.Append(CCFLAGS = '-DPGM_GNUC_INTERNAL=G_GNUC_INTERNAL') +else: + env.Append(CCFLAGS = '-DPGM_GNUC_INTERNAL=') + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +SConscript(ref_node + 'SConscript.libpgm'); +SConscript(ref_node + 'SConscript.libpgmex'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/SConstruct.Solaris.gcc64 b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.Solaris.gcc64 new file mode 100644 index 0000000..01508dd --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.Solaris.gcc64 @@ -0,0 +1,350 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 1, 0 ) +SConsignFile('scons.signatures' + '-' + platform.system() + '-' + platform.machine() + '-gcc64'); + +vars = Variables() +vars.AddVariables ( + EnumVariable ('BUILD', 'build environment', 'debug', + allowed_values=('release', 'debug', 'profile', 'thirtytwo')), + EnumVariable ('BRANCH', 'branch prediction', 'none', + allowed_values=('none', 'profile', 'seed')), + EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CHECK', 'Check test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_TEST', 'Network test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CC', 'C++ examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_PLUS', 'libpgmplus GPL library', 'false', + allowed_values=('true', 'false')), +) + +#----------------------------------------------------------------------------- +# Dependencies + +def force_gcc(env): + env.PrependENVPath('PATH', '/usr/sfw/bin'); + env.PrependENVPath('PATH', '/opt/glib-gcc64/bin'); + env.PrependENVPath('PATH', '/usr/local/bin'); + env.Tool('gcc'); + env.Tool('g++'); + +env = Environment(); +force_gcc(env); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment( + variables = vars, + ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', +# '-Wunsafe-loop-optimizations', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', + '-pedantic', +# C99 + '-std=gnu99', + '-D_XOPEN_SOURCE=600', + '-D__EXTENSIONS__', + '-DBSD_COMP', + '-D_BSD_SOURCE', +# re-entrant libc + '-D_REENTRANT', + '-DCONFIG_HAVE_GETPROTOBYNAME_R', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header + '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system +# '-DCONFIG_HAVE_PROC', +# example: crash handling +# '-DCONFIG_HAVE_BACKTRACE', +# timing +# '-DCONFIG_HAVE_PSELECT', +# '-DCONFIG_HAVE_RTC', +# '-DCONFIG_HAVE_TSC', +# '-DCONFIG_HAVE_HPET', +# event handling + '-DCONFIG_HAVE_POLL', +# '-DCONFIG_HAVE_EPOLL', +# interface enumeration +# '-DCONFIG_HAVE_GETIFADDRS', +# '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg +# '-DCONFIG_HAVE_WSACMSGHDR', +# multicast + '-DCONFIG_HAVE_MCAST_JOIN', +# '-DCONFIG_HAVE_IP_MREQN', +# sprintf +# '-DCONFIG_HAVE_SPRINTF_GROUPING', +# '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE', +# GNU getopt + '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ '-pipe' + ], + LIBS = [ +# histogram math + 'm', +# clock_gettime() + 'rt', +# Solaris sockets + 'resolv', + 'socket', + 'nsl' + ], + PROTOBUF_CCFLAGS = '-I/opt/glib-gcc64/include', + PROTOBUF_LIBS = '/opt/glib-gcc64/lib/sparcv9/libprotobuf.a', + PROTOBUF_PROTOC = '/opt/glib-gcc64/bin/protoc' +) +force_gcc(env); + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = ['-O2','-m64'], LINKFLAGS = '-m64') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb','-m64'], LINKFLAGS = ['-gdb','-m64']) + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O2','-pg','-m64'], LINKFLAGS = ['-pg','-m64']) + +thirtytwo = env.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = ['-O2','-m32'], LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +def list_remove(list, target): + newlist = []; + for item in str(list).split(' '): + if item != target: + newlist.append(item); + return newlist; + +# managed environment for libpgmsnmp, libpgmhttp +if env['WITH_SNMP'] == 'true': +# net-snmp-config is broken in Solaris 10 and requires two separate calls + env['SNMP_FLAGS'] = env.ParseFlags(['!net-snmp-config-64 --cflags', + '!net-snmp-config-64 --agent-libs']); +# GCC error: language arch=v9 not recognized + ccflags = env['SNMP_FLAGS'].get('CCFLAGS', ''); + env['SNMP_FLAGS']['CCFLAGS'] = list_remove(ccflags, '-xarch=v9'); + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); +# backup = context.env.Clone().Dictionary(); + lastASFLAGS = context.env.get('ASFLAGS', ''); + lastCCFLAGS = context.env.get('CCFLAGS', ''); + lastCFLAGS = context.env.get('CFLAGS', ''); + lastCPPDEFINES = context.env.get('CPPDEFINES', ''); + lastCPPFLAGS = context.env.get('CPPFLAGS', ''); + lastCPPPATH = context.env.get('CPPPATH', ''); + lastLIBPATH = context.env.get('LIBPATH', ''); + lastLIBS = context.env.get('LIBS', ''); + lastLINKFLAGS = context.env.get('LINKFLAGS', ''); + lastRPATH = context.env.get('RPATH', ''); + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); +# context.env.Replace(**backup); + context.env.Replace(ASFLAGS = lastASFLAGS, + CCFLAGS = lastCCFLAGS, + CFLAGS = lastCFLAGS, + CPPDEFINES = lastCPPDEFINES, + CPPFLAGS = lastCPPFLAGS, + CPPPATH = lastCPPPATH, + LIBPATH = lastLIBPATH, + LIBS = lastLIBS, + LINKFLAGS = lastLINKFLAGS, + RPATH = lastRPATH); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '-gcc64/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/SConstruct.Solaris.sungcc b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.Solaris.sungcc new file mode 100644 index 0000000..9d50481 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.Solaris.sungcc @@ -0,0 +1,337 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 1, 0 ) +SConsignFile('scons.signatures'+ '-' + platform.system() + '-' + platform.machine() + '-sungcc'); + +vars = Variables() +vars.AddVariables ( + EnumVariable ('BUILD', 'build environment', 'debug', + allowed_values=('release', 'debug', 'profile')), + EnumVariable ('BRANCH', 'branch prediction', 'none', + allowed_values=('none', 'profile', 'seed')), + EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CHECK', 'Check test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_TEST', 'Network test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CC', 'C++ examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', + allowed_values=('true', 'false')), +) + +#----------------------------------------------------------------------------- +# Dependencies + +def force_gcc(env): + env.PrependENVPath('PATH', '/usr/sfw/bin'); + env.PrependENVPath('PATH', '/opt/glib-gcc/bin'); + env.Tool('gcc'); + env.Tool('g++'); + +env = Environment(); +force_gcc(env); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment( + variables = vars, + ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', +# '-Wunsafe-loop-optimizations', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', + '-pedantic', +# C99 + '-std=gnu99', + '-D_XOPEN_SOURCE=600', + '-D__EXTENSIONS__', + '-DBSD_COMP', + '-D_BSD_SOURCE', +# re-entrant libc + '-D_REENTRANT', + '-DCONFIG_HAVE_GETPROTOBYNAME_R', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header + '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system +# '-DCONFIG_HAVE_PROC', +# example: crash handling +# '-DCONFIG_HAVE_BACKTRACE', +# timing +# '-DCONFIG_HAVE_PSELECT', +# '-DCONFIG_HAVE_RTC', +# '-DCONFIG_HAVE_TSC', +# '-DCONFIG_HAVE_HPET', +# event handling + '-DCONFIG_HAVE_POLL', +# '-DCONFIG_HAVE_EPOLL', +# interface enumeration +# '-DCONFIG_HAVE_GETIFADDRS', +# '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg +# '-DCONFIG_HAVE_WSACMSGHDR', +# multicast + '-DCONFIG_HAVE_MCAST_JOIN', +# '-DCONFIG_HAVE_IP_MREQN', +# sprintf +# '-DCONFIG_HAVE_SPRINTF_GROUPING', +# '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE', +# GNU getopt + '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ '-pipe' + ], + LIBS = [ +# histogram math + 'm', +# clock_gettime() + 'rt', +# Solaris sockets + 'resolv', + 'socket', + 'nsl' + ], + PROTOBUF_CCFLAGS = '-I/opt/glib-gcc/include', + PROTOBUF_LIBS = '/opt/glib-gcc/lib/libprotobuf.a', + PROTOBUF_PROTOC = '/opt/glib-gcc/bin/protoc' +) +force_gcc(env); + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = ['-O2'], LINKFLAGS = []) + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG', '-ggdb'], LINKFLAGS = ['-gdb']) + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = ['-pg']) + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +if env['WITH_SNMP'] == 'true': +# net-snmp-config is broken in Solaris 10 and requires two separate calls + env['SNMP_FLAGS'] = env.ParseFlags(['!net-snmp-config-32 --cflags', + '!net-snmp-config-32 --agent-libs']); + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); +# backup = context.env.Clone().Dictionary(); + lastASFLAGS = context.env.get('ASFLAGS', ''); + lastCCFLAGS = context.env.get('CCFLAGS', ''); + lastCFLAGS = context.env.get('CFLAGS', ''); + lastCPPDEFINES = context.env.get('CPPDEFINES', ''); + lastCPPFLAGS = context.env.get('CPPFLAGS', ''); + lastCPPPATH = context.env.get('CPPPATH', ''); + lastLIBPATH = context.env.get('LIBPATH', ''); + lastLIBS = context.env.get('LIBS', ''); + lastLINKFLAGS = context.env.get('LINKFLAGS', ''); + lastRPATH = context.env.get('RPATH', ''); + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); +# context.env.Replace(**backup); + context.env.Replace(ASFLAGS = lastASFLAGS, + CCFLAGS = lastCCFLAGS, + CFLAGS = lastCFLAGS, + CPPDEFINES = lastCPPDEFINES, + CPPFLAGS = lastCPPFLAGS, + CPPPATH = lastCPPPATH, + LIBPATH = lastLIBPATH, + LIBS = lastLIBS, + LINKFLAGS = lastLINKFLAGS, + RPATH = lastRPATH); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '-sungcc/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/SConstruct.Solaris.sunstudio b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.Solaris.sunstudio new file mode 100644 index 0000000..0144291 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.Solaris.sunstudio @@ -0,0 +1,322 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 1, 0 ) +SConsignFile('scons.signatures' + '-' + platform.system() + '-' + platform.machine() + '-sunstudio'); + +vars = Variables() +vars.AddVariables ( + EnumVariable ('BUILD', 'build environment', 'debug', + allowed_values=('release', 'debug', 'profile')), + EnumVariable ('BRANCH', 'branch prediction', 'none', + allowed_values=('none', 'profile', 'seed')), + EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CHECK', 'Check test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_TEST', 'Network test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CC', 'C++ examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_PLUS', 'libpgmplus GPL library', 'false', + allowed_values=('true', 'false')), +) + +#----------------------------------------------------------------------------- +# Dependencies + +def force_sunstudio(env): + env.PrependENVPath('PATH', '/usr/ccs/bin'); + env.PrependENVPath('PATH', '/opt/glib-sunstudio/bin'); + env.PrependENVPath('PATH', '/opt/sunstudio12.1/bin'); + env.Tool('sunc++'); + env.Tool('suncc'); + env.Tool('sunlink'); + env.Tool('sunar'); + +env = Environment(); +force_sunstudio(env); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' + Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' + Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' + Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment( + variables = vars, + ENV = os.environ, + CCFLAGS = [ '-v', +# C99 + '-xc99=all', + '-D_XOPEN_SOURCE=600', + '-D__EXTENSIONS__', + '-DBSD_COMP', + '-D_BSD_SOURCE', +# re-entrant libc + '-D_REENTRANT', + '-DCONFIG_HAVE_GETPROTOBYNAME_R', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header + '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system +# '-DCONFIG_HAVE_PROC', +# example: crash handling +# '-DCONFIG_HAVE_BACKTRACE', +# timing +# '-DCONFIG_HAVE_PSELECT', +# '-DCONFIG_HAVE_RTC', +# '-DCONFIG_HAVE_TSC', +# '-DCONFIG_HAVE_HPET', +# event handling + '-DCONFIG_HAVE_POLL', +# '-DCONFIG_HAVE_EPOLL', +# interface enumeration +# '-DCONFIG_HAVE_GETIFADDRS', +# '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg +# '-DCONFIG_HAVE_WSACMSGHDR', +# multicast + '-DCONFIG_HAVE_MCAST_JOIN', +# '-DCONFIG_HAVE_IP_MREQN', +# sprintf +# '-DCONFIG_HAVE_SPRINTF_GROUPING', +# '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE', +# GNU getopt + '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [], + LIBS = [ +# histogram math + 'm', +# clock_gettime() + 'rt', +# Solaris sockets + 'resolv', + 'socket', + 'nsl' + ], + PROTOBUF_CCFLAGS = '-I/opt/glib-sunstudio/include', + PROTOBUF_LIBS = '/opt/glib-sunstudio/lib/sparcv9/libprotobuf.a', + PROTOBUF_PROTOC = '/opt/glib-sunstudio/bin/protoc' +) +force_sunstudio(env); + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = ['-xO2','-m64'], LINKFLAGS = '-m64') + +# outstanding defect with 12u1 cannot compile extended asm without optimization.:w +# +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG','-xO1', '-g','-m64'], LINKFLAGS = ['-g','-m64']) + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-xO2','-pg','-m64'], LINKFLAGS = ['-pg','-m64']) + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = ['-xO2','-m32'], LINKFLAGS = '-m32'); + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +if env['WITH_SNMP'] == 'true': +# net-snmp-config is broken in Solaris 10 and requires two separate calls + env['SNMP_FLAGS'] = env.ParseFlags(['!/usr/sfw/bin/net-snmp-config-64 --cflags', + '!/usr/sfw/bin/net-snmp-config-64 --agent-libs']); + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); +# backup = context.env.Clone().Dictionary(); + lastASFLAGS = context.env.get('ASFLAGS', ''); + lastCCFLAGS = context.env.get('CCFLAGS', ''); + lastCFLAGS = context.env.get('CFLAGS', ''); + lastCPPDEFINES = context.env.get('CPPDEFINES', ''); + lastCPPFLAGS = context.env.get('CPPFLAGS', ''); + lastCPPPATH = context.env.get('CPPPATH', ''); + lastLIBPATH = context.env.get('LIBPATH', ''); + lastLIBS = context.env.get('LIBS', ''); + lastLINKFLAGS = context.env.get('LINKFLAGS', ''); + lastRPATH = context.env.get('RPATH', ''); + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); +# context.env.Replace(**backup); + context.env.Replace(ASFLAGS = lastASFLAGS, + CCFLAGS = lastCCFLAGS, + CFLAGS = lastCFLAGS, + CPPDEFINES = lastCPPDEFINES, + CPPFLAGS = lastCPPFLAGS, + CPPPATH = lastCPPPATH, + LIBPATH = lastLIBPATH, + LIBS = lastLIBS, + LINKFLAGS = lastLINKFLAGS, + RPATH = lastRPATH); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '-sunstudio/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/SConstruct.clang b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.clang new file mode 100644 index 0000000..beed2db --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.clang @@ -0,0 +1,351 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 1, 0 ) +SConsignFile('scons.signatures' + '-' + platform.system() + '-' + platform.machine() + '-clang'); + +vars = Variables() +vars.AddVariables ( + EnumVariable ('BUILD', 'build environment', 'debug', + allowed_values=('release', 'debug', 'profile')), + EnumVariable ('BRANCH', 'branch prediction', 'none', + allowed_values=('none', 'profile', 'seed')), + EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', + allowed_values=('true', 'false')), + EnumVariable ('COVERAGE', 'test coverage', 'none', + allowed_values=('none', 'full')), + EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CHECK', 'Check test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_TEST', 'Network test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CC', 'C++ examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', + allowed_values=('true', 'false')), +) + +#----------------------------------------------------------------------------- +# Dependencies + +def force_clang(env): + env['CC'] = 'clang'; + +env = Environment(); +force_clang(env); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment( + variables = vars, + ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', +# '-Wunsafe-loop-optimizations', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', +# '-pedantic', +# C99 + '-std=gnu99', + '-D_XOPEN_SOURCE=600', + '-D_BSD_SOURCE', +# re-entrant libc + '-D_REENTRANT', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R', + '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header + '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system + '-DCONFIG_HAVE_PROC', +# example: crash handling + '-DCONFIG_HAVE_BACKTRACE', +# timing + '-DCONFIG_HAVE_PSELECT', + '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', + '-DCONFIG_HAVE_HPET', +# event handling + '-DCONFIG_HAVE_POLL', + '-DCONFIG_HAVE_EPOLL', +# interface enumeration + '-DCONFIG_HAVE_GETIFADDRS', + '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg +# '-DCONFIG_HAVE_WSACMSGHDR', +# multicast + '-DCONFIG_HAVE_MCAST_JOIN', + '-DCONFIG_HAVE_IP_MREQN', +# sprintf +# NB: raises invalid conversion specifier ''' [-Wformat] in clang +# '-DCONFIG_HAVE_SPRINTF_GROUPING', + '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE', +# GNU getopt + '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ '-pipe' + ], + LIBS = [ +# histogram math + 'm', +# clock_gettime() + 'rt' + ] +) +force_clang(env); + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Coverage analysis +if env['COVERAGE'] == 'full': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(CCFLAGS = '-ftest-coverage') + env.Append(LINKFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-lgcov') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = '-O2') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = '-gdb') + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = '-pg') + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs glib-2.0 gthread-2.0'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +if env['WITH_SNMP'] == 'true': + env['SNMP_FLAGS'] = env.ParseFlags('!net-snmp-config --cflags --agent-libs'); + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); +# backup = context.env.Clone().Dictionary(); + lastASFLAGS = context.env.get('ASFLAGS', ''); + lastCCFLAGS = context.env.get('CCFLAGS', ''); + lastCFLAGS = context.env.get('CFLAGS', ''); + lastCPPDEFINES = context.env.get('CPPDEFINES', ''); + lastCPPFLAGS = context.env.get('CPPFLAGS', ''); + lastCPPPATH = context.env.get('CPPPATH', ''); + lastLIBPATH = context.env.get('LIBPATH', ''); + lastLIBS = context.env.get('LIBS', ''); + lastLINKFLAGS = context.env.get('LINKFLAGS', ''); + lastRPATH = context.env.get('RPATH', ''); + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); +# context.env.Replace(**backup); + context.env.Replace(ASFLAGS = lastASFLAGS, + CCFLAGS = lastCCFLAGS, + CFLAGS = lastCFLAGS, + CPPDEFINES = lastCPPDEFINES, + CPPFLAGS = lastCPPFLAGS, + CPPPATH = lastCPPPATH, + LIBPATH = lastLIBPATH, + LIBS = lastLIBS, + LINKFLAGS = lastLINKFLAGS, + RPATH = lastRPATH); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +def CheckEventFD(context): + context.Message('Checking eventfd...'); + result = context.TryLink(""" +#include +int main(int argc, char**argv) +{ + eventfd(0,0); + return 0; +} +""", '.c') + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck, + 'CheckEventFD': CheckEventFD +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +if conf.CheckEventFD(): + print 'Enabling kernel eventfd notification mechanism.'; + conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-' + platform.system() + '-' + platform.machine() + '-clang/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/SConstruct.mingw b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.mingw new file mode 100644 index 0000000..c896256 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.mingw @@ -0,0 +1,331 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 1, 0 ) +SConsignFile('scons.signatures' + '-Win32-' + platform.machine()); + +vars = Variables() +vars.AddVariables ( + EnumVariable ('BUILD', 'build environment', 'debug', + allowed_values=('release', 'debug', 'profile')), + EnumVariable ('BRANCH', 'branch prediction', 'none', + allowed_values=('none', 'profile', 'seed')), + EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', + allowed_values=('true', 'false')), + EnumVariable ('COVERAGE', 'test coverage', 'none', + allowed_values=('none', 'full')), + EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CHECK', 'Check test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_TEST', 'Network test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CC', 'C++ examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', + allowed_values=('true', 'false')), +) + +#----------------------------------------------------------------------------- +# Dependencies + +def force_mingw(env): + env.Tool('crossmingw', toolpath=['.']); + +env = Environment(); +force_mingw(env); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment( + variables = vars, + ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', + '-Wunsafe-loop-optimizations', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', + '-pedantic', +# C99 + '-std=gnu99', + '-D_BSD_SOURCE', + '-D_WIN32_WINNT=0x0501', +# re-entrant libc + '-D_REENTRANT', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header +# '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system +# '-DCONFIG_HAVE_PROC', +# example: crash handling +# '-DCONFIG_HAVE_BACKTRACE', +# timing +# '-DCONFIG_HAVE_PSELECT', +# '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', +# '-DCONFIG_HAVE_HPET', +# event handling +# '-DCONFIG_HAVE_POLL', +# '-DCONFIG_HAVE_EPOLL', +# interface enumeration +# '-DCONFIG_HAVE_GETIFADDRS', +# '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg + '-DCONFIG_HAVE_WSACMSGHDR', +# multicast +# '-DCONFIG_HAVE_MCAST_JOIN', +# '-DCONFIG_HAVE_IP_MREQN', +# sprintf +# '-DCONFIG_HAVE_SPRINTF_GROUPING', +# '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support +# '-DCONFIG_TARGET_WINE', +# GNU getopt +# '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ '-pipe' + ], + LIBS = [ + 'iphlpapi.lib', + 'ws2_32.lib' + ] +) +force_mingw(env); + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Coverage analysis +if env['COVERAGE'] == 'full': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(CCFLAGS = '-ftest-coverage') + env.Append(LINKFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-lgcov') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = '-O2') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = ['-gdb']) + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = '-pg') + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --cflags --libs glib-2.0 gthread-2.0'); + env.MergeFlags('-Iwin/include -Lwin/lib'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +if env['WITH_SNMP'] == 'true': +# no command line helper, so hard code + env['SNMP_FLAGS'] = env.ParseFlags('-Iwin/include -Lwin/lib -lnetsnmpagent -lnetsnmphelpers -lnetsnmpmibs -lnetsnmp'); + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); + lastLIBS = context.env['LIBS']; + lastCCFLAGS= context.env['CCFLAGS']; + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); + context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +def CheckEventFD(context): + context.Message('Checking eventfd...'); + result = context.TryLink(""" +#include +int main(int argc, char**argv) +{ + eventfd(0,0); + return 0; +} +""", '.c') + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck, + 'CheckEventFD': CheckEventFD +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +if conf.CheckEventFD(): + print 'Enabling kernel eventfd notification mechanism.'; + conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-Win32-' + platform.machine() + '/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/SConstruct.mingw-wine b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.mingw-wine new file mode 100644 index 0000000..6af1e0a --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/SConstruct.mingw-wine @@ -0,0 +1,339 @@ +# -*- mode: python -*- +# OpenPGM build script + +import platform +import os +import time +import sys + +EnsureSConsVersion( 1, 0 ) +SConsignFile('scons.signatures' + '-Wine-' + platform.machine()); + +vars = Variables() +vars.AddVariables ( + EnumVariable ('BUILD', 'build environment', 'debug', + allowed_values=('release', 'debug', 'profile')), + EnumVariable ('BRANCH', 'branch prediction', 'none', + allowed_values=('none', 'profile', 'seed')), + EnumVariable ('WITH_GETTEXT', 'l10n support via libintl', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_GLIB', 'Build GLib dependent modules', 'false', + allowed_values=('true', 'false')), + EnumVariable ('COVERAGE', 'test coverage', 'none', + allowed_values=('none', 'full')), + EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_HTTP', 'HTTP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_SNMP', 'SNMP administration', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CHECK', 'Check test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_TEST', 'Network test system', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_CC', 'C++ examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_EXAMPLES', 'Examples', 'true', + allowed_values=('true', 'false')), + EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false', + allowed_values=('true', 'false')), + EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false', + allowed_values=('true', 'false')), +) + +#----------------------------------------------------------------------------- +# Dependencies + +def force_mingw(env): + env.Tool('crossmingw', toolpath=['.']); + +env = Environment(); +force_mingw(env); + +def CheckPKGConfig(context, version): + context.Message( 'Checking for pkg-config... ' ) + ret = context.TryAction('PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --atleast-pkgconfig-version=%s' % version)[0] + context.Result( ret ) + return ret + +def CheckPKG(context, name): + context.Message( 'Checking for %s... ' % name ) + ret = context.TryAction('PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --exists \'%s\'' % name)[0] + context.Result( ret ) + return ret + +conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig, + 'CheckPKG' : CheckPKG }) + +if not conf.CheckPKGConfig('0.15.0'): + print 'pkg-config >= 0.15.0 not found.' +# Exit(1) + +if not conf.CheckPKG('glib-2.0 >= 2.10'): + print 'glib-2.0 >= 2.10 not found.' +# Exit(1) + +if not conf.CheckPKG('gthread-2.0'): + print 'gthread-2.0 not found.' +# Exit(1) + +env = conf.Finish(); + +#----------------------------------------------------------------------------- +# Platform specifics + +env = Environment( + variables = vars, + ENV = os.environ, + CCFLAGS = [ '-pipe', + '-Wall', + '-Wextra', + '-Wfloat-equal', + '-Wshadow', + '-Wunsafe-loop-optimizations', + '-Wpointer-arith', + '-Wbad-function-cast', + '-Wcast-qual', + '-Wcast-align', + '-Wwrite-strings', + '-Waggregate-return', + '-Wstrict-prototypes', + '-Wold-style-definition', + '-Wmissing-prototypes', + '-Wmissing-declarations', + '-Wmissing-noreturn', + '-Wmissing-format-attribute', + '-Wredundant-decls', + '-Wnested-externs', + '-Winline', + '-pedantic', +# C99 + '-std=gnu99', + '-D_GNU_SOURCE', + '-D_WIN32_WINNT=0x0501', +# re-entrant libc + '-D_REENTRANT', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R', +# '-DCONFIG_HAVE_GETPROTOBYNAME_R2', +# variadic macros + '-DCONFIG_HAVE_ISO_VARARGS', +# '-DCONFIG_HAVE_GNUC_VARARGS', +# stack memory api header +# '-DCONFIG_HAVE_ALLOCA_H', +# optimium checksum implementation +# '-DCONFIG_8BIT_CHECKSUM', + '-DCONFIG_16BIT_CHECKSUM', +# '-DCONFIG_32BIT_CHECKSUM', +# '-DCONFIG_64BIT_CHECKSUM', +# '-DCONFIG_VECTOR_CHECKSUM', +# useful /proc system +# '-DCONFIG_HAVE_PROC', +# example: crash handling +# '-DCONFIG_HAVE_BACKTRACE', +# timing +# '-DCONFIG_HAVE_PSELECT', +# '-DCONFIG_HAVE_RTC', + '-DCONFIG_HAVE_TSC', +# '-DCONFIG_HAVE_HPET', +# event handling +# '-DCONFIG_HAVE_POLL', +# '-DCONFIG_HAVE_EPOLL', +# interface enumeration +# '-DCONFIG_HAVE_GETIFADDRS', +# '-DCONFIG_HAVE_IFR_NETMASK', +# win32 cmsg +# '-DCONFIG_HAVE_WSACMSGHDR', +# multicast +# '-DCONFIG_HAVE_MCAST_JOIN', +# '-DCONFIG_HAVE_IP_MREQN', +# sprintf +# '-DCONFIG_HAVE_SPRINTF_GROUPING', +# '-DCONFIG_HAVE_VASPRINTF', +# symbol linking scope + '-DCONFIG_HAVE_DSO_VISIBILITY', +# socket binding + '-DCONFIG_BIND_INADDR_ANY', +# IP header order as per IP(4) on FreeBSD +# '-DCONFIG_HOST_ORDER_IP_LEN', +# '-DCONFIG_HOST_ORDER_IP_OFF', +# optimum galois field multiplication + '-DCONFIG_GALOIS_MUL_LUT', +# Wine limited API support + '-DCONFIG_TARGET_WINE', +# GNU getopt +# '-DCONFIG_HAVE_GETOPT' + ], + LINKFLAGS = [ '-pipe' + ], + LIBS = [ + 'iphlpapi.lib', + 'ws2_32.lib' + ] +) +force_mingw(env); + +# Branch prediction +if env['BRANCH'] == 'profile': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-fprofile-arcs') +elif env['BRANCH'] == 'seed': + env.Append(CCFLAGS = '-fbranch-probabilities') + +# Coverage analysis +if env['COVERAGE'] == 'full': + env.Append(CCFLAGS = '-fprofile-arcs') + env.Append(CCFLAGS = '-ftest-coverage') + env.Append(LINKFLAGS = '-fprofile-arcs') + env.Append(LINKFLAGS = '-lgcov') + +# Define separate build environments +release = env.Clone(BUILD = 'release') +release.Append(CCFLAGS = '-O2') + +debug = env.Clone(BUILD = 'debug') +debug.Append(CCFLAGS = ['-DPGM_DEBUG','-ggdb'], LINKFLAGS = ['-gdb']) + +profile = env.Clone(BUILD = 'profile') +profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = '-pg') + +thirtytwo = release.Clone(BUILD = 'thirtytwo') +thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32') + +# choose and environment to build +if env['BUILD'] == 'release': + Export({'env':release}) +elif env['BUILD'] == 'profile': + Export({'env':profile}) +elif env['BUILD'] == 'thirtytwo': + Export({'env':thirtytwo}) +else: + Export({'env':debug}) + +#----------------------------------------------------------------------------- +# Re-analyse dependencies + +Import('env') + +# vanilla environment +if env['WITH_GLIB'] == 'true': + env['GLIB_FLAGS'] = env.ParseFlags('!PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --cflags --libs glib-2.0 gthread-2.0'); + env.MergeFlags('-Iwin/include -Lwin/lib'); +else: + env['GLIB_FLAGS'] = ''; + +# l10n +if env['WITH_GETTEXT'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HAVE_GETTEXT'); + +# instrumentation +if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true': + env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS'); + +# managed environment for libpgmsnmp, libpgmhttp +env['SNMP_FLAGS'] = { + 'CCFLAGS' : [], + 'LIBS' : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ], +}; + +def CheckSNMP(context): + context.Message('Checking Net-SNMP...'); + lastLIBS = context.env['LIBS']; + lastCCFLAGS= context.env['CCFLAGS']; + context.env.MergeFlags(env['SNMP_FLAGS']); + result = context.TryLink(""" +int main(int argc, char**argv) +{ + init_agent("PGM"); + return 0; +} +""", '.c'); + context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS); + context.Result(not result); + return result; + +def CheckCheck(context): + context.Message('Checking Check unit test framework...'); + result = context.TryAction('PKG_CONFIG_PATH=win/lib/pkgconfig pkg-config --cflags --libs check')[0]; + context.Result(result); + return result; + +def CheckEventFD(context): + context.Message('Checking eventfd...'); + result = context.TryLink(""" +#include +int main(int argc, char**argv) +{ + eventfd(0,0); + return 0; +} +""", '.c') + context.Result(result); + return result; + +tests = { + 'CheckCheck': CheckCheck, + 'CheckEventFD': CheckEventFD +} +if env['WITH_SNMP'] == 'true': + tests['CheckSNMP'] = CheckSNMP; +conf = Configure(env, custom_tests = tests); + +if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP(): + print 'Enabling extra Red Hat dependencies for Net-SNMP.'; + conf.env['SNMP_FLAGS']['LIBS'].append(['librpm', 'libsensors', 'libdl', 'libwrap']); + lastLIBS = conf.env['LIBS']; + conf.env.ParseConfig('perl -MExtUtils::Embed -e ldopts'); + conf.env['SNMP_FLAGS']['LIBS'].append(conf.env['LIBS']); + conf.env.Replace(LIBS = lastLIBS); + if not conf.CheckSNMP(): + print 'Net-SNMP libraries not compatible.'; + Exit(1); + +if env['WITH_CHECK'] == 'true' and conf.CheckCheck(): + print 'Enabling Check unit tests.'; + conf.env['CHECK'] = 'true'; +else: + print 'Disabling Check unit tests.'; + conf.env['CHECK'] = 'false'; + +if conf.CheckEventFD(): + print 'Enabling kernel eventfd notification mechanism.'; + conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD'); + +env = conf.Finish(); + +# add builder to create PIC static libraries for including in shared libraries +action_list = [ Action("$ARCOM", "$ARCOMSTR") ]; +if env.Detect('ranlib'): + ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR"); + action_list.append(ranlib_action); +pic_lib = Builder( action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'SharedObject') +env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib}); + + +#----------------------------------------------------------------------------- + +ref_node = 'ref/' + env['BUILD'] + '-Wine-' + platform.machine() + '/'; +BuildDir(ref_node, '.', duplicate=0) + +env.Append(CPPPATH = os.getcwd() + '/include'); +env.Append(LIBPATH = os.getcwd() + '/' + ref_node); + +if env['WITH_GLIB'] == 'true': + SConscript(ref_node + 'SConscript.libpgmex'); +SConscript(ref_node + 'SConscript.libpgm'); +if env['WITH_HTTP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmhttp'); +if env['WITH_SNMP'] == 'true': + SConscript(ref_node + 'SConscript.libpgmsnmp'); +if env['WITH_TEST'] == 'true': + SConscript(ref_node + 'test/SConscript'); +if env['WITH_EXAMPLES'] == 'true': + SConscript(ref_node + 'examples/SConscript'); + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/atomic_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/atomic_unittest.c new file mode 100644 index 0000000..a301c28 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/atomic_unittest.c @@ -0,0 +1,166 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for atomic operations. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include + + +/* mock state */ + + +/* mock functions for external references */ + +#define PGM_COMPILATION +#include "pgm/atomic.h" + + +/* target: + * uint32_t + * pgm_atomic_exchange_and_add32 ( + * volatile uint32_t* atomic, + * const uint32_t val + * ) + */ + +START_TEST (test_int32_exchange_and_add_pass_001) +{ + volatile uint32_t atomic = 0; + fail_unless (0 == pgm_atomic_exchange_and_add32 (&atomic, 5)); + fail_unless (5 == atomic); + fail_unless (5 == pgm_atomic_exchange_and_add32 (&atomic, (uint32_t)-10)); + fail_unless ((uint32_t)-5 == atomic); +} +END_TEST + +/* target: + * void + * pgm_atomic_add32 ( + * volatile uint32_t* atomic, + * const uint32_t val + * ) + */ + +START_TEST (test_int32_add_pass_001) +{ + volatile uint32_t atomic = (uint32_t)-5; + pgm_atomic_add32 (&atomic, 20); + fail_unless (15 == atomic); + pgm_atomic_add32 (&atomic, (uint32_t)-35); + fail_unless ((uint32_t)-20 == atomic); +} +END_TEST + +/* ensure wrap around when casting uint32 */ +START_TEST (test_int32_add_pass_002) +{ + volatile uint32_t atomic = 0; + pgm_atomic_add32 (&atomic, UINT32_MAX/2); + fail_unless ((UINT32_MAX/2) == atomic); + pgm_atomic_add32 (&atomic, UINT32_MAX - (UINT32_MAX/2)); + fail_unless (UINT32_MAX == atomic); + pgm_atomic_add32 (&atomic, 1); + fail_unless (0 == atomic); +} +END_TEST + +/* target: + * uint32_t + * pgm_atomic_read32 ( + * volatile uint32_t* atomic + * ) + */ + +START_TEST (test_int32_get_pass_001) +{ + volatile uint32_t atomic = (uint32_t)-20; + fail_unless ((uint32_t)-20 == pgm_atomic_read32 (&atomic)); +} +END_TEST + +/* target: + * void + * pgm_atomic_int32_set ( + * volatile int32_t* atomic, + * const int32_t val + * ) + */ + +START_TEST (test_int32_set_pass_001) +{ + volatile uint32_t atomic = (uint32_t)-20; + pgm_atomic_write32 (&atomic, 5); + fail_unless (5 == atomic); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_exchange_and_add = tcase_create ("exchange-and-add"); + suite_add_tcase (s, tc_exchange_and_add); + tcase_add_test (tc_exchange_and_add, test_int32_exchange_and_add_pass_001); + + TCase* tc_add = tcase_create ("add"); + suite_add_tcase (s, tc_add); + tcase_add_test (tc_add, test_int32_add_pass_001); + tcase_add_test (tc_add, test_int32_add_pass_002); + + TCase* tc_get = tcase_create ("get"); + suite_add_tcase (s, tc_get); + tcase_add_test (tc_get, test_int32_get_pass_001); + + TCase* tc_set = tcase_create ("set"); + suite_add_tcase (s, tc_set); + tcase_add_test (tc_set, test_int32_set_pass_001); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/backtrace.c b/3rdparty/openpgm-svn-r1135/pgm/backtrace.c new file mode 100644 index 0000000..86a3cd0 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/backtrace.c @@ -0,0 +1,69 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Dump back trace to stderr and try gdb. + * + * Copyright (c) 2006-2007 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef CONFIG_HAVE_BACKTRACE +# include +# include +# include +# include +#endif +#include +#include +#include + + +void +on_sigsegv ( + G_GNUC_UNUSED int signum + ) +{ +#ifdef CONFIG_HAVE_BACKTRACE + void* array[256]; + char** names; + char cmd[1024]; + int i, size; + gchar *out, *err; + gint exit_status; + + fprintf (stderr, "\n======= Backtrace: =========\n"); + + size = backtrace (array, G_N_ELEMENTS(array)); + names = backtrace_symbols (array, size); + + for (i = 0; i < size; i++) + fprintf (stderr, "%s\n", names[i]); + + free (names); + fflush (stderr); + + sprintf (cmd, "gdb --ex 'attach %ld' --ex 'info threads' --ex 'thread apply all bt' --batch", (long)getpid ()); + if ( g_spawn_command_line_sync (cmd, &out, &err, &exit_status, NULL) ) + { + fprintf (stderr, "======= GDB Backtrace: =========\n"); + fprintf (stderr, "%s\n", out); + } +#endif /* CONFIG_HAVE_BACKTRACE */ + + abort (); +} + +/* eof */ + diff --git a/3rdparty/openpgm-svn-r1135/pgm/checksum.c b/3rdparty/openpgm-svn-r1135/pgm/checksum.c new file mode 100644 index 0000000..5e367ea --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/checksum.c @@ -0,0 +1,941 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM checksum routines + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + + +/* locals */ + +static inline uint16_t do_csum (const void*, uint16_t, uint32_t) PGM_GNUC_PURE; +static uint16_t do_csum_8bit (const void*, uint16_t, uint32_t) PGM_GNUC_PURE; +static uint16_t do_csum_16bit (const void*, uint16_t, uint32_t) PGM_GNUC_PURE; +static uint16_t do_csum_32bit (const void*, uint16_t, uint32_t) PGM_GNUC_PURE; +static uint16_t do_csum_64bit (const void*, uint16_t, uint32_t) PGM_GNUC_PURE; +#if defined(__amd64) || defined(__x86_64__) +static uint16_t do_csum_vector (const void*, uint16_t, uint32_t) PGM_GNUC_PURE; +#endif + + +/* endian independent checksum routine + */ + +static +uint16_t +do_csum_8bit ( + const void* addr, + uint16_t len, + uint32_t csum + ) +{ + uint_fast32_t acc; + uint16_t src; + const uint8_t* buf; + + acc = csum; + buf = (const uint8_t*)addr; + while (len > 1) { +/* first byte as most significant */ + src = (*buf++) << 8; +/* second byte as least significant */ + src |= (*buf++); + acc += src; + len -= 2; + } +/* trailing odd byte */ + if (len > 0) { + src = (*buf) << 8; + acc += src; + } + acc = (acc >> 16) + (acc & 0xffff); + acc += (acc >> 16); + return htons ((uint16_t)acc); +} + +static +uint16_t +do_csumcpy_8bit ( + const void* restrict srcaddr, + void* restrict dstaddr, + uint16_t len, + uint32_t csum + ) +{ + uint_fast32_t acc; + const uint8_t*restrict srcbuf; + uint8_t*restrict dstbuf; + uint_fast16_t val16; + + acc = csum; + srcbuf = (const uint8_t*restrict)srcaddr; + dstbuf = (uint8_t*restrict)dstaddr; + while (len > 1) { +/* first byte as most significant */ + val16 = (*dstbuf++ = *srcbuf++) << 8; +/* second byte as least significant */ + val16 |= (*dstbuf++ = *srcbuf++); + acc += val16; + len -= 2; + } +/* trailing odd byte */ + if (len > 0) { + val16 = (*dstbuf = *srcbuf) << 8; + acc += val16; + } + acc = (acc >> 16) + (acc & 0xffff); + acc += (acc >> 16); + return htons ((uint16_t)acc); +} + +static +uint16_t +do_csum_16bit ( + const void* addr, + uint16_t len, + uint32_t csum + ) +{ + uint_fast32_t acc; + const uint8_t* buf; + uint16_t remainder; + uint_fast16_t count8; + bool is_odd; + + acc = csum; + buf = (const uint8_t*)addr; + remainder = 0; + + if (PGM_UNLIKELY(len == 0)) + return (uint16_t)acc; + is_odd = ((uintptr_t)buf & 1); +/* align first byte */ + if (PGM_UNLIKELY(is_odd)) { + ((uint8_t*)&remainder)[1] = *buf++; + len--; + } +/* 8-byte unrolls */ + count8 = len >> 3; + while (count8--) { + acc += ((const uint16_t*)buf)[ 0 ]; + acc += ((const uint16_t*)buf)[ 1 ]; + acc += ((const uint16_t*)buf)[ 2 ]; + acc += ((const uint16_t*)buf)[ 3 ]; + buf = &buf[ 8 ]; + } + len %= 8; +/* final 7 bytes */ + while (len > 1) { + acc += ((const uint16_t*)buf)[ 0 ]; + buf = &buf[ 2 ]; + len -= 2; + } +/* trailing odd byte */ + if (len > 0) { + ((uint8_t*)&remainder)[0] = *buf; + } + acc += remainder; + acc = (acc >> 16) + (acc & 0xffff); + acc += (acc >> 16); + if (PGM_UNLIKELY(is_odd)) + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + return (uint16_t)acc; +} + +static +uint16_t +do_csumcpy_16bit ( + const void* restrict srcaddr, + void* restrict dstaddr, + uint16_t len, + uint32_t csum + ) +{ + uint_fast32_t acc; + const uint8_t*restrict srcbuf; + uint8_t*restrict dstbuf; + uint16_t remainder; + uint_fast16_t count8; + bool is_odd; + + acc = csum; + srcbuf = (const uint8_t*restrict)srcaddr; + dstbuf = (uint8_t*restrict)dstaddr; + remainder = 0; + + if (PGM_UNLIKELY(len == 0)) + return (uint16_t)acc; + is_odd = ((uintptr_t)srcbuf & 1); +/* align first byte */ + if (PGM_UNLIKELY(is_odd)) { + ((uint8_t*restrict)&remainder)[1] = *dstbuf++ = *srcbuf++; + len--; + } +/* 8-byte unrolls, anything larger than 16-byte or less than 8 loses performance */ + count8 = len >> 3; + while (count8--) { + acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; + acc += ((uint16_t*restrict)dstbuf)[ 1 ] = ((const uint16_t*restrict)srcbuf)[ 1 ]; + acc += ((uint16_t*restrict)dstbuf)[ 2 ] = ((const uint16_t*restrict)srcbuf)[ 2 ]; + acc += ((uint16_t*restrict)dstbuf)[ 3 ] = ((const uint16_t*restrict)srcbuf)[ 3 ]; + srcbuf = &srcbuf[ 8 ]; + dstbuf = &dstbuf[ 8 ]; + } + len %= 8; +/* final 7 bytes */ + while (len > 1) { + acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; + srcbuf = &srcbuf[ 2 ]; + dstbuf = &dstbuf[ 2 ]; + len -= 2; + } +/* trailing odd byte */ + if (len > 0) { + ((uint8_t*restrict)&remainder)[0] = *dstbuf = *srcbuf; + } + acc += remainder; + acc = (acc >> 16) + (acc & 0xffff); + acc += (acc >> 16); + if (PGM_UNLIKELY(is_odd)) + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + return (uint16_t)acc; +} + +static +uint16_t +do_csum_32bit ( + const void* addr, + uint16_t len, + uint32_t csum + ) +{ + uint_fast32_t acc; + const uint8_t* buf; + uint16_t remainder; + uint_fast16_t count; + bool is_odd; + + acc = csum; + buf = (const uint8_t*)addr; + remainder = 0; + + if (PGM_UNLIKELY(len == 0)) + return (uint16_t)acc; + is_odd = ((uintptr_t)buf & 1); +/* align first byte */ + if (PGM_UNLIKELY(is_odd)) { + ((uint8_t*)&remainder)[1] = *buf++; + len--; + } +/* 16-bit words */ + count = len >> 1; + if (count) + { + if ((uintptr_t)buf & 2) { + acc += ((const uint16_t*)buf)[ 0 ]; + buf = &buf[ 2 ]; + count--; + len -= 2; + } +/* 32-bit words */ + count >>= 1; + if (count) + { + uint32_t carry = 0; + while (count) { + acc += carry; + acc += ((const uint32_t*)buf)[ 0 ]; + carry = ((const uint32_t*)buf)[ 0 ] > acc; + buf = &buf[ 4 ]; + count--; + } + acc += carry; + acc = (acc >> 16) + (acc & 0xffff); + } + if (len & 2) { + acc += ((const uint16_t*)buf)[ 0 ]; + buf = &buf[ 2 ]; + } + } +/* trailing odd byte */ + if (len & 1) { + ((uint8_t*)&remainder)[0] = *buf; + } + acc += remainder; + acc = (acc >> 16) + (acc & 0xffff); + acc += (acc >> 16); + if (PGM_UNLIKELY(is_odd)) + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + return (uint16_t)acc; +} + +static +uint16_t +do_csumcpy_32bit ( + const void* restrict srcaddr, + void* restrict dstaddr, + uint16_t len, + uint32_t csum + ) +{ + uint_fast32_t acc; + const uint8_t*restrict srcbuf; + uint8_t*restrict dstbuf; + uint16_t remainder; + uint_fast16_t count; + bool is_odd; + + acc = csum; + srcbuf = (const uint8_t*restrict)srcaddr; + dstbuf = (uint8_t*restrict)dstaddr; + remainder = 0; + + if (PGM_UNLIKELY(len == 0)) + return (uint16_t)acc; + is_odd = ((uintptr_t)srcbuf & 1); +/* align first byte */ + if (PGM_UNLIKELY(is_odd)) { + ((uint8_t*restrict)&remainder)[1] = *dstbuf++ = *srcbuf++; + len--; + } +/* 16-bit words */ + count = len >> 1; + if (count) + { + if ((uintptr_t)srcbuf & 2) { + acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; + srcbuf = &srcbuf[ 2 ]; + dstbuf = &dstbuf[ 2 ]; + count--; + len -= 2; + } +/* 32-bit words */ + count >>= 1; + if (count) + { + uint32_t carry = 0; + while (count) { + acc += carry; + acc += ((uint32_t*restrict)dstbuf)[ 0 ] = ((const uint32_t*restrict)srcbuf)[ 0 ]; + carry = ((const uint32_t*restrict)dstbuf)[ 0 ] > acc; + srcbuf = &srcbuf[ 4 ]; + dstbuf = &dstbuf[ 4 ]; + count--; + } + acc += carry; + acc = (acc >> 16) + (acc & 0xffff); + } + if (len & 2) { + acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; + srcbuf = &srcbuf[ 2 ]; + dstbuf = &dstbuf[ 2 ]; + } + } +/* trailing odd byte */ + if (len & 1) { + ((uint8_t*restrict)&remainder)[0] = *dstbuf = *srcbuf; + } + acc += remainder; + acc = (acc >> 16) + (acc & 0xffff); + acc += (acc >> 16); + if (PGM_UNLIKELY(is_odd)) + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + return (uint16_t)acc; +} + +/* best if architecture has native 64-bit words + */ + +static +uint16_t +do_csum_64bit ( + const void* addr, + uint16_t len, + uint32_t csum + ) +{ + uint_fast64_t acc; + const uint8_t* buf; + uint16_t remainder; + uint_fast16_t count; + bool is_odd; + + acc = csum; + buf = (const uint8_t*)addr; + remainder = 0; + + if (PGM_UNLIKELY(len == 0)) + return (uint16_t)acc; + is_odd = ((uintptr_t)buf & 1); +/* align first byte */ + if (PGM_UNLIKELY(is_odd)) { + ((uint8_t*)&remainder)[1] = *buf++; + len--; + } +/* 16-bit words */ + count = len >> 1; + if (count) + { + if ((uintptr_t)buf & 2) { + acc += ((const uint16_t*)buf)[ 0 ]; + buf = &buf[ 2 ]; + count--; + len -= 2; + } +/* 32-bit words */ + count >>= 1; + if (count) + { + if ((uintptr_t)buf & 4) { + acc += ((const uint32_t*)buf)[ 0 ]; + buf = &buf[ 4 ]; + count--; + len -= 4; + } +/* 64-bit words */ + count >>= 1; + if (count) + { + uint_fast64_t carry = 0; + while (count) { + acc += carry; + acc += ((const uint64_t*)buf)[ 0 ]; + carry = ((const uint64_t*)buf)[ 0 ] > acc; + buf = &buf[ 8 ]; + count--; + } + acc += carry; + acc = (acc >> 32) + (acc & 0xffffffff); + } + if (len & 4) { + acc += ((const uint32_t*)buf)[ 0 ]; + buf = &buf[ 4 ]; + } + } + if (len & 2) { + acc += ((const uint16_t*)buf)[ 0 ]; + buf = &buf[ 2 ]; + } + } +/* trailing odd byte */ + if (len & 1) { + ((uint8_t*)&remainder)[0] = *buf; + } + acc += remainder; + acc = (acc >> 32) + (acc & 0xffffffff); + acc = (acc >> 16) + (acc & 0xffff); + acc = (acc >> 16) + (acc & 0xffff); + acc += (acc >> 16); + if (PGM_UNLIKELY(is_odd)) + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + return (uint16_t)acc; +} + +static +uint16_t +do_csumcpy_64bit ( + const void* restrict srcaddr, + void* restrict dstaddr, + uint16_t len, + uint32_t csum + ) +{ + uint_fast64_t acc; + const uint8_t* restrict srcbuf; + uint8_t* restrict dstbuf; + uint16_t remainder; + uint_fast16_t count; + bool is_odd; + + acc = csum; + srcbuf = (const uint8_t*restrict)srcaddr; + dstbuf = (uint8_t*restrict)dstaddr; + remainder = 0; + + if (PGM_UNLIKELY(len == 0)) + return (uint16_t)acc; + is_odd = ((uintptr_t)srcbuf & 1); +/* align first byte */ + if (PGM_UNLIKELY(is_odd)) { + ((uint8_t*restrict)&remainder)[1] = *dstbuf++ = *srcbuf++; + len--; + } +/* 16-bit words */ + count = len >> 1; + if (count) + { + if ((uintptr_t)srcbuf & 2) { + acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; + srcbuf = &srcbuf[ 2 ]; + dstbuf = &dstbuf[ 2 ]; + count--; + len -= 2; + } +/* 32-bit words */ + count >>= 1; + if (count) + { + if ((uintptr_t)srcbuf & 4) { + acc += ((uint32_t*restrict)dstbuf)[ 0 ] = ((const uint32_t*restrict)srcbuf)[ 0 ]; + srcbuf = &srcbuf[ 4 ]; + dstbuf = &dstbuf[ 4 ]; + count--; + len -= 4; + } +/* 64-bit words */ + count >>= 1; + if (count) + { +/* 64-byte blocks */ + uint_fast64_t carry = 0; + uint_fast16_t count64 = count >> 3; + if (count64) + { + carry = 0; + while (count64) { + acc += carry; + acc += ((uint64_t*restrict)dstbuf)[ 0 ] = ((const uint64_t*restrict)srcbuf)[ 0 ]; + carry = ((const uint64_t*restrict)dstbuf)[ 0 ] > acc; + acc += carry; + acc += ((uint64_t*restrict)dstbuf)[ 1 ] = ((const uint64_t*restrict)srcbuf)[ 1 ]; + carry = ((const uint64_t*restrict)dstbuf)[ 1 ] > acc; + acc += carry; + acc += ((uint64_t*restrict)dstbuf)[ 2 ] = ((const uint64_t*restrict)srcbuf)[ 2 ]; + carry = ((const uint64_t*restrict)dstbuf)[ 2 ] > acc; + acc += carry; + acc += ((uint64_t*restrict)dstbuf)[ 3 ] = ((const uint64_t*restrict)srcbuf)[ 3 ]; + carry = ((const uint64_t*restrict)dstbuf)[ 3 ] > acc; + acc += carry; + acc += ((uint64_t*restrict)dstbuf)[ 4 ] = ((const uint64_t*restrict)srcbuf)[ 4 ]; + carry = ((const uint64_t*restrict)dstbuf)[ 4 ] > acc; + acc += carry; + acc += ((uint64_t*restrict)dstbuf)[ 5 ] = ((const uint64_t*restrict)srcbuf)[ 5 ]; + carry = ((const uint64_t*restrict)dstbuf)[ 5 ] > acc; + acc += carry; + acc += ((uint64_t*restrict)dstbuf)[ 6 ] = ((const uint64_t*restrict)srcbuf)[ 6 ]; + carry = ((const uint64_t*restrict)dstbuf)[ 6 ] > acc; + acc += carry; + acc += ((uint64_t*restrict)dstbuf)[ 7 ] = ((const uint64_t*restrict)srcbuf)[ 7 ]; + carry = ((const uint64_t*restrict)dstbuf)[ 7 ] > acc; + srcbuf = &srcbuf[ 64 ]; + dstbuf = &dstbuf[ 64 ]; + count64--; + } + acc += carry; + acc = (acc >> 32) + (acc & 0xffffffff); + count %= 8; + } + +/* last 56 bytes */ + carry = 0; + while (count) { + acc += carry; + acc += ((uint64_t*restrict)dstbuf)[ 0 ] = ((const uint64_t*restrict)srcbuf)[ 0 ]; + carry = ((const uint64_t*restrict)dstbuf)[ 0 ] > acc; + srcbuf = &srcbuf[ 8 ]; + dstbuf = &dstbuf[ 8 ]; + count--; + } + acc += carry; + acc = (acc >> 32) + (acc & 0xffffffff); + } + if (len & 4) { + acc += ((uint32_t*restrict)dstbuf)[ 0 ] = ((const uint32_t*restrict)srcbuf)[ 0 ]; + srcbuf = &srcbuf[ 4 ]; + dstbuf = &dstbuf[ 4 ]; + } + } + if (len & 2) { + acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; + srcbuf = &srcbuf[ 2 ]; + dstbuf = &dstbuf[ 2 ]; + } + } +/* trailing odd byte */ + if (len & 1) { + ((uint8_t*restrict)&remainder)[0] = *dstbuf = *srcbuf; + } + acc += remainder; + acc = (acc >> 32) + (acc & 0xffffffff); + acc = (acc >> 16) + (acc & 0xffff); + acc = (acc >> 16) + (acc & 0xffff); + acc += (acc >> 16); + if (PGM_UNLIKELY(is_odd)) + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + return (uint16_t)acc; +} + +#if defined(__amd64) || defined(__x86_64__) +/* simd instructions unique to AMD/Intel 64-bit, so always little endian. + */ + +static +uint16_t +do_csum_vector ( + const void* addr, + uint16_t len, + uint32_t csum + ) +{ + uint64_t acc; /* fixed size for asm */ + const uint8_t* buf; + uint16_t remainder; /* fixed size for endian swap */ + uint_fast16_t count; + bool is_odd; + + acc = csum; + buf = (const uint8_t*)addr; + remainder = 0; + + if (PGM_UNLIKELY(len == 0)) + return (uint16_t)acc; +/* align first byte */ + is_odd = ((uintptr_t)buf & 1); + if (PGM_UNLIKELY(is_odd)) { + ((uint8_t*)&remainder)[1] = *buf++; + len--; + } +/* 16-bit words */ + count = len >> 1; + if (count) + { + if ((uintptr_t)buf & 2) { + acc += ((const uint16_t*)buf)[ 0 ]; + buf = &buf[ 2 ]; + count--; + len -= 2; + } +/* 32-bit words */ + count >>= 1; + if (count) + { + if ((uintptr_t)buf & 4) { + acc += ((const uint32_t*)buf)[ 0 ]; + buf = &buf[ 4 ]; + count--; + len -= 4; + } +/* 64-bit words */ + count >>= 1; + if (count) + { + uint64_t carry = 0; + while (count) { + asm volatile ( "addq %1, %0\n\t" + "adcq %2, %0" + : "=r" (acc) + : "m" (*(const uint64_t*)buf), "r" (carry), "0" (acc) + : "cc" ); + buf = &buf[ 8 ]; + count--; + } + acc += carry; + acc = (acc >> 32) + (acc & 0xffffffff); + } + if (len & 4) { + acc += ((const uint32_t*)buf)[ 0 ]; + buf = &buf[ 4 ]; + } + } + if (len & 2) { + acc += ((const uint16_t*)buf)[ 0 ]; + buf = &buf[ 2 ]; + } + } +/* trailing odd byte */ + if (len & 1) { + ((uint8_t*)&remainder)[0] = *buf; + } + acc += remainder; + acc = (acc >> 32) + (acc & 0xffffffff); + acc = (acc >> 16) + (acc & 0xffff); + acc = (acc >> 16) + (acc & 0xffff); + acc += (acc >> 16); + if (PGM_UNLIKELY(is_odd)) + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + return (uint16_t)acc; +} + +static +uint16_t +do_csumcpy_vector ( + const void* restrict srcaddr, + void* restrict dstaddr, + uint16_t len, + uint32_t csum + ) +{ + uint64_t acc; /* fixed size for asm */ + const uint8_t*restrict srcbuf; + uint8_t*restrict dstbuf; + uint16_t remainder; /* fixed size for endian swap */ + uint_fast16_t count; + bool is_odd; + + acc = csum; + srcbuf = (const uint8_t*restrict)srcaddr; + dstbuf = (uint8_t*restrict)dstaddr; + remainder = 0; + + if (PGM_UNLIKELY(len == 0)) + return (uint16_t)acc; +/* fill cache line with source buffer, invalidate destination buffer, + * perversly for testing high temporal locality is better than no locality, + * whilst in production no locality may be preferred depending on skb re-use. + */ + pgm_prefetch (srcbuf); + pgm_prefetchw (dstbuf); +/* align first byte */ + is_odd = ((uintptr_t)srcbuf & 1); + if (PGM_UNLIKELY(is_odd)) { + ((uint8_t*restrict)&remainder)[1] = *dstbuf++ = *srcbuf++; + len--; + } +/* 16-bit words */ + count = len >> 1; + if (count) + { + if ((uintptr_t)srcbuf & 2) { + acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; + srcbuf = &srcbuf[ 2 ]; + dstbuf = &dstbuf[ 2 ]; + count--; + len -= 2; + } +/* 32-bit words */ + count >>= 1; + if (count) + { + if ((uintptr_t)srcbuf & 4) { + acc += ((uint32_t*restrict)dstbuf)[ 0 ] = ((const uint32_t*restrict)srcbuf)[ 0 ]; + srcbuf = &srcbuf[ 4 ]; + dstbuf = &dstbuf[ 4 ]; + count--; + len -= 4; + } +/* 64-bit words */ + count >>= 1; + if (count) + { +/* 64-byte blocks */ + uint64_t carry = 0; + uint_fast16_t count64 = count >> 3; + + while (count64) + { + pgm_prefetch (&srcbuf[ 64 ]); + pgm_prefetchw (&dstbuf[ 64 ]); + asm volatile ( "movq 0*8(%1), %%r8\n\t" /* load */ + "movq 1*8(%1), %%r9\n\t" + "movq 2*8(%1), %%r10\n\t" + "movq 3*8(%1), %%r11\n\t" + "movq 4*8(%1), %%r12\n\t" + "movq 5*8(%1), %%r13\n\t" + "movq 6*8(%1), %%r14\n\t" + "movq 7*8(%1), %%r15\n\t" + "adcq %%r8, %0\n\t" /* checksum */ + "adcq %%r9, %0\n\t" + "adcq %%r10, %0\n\t" + "adcq %%r11, %0\n\t" + "adcq %%r12, %0\n\t" + "adcq %%r13, %0\n\t" + "adcq %%r14, %0\n\t" + "adcq %%r15, %0\n\t" + "adcq %3, %0\n\t" + "movq %%r8, 0*8(%2)\n\t" /* save */ + "movq %%r9, 1*8(%2)\n\t" + "movq %%r10, 2*8(%2)\n\t" + "movq %%r11, 3*8(%2)\n\t" + "movq %%r12, 4*8(%2)\n\t" + "movq %%r13, 5*8(%2)\n\t" + "movq %%r14, 6*8(%2)\n\t" + "movq %%r15, 7*8(%2)" + : "=r" (acc) + : "r" (srcbuf), "r" (dstbuf), "r" (carry), "0" (acc) + : "cc", "memory", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" ); + srcbuf = &srcbuf[ 64 ]; + dstbuf = &dstbuf[ 64 ]; + count64--; + } + count %= 8; +/* last 56 bytes */ + while (count) { + asm volatile ( "addq %1, %0\n\t" + "adcq %2, %0" + : "=r" (acc) + : "m" (*(const uint64_t*restrict)srcbuf), "r" (carry), "0" (acc) + : "cc" ); + srcbuf = &srcbuf[ 8 ]; + count--; + } + acc += carry; + acc = (acc >> 32) + (acc & 0xffffffff); + } + if (len & 4) { + acc += ((uint32_t*restrict)dstbuf)[ 0 ] = ((const uint32_t*restrict)srcbuf)[ 0 ]; + srcbuf = &srcbuf[ 4 ]; + dstbuf = &dstbuf[ 4 ]; + } + } + if (len & 2) { + acc += ((uint16_t*restrict)dstbuf)[ 0 ] = ((const uint16_t*restrict)srcbuf)[ 0 ]; + srcbuf = &srcbuf[ 2 ]; + dstbuf = &dstbuf[ 2 ]; + } + } +/* trailing odd byte */ + if (len & 1) { + ((uint8_t*restrict)&remainder)[0] = *dstbuf = *srcbuf; + } + acc += remainder; + acc = (acc >> 32) + (acc & 0xffffffff); + acc = (acc >> 16) + (acc & 0xffff); + acc = (acc >> 16) + (acc & 0xffff); + acc += (acc >> 16); + if (PGM_UNLIKELY(is_odd)) + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + return (uint16_t)acc; +} + +#endif + +static inline +uint16_t +do_csum ( + const void* addr, + uint16_t len, + uint32_t csum + ) +{ +#if defined(CONFIG_8BIT_CHECKSUM) + return do_csum_8bit (addr, len, csum); +#elif defined(CONFIG_16BIT_CHECKSUM) + return do_csum_16bit (addr, len, csum); +#elif defined(CONFIG_32BIT_CHECKSUM) + return do_csum_32bit (addr, len, csum); +#elif defined(CONFIG_64BIT_CHECKSUM) + return do_csum_64bit (addr, len, csum); +#elif defined(CONFIG_VECTOR_CHECKSUM) + return do_csum_vector (addr, len, csum); +#else +# error "checksum routine undefined" +#endif +} + +/* Calculate an IP header style checksum + */ + +uint16_t +pgm_inet_checksum ( + const void* addr, + uint16_t len, + uint16_t csum + ) +{ +/* pre-conditions */ + pgm_assert (NULL != addr); + + return ~do_csum (addr, len, csum); +} + +/* Calculate a partial (unfolded) checksum + */ + +uint32_t +pgm_compat_csum_partial ( + const void* addr, + uint16_t len, + uint32_t csum + ) +{ +/* pre-conditions */ + pgm_assert (NULL != addr); + + csum = (csum >> 16) + (csum & 0xffff); + csum += do_csum (addr, len, 0); + csum = (csum >> 16) + (csum & 0xffff); + + return csum; +} + +/* Calculate & copy a partial PGM checksum + */ + +uint32_t +pgm_compat_csum_partial_copy ( + const void* restrict src, + void* restrict dst, + uint16_t len, + uint32_t csum + ) +{ +/* pre-conditions */ + pgm_assert (NULL != src); + pgm_assert (NULL != dst); + +#if defined(CONFIG_8BIT_CHECKSUM) + return do_csumcpy_8bit (src, dst, len, csum); +#elif defined(CONFIG_16BIT_CHECKSUM) + return do_csumcpy_16bit (src, dst, len, csum); +#elif defined(CONFIG_32BIT_CHECKSUM) + return do_csumcpy_32bit (src, dst, len, csum); +#elif defined(CONFIG_64BIT_CHECKSUM) + return do_csumcpy_64bit (src, dst, len, csum); +#elif defined(CONFIG_VECTOR_CHECKSUM) + return do_csumcpy_vector (src, dst, len, csum); +#else + memcpy (dst, src, len); + return pgm_csum_partial (dst, len, csum); +#endif +} + +/* Fold 32 bit checksum accumulator into 16 bit final value. + */ + +uint16_t +pgm_csum_fold ( + uint32_t csum + ) +{ + csum = (csum >> 16) + (csum & 0xffff); + csum += (csum >> 16); + +/* handle special case of no checksum */ + return (uint16_t)(csum == 0xffff ? csum : ~csum); +} + +/* Add together two unfolded checksum accumulators + */ + +uint32_t +pgm_csum_block_add ( + uint32_t csum, + uint32_t csum2, + const uint16_t offset + ) +{ + if (offset & 1) /* byte magic on odd offset */ + csum2 = ((csum2 & 0xff00ff) << 8) + + ((csum2 >> 8) & 0xff00ff); + + csum += csum2; + return csum + (csum < csum2); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/checksum_perftest.c b/3rdparty/openpgm-svn-r1135/pgm/checksum_perftest.c new file mode 100644 index 0000000..678a066 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/checksum_perftest.c @@ -0,0 +1,807 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * performance tests for PGM checksum routines + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include +#include +#include + + +/* mock state */ + +static unsigned perf_testsize = 0; +static unsigned perf_answer = 0; + + +static +void +mock_setup_100b (void) +{ + perf_testsize = 100; + perf_answer = 0x6ea8; +} + +static +void +mock_setup_200b (void) +{ + perf_testsize = 200; + perf_answer = 0x86e1; +} + +static +void +mock_setup_1500b (void) +{ + perf_testsize = 1500; + perf_answer = 0xec69; +} + +static +void +mock_setup_9kb (void) +{ + perf_testsize = 9000; + perf_answer = 0x576e; +} + +static +void +mock_setup_64kb (void) +{ + perf_testsize = 65535; + perf_answer = 0x3fc0; +} + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + +#define CHECKSUM_DEBUG +#include "checksum.c" + + +static +void +mock_setup (void) +{ + g_assert (pgm_time_init (NULL)); +} + +static +void +mock_teardown (void) +{ + g_assert (pgm_time_shutdown ()); +} + + +/* target: + * guint16 + * pgm_inet_checksum ( + * const void* src, + * guint len, + * int csum + * ) + */ + +START_TEST (test_8bit) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + csum = ~do_csum_8bit (source, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("8-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +/* checksum + memcpy */ +START_TEST (test_8bit_memcpy) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + char* target = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + memcpy (target, source, perf_testsize); + csum = ~do_csum_8bit (target, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("8-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +/* checksum & copy */ +START_TEST (test_8bit_csumcpy) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + char* target = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + csum = ~do_csumcpy_8bit (source, target, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("8-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +START_TEST (test_16bit) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + csum = ~do_csum_16bit (source, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("16-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +START_TEST (test_16bit_memcpy) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + char* target = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + memcpy (target, source, perf_testsize); + csum = ~do_csum_16bit (target, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("16-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +START_TEST (test_16bit_csumcpy) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + char* target = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + csum = ~do_csumcpy_16bit (source, target, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("16-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +START_TEST (test_32bit) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + csum = ~do_csum_32bit (source, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("32-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +START_TEST (test_32bit_memcpy) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + char* target = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + memcpy (target, source, perf_testsize); + csum = ~do_csum_32bit (target, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("32-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +START_TEST (test_32bit_csumcpy) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + char* target = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + csum = ~do_csumcpy_32bit (source, target, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("32-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +START_TEST (test_64bit) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + csum = ~do_csum_64bit (source, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("64-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +START_TEST (test_64bit_memcpy) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + char* target = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + memcpy (target, source, perf_testsize); + csum = ~do_csum_64bit (target, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("64-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +START_TEST (test_64bit_csumcpy) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + char* target = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + csum = ~do_csumcpy_64bit (source, target, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("64-bit/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +#if defined(__amd64) || defined(__x86_64__) +START_TEST (test_vector) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + csum = ~do_csum_vector (source, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("vector/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +START_TEST (test_vector_memcpy) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + char* target = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + memcpy (target, source, perf_testsize); + csum = ~do_csum_vector (target, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("vector/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST + +START_TEST (test_vector_csumcpy) +{ + const unsigned iterations = 1000; + char* source = alloca (perf_testsize); + char* target = alloca (perf_testsize); + for (unsigned i = 0, j = 0; i < perf_testsize; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = perf_answer; /* network order */ + + guint16 csum; + pgm_time_t start, check; + + start = pgm_time_update_now(); + for (unsigned i = iterations; i; i--) { + csum = ~do_csumcpy_vector (source, target, perf_testsize, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + fail_unless (answer == csum, "checksum mismatch 0x%04x (0x%04x)", csum, answer); + } + + check = pgm_time_update_now(); + g_message ("vector/%u: elapsed time %" PGM_TIME_FORMAT " us, unit time %" PGM_TIME_FORMAT " us", + perf_testsize, + (guint64)(check - start), + (guint64)((check - start) / iterations)); +} +END_TEST +#endif /* defined(__amd64) || defined(__x86_64__) */ + + + +static +Suite* +make_csum_performance_suite (void) +{ + Suite* s; + + s = suite_create ("Raw checksum performance"); + + TCase* tc_100b = tcase_create ("100b"); + suite_add_tcase (s, tc_100b); + tcase_add_checked_fixture (tc_100b, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_100b, mock_setup_100b, NULL); + tcase_add_test (tc_100b, test_8bit); + tcase_add_test (tc_100b, test_16bit); + tcase_add_test (tc_100b, test_32bit); + tcase_add_test (tc_100b, test_64bit); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_100b, test_vector); +#endif + + TCase* tc_200b = tcase_create ("200b"); + suite_add_tcase (s, tc_200b); + tcase_add_checked_fixture (tc_200b, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_200b, mock_setup_200b, NULL); + tcase_add_test (tc_200b, test_8bit); + tcase_add_test (tc_200b, test_16bit); + tcase_add_test (tc_200b, test_32bit); + tcase_add_test (tc_200b, test_64bit); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_200b, test_vector); +#endif + + TCase* tc_1500b = tcase_create ("1500b"); + suite_add_tcase (s, tc_1500b); + tcase_add_checked_fixture (tc_1500b, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_1500b, mock_setup_1500b, NULL); + tcase_add_test (tc_1500b, test_8bit); + tcase_add_test (tc_1500b, test_16bit); + tcase_add_test (tc_1500b, test_32bit); + tcase_add_test (tc_1500b, test_64bit); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_1500b, test_vector); +#endif + + TCase* tc_9kb = tcase_create ("9KB"); + suite_add_tcase (s, tc_9kb); + tcase_add_checked_fixture (tc_9kb, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_9kb, mock_setup_9kb, NULL); + tcase_add_test (tc_9kb, test_8bit); + tcase_add_test (tc_9kb, test_16bit); + tcase_add_test (tc_9kb, test_32bit); + tcase_add_test (tc_9kb, test_64bit); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_9kb, test_vector); +#endif + + TCase* tc_64kb = tcase_create ("64KB"); + suite_add_tcase (s, tc_64kb); + tcase_add_checked_fixture (tc_64kb, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_64kb, mock_setup_64kb, NULL); + tcase_add_test (tc_64kb, test_8bit); + tcase_add_test (tc_64kb, test_16bit); + tcase_add_test (tc_64kb, test_32bit); + tcase_add_test (tc_64kb, test_64bit); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_64kb, test_vector); +#endif + + return s; +} + +static +Suite* +make_csum_memcpy_performance_suite (void) +{ + Suite* s; + + s = suite_create ("Checksum and memcpy performance"); + + TCase* tc_100b = tcase_create ("100b"); + suite_add_tcase (s, tc_100b); + tcase_add_checked_fixture (tc_100b, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_100b, mock_setup_100b, NULL); + tcase_add_test (tc_100b, test_8bit_memcpy); + tcase_add_test (tc_100b, test_16bit_memcpy); + tcase_add_test (tc_100b, test_32bit_memcpy); + tcase_add_test (tc_100b, test_64bit_memcpy); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_100b, test_vector_memcpy); +#endif + + TCase* tc_200b = tcase_create ("200b"); + suite_add_tcase (s, tc_200b); + tcase_add_checked_fixture (tc_200b, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_200b, mock_setup_200b, NULL); + tcase_add_test (tc_200b, test_8bit_memcpy); + tcase_add_test (tc_200b, test_16bit_memcpy); + tcase_add_test (tc_200b, test_32bit_memcpy); + tcase_add_test (tc_200b, test_64bit_memcpy); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_200b, test_vector_memcpy); +#endif + + TCase* tc_1500b = tcase_create ("1500b"); + suite_add_tcase (s, tc_1500b); + tcase_add_checked_fixture (tc_1500b, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_1500b, mock_setup_1500b, NULL); + tcase_add_test (tc_1500b, test_8bit_memcpy); + tcase_add_test (tc_1500b, test_16bit_memcpy); + tcase_add_test (tc_1500b, test_32bit_memcpy); + tcase_add_test (tc_1500b, test_64bit_memcpy); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_1500b, test_vector_memcpy); +#endif + + TCase* tc_9kb = tcase_create ("9KB"); + suite_add_tcase (s, tc_9kb); + tcase_add_checked_fixture (tc_9kb, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_9kb, mock_setup_9kb, NULL); + tcase_add_test (tc_9kb, test_8bit_memcpy); + tcase_add_test (tc_9kb, test_16bit_memcpy); + tcase_add_test (tc_9kb, test_32bit_memcpy); + tcase_add_test (tc_9kb, test_64bit_memcpy); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_9kb, test_vector_memcpy); +#endif + + TCase* tc_64kb = tcase_create ("64KB"); + suite_add_tcase (s, tc_64kb); + tcase_add_checked_fixture (tc_64kb, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_64kb, mock_setup_64kb, NULL); + tcase_add_test (tc_64kb, test_8bit_memcpy); + tcase_add_test (tc_64kb, test_16bit_memcpy); + tcase_add_test (tc_64kb, test_32bit_memcpy); + tcase_add_test (tc_64kb, test_64bit_memcpy); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_64kb, test_vector_memcpy); +#endif + + return s; +} + +static +Suite* +make_csumcpy_performance_suite (void) +{ + Suite* s; + + s = suite_create ("Checksum copy performance"); + + TCase* tc_100b = tcase_create ("100b"); + suite_add_tcase (s, tc_100b); + tcase_add_checked_fixture (tc_100b, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_100b, mock_setup_100b, NULL); + tcase_add_test (tc_100b, test_8bit_csumcpy); + tcase_add_test (tc_100b, test_16bit_csumcpy); + tcase_add_test (tc_100b, test_32bit_csumcpy); + tcase_add_test (tc_100b, test_64bit_csumcpy); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_100b, test_vector_csumcpy); +#endif + + TCase* tc_200b = tcase_create ("200b"); + suite_add_tcase (s, tc_200b); + tcase_add_checked_fixture (tc_200b, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_200b, mock_setup_200b, NULL); + tcase_add_test (tc_200b, test_8bit_csumcpy); + tcase_add_test (tc_200b, test_16bit_csumcpy); + tcase_add_test (tc_200b, test_32bit_csumcpy); + tcase_add_test (tc_200b, test_64bit_csumcpy); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_200b, test_vector_csumcpy); +#endif + + TCase* tc_1500b = tcase_create ("1500b"); + suite_add_tcase (s, tc_1500b); + tcase_add_checked_fixture (tc_1500b, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_1500b, mock_setup_1500b, NULL); + tcase_add_test (tc_1500b, test_8bit_csumcpy); + tcase_add_test (tc_1500b, test_16bit_csumcpy); + tcase_add_test (tc_1500b, test_32bit_csumcpy); + tcase_add_test (tc_1500b, test_64bit_csumcpy); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_1500b, test_vector_csumcpy); +#endif + + TCase* tc_9kb = tcase_create ("9KB"); + suite_add_tcase (s, tc_9kb); + tcase_add_checked_fixture (tc_9kb, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_9kb, mock_setup_9kb, NULL); + tcase_add_test (tc_9kb, test_8bit_csumcpy); + tcase_add_test (tc_9kb, test_16bit_csumcpy); + tcase_add_test (tc_9kb, test_32bit_csumcpy); + tcase_add_test (tc_9kb, test_64bit_csumcpy); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_9kb, test_vector_csumcpy); +#endif + + TCase* tc_64kb = tcase_create ("64KB"); + suite_add_tcase (s, tc_64kb); + tcase_add_checked_fixture (tc_64kb, mock_setup, mock_teardown); + tcase_add_checked_fixture (tc_64kb, mock_setup_64kb, NULL); + tcase_add_test (tc_64kb, test_8bit_csumcpy); + tcase_add_test (tc_64kb, test_16bit_csumcpy); + tcase_add_test (tc_64kb, test_32bit_csumcpy); + tcase_add_test (tc_64kb, test_64bit_csumcpy); +#if defined(__amd64) || defined(__x86_64__) + tcase_add_test (tc_64kb, test_vector_csumcpy); +#endif + + return s; +} + + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_csum_performance_suite ()); + srunner_add_suite (sr, make_csum_memcpy_performance_suite ()); + srunner_add_suite (sr, make_csumcpy_performance_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/checksum_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/checksum_unittest.c new file mode 100644 index 0000000..a25d36a --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/checksum_unittest.c @@ -0,0 +1,278 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for PGM checksum routines + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include + + +/* mock state */ + + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + + +#define CHECKSUM_DEBUG +#include "checksum.c" + + +/* target: + * uint16_t + * pgm_inet_checksum ( + * const void* src, + * uint16_t len, + * uint16_t csum + * ) + */ + +START_TEST (test_inet_pass_001) +{ + const char source[] = "i am not a string"; + const guint16 answer = 0x1fda; /* network order */ + + guint16 csum = pgm_inet_checksum (source, sizeof(source), 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + g_message ("IP checksum of \"%s\" (%d) is %u (%u)", + source, sizeof(source), csum, answer); + + fail_unless (answer == csum, "checksum mismatch"); +} +END_TEST + +START_TEST (test_inet_pass_002) +{ + char* source = alloca (65535); + for (unsigned i = 0, j = 0; i < 65535; i++) { + j = j * 1103515245 + 12345; + source[i] = j; + } + const guint16 answer = 0x3fc0; /* network order */ + + guint16 csum = pgm_inet_checksum (source, 65535, 0); +/* function calculates answer in host order */ + csum = g_htons (csum); + g_message ("IP checksum of 64KB is %u (%u)", + csum, answer); + + fail_unless (answer == csum, "checksum mismatch"); +} +END_TEST + +START_TEST (test_inet_fail_001) +{ + pgm_inet_checksum (NULL, 0, 0); + fail ("reached"); +} +END_TEST + +/* target: + * guint16 + * pgm_csum_fold ( + * guint32 csum + * ) + */ + +START_TEST (test_fold_pass_001) +{ + const guint32 csum = 0xdd250300; /* network order */ + const guint16 answer = 0x1fda; + + guint16 folded_csum = pgm_csum_fold (g_ntohl (csum)); + folded_csum = g_htons (folded_csum); + g_message ("32-bit checksum %u folds into %u (%u)", csum, folded_csum, answer); + + fail_unless (answer == folded_csum, "folded checksum mismatch"); +} +END_TEST + + +/* target: + * guint32 + * pgm_csum_partial ( + * const void* src, + * guint len, + * guint32 sum + * ) + */ + +START_TEST (test_partial_pass_001) +{ + const char source[] = "i am not a string"; +#if __BYTE_ORDER == __BIG_ENDIAN + const guint32 answer = 0x0000e025; /* network order */ +#elif __BYTE_ORDER == __LITTLE_ENDIAN + const guint32 answer = 0xe0250000; /* network order */ +#else +# error "__BYTE_ORDER not supported." +#endif + + guint32 csum = pgm_csum_partial (source, sizeof(source), 0); + csum = g_htonl (csum); + g_message ("Partial checksum of \"%s\" is %u (%u)", source, csum, answer); + + fail_unless (answer == csum, "checksum mismatch"); +} +END_TEST + +START_TEST (test_partial_fail_001) +{ + pgm_csum_partial (NULL, 0, 0); + fail ("reached"); +} +END_TEST + +/* target: + * guint32 + * pgm_csum_partial_copy ( + * const void* src, + * void* dst, + * guint len, + * guint32 sum + * ) + */ + +START_TEST (test_partial_copy_pass_001) +{ + const char source[] = "i am not a string"; +#if __BYTE_ORDER == __BIG_ENDIAN + const guint32 answer = 0x0000e025; /* network order */ +#elif __BYTE_ORDER == __LITTLE_ENDIAN + const guint32 answer = 0xe0250000; /* network order */ +#else +# error "__BYTE_ORDER not supported." +#endif + + char dest[1024]; + guint32 csum_source = pgm_csum_partial_copy (source, dest, sizeof(source), 0); + csum_source = g_htonl (csum_source); + guint32 csum_dest = pgm_csum_partial (dest, sizeof(source), 0); + csum_dest = g_htonl (csum_dest); + g_message ("Partial copy checksum of \"%s\" is %u, partial checksum is %u (%u)", + source, csum_source, csum_dest, answer); + fail_unless (answer == csum_source, "checksum mismatch in partial-copy"); + fail_unless (answer == csum_dest, "checksum mismatch in partial"); +} +END_TEST + +START_TEST (test_partial_copy_fail_001) +{ + pgm_csum_partial_copy (NULL, NULL, 0, 0); + fail ("reached"); +} +END_TEST + +/* target: + * guint32 + * pgm_csum_block_add ( + * guint32 csum, + * guint32 csum2, + * guint offset + * ) + */ + +START_TEST (test_block_add_pass_001) +{ + const char source[] = "i am not a string"; + const guint16 answer = 0x1fda; /* network order */ + + guint32 csum_a = pgm_csum_partial (source, sizeof(source) / 2, 0); + guint32 csum_b = pgm_csum_partial (source + (sizeof(source) / 2), sizeof(source) - (sizeof(source) / 2), 0); + guint32 csum = pgm_csum_block_add (csum_a, csum_b, sizeof(source) / 2); + guint16 fold = pgm_csum_fold (csum); +/* convert to display in network order */ + csum_a = g_htonl (csum_a); + csum_b = g_htonl (csum_b); + csum = g_htonl (csum); + fold = g_htons (fold); + g_message ("Checksum A:%u + B:%u = %u -> %u (%u)", + csum_a, csum_b, csum, fold, answer); + fail_unless (answer == fold, "checksum mismatch"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_inet = tcase_create ("inet"); + suite_add_tcase (s, tc_inet); + tcase_add_test (tc_inet, test_inet_pass_001); + tcase_add_test (tc_inet, test_inet_pass_002); + tcase_add_test_raise_signal (tc_inet, test_inet_fail_001, SIGABRT); + + TCase* tc_fold = tcase_create ("fold"); + suite_add_tcase (s, tc_fold); + tcase_add_test (tc_fold, test_fold_pass_001); + + TCase* tc_block_add = tcase_create ("block-add"); + suite_add_tcase (s, tc_block_add); + tcase_add_test (tc_block_add, test_block_add_pass_001); + + TCase* tc_partial = tcase_create ("partial"); + suite_add_tcase (s, tc_partial); + tcase_add_test (tc_partial, test_partial_pass_001); + tcase_add_test_raise_signal (tc_partial, test_partial_fail_001, SIGABRT); + + TCase* tc_partial_copy = tcase_create ("partial-copy"); + suite_add_tcase (s, tc_partial_copy); + tcase_add_test (tc_partial_copy, test_partial_copy_pass_001); + tcase_add_test_raise_signal (tc_partial_copy, test_partial_copy_fail_001, SIGABRT); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/crossmingw.py b/3rdparty/openpgm-svn-r1135/pgm/crossmingw.py new file mode 100644 index 0000000..2981506 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/crossmingw.py @@ -0,0 +1,144 @@ +import os +import os.path +import string + +import SCons.Action +import SCons.Builder +import SCons.Tool +import SCons.Util + +# This is what we search for to find mingw: +prefixes = SCons.Util.Split(""" + mingw32- + i386-mingw32msvc- + i486-mingw32msvc- + i586-mingw32msvc- + i686-mingw32msvc- +""") + +def find(env): + for prefix in prefixes: + # First search in the SCons path and then the OS path: + if env.WhereIs(prefix + 'gcc') or SCons.Util.WhereIs(prefix + 'gcc'): + return prefix + + return '' + +def shlib_generator(target, source, env, for_signature): + cmd = SCons.Util.CLVar(['$SHLINK', '$SHLINKFLAGS']) + + dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') + if dll: cmd.extend(['-o', dll]) + + cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS']) + + implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') + if implib: cmd.append('-Wl,--out-implib,'+implib.get_string(for_signature)) + + def_target = env.FindIxes(target, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX') + if def_target: cmd.append('-Wl,--output-def,'+def_target.get_string(for_signature)) + + return [cmd] + +def shlib_emitter(target, source, env): + dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') + no_import_lib = env.get('no_import_lib', 0) + + if not dll: + raise SCons.Errors.UserError, "A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX") + + if not no_import_lib and \ + not env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX'): + + # Append an import library to the list of targets. + target.append(env.ReplaceIxes(dll, + 'SHLIBPREFIX', 'SHLIBSUFFIX', + 'LIBPREFIX', 'LIBSUFFIX')) + + # Append a def file target if there isn't already a def file target + # or a def file source. There is no option to disable def file + # target emitting, because I can't figure out why someone would ever + # want to turn it off. + def_source = env.FindIxes(source, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX') + def_target = env.FindIxes(target, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX') + if not def_source and not def_target: + target.append(env.ReplaceIxes(dll, + 'SHLIBPREFIX', 'SHLIBSUFFIX', + 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX')) + + return (target, source) + +# TODO: Backported to old scons version +#shlib_action = SCons.Action.CommandGenerator(shlib_generator) +shlib_action = SCons.Action.Action(shlib_generator,generator=1) + +res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR') + +res_builder = SCons.Builder.Builder(action=res_action, suffix='.o', + source_scanner=SCons.Tool.SourceFileScanner) +SCons.Tool.SourceFileScanner.add_scanner('.rc', SCons.Defaults.CScan) + +def generate(env): + mingw_prefix = find(env) + + if mingw_prefix: + dir = os.path.dirname(env.WhereIs(mingw_prefix + 'gcc') or SCons.Util.WhereIs(mingw_prefix + 'gcc')) + + # The mingw bin directory must be added to the path: + path = env['ENV'].get('PATH', []) + if not path: + path = [] + if SCons.Util.is_String(path): + path = string.split(path, os.pathsep) + + env['ENV']['PATH'] = string.join([dir] + path, os.pathsep) + + # Most of mingw is the same as gcc and friends... + gnu_tools = ['gcc', 'g++', 'gnulink', 'ar', 'gas'] + for tool in gnu_tools: + SCons.Tool.Tool(tool)(env) + + #... but a few things differ: + env['CC'] = mingw_prefix + 'gcc' + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') + env['CXX'] = mingw_prefix + 'g++' + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') + env['SHLINKCOM'] = shlib_action + env.Append(SHLIBEMITTER = [shlib_emitter]) + # This line isn't required and breaks C++ linking + #env['LINK'] = mingw_prefix + 'g++' + env['AS'] = mingw_prefix + 'as' + env['AR'] = mingw_prefix + 'ar' + env['RANLIB'] = mingw_prefix + 'ranlib' + env['WIN32DEFPREFIX'] = '' + env['WIN32DEFSUFFIX'] = '.def' + env['SHOBJSUFFIX'] = '.o' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 + + env['RC'] = mingw_prefix + 'windres' + env['RCFLAGS'] = SCons.Util.CLVar('') + env['RCINCFLAGS'] = '$( ${_concat(RCINCPREFIX, CPPPATH, RCINCSUFFIX, __env__, RDirs, TARGET)} $)' + env['RCINCPREFIX'] = '--include-dir ' + env['RCINCSUFFIX'] = '' + env['RCCOM'] = '$RC $RCINCFLAGS $RCINCPREFIX $SOURCE.dir $RCFLAGS -i $SOURCE -o $TARGET' + env['BUILDERS']['RES'] = res_builder + + # Some setting from the platform also have to be overridden: + env['OBJPREFIX'] = '' + env['OBJSUFFIX'] = '.o' + env['LIBPREFIX'] = 'lib' + env['LIBSUFFIX'] = '.a' + env['SHOBJPREFIX'] = '$OBJPREFIX' + env['SHOBJSUFFIX'] = '$OBJSUFFIX' + env['PROGPREFIX'] = '' + env['PROGSUFFIX'] = '.exe' + env['LIBPREFIX'] = '' + env['LIBSUFFIX'] = '.lib' + env['SHLIBPREFIX'] = '' + env['SHLIBSUFFIX'] = '.dll' + env['LIBPREFIXES'] = [ '$LIBPREFIX' ] + env['LIBSUFFIXES'] = [ '$LIBSUFFIX' ] + +def exists(env): + return find(env) diff --git a/3rdparty/openpgm-svn-r1135/pgm/crossmingw64.py b/3rdparty/openpgm-svn-r1135/pgm/crossmingw64.py new file mode 100644 index 0000000..111e0ed --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/crossmingw64.py @@ -0,0 +1,140 @@ +import os +import os.path +import string + +import SCons.Action +import SCons.Builder +import SCons.Tool +import SCons.Util + +# This is what we search for to find mingw: +prefixes = SCons.Util.Split(""" + x86_64-w64-mingw32- +""") + +def find(env): + for prefix in prefixes: + # First search in the SCons path and then the OS path: + if env.WhereIs(prefix + 'gcc') or SCons.Util.WhereIs(prefix + 'gcc'): + return prefix + + return '' + +def shlib_generator(target, source, env, for_signature): + cmd = SCons.Util.CLVar(['$SHLINK', '$SHLINKFLAGS']) + + dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') + if dll: cmd.extend(['-o', dll]) + + cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS']) + + implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') + if implib: cmd.append('-Wl,--out-implib,'+implib.get_string(for_signature)) + + def_target = env.FindIxes(target, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX') + if def_target: cmd.append('-Wl,--output-def,'+def_target.get_string(for_signature)) + + return [cmd] + +def shlib_emitter(target, source, env): + dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') + no_import_lib = env.get('no_import_lib', 0) + + if not dll: + raise SCons.Errors.UserError, "A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX") + + if not no_import_lib and \ + not env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX'): + + # Append an import library to the list of targets. + target.append(env.ReplaceIxes(dll, + 'SHLIBPREFIX', 'SHLIBSUFFIX', + 'LIBPREFIX', 'LIBSUFFIX')) + + # Append a def file target if there isn't already a def file target + # or a def file source. There is no option to disable def file + # target emitting, because I can't figure out why someone would ever + # want to turn it off. + def_source = env.FindIxes(source, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX') + def_target = env.FindIxes(target, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX') + if not def_source and not def_target: + target.append(env.ReplaceIxes(dll, + 'SHLIBPREFIX', 'SHLIBSUFFIX', + 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX')) + + return (target, source) + +# TODO: Backported to old scons version +#shlib_action = SCons.Action.CommandGenerator(shlib_generator) +shlib_action = SCons.Action.Action(shlib_generator,generator=1) + +res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR') + +res_builder = SCons.Builder.Builder(action=res_action, suffix='.o', + source_scanner=SCons.Tool.SourceFileScanner) +SCons.Tool.SourceFileScanner.add_scanner('.rc', SCons.Defaults.CScan) + +def generate(env): + mingw_prefix = find(env) + + if mingw_prefix: + dir = os.path.dirname(env.WhereIs(mingw_prefix + 'gcc') or SCons.Util.WhereIs(mingw_prefix + 'gcc')) + + # The mingw bin directory must be added to the path: + path = env['ENV'].get('PATH', []) + if not path: + path = [] + if SCons.Util.is_String(path): + path = string.split(path, os.pathsep) + + env['ENV']['PATH'] = string.join([dir] + path, os.pathsep) + + # Most of mingw is the same as gcc and friends... + gnu_tools = ['gcc', 'g++', 'gnulink', 'ar', 'gas'] + for tool in gnu_tools: + SCons.Tool.Tool(tool)(env) + + #... but a few things differ: + env['CC'] = mingw_prefix + 'gcc' + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') + env['CXX'] = mingw_prefix + 'g++' + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') + env['SHLINKCOM'] = shlib_action + env.Append(SHLIBEMITTER = [shlib_emitter]) + # This line isn't required and breaks C++ linking + #env['LINK'] = mingw_prefix + 'g++' + env['AS'] = mingw_prefix + 'as' + env['AR'] = mingw_prefix + 'ar' + env['RANLIB'] = mingw_prefix + 'ranlib' + env['WIN32DEFPREFIX'] = '' + env['WIN32DEFSUFFIX'] = '.def' + env['SHOBJSUFFIX'] = '.o' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 + + env['RC'] = mingw_prefix + 'windres' + env['RCFLAGS'] = SCons.Util.CLVar('') + env['RCINCFLAGS'] = '$( ${_concat(RCINCPREFIX, CPPPATH, RCINCSUFFIX, __env__, RDirs, TARGET)} $)' + env['RCINCPREFIX'] = '--include-dir ' + env['RCINCSUFFIX'] = '' + env['RCCOM'] = '$RC $RCINCFLAGS $RCINCPREFIX $SOURCE.dir $RCFLAGS -i $SOURCE -o $TARGET' + env['BUILDERS']['RES'] = res_builder + + # Some setting from the platform also have to be overridden: + env['OBJPREFIX'] = '' + env['OBJSUFFIX'] = '.o' + env['LIBPREFIX'] = 'lib' + env['LIBSUFFIX'] = '.a' + env['SHOBJPREFIX'] = '$OBJPREFIX' + env['SHOBJSUFFIX'] = '$OBJSUFFIX' + env['PROGPREFIX'] = '' + env['PROGSUFFIX'] = '.exe' + env['LIBPREFIX'] = '' + env['LIBSUFFIX'] = '.lib' + env['SHLIBPREFIX'] = '' + env['SHLIBSUFFIX'] = '.dll' + env['LIBPREFIXES'] = [ '$LIBPREFIX' ] + env['LIBSUFFIXES'] = [ '$LIBSUFFIX' ] + +def exists(env): + return find(env) diff --git a/3rdparty/openpgm-svn-r1135/pgm/engine.c b/3rdparty/openpgm-svn-r1135/pgm/engine.c new file mode 100644 index 0000000..dbe50b4 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/engine.c @@ -0,0 +1,282 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM engine. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* getprotobyname_r */ +#ifndef _BSD_SOURCE +# define _BSD_SOURCE 1 +#endif + +#ifndef _WIN32 +# include +#endif +#include +#include +#include +#include +#include +#include +#include + + +//#define ENGINE_DEBUG + + +/* globals */ +int pgm_ipproto_pgm PGM_GNUC_READ_MOSTLY = IPPROTO_PGM; + +#ifdef _WIN32 +LPFN_WSARECVMSG pgm_WSARecvMsg PGM_GNUC_READ_MOSTLY = NULL; +#endif + +#ifdef PGM_DEBUG +unsigned pgm_loss_rate PGM_GNUC_READ_MOSTLY = 0; +#endif + +/* locals */ +static bool pgm_is_supported = FALSE; +static volatile uint32_t pgm_ref_count = 0; + +#ifdef _WIN32 +# ifndef WSAID_WSARECVMSG +/* http://cvs.winehq.org/cvsweb/wine/include/mswsock.h */ +# define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}} +# endif +#endif + + +/* startup PGM engine, mainly finding PGM protocol definition, if any from NSS + * + * returns TRUE on success, returns FALSE if an error occurred, implying some form of + * system re-configuration is required to resolve before trying again. + * + * NB: Valgrind loves generating errors in getprotobyname(). + */ +bool +pgm_init ( + pgm_error_t** error + ) +{ + if (pgm_atomic_exchange_and_add32 (&pgm_ref_count, 1) > 0) + return TRUE; + +/* initialise dependent modules */ + pgm_messages_init(); + + pgm_minor ("OpenPGM %d.%d.%d%s%s%s %s %s %s %s", + pgm_major_version, pgm_minor_version, pgm_micro_version, + pgm_build_revision ? " (" : "", pgm_build_revision ? pgm_build_revision : "", pgm_build_revision ? ")" : "", + pgm_build_date, pgm_build_time, pgm_build_system, pgm_build_machine); + + pgm_thread_init(); + pgm_mem_init(); + pgm_rand_init(); + +#ifdef _WIN32 + WORD wVersionRequested = MAKEWORD (2, 2); + WSADATA wsaData; + if (WSAStartup (wVersionRequested, &wsaData) != 0) + { + const int save_errno = WSAGetLastError (); + pgm_set_error (error, + PGM_ERROR_DOMAIN_ENGINE, + pgm_error_from_wsa_errno (save_errno), + _("WSAStartup failure: %s"), + pgm_wsastrerror (save_errno)); + goto err_shutdown; + } + + if (LOBYTE (wsaData.wVersion) != 2 || HIBYTE (wsaData.wVersion) != 2) + { + WSACleanup(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_ENGINE, + PGM_ERROR_FAILED, + _("WSAStartup failed to provide requested version 2.2.")); + goto err_shutdown; + } + +# ifndef CONFIG_TARGET_WINE +/* find WSARecvMsg API */ + if (NULL == pgm_WSARecvMsg) { + GUID WSARecvMsg_GUID = WSAID_WSARECVMSG; + DWORD cbBytesReturned; + const SOCKET sock = socket (AF_INET, SOCK_DGRAM, 0); + if (SOCKET_ERROR == sock) { + WSACleanup(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_ENGINE, + PGM_ERROR_FAILED, + _("Cannot open socket.")); + goto err_shutdown; + } + if (SOCKET_ERROR == WSAIoctl (sock, + SIO_GET_EXTENSION_FUNCTION_POINTER, + &WSARecvMsg_GUID, sizeof(WSARecvMsg_GUID), + &pgm_WSARecvMsg, sizeof(pgm_WSARecvMsg), + &cbBytesReturned, + NULL, + NULL)) + { + closesocket (sock); + WSACleanup(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_ENGINE, + PGM_ERROR_FAILED, + _("WSARecvMsg function not found.")); + goto err_shutdown; + } + pgm_debug ("Retrieved address of WSARecvMsg."); + closesocket (sock); + } +# endif +#endif /* _WIN32 */ + +/* find PGM protocol id overriding default value, use first value from NIS */ +#ifdef CONFIG_HAVE_GETPROTOBYNAME_R + char b[1024]; + struct protoent protobuf; + const struct protoent* proto = getprotobyname_r ("pgm", &protobuf, b, sizeof(b)); + if (NULL != proto) { + if (proto->p_proto != pgm_ipproto_pgm) { + pgm_minor (_("Setting PGM protocol number to %i from /etc/protocols."), + proto->p_proto); + pgm_ipproto_pgm = proto->p_proto; + } + } +#elif defined(CONFIG_HAVE_GETPROTOBYNAME_R2) + char b[1024]; + struct protoent protobuf, *proto; + const int e = getprotobyname_r ("pgm", &protobuf, b, sizeof(b), &proto); + if (e != -1 && proto != NULL) { + if (proto->p_proto != pgm_ipproto_pgm) { + pgm_minor (_("Setting PGM protocol number to %i from /etc/protocols."), + proto->p_proto); + pgm_ipproto_pgm = proto->p_proto; + } + } +#else + const struct protoent *proto = getprotobyname ("pgm"); + if (proto != NULL) { + if (proto->p_proto != pgm_ipproto_pgm) { +#ifndef _WIN32 + pgm_minor (_("Setting PGM protocol number to %i from /etc/protocols."), + proto->p_proto); +#else + pgm_minor (_("Setting PGM protocol number to %i from %%SYSTEMROOT%%\\system32\\drivers\\etc\\protocols."), + proto->p_proto); +#endif + pgm_ipproto_pgm = proto->p_proto; + } + } +#endif + +/* ensure timing enabled */ + pgm_error_t* sub_error = NULL; + if (!pgm_time_init (&sub_error)) { + if (sub_error) + pgm_propagate_error (error, sub_error); +#ifdef _WIN32 + WSACleanup(); +#endif + goto err_shutdown; + } + +/* receiver simulated loss rate */ +#ifdef PGM_DEBUG + const char *loss_rate = getenv ("PGM_LOSS_RATE"); + if (NULL != loss_rate) { + int value = atoi (loss_rate); + if (value > 0 && value <= 100) { + pgm_loss_rate = value; + pgm_minor (_("Setting PGM packet loss rate to %i%%."), pgm_loss_rate); + } + } +#endif + +/* create global sock list lock */ + pgm_rwlock_init (&pgm_sock_list_lock); + + pgm_is_supported = TRUE; + return TRUE; + +err_shutdown: + pgm_rand_shutdown(); + pgm_mem_shutdown(); + pgm_thread_shutdown(); + pgm_messages_shutdown(); + pgm_atomic_dec32 (&pgm_ref_count); + return FALSE; +} + +/* returns TRUE if PGM engine has been initialized + */ + +bool +pgm_supported (void) +{ + return ( pgm_is_supported == TRUE ); +} + +/* returns TRUE on success, returns FALSE if an error occurred. + */ + +bool +pgm_shutdown (void) +{ + pgm_return_val_if_fail (pgm_atomic_read32 (&pgm_ref_count) > 0, FALSE); + + if (pgm_atomic_exchange_and_add32 (&pgm_ref_count, (uint32_t)-1) != 1) + return TRUE; + + pgm_is_supported = FALSE; + +/* destroy all open socks */ + while (pgm_sock_list) { + pgm_close ((pgm_sock_t*)pgm_sock_list->data, FALSE); + } + + pgm_time_shutdown(); + +#ifdef _WIN32 + WSACleanup(); +#endif + + pgm_rand_shutdown(); + pgm_mem_shutdown(); + pgm_thread_shutdown(); + pgm_messages_shutdown(); + return TRUE; +} + +/* helper to drop out of setuid 0 after creating PGM sockets + */ +void +pgm_drop_superuser (void) +{ +#ifndef _WIN32 + if (0 == getuid()) { + setuid((gid_t)65534); + setgid((uid_t)65534); + } +#endif +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/engine.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/engine.c.c89.patch new file mode 100644 index 0000000..30021c2 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/engine.c.c89.patch @@ -0,0 +1,58 @@ +--- engine.c 2010-05-23 15:43:35.000000000 +0800 ++++ engine.c89 2010-08-04 10:30:53.000000000 +0800 +@@ -85,6 +85,7 @@ + pgm_rand_init(); + + #ifdef _WIN32 ++ { + WORD wVersionRequested = MAKEWORD (2, 2); + WSADATA wsaData; + if (WSAStartup (wVersionRequested, &wsaData) != 0) +@@ -142,6 +143,7 @@ + closesocket (sock); + } + # endif ++ } + #endif /* _WIN32 */ + + /* find PGM protocol id overriding default value, use first value from NIS */ +@@ -168,6 +170,7 @@ + } + } + #else ++ { + const struct protoent *proto = getprotobyname ("pgm"); + if (proto != NULL) { + if (proto->p_proto != pgm_ipproto_pgm) { +@@ -181,9 +184,11 @@ + pgm_ipproto_pgm = proto->p_proto; + } + } ++ } + #endif + + /* ensure timing enabled */ ++ { + pgm_error_t* sub_error = NULL; + if (!pgm_time_init (&sub_error)) { + if (sub_error) +@@ -193,9 +198,11 @@ + #endif + goto err_shutdown; + } ++ } + + /* receiver simulated loss rate */ + #ifdef PGM_DEBUG ++ { + const char *loss_rate = getenv ("PGM_LOSS_RATE"); + if (NULL != loss_rate) { + int value = atoi (loss_rate); +@@ -204,6 +211,7 @@ + pgm_minor (_("Setting PGM packet loss rate to %i%%."), pgm_loss_rate); + } + } ++ } + #endif + + /* create global sock list lock */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/engine_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/engine_unittest.c new file mode 100644 index 0000000..13b448c --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/engine_unittest.c @@ -0,0 +1,234 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for PGM engine. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* getprotobyname_r */ +#define _BSD_SOURCE 1 + +#include +#include +#include +#include +#include + + +/* mock state */ + +struct pgm_rwlock_t; +struct pgm_slist_t; + +static gint mock_time_init = 0; +static struct pgm_rwlock_t mock_pgm_sock_list_lock; +static struct pgm_slist_t* mock_pgm_sock_list = NULL; + +#define pgm_time_init mock_pgm_time_init +#define pgm_time_shutdown mock_pgm_time_shutdown +#define pgm_close mock_pgm_close +#define pgm_sock_list_lock mock_pgm_sock_list_lock +#define pgm_sock_list mock_pgm_sock_list + +#define ENGINE_DEBUG +#include "engine.c" + + +static +void +mock_setup (void) +{ + mock_time_init = FALSE; +} + +static +void +mock_teardown (void) +{ +// null +} + + +/* mock functions for external references */ + +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return 0; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_time_init ( + pgm_error_t** error + ) +{ + mock_time_init++; + return TRUE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_time_shutdown (void) +{ + if (!mock_time_init) + return FALSE; + mock_time_init--; + return TRUE; +} + +bool +mock_pgm_close ( + pgm_sock_t* sock, + bool flush + ) +{ + return TRUE; +} + + +/* target: + * bool + * pgm_init (pgm_error_t** error) + */ + +/* reference counting on init */ +START_TEST (test_init_pass_001) +{ + fail_unless (TRUE == pgm_init (NULL), "init failed"); + fail_unless (TRUE == pgm_init (NULL), "init failed"); +} +END_TEST + +/* timing module already init */ +START_TEST (test_init_pass_003) +{ + pgm_error_t* err = NULL; + fail_unless (TRUE == pgm_time_init (&err), "time-init failed: %s", (err && err->message) ? err->message : "(null)"); + fail_unless (TRUE == pgm_init (&err), "init failed: %s", (err && err->message) ? err->message : "(null)"); + fail_unless (TRUE == pgm_init (&err), "init failed: %s", (err && err->message) ? err->message : "(null)"); +} +END_TEST + +/* target: + * bool + * pgm_shutdown (void) + */ + +START_TEST (test_shutdown_pass_001) +{ + fail_unless (TRUE == pgm_init (NULL), "init failed"); + fail_unless (TRUE == pgm_shutdown (), "shutdown failed"); +} +END_TEST + +/* no init */ +START_TEST (test_shutdown_pass_002) +{ + fail_unless (FALSE == pgm_shutdown (), "shutdown failed"); +} +END_TEST + +/* double call */ +START_TEST (test_shutdown_pass_003) +{ + fail_unless (TRUE == pgm_init (NULL), "init failed"); + fail_unless (TRUE == pgm_shutdown (), "shutdown failed"); + fail_unless (FALSE == pgm_shutdown (), "shutdown failed"); +} +END_TEST + +/* check reference counting */ +START_TEST (test_shutdown_pass_004) +{ + fail_unless (TRUE == pgm_init (NULL), "init failed"); + fail_unless (TRUE == pgm_init (NULL), "init failed"); + fail_unless (TRUE == pgm_shutdown (), "shutdown failed"); + fail_unless (TRUE == pgm_shutdown (), "shutdown failed"); + fail_unless (FALSE == pgm_shutdown (), "shutdown failed"); +} +END_TEST + +/* target: + * bool + * pgm_supported (void) + */ + +START_TEST (test_supported_pass_001) +{ + fail_unless (FALSE == pgm_supported(), "supported failed"); + fail_unless (TRUE == pgm_init (NULL), "init failed"); + fail_unless (TRUE == pgm_supported(), "supported failed"); + fail_unless (TRUE == pgm_shutdown (), "shutdown failed"); + fail_unless (FALSE == pgm_supported(), "supported failed"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_init = tcase_create ("init"); + tcase_add_checked_fixture (tc_init, mock_setup, mock_teardown); + suite_add_tcase (s, tc_init); + tcase_add_test (tc_init, test_init_pass_001); + tcase_add_test (tc_init, test_init_pass_003); + + TCase* tc_shutdown = tcase_create ("shutdown"); + tcase_add_checked_fixture (tc_shutdown, mock_setup, mock_teardown); + suite_add_tcase (s, tc_shutdown); + tcase_add_test (tc_shutdown, test_shutdown_pass_001); + tcase_add_test (tc_shutdown, test_shutdown_pass_002); + tcase_add_test (tc_shutdown, test_shutdown_pass_003); + tcase_add_test (tc_shutdown, test_shutdown_pass_004); + + TCase* tc_supported = tcase_create ("supported"); + tcase_add_checked_fixture (tc_supported, mock_setup, mock_teardown); + suite_add_tcase (s, tc_supported); + tcase_add_test (tc_supported, test_supported_pass_001); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/error.c b/3rdparty/openpgm-svn-r1135/pgm/error.c new file mode 100644 index 0000000..3f3fe30 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/error.c @@ -0,0 +1,518 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable error reporting. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _WIN32 +# include +#endif +#include +#include + + +//#define ERROR_DEBUG + + +#define ERROR_OVERWRITTEN_WARNING "pgm_error_t set over the top of a previous pgm_error_t or uninitialized memory.\n" \ + "This indicates a bug. You must ensure an error is NULL before it's set.\n" \ + "The overwriting error message was: %s" + +static pgm_error_t* pgm_error_new_valist (const int, const int, const char*, va_list) PGM_GNUC_PRINTF(3, 0); +static void pgm_error_add_prefix (char**restrict, const char*restrict, va_list) PGM_GNUC_PRINTF(2, 0); + + +static +pgm_error_t* +pgm_error_new_valist ( + const int error_domain, + const int error_code, + const char* format, + va_list args + ) +{ + pgm_error_t *error = pgm_new (pgm_error_t, 1); + error->domain = error_domain; + error->code = error_code; + error->message = pgm_strdup_vprintf (format, args); + return error; +} + +void +pgm_error_free ( + pgm_error_t* error + ) +{ + pgm_return_if_fail (error != NULL); + pgm_free (error->message); + pgm_free (error); +} + +void +pgm_set_error ( + pgm_error_t** restrict err, + const int error_domain, + const int error_code, + const char* restrict format, + ... + ) +{ + pgm_error_t *new; + va_list args; + + if (NULL == err) + return; + + va_start (args, format); + new = pgm_error_new_valist (error_domain, error_code, format, args); + va_end (args); + + if (NULL == *err) + *err = new; + else + pgm_warn (_(ERROR_OVERWRITTEN_WARNING), new->message); +} + +void +pgm_propagate_error ( + pgm_error_t** restrict dest, + pgm_error_t* restrict src + ) +{ + pgm_return_if_fail (src != NULL); + + if (NULL == dest) { + if (src) + pgm_error_free (src); + return; + } else { + if (NULL != *dest) { + pgm_warn (_(ERROR_OVERWRITTEN_WARNING), src->message); + } else { + *dest = src; + } + } +} + +void +pgm_clear_error ( + pgm_error_t** err + ) +{ + if (err && *err) { + pgm_error_free (*err); + *err = NULL; + } +} + +static +void +pgm_error_add_prefix ( + char** restrict string, + const char* restrict format, + va_list ap + ) +{ + char* prefix = pgm_strdup_vprintf (format, ap); + char* oldstring = *string; + *string = pgm_strconcat (prefix, oldstring, NULL); + pgm_free (oldstring); + pgm_free (prefix); +} + +void +pgm_prefix_error ( + pgm_error_t** restrict err, + const char* restrict format, + ... + ) +{ + if (err && *err) { + va_list ap; + va_start (ap, format); + pgm_error_add_prefix (&(*err)->message, format, ap); + va_end (ap); + } +} + +/* error from libc. + */ + +int +pgm_error_from_errno ( + const int from_errno + ) +{ + switch (from_errno) { +#ifdef EAFNOSUPPORT + case EAFNOSUPPORT: + return PGM_ERROR_AFNOSUPPORT; + break; +#endif + +#ifdef EAGAIN + case EAGAIN: + return PGM_ERROR_AGAIN; + break; +#endif + +#ifdef EBADF + case EBADF: + return PGM_ERROR_BADF; + break; +#endif + +#ifdef ECONNRESET + case ECONNRESET: + return PGM_ERROR_CONNRESET; + break; +#endif + +#ifdef EFAULT + case EFAULT: + return PGM_ERROR_FAULT; + break; +#endif + +#ifdef EINTR + case EINTR: + return PGM_ERROR_INTR; + break; +#endif + +#ifdef EINVAL + case EINVAL: + return PGM_ERROR_INVAL; + break; +#endif + +#ifdef EMFILE + case EMFILE: + return PGM_ERROR_MFILE; + break; +#endif + +#ifdef ENFILE + case ENFILE: + return PGM_ERROR_NFILE; + break; +#endif + +#ifdef ENODEV + case ENODEV: + return PGM_ERROR_NODEV; + break; +#endif + +#ifdef ENOENT + case ENOENT: + return PGM_ERROR_NOENT; + break; +#endif + +#ifdef ENOMEM + case ENOMEM: + return PGM_ERROR_NOMEM; + break; +#endif + +#ifdef ENONET + case ENONET: + return PGM_ERROR_NONET; + break; +#endif + +#ifdef ENOPROTOOPT + case ENOPROTOOPT: + return PGM_ERROR_NOPROTOOPT; + break; +#endif + +#ifdef ENOTUNIQ + case ENOTUNIQ: + return PGM_ERROR_NOTUNIQ; + break; +#endif + +#ifdef ENXIO + case ENXIO: + return PGM_ERROR_NXIO; + break; +#endif + +#ifdef EPERM + case EPERM: + return PGM_ERROR_PERM; + break; +#endif + +#ifdef EPROTO + case EPROTO: + return PGM_ERROR_PROTO; + break; +#endif + +#ifdef ERANGE + case ERANGE: + return PGM_ERROR_RANGE; + break; +#endif + +#ifdef EXDEV + case EXDEV: + return PGM_ERROR_XDEV; + break; +#endif + + default : + return PGM_ERROR_FAILED; + break; + } +} + +/* h_errno from gethostbyname. + */ + +int +pgm_error_from_h_errno ( + const int from_h_errno + ) +{ + switch (from_h_errno) { +#ifdef HOST_NOT_FOUND + case HOST_NOT_FOUND: + return PGM_ERROR_NONAME; + break; +#endif + +#ifdef TRY_AGAIN + case TRY_AGAIN: + return PGM_ERROR_AGAIN; + break; +#endif + +#ifdef NO_RECOVERY + case NO_RECOVERY: + return PGM_ERROR_FAIL; + break; +#endif + +#ifdef NO_DATA + case NO_DATA: + return PGM_ERROR_NODATA; + break; +#endif + + default: + return PGM_ERROR_FAILED; + break; + } +} + +/* errno must be preserved before calling to catch correct error + * status with EAI_SYSTEM. + */ + +int +pgm_error_from_eai_errno ( + const int from_eai_errno, +#ifdef EAI_SYSTEM + const int from_errno +#else + PGM_GNUC_UNUSED const int from_errno +#endif + ) +{ + switch (from_eai_errno) { +#ifdef EAI_ADDRFAMILY + case EAI_ADDRFAMILY: + return PGM_ERROR_ADDRFAMILY; + break; +#endif + +#ifdef EAI_AGAIN + case EAI_AGAIN: + return PGM_ERROR_AGAIN; + break; +#endif + +#ifdef EAI_BADFLAGS + case EAI_BADFLAGS: + return PGM_ERROR_INVAL; + break; +#endif + +#ifdef EAI_FAIL + case EAI_FAIL: + return PGM_ERROR_FAIL; + break; +#endif + +#ifdef EAI_FAMILY + case EAI_FAMILY: + return PGM_ERROR_AFNOSUPPORT; + break; +#endif + +#ifdef EAI_MEMORY + case EAI_MEMORY: + return PGM_ERROR_NOMEM; + break; +#endif + +#ifdef EAI_NODATA + case EAI_NODATA: + return PGM_ERROR_NODATA; + break; +#endif + +#if defined(EAI_NONAME) && EAI_NONAME != EAI_NODATA + case EAI_NONAME: + return PGM_ERROR_NONAME; + break; +#endif + +#ifdef EAI_SERVICE + case EAI_SERVICE: + return PGM_ERROR_SERVICE; + break; +#endif + +#ifdef EAI_SOCKTYPE + case EAI_SOCKTYPE: + return PGM_ERROR_SOCKTNOSUPPORT; + break; +#endif + +#ifdef EAI_SYSTEM + case EAI_SYSTEM: + return pgm_error_from_errno (from_errno); + break; +#endif + + default : + return PGM_ERROR_FAILED; + break; + } +} + +/* from WSAGetLastError() + */ + +int +pgm_error_from_wsa_errno ( + const int from_wsa_errno + ) +{ + switch (from_wsa_errno) { +#ifdef WSAEINVAL + case WSAEINVAL: + return PGM_ERROR_INVAL; + break; +#endif +#ifdef WSAEMFILE + case WSAEMFILE: + return PGM_ERROR_MFILE; + break; +#endif +#ifdef WSA_NOT_ENOUGH_MEMORY + case WSA_NOT_ENOUGH_MEMORY: + return PGM_ERROR_NOMEM; + break; +#endif +#ifdef WSAENOPROTOOPT + case WSAENOPROTOOPT: + return PGM_ERROR_NOPROTOOPT; + break; +#endif +#ifdef WSAECONNRESET + case WSAECONNRESET: + return PGM_ERROR_CONNRESET; + break; +#endif + + default : + return PGM_ERROR_FAILED; + break; + } +} + +/* from Last-Error codes, i.e. Windows non-WinSock and non-DOS errors. + */ + +int +pgm_error_from_win_errno ( + const int from_win_errno + ) +{ + switch (from_win_errno) { +#ifdef ERROR_ADDRESS_NOT_ASSOCIATED + case ERROR_ADDRESS_NOT_ASSOCIATED: + return PGM_ERROR_NODATA; + break; +#endif + +#ifdef ERROR_BUFFER_OVERFLOW + case ERROR_BUFFER_OVERFLOW: + return PGM_ERROR_NOBUFS; + break; +#endif + +#ifdef ERROR_INVALID_DATA + case ERROR_INVALID_DATA: + return PGM_ERROR_BADE; + break; +#endif + +#ifdef ERROR_INSUFFICIENT_BUFFER + case ERROR_INSUFFICIENT_BUFFER: + return PGM_ERROR_NOMEM; + break; +#endif + +#ifdef ERROR_INVALID_PARAMETER + case ERROR_INVALID_PARAMETER: + return PGM_ERROR_INVAL; + break; +#endif + +#ifdef ERROR_NOT_ENOUGH_MEMORY + case ERROR_NOT_ENOUGH_MEMORY: + return PGM_ERROR_NOMEM; + break; +#endif + +#ifdef ERROR_NO_DATA + case ERROR_NO_DATA: + return PGM_ERROR_NODATA; + break; +#endif + +#ifdef ERROR_NOT_SUPPORTED + case ERROR_NOT_SUPPORTED: + return PGM_ERROR_NOSYS; + break; +#endif + + default : + return PGM_ERROR_FAILED; + break; + } +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/error_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/error_unittest.c new file mode 100644 index 0000000..035c0f3 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/error_unittest.c @@ -0,0 +1,292 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for error reporting. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include + + +/* mock state */ + + + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + + +#define ERROR_DEBUG +#include "error.c" + + +/* target: + * void + * pgm_set_error ( + * pgm_error_t** err, + * int err_domain, + * int err_code, + * const char* format, + * ... + * ) + */ + +START_TEST (test_set_error_pass_001) +{ + pgm_error_t* err = NULL; + const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; + const gint err_code = 100; + pgm_set_error (&err, err_domain, err_code, "an error occurred."); + fail_unless (NULL != err); +} +END_TEST + +START_TEST (test_set_error_pass_002) +{ + pgm_error_t* err = NULL; + const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; + const gint err_code = 100; + pgm_set_error (&err, err_domain, err_code, "an error occurred: value=%d.", 123); + fail_unless (NULL != err); +} +END_TEST + +/* ignore NULL error */ +START_TEST (test_set_error_pass_003) +{ + pgm_error_t* err = NULL; + const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; + const gint err_code = 100; + pgm_set_error (NULL, err_domain, err_code, "an error occurred."); +} +END_TEST + +/* overwritten error */ +START_TEST (test_set_error_pass_004) +{ + pgm_error_t* err = NULL; + const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; + const gint err_code = 100; + pgm_set_error (&err, err_domain, err_code, "an error occurred."); + fail_unless (NULL != err); + pgm_set_error (&err, err_domain, err_code, "another error occurred."); +} +END_TEST + +/* target: + * void + * pgm_prefix_error ( + * pgm_error_t** err, + * const char* format, + * ... + * ) + */ + +START_TEST (test_prefix_error_pass_001) +{ + pgm_error_t* err = NULL; + const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; + const gint err_code = 100; + pgm_set_error (&err, err_domain, err_code, "an error occurred."); + fail_unless (NULL != err); + pgm_prefix_error (&err, "i am a prefix:"); + pgm_prefix_error (&err, "i am another prefix, value=%d:", 123); +} +END_TEST + +/* ignore null original error */ +START_TEST (test_prefix_error_pass_002) +{ + pgm_error_t* err = NULL; + pgm_prefix_error (&err, "i am a prefix:"); +} +END_TEST + +/* target: + * void + * pgm_propagate_error ( + * pgm_error_t** dest, + * pgm_error_t* src, + * ) + */ + +START_TEST (test_propagate_error_pass_001) +{ + pgm_error_t* dest = NULL; + pgm_error_t* err = NULL; + const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; + const gint err_code = 100; + pgm_set_error (&err, err_domain, err_code, "an error occurred."); + fail_unless (NULL != err); + pgm_propagate_error (&dest, err); + fail_unless (NULL != dest); +} +END_TEST + +/* ignore NULL destination */ +START_TEST (test_propagate_error_pass_002) +{ + pgm_error_t* err = NULL; + const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; + const gint err_code = 100; + pgm_set_error (&err, err_domain, err_code, "an error occurred."); + fail_unless (NULL != err); + pgm_propagate_error (NULL, err); +} +END_TEST + +/* src error SHOULD be valid */ +START_TEST (test_propagate_error_pass_003) +{ + pgm_error_t* dest = NULL; + pgm_error_t* err = NULL; + pgm_propagate_error (&dest, err); +} +END_TEST + +/* target: + * void + * pgm_clear_error ( + * pgm_error_t** err + * ) + */ + +START_TEST (test_clear_error_pass_001) +{ + pgm_error_t* err = NULL; + const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; + const gint err_code = 100; + pgm_set_error (&err, err_domain, err_code, "an error occurred."); + fail_unless (NULL != err); + pgm_clear_error (&err); + fail_unless (NULL == err); +} +END_TEST + +START_TEST (test_clear_error_pass_002) +{ + pgm_error_t* err = NULL; + pgm_clear_error (&err); + fail_unless (NULL == err); +} +END_TEST + +START_TEST (test_clear_error_pass_003) +{ + pgm_clear_error (NULL); +} +END_TEST + +/* target: + * void + * pgm_error_free ( + * pgm_error_t* err + * ) + */ + +START_TEST (test_error_free_pass_001) +{ + pgm_error_t* err = NULL; + const gint err_domain = PGM_ERROR_DOMAIN_ENGINE; + const gint err_code = 100; + pgm_set_error (&err, err_domain, err_code, "an error occurred."); + fail_unless (NULL != err); + pgm_error_free (err); +} +END_TEST + +START_TEST (test_error_free_pass_002) +{ + pgm_error_free (NULL); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_set_error = tcase_create ("set-error"); + suite_add_tcase (s, tc_set_error); + tcase_add_test (tc_set_error, test_set_error_pass_001); + tcase_add_test (tc_set_error, test_set_error_pass_002); + tcase_add_test (tc_set_error, test_set_error_pass_003); + tcase_add_test (tc_set_error, test_set_error_pass_004); + + TCase* tc_prefix_error = tcase_create ("prefix-error"); + suite_add_tcase (s, tc_prefix_error); + tcase_add_test (tc_prefix_error, test_prefix_error_pass_001); + tcase_add_test (tc_prefix_error, test_prefix_error_pass_002); + + TCase* tc_propagate_error = tcase_create ("propagate-error"); + suite_add_tcase (s, tc_propagate_error); + tcase_add_test (tc_propagate_error, test_propagate_error_pass_001); + tcase_add_test (tc_propagate_error, test_propagate_error_pass_002); + tcase_add_test (tc_propagate_error, test_propagate_error_pass_003); + + TCase* tc_clear_error = tcase_create ("clear-error"); + suite_add_tcase (s, tc_clear_error); + tcase_add_test (tc_clear_error, test_clear_error_pass_001); + tcase_add_test (tc_clear_error, test_clear_error_pass_002); + tcase_add_test (tc_clear_error, test_clear_error_pass_003); + + TCase* tc_error_free = tcase_create ("error-free"); + suite_add_tcase (s, tc_error_free); + tcase_add_test (tc_error_free, test_error_free_pass_001); + tcase_add_test (tc_error_free, test_error_free_pass_002); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/SConscript b/3rdparty/openpgm-svn-r1135/pgm/examples/SConscript new file mode 100644 index 0000000..46ebce3 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/SConscript @@ -0,0 +1,88 @@ +# -*- mode: python -*- +# OpenPGM build script +# $Id$ + +import os; + +Import('env'); +e = env.Clone(); +e.Prepend(LIBS = ['libpgm']); +p = e.Clone(); +if '-DCONFIG_HAVE_GETOPT' in env['CCFLAGS']: + getopt = [] +else: + getopt = ['getopt.c'] + +if e['WITH_GLIB'] == 'true': + e.Prepend(LIBS = ['libpgmex']); + e.MergeFlags(env['GLIB_FLAGS']); + e2 = e.Clone(); + if e2['WITH_SNMP'] == 'true': + e2.Append(CCFLAGS = ['-DCONFIG_WITH_SNMP']); + e2.Prepend(LIBS = ['libpgmsnmp']); + e2.MergeFlags(e['SNMP_FLAGS']); + if e2['WITH_HTTP'] == 'true': + e2.Append(CCFLAGS = ['-DCONFIG_WITH_HTTP']); + e2.Prepend(LIBS = ['libpgmhttp']); + +# core preferred examples + e.Program(['pgmdump.c']) + e2.Program(['pgmsend.c']) + e2.Program(['pgmrecv.c']) + +# sync examples + e.Program(['blocksyncrecv.c']) + e.Program(['snonblocksyncrecv.c']) + if '-DCONFIG_HAVE_POLL' in e['CCFLAGS']: + e.Program(['pnonblocksyncrecv.c']) + +# epoll based examples + if '-DCONFIG_HAVE_EPOLL' in e['CCFLAGS']: + e.Program(['enonblocksyncrecv.c']) + e.Program(['enonblocksyncrecvmsg.c']) + e.Program(['enonblocksyncrecvmsgv.c']) + +# ncurses examples + if e['WITH_NCURSES'] == 'true': + en = e.Clone() + en.Append(LIBS = ['panel', 'ncurses']); + en.Program(['pgmtop.c']) + +# Google Protocol Buffer example + if e['WITH_PROTOBUF'] == 'true': + ep = e2.Clone(); + newCCFLAGS = []; + for flag in ep['CCFLAGS']: + if ("-W" != flag[:2]) and ("-std=gnu99" != flag[:10]) and ("-pedantic" != flag[:9]) and ("-D_XOPEN_SOURCE=600" != flag[:19]) and ("-xc99=all" != flag[:9]): + newCCFLAGS.append(flag); + if ("-D_XOPEN_SOURCE=600" == flag[:19]): + newCCFLAGS.append("-D_XOPEN_SOURCE=500"); + ep['CCFLAGS'] = newCCFLAGS; + ep.Append(CPPPATH = '.'); + ep.Append(CCFLAGS = ep['PROTOBUF_CCFLAGS']); + ep.Depends('pgmping.cc', ['ping.pb.cc', 'ping.pb.h']); + protobuf = Builder(action = 'cd ${SOURCE.dir} && %s ${SOURCE.file} --cpp_out=../${TARGET.dir}' % ep['PROTOBUF_PROTOC']) + ep.Append(BUILDERS = {'Protobuf' : protobuf}) + ep.Protobuf('ping.pb.cc', 'ping.proto') + ep.Program(['pgmping.cc', 'ping.pb.cc', ep['PROTOBUF_LIBS']]) + +# Vanilla example +p.Program(['purinsend.c'] + getopt) +p.Program(['purinrecv.c'] + getopt) +p.Program(['daytime.c'] + getopt) +p.Program(['shortcakerecv.c', 'async.c'] + getopt) + +# Vanilla C++ example +if e['WITH_CC'] == 'true': + pcc = p.Clone(); + newCCFLAGS = []; + for flag in pcc['CCFLAGS']: + if ("-W" != flag[:2]) and ("-std=gnu99" != flag[:10]) and ("-pedantic" != flag[:9]) and ("-D_XOPEN_SOURCE=600" != flag[:19]) and ("-xc99=all" != flag[:9]): + newCCFLAGS.append(flag); + if ("-D_XOPEN_SOURCE=600" == flag[:19]): + newCCFLAGS.append("-D_XOPEN_SOURCE=500"); + pcc['CCFLAGS'] = newCCFLAGS; + pcc.Program('purinsendcc', ['purinsendcc.cc'] + p.Object(getopt)) + pcc.Program('purinrecvcc', ['purinrecvcc.cc'] + p.Object(getopt)) + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/SConscript89 b/3rdparty/openpgm-svn-r1135/pgm/examples/SConscript89 new file mode 100644 index 0000000..5595d3d --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/SConscript89 @@ -0,0 +1,41 @@ +# -*- mode: python -*- +# OpenPGM build script +# $Id$ + +import os; + +Import('env'); +e = env.Clone(); +e.Prepend(LIBS = ['libpgm89']); +p = e.Clone(); +if '-DCONFIG_HAVE_GETOPT' in env['CCFLAGS']: + getopt = [] +else: + getopt = ['getopt.c'] + +c89source = Builder(action = 'perl -p -e "s/%z(u|d)/%l\\1/g" $SOURCE > $TARGET', + suffix = '.c89.c', + src_suffix = '.c', + single_source = 1); +p.Append(BUILDERS = {'C89Source' : c89source}) + +for c99file in ['purinsend.c', 'purinrecv.c']: + p.C89Source(c99file); + +p.Program('purinsend', ['purinsend.c89.c'] + getopt) +p.Program('purinrecv', ['purinrecv.c89.c'] + getopt) + +# Vanilla C++ example +if e['WITH_CC'] == 'true': + pcc = p.Clone(); + newCCFLAGS = []; + for flag in pcc['CCFLAGS']: + if ("-W" != flag[:2]) and ("-std=gnu99" != flag[:10]) and ("-pedantic" != flag[:9]) and ("-D_XOPEN_SOURCE=600" != flag[:19]) and ("-xc99=all" != flag[:9]): + newCCFLAGS.append(flag); + if ("-D_XOPEN_SOURCE=600" == flag[:19]): + newCCFLAGS.append("-D_XOPEN_SOURCE=500"); + pcc['CCFLAGS'] = newCCFLAGS; + pcc.Program('purinsendcc', ['purinsendcc.cc'] + p.Object(getopt)) + pcc.Program('purinrecvcc', ['purinrecvcc.cc'] + p.Object(getopt)) + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/async.c b/3rdparty/openpgm-svn-r1135/pgm/examples/async.c new file mode 100644 index 0000000..9ac15dd --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/async.c @@ -0,0 +1,441 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Asynchronous queue for receiving packets in a separate managed thread. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#ifndef _WIN32 +# include +# include +# include +#else +# include +#endif +#include + +#include "async.h" + + +/* locals */ + +struct async_event_t { + struct async_event_t *next, *prev; + size_t len; + struct pgm_sockaddr_t addr; +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + char data[]; +#elif defined(__cplusplus) + char data[1]; +#else + char data[0]; +#endif +}; + + +static void on_data (async_t*const restrict, const void*restrict, const size_t, const struct pgm_sockaddr_t*restrict, const socklen_t); + + +/* queued data is stored as async_event_t objects + */ + +static inline +struct async_event_t* +async_event_alloc ( + size_t len + ) +{ + struct async_event_t* event; + event = (struct async_event_t*)calloc (1, len + sizeof(struct async_event_t)); + event->len = len; + return event; +} + +static inline +void +async_event_unref ( + struct async_event_t* const event + ) +{ + free (event); +} + +/* async_t implements a queue + */ + +static inline +void +async_push_event ( + async_t* restrict async, + struct async_event_t* restrict event + ) +{ + event->next = async->head; + if (async->head) + async->head->prev = event; + else + async->tail = event; + async->head = event; + async->length++; +} + +static inline +struct async_event_t* +async_pop_event ( + async_t* async + ) +{ + if (async->tail) + { + struct async_event_t *event = async->tail; + + async->tail = event->prev; + if (async->tail) + { + async->tail->next = NULL; + event->prev = NULL; + } + else + async->head = NULL; + async->length--; + + return event; + } + + return NULL; +} + +/* asynchronous receiver thread, sits in a loop processing incoming packets + */ + +static +#ifndef _WIN32 +void* +#else +unsigned +__stdcall +#endif +receiver_routine ( + void* arg + ) +{ + assert (NULL != arg); + async_t* async = (async_t*)arg; + assert (NULL != async->sock); +#ifndef _WIN32 + int fds; + fd_set readfds; +#else + int n_handles = 3, recv_sock, pending_sock; + HANDLE waitHandles[ 3 ]; + DWORD dwTimeout, dwEvents; + WSAEVENT recvEvent, pendingEvent; + socklen_t socklen = sizeof(int); + + recvEvent = WSACreateEvent (); + pgm_getsockopt (async->sock, IPPROTO_PGM, PGM_RECV_SOCK, &recv_sock, &socklen); + WSAEventSelect (recv_sock, recvEvent, FD_READ); + pendingEvent = WSACreateEvent (); + pgm_getsockopt (async->sock, IPPROTO_PGM, PGM_PENDING_SOCK, &pending_sock, &socklen); + WSAEventSelect (pending_sock, pendingEvent, FD_READ); + + waitHandles[0] = async->destroy_event; + waitHandles[1] = recvEvent; + waitHandles[2] = pendingEvent; +#endif /* !_WIN32 */ + +/* dispatch loop */ + do { + struct timeval tv; + char buffer[4096]; + size_t len; + struct pgm_sockaddr_t from; + socklen_t fromlen = sizeof (from); + const int status = pgm_recvfrom (async->sock, + buffer, + sizeof(buffer), + 0, + &len, + &from, + &fromlen, + NULL); + switch (status) { + case PGM_IO_STATUS_NORMAL: + on_data (async, buffer, len, &from, fromlen); + break; + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (async->sock, IPPROTO_PGM, PGM_TIME_REMAIN, &tv, &optlen); + } + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (async->sock, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &optlen); + } + case PGM_IO_STATUS_WOULD_BLOCK: +/* select for next event */ +block: +#ifndef _WIN32 + fds = async->destroy_pipe[0] + 1; + FD_ZERO(&readfds); + FD_SET(async->destroy_pipe[0], &readfds); + pgm_select_info (async->sock, &readfds, NULL, &fds); + fds = select (fds, &readfds, NULL, NULL, PGM_IO_STATUS_WOULD_BLOCK == status ? NULL : &tv); +#else + dwTimeout = PGM_IO_STATUS_WOULD_BLOCK == status ? INFINITE : (DWORD)((tv.tv_sec * 1000) + (tv. +tv_usec / 1000)); + dwEvents = WaitForMultipleObjects (n_handles, waitHandles, FALSE, dwTimeout); + switch (dwEvents) { + case WAIT_OBJECT_0+1: WSAResetEvent (recvEvent); break; + case WAIT_OBJECT_0+2: WSAResetEvent (pendingEvent); break; + default: break; + } +#endif /* !_WIN32 */ + break; + + default: + if (PGM_IO_STATUS_ERROR == status) + break; + } + } while (!async->is_destroyed); + +/* cleanup */ +#ifndef _WIN32 + return NULL; +#else + WSACloseEvent (recvEvent); + WSACloseEvent (pendingEvent); + _endthread(); + return 0; +#endif /* !_WIN32 */ +} + +/* enqueue a new data event. + */ + +static +void +on_data ( + async_t*const restrict async, + const void* restrict data, + const size_t len, + const struct pgm_sockaddr_t* restrict from, + const socklen_t fromlen + ) +{ + struct async_event_t* event = async_event_alloc (len); + memcpy (&event->addr, from, fromlen); + memcpy (&event->data, data, len); +#ifndef _WIN32 + pthread_mutex_lock (&async->pthread_mutex); + async_push_event (async, event); + if (1 == async->length) { + const char one = '1'; + const size_t writelen = write (async->notify_pipe[1], &one, sizeof(one)); + assert (sizeof(one) == writelen); + } + pthread_mutex_unlock (&async->pthread_mutex); +#else + WaitForSingleObject (async->win32_mutex, INFINITE); + async_push_event (async, event); + if (1 == async->length) { + SetEvent (async->notify_event); + } + ReleaseMutex (async->win32_mutex); +#endif /* _WIN32 */ +} + +/* create asynchronous thread handler from bound PGM sock. + * + * on success, 0 is returned. on error, -1 is returned, and errno set appropriately. + */ + +int +async_create ( + async_t** restrict async, + pgm_sock_t* const restrict sock + ) +{ + async_t* new_async; + + if (NULL == async || NULL == sock) { + errno = EINVAL; + return -1; + } + + new_async = (async_t*)calloc (1, sizeof(async_t)); + new_async->sock = sock; +#ifndef _WIN32 + int e; + e = pthread_mutex_init (&new_async->pthread_mutex, NULL); + if (0 != e) goto err_destroy; + e = pipe (new_async->notify_pipe); + const int flags = fcntl (new_async->notify_pipe[0], F_GETFL); + fcntl (new_async->notify_pipe[0], F_SETFL, flags | O_NONBLOCK); + if (0 != e) goto err_destroy; + e = pipe (new_async->destroy_pipe); + if (0 != e) goto err_destroy; + const int status = pthread_create (&new_async->thread, NULL, &receiver_routine, new_async); + if (0 != status) goto err_destroy; +#else + new_async->win32_mutex = CreateMutex (NULL, FALSE, NULL); + new_async->notify_event = CreateEvent (NULL, TRUE, FALSE, TEXT("AsyncNotify")); + new_async->destroy_event = CreateEvent (NULL, TRUE, FALSE, TEXT("AsyncDestroy")); + new_async->thread = (HANDLE)_beginthreadex (NULL, 0, &receiver_routine, new_async, 0, NULL); + if (0 == new_async->thread) goto err_destroy; +#endif /* _WIN32 */ + +/* return new object */ + *async = new_async; + return 0; + +err_destroy: +#ifndef _WIN32 + close (new_async->destroy_pipe[0]); + close (new_async->destroy_pipe[1]); + close (new_async->notify_pipe[0]); + close (new_async->notify_pipe[1]); + pthread_mutex_destroy (&new_async->pthread_mutex); +#else + CloseHandle (new_async->destroy_event); + CloseHandle (new_async->notify_event); + CloseHandle (new_async->win32_mutex); +#endif /* _WIN32 */ + if (new_async) + free (new_async); + return -1; +} + +/* Destroy asynchronous receiver, there must be no active queue consumer. + * + * on success, 0 is returned, on error -1 is returned and errno set appropriately. + */ + +int +async_destroy ( + async_t* const async + ) +{ + if (NULL == async || async->is_destroyed) { + errno = EINVAL; + return -1; + } + + async->is_destroyed = TRUE; +#ifndef _WIN32 + const char one = '1'; + const size_t writelen = write (async->destroy_pipe[1], &one, sizeof(one)); + assert (sizeof(one) == writelen); + pthread_join (async->thread, NULL); + close (async->destroy_pipe[0]); + close (async->destroy_pipe[1]); + close (async->notify_pipe[0]); + close (async->notify_pipe[1]); + pthread_mutex_destroy (&async->pthread_mutex); +#else + SetEvent (async->destroy_event); + WaitForSingleObject (async->thread, INFINITE); + CloseHandle (async->thread); + CloseHandle (async->destroy_event); + CloseHandle (async->notify_event); + CloseHandle (async->win32_mutex); +#endif /* !_WIN32 */ + while (async->head) { + struct async_event_t *next = async->head->next; + async_event_unref (async->head); + async->head = next; + async->length--; + } + free (async); + return 0; +} + +/* synchronous reading from the queue. + * + * returns GIOStatus with success, error, again, or eof. + */ + +ssize_t +async_recvfrom ( + async_t* const restrict async, + void* restrict buf, + size_t len, + struct pgm_sockaddr_t* restrict from, + socklen_t* restrict fromlen + ) +{ + struct async_event_t* event; + + if (NULL == async || NULL == buf || async->is_destroyed) { + errno = EINVAL; + return -1; + } + +#ifndef _WIN32 + pthread_mutex_lock (&async->pthread_mutex); + if (0 == async->length) { +/* flush event pipe */ + char tmp; + while (sizeof(tmp) == read (async->notify_pipe[0], &tmp, sizeof(tmp))); + pthread_mutex_unlock (&async->pthread_mutex); + errno = EAGAIN; + return -1; + } + event = async_pop_event (async); + pthread_mutex_unlock (&async->pthread_mutex); +#else + WaitForSingleObject (async->win32_mutex, INFINITE); + if (0 == async->length) { +/* clear event */ + ResetEvent (async->notify_event); + ReleaseMutex (async->win32_mutex); + errno = EAGAIN; + return -1; + } + event = async_pop_event (async); + ReleaseMutex (async->win32_mutex); +#endif /* _WIN32 */ + assert (NULL != event); + +/* pass data back to callee */ + const size_t event_len = MIN(event->len, len); + if (NULL != from && sizeof(struct pgm_sockaddr_t) == *fromlen) { + memcpy (from, &event->addr, *fromlen); + } + memcpy (buf, event->data, event_len); + async_event_unref (event); + return event_len; +} + +ssize_t +async_recv ( + async_t* const restrict async, + void* restrict buf, + size_t len + ) +{ + return async_recvfrom (async, buf, len, NULL, NULL); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/async.h b/3rdparty/openpgm-svn-r1135/pgm/examples/async.h new file mode 100644 index 0000000..788a777 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/async.h @@ -0,0 +1,82 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Asynchronous receive thread helper + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_ASYNC_H__ +#define __PGM_ASYNC_H__ + +struct async_event_t; + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct async_t { + pgm_sock_t* sock; +#ifndef _WIN32 + pthread_t thread; + int notify_pipe[2]; + int destroy_pipe[2]; + pthread_mutex_t pthread_mutex; +#else + HANDLE thread; + HANDLE notify_event; + HANDLE destroy_event; + HANDLE win32_mutex; +#endif + struct async_event_t *head, *tail; + unsigned length; + bool is_destroyed; +}; +typedef struct async_t async_t; + +int async_create (async_t** restrict, pgm_sock_t*const restrict); +int async_destroy (async_t* const); +ssize_t async_recv (async_t*const restrict, void* restrict, size_t); +ssize_t async_recvfrom (async_t*const restrict, void*restrict, size_t, struct pgm_sockaddr_t*restrict, socklen_t*restrict); + +#ifndef _WIN32 +static inline int async_get_fd (async_t* async) +{ + if (NULL == async) { + errno = EINVAL; + return -1; + } + return async->notify_pipe[0]; +} +#else +static inline HANDLE async_get_event (async_t* async) +{ + if (NULL == async) { + errno = EINVAL; + return NULL; + } + return async->notify_event; +} +#endif /* _WIN32 */ + +#ifdef __cplusplus +} +#endif + +#endif /* __PGM_ASYNC_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/blocksyncrecv.c b/3rdparty/openpgm-svn-r1135/pgm/examples/blocksyncrecv.c new file mode 100644 index 0000000..dec87be --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/blocksyncrecv.c @@ -0,0 +1,350 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Simple PGM receiver: blocking synchronous receiver + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#ifndef G_OS_WIN32 +# include +#else +# include "getopt.h" +#endif +#include + +/* example dependencies */ +#include +#include + + +/* typedefs */ + +/* globals */ + +static int g_port = 0; +static const char* g_network = ""; +static gboolean g_multicast_loop = FALSE; +static int g_udp_encap_port = 0; + +static int g_max_tpdu = 1500; +static int g_sqns = 100; + +static pgm_sock_t* g_sock = NULL; +static gboolean g_quit = FALSE; + +#ifdef G_OS_UNIX +static void on_signal (int); +#else +static BOOL on_console_ctrl (DWORD); +#endif +static gboolean on_startup (void); +static int on_data (gconstpointer, size_t, struct pgm_sockaddr_t*); + + +G_GNUC_NORETURN static +void +usage ( + const char* bin + ) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); + exit (1); +} + +int +main ( + int argc, + char* argv[] + ) +{ + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + + log_init (); + g_message ("blocksyncrecv"); + + if (!pgm_init (&pgm_err)) { + g_error ("Unable to start PGM engine: %s", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:lh")) != -1) + { + switch (c) { + case 'n': g_network = optarg; break; + case 's': g_port = atoi (optarg); break; + case 'p': g_udp_encap_port = atoi (optarg); break; + case 'l': g_multicast_loop = TRUE; break; + + case 'h': + case '?': usage (binary_name); + } + } + +/* setup signal handlers */ + signal(SIGSEGV, on_sigsegv); +#ifdef SIGHUP + signal(SIGHUP, SIG_IGN); +#endif +#ifdef G_OS_UNIX + signal(SIGINT, on_signal); + signal(SIGTERM, on_signal); +#else + SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); +#endif + + on_startup(); + +/* dispatch loop */ + g_message ("entering PGM message loop ... "); + do { + char buffer[4096]; + size_t len; + struct pgm_sockaddr_t from; + socklen_t fromlen = sizeof(from); + const int status = pgm_recvfrom (g_sock, + buffer, + sizeof(buffer), + 0, + &len, + &from, + &fromlen, + &pgm_err); + if (PGM_IO_STATUS_NORMAL == status) + on_data (buffer, len, &from); + else { + if (pgm_err) { + g_warning ("%s", pgm_err->message); + pgm_error_free (pgm_err); + pgm_err = NULL; + } + if (PGM_IO_STATUS_ERROR == status) + break; + } + } while (!g_quit); + + g_message ("message loop terminated, cleaning up."); + +/* cleanup */ + if (g_sock) { + g_message ("closing PGM socket."); + pgm_close (g_sock, TRUE); + g_sock = NULL; + } + + g_message ("PGM engine shutdown."); + pgm_shutdown (); + g_message ("finished."); + return EXIT_SUCCESS; +} + +#ifdef G_OS_UNIX +static +void +on_signal ( + int signum + ) +{ + g_message ("on_signal (signum:%d)", signum); + g_quit = TRUE; +} +#else +static +BOOL +on_console_ctrl ( + DWORD dwCtrlType + ) +{ + g_message ("on_console_ctrl (dwCtrlType:%lu)", (unsigned long)dwCtrlType); + g_quit = TRUE; + return TRUE; +} +#endif /* !G_OS_UNIX */ + +static +gboolean +on_startup (void) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + + g_message ("startup."); + +/* parse network parameter into transport address structure */ + if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { + g_error ("parsing network parameter: %s", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + if (g_udp_encap_port) { + g_message ("create PGM/UDP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + } else { + g_message ("create PGM/IP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int recv_only = 1, + passive = 0, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_secs (2), + nak_rdata_ivl = pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_PASSIVE, &passive, sizeof(passive)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + g_error ("creating GSI: %s", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (g_sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + g_error ("binding PGM socket: %s", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int blocking = 0, + multicast_loop = g_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NOBLOCK, &blocking, sizeof(blocking)); + + if (!pgm_connect (g_sock, &pgm_err)) { + g_error ("connecting PGM socket: %s", pgm_err->message); + goto err_abort; + } + + g_message ("startup complete."); + return TRUE; + +err_abort: + if (NULL != g_sock) { + pgm_close (g_sock, FALSE); + g_sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +static +int +on_data ( + gconstpointer data, + size_t len, + struct pgm_sockaddr_t* from + ) +{ +/* protect against non-null terminated strings */ + char buf[1024], tsi[PGM_TSISTRLEN]; + const size_t buflen = MIN( sizeof(buf) - 1, len ); + strncpy (buf, data, buflen); + buf[buflen] = '\0'; + pgm_tsi_print_r (&from->sa_addr, tsi, sizeof(tsi)); + + g_message ("\"%s\" (%u bytes from %s)", + buf, + (unsigned)len, + tsi); + + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/daytime.c b/3rdparty/openpgm-svn-r1135/pgm/examples/daytime.c new file mode 100644 index 0000000..52779a9 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/daytime.c @@ -0,0 +1,546 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Daytime broadcast service. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#ifndef _WIN32 +# include +# include +# include +#else +# include +# include +# include "getopt.h" +# define snprintf _snprintf +#endif +#include + + +/* globals */ +#define TIME_FORMAT "%a, %d %b %Y %H:%M:%S %z" + +static int port = 0; +static const char* network = ""; +static bool use_multicast_loop = FALSE; +static int udp_encap_port = 0; + +static int max_tpdu = 1500; +static int max_rte = 400*1000; /* very conservative rate, 2.5mb/s */ +static int sqns = 100; + +static bool use_pgmcc = FALSE; +static bool use_fec = FALSE; +static bool use_ondemand_parity = FALSE; +static int proactive_packets = 0; +static int rs_k = 8; +static int rs_n = 255; + +static pgm_sock_t* sock = NULL; +static bool is_terminated = FALSE; + +#ifndef _WIN32 +static pthread_t nak_thread; +static int terminate_pipe[2]; +static void on_signal (int); +static void* nak_routine (void*); +#else +static HANDLE nak_thread; +static HANDLE terminate_event; +static BOOL on_console_ctrl (DWORD); +static unsigned __stdcall nak_routine (void*); +#endif +#ifndef _MSC_VER +static void usage (const char*) __attribute__((__noreturn__)); +#else +static void usage (const char*); +#endif + +static bool on_startup (void); +static bool create_sock (void); +static bool create_nak_thread (void); + + +static void +usage ( + const char* bin + ) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -r : Regulate to rate bytes per second\n"); + fprintf (stderr, " -c : Enable PGMCC\n"); + fprintf (stderr, " -f : Enable FEC: proactive, ondemand, or both\n"); + fprintf (stderr, " -N : Reed-Solomon block size (255)\n"); + fprintf (stderr, " -K : Reed-Solomon group size (8)\n"); + fprintf (stderr, " -P : Number of pro-active parity packets (h)\n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); + fprintf (stderr, " -i : List available interfaces\n"); + exit (EXIT_SUCCESS); +} + +int +main ( + int argc, + char *argv[] + ) +{ + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + + puts ("PGM daytime service"); + + if (!pgm_init (&pgm_err)) { + fprintf (stderr, "Unable to start PGM engine: %s\n", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:r:cf:N:K:P:lih")) != -1) + { + switch (c) { + case 'n': network = optarg; break; + case 's': port = atoi (optarg); break; + case 'p': udp_encap_port = atoi (optarg); break; + case 'r': max_rte = atoi (optarg); break; + case 'c': use_pgmcc = TRUE; break; + case 'f': + use_fec = TRUE; + switch (optarg[0]) { + case 'p': + case 'P': + proactive_packets = 1; + break; + case 'b': + case 'B': + proactive_packets = 1; + case 'o': + case 'O': + use_ondemand_parity = TRUE; + break; + } + break; + case 'N': rs_n = atoi (optarg); break; + case 'K': rs_k = atoi (optarg); break; + case 'P': proactive_packets = atoi (optarg); break; + + case 'l': use_multicast_loop = TRUE; break; + + case 'i': + pgm_if_print_all(); + return EXIT_SUCCESS; + + case 'h': + case '?': + usage (binary_name); + } + } + + if (use_fec && ( !rs_n || !rs_k )) { + fprintf (stderr, "Invalid Reed-Solomon parameters RS(%d,%d).\n", rs_n, rs_k); + usage (binary_name); + } + +/* setup signal handlers */ +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif +#ifndef _WIN32 + int e = pipe (terminate_pipe); + assert (0 == e); + const int flags = fcntl (terminate_pipe[0], F_GETFL); + fcntl (terminate_pipe[0], F_SETFL, flags | O_NONBLOCK); + signal (SIGINT, on_signal); + signal (SIGTERM, on_signal); +#else + terminate_event = CreateEvent (NULL, TRUE, FALSE, TEXT("TerminateEvent")); + SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); +#endif /* !_WIN32 */ + + if (!on_startup()) { + fprintf (stderr, "Startup failed\n"); + return EXIT_FAILURE; + } + +/* service loop */ + do { + time_t now; + time (&now); + const struct tm* time_ptr = localtime(&now); +#ifndef _WIN32 + char s[1024]; + const size_t slen = strftime (s, sizeof(s), TIME_FORMAT, time_ptr); + const int status = pgm_send (sock, s, slen + 1, NULL); +#else + char s[1024]; + const size_t slen = strftime (s, sizeof(s), TIME_FORMAT, time_ptr); + wchar_t ws[1024]; + size_t wslen = MultiByteToWideChar (CP_ACP, 0, s, slen, ws, 1024); + char us[1024]; + size_t uslen = WideCharToMultiByte (CP_UTF8, 0, ws, wslen + 1, us, sizeof(us), NULL, NULL); + const int status = pgm_send (sock, us, uslen + 1, NULL); +#endif + if (PGM_IO_STATUS_NORMAL != status) { + fprintf (stderr, "pgm_send() failed.\n"); + } +#ifndef _WIN32 + sleep (1); +#else + Sleep (1 * 1000); +#endif + } while (!is_terminated); + +/* cleanup */ + puts ("Waiting for NAK thread."); +#ifndef _WIN32 + pthread_join (nak_thread, NULL); + close (terminate_pipe[0]); + close (terminate_pipe[1]); +#else + WaitForSingleObject (nak_thread, INFINITE); + CloseHandle (nak_thread); + CloseHandle (terminate_event); +#endif /* !_WIN32 */ + + if (sock) { + puts ("Closing PGM sock."); + pgm_close (sock, TRUE); + sock = NULL; + } + + puts ("PGM engine shutdown."); + pgm_shutdown(); + puts ("finished."); + return EXIT_SUCCESS; +} + +#ifndef _WIN32 +static +void +on_signal ( + int signum + ) +{ + printf ("on_signal (signum:%d)\n", signum); + is_terminated = TRUE; + const char one = '1'; + const size_t writelen = write (terminate_pipe[1], &one, sizeof(one)); + assert (sizeof(one) == writelen); +} +#else +static +BOOL +on_console_ctrl ( + DWORD dwCtrlType + ) +{ + printf ("on_console_ctrl (dwCtrlType:%lu)\n", (unsigned long)dwCtrlType); + is_terminated = TRUE; + SetEvent (terminate_event); + return TRUE; +} +#endif /* !_WIN32 */ + +static +bool +on_startup (void) +{ + bool status = (create_sock() && create_nak_thread()); + if (status) + puts ("Startup complete."); + return status; +} + +static +bool +create_sock (void) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + +/* parse network parameter into sock address structure */ + if (!pgm_getaddrinfo (network, NULL, &res, &pgm_err)) { + fprintf (stderr, "Parsing network parameter: %s\n", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + puts ("Create PGM socket."); + if (udp_encap_port) { + if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + fprintf (stderr, "Creating PGM/UDP socket: %s\n", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (sock, IPPROTO_PGM, PGM_UDP_ENCAP_UCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_UDP_ENCAP_MCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + } else { + if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + fprintf (stderr, "Creating PGM/IP socket: %s\n", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (sock, IPPROTO_PGM, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int send_only = 1, + ambient_spm = pgm_secs (30), + heartbeat_spm[] = { pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (1300), + pgm_secs (7), + pgm_secs (16), + pgm_secs (25), + pgm_secs (30) }; + + pgm_setsockopt (sock, IPPROTO_PGM, PGM_SEND_ONLY, &send_only, sizeof(send_only)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_MTU, &max_tpdu, sizeof(max_tpdu)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_TXW_SQNS, &sqns, sizeof(sqns)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_TXW_MAX_RTE, &max_rte, sizeof(max_rte)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_AMBIENT_SPM, &ambient_spm, sizeof(ambient_spm)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_HEARTBEAT_SPM, &heartbeat_spm, sizeof(heartbeat_spm)); + +#ifdef I_UNDERSTAND_PGMCC_AND_FEC_ARE_NOT_SUPPORTED + if (use_pgmcc) { + struct pgm_pgmccinfo_t pgmccinfo; + pgmccinfo.ack_bo_ivl = pgm_msecs (50); + pgmccinfo.ack_c = 75; + pgmccinfo.ack_c_p = 500; + pgm_setsockopt (sock, IPPROTO_PGM, PGM_USE_PGMCC, &pgmccinfo, sizeof(pgmccinfo)); + } + if (use_fec) { + struct pgm_fecinfo_t fecinfo; + fecinfo.block_size = rs_n; + fecinfo.proactive_packets = proactive_packets; + fecinfo.group_size = rs_k; + fecinfo.ondemand_parity_enabled = use_ondemand_parity; + fecinfo.var_pktlen_enabled = TRUE; + pgm_setsockopt (sock, IPPROTO_PGM, PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); + } +#endif + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = port ? port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + fprintf (stderr, "Creating GSI: %s\n", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + fprintf (stderr, "Binding PGM socket: %s\n", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (sock, IPPROTO_PGM, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int nonblocking = 1, + multicast_loop = use_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (sock, IPPROTO_PGM, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (sock, IPPROTO_PGM, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); + + if (!pgm_connect (sock, &pgm_err)) { + fprintf (stderr, "Connecting PGM socket: %s\n", pgm_err->message); + goto err_abort; + } + + return TRUE; + +err_abort: + if (NULL != sock) { + pgm_close (sock, FALSE); + sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +static +bool +create_nak_thread (void) +{ +#ifndef _WIN32 + const int status = pthread_create (&nak_thread, NULL, &nak_routine, sock); + if (0 != status) { + fprintf (stderr, "Creating new thread: %s\n", strerror (status)); + return FALSE; + } +#else + nak_thread = (HANDLE)_beginthreadex (NULL, 0, &nak_routine, sock, 0, NULL); + const int save_errno = errno; + if (0 == nak_thread) { + fprintf (stderr, "Creating new thread: %s\n", strerror (save_errno)); + return FALSE; + } +#endif /* _WIN32 */ + return TRUE; +} + +static +#ifndef _WIN32 +void* +#else +unsigned +__stdcall +#endif +nak_routine ( + void* arg + ) +{ +/* dispatch loop */ + pgm_sock_t* nak_sock = (pgm_sock_t*)arg; +#ifndef _WIN32 + int fds; + fd_set readfds; +#else + int n_handles = 4, recv_sock, repair_sock, pending_sock; + HANDLE waitHandles[ 4 ]; + DWORD dwTimeout, dwEvents; + WSAEVENT recvEvent, repairEvent, pendingEvent; + socklen_t socklen = sizeof(int); + + recvEvent = WSACreateEvent (); + pgm_getsockopt (nak_sock, IPPROTO_PGM, PGM_RECV_SOCK, &recv_sock, &socklen); + WSAEventSelect (recv_sock, recvEvent, FD_READ); + repairEvent = WSACreateEvent (); + pgm_getsockopt (nak_sock, IPPROTO_PGM, PGM_REPAIR_SOCK, &repair_sock, &socklen); + WSAEventSelect (repair_sock, repairEvent, FD_READ); + pendingEvent = WSACreateEvent (); + pgm_getsockopt (nak_sock, IPPROTO_PGM, PGM_PENDING_SOCK, &pending_sock, &socklen); + WSAEventSelect (pending_sock, pendingEvent, FD_READ); + + waitHandles[0] = terminate_event; + waitHandles[1] = recvEvent; + waitHandles[2] = repairEvent; + waitHandles[3] = pendingEvent; +#endif /* !_WIN32 */ + do { + struct timeval tv; + char buf[4064]; + pgm_error_t* pgm_err = NULL; + const int status = pgm_recv (nak_sock, buf, sizeof(buf), 0, NULL, &pgm_err); + switch (status) { + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (sock, IPPROTO_PGM, PGM_TIME_REMAIN, &tv, &optlen); + } + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (sock, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &optlen); + } + case PGM_IO_STATUS_WOULD_BLOCK: +block: +#ifndef _WIN32 + fds = terminate_pipe[0] + 1; + FD_ZERO(&readfds); + FD_SET(terminate_pipe[0], &readfds); + pgm_select_info (nak_sock, &readfds, NULL, &fds); + fds = select (fds, &readfds, NULL, NULL, PGM_IO_STATUS_WOULD_BLOCK == status ? NULL : &tv); +#else + dwTimeout = PGM_IO_STATUS_WOULD_BLOCK == status ? INFINITE : (DWORD)((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + dwEvents = WaitForMultipleObjects (n_handles, waitHandles, FALSE, dwTimeout); + switch (dwEvents) { + case WAIT_OBJECT_0+1: WSAResetEvent (recvEvent); break; + case WAIT_OBJECT_0+2: WSAResetEvent (repairEvent); break; + case WAIT_OBJECT_0+3: WSAResetEvent (pendingEvent); break; + default: break; + } +#endif /* !_WIN32 */ + break; + + default: + if (pgm_err) { + fprintf (stderr, "%s\n", pgm_err->message ? pgm_err->message : "(null)"); + pgm_error_free (pgm_err); + pgm_err = NULL; + } + if (PGM_IO_STATUS_ERROR == status) + break; + } + } while (!is_terminated); +#ifndef _WIN32 + return NULL; +#else + WSACloseEvent (recvEvent); + WSACloseEvent (repairEvent); + WSACloseEvent (pendingEvent); + _endthread(); + return 0; +#endif +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/enonblocksyncrecv.c b/3rdparty/openpgm-svn-r1135/pgm/examples/enonblocksyncrecv.c new file mode 100644 index 0000000..3f24f76 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/enonblocksyncrecv.c @@ -0,0 +1,382 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Simple PGM receiver: epoll based non-blocking synchronous receiver. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef G_OS_UNIX +# include +# include +# include +# include +#endif +#include + +/* example dependencies */ +#include +#include + + +/* typedefs */ + +/* globals */ + +static int g_port = 0; +static const char* g_network = ""; +static gboolean g_multicast_loop = FALSE; +static int g_udp_encap_port = 0; + +static int g_max_tpdu = 1500; +static int g_sqns = 100; + +static pgm_sock_t* g_sock = NULL; +static gboolean g_quit = FALSE; + +static void on_signal (int); +static gboolean on_startup (void); + +static int on_data (gconstpointer, size_t, struct pgm_sockaddr_t*); + + +G_GNUC_NORETURN static +void +usage ( + const char* bin + ) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); + exit (1); +} + +int +main ( + int argc, + char* argv[] + ) +{ + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + + log_init (); + g_message ("enonblocksyncrecv"); + + if (!pgm_init (&pgm_err)) { + g_error ("Unable to start PGM engine: %s", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:lh")) != -1) + { + switch (c) { + case 'n': g_network = optarg; break; + case 's': g_port = atoi (optarg); break; + case 'p': g_udp_encap_port = atoi (optarg); break; + case 'l': g_multicast_loop = TRUE; break; + + case 'h': + case '?': usage (binary_name); + } + } + + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); + signal (SIGINT, on_signal); + signal (SIGTERM, on_signal); +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif + + if (!on_startup ()) { + g_error ("startup failed"); + return EXIT_FAILURE; + } + +/* epoll file descriptor */ + int efd = epoll_create (IP_MAX_MEMBERSHIPS); + if (efd < 0) { + g_error ("epoll_create failed errno %i: \"%s\"", errno, strerror(errno)); + return EXIT_FAILURE; + } + + int retval = pgm_epoll_ctl (g_sock, efd, EPOLL_CTL_ADD, EPOLLIN); + if (retval < 0) { + g_error ("pgm_epoll_ctl failed."); + return EXIT_FAILURE; + } + + struct epoll_event events[1]; /* wait for maximum 1 event */ + +/* dispatch loop */ + g_message ("entering PGM message loop ... "); + do { + struct timeval tv; + int timeout; + char buffer[4096]; + size_t len; + struct pgm_sockaddr_t from; + socklen_t fromlen = sizeof(from); + const int status = pgm_recvfrom (g_sock, + buffer, + sizeof(buffer), + 0, + &len, + &from, + &fromlen, + &pgm_err); + switch (status) { + case PGM_IO_STATUS_NORMAL: + on_data (buffer, len, &from); + break; + + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (g_sock, IPPROTO_PGM, PGM_TIME_REMAIN, &tv, &optlen); + } + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (g_sock, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &optlen); + } +/* fall through */ + case PGM_IO_STATUS_WOULD_BLOCK: +/* poll for next event */ +block: + timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + epoll_wait (efd, events, G_N_ELEMENTS(events), timeout /* ms */); + break; + + default: + if (pgm_err) { + g_warning ("%s", pgm_err->message); + pgm_error_free (pgm_err); + pgm_err = NULL; + } + if (PGM_IO_STATUS_ERROR == status) + break; + } + } while (!g_quit); + + g_message ("message loop terminated, cleaning up."); + +/* cleanup */ + close (efd); + if (g_sock) { + g_message ("closing PGM socket."); + pgm_close (g_sock, TRUE); + g_sock = NULL; + } + + g_message ("PGM engine shutdown."); + pgm_shutdown (); + g_message ("finished."); + return EXIT_SUCCESS; +} + +static +void +on_signal ( + int signum + ) +{ + g_message ("on_signal (signum:%d)", signum); + g_quit = TRUE; +} + +static +gboolean +on_startup (void) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + + g_message ("startup."); + +/* parse network parameter into transport address structure */ + if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { + g_error ("parsing network parameter: %s", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + if (g_udp_encap_port) { + g_message ("create PGM/UDP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + } else { + g_message ("create PGM/IP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int recv_only = 1, + passive = 0, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_secs (2), + nak_rdata_ivl = pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_PASSIVE, &passive, sizeof(passive)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + g_error ("creating GSI: %s", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (g_sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + g_error ("binding PGM socket: %s", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int nonblocking = 1, + multicast_loop = g_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); + + if (!pgm_connect (g_sock, &pgm_err)) { + g_error ("connecting PGM socket: %s", pgm_err->message); + goto err_abort; + } + + g_message ("startup complete."); + return TRUE; + +err_abort: + if (NULL != g_sock) { + pgm_close (g_sock, FALSE); + g_sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +static +int +on_data ( + gconstpointer data, + size_t len, + struct pgm_sockaddr_t* from + ) +{ +/* protect against non-null terminated strings */ + char buf[1024], tsi[PGM_TSISTRLEN]; + const size_t buflen = MIN(sizeof(buf) - 1, len); + strncpy (buf, (const char*)data, buflen); + buf[buflen] = '\0'; + pgm_tsi_print_r (&from->sa_addr, tsi, sizeof(tsi)); + + g_message ("\"%s\" (%u bytes from %s)", + buf, + (unsigned)len, + tsi); + + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/enonblocksyncrecvmsg.c b/3rdparty/openpgm-svn-r1135/pgm/examples/enonblocksyncrecvmsg.c new file mode 100644 index 0000000..43deb89 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/enonblocksyncrecvmsg.c @@ -0,0 +1,382 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Simple PGM receiver: blocking synchronous receiver with scatter/gather io + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef G_OS_UNIX +# include +# include +# include +# include +#endif +#include + +/* example dependencies */ +#include +#include + + +/* typedefs */ + +/* globals */ + +static int g_port = 0; +static const char* g_network = ""; +static gboolean g_multicast_loop = FALSE; +static int g_udp_encap_port = 0; + +static int g_max_tpdu = 1500; +static int g_sqns = 100; + +static pgm_sock_t* g_sock = NULL; +static gboolean g_quit = FALSE; + +static void on_signal (int); +static gboolean on_startup (void); + +static int on_datav (struct pgm_msgv_t*, size_t); + + +G_GNUC_NORETURN static +void +usage ( + const char* bin + ) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); + exit (1); +} + +int +main ( + int argc, + char* argv[] + ) +{ + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + + log_init (); + g_message ("enonblocksyncrecvmsg"); + + if (!pgm_init (&pgm_err)) { + g_error ("Unable to start PGM engine: %s", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:lh")) != -1) + { + switch (c) { + case 'n': g_network = optarg; break; + case 's': g_port = atoi (optarg); break; + case 'p': g_udp_encap_port = atoi (optarg); break; + case 'l': g_multicast_loop = TRUE; break; + + case 'h': + case '?': usage (binary_name); + } + } + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); + signal (SIGINT, on_signal); + signal (SIGTERM, on_signal); +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif + + if (!on_startup ()) { + g_error ("startup failed"); + return EXIT_FAILURE; + } + +/* epoll file descriptor */ + int efd = epoll_create (IP_MAX_MEMBERSHIPS); + if (efd < 0) { + g_error ("epoll_create failed errno %i: \"%s\"", errno, strerror(errno)); + return EXIT_FAILURE; + } + + int retval = pgm_epoll_ctl (g_sock, efd, EPOLL_CTL_ADD, EPOLLIN); + if (retval < 0) { + g_error ("pgm_epoll_ctl failed."); + return EXIT_FAILURE; + } + +/* incoming message buffer */ + struct pgm_msgv_t msgv; + struct epoll_event events[1]; /* wait for maximum 1 event */ + +/* dispatch loop */ + g_message ("entering PGM message loop ... "); + do { + struct timeval tv; + int timeout; + size_t len; + const int status = pgm_recvmsg (g_sock, + &msgv, + 0, + &len, + &pgm_err); + switch (status) { + case PGM_IO_STATUS_NORMAL: + on_datav (&msgv, len); + break; + + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (g_sock, IPPROTO_PGM, PGM_TIME_REMAIN, &tv, &optlen); + } + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (g_sock, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &optlen); + } +/* fall through */ + case PGM_IO_STATUS_WOULD_BLOCK: +/* poll for next event */ +block: + timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + epoll_wait (efd, events, G_N_ELEMENTS(events), timeout /* ms */); + break; + + default: + if (pgm_err) { + g_warning ("%s", pgm_err->message); + pgm_error_free (pgm_err); + pgm_err = NULL; + } + if (PGM_IO_STATUS_ERROR == status) + break; + } + } while (!g_quit); + + g_message ("message loop terminated, cleaning up."); + +/* cleanup */ + close (efd); + if (g_sock) { + g_message ("closing PGM socket."); + pgm_close (g_sock, TRUE); + g_sock = NULL; + } + + g_message ("PGM engine shutdown."); + pgm_shutdown (); + g_message ("finished."); + return EXIT_SUCCESS; +} + +static +void +on_signal ( + int signum + ) +{ + g_message ("on_signal (signum:%d)", signum); + g_quit = TRUE; +} + +static +gboolean +on_startup (void) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + + g_message ("startup."); + +/* parse network parameter into transport address structure */ + if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { + g_error ("parsing network parameter: %s", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + if (g_udp_encap_port) { + g_message ("create PGM/UDP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + } else { + g_message ("create PGM/IP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int recv_only = 1, + passive = 0, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_secs (2), + nak_rdata_ivl = pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_PASSIVE, &passive, sizeof(passive)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + g_error ("creating GSI: %s", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (g_sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + g_error ("binding PGM socket: %s", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int nonblocking = 1, + multicast_loop = g_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); + + if (!pgm_connect (g_sock, &pgm_err)) { + g_error ("connecting PGM socket: %s", pgm_err->message); + goto err_abort; + } + + g_message ("startup complete."); + return TRUE; + +err_abort: + if (NULL != g_sock) { + pgm_close (g_sock, FALSE); + g_sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +static +int +on_datav ( + struct pgm_msgv_t* datav, /* one msgv object */ + size_t len + ) +{ + char tsi[PGM_TSISTRLEN]; + pgm_tsi_print_r (&datav->msgv_skb[0]->tsi, tsi, sizeof(tsi)); + g_message ("(%u bytes from %s)", (unsigned)len, tsi); + +/* protect against non-null terminated strings */ + const struct pgm_sk_buff_t* skb = datav->msgv_skb[0]; + int i = 0; + while (len) + { + char buf[1024]; + const size_t buflen = MIN( sizeof(buf) - 1, skb->len ); + strncpy (buf, (const char*)skb->data, buflen); + buf[buflen] = '\0'; + g_message ("\t%i: %s (%" G_GUINT16_FORMAT " bytes)", ++i, buf, skb->len); + len -= skb->len; + skb++; + } + + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/enonblocksyncrecvmsgv.c b/3rdparty/openpgm-svn-r1135/pgm/examples/enonblocksyncrecvmsgv.c new file mode 100644 index 0000000..f41a002 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/enonblocksyncrecvmsgv.c @@ -0,0 +1,397 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Simple PGM receiver: epoll based non-blocking synchronous receiver with scatter/gather io + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef G_OS_UNIX +# include +# include +# include +# include +# include +#endif +#include + +/* example dependencies */ +#include +#include + + +/* typedefs */ + +/* globals */ + +static int g_port = 0; +static const char* g_network = ""; +static gboolean g_multicast_loop = FALSE; +static int g_udp_encap_port = 0; + +static int g_max_tpdu = 1500; +static int g_sqns = 100; + +static pgm_sock_t* g_sock = NULL; +static gboolean g_quit = FALSE; + +static void on_signal (int); +static gboolean on_startup (void); + +static int on_msgv (struct pgm_msgv_t*, size_t); + + +G_GNUC_NORETURN static +void +usage ( + const char* bin + ) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); + exit (1); +} + +int +main ( + int argc, + char* argv[] + ) +{ + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + + log_init (); + g_message ("enonblocksyncrecvmsgv"); + + if (!pgm_init (&pgm_err)) { + g_error ("Unable to start PGM engine: %s", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:lh")) != -1) + { + switch (c) { + case 'n': g_network = optarg; break; + case 's': g_port = atoi (optarg); break; + case 'p': g_udp_encap_port = atoi (optarg); break; + case 'l': g_multicast_loop = TRUE; break; + + case 'h': + case '?': usage (binary_name); + } + } + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); + signal (SIGINT, on_signal); + signal (SIGTERM, on_signal); +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif + + if (!on_startup ()) { + g_error ("startup failed"); + return EXIT_FAILURE; + } + +/* incoming message buffer, iov_len must be less than SC_IOV_MAX */ + const long iov_len = 8; + const long ev_len = 1; + g_message ("Using iov_len %li ev_len %li", iov_len, ev_len); + + struct pgm_msgv_t msgv[iov_len]; + struct epoll_event events[ev_len]; /* wait for maximum 1 event */ + +/* epoll file descriptor */ + const int efd = epoll_create (IP_MAX_MEMBERSHIPS); + if (efd < 0) { + g_error ("epoll_create failed errno %i: \"%s\"", errno, strerror(errno)); + return EXIT_FAILURE; + } + + const int retval = pgm_epoll_ctl (g_sock, efd, EPOLL_CTL_ADD, EPOLLIN); + if (retval < 0) { + g_error ("pgm_epoll_ctl failed."); + return EXIT_FAILURE; + } + +/* dispatch loop */ + g_message ("entering PGM message loop ... "); + do { + struct timeval tv; + int timeout; + size_t len; + const int status = pgm_recvmsgv (g_sock, + msgv, + iov_len, + 0, + &len, + &pgm_err); + switch (status) { + case PGM_IO_STATUS_NORMAL: + on_msgv (msgv, len); + break; + + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (g_sock, IPPROTO_PGM, PGM_TIME_REMAIN, &tv, &optlen); + } + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (g_sock, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &optlen); + } +/* fall through */ + case PGM_IO_STATUS_WOULD_BLOCK: +/* poll for next event */ +block: + timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + epoll_wait (efd, events, G_N_ELEMENTS(events), timeout /* ms */); + break; + + default: + if (pgm_err) { + g_warning ("%s", pgm_err->message); + pgm_error_free (pgm_err); + pgm_err = NULL; + } + if (PGM_IO_STATUS_ERROR == status) + break; + } + } while (!g_quit); + + g_message ("message loop terminated, cleaning up."); + +/* cleanup */ + close (efd); + if (g_sock) { + g_message ("closing PGM socket."); + pgm_close (g_sock, TRUE); + g_sock = NULL; + } + + g_message ("PGM engine shutdown."); + pgm_shutdown (); + g_message ("finished."); + return EXIT_SUCCESS; +} + +static +void +on_signal ( + int signum + ) +{ + g_message ("on_signal (signum:%d)", signum); + g_quit = TRUE; +} + +static +gboolean +on_startup (void) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + + g_message ("startup."); + +/* parse network parameter into transport address structure */ + if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { + g_error ("parsing network parameter: %s", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + if (g_udp_encap_port) { + g_message ("create PGM/UDP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + } else { + g_message ("create PGM/IP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int recv_only = 1, + passive = 0, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_secs (2), + nak_rdata_ivl = pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_PASSIVE, &passive, sizeof(passive)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + g_error ("creating GSI: %s", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (g_sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + g_error ("binding PGM socket: %s", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int nonblocking = 1, + multicast_loop = g_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); + + if (!pgm_connect (g_sock, &pgm_err)) { + g_error ("connecting PGM socket: %s", pgm_err->message); + goto err_abort; + } + + g_message ("startup complete."); + return TRUE; + +err_abort: + if (NULL != g_sock) { + pgm_close (g_sock, FALSE); + g_sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +static +int +on_msgv ( + struct pgm_msgv_t* msgv, /* an array of msgv's */ + size_t len /* total size of all msgv's */ + ) +{ + g_message ("(%u bytes)", + (unsigned)len); + + guint i = 0; + +/* for each apdu display each fragment */ + do { + const struct pgm_sk_buff_t* pskb = msgv[i].msgv_skb[0]; + gsize apdu_len = 0; + for (unsigned j = 0; j < msgv[i].msgv_len; j++) + apdu_len += msgv[i].msgv_skb[j]->len; +/* truncate to first fragment to make GLib printing happy */ + char buf[1024], tsi[PGM_TSISTRLEN]; + const size_t buflen = MIN( sizeof(buf) - 1, pskb->len ); + strncpy (buf, (const char*)pskb->data, buflen); + buf[buflen] = '\0'; + pgm_tsi_print_r (&pskb->tsi, tsi, sizeof(tsi)); + if (msgv[i].msgv_len > 1) { + g_message ("\t%u: \"%s\" ... (%" G_GSIZE_FORMAT " bytes from %s)", i, buf, apdu_len, tsi); + } else { + g_message ("\t%u: \"%s\" (%" G_GSIZE_FORMAT " bytes from %s)", i, buf, apdu_len, tsi); + } + i++; + len -= apdu_len; + } while (len); + + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/getopt.c b/3rdparty/openpgm-svn-r1135/pgm/examples/getopt.c new file mode 100644 index 0000000..8c655b6 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/getopt.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include "getopt.h" + +int opterr = 1, /* if error message should be printed */ + optind = 1, /* index into parent argv vector */ + optopt, /* character checked for validity */ + optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int +getopt(int nargc, char* const* nargv, const char* ostr) +{ + static char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc || *(place = nargv[optind]) != '-') { + place = EMSG; + return (-1); + } + if (place[1] && *++place == '-') { /* found "--" */ + ++optind; + place = EMSG; + return (-1); + } + } /* option letter okay? */ + if ((optopt = (int)*place++) == (int)':' || + !(oli = strchr(ostr, optopt))) { + /* + * if the user didn't specify '-' as an option, + * assume it means -1. + */ + if (optopt == (int)'-') + return (-1); + if (!*place) + ++optind; + if (opterr && *ostr != ':' && optopt != BADCH) + (void)fprintf(stderr, "%s: illegal option -- %c\n", + "progname", optopt); + return (BADCH); + } + if (*++oli != ':') { /* don't need argument */ + optarg = NULL; + if (!*place) + ++optind; + } + else { /* need an argument */ + if (*place) /* no white space */ + optarg = place; + else if (nargc <= ++optind) { /* no arg */ + place = EMSG; + if (*ostr == ':') + return (BADARG); + if (opterr) + (void)fprintf(stderr, + "%s: option requires an argument -- %c\n", + "progname", optopt); + return (BADCH); + } + else /* white space */ + optarg = nargv[optind]; + place = EMSG; + ++optind; + } + return (optopt); /* dump back option letter */ +} + diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/getopt.h b/3rdparty/openpgm-svn-r1135/pgm/examples/getopt.h new file mode 100644 index 0000000..f04387b --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/getopt.h @@ -0,0 +1,62 @@ +/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */ +/* $FreeBSD: src/include/getopt.h,v 1.1 2002/09/29 04:14:30 eric Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _GETOPT_H_ +#define _GETOPT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* These are global getopt variables */ +extern int opterr, /* if error message should be printed */ + optind, /* index into parent argv vector */ + optopt, /* character checked for validity */ + optreset; /* reset getopt */ +extern char* optarg; /* argument associated with option */ + +/* Original getopt */ +int getopt (int, char*const*, const char*); + +#ifdef __cplusplus +} +#endif + +#endif /* !_GETOPT_H_ */ + diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/pgmdump.c b/3rdparty/openpgm-svn-r1135/pgm/examples/pgmdump.c new file mode 100644 index 0000000..481b12e --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/pgmdump.c @@ -0,0 +1,279 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Dump PGM packets to the console. + * + * Copyright (c) 2006-2007 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef G_OS_UNIX +# include +# include +# include +# include +#endif + +#include + +/* PGM internals */ +#include + +/* example dependencies */ +#include +#include + + +/* globals */ + +static const char* g_network = "239.192.0.1"; + +static GIOChannel* g_io_channel = NULL; +static GMainLoop* g_loop = NULL; + + +static void on_signal (int); +static gboolean on_startup (gpointer); +static gboolean on_mark (gpointer); + +static gboolean on_io_data (GIOChannel*, GIOCondition, gpointer); + + +int +main ( + G_GNUC_UNUSED int argc, + G_GNUC_UNUSED char *argv[] + ) +{ + GError* err = NULL; + + setlocale (LC_ALL, ""); + + log_init (); + g_message ("pgmdump"); + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); + signal (SIGINT, on_signal); + signal (SIGTERM, on_signal); +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif + +/* delayed startup */ + g_message ("scheduling startup."); + g_timeout_add (0, (GSourceFunc)on_startup, NULL); + +/* dispatch loop */ + g_loop = g_main_loop_new (NULL, FALSE); + + g_message ("entering main event loop ... "); + g_main_loop_run (g_loop); + + g_message ("event loop terminated, cleaning up."); + +/* cleanup */ + g_main_loop_unref (g_loop); + g_loop = NULL; + + if (g_io_channel) { + g_message ("closing socket."); + g_io_channel_shutdown (g_io_channel, FALSE, &err); + g_io_channel = NULL; + } + + g_message ("finished."); + return EXIT_SUCCESS; +} + +static void +on_signal ( + G_GNUC_UNUSED int signum + ) +{ + puts ("on_signal"); + + g_main_loop_quit(g_loop); +} + +static +gboolean +on_startup ( + G_GNUC_UNUSED gpointer data + ) +{ + int e; + + g_message ("startup."); + +/* find PGM protocol id */ +// TODO: fix valgrind errors + int ipproto_pgm = IPPROTO_PGM; +#ifdef HAVE_GETPROTOBYNAME_R + char b[1024]; + struct protoent protobuf, *proto; + e = getprotobyname_r ("pgm", &protobuf, b, sizeof(b), &proto); + if (e != -1 && proto != NULL) { + if (proto->p_proto != ipproto_pgm) { + g_message ("Setting PGM protocol number to %i from /etc/protocols.\n"); + ipproto_pgm = proto->p_proto; + } + } +#else + struct protoent *proto = getprotobyname ("pgm"); + if (proto != NULL) { + if (proto->p_proto != ipproto_pgm) { + g_message ("Setting PGM protocol number to %i from /etc/protocols.\n", proto +->p_proto); + ipproto_pgm = proto->p_proto; + } + } +#endif + +/* open socket for snooping */ + g_message ("opening raw socket."); + int sock = socket (PF_INET, SOCK_RAW, ipproto_pgm); + if (sock < 0) { + perror("on_startup() failed"); +#ifdef G_OS_UNIX + if (EPERM == errno && 0 != getuid()) { + g_message ("PGM protocol requires this program to run as superuser."); + } +#endif + g_main_loop_quit (g_loop); + return FALSE; + } + +#ifdef G_OS_UNIX +/* drop out of setuid 0 */ + if (0 == getuid ()) { + g_message ("dropping superuser privileges."); + setuid ((gid_t)65534); + setgid ((uid_t)65534); + } +#endif + + char _t = 1; + e = setsockopt (sock, IPPROTO_IP, IP_HDRINCL, &_t, sizeof(_t)); + if (e < 0) { + perror ("on_startup() failed"); + close (sock); + g_main_loop_quit (g_loop); + return FALSE; + } + +/* buffers */ + int buffer_size = 0; + socklen_t len = 0; + e = getsockopt (sock, SOL_SOCKET, SO_RCVBUF, (char*)&buffer_size, &len); + if (e == 0) { + g_message ("receive buffer set at %i bytes.\n", buffer_size); + } + e = getsockopt (sock, SOL_SOCKET, SO_SNDBUF, (char*)&buffer_size, &len); + if (e == 0) { + g_message ("send buffer set at %i bytes.\n", buffer_size); + } + +/* bind */ + struct sockaddr_in addr; + memset (&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + e = bind (sock, (struct sockaddr*)&addr, sizeof(addr)); + if (e < 0) { + perror ("on_startup() failed"); + close (sock); + g_main_loop_quit (g_loop); + return FALSE; + } + +/* multicast */ + struct ip_mreq mreq; + memset (&mreq, 0, sizeof(mreq)); + mreq.imr_interface.s_addr = htonl (INADDR_ANY); + g_message ("listening on interface %s.\n", inet_ntoa (mreq.imr_interface)); + mreq.imr_multiaddr.s_addr = inet_addr (g_network); + g_message ("subscription on multicast address %s.\n", inet_ntoa (mreq.imr_multiaddr)); + e = setsockopt (sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)); + if (e < 0) { + perror ("on_startup() failed"); + close (sock); + g_main_loop_quit (g_loop); + return FALSE; + } + +/* multicast loopback */ +/* multicast ttl */ + +/* add socket to event manager */ + g_io_channel = g_io_channel_unix_new (sock); + g_message ("socket opened with encoding %s.\n", g_io_channel_get_encoding (g_io_channel)); + + /* guint event = */ g_io_add_watch (g_io_channel, G_IO_IN | G_IO_PRI, on_io_data, NULL); + +/* period timer to indicate some form of life */ +// TODO: Gnome 2.14: replace with g_timeout_add_seconds() + g_timeout_add (10 * 1000, (GSourceFunc)on_mark, NULL); + + g_message ("startup complete."); + return FALSE; +} + +static gboolean +on_mark ( + G_GNUC_UNUSED gpointer data + ) +{ + g_message ("-- MARK --"); + return TRUE; +} + +static gboolean +on_io_data ( + GIOChannel* source, + G_GNUC_UNUSED GIOCondition condition, + G_GNUC_UNUSED gpointer data + ) +{ + char buffer[4096]; + + int fd = g_io_channel_unix_get_fd (source); + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + int len = recvfrom (fd, buffer, sizeof(buffer), MSG_DONTWAIT, (struct sockaddr*)&addr, &addr_len); + + g_message ("%i bytes received from %s.\n", len, inet_ntoa (addr.sin_addr)); + + if (!pgm_print_packet (buffer, len)) { + g_message ("invalid packet :("); + } + + fflush (stdout); + + return TRUE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/pgmping.cc b/3rdparty/openpgm-svn-r1135/pgm/examples/pgmping.cc new file mode 100644 index 0000000..0c7e702 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/pgmping.cc @@ -0,0 +1,1225 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Simple send/reply ping tool using the PGM transport. + * + * With no arguments, one message is sent per second. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* c99 compatibility for c++ */ +#define __STDC_LIMIT_MACROS + +/* Must be first for Sun */ +#include "ping.pb.h" + +/* c99 compatibility for c++ */ +#define __STDC_FORMAT_MACROS +#define restrict + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#ifdef CONFIG_HAVE_EPOLL +# include +#endif +#include +#ifndef _WIN32 +# include +# include +# include +# include +# include +#endif +#include +#include +#ifdef CONFIG_WITH_HTTP +# include +#endif +#ifdef CONFIG_WITH_SNMP +# include +#endif + +/* PGM internal time keeper */ +typedef pgm_time_t (*pgm_time_update_func)(void); +extern pgm_time_update_func pgm_time_update_now; +extern "C" { + size_t pgm_pkt_offset (bool, sa_family_t); +} + +/* example dependencies */ +#include +#include +#include + + +using namespace std; + + +/* globals */ + +static int g_port = 0; +static const char* g_network = ""; +static int g_udp_encap_port = 0; + +static int g_odata_rate = 0; +static int g_odata_interval = 0; +static guint32 g_payload = 0; +static int g_max_tpdu = 1500; +static int g_max_rte = 16*1000*1000; +static int g_sqns = 200; + +static gboolean g_use_pgmcc = FALSE; +static sa_family_t g_pgmcc_family = 0; /* 0 = disabled */ + +static gboolean g_use_fec = FALSE; +static int g_rs_k = 8; +static int g_rs_n = 255; + +static enum { + PGMPING_MODE_SOURCE, + PGMPING_MODE_RECEIVER, + PGMPING_MODE_INITIATOR, + PGMPING_MODE_REFLECTOR +} g_mode = PGMPING_MODE_INITIATOR; + +static pgm_sock_t* g_sock = NULL; + +/* stats */ +static guint64 g_msg_sent = 0; +static guint64 g_msg_received = 0; +static pgm_time_t g_interval_start = 0; +static pgm_time_t g_latency_current = 0; +static guint64 g_latency_seqno = 0; +static guint64 g_last_seqno = 0; +static double g_latency_total = 0.0; +static double g_latency_square_total = 0.0; +static guint64 g_latency_count = 0; +static double g_latency_max = 0.0; +#ifdef INFINITY +static double g_latency_min = INFINITY; +#else +static double g_latency_min = INT64_MAX; +#endif +static double g_latency_running_average = 0.0; +static guint64 g_out_total = 0; +static guint64 g_in_total = 0; + +static GMainLoop* g_loop = NULL; +static GThread* g_sender_thread = NULL; +static GThread* g_receiver_thread = NULL; +static gboolean g_quit; +#ifdef G_OS_UNIX +static int g_quit_pipe[2]; +static void on_signal (int, gpointer); +#else +static HANDLE g_quit_event; +static BOOL on_console_ctrl (DWORD); +#endif + +static gboolean on_startup (gpointer); +static gboolean on_shutdown (gpointer); +static gboolean on_mark (gpointer); + +static void send_odata (void); +static int on_msgv (struct pgm_msgv_t*, size_t); + +static gpointer sender_thread (gpointer); +static gpointer receiver_thread (gpointer); + + +G_GNUC_NORETURN static void +usage (const char* bin) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -d : Terminate transport after duration.\n"); + fprintf (stderr, " -m : Number of message to send per second\n"); + fprintf (stderr, " -o : Send-only mode (default send & receive mode)\n"); + fprintf (stderr, " -l : Listen-only mode\n"); + fprintf (stderr, " -e : Relect mode\n"); + fprintf (stderr, " -r : Regulate to rate bytes per second\n"); + fprintf (stderr, " -c : Enable PGMCC\n"); + fprintf (stderr, " -f : Enable FEC with either proactive or ondemand parity\n"); + fprintf (stderr, " -K : Configure Reed-Solomon code (n, k)\n"); + fprintf (stderr, " -N \n"); + fprintf (stderr, " -H : Enable HTTP administrative interface\n"); + fprintf (stderr, " -S : Enable SNMP interface\n"); + exit (1); +} + +int +main ( + int argc, + char *argv[] + ) +{ + GError* err = NULL; + pgm_error_t* pgm_err = NULL; + gboolean enable_http = FALSE; + gboolean enable_snmpx = FALSE; + int timeout = 0; + + GOOGLE_PROTOBUF_VERIFY_VERSION; + + setlocale (LC_ALL, ""); + setenv ("PGM_TIMER", "GTOD", 1); + setenv ("PGM_SLEEP", "USLEEP", 1); + + log_init (); + g_message ("pgmping"); + + g_thread_init (NULL); + + if (!pgm_init (&pgm_err)) { + g_error ("Unable to start PGM engine: %s", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = g_get_prgname(); + int c; + while ((c = getopt (argc, argv, "s:n:p:m:old:r:cfeK:N:HSh")) != -1) + { + switch (c) { + case 'n': g_network = optarg; break; + case 's': g_port = atoi (optarg); break; + case 'p': g_udp_encap_port = atoi (optarg); break; + case 'r': g_max_rte = atoi (optarg); break; + + case 'c': g_use_pgmcc = TRUE; break; + + case 'f': g_use_fec = TRUE; break; + case 'K': g_rs_k = atoi (optarg); break; + case 'N': g_rs_n = atoi (optarg); break; + + case 'H': enable_http = TRUE; break; + case 'S': enable_snmpx = TRUE; break; + + case 'm': g_odata_rate = atoi (optarg); + g_odata_interval = (1000 * 1000) / g_odata_rate; break; + case 'd': timeout = 1000 * atoi (optarg); break; + + case 'o': g_mode = PGMPING_MODE_SOURCE; break; + case 'l': g_mode = PGMPING_MODE_RECEIVER; break; + case 'e': g_mode = PGMPING_MODE_REFLECTOR; break; + + case 'h': + case '?': usage (binary_name); + } + } + + if (g_use_fec && ( !g_rs_k || !g_rs_n )) { + g_error ("Invalid Reed-Solomon parameters."); + usage (binary_name); + } + +#ifdef CONFIG_WITH_HTTP + if (enable_http) { + if (!pgm_http_init (PGM_HTTP_DEFAULT_SERVER_PORT, &pgm_err)) { + g_error ("Unable to start HTTP interface: %s", pgm_err->message); + pgm_error_free (pgm_err); + pgm_shutdown (); + return EXIT_FAILURE; + } + } +#endif +#ifdef CONFIG_WITH_SNMP + if (enable_snmpx) { + if (!pgm_snmp_init (&pgm_err)) { + g_error ("Unable to start SNMP interface: %s", pgm_err->message); + pgm_error_free (pgm_err); +#ifdef CONFIG_WITH_HTTP + if (enable_http) + pgm_http_shutdown (); +#endif + pgm_shutdown (); + return EXIT_FAILURE; + } + } +#endif + + g_loop = g_main_loop_new (NULL, FALSE); + + g_quit = FALSE; + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif +#ifdef G_OS_UNIX + pipe (g_quit_pipe); + pgm_signal_install (SIGINT, on_signal, g_loop); + pgm_signal_install (SIGTERM, on_signal, g_loop); +#else + g_quit_event = CreateEvent (NULL, TRUE, FALSE, TEXT("QuitEvent")); + SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); +#endif /* !G_OS_UNIX */ + +/* delayed startup */ + g_message ("scheduling startup."); + g_timeout_add (0, (GSourceFunc)on_startup, g_loop); + + if (timeout) { + g_message ("scheduling shutdown."); + g_timeout_add (timeout, (GSourceFunc)on_shutdown, g_loop); + } + +/* dispatch loop */ + g_message ("entering main event loop ... "); + g_main_loop_run (g_loop); + + g_message ("event loop terminated, cleaning up."); + +/* cleanup */ + g_quit = TRUE; +#ifdef G_OS_UNIX + const char one = '1'; + write (g_quit_pipe[1], &one, sizeof(one)); + if (PGMPING_MODE_SOURCE == g_mode || PGMPING_MODE_INITIATOR == g_mode) + g_thread_join (g_sender_thread); + g_thread_join (g_receiver_thread); + close (g_quit_pipe[0]); + close (g_quit_pipe[1]); +#else + SetEvent (g_quit_event); + if (PGMPING_MODE_SOURCE == g_mode || PGMPING_MODE_INITIATOR == g_mode) + g_thread_join (g_sender_thread); + g_thread_join (g_receiver_thread); + CloseHandle (g_quit_event); +#endif + + g_main_loop_unref (g_loop); + g_loop = NULL; + + if (g_sock) { + g_message ("closing PGM socket."); + pgm_close (g_sock, TRUE); + g_sock = NULL; + } + +#ifdef CONFIG_WITH_HTTP + if (enable_http) + pgm_http_shutdown(); +#endif +#ifdef CONFIG_WITH_SNMP + if (enable_snmpx) + pgm_snmp_shutdown(); +#endif + + google::protobuf::ShutdownProtobufLibrary(); + + g_message ("PGM engine shutdown."); + pgm_shutdown (); + g_message ("finished."); + return EXIT_SUCCESS; +} + +#ifdef G_OS_UNIX +static +void +on_signal ( + int signum, + gpointer user_data + ) +{ + GMainLoop* loop = (GMainLoop*)user_data; + g_message ("on_signal (signum:%d user-data:%p)", + signum, user_data); + g_main_loop_quit (loop); +} +#else +static +BOOL +on_console_ctrl ( + DWORD dwCtrlType + ) +{ + g_message ("on_console_ctrl (dwCtrlType:%lu)", (unsigned long)dwCtrlType); + g_main_loop_quit (g_loop); + return TRUE; +} +#endif /* !G_OS_UNIX */ + +static +gboolean +on_shutdown ( + gpointer user_data + ) +{ + GMainLoop* loop = (GMainLoop*)user_data; + g_message ("on_shutdown (user-data:%p)", user_data); + g_main_loop_quit (loop); + return FALSE; +} + +static +gboolean +on_startup ( + gpointer user_data + ) +{ + GMainLoop* loop = (GMainLoop*)user_data; + struct pgm_addrinfo_t* res = NULL; + GError* err = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + + g_message ("startup."); + +/* parse network parameter into transport address structure */ + if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { + g_error ("parsing network parameter: %s", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + if (g_use_pgmcc) + g_pgmcc_family = sa_family; + + if (g_udp_encap_port) { + g_message ("create PGM/UDP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port))) { + g_error ("setting PGM_UDP_ENCAP_UCAST_PORT = %d", g_udp_encap_port); + goto err_abort; + } + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port))) { + g_error ("setting PGM_UDP_ENCAP_MCAST_PORT = %d", g_udp_encap_port); + goto err_abort; + } + } else { + g_message ("create PGM/IP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + { + const int no_router_assist = 0; + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist))) { + g_error ("setting PGM_IP_ROUTER_ALERT = %d", no_router_assist); + goto err_abort; + } + } + + pgm_drop_superuser(); + +/* set PGM parameters */ +/* common */ + { + const int bufsize = 1024 * 1024, + max_tpdu = g_max_tpdu; + + if (!pgm_setsockopt (g_sock, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize))) { + g_error ("setting SO_RCVBUF = %d", bufsize); + goto err_abort; + } + if (!pgm_setsockopt (g_sock, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize))) { + g_error ("setting SO_SNDBUF = %d", bufsize); + goto err_abort; + } + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MTU, &max_tpdu, sizeof(max_tpdu))) { + g_error ("setting PGM_MTU = %d", max_tpdu); + goto err_abort; + } + } + +/* send side */ + if (PGMPING_MODE_SOURCE == g_mode || + PGMPING_MODE_INITIATOR == g_mode || + PGMPING_MODE_REFLECTOR == g_mode ) + { + const int send_only = (PGMPING_MODE_SOURCE == g_mode) ? 1 : 0, + txw_sqns = g_sqns * 4, + txw_max_rte = g_max_rte, + ambient_spm = pgm_secs (30), + heartbeat_spm[] = { pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (1300), + pgm_secs (7), + pgm_secs (16), + pgm_secs (25), + pgm_secs (30) }; + + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_SEND_ONLY, &send_only, sizeof(send_only))) { + g_error ("setting PGM_SEND_ONLY = %d", send_only); + goto err_abort; + } + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_TXW_SQNS, &txw_sqns, sizeof(txw_sqns))) { + g_error ("setting PGM_TXW_SQNS = %d", txw_sqns); + goto err_abort; + } + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_TXW_MAX_RTE, &txw_max_rte, sizeof(txw_max_rte))) { + g_error ("setting PGM_TXW_MAX_RTE = %d", txw_max_rte); + goto err_abort; + } + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_AMBIENT_SPM, &ambient_spm, sizeof(ambient_spm))) { + g_error ("setting PGM_AMBIENT_SPM = %d", ambient_spm); + goto err_abort; + } + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_HEARTBEAT_SPM, &heartbeat_spm, sizeof(heartbeat_spm))) { + char buffer[1024]; + sprintf (buffer, "%d", heartbeat_spm[0]); + for (unsigned i = 1; i < G_N_ELEMENTS(heartbeat_spm); i++) { + char t[1024]; + sprintf (t, ", %d", heartbeat_spm[i]); + strcat (buffer, t); + } + g_error ("setting HEARTBEAT_SPM = { %s }", buffer); + goto err_abort; + } + + } + +/* receive side */ + if (PGMPING_MODE_RECEIVER == g_mode || + PGMPING_MODE_INITIATOR == g_mode || + PGMPING_MODE_REFLECTOR == g_mode ) + { + const int recv_only = (PGMPING_MODE_RECEIVER == g_mode) ? 1 : 0, + not_passive = 0, + rxw_sqns = g_sqns, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_msecs (200), //pgm_secs (2), + nak_rdata_ivl = pgm_msecs (200), //pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_RECV_ONLY, &recv_only, sizeof(recv_only))) { + g_error ("setting PGM_RECV_ONLY = %d", recv_only); + goto err_abort; + } + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_PASSIVE, ¬_passive, sizeof(not_passive))) { + g_error ("setting PGM_PASSIVE = %d", not_passive); + goto err_abort; + } + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_RXW_SQNS, &rxw_sqns, sizeof(rxw_sqns))) { + g_error ("setting PGM_RXW_SQNS = %d", rxw_sqns); + goto err_abort; + } + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry))) { + g_error ("setting PGM_PEER_EXPIRY = %d", peer_expiry); + goto err_abort; + } + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry))) { + g_error ("setting PGM_SPMR_EXPIRY = %d", spmr_expiry); + goto err_abort; + } + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl))) { + g_error ("setting PGM_NAK_BO_IVL = %d", nak_bo_ivl); + goto err_abort; + } + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl))) { + g_error ("setting PGM_NAK_RPT_IVL = %d", nak_rpt_ivl); + goto err_abort; + } + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl))) { + g_error ("setting PGM_NAK_RDATA_IVL = %d", nak_rdata_ivl); + goto err_abort; + } + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries))) { + g_error ("setting PGM_NAK_DATA_RETRIES = %d", nak_data_retries); + goto err_abort; + } + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries))) { + g_error ("setting PGM_NAK_NCF_RETRIES = %d", nak_ncf_retries); + goto err_abort; + } + } + +#ifdef I_UNDERSTAND_PGMCC_AND_FEC_ARE_NOT_SUPPORTED +/* PGMCC congestion control */ + if (g_use_pgmcc) { + struct pgm_pgmccinfo_t pgmccinfo; + pgmccinfo.ack_bo_ivl = pgm_msecs (50); + pgmccinfo.ack_c = 75; + pgmccinfo.ack_c_p = 500; + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_USE_PGMCC, &pgmccinfo, sizeof(pgmccinfo))) { + g_error ("setting PGM_USE_PGMCC = { ack_bo_ivl = %d ack_c = %d ack_c_p = %d }", + pgmccinfo.ack_bo_ivl, + pgmccinfo.ack_c, + pgmccinfo.ack_c_p); + goto err_abort; + } + } + +/* Reed Solomon forward error correction */ + if (g_use_fec) { + struct pgm_fecinfo_t fecinfo; + fecinfo.block_size = g_rs_n; + fecinfo.proactive_packets = 0; + fecinfo.group_size = g_rs_k; + fecinfo.ondemand_parity_enabled = TRUE; + fecinfo.var_pktlen_enabled = TRUE; + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_USE_FEC, &fecinfo, sizeof(fecinfo))) { + g_error ("setting PGM_USE_FEC = { block_size = %d proactive_packets = %d group_size = %d ondemand_parity_enabled = %s var_pktlen_enabled = %s }", + fecinfo.block_size, + fecinfo.proactive_packets, + fecinfo.group_size, + fecinfo.ondemand_parity_enabled ? "TRUE" : "FALSE", + fecinfo.var_pktlen_enabled ? "TRUE" : "FALSE"); + goto err_abort; + } + } +#endif + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = (0 != g_port) ? g_port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + g_error ("creating GSI: %s", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (g_sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + g_error ("binding PGM socket: %s", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + { + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req))) { + char group[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&res->ai_recv_addrs[i].gsr_group, sizeof(struct sockaddr_in), + group, sizeof(group), + NULL, 0, + NI_NUMERICHOST); + g_error ("setting PGM_JOIN_GROUP = { #%u %s }", + (unsigned)res->ai_recv_addrs[i].gsr_interface, + group); + goto err_abort; + } + } + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req))) { + char group[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&res->ai_send_addrs[0].gsr_group, sizeof(struct sockaddr_in), + group, sizeof(group), + NULL, 0, + NI_NUMERICHOST); + g_error ("setting PGM_SEND_GROUP = { #%u %s }", + (unsigned)res->ai_send_addrs[0].gsr_interface, + group); + goto err_abort; + } + pgm_freeaddrinfo (res); + +/* set IP parameters */ + { + const int nonblocking = 1, + multicast_direct = 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MULTICAST_LOOP, &multicast_direct, sizeof(multicast_direct))) { + g_error ("setting PGM_MULTICAST_LOOP = %d", multicast_direct); + goto err_abort; + } + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops))) { + g_error ("setting PGM_MULTICAST_HOPS = %d", multicast_hops); + goto err_abort; + } + if (AF_INET6 != sa_family) { + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_TOS, &dscp, sizeof(dscp))) { + g_error ("setting PGM_TOS = 0x%x", dscp); + goto err_abort; + } + } + if (!pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking))) { + g_error ("setting PGM_NOBLOCK = %d", nonblocking); + goto err_abort; + } + } + + if (!pgm_connect (g_sock, &pgm_err)) { + g_error ("connecting PGM socket: %s", pgm_err->message); + goto err_abort; + } + +/* period timer to indicate some form of life */ +// TODO: Gnome 2.14: replace with g_timeout_add_seconds() + g_timeout_add (2 * 1000, (GSourceFunc)on_mark, NULL); + + if (PGMPING_MODE_SOURCE == g_mode || PGMPING_MODE_INITIATOR == g_mode) + { + g_sender_thread = g_thread_create_full (sender_thread, + g_sock, + 0, + TRUE, + TRUE, + G_THREAD_PRIORITY_NORMAL, + &err); + if (!g_sender_thread) { + g_critical ("g_thread_create_full failed errno %i: \"%s\"", err->code, err->message); + goto err_abort; + } + } + + { + g_receiver_thread = g_thread_create_full (receiver_thread, + g_sock, + 0, + TRUE, + TRUE, + G_THREAD_PRIORITY_HIGH, + &err); + if (!g_receiver_thread) { + g_critical ("g_thread_create_full failed errno %i: \"%s\"", err->code, err->message); + goto err_abort; + } + } + + g_message ("startup complete."); + return FALSE; + +err_abort: + if (NULL != g_sock) { + pgm_close (g_sock, FALSE); + g_sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +static +gpointer +sender_thread ( + gpointer user_data + ) +{ + pgm_sock_t* tx_sock = (pgm_sock_t*)user_data; + example::Ping ping; + string subject("PING.PGM.TEST."); + char hostname[NI_MAXHOST + 1]; + const long payload_len = 1000; + char payload[payload_len]; + gpointer buffer = NULL; + guint64 latency, now, last; + +#ifdef CONFIG_HAVE_EPOLL + const long ev_len = 1; + struct epoll_event events[ev_len]; + + int efd_again = epoll_create (IP_MAX_MEMBERSHIPS); + if (efd_again < 0) { + g_error ("epoll_create failed errno %i: \"%s\"", errno, strerror(errno)); + g_main_loop_quit (g_loop); + return NULL; + } +/* Add write event to epoll domain in order to re-enable as required by return + * value. We use one-shot flag to disable ASAP, as we don't want such events + * until triggered. + */ + if (pgm_epoll_ctl (tx_sock, efd_again, EPOLL_CTL_ADD, EPOLLOUT | EPOLLONESHOT) < 0) { + g_error ("pgm_epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); + g_main_loop_quit (g_loop); + return NULL; + } + struct epoll_event event; + memset (&event, 0, sizeof(event)); + event.events = EPOLLIN; + event.data.fd = g_quit_pipe[0]; + if (epoll_ctl (efd_again, EPOLL_CTL_ADD, g_quit_pipe[0], &event) < 0) { + g_error ("epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); + g_main_loop_quit (g_loop); + return NULL; + } +#elif defined(CONFIG_HAVE_POLL) + int n_fds = 2; + struct pollfd fds[ 2 + 1 ]; +#endif /* !CONFIG_HAVE_EPOLL */ + + gethostname (hostname, sizeof(hostname)); + subject.append(hostname); + memset (payload, 0, sizeof(payload)); + + ping.mutable_subscription_header()->set_subject (subject); + ping.mutable_market_data_header()->set_msg_type (example::MarketDataHeader::MSG_VERIFY); + ping.mutable_market_data_header()->set_rec_type (example::MarketDataHeader::PING); + ping.mutable_market_data_header()->set_rec_status (example::MarketDataHeader::STATUS_OK); + ping.set_time (last); + + last = now = pgm_time_update_now(); + do { + if (g_msg_sent && g_latency_seqno + 1 == g_msg_sent) + latency = g_latency_current; + else + latency = g_odata_interval; + + ping.set_seqno (g_msg_sent); + ping.set_latency (latency); + ping.set_payload (payload, sizeof(payload)); + + const size_t header_size = pgm_pkt_offset (FALSE, g_pgmcc_family); + const size_t apdu_size = ping.ByteSize(); + struct pgm_sk_buff_t* skb = pgm_alloc_skb (g_max_tpdu); + pgm_skb_reserve (skb, header_size); + pgm_skb_put (skb, apdu_size); + +/* wait on packet rate limit */ + if ((last + g_odata_interval) > now) { +#ifndef _WIN32 + const unsigned int usec = g_odata_interval - (now - last); + usleep (usec); +#else + const DWORD msec = usecs_to_msecs (g_odata_interval - (now - last)); + Sleep (msec); +#endif + now = pgm_time_update_now(); + } + last += g_odata_interval; + ping.set_time (now); + ping.SerializeToArray (skb->data, skb->len); + + struct timeval tv; + int timeout; + size_t bytes_written; + int status; +again: + status = pgm_send_skbv (tx_sock, &skb, 1, TRUE, &bytes_written); + switch (status) { +/* rate control */ + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + const gboolean status = pgm_getsockopt (tx_sock, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &optlen); + if (G_UNLIKELY(!status)) { + g_error ("getting PGM_RATE_REMAIN failed"); + break; + } + timeout = (tv.tv_sec * 1000) + ((tv.tv_usec + 500) / 1000); +/* busy wait under 2ms */ + if (timeout < 2) timeout = 0; +#ifdef CONFIG_HAVE_EPOLL + const int ready = epoll_wait (efd_again, events, G_N_ELEMENTS(events), timeout /* ms */); +#elif defined(CONFIG_HAVE_POLL) + memset (fds, 0, sizeof(fds)); + fds[0].fd = g_quit_pipe[0]; + fds[0].events = POLLIN; + pgm_poll_info (tx_sock, &fds[1], &n_fds, POLLIN); + poll (fds, 1 + n_fds, timeout /* ms */); +#endif /* !CONFIG_HAVE_EPOLL */ + if (G_UNLIKELY(g_quit)) + break; + goto again; + } +/* congestion control */ + case PGM_IO_STATUS_CONGESTION: +/* kernel feedback */ + case PGM_IO_STATUS_WOULD_BLOCK: + { +#ifdef CONFIG_HAVE_EPOLL +# if 1 +/* re-enable write event for one-shot */ + if (pgm_epoll_ctl (tx_sock, efd_again, EPOLL_CTL_MOD, EPOLLOUT | EPOLLONESHOT) < 0) + { + g_error ("pgm_epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); + g_main_loop_quit (g_loop); + return NULL; + } + const int ready = epoll_wait (efd_again, events, G_N_ELEMENTS(events), -1 /* ms */); + if (G_UNLIKELY(g_quit)) + break; +# else + const int ready = epoll_wait (efd_again, events, G_N_ELEMENTS(events), -1 /* ms */); + if (G_UNLIKELY(g_quit)) + break; + if (ready > 0 && + pgm_epoll_ctl (tx_sock, efd_again, EPOLL_CTL_MOD, EPOLLOUT | EPOLLONESHOT) < 0) + { + g_error ("pgm_epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); + g_main_loop_quit (g_loop); + return NULL; + } +# endif +#elif defined(CONFIG_HAVE_POLL) + memset (fds, 0, sizeof(fds)); + fds[0].fd = g_quit_pipe[0]; + fds[0].events = POLLIN; + pgm_poll_info (g_sock, &fds[1], &n_fds, POLLOUT); + poll (fds, 1 + n_fds, -1 /* ms */); +#endif /* !CONFIG_HAVE_EPOLL */ + goto again; + } +/* successful delivery */ + case PGM_IO_STATUS_NORMAL: +// g_message ("sent payload: %s", ping.DebugString().c_str()); +// g_message ("sent %u bytes", (unsigned)bytes_written); + break; + default: + g_warning ("pgm_send_skbv failed, status:%i", status); + g_main_loop_quit (g_loop); + return NULL; + } + g_out_total += bytes_written; + g_msg_sent++; + } while (!g_quit); + +#ifdef CONFIG_HAVE_EPOLL + close (efd_again); +#endif + return NULL; +} + +static +gpointer +receiver_thread ( + gpointer data + ) +{ + pgm_sock_t* rx_sock = (pgm_sock_t*)data; + const long iov_len = 20; + struct pgm_msgv_t msgv[iov_len]; + pgm_time_t lost_tstamp = 0; + pgm_tsi_t lost_tsi; + guint32 lost_count = 0; + +#ifdef CONFIG_HAVE_EPOLL + const long ev_len = 1; + struct epoll_event events[ev_len]; + + int efd = epoll_create (IP_MAX_MEMBERSHIPS); + if (efd < 0) { + g_error ("epoll_create failed errno %i: \"%s\"", errno, strerror(errno)); + g_main_loop_quit (g_loop); + return NULL; + } + if (pgm_epoll_ctl (rx_sock, efd, EPOLL_CTL_ADD, EPOLLIN) < 0) { + g_error ("pgm_epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); + g_main_loop_quit (g_loop); + return NULL; + } + struct epoll_event event; + memset (&event, 0, sizeof(event)); + event.events = EPOLLIN; + event.data.fd = g_quit_pipe[0]; + if (epoll_ctl (efd, EPOLL_CTL_ADD, g_quit_pipe[0], &event) < 0) { + g_error ("epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); + g_main_loop_quit (g_loop); + return NULL; + } +#elif defined(CONFIG_HAVE_POLL) + int n_fds = 3; + struct pollfd fds[ 3 + 1 ]; +#endif /* !CONFIG_HAVE_EPOLL */ + + memset (&lost_tsi, 0, sizeof(lost_tsi)); + + do { + struct timeval tv; + int timeout; + size_t len; + pgm_error_t* pgm_err = NULL; + const int status = pgm_recvmsgv (rx_sock, + msgv, + G_N_ELEMENTS(msgv), + MSG_ERRQUEUE, + &len, + &pgm_err); + if (lost_count) { + pgm_time_t elapsed = pgm_time_update_now() - lost_tstamp; + if (elapsed >= pgm_secs(1)) { + g_warning ("pgm data lost %" G_GUINT32_FORMAT " packets detected from %s", + lost_count, pgm_tsi_print (&lost_tsi)); + lost_count = 0; + } + } + + switch (status) { + case PGM_IO_STATUS_NORMAL: +// g_message ("recv %u bytes", (unsigned)len); + on_msgv (msgv, len); + break; + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + const gboolean status = pgm_getsockopt (g_sock, IPPROTO_PGM, PGM_TIME_REMAIN, &tv, &optlen); +//g_message ("timer pending %d", ((tv.tv_sec * 1000) + (tv.tv_usec / 1000))); + if (G_UNLIKELY(!status)) { + g_error ("getting PGM_TIME_REMAIN failed"); + break; + } + } + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + const gboolean status = pgm_getsockopt (g_sock, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &optlen); +//g_message ("rate limited %d", ((tv.tv_sec * 1000) + (tv.tv_usec / 1000))); + if (G_UNLIKELY(!status)) { + g_error ("getting PGM_RATE_REMAIN failed"); + break; + } + } +/* fall through */ + case PGM_IO_STATUS_WOULD_BLOCK: +//g_message ("would block"); +block: + timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); +/* busy wait under 2ms */ + if (timeout > 0 && timeout < 2) timeout = 0; +#ifdef CONFIG_HAVE_EPOLL + epoll_wait (efd, events, G_N_ELEMENTS(events), timeout /* ms */); +#elif defined(CONFIG_HAVE_POLL) + memset (fds, 0, sizeof(fds)); + fds[0].fd = g_quit_pipe[0]; + fds[0].events = POLLIN; + pgm_poll_info (g_sock, &fds[1], &n_fds, POLLIN); + poll (fds, 1 + n_fds, timeout /* ms */); +#endif /* !CONFIG_HAVE_EPOLL */ + break; + case PGM_IO_STATUS_RESET: + { + struct pgm_sk_buff_t* skb = msgv[0].msgv_skb[0]; + lost_tstamp = skb->tstamp; + if (pgm_tsi_equal (&skb->tsi, &lost_tsi)) + lost_count += skb->sequence; + else { + lost_count = skb->sequence; + memcpy (&lost_tsi, &skb->tsi, sizeof(pgm_tsi_t)); + } + pgm_free_skb (skb); + break; + } + default: + if (pgm_err) { + g_warning ("%s", pgm_err->message); + pgm_error_free (pgm_err); + pgm_err = NULL; + } + break; + } + } while (!g_quit); + +#ifdef CONFIG_HAVE_EPOLL + close (efd); +#endif + return NULL; +} + +static +int +on_msgv ( + struct pgm_msgv_t* msgv, /* an array of msgvs */ + size_t len + ) +{ + example::Ping ping; + guint i = 0; + static pgm_time_t last_time = pgm_time_update_now(); + + while (len) + { + const struct pgm_sk_buff_t* pskb = msgv[i].msgv_skb[0]; + gsize apdu_len = 0; + for (unsigned j = 0; j < msgv[i].msgv_len; j++) + apdu_len += msgv[i].msgv_skb[j]->len; + + if (PGMPING_MODE_REFLECTOR == g_mode) + { + int status; +again: + status = pgm_send (g_sock, pskb->data, pskb->len, NULL); + switch (status) { + case PGM_IO_STATUS_RATE_LIMITED: +g_message ("ratelimit"); goto again; + case PGM_IO_STATUS_CONGESTION: +g_message ("congestion"); goto again; + case PGM_IO_STATUS_WOULD_BLOCK: +g_message ("would block"); +/* busy wait always as reflector */ + goto again; + + case PGM_IO_STATUS_NORMAL: + break; + + default: + g_warning ("pgm_send_skbv failed"); + g_main_loop_quit (g_loop); + return 0; + } + goto next_msg; + } + +/* only parse first fragment of each apdu */ + if (!ping.ParseFromArray (pskb->data, pskb->len)) + goto next_msg; +// g_message ("payload: %s", ping.DebugString().c_str()); + + { + const pgm_time_t send_time = ping.time(); + const pgm_time_t recv_time = pskb->tstamp; + const guint64 seqno = ping.seqno(); + const guint64 latency = ping.latency(); + + if (seqno < g_latency_seqno) { + g_message ("seqno replay?"); + goto next_msg; + } + + g_in_total += pskb->len; + g_msg_received++; + +/* handle ping */ + const pgm_time_t now = pgm_time_update_now(); + if (send_time > now) + g_warning ("send time %" PGM_TIME_FORMAT " newer than now %" PGM_TIME_FORMAT, + send_time, now); + if (recv_time > now) + g_warning ("recv time %" PGM_TIME_FORMAT " newer than now %" PGM_TIME_FORMAT, + recv_time, now); + if (send_time >= recv_time){ + g_message ("timer mismatch, send time = recv time + %.3f ms (last time + %.3f ms)", + pgm_to_msecsf(send_time - recv_time), + pgm_to_msecsf(last_time - send_time)); + goto next_msg; + } + g_latency_current = pgm_to_secs(recv_time - send_time); + g_latency_seqno = seqno; + + const double elapsed = pgm_to_usecsf (recv_time - send_time); + g_latency_total += elapsed; + g_latency_square_total += elapsed * elapsed; + + if (elapsed > g_latency_max) + g_latency_max = elapsed; + if (elapsed < g_latency_min) + g_latency_min = elapsed; + + g_latency_running_average += elapsed; + g_latency_count++; + last_time = recv_time; + } + +/* move onto next apdu */ +next_msg: + i++; + len -= apdu_len; + } + + return 0; +} + +/* idle log notification + */ + +static +gboolean +on_mark ( + G_GNUC_UNUSED gpointer data + ) +{ + const pgm_time_t now = pgm_time_update_now (); + const double interval = pgm_to_secsf(now - g_interval_start); + g_interval_start = now; + +/* receiving a ping */ + if (g_latency_count) + { + const double average = g_latency_total / g_latency_count; + const double variance = g_latency_square_total / g_latency_count + - average * average; + const double standard_deviation = sqrt (variance); + + if (g_latency_count < 10) + { + if (average < 1000.0) + g_message ("seqno=%" G_GUINT64_FORMAT " time=%.01f us", + g_latency_seqno, average); + else + g_message ("seqno=%" G_GUINT64_FORMAT " time=%.01f ms", + g_latency_seqno, average / 1000); + } + else + { + double seq_rate = (g_latency_seqno - g_last_seqno) / interval; + double out_rate = g_out_total * 8.0 / 1000000.0 / interval; + double in_rate = g_in_total * 8.0 / 1000000.0 / interval; + if (g_latency_min < 1000.0) + g_message ("s=%.01f avg=%.01f min=%.01f max=%.01f stddev=%0.1f us o=%.2f i=%.2f mbit", + seq_rate, average, g_latency_min, g_latency_max, standard_deviation, out_rate, in_rate); + else + g_message ("s=%.01f avg=%.01f min=%.01f max=%.01f stddev=%0.1f ms o=%.2f i=%.2f mbit", + seq_rate, average / 1000, g_latency_min / 1000, g_latency_max / 1000, standard_deviation / 1000, out_rate, in_rate); + } + +/* reset interval counters */ + g_latency_total = 0.0; + g_latency_square_total = 0.0; + g_latency_count = 0; + g_last_seqno = g_latency_seqno; +#ifdef INFINITY + g_latency_min = INFINITY; +#else + g_latency_min = INT64_MAX; +#endif + g_latency_max = 0.0; + g_out_total = 0; + g_in_total = 0; + } + + return TRUE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/pgmrecv.c b/3rdparty/openpgm-svn-r1135/pgm/examples/pgmrecv.c new file mode 100644 index 0000000..eddbbc7 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/pgmrecv.c @@ -0,0 +1,649 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Simple receiver using the PGM transport, based on enonblocksyncrecvmsgv :/ + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HAVE_EPOLL +# include +#endif +#include +#include +#ifdef G_OS_UNIX +# include +# include +# include +# include +# include +# include +# include +#else +# include "getopt.h" +#endif +#include +#ifdef CONFIG_WITH_HTTP +# include +#endif +#ifdef CONFIG_WITH_SNMP +# include +#endif + +/* example dependencies */ +#include +#include +#include + + +/* globals */ + +static int g_port = 0; +static const char* g_network = ""; +static const char* g_source = ""; +static gboolean g_multicast_loop = FALSE; +static int g_udp_encap_port = 0; + +static int g_max_tpdu = 1500; +static int g_sqns = 100; + +static pgm_sock_t* g_sock = NULL; +static GThread* g_thread = NULL; +static GMainLoop* g_loop = NULL; +static gboolean g_quit; +#ifdef G_OS_UNIX +static int g_quit_pipe[2]; +static void on_signal (int, gpointer); +#else +static HANDLE g_quit_event; +static BOOL on_console_ctrl (DWORD); +#endif + +static gboolean on_startup (gpointer); +static gboolean on_mark (gpointer); + +static gpointer receiver_thread (gpointer); +static int on_msgv (struct pgm_msgv_t*, size_t); + + +G_GNUC_NORETURN static void +usage ( + const char* bin + ) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -a : Source unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); +#ifdef CONFIG_WITH_HTTP + fprintf (stderr, " -H : Enable HTTP administrative interface\n"); +#endif +#ifdef CONFIG_WITH_SNMP + fprintf (stderr, " -S : Enable SNMP interface\n"); +#endif + fprintf (stderr, " -i : List available interfaces\n"); + exit (1); +} + +int +main ( + int argc, + char* argv[] + ) +{ + int e; + pgm_error_t* pgm_err = NULL; +#ifdef CONFIG_WITH_HTTP + gboolean enable_http = FALSE; +#endif +#ifdef CONFIG_WITH_SNMP + gboolean enable_snmpx = FALSE; +#endif + + setlocale (LC_ALL, ""); + +/* pre-initialise PGM messages module to add hook for GLib logging */ + pgm_messages_init(); + log_init (); + g_message ("pgmrecv"); + + if (!pgm_init (&pgm_err)) { + g_error ("Unable to start PGM engine: %s", (pgm_err && pgm_err->message) ? pgm_err->message : "(null)"); + pgm_error_free (pgm_err); + pgm_messages_shutdown(); + return EXIT_FAILURE; + } + + g_thread_init (NULL); + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "a:s:n:p:lih" +#ifdef CONFIG_WITH_HTTP + "H" +#endif +#ifdef CONFIG_WITH_SNMP + "S" +#endif + )) != -1) + { + switch (c) { + case 'n': g_network = optarg; break; + case 'a': g_source = optarg; break; + case 's': g_port = atoi (optarg); break; + case 'p': g_udp_encap_port = atoi (optarg); break; + + case 'l': g_multicast_loop = TRUE; break; +#ifdef CONFIG_WITH_HTTP + case 'H': enable_http = TRUE; break; +#endif +#ifdef CONFIG_WITH_SNMP + case 'S': enable_snmpx = TRUE; break; +#endif + + case 'i': + pgm_if_print_all(); + pgm_messages_shutdown(); + return EXIT_SUCCESS; + + case 'h': + case '?': + pgm_messages_shutdown(); + usage (binary_name); + } + } + +#ifdef CONFIG_WITH_HTTP + if (enable_http) { + if (!pgm_http_init (PGM_HTTP_DEFAULT_SERVER_PORT, &pgm_err)) { + g_error ("Unable to start HTTP interface: %s", pgm_err->message); + pgm_error_free (pgm_err); + pgm_shutdown(); + pgm_messages_shutdown(); + return EXIT_FAILURE; + } + } +#endif +#ifdef CONFIG_WITH_SNMP + if (enable_snmpx) { + if (!pgm_snmp_init (&pgm_err)) { + g_error ("Unable to start SNMP interface: %s", pgm_err->message); + pgm_error_free (pgm_err); +#ifdef CONFIG_WITH_HTTP + if (enable_http) + pgm_http_shutdown (); +#endif + pgm_shutdown (); + pgm_messages_shutdown(); + return EXIT_FAILURE; + } + } +#endif + + g_loop = g_main_loop_new (NULL, FALSE); + + g_quit = FALSE; + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif +#ifdef G_OS_UNIX + e = pipe (g_quit_pipe); + g_assert (0 == e); + pgm_signal_install (SIGINT, on_signal, g_loop); + pgm_signal_install (SIGTERM, on_signal, g_loop); +#else + g_quit_event = CreateEvent (NULL, TRUE, FALSE, TEXT("QuitEvent")); + SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); +#endif + +/* delayed startup */ + g_message ("scheduling startup."); + g_timeout_add (0, (GSourceFunc)on_startup, NULL); + +/* dispatch loop */ + g_message ("entering main event loop ... "); + g_main_loop_run (g_loop); + + g_message ("event loop terminated, cleaning up."); + +/* cleanup */ + g_quit = TRUE; +#ifdef G_OS_UNIX + const char one = '1'; + const size_t writelen = write (g_quit_pipe[1], &one, sizeof(one)); + g_assert (sizeof(one) == writelen); + g_thread_join (g_thread); + close (g_quit_pipe[0]); + close (g_quit_pipe[1]); +#else + SetEvent (g_quit_event); + g_thread_join (g_thread); + CloseHandle (g_quit_event); +#endif + + g_main_loop_unref (g_loop); + g_loop = NULL; + + if (g_sock) { + g_message ("closing PGM socket."); + + pgm_close (g_sock, TRUE); + g_sock = NULL; + } + +#ifdef CONFIG_WITH_HTTP + if (enable_http) + pgm_http_shutdown(); +#endif +#ifdef CONFIG_WITH_SNMP + if (enable_snmpx) + pgm_snmp_shutdown(); +#endif + + g_message ("PGM engine shutdown."); + pgm_shutdown(); + g_message ("finished."); + pgm_messages_shutdown(); + return EXIT_SUCCESS; +} + +#ifdef G_OS_UNIX +static +void +on_signal ( + int signum, + gpointer user_data + ) +{ + GMainLoop* loop = (GMainLoop*)user_data; + g_message ("on_signal (signum:%d user_data:%p)", + signum, user_data); + g_main_loop_quit (loop); +} +#else +static +BOOL +on_console_ctrl ( + DWORD dwCtrlType + ) +{ + g_message ("on_console_ctrl (dwCtrlType:%lu)", (unsigned long)dwCtrlType); + g_main_loop_quit (g_loop); + return TRUE; +} +#endif /* !G_OS_UNIX */ + +static +gboolean +on_startup ( + G_GNUC_UNUSED gpointer data + ) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + + g_message ("startup."); + +/* parse network parameter into transport address structure */ + if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { + g_error ("parsing network parameter: %s", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + if (g_udp_encap_port) { + g_message ("create PGM/UDP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + } else { + g_message ("create PGM/IP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int recv_only = 1, + passive = 0, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_secs (2), + nak_rdata_ivl = pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_PASSIVE, &passive, sizeof(passive)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + g_error ("creating GSI: %s", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (g_sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + g_error ("binding PGM socket: %s", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int nonblocking = 1, + multicast_loop = g_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); + + if (!pgm_connect (g_sock, &pgm_err)) { + g_error ("connecting PGM socket: %s", pgm_err->message); + goto err_abort; + } + +/* create receiver thread */ + GError* glib_err = NULL; + g_thread = g_thread_create_full (receiver_thread, + g_sock, + 0, + TRUE, + TRUE, + G_THREAD_PRIORITY_HIGH, + &glib_err); + if (!g_thread) { + g_error ("g_thread_create_full failed errno %i: \"%s\"", glib_err->code, glib_err->message); + g_error_free (glib_err); + goto err_abort; + } + +/* period timer to indicate some form of life */ +// TODO: Gnome 2.14: replace with g_timeout_add_seconds() + g_timeout_add(10 * 1000, (GSourceFunc)on_mark, NULL); + + g_message ("startup complete."); + return FALSE; + +err_abort: + if (NULL != g_sock) { + pgm_close (g_sock, FALSE); + g_sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + g_main_loop_quit (g_loop); + return FALSE; +} + +/* idle log notification + */ + +static +gboolean +on_mark ( + G_GNUC_UNUSED gpointer data + ) +{ + g_message ("-- MARK --"); + return TRUE; +} + +static +gpointer +receiver_thread ( + gpointer data + ) +{ + pgm_sock_t* rx_sock = (pgm_sock_t*)data; + const long iov_len = 20; + const long ev_len = 1; + struct pgm_msgv_t msgv[iov_len]; + +#ifdef CONFIG_HAVE_EPOLL + struct epoll_event events[ev_len]; /* wait for maximum 1 event */ + int timeout; + const int efd = epoll_create (IP_MAX_MEMBERSHIPS); + if (efd < 0) { + g_error ("epoll_create failed errno %i: \"%s\"", errno, strerror(errno)); + g_main_loop_quit (g_loop); + return NULL; + } + + if (pgm_epoll_ctl (rx_sock, efd, EPOLL_CTL_ADD, EPOLLIN) < 0) + { + g_error ("pgm_epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); + g_main_loop_quit (g_loop); + return NULL; + } + struct epoll_event event; + event.events = EPOLLIN; + event.data.fd = g_quit_pipe[0]; + if (epoll_ctl (efd, EPOLL_CTL_ADD, g_quit_pipe[0], &event) < 0) + { + g_error ("epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); + g_main_loop_quit (g_loop); + return NULL; + } +#elif defined(CONFIG_HAVE_POLL) + int timeout; + int n_fds = 2; + struct pollfd fds[ 1 + n_fds ]; +#elif defined(G_OS_UNIX) /* HAVE_SELECT */ + int n_fds; + fd_set readfds; +#else /* G_OS_WIN32 */ + int n_handles = 3; +# if (__STDC_VERSION__ >= 199901L) + HANDLE waitHandles[n_handles]; +# else + HANDLE* waitHandles = (HANDLE*)g_malloc (n_handles * sizeof(HANDLE));; +# endif + DWORD timeout, dwEvents; + WSAEVENT recvEvent, pendingEvent; + + recvEvent = WSACreateEvent (); + WSAEventSelect (pgm_transport_get_recv_fd (g_transport), recvEvent, FD_READ); + pendingEvent = WSACreateEvent (); + WSAEventSelect (pgm_transport_get_pending_fd (g_transport), pendingEvent, FD_READ); + + waitHandles[0] = g_quit_event; + waitHandles[1] = recvEvent; + waitHandles[2] = pendingEvent; +#endif /* !CONFIG_HAVE_EPOLL */ + + do { + struct timeval tv; + size_t len; + pgm_error_t* pgm_err = NULL; + const int status = pgm_recvmsgv (rx_sock, + msgv, + G_N_ELEMENTS(msgv), + 0, + &len, + &pgm_err); + switch (status) { + case PGM_IO_STATUS_NORMAL: + on_msgv (msgv, len); + break; + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (rx_sock, IPPROTO_PGM, PGM_TIME_REMAIN, &tv, &optlen); + } + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (rx_sock, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &optlen); + } +/* fall through */ + case PGM_IO_STATUS_WOULD_BLOCK: +block: +#ifdef CONFIG_HAVE_EPOLL + timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + epoll_wait (efd, events, G_N_ELEMENTS(events), timeout /* ms */); +#elif defined(CONFIG_HAVE_POLL) + timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + memset (fds, 0, sizeof(fds)); + fds[0].fd = g_quit_pipe[0]; + fds[0].events = POLLIN; + pgm_poll_info (rx_sock, &fds[1], &n_fds, POLLIN); + poll (fds, 1 + n_fds, timeout /* ms */); +#elif defined(G_OS_UNIX) /* HAVE_SELECT */ + FD_ZERO(&readfds); + FD_SET(g_quit_pipe[0], &readfds); + n_fds = g_quit_pipe[0] + 1; + pgm_select_info (rx_sock, &readfds, NULL, &n_fds); + select (n_fds, &readfds, NULL, NULL, PGM_IO_STATUS_RATE_LIMITED == status ? &tv : NULL); +#else /* G_OS_WIN32 */ + timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? INFINITE : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + dwEvents = WaitForMultipleObjects (n_handles, waitHandles, FALSE, timeout); + switch (dwEvents) { + case WAIT_OBJECT_0+1: WSAResetEvent (recvEvent); break; + case WAIT_OBJECT_0+2: WSAResetEvent (pendingEvent); break; + default: break; + } +#endif /* !CONFIG_HAVE_EPOLL */ + break; + + default: + if (pgm_err) { + g_warning ("%s", pgm_err->message); + pgm_error_free (pgm_err); + pgm_err = NULL; + } + if (PGM_IO_STATUS_ERROR == status) + break; + } + } while (!g_quit); + +#ifdef CONFIG_HAVE_EPOLL + close (efd); +#elif defined(G_OS_WIN32) + WSACloseEvent (recvEvent); + WSACloseEvent (pendingEvent); +# if (__STDC_VERSION__ < 199901L) + g_free (waitHandles); +# endif +#endif + return NULL; +} + +static +int +on_msgv ( + struct pgm_msgv_t* msgv, /* an array of msgvs */ + size_t len + ) +{ + g_message ("(%u bytes)", + (unsigned)len); + + guint i = 0; +/* for each apdu */ + do { + const struct pgm_sk_buff_t* pskb = msgv[i].msgv_skb[0]; + gsize apdu_len = 0; + for (unsigned j = 0; j < msgv[i].msgv_len; j++) + apdu_len += msgv[i].msgv_skb[j]->len; +/* truncate to first fragment to make GLib printing happy */ + char buf[2048], tsi[PGM_TSISTRLEN]; + const gsize buflen = MIN(sizeof(buf) - 1, pskb->len); + strncpy (buf, (const char*)pskb->data, buflen); + buf[buflen] = '\0'; + pgm_tsi_print_r (&pskb->tsi, tsi, sizeof(tsi)); + if (msgv[i].msgv_len > 1) + g_message ("\t%u: \"%s\" ... (%" G_GSIZE_FORMAT " bytes from %s)", + i, buf, apdu_len, tsi); + else + g_message ("\t%u: \"%s\" (%" G_GSIZE_FORMAT " bytes from %s)", + i, buf, apdu_len, tsi); + i++; + len -= apdu_len; + } while (len); + + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/pgmsend.c b/3rdparty/openpgm-svn-r1135/pgm/examples/pgmsend.c new file mode 100644 index 0000000..6fd85a1 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/pgmsend.c @@ -0,0 +1,305 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Simple sender using the PGM transport. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef G_OS_UNIX +# include +# include +# include +# include +# include +# include +#else +# include "getopt.h" +#endif +#include + +/* example dependencies */ +#include +#include + + +/* typedefs */ + +/* globals */ + +static int g_port = 0; +static const char* g_network = ""; +static gboolean g_multicast_loop = FALSE; +static int g_udp_encap_port = 0; + +static int g_max_tpdu = 1500; +static int g_max_rte = 400*1000; +static int g_sqns = 100; + +static gboolean g_fec = FALSE; +static int g_k = 8; +static int g_n = 255; + +static pgm_sock_t* g_sock = NULL; + +static gboolean create_pgm_socket (void); + + +G_GNUC_NORETURN static +void +usage (const char* bin) +{ + fprintf (stderr, "Usage: %s [options] message\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -r : Regulate to rate bytes per second\n"); + fprintf (stderr, " -f : Enable FEC with either proactive or ondemand parity\n"); + fprintf (stderr, " -K : Configure Reed-Solomon code (n, k)\n"); + fprintf (stderr, " -N \n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); + fprintf (stderr, " -i : List available interfaces\n"); + exit (1); +} + +int +main ( + int argc, + char *argv[] + ) +{ + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + +/* pre-initialise PGM messages module to add hook for GLib logging */ + pgm_messages_init(); + log_init(); + if (!pgm_init (&pgm_err)) { + g_error ("Unable to start PGM engine: %s", pgm_err->message); + pgm_error_free (pgm_err); + pgm_messages_shutdown(); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:r:f:K:N:lih")) != -1) + { + switch (c) { + case 'n': g_network = optarg; break; + case 's': g_port = atoi (optarg); break; + case 'p': g_udp_encap_port = atoi (optarg); break; + case 'r': g_max_rte = atoi (optarg); break; + + case 'f': g_fec = TRUE; break; + case 'K': g_k = atoi (optarg); break; + case 'N': g_n = atoi (optarg); break; + + case 'l': g_multicast_loop = TRUE; break; + + case 'i': + pgm_if_print_all(); + pgm_messages_shutdown(); + return EXIT_SUCCESS; + + case 'h': + case '?': + pgm_messages_shutdown(); + usage (binary_name); + } + } + + if (g_fec && ( !g_k || !g_n )) { + pgm_messages_shutdown(); + g_error ("Invalid Reed-Solomon parameters RS(%d, %d).", g_n, g_k); + usage (binary_name); + } + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif + + if (create_pgm_socket()) + { + while (optind < argc) { + const int status = pgm_send (g_sock, argv[optind], strlen(argv[optind]) + 1, NULL); + if (PGM_IO_STATUS_NORMAL != status) { + g_warning ("pgm_send failed."); + } + optind++; + } + } + +/* cleanup */ + if (g_sock) { + pgm_close (g_sock, TRUE); + g_sock = NULL; + } + pgm_shutdown(); + pgm_messages_shutdown(); + return EXIT_SUCCESS; +} + +static +gboolean +create_pgm_socket (void) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + +/* parse network parameter into PGM socket address structure */ + if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { + g_error ("Parsing network parameter: %s", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + if (g_udp_encap_port) { + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + g_error ("Creating PGM/UDP socket: %s", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + } else { + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + g_error ("Creating PGM/IP socket: %s", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int send_only = 1, + ambient_spm = pgm_secs (30), + heartbeat_spm[] = { pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (1300), + pgm_secs (7), + pgm_secs (16), + pgm_secs (25), + pgm_secs (30) }; + + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_SEND_ONLY, &send_only, sizeof(send_only)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_TXW_SQNS, &g_sqns, sizeof(g_sqns)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_TXW_MAX_RTE, &g_max_rte, sizeof(g_max_rte)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_AMBIENT_SPM, &ambient_spm, sizeof(ambient_spm)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_HEARTBEAT_SPM, &heartbeat_spm, sizeof(heartbeat_spm)); + if (g_fec) { + struct pgm_fecinfo_t fecinfo; + fecinfo.block_size = g_n; + fecinfo.proactive_packets = 0; + fecinfo.group_size = g_k; + fecinfo.ondemand_parity_enabled = TRUE; + fecinfo.var_pktlen_enabled = TRUE; + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); + } + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + g_error ("Creating GSI: %s", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (g_sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + g_error ("Binding PGM socket: %s", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int blocking = 0, + multicast_loop = g_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NOBLOCK, &blocking, sizeof(blocking)); + + if (!pgm_connect (g_sock, &pgm_err)) { + g_error ("Connecting PGM socket: %s", pgm_err->message); + goto err_abort; + } + + return TRUE; + +err_abort: + if (NULL != g_sock) { + pgm_close (g_sock, FALSE); + g_sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/pgmtop.c b/3rdparty/openpgm-svn-r1135/pgm/examples/pgmtop.c new file mode 100644 index 0000000..9b18310 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/pgmtop.c @@ -0,0 +1,1031 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM packet monitor. + * + * Copyright (c) 2006-2007 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* PGM internals */ +#include + +/* example dependencies */ +#include +#include + + +struct ncurses_window; + +typedef void (*paint_func)(struct ncurses_window*); +typedef void (*resize_func)(struct ncurses_window*, int, int); + +struct ncurses_window { + WINDOW* window; + PANEL* panel; + char* title; + paint_func paint; + resize_func resize; +}; + +struct pgm_stat { + gulong count, snap_count; + gulong bytes, snap_bytes; + gulong tsdu; + + gulong duplicate; + gulong invalid; + + struct timeval last; + struct timeval last_valid; + struct timeval last_invalid; +}; + +struct pgm_netstat { + struct in_addr addr; + gulong corrupt; +}; + +struct pgm_hoststat { + pgm_tsi_t tsi; + + struct in_addr last_addr; + struct in_addr nla; + + gulong txw_secs; + gulong txw_trail; + gulong txw_lead; + gulong txw_sqns; + + gulong rxw_trail; + gulong rxw_lead; + + gulong rxw_trail_init; + gboolean window_defined; + gboolean rxw_constrained; + + gulong spm_sqn; + + struct pgm_stat spm, + poll, + polr, + odata, + rdata, + nak, + nnak, + ncf, + spmr, + + general; + + struct timeval session_start; +}; + + +/* globals */ + +static int g_port = 7500; +static const char* g_network = "239.192.0.1"; +static struct in_addr g_filter = { 0 }; + +static GIOChannel* g_io_channel = NULL; +static GIOChannel* g_stdin_channel = NULL; + +static GMainLoop* g_loop = NULL; + +static guint g_status_height = 6; +static guint g_info_width = 10; +static time_t start_time; + +static struct ncurses_window *g_peer, *g_info, *g_status, *g_active; +static GList* g_window_list = NULL; +static guint g_paint_interval = ( 1 * 1000 ) / 15; +static guint g_snap_interval = 10 * 1000; +static struct timeval g_last_snap, g_now; + +static GList* g_status_list = NULL; + +static guint32 g_packets = 0; +static GHashTable *g_hosts = NULL; +static GHashTable *g_nets = NULL; + +static void init_ncurses (void); +static void paint_ncurses (void); +static void resize_ncurses (int, int); + +static void paint_peer (struct ncurses_window*); +static gboolean tsi_row (gpointer, gpointer, gpointer); + +static void paint_info (struct ncurses_window*); +static void paint_status (struct ncurses_window*); +static void resize_peer (struct ncurses_window*, int, int); +static void resize_info (struct ncurses_window*, int, int); +static void resize_status (struct ncurses_window*, int, int); + +static void write_status (const gchar*, ...) G_GNUC_PRINTF (1, 2); +static void write_statusv (const gchar*, va_list); + +static void on_signal (int, gpointer); +static void on_winch (int); +static gboolean on_startup (gpointer); +static gboolean on_snap (gpointer); +static gboolean on_paint (gpointer); + +static gboolean on_io_data (GIOChannel*, GIOCondition, gpointer); +static gboolean on_io_error (GIOChannel*, GIOCondition, gpointer); + +static gboolean on_stdin_data (GIOChannel*, GIOCondition, gpointer); + +int +main ( + G_GNUC_UNUSED int argc, + G_GNUC_UNUSED char *argv[] + ) +{ + GError* err = NULL; + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + + log_init (); + g_message ("pgmtop"); + + if (!pgm_init (&pgm_err)) { + g_error ("Unable to start PGM engine: %s", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + + g_loop = g_main_loop_new (NULL, FALSE); + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); + signal (SIGHUP, SIG_IGN); + pgm_signal_install (SIGINT, on_signal, g_loop); + pgm_signal_install (SIGTERM, on_signal, g_loop); + +/* delayed startup */ + g_message ("scheduling startup.n"); + g_timeout_add(0, (GSourceFunc)on_startup, g_loop); + +/* dispatch loop */ + g_message ("entering main event loop ..."); + g_main_loop_run (g_loop); + + endwin(); + g_message ("event loop terminated, cleaning up."); + +/* cleanup */ + g_main_loop_unref (g_loop); + g_loop = NULL; + if (g_io_channel) { + g_message ("closing socket."); + g_io_channel_shutdown (g_io_channel, FALSE, &err); + g_io_channel = NULL; + } + + if (g_stdin_channel) { + g_message ("unbinding stdin."); + g_io_channel_unref (g_stdin_channel); + g_stdin_channel = NULL; + } + + g_message ("PGM engine shutdown."); + pgm_shutdown (); + g_message ("finished."); + return EXIT_SUCCESS; +} + +static struct ncurses_window* +create_window ( + char* name, + paint_func paint, + resize_func resize + ) +{ + struct ncurses_window* nw = g_malloc0 (sizeof(struct ncurses_window)); + nw->window = newwin (0, 0, 0, 0); + nw->panel = new_panel (nw->window); + nw->title = name; + + nw->paint = paint; + nw->resize = resize; + + g_window_list = g_list_append (g_window_list, nw); + return nw; +} + +/* +-Peer list --------------++-Info-+ + * | || | + * | < peer window > || < info window > + * | || | + * +-------------------------++------+ + * +-Status--------------------------+ + * | < status window > | + * +---------------------------------+ + */ + +static void +init_ncurses (void) +{ +/* setup ncurses terminal display */ + initscr(); /* init ncurses library */ + +// signal_install (SIGWINCH, on_winch); + + noecho(); /* hide entered keys */ + cbreak(); + +/* setup ncurses windows */ + g_peer = create_window ("Peers", paint_peer, resize_peer); + + g_info = create_window ("Info", paint_info, resize_info); + start_time = time (0); + + g_status = create_window ("Status", paint_status, resize_status); + scrollok (g_status->window, 1); + + g_active = g_peer; + top_panel (g_active->panel); + + paint_ncurses(); +} + +static void +resize_ncurses ( + int hsize, + int vsize + ) +{ + GList* nw_list = g_window_list; + while (nw_list) + { + struct ncurses_window* nw = (struct ncurses_window*)nw_list->data; + + nw->resize (nw, hsize, vsize); + + nw_list = nw_list->next; + } +} + +static void +paint_ncurses (void) +{ + static int hsize = 0, vsize = 0; + + if (hsize != COLS || vsize != LINES) + { + hsize = COLS; vsize = LINES; + resize_ncurses(hsize, vsize); + } + + GList* nw_list = g_window_list; + while (nw_list) + { + struct ncurses_window* nw = (struct ncurses_window*)nw_list->data; + werase (nw->window); + + box (nw->window, ACS_VLINE, ACS_HLINE); + mvwaddstr (nw->window, 0, 2, nw->title); + + nw->paint (nw); + + nw_list = nw_list->next; + } + +/* have cursor stay at top left of active window */ + wmove (g_active->window, 0, 0); + + update_panels(); /* update virtual screen */ + doupdate(); /* update real screen */ +} + +/* peer window */ + +static void +paint_peer ( + struct ncurses_window* nw + ) +{ + +/* 1 2 3 4 5 6 7 8 + * 012345678901234567890123456789012345678901234567890123456789012345678901234567890 + * TSI Packets Bytes Packet/s Bit/s Data Inv Dupe + * 100.200.300.400.500.600.70000 1,000K 1,000MB 1,000 1,000 100% 100% 100% + */ + mvwaddstr (nw->window, 1, 1, "TSI"); + mvwaddstr (nw->window, 1, 32, "Packets"); + mvwaddstr (nw->window, 1, 40, "Bytes"); + mvwaddstr (nw->window, 1, 48, "Packet/s"); + mvwaddstr (nw->window, 1, 58, "Bit/s"); + mvwaddstr (nw->window, 1, 68, "Data"); + mvwaddstr (nw->window, 1, 73, "Inv"); + mvwaddstr (nw->window, 1, 78, "Dupe"); + + if (g_hosts) + { + int row = 2; + gettimeofday(&g_now, NULL); + g_hash_table_foreach (g_hosts, (GHFunc)tsi_row, &row); + } +} + +static char* +print_si ( + float* v + ) +{ + static char prefix[5] = ""; + + if (*v > 100 * 1000 * 1000) { + strcpy (prefix, "G"); + *v /= 1000.0 * 1000.0 * 1000.0; + } else if (*v > 100 * 1000) { + strcpy (prefix, "M"); + *v /= 1000.0 * 1000.0; + } else if (*v > 100) { + strcpy (prefix, "K"); + *v /= 1000.0; + } else { + *prefix = 0; + } + + return prefix; +} + +static gboolean +tsi_row ( + G_GNUC_UNUSED gpointer key, + gpointer value, + gpointer user_data + ) +{ + struct pgm_hoststat* hoststat = value; + int* row = user_data; + + float secs = (g_now.tv_sec - g_last_snap.tv_sec) + + ( (g_now.tv_usec - g_last_snap.tv_usec) / 1000.0 / 1000.0 ); + +/* TSI */ + char* tsi_string = pgm_tsi_print (&hoststat->tsi); + mvwaddstr (g_peer->window, *row, 1, tsi_string); + +/* Packets */ + char buffer[100]; + float v = hoststat->general.count; + char* prefix = print_si (&v); + snprintf (buffer, sizeof(buffer), "%lu%s", (gulong)v, prefix); + mvwaddstr (g_peer->window, *row, 32, buffer); + +/* Bytes */ + v = hoststat->general.bytes; + prefix = print_si (&v); + snprintf (buffer, sizeof(buffer), "%lu%s", (gulong)v, prefix); + mvwaddstr (g_peer->window, *row, 40, buffer); + +/* Packet/s */ + v = ( hoststat->general.count - hoststat->general.snap_count ) / secs; + prefix = print_si (&v); + snprintf (buffer, sizeof(buffer), "%.1f%s", v, prefix); + mvwaddstr (g_peer->window, *row, 48, buffer); + +/* Bit/s */ + float bitrate = ((float)( hoststat->general.bytes - hoststat->general.snap_bytes ) * 8.0 / secs); + char* bitprefix = print_si (&bitrate); + snprintf (buffer, sizeof(buffer), "%.1f%s", bitrate, bitprefix); + mvwaddstr (g_peer->window, *row, 58, buffer); + +/* % Data */ + snprintf (buffer, sizeof(buffer), "%d%%", (int)((100.0 * hoststat->odata.tsdu) / hoststat->general.bytes)); + mvwaddstr (g_peer->window, *row, 68, buffer); + +/* % Invalid */ + snprintf (buffer, sizeof(buffer), "%d%%", (int)(hoststat->general.invalid ? (100.0 * hoststat->general.invalid) / hoststat->general.count : 0.0)); + mvwaddstr (g_peer->window, *row, 73, buffer); + +/* % Duplicate */ + snprintf (buffer, sizeof(buffer), "%d%%", (int)(hoststat->general.duplicate ? (100.0 * hoststat->general.duplicate) / hoststat->general.count : 0.0)); + mvwaddstr (g_peer->window, *row, 78, buffer); + + *row = *row + 1; + + return FALSE; +} + +static void +resize_peer ( + struct ncurses_window* nw, + int hsize, /* COLS */ + int vsize /* LINES */ + ) +{ + wresize (nw->window, vsize - g_status_height, hsize - g_info_width); + replace_panel (nw->panel, nw->window); + move_panel (nw->panel, 0, 0); +} + +/* info window */ + +static void +paint_info ( + struct ncurses_window* nw + ) +{ + char buffer[20]; + + mvwaddstr (nw->window, 1, 2, "Peers"); + snprintf (buffer, sizeof(buffer), "%d", g_hosts ? g_hash_table_size (g_hosts) : 0); + mvwaddstr (nw->window, 2, 2, buffer); + + mvwaddstr (nw->window, 3, 2, "Packets"); + snprintf (buffer, sizeof(buffer), "%d", g_packets); + mvwaddstr (nw->window, 4, 2, buffer); + + mvwaddstr (nw->window, LINES - g_status_height - 2, 2, "Elapsed"); + time_t elapsed = time(0) - start_time; + snprintf (buffer, sizeof(buffer), "%02d:%02d:%02d", + (int) (elapsed / 60) / 60, (int) (elapsed / 60) % 60, + (int) elapsed % 60); + mvwaddstr (nw->window, LINES - g_status_height - 1, 1, buffer); +} + +static void +resize_info ( + struct ncurses_window* nw, + int hsize, /* COLS */ + int vsize /* LINES */ + ) +{ + wresize (nw->window, vsize - g_status_height, g_info_width); + replace_panel (nw->panel, nw->window); + move_panel (nw->panel, 0, hsize - g_info_width); +} + +/* status window */ + +static void +paint_status ( + G_GNUC_UNUSED struct ncurses_window* nw + ) +{ + if (!g_status_list) return; + + guint len = g_list_length (g_status_list); + while (len > g_status_height) { + g_free (g_status_list->data); + g_status_list = g_list_delete_link (g_status_list, g_status_list); + len--; + } + guint y = 1; + GList* list = g_status_list; + while (list) { + mvwaddstr (g_status->window, y++, 3, (char*)list->data); + list = list->next; + } +} + +static void +resize_status ( + struct ncurses_window* nw, + int hsize, /* COLS */ + int vsize /* LINES */ + ) +{ + wresize (nw->window, g_status_height, hsize); + replace_panel (nw->panel, nw->window); + move_panel (nw->panel, vsize - g_status_height, 0); +} + +static void +write_status ( + const gchar* format, + ... + ) +{ + va_list args; + + va_start (args, format); + write_statusv (format, args); + va_end (args); +} + +static void +write_statusv ( + const gchar* format, + va_list args1 + ) +{ + char buffer[1024]; + vsnprintf (buffer, sizeof(buffer), format, args1); + + g_status_list = g_list_append (g_status_list, g_memdup (buffer, strlen(buffer)+1)); +} + +static +void +on_signal ( + int signum, + gpointer user_data + ) +{ + GMainLoop* loop = (GMainLoop*)user_data; + puts ("on_signal"); + g_main_loop_quit (loop); +} + +/* terminal resize signal + */ + +static void +on_winch ( + G_GNUC_UNUSED int signum + ) +{ + paint_ncurses (); +} + +static gboolean +on_startup ( + gpointer user_data + ) +{ + GMainLoop* loop = (GMainLoop*)user_data; + int e; + + puts ("startup."); + +/* find PGM protocol id */ +// TODO: fix valgrind errors + int ipproto_pgm = IPPROTO_PGM; +#if HAVE_GETPROTOBYNAME_R + char b[1024]; + struct protoent protobuf, *proto; + e = getprotobyname_r ("pgm", &protobuf, b, sizeof(b), &proto); + if (e != -1 && proto != NULL) { + if (proto->p_proto != ipproto_pgm) { + print f("Setting PGM protocol number to %i from /etc/protocols.\n", proto->p_proto); + ipproto_pgm = proto->p_proto; + } + } +#else + struct protoent *proto = getprotobyname ("pgm"); + if (proto != NULL) { + if (proto->p_proto != ipproto_pgm) { + printf("Setting PGM protocol number to %i from /etc/protocols.\n", proto->p_proto); + ipproto_pgm = proto->p_proto; + } + } +#endif + +/* open socket for snooping */ + puts ("opening raw socket."); + int sock = socket (PF_INET, SOCK_RAW, ipproto_pgm); + if (sock < 0) { + int _e = errno; + puts ("on_startup() failed"); + + if (_e == EPERM && 0 != getuid()) { + puts ("PGM protocol requires this program to run as superuser."); + } + g_main_loop_quit (loop); + return FALSE; + } + +/* drop out of setuid 0 */ + if (0 == getuid ()) { + puts ("dropping superuser privileges."); + setuid ((gid_t)65534); + setgid ((uid_t)65534); + } + + char _t = 1; + e = setsockopt (sock, IPPROTO_IP, IP_HDRINCL, &_t, sizeof(_t)); + if (e < 0) { + printw ("on_startup() failed\n"); + close (sock); + g_main_loop_quit (loop); + return FALSE; + } + +/* buffers */ + int buffer_size = 0; + socklen_t len = 0; + e = getsockopt (sock, SOL_SOCKET, SO_RCVBUF, &buffer_size, &len); + if (e == 0) { + printf ("receive buffer set at %i bytes.\n", buffer_size); + } + e = getsockopt (sock, SOL_SOCKET, SO_SNDBUF, &buffer_size, &len); + if (e == 0) { + printf ("send buffer set at %i bytes.\n", buffer_size); + } + +/* bind */ + struct sockaddr_in addr; + memset (&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + e = bind (sock, (struct sockaddr*)&addr, sizeof(addr)); + if (e < 0) { + printw ("on_startup() failed\n"); + close (sock); + g_main_loop_quit (loop); + return FALSE; + } + +/* multicast */ + struct ip_mreq mreq; + memset (&mreq, 0, sizeof(mreq)); + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + printf ("listening on interface %s.\n", inet_ntoa(mreq.imr_interface)); + mreq.imr_multiaddr.s_addr = inet_addr(g_network); + printf ("subscription on multicast address %s.\n", inet_ntoa(mreq.imr_multiaddr)); + e = setsockopt (sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + if (e < 0) { + printw ("on_startup() failed\n"); + close (sock); + g_main_loop_quit (loop); + return FALSE; + } + +/* multicast loopback */ +/* multicast ttl */ + +/* add socket to event manager */ + g_io_channel = g_io_channel_unix_new (sock); + printf ("socket opened with encoding %s.\n", g_io_channel_get_encoding(g_io_channel)); + + /* guint event = */ g_io_add_watch (g_io_channel, G_IO_IN | G_IO_PRI, on_io_data, NULL); + /* guint event = */ g_io_add_watch (g_io_channel, G_IO_ERR | G_IO_HUP | G_IO_NVAL, on_io_error, NULL); + +/* add stdin to event manager */ + g_stdin_channel = g_io_channel_unix_new (fileno(stdin)); + printf ("binding stdin with encoding %s.\n", g_io_channel_get_encoding(g_stdin_channel)); + + g_io_add_watch (g_stdin_channel, G_IO_IN | G_IO_PRI, on_stdin_data, NULL); + +/* periodic timer to snapshot statistics */ + g_timeout_add (g_snap_interval, (GSourceFunc)on_snap, NULL); + +/* period timer to update screen */ + g_timeout_add (g_paint_interval, (GSourceFunc)on_paint, NULL); + + puts ("READY"); + + init_ncurses(); + return FALSE; +} + +static gboolean +on_paint ( + G_GNUC_UNUSED gpointer data + ) +{ + paint_ncurses(); + + return TRUE; +} + +static guint +tsi_hash ( + gconstpointer v + ) +{ + return g_str_hash(pgm_tsi_print(v)); +} + +static gint +tsi_equal ( + gconstpointer v, + gconstpointer v2 + ) +{ + return memcmp (v, v2, (6 * sizeof(guint8)) + sizeof(guint16)) == 0; +} + +static gboolean +on_io_data ( + GIOChannel* source, + G_GNUC_UNUSED GIOCondition condition, + G_GNUC_UNUSED gpointer user_data + ) +{ + struct timeval now; + struct pgm_sk_buff_t* skb = pgm_alloc_skb (4096); + struct sockaddr_storage src, dst; + struct sockaddr_in* sin = (struct sockaddr_in*)&src; + socklen_t src_addr_len = sizeof(src); + int fd = g_io_channel_unix_get_fd(source); + + skb->len = recvfrom(fd, skb->head, 4096, MSG_DONTWAIT, (struct sockaddr*)&src, &src_addr_len); + + gettimeofday (&now, NULL); + g_packets++; + + GError* err = NULL; + gboolean is_valid = pgm_parse_raw (skb, (struct sockaddr*)&dst, &err); + if (!is_valid && err && PGM_PACKET_ERROR_CKSUM == err->code) + { +/* corrupt packet */ + if (!g_nets) { + g_nets = g_hash_table_new (g_int_hash, g_int_equal); + } + + struct pgm_netstat* netstat = g_hash_table_lookup (g_nets, &sin->sin_addr); + if (netstat == NULL) { + write_status ("new host publishing corrupt data, local nla %s", inet_ntoa(sin->sin_addr)); + netstat = g_malloc0(sizeof(struct pgm_netstat)); + netstat->addr = sin->sin_addr; + g_hash_table_insert (g_nets, (gpointer)&netstat->addr, (gpointer)netstat); + } + + netstat->corrupt++; + pgm_free_skb (skb); + return TRUE; + } + else if (!is_valid) + { +/* general error */ + pgm_free_skb (skb); + return TRUE; + } + +/* search for existing session */ + if (!g_hosts) { + g_hosts = g_hash_table_new (tsi_hash, tsi_equal); + } + + struct pgm_hoststat* hoststat = g_hash_table_lookup (g_hosts, &skb->tsi); + if (hoststat == NULL) { + write_status ("new tsi %s with local nla %s", pgm_tsi_print (&skb->tsi), inet_ntoa(sin->sin_addr)); + + hoststat = g_malloc0(sizeof(struct pgm_hoststat)); + memcpy (&hoststat->tsi, &skb->tsi, sizeof(pgm_tsi_t)); + hoststat->session_start = now; + + g_hash_table_insert (g_hosts, (gpointer)&hoststat->tsi, (gpointer)hoststat); + } + +/* increment statistics */ + memcpy (&hoststat->last_addr, &sin->sin_addr, sizeof(sin->sin_addr)); + hoststat->general.count++; + hoststat->general.bytes += skb->len; + hoststat->general.last = now; + + skb->data = (guint8*)skb->data + sizeof(struct pgm_header); + skb->len -= sizeof(struct pgm_header); + +/* repurpose is_valid for PGM subtype */ + is_valid = FALSE; + switch (skb->pgm_header->pgm_type) { + case PGM_SPM: + hoststat->spm.count++; + hoststat->spm.bytes += skb->len; + hoststat->spm.last = now; + + is_valid = pgm_verify_spm (skb); + if (!is_valid) { + hoststat->spm.invalid++; + hoststat->spm.last_invalid = now; + } else { + const struct pgm_spm* spm = (struct pgm_spm*)skb->data; + + hoststat->nla.s_addr = spm->spm_nla.s_addr; + if (pgm_uint32_lte (g_ntohl( spm->spm_sqn ), hoststat->spm_sqn)) { + hoststat->general.duplicate++; + break; + } + hoststat->spm_sqn = g_ntohl( spm->spm_sqn ); + hoststat->txw_trail = g_ntohl( spm->spm_trail ); + hoststat->txw_lead = g_ntohl( spm->spm_lead ); + hoststat->rxw_trail = hoststat->txw_trail; + hoststat->window_defined = TRUE; + } + break; + + case PGM_ODATA: + hoststat->odata.count++; + hoststat->odata.bytes += skb->len; + hoststat->odata.last = now; + + const struct pgm_data* data = (struct pgm_data*)skb->data; + + if (!hoststat->window_defined) { + hoststat->rxw_lead = g_ntohl (data->data_sqn) - 1; + hoststat->rxw_trail = hoststat->rxw_trail_init = hoststat->rxw_lead + 1; + hoststat->rxw_constrained = TRUE; + hoststat->window_defined = TRUE; + } else { + if (! pgm_uint32_gte( g_ntohl (data->data_sqn) , hoststat->rxw_trail ) ) + { + hoststat->odata.invalid++; + hoststat->odata.last_invalid = now; + break; + } + hoststat->rxw_trail = g_ntohl (data->data_trail); + } + + if (hoststat->rxw_constrained && hoststat->txw_trail > hoststat->rxw_trail_init) { + hoststat->rxw_constrained = FALSE; + } + + if ( pgm_uint32_lte ( g_ntohl (data->data_sqn), hoststat->rxw_lead ) ) { + hoststat->general.duplicate++; + break; + } else { + hoststat->rxw_lead = g_ntohl (data->data_sqn); + + hoststat->odata.tsdu += g_ntohs (skb->pgm_header->pgm_tsdu_length); + } + break; + + case PGM_RDATA: + hoststat->rdata.count++; + hoststat->rdata.bytes += skb->len; + hoststat->rdata.last = now; + break; + + case PGM_POLL: + hoststat->poll.count++; + hoststat->poll.bytes += skb->len; + hoststat->poll.last = now; + break; + + case PGM_POLR: + hoststat->polr.count++; + hoststat->polr.bytes += skb->len; + hoststat->polr.last = now; + break; + + case PGM_NAK: + hoststat->nak.count++; + hoststat->nak.bytes += skb->len; + hoststat->nak.last = now; + + is_valid = pgm_verify_nak (skb); + if (!is_valid) { + hoststat->nak.invalid++; + hoststat->nak.last_invalid = now; + } + break; + + case PGM_NNAK: + hoststat->nnak.count++; + hoststat->nnak.bytes += skb->len; + hoststat->nnak.last = now; + break; + + case PGM_NCF: + hoststat->ncf.count++; + hoststat->ncf.bytes += skb->len; + hoststat->ncf.last = now; + break; + + case PGM_SPMR: + hoststat->spmr.count++; + hoststat->spmr.bytes += skb->len; + hoststat->spmr.last = now; + + is_valid = pgm_verify_spmr (skb); + if (!is_valid) { + hoststat->spmr.invalid++; + hoststat->spmr.last_invalid = now; + } + break; + + default: + break; + } + + if (!is_valid) { + hoststat->general.invalid++; + hoststat->general.last_invalid = now; + } else { + hoststat->general.last_valid = now; + } + + pgm_free_skb (skb); + return TRUE; +} + +static gboolean +on_io_error ( + GIOChannel* source, + G_GNUC_UNUSED GIOCondition condition, + G_GNUC_UNUSED gpointer data + ) +{ + puts ("on_error."); + + GError *err; + g_io_channel_shutdown (source, FALSE, &err); + +/* remove event */ + return FALSE; +} + +/* process input commands from stdin/fd + */ + +static gboolean +on_stdin_data ( + G_GNUC_UNUSED GIOChannel* source, + G_GNUC_UNUSED GIOCondition condition, + G_GNUC_UNUSED gpointer data + ) +{ + int ch = wgetch (g_active->window); + if (ch == ERR) { + goto out; + } + +/* force redraw */ + if (ch == 12) { + clearok (curscr, TRUE); + paint_ncurses (); + goto out; + } + + if (ch == 'q') { + g_main_loop_quit(g_loop); + } + +out: + return TRUE; +} + +static gboolean +snap_stat ( + G_GNUC_UNUSED gpointer key, + gpointer value, + G_GNUC_UNUSED gpointer user_data + ) +{ + struct pgm_hoststat* hoststat = value; + +#define SNAP_STAT(name) \ + { \ + hoststat->name.snap_count = hoststat->name.count; \ + hoststat->name.snap_bytes = hoststat->name.bytes; \ + } + + SNAP_STAT(spm); + SNAP_STAT(poll); + SNAP_STAT(polr); + SNAP_STAT(odata); + SNAP_STAT(rdata); + SNAP_STAT(nak); + SNAP_STAT(nnak); + SNAP_STAT(ncf); + SNAP_STAT(spmr); + + SNAP_STAT(general); + + return FALSE; +} + +static gboolean +on_snap ( + gpointer data + ) +{ + if (!g_hosts) return TRUE; + + gettimeofday (&g_last_snap, NULL); + g_hash_table_foreach (g_hosts, (GHFunc)snap_stat, NULL); + + return TRUE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/ping.proto b/3rdparty/openpgm-svn-r1135/pgm/examples/ping.proto new file mode 100644 index 0000000..8c6dfd1 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/ping.proto @@ -0,0 +1,47 @@ +package example; + +message SubscriptionHeader { + required string subject = 1; +} + +message MarketDataHeader { + enum MsgType { + MSG_VERIFY = 0; + MSG_UPDATE = 1; + MSG_CORRECT = 2; + MSG_CLOSING = 3; + MSG_DROP = 4; + MSG_AGGREGATE = 5; + MSG_STATUS = 6; + MSG_CANCEL = 7; + MSG_INITIAL = 8; + } + required MsgType msg_type = 1; + enum RecType { + PING = 1; + } + required RecType rec_type = 2; + enum RecStatus { + STATUS_OK = 0; + STATUS_BAD_NAME = 1; + STATUS_BAD_LINE = 2; + STATUS_CACHE_FULL = 3; + STATUS_PERMISSION_DENIED = 4; + STATUS_PREEMPTED = 5; + STATUS_BAD_ACCESS = 6; + STATUS_TEMP_UNAVAIL = 7; + STATUS_REASSIGN = 8; + STATUS_NOSUBSCRIBERS = 9; + STATUS_EXPIRED = 10; + } + required RecStatus rec_status = 3; +} + +message Ping { + required SubscriptionHeader subscription_header = 1; + required MarketDataHeader market_data_header = 2; + required fixed64 time = 3; + required fixed64 seqno = 4; + required fixed64 latency = 5; + required bytes payload = 6; +} diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/pnonblocksyncrecv.c b/3rdparty/openpgm-svn-r1135/pgm/examples/pnonblocksyncrecv.c new file mode 100644 index 0000000..b5a2e13 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/pnonblocksyncrecv.c @@ -0,0 +1,385 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Simple PGM receiver: poll based non-blocking synchronous receiver. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef G_OS_UNIX +# include +# include +# include +# include +#endif +#include + +/* example dependencies */ +#include +#include + + +/* typedefs */ + +/* globals */ + +static int g_port = 0; +static const char* g_network = ""; +static gboolean g_multicast_loop = FALSE; +static int g_udp_encap_port = 0; + +static int g_max_tpdu = 1500; +static int g_sqns = 100; + +static pgm_sock_t* g_sock = NULL; +static gboolean g_quit; +static int g_quit_pipe[2]; + +static void on_signal (int); +static gboolean on_startup (void); + +static int on_data (gconstpointer, size_t, struct pgm_sockaddr_t*); + + +G_GNUC_NORETURN static +void +usage ( + const char* bin + ) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); + exit (1); +} + +int +main ( + int argc, + char* argv[] + ) +{ + int e; + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + + log_init (); + g_message ("pnonblocksyncrecv"); + + if (!pgm_init (&pgm_err)) { + g_error ("Unable to start PGM engine: %s", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:lh")) != -1) + { + switch (c) { + case 'n': g_network = optarg; break; + case 's': g_port = atoi (optarg); break; + case 'p': g_udp_encap_port = atoi (optarg); break; + case 'l': g_multicast_loop = TRUE; break; + + case 'h': + case '?': usage (binary_name); + } + } + + g_quit = FALSE; +#ifdef G_OS_UNIX + e = pipe (g_quit_pipe); +#else + e = _pipe (g_quit_pipe, 4096, _O_BINARY | _O_NOINHERIT); +#endif + g_assert (0 == e); + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); + signal (SIGINT, on_signal); + signal (SIGTERM, on_signal); +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif + + if (!on_startup()) { + g_error ("startup failed"); + exit(1); + } + +/* dispatch loop */ + g_message ("entering PGM message loop ... "); + do { + struct timeval tv; + int timeout; + int n_fds = 2; + struct pollfd fds[ 1 + n_fds ]; + char buffer[4096]; + size_t len; + struct pgm_sockaddr_t from; + socklen_t fromlen = sizeof(from); + const int status = pgm_recvfrom (g_sock, + buffer, + sizeof(buffer), + 0, + &len, + &from, + &fromlen, + &pgm_err); + switch (status) { + case PGM_IO_STATUS_NORMAL: + on_data (buffer, len, &from); + break; + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (g_sock, IPPROTO_PGM, PGM_TIME_REMAIN, &tv, &optlen); + } + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (g_sock, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &optlen); + } +/* fall through */ + case PGM_IO_STATUS_WOULD_BLOCK: +/* poll for next event */ +block: + timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + memset (fds, 0, sizeof(fds)); + fds[0].fd = g_quit_pipe[0]; + fds[0].events = POLLIN; + pgm_poll_info (g_sock, &fds[1], &n_fds, POLLIN); + poll (fds, 1 + n_fds, timeout /* ms */); + break; + default: + if (pgm_err) { + g_warning ("%s", pgm_err->message); + pgm_error_free (pgm_err); + pgm_err = NULL; + } + if (PGM_IO_STATUS_ERROR == status) + break; + } + } while (!g_quit); + + g_message ("message loop terminated, cleaning up."); + +/* cleanup */ + close (g_quit_pipe[0]); + close (g_quit_pipe[1]); + + if (g_sock) { + g_message ("closing PGM socket."); + pgm_close (g_sock, TRUE); + g_sock = NULL; + } + + g_message ("PGM engine shutdown."); + pgm_shutdown (); + g_message ("finished."); + return EXIT_SUCCESS; +} + +static +void +on_signal ( + int signum + ) +{ + g_message ("on_signal (signum:%d)", signum); + g_quit = TRUE; + const char one = '1'; + const size_t writelen = write (g_quit_pipe[1], &one, sizeof(one)); + g_assert (sizeof(one) == writelen); +} + +static +gboolean +on_startup (void) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + + g_message ("startup."); + +/* parse network parameter into transport address structure */ + if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { + g_error ("parsing network parameter: %s", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + if (g_udp_encap_port) { + g_message ("create PGM/UDP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + } else { + g_message ("create PGM/IP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int recv_only = 1, + passive = 0, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_secs (2), + nak_rdata_ivl = pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_PASSIVE, &passive, sizeof(passive)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + g_error ("creating GSI: %s", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (g_sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + g_error ("binding PGM socket: %s", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int nonblocking = 1, + multicast_loop = g_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); + + if (!pgm_connect (g_sock, &pgm_err)) { + g_error ("connecting PGM socket: %s", pgm_err->message); + goto err_abort; + } + + g_message ("startup complete."); + return TRUE; + +err_abort: + if (NULL != g_sock) { + pgm_close (g_sock, FALSE); + g_sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +static +int +on_data ( + gconstpointer data, + size_t len, + struct pgm_sockaddr_t* from + ) +{ +/* protect against non-null terminated strings */ + char buf[1024], tsi[PGM_TSISTRLEN]; + const size_t buflen = MIN(sizeof(buf) - 1, len); + strncpy (buf, (const char*)data, buflen); + buf[buflen] = '\0'; + pgm_tsi_print_r (&from->sa_addr, tsi, sizeof(tsi)); + + g_message ("\"%s\" (%u bytes from %s)", + buf, + (unsigned)len, + tsi); + + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/purinrecv.c b/3rdparty/openpgm-svn-r1135/pgm/examples/purinrecv.c new file mode 100644 index 0000000..0ac38d9 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/purinrecv.c @@ -0,0 +1,479 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * プリン PGM receiver + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#ifndef _WIN32 +# include +#else +# include "getopt.h" +#endif +#include + + +/* globals */ + +static int port = 0; +static const char* network = ""; +static bool use_multicast_loop = FALSE; +static int udp_encap_port = 0; + +static int max_tpdu = 1500; +static int sqns = 100; + +static bool use_pgmcc = FALSE; +static bool use_fec = FALSE; +static int rs_k = 8; +static int rs_n = 255; + +static pgm_sock_t* sock = NULL; +static bool is_terminated = FALSE; + +#ifndef _WIN32 +static int terminate_pipe[2]; +static void on_signal (int); +#else +static HANDLE terminate_event; +static BOOL on_console_ctrl (DWORD); +#endif +#ifndef _MSC_VER +static void usage (const char*) __attribute__((__noreturn__)); +#else +static void usage (const char*); +#endif + +static bool on_startup (void); +static int on_data (const void*restrict, const size_t, const struct pgm_sockaddr_t*restrict); + + +static void +usage ( + const char* bin + ) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -c : Enable PGMCC\n"); + fprintf (stderr, " -f : Enable FEC with either proactive or ondemand parity\n"); + fprintf (stderr, " -K : Configure Reed-Solomon code (n, k)\n"); + fprintf (stderr, " -N \n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); + fprintf (stderr, " -i : List available interfaces\n"); + exit (EXIT_SUCCESS); +} + +int +main ( + int argc, + char* argv[] + ) +{ + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + +#if !defined(_WIN32) || defined(CONFIG_TARGET_WINE) + puts ("プリン プリン"); +#else + _putws (L"プリン プリン"); +#endif + + if (!pgm_init (&pgm_err)) { + fprintf (stderr, "Unable to start PGM engine: %s\n", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ +#ifdef _WIN32 + const char* binary_name = strrchr (argv[0], '\\') + 1; +#else + const char* binary_name = strrchr (argv[0], '/') + 1; +#endif + int c; + while ((c = getopt (argc, argv, "s:n:p:cf:K:N:lih")) != -1) + { + switch (c) { + case 'n': network = optarg; break; + case 's': port = atoi (optarg); break; + case 'p': udp_encap_port = atoi (optarg); break; + case 'c': use_pgmcc = TRUE; break; + case 'f': use_fec = TRUE; break; + case 'K': rs_k = atoi (optarg); break; + case 'N': rs_n = atoi (optarg); break; + case 'l': use_multicast_loop = TRUE; break; + + case 'i': + pgm_if_print_all(); + return EXIT_SUCCESS; + + case 'h': + case '?': usage (binary_name); + } + } + + if (use_fec && ( !rs_n || !rs_k )) { + fprintf (stderr, "Invalid Reed-Solomon parameters RS(%d,%d).\n", rs_n, rs_k); + usage (binary_name); + } + +/* setup signal handlers */ +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif +#ifndef _WIN32 + int e = pipe (terminate_pipe); + assert (0 == e); + signal (SIGINT, on_signal); + signal (SIGTERM, on_signal); +#else + terminate_event = CreateEvent (NULL, TRUE, FALSE, TEXT("TerminateEvent")); + SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); +#endif /* !_WIN32 */ + + if (!on_startup()) { + fprintf (stderr, "Startup failed\n"); + return EXIT_FAILURE; + } + +/* dispatch loop */ +#ifndef _WIN32 + int fds; + fd_set readfds; +#else + int n_handles = 3, recv_sock, pending_sock; + HANDLE waitHandles[ 3 ]; + DWORD dwTimeout, dwEvents; + WSAEVENT recvEvent, pendingEvent; + socklen_t socklen = sizeof(int); + + recvEvent = WSACreateEvent (); + pgm_getsockopt (sock, IPPROTO_PGM, PGM_RECV_SOCK, &recv_sock, &socklen); + WSAEventSelect (recv_sock, recvEvent, FD_READ); + pendingEvent = WSACreateEvent (); + pgm_getsockopt (sock, IPPROTO_PGM, PGM_PENDING_SOCK, &pending_sock, &socklen); + WSAEventSelect (pending_sock, pendingEvent, FD_READ); + + waitHandles[0] = terminate_event; + waitHandles[1] = recvEvent; + waitHandles[2] = pendingEvent; +#endif /* !_WIN32 */ + puts ("Entering PGM message loop ... "); + do { + struct timeval tv; + char buffer[4096]; + size_t len; + struct pgm_sockaddr_t from; + socklen_t fromlen = sizeof (from); + const int status = pgm_recvfrom (sock, + buffer, + sizeof(buffer), + 0, + &len, + &from, + &fromlen, + &pgm_err); + switch (status) { + case PGM_IO_STATUS_NORMAL: + on_data (buffer, len, &from); + break; + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (sock, IPPROTO_PGM, PGM_TIME_REMAIN, &tv, &optlen); + } + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (sock, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &optlen); + } + case PGM_IO_STATUS_WOULD_BLOCK: +/* select for next event */ +block: +#ifndef _WIN32 + fds = terminate_pipe[0] + 1; + FD_ZERO(&readfds); + FD_SET(terminate_pipe[0], &readfds); + pgm_select_info (sock, &readfds, NULL, &fds); + fds = select (fds, &readfds, NULL, NULL, PGM_IO_STATUS_WOULD_BLOCK == status ? NULL : &tv); +#else + dwTimeout = PGM_IO_STATUS_WOULD_BLOCK == status ? INFINITE : (DWORD)((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + dwEvents = WaitForMultipleObjects (n_handles, waitHandles, FALSE, dwTimeout); + switch (dwEvents) { + case WAIT_OBJECT_0+1: WSAResetEvent (recvEvent); break; + case WAIT_OBJECT_0+2: WSAResetEvent (pendingEvent); break; + default: break; + } +#endif /* !_WIN32 */ + break; + + default: + if (pgm_err) { + fprintf (stderr, "%s\n", pgm_err->message); + pgm_error_free (pgm_err); + pgm_err = NULL; + } + if (PGM_IO_STATUS_ERROR == status) + break; + } + } while (!is_terminated); + + puts ("Message loop terminated, cleaning up."); + +/* cleanup */ +#ifndef _WIN32 + close (terminate_pipe[0]); + close (terminate_pipe[1]); +#else + WSACloseEvent (recvEvent); + WSACloseEvent (pendingEvent); + CloseHandle (terminate_event); +#endif /* !_WIN32 */ + + if (sock) { + puts ("Destroying PGM socket."); + pgm_close (sock, TRUE); + sock = NULL; + } + + puts ("PGM engine shutdown."); + pgm_shutdown (); + puts ("finished."); + return EXIT_SUCCESS; +} + +#ifndef _WIN32 +static +void +on_signal ( + int signum + ) +{ + printf ("on_signal (signum:%d)\n", signum); + is_terminated = TRUE; + const char one = '1'; + const size_t writelen = write (terminate_pipe[1], &one, sizeof(one)); + assert (sizeof(one) == writelen); +} +#else +static +BOOL +on_console_ctrl ( + DWORD dwCtrlType + ) +{ + printf ("on_console_ctrl (dwCtrlType:%lu)\n", (unsigned long)dwCtrlType); + is_terminated = TRUE; + SetEvent (terminate_event); + return TRUE; +} +#endif /* !_WIN32 */ + +static +bool +on_startup (void) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + +/* parse network parameter into PGM socket address structure */ + if (!pgm_getaddrinfo (network, NULL, &res, &pgm_err)) { + fprintf (stderr, "Parsing network parameter: %s\n", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + if (udp_encap_port) { + puts ("Create PGM/UDP socket."); + if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + fprintf (stderr, "Creating PGM/UDP socket: %s\n", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (sock, IPPROTO_PGM, PGM_UDP_ENCAP_UCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_UDP_ENCAP_MCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + } else { + puts ("Create PGM/IP socket."); + if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + fprintf (stderr, "Creating PGM/IP socket: %s\n", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (sock, IPPROTO_PGM, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int recv_only = 1, + passive = 0, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_secs (2), + nak_rdata_ivl = pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + pgm_setsockopt (sock, IPPROTO_PGM, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_PASSIVE, &passive, sizeof(passive)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_MTU, &max_tpdu, sizeof(max_tpdu)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_RXW_SQNS, &sqns, sizeof(sqns)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + +#ifdef I_UNDERSTAND_PGMCC_AND_FEC_ARE_NOT_SUPPORTED + if (use_pgmcc) { + struct pgm_pgmccinfo_t pgmccinfo; + pgmccinfo.ack_bo_ivl = pgm_msecs (50); + pgmccinfo.ack_c = 75; + pgmccinfo.ack_c_p = 500; + pgm_setsockopt (sock, IPPROTO_PGM, PGM_USE_PGMCC, &pgmccinfo, sizeof(pgmccinfo)); + } + if (use_fec) { + struct pgm_fecinfo_t fecinfo; + fecinfo.block_size = rs_n; + fecinfo.proactive_packets = 0; + fecinfo.group_size = rs_k; + fecinfo.ondemand_parity_enabled = TRUE; + fecinfo.var_pktlen_enabled = FALSE; + pgm_setsockopt (sock, IPPROTO_PGM, PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); + } +#endif + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = port ? port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + fprintf (stderr, "Creating GSI: %s\n", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + fprintf (stderr, "Binding PGM socket: %s\n", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (sock, IPPROTO_PGM, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int nonblocking = 1, + multicast_loop = use_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (sock, IPPROTO_PGM, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (sock, IPPROTO_PGM, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); + + if (!pgm_connect (sock, &pgm_err)) { + fprintf (stderr, "Connecting PGM socket: %s\n", pgm_err->message); + goto err_abort; + } + + puts ("Startup complete."); + return TRUE; + +err_abort: + if (NULL != sock) { + pgm_close (sock, FALSE); + sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + if (NULL != sock) { + pgm_close (sock, FALSE); + sock = NULL; + } + return FALSE; +} + +static +int +on_data ( + const void* restrict data, + const size_t len, + const struct pgm_sockaddr_t* restrict from + ) +{ +/* protect against non-null terminated strings */ + char buf[1024], tsi[PGM_TSISTRLEN]; + const size_t buflen = MIN(sizeof(buf) - 1, len); +#ifndef _MSC_VER + strncpy (buf, (const char*)data, buflen); + buf[buflen] = '\0'; + pgm_tsi_print_r (&from->sa_addr, tsi, sizeof(tsi)); + printf ("\"%s\" (%zu bytes from %s)\n", + buf, len, tsi); +#else + strncpy_s (buf, buflen, (const char*)data, _TRUNCATE); + pgm_tsi_print_r (&from->sa_addr, tsi, sizeof(tsi)); +/* Microsoft CRT will crash on %zu */ + printf ("\"%s\" (%u bytes from %s)\n", + buf, (unsigned)len, tsi); +#endif + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/purinrecvcc.cc b/3rdparty/openpgm-svn-r1135/pgm/examples/purinrecvcc.cc new file mode 100644 index 0000000..84631dd --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/purinrecvcc.cc @@ -0,0 +1,434 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * プリン PGM receiver + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#ifndef _WIN32 +# include +# include +# include +#else +# include "getopt.h" +#endif +#include + + +/* globals */ + +static int port = 0; +static const char* network = ""; +static bool use_multicast_loop = FALSE; +static int udp_encap_port = 0; + +static int max_tpdu = 1500; +static int sqns = 100; + +static bool use_fec = FALSE; +static int rs_k = 8; +static int rs_n = 255; + +static ip::pgm::endpoint* endpoint = NULL; +static ip::pgm::socket* sock = NULL; +static bool is_terminated = FALSE; + +#ifndef _WIN32 +static int terminate_pipe[2]; +static void on_signal (int); +#else +static HANDLE terminate_event; +static BOOL on_console_ctrl (DWORD); +#endif +#ifndef _MSC_VER +static void usage (const char*) __attribute__((__noreturn__)); +#else +static void usage (const char*); +#endif + +static bool on_startup (void); +static int on_data (const void*, size_t, const ip::pgm::endpoint&); + + +static void +usage ( + const char* bin + ) +{ + std::cerr << "Usage: " << bin << " [options]" << std::endl; + std::cerr << " -n : Multicast group or unicast IP address" << std::endl; + std::cerr << " -s : IP port" << std::endl; + std::cerr << " -p : Encapsulate PGM in UDP on IP port" << std::endl; + std::cerr << " -f : Enable FEC with either proactive or ondemand parity" << std::endl; + std::cerr << " -K : Configure Reed-Solomon code (n, k)" << std::endl; + std::cerr << " -N " << std::endl; + std::cerr << " -l : Enable multicast loopback and address sharing" << std::endl; + std::cerr << " -i : List available interfaces" << std::endl; + exit (EXIT_SUCCESS); +} + +int +main ( + int argc, + char* argv[] + ) +{ + cpgm::pgm_error_t* pgm_err = NULL; + + std::setlocale (LC_ALL, ""); + +#if !defined(_WIN32) || defined(CONFIG_TARGET_WINE) + std::cout << "プリン プリン" << std::endl; +#else + std::wcout << L"プリン プリン" << std::endl; +#endif + + if (!cpgm::pgm_init (&pgm_err)) { + std::cerr << "Unable to start PGM engine: " << pgm_err->message << std::endl; + cpgm::pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = std::strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:f:K:N:lih")) != -1) + { + switch (c) { + case 'n': network = optarg; break; + case 's': port = atoi (optarg); break; + case 'p': udp_encap_port = atoi (optarg); break; + case 'f': use_fec = TRUE; break; + case 'K': rs_k = atoi (optarg); break; + case 'N': rs_n = atoi (optarg); break; + case 'l': use_multicast_loop = TRUE; break; + + case 'i': + cpgm::pgm_if_print_all(); + return EXIT_SUCCESS; + + case 'h': + case '?': usage (binary_name); + } + } + + if (use_fec && ( !rs_n || !rs_k )) { + std::cerr << "Invalid Reed-Solomon parameters RS(" << rs_n << "," << rs_k << ")." << std::endl; + usage (binary_name); + } + +/* setup signal handlers */ +#ifdef SIGHUP + std::signal (SIGHUP, SIG_IGN); +#endif +#ifndef _WIN32 + int e = pipe (terminate_pipe); + assert (0 == e); + std::signal (SIGINT, on_signal); + std::signal (SIGTERM, on_signal); +#else + terminate_event = CreateEvent (NULL, TRUE, FALSE, TEXT("TerminateEvent")); + SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); +#endif /* !_WIN32 */ + + if (!on_startup()) { + std::cerr << "Startup failed" << std::endl; + return EXIT_FAILURE; + } + +/* dispatch loop */ +#ifndef _WIN32 + int fds; + fd_set readfds; +#else + int n_handles = 3, recv_sock, pending_sock; + HANDLE waitHandles[ 3 ]; + DWORD dwTimeout, dwEvents; + WSAEVENT recvEvent, pendingEvent; + socklen_t socklen = sizeof(int); + + recvEvent = WSACreateEvent (); + sock->get_option (IPPROTO_PGM, cpgm::PGM_RECV_SOCK, &recv_sock, &socklen); + WSAEventSelect (recv_sock, recvEvent, FD_READ); + pendingEvent = WSACreateEvent (); + sock->get_option (IPPROTO_PGM, cpgm::PGM_PENDING_SOCK, &pending_sock, &socklen); + WSAEventSelect (pending_sock, pendingEvent, FD_READ); + + waitHandles[0] = terminate_event; + waitHandles[1] = recvEvent; + waitHandles[2] = pendingEvent; +#endif /* !_WIN32 */ + std::cout << "Entering PGM message loop ... " << std::endl; + do { + socklen_t optlen; + struct timeval tv; + char buffer[4096]; + size_t len; + ip::pgm::endpoint from; + const int status = sock->receive_from (buffer, + sizeof(buffer), + 0, + &len, + &from, + &pgm_err); + switch (status) { + case cpgm::PGM_IO_STATUS_NORMAL: + on_data (buffer, len, from); + break; + case cpgm::PGM_IO_STATUS_TIMER_PENDING: + optlen = sizeof (tv); + sock->get_option (IPPROTO_PGM, cpgm::PGM_TIME_REMAIN, &tv, &optlen); + goto block; + case cpgm::PGM_IO_STATUS_RATE_LIMITED: + optlen = sizeof (tv); + sock->get_option (IPPROTO_PGM, cpgm::PGM_RATE_REMAIN, &tv, &optlen); + case cpgm::PGM_IO_STATUS_WOULD_BLOCK: +/* select for next event */ +block: +#ifndef _WIN32 + fds = terminate_pipe[0] + 1; + FD_ZERO(&readfds); + FD_SET(terminate_pipe[0], &readfds); + pgm_select_info (sock->native(), &readfds, NULL, &fds); + fds = select (fds, &readfds, NULL, NULL, cpgm::PGM_IO_STATUS_WOULD_BLOCK == status ? NULL : &tv); +#else + dwTimeout = cpgm::PGM_IO_STATUS_WOULD_BLOCK == status ? INFINITE : (DWORD)((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + dwEvents = WaitForMultipleObjects (n_handles, waitHandles, FALSE, dwTimeout); + switch (dwEvents) { + case WAIT_OBJECT_0+1: WSAResetEvent (recvEvent); break; + case WAIT_OBJECT_0+2: WSAResetEvent (pendingEvent); break; + default: break; + } +#endif /* !_WIN32 */ + break; + + default: + if (pgm_err) { + std::cerr << pgm_err->message << std::endl; + cpgm::pgm_error_free (pgm_err); + pgm_err = NULL; + } + if (cpgm::PGM_IO_STATUS_ERROR == status) + break; + } + } while (!is_terminated); + + std::cout << "Message loop terminated, cleaning up." << std::endl; + +/* cleanup */ +#ifndef _WIN32 + close (terminate_pipe[0]); + close (terminate_pipe[1]); +#else + WSACloseEvent (recvEvent); + WSACloseEvent (pendingEvent); + CloseHandle (terminate_event); +#endif /* !_WIN32 */ + + if (sock) { + std::cout << "Closing PGM socket." << std::endl; + sock->close (TRUE); + sock = NULL; + } + + std::cout << "PGM engine shutdown." << std::endl; + cpgm::pgm_shutdown (); + std::cout << "finished." << std::endl; + return EXIT_SUCCESS; +} + +#ifndef _WIN32 +static +void +on_signal ( + int signum + ) +{ + std::cout << "on_signal (signum:" << signum << ")" << std::endl; + is_terminated = TRUE; + const char one = '1'; + const size_t writelen = write (terminate_pipe[1], &one, sizeof(one)); + assert (sizeof(one) == writelen); +} +#else +static +BOOL +on_console_ctrl ( + DWORD dwCtrlType + ) +{ + std::cout << "on_console_ctrl (dwCtrlType:" << dwCtrlType << ")" << std::endl; + is_terminated = TRUE; + SetEvent (terminate_event); + return TRUE; +} +#endif /* !_WIN32 */ + +static +bool +on_startup (void) +{ + struct cpgm::pgm_addrinfo_t* res = NULL; + cpgm::pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + +/* parse network parameter into PGM socket address structure */ + if (!pgm_getaddrinfo (network, NULL, &res, &pgm_err)) { + std::cerr << "Parsing network parameter: " << pgm_err->message << std::endl; + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + sock = new ip::pgm::socket(); + + if (udp_encap_port) { + std::cout << "Create PGM/UDP socket." << std::endl; + if (!sock->open (sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + std::cerr << "Creating PGM/UDP socket: " << pgm_err->message << std::endl; + goto err_abort; + } + sock->set_option (IPPROTO_PGM, cpgm::PGM_UDP_ENCAP_UCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_UDP_ENCAP_MCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + } else { + std::cout << "Create PGM/IP socket." << std::endl; + if (!sock->open (sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + std::cerr << "Creating PGM/IP socket: " << pgm_err->message << std::endl; + goto err_abort; + } + } + + { +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + sock->set_option (IPPROTO_PGM, cpgm::PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + } + + cpgm::pgm_drop_superuser(); + + { +/* set PGM parameters */ + const int recv_only = 1, + passive = 0, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_secs (2), + nak_rdata_ivl = pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + sock->set_option (IPPROTO_PGM, cpgm::PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_PASSIVE, &passive, sizeof(passive)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_MTU, &max_tpdu, sizeof(max_tpdu)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_RXW_SQNS, &sqns, sizeof(sqns)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + } + if (use_fec) { + struct cpgm::pgm_fecinfo_t fecinfo; + fecinfo.block_size = rs_n; + fecinfo.proactive_packets = 0; + fecinfo.group_size = rs_k; + fecinfo.ondemand_parity_enabled = TRUE; + fecinfo.var_pktlen_enabled = FALSE; + sock->set_option (IPPROTO_PGM, cpgm::PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); + } + +/* create global session identifier */ + endpoint = new ip::pgm::endpoint (DEFAULT_DATA_DESTINATION_PORT); + +/* assign socket to specified address */ + if (!sock->bind (*endpoint, &pgm_err)) { + std::cerr << "Binding PGM socket: " << pgm_err->message << std::endl; + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + sock->set_option (IPPROTO_PGM, cpgm::PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + + { +/* set IP parameters */ + const int nonblocking = 1, + multicast_loop = use_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + sock->set_option (IPPROTO_PGM, cpgm::PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_TOS, &dscp, sizeof(dscp)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); + } + + if (!sock->connect (&pgm_err)) { + std::cerr << "Connecting PGM socket: " << pgm_err->message << std::endl; + goto err_abort; + } + + std::cout << "Startup complete." << std::endl; + return TRUE; + +err_abort: + if (NULL != sock) { + sock->close (FALSE); + sock = NULL; + } + if (NULL != res) { + cpgm::pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + cpgm::pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +static +int +on_data ( + const void* data, + size_t len, + const ip::pgm::endpoint& from + ) +{ +/* protect against non-null terminated strings */ + char buf[1024], tsi[PGM_TSISTRLEN]; + const size_t buflen = MIN(sizeof(buf) - 1, len); + strncpy (buf, (char*)data, buflen); + buf[buflen] = '\0'; + cpgm::pgm_tsi_print_r (from.address(), tsi, sizeof(tsi)); + std::cout << "\"" << buf << "\" (" << len << " bytes from " << tsi << ")" << std::endl; + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/purinsend.c b/3rdparty/openpgm-svn-r1135/pgm/examples/purinsend.c new file mode 100644 index 0000000..871c631 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/purinsend.c @@ -0,0 +1,284 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * プリン PGM sender + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#ifndef _WIN32 +# include +#else +# include "getopt.h" +#endif +#include + + +/* globals */ + +static int port = 0; +static const char* network = ""; +static bool use_multicast_loop = FALSE; +static int udp_encap_port = 0; + +static int max_tpdu = 1500; +static int max_rte = 400*1000; /* very conservative rate, 2.5mb/s */ +static int sqns = 100; + +static bool use_fec = FALSE; +static int rs_k = 8; +static int rs_n = 255; + +static pgm_sock_t* sock = NULL; + +#ifndef _MSC_VER +static void usage (const char*) __attribute__((__noreturn__)); +#else +static void usage (const char*); +#endif +static bool create_sock (void); + + +static void +usage ( + const char* bin + ) +{ + fprintf (stderr, "Usage: %s [options] message\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -r : Regulate to rate bytes per second\n"); + fprintf (stderr, " -f : Enable FEC with either proactive or ondemand parity\n"); + fprintf (stderr, " -K : Configure Reed-Solomon code (n, k)\n"); + fprintf (stderr, " -N \n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); + fprintf (stderr, " -i : List available interfaces\n"); + exit (EXIT_SUCCESS); +} + +int +main ( + int argc, + char *argv[] + ) +{ + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + + if (!pgm_init (&pgm_err)) { + fprintf (stderr, "Unable to start PGM engine: %s\n", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ +#ifdef _WIN32 + const char* binary_name = strrchr (argv[0], '\\') + 1; +#else + const char* binary_name = strrchr (argv[0], '/') + 1; +#endif + int c; + while ((c = getopt (argc, argv, "s:n:p:r:f:K:N:lih")) != -1) + { + switch (c) { + case 'n': network = optarg; break; + case 's': port = atoi (optarg); break; + case 'p': udp_encap_port = atoi (optarg); break; + case 'r': max_rte = atoi (optarg); break; + case 'f': use_fec = TRUE; break; + case 'K': rs_k = atoi (optarg); break; + case 'N': rs_n = atoi (optarg); break; + + case 'l': use_multicast_loop = TRUE; break; + + case 'i': + pgm_if_print_all(); + return EXIT_SUCCESS; + + case 'h': + case '?': + usage (binary_name); + } + } + + if (use_fec && ( !rs_n || !rs_k )) { + fprintf (stderr, "Invalid Reed-Solomon parameters RS(%d,%d).\n", rs_n, rs_k); + usage (binary_name); + } + + if (create_sock()) + { + while (optind < argc) { + const int status = pgm_send (sock, argv[optind], strlen (argv[optind]) + 1, NULL); + if (PGM_IO_STATUS_NORMAL != status) { + fprintf (stderr, "pgm_send() failed.\n"); + } + optind++; + } + } + +/* cleanup */ + if (sock) { + pgm_close (sock, TRUE); + sock = NULL; + } + pgm_shutdown(); + return EXIT_SUCCESS; +} + +static +bool +create_sock (void) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + +/* parse network parameter into PGM socket address structure */ + if (!pgm_getaddrinfo (network, NULL, &res, &pgm_err)) { + fprintf (stderr, "Parsing network parameter: %s\n", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + if (udp_encap_port) { + if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + fprintf (stderr, "Creating PGM/UDP socket: %s\n", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (sock, IPPROTO_PGM, PGM_UDP_ENCAP_UCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_UDP_ENCAP_MCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + } else { + if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + fprintf (stderr, "Creating PGM/IP socket: %s\n", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (sock, IPPROTO_PGM, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int send_only = 1, + ambient_spm = pgm_secs (30), + heartbeat_spm[] = { pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (1300), + pgm_secs (7), + pgm_secs (16), + pgm_secs (25), + pgm_secs (30) }; + + pgm_setsockopt (sock, IPPROTO_PGM, PGM_SEND_ONLY, &send_only, sizeof(send_only)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_MTU, &max_tpdu, sizeof(max_tpdu)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_TXW_SQNS, &sqns, sizeof(sqns)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_TXW_MAX_RTE, &max_rte, sizeof(max_rte)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_AMBIENT_SPM, &ambient_spm, sizeof(ambient_spm)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_HEARTBEAT_SPM, &heartbeat_spm, sizeof(heartbeat_spm)); + if (use_fec) { + struct pgm_fecinfo_t fecinfo; + fecinfo.block_size = rs_n; + fecinfo.proactive_packets = 0; + fecinfo.group_size = rs_k; + fecinfo.ondemand_parity_enabled = TRUE; + fecinfo.var_pktlen_enabled = TRUE; + pgm_setsockopt (sock, IPPROTO_PGM, PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); + } + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = port ? port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + fprintf (stderr, "Creating GSI: %s\n", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + fprintf (stderr, "Binding PGM socket: %s\n", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + unsigned i; + for (i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (sock, IPPROTO_PGM, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int blocking = 0, + multicast_loop = use_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (sock, IPPROTO_PGM, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (sock, IPPROTO_PGM, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_NOBLOCK, &blocking, sizeof(blocking)); + + if (!pgm_connect (sock, &pgm_err)) { + fprintf (stderr, "Connecting PGM socket: %s\n", pgm_err->message); + goto err_abort; + } + + return TRUE; + +err_abort: + if (NULL != sock) { + pgm_close (sock, FALSE); + sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/purinsendcc.cc b/3rdparty/openpgm-svn-r1135/pgm/examples/purinsendcc.cc new file mode 100644 index 0000000..6ce9648 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/purinsendcc.cc @@ -0,0 +1,269 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * プリン PGM sender + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#ifndef _WIN32 +# include +# include +#else +# include "getopt.h" +#endif +#include + + +/* globals */ + +static int port = 0; +static const char* network = ""; +static bool use_multicast_loop = FALSE; +static int udp_encap_port = 0; + +static int max_tpdu = 1500; +static int max_rte = 400*1000; /* very conservative rate, 2.5mb/s */ +static int sqns = 100; + +static bool use_fec = FALSE; +static int rs_k = 8; +static int rs_n = 255; + +static ip::pgm::endpoint* endpoint = NULL; +static ip::pgm::socket* sock = NULL; + +#ifndef _MSC_VER +static void usage (const char*) __attribute__((__noreturn__)); +#else +static void usage (const char*); +#endif +static bool create_sock (void); + + +static void +usage ( + const char* bin + ) +{ + std::cerr << "Usage: " << bin << " [options] message" << std::endl; + std::cerr << " -n : Multicast group or unicast IP address" << std::endl; + std::cerr << " -s : IP port" << std::endl; + std::cerr << " -p : Encapsulate PGM in UDP on IP port" << std::endl; + std::cerr << " -r : Regulate to rate bytes per second" << std::endl; + std::cerr << " -f : Enable FEC with either proactive or ondemand parity" << std::endl; + std::cerr << " -K : Configure Reed-Solomon code (n, k)" << std::endl; + std::cerr << " -N " << std::endl; + std::cerr << " -l : Enable multicast loopback and address sharing" << std::endl; + std::cerr << " -i : List available interfaces" << std::endl; + exit (EXIT_SUCCESS); +} + +int +main ( + int argc, + char *argv[] + ) +{ + cpgm::pgm_error_t* pgm_err = NULL; + + std::setlocale (LC_ALL, ""); + + if (!cpgm::pgm_init (&pgm_err)) { + std::cerr << "Unable to start PGM engine: " << pgm_err->message << std::endl; + cpgm::pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:r:f:K:N:lih")) != -1) + { + switch (c) { + case 'n': network = optarg; break; + case 's': port = atoi (optarg); break; + case 'p': udp_encap_port = atoi (optarg); break; + case 'r': max_rte = atoi (optarg); break; + case 'f': use_fec = TRUE; break; + case 'K': rs_k = atoi (optarg); break; + case 'N': rs_n = atoi (optarg); break; + + case 'l': use_multicast_loop = TRUE; break; + + case 'i': + cpgm::pgm_if_print_all(); + return EXIT_SUCCESS; + + case 'h': + case '?': + usage (binary_name); + } + } + + if (use_fec && ( !rs_n || !rs_k )) { + std::cerr << "Invalid Reed-Solomon parameters RS(" << rs_n << "," << rs_k << ")." << std::endl; + usage (binary_name); + } + + if (create_sock()) + { + while (optind < argc) { + const int status = sock->send (argv[optind], strlen (argv[optind]) + 1, NULL); + if (cpgm::PGM_IO_STATUS_NORMAL != status) { + std::cerr << "pgm_send() failed.." << std::endl; + } + optind++; + } + } + +/* cleanup */ + if (sock) { + sock->close (TRUE); + sock = NULL; + } + cpgm::pgm_shutdown(); + return EXIT_SUCCESS; +} + +static +bool +create_sock (void) +{ + struct cpgm::pgm_addrinfo_t* res = NULL; + cpgm::pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + +/* parse network parameter into PGM socket address structure */ + if (!cpgm::pgm_getaddrinfo (network, NULL, &res, &pgm_err)) { + std::cerr << "Parsing network parameter: " << pgm_err->message << std::endl; + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + sock = new ip::pgm::socket(); + + if (udp_encap_port) { + if (!sock->open (sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + std::cerr << "Creating PGM/UDP socket: " << pgm_err->message << std::endl; + goto err_abort; + } + sock->set_option (IPPROTO_PGM, cpgm::PGM_UDP_ENCAP_UCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_UDP_ENCAP_MCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + } else { + if (!sock->open (sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + std::cerr << "Creating PGM/IP socket: " << pgm_err->message << std::endl; + goto err_abort; + } + } + + { +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + sock->set_option (IPPROTO_PGM, cpgm::PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + } + + cpgm::pgm_drop_superuser(); + + { +/* set PGM parameters */ + const int send_only = 1, + ambient_spm = pgm_secs (30), + heartbeat_spm[] = { pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (1300), + pgm_secs (7), + pgm_secs (16), + pgm_secs (25), + pgm_secs (30) }; + + sock->set_option (IPPROTO_PGM, cpgm::PGM_SEND_ONLY, &send_only, sizeof(send_only)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_MTU, &max_tpdu, sizeof(max_tpdu)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_TXW_SQNS, &sqns, sizeof(sqns)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_TXW_MAX_RTE, &max_rte, sizeof(max_rte)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_AMBIENT_SPM, &ambient_spm, sizeof(ambient_spm)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_HEARTBEAT_SPM, &heartbeat_spm, sizeof(heartbeat_spm)); + } + if (use_fec) { + struct cpgm::pgm_fecinfo_t fecinfo; + fecinfo.block_size = rs_n; + fecinfo.proactive_packets = 0; + fecinfo.group_size = rs_k; + fecinfo.ondemand_parity_enabled = TRUE; + fecinfo.var_pktlen_enabled = TRUE; + sock->set_option (IPPROTO_PGM, cpgm::PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); + } + +/* create global session identifier */ + endpoint = new ip::pgm::endpoint (DEFAULT_DATA_DESTINATION_PORT); + +/* assign socket to specified address */ + if (!sock->bind (*endpoint, &pgm_err)) { + std::cerr << "Binding PGM socket: " << pgm_err->message << std::endl; + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + sock->set_option (IPPROTO_PGM, cpgm::PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + cpgm::pgm_freeaddrinfo (res); + + { +/* set IP parameters */ + const int blocking = 0, + multicast_loop = use_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + sock->set_option (IPPROTO_PGM, cpgm::PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_TOS, &dscp, sizeof(dscp)); + sock->set_option (IPPROTO_PGM, cpgm::PGM_NOBLOCK, &blocking, sizeof(blocking)); + } + + if (!sock->connect (&pgm_err)) { + fprintf (stderr, "Connecting PGM socket: %s\n", pgm_err->message); + goto err_abort; + } + + return TRUE; + +err_abort: + if (NULL != sock) { + sock->close (FALSE); + sock = NULL; + } + if (NULL != res) { + cpgm::pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + cpgm::pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/shortcakerecv.c b/3rdparty/openpgm-svn-r1135/pgm/examples/shortcakerecv.c new file mode 100644 index 0000000..bcaec4a --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/shortcakerecv.c @@ -0,0 +1,430 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * ショートケーキ PGM receiver + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#ifndef _WIN32 +# include +#else +# include "getopt.h" +# define snprintf _snprintf +#endif +#include + +#include "async.h" + + +/* globals */ + +static int port = 0; +static const char* network = ""; +static bool use_multicast_loop = FALSE; +static int udp_encap_port = 0; + +static int max_tpdu = 1500; +static int sqns = 100; + +static bool use_fec = FALSE; +static int rs_k = 8; +static int rs_n = 255; + +static pgm_sock_t* sock = NULL; +static async_t* async = NULL; +static bool is_terminated = FALSE; + +#ifndef _WIN32 +static int terminate_pipe[2]; +static void on_signal (int); +#else +static HANDLE terminate_event; +static BOOL on_console_ctrl (DWORD); +#endif +#ifndef _MSC_VER +static void usage (const char*) __attribute__((__noreturn__)); +#else +static void usage (const char*); +#endif + +static bool on_startup (void); +static int on_data (const void*restrict, const size_t, const struct pgm_sockaddr_t*restrict); + + +static void +usage ( + const char* bin + ) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -f : Enable FEC with either proactive or ondemand parity\n"); + fprintf (stderr, " -K : Configure Reed-Solomon code (n, k)\n"); + fprintf (stderr, " -N \n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); + fprintf (stderr, " -i : List available interfaces\n"); + exit (EXIT_SUCCESS); +} + +int +main ( + int argc, + char* argv[] + ) +{ + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + +#if !defined(_WIN32) || defined(CONFIG_TARGET_WINE) + puts ("いちごのショートケーキ"); +#else + _putws (L"いちごのショートケーキ"); +#endif + + if (!pgm_init (&pgm_err)) { + fprintf (stderr, "Unable to start PGM engine: %s\n", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:f:K:N:lih")) != -1) + { + switch (c) { + case 'n': network = optarg; break; + case 's': port = atoi (optarg); break; + case 'p': udp_encap_port = atoi (optarg); break; + case 'f': use_fec = TRUE; break; + case 'K': rs_k = atoi (optarg); break; + case 'N': rs_n = atoi (optarg); break; + case 'l': use_multicast_loop = TRUE; break; + + case 'i': + pgm_if_print_all(); + return EXIT_SUCCESS; + + case 'h': + case '?': usage (binary_name); + } + } + + if (use_fec && ( !rs_n || !rs_k )) { + fprintf (stderr, "Invalid Reed-Solomon parameters RS(%d,%d).\n", rs_n, rs_k); + usage (binary_name); + } + +/* setup signal handlers */ +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif +#ifndef _WIN32 + int e = pipe (terminate_pipe); + assert (0 == e); + signal (SIGINT, on_signal); + signal (SIGTERM, on_signal); +#else + terminate_event = CreateEvent (NULL, TRUE, FALSE, TEXT("TerminateEvent")); + SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); +#endif /* !_WIN32 */ + + if (!on_startup()) { + fprintf (stderr, "Startup failed\n"); + return EXIT_FAILURE; + } + +/* dispatch loop */ +#ifndef _WIN32 + int fds, read_fd = async_get_fd (async); + fd_set readfds; +#else + int n_handles = 2; + HANDLE waitHandles[ 2 ]; + DWORD dwEvents; + WSAEVENT recvEvent; + + recvEvent = async_get_event (async); + + waitHandles[0] = terminate_event; + waitHandles[1] = recvEvent; +#endif /* !_WIN32 */ + puts ("Entering PGM message loop ... "); + do { + char buffer[4096]; + struct pgm_sockaddr_t from; + socklen_t fromlen = sizeof (from); + const ssize_t len = async_recvfrom (async, + buffer, + sizeof(buffer), + &from, + &fromlen); + if (len >= 0) { + on_data (buffer, len, &from); + } else { +#ifndef _WIN32 + fds = MAX(terminate_pipe[0], read_fd) + 1; + FD_ZERO(&readfds); + FD_SET(terminate_pipe[0], &readfds); + FD_SET(read_fd, &readfds); + fds = select (fds, &readfds, NULL, NULL, NULL); +#else + dwEvents = WaitForMultipleObjects (n_handles, waitHandles, FALSE, INFINITE); + switch (dwEvents) { + case WAIT_OBJECT_0+1: ResetEvent (recvEvent); break; + default: break; + } +#endif /* _WIN32 */ + } + } while (!is_terminated); + + puts ("Message loop terminated, cleaning up."); + +/* cleanup */ +#ifndef _WIN32 + close (terminate_pipe[0]); + close (terminate_pipe[1]); +#else + CloseHandle (terminate_event); +#endif /* !_WIN32 */ + + if (async) { + puts ("Destroying asynchronous queue."); + async_destroy (async); + async = NULL; + } + + if (sock) { + puts ("Closing PGM socket."); + pgm_close (sock, TRUE); + sock = NULL; + } + + puts ("PGM engine shutdown."); + pgm_shutdown (); + puts ("finished."); + return EXIT_SUCCESS; +} + +#ifndef _WIN32 +static +void +on_signal ( + int signum + ) +{ + printf ("on_signal (signum:%d)\n", signum); + is_terminated = TRUE; + const char one = '1'; + const size_t writelen = write (terminate_pipe[1], &one, sizeof(one)); + assert (sizeof(one) == writelen); +} +#else +static +BOOL +on_console_ctrl ( + DWORD dwCtrlType + ) +{ + printf ("on_console_ctrl (dwCtrlType:%lu)\n", (unsigned long)dwCtrlType); + is_terminated = TRUE; + SetEvent (terminate_event); + return TRUE; +} +#endif /* !_WIN32 */ + +static +bool +on_startup (void) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + +/* parse network parameter into PGM socket address structure */ + if (!pgm_getaddrinfo (network, NULL, &res, &pgm_err)) { + fprintf (stderr, "Parsing network parameter: %s\n", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + puts ("Create PGM socket."); + if (udp_encap_port) { + if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + fprintf (stderr, "Creating PGM/UDP socket: %s\n", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (sock, IPPROTO_PGM, PGM_UDP_ENCAP_UCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_UDP_ENCAP_MCAST_PORT, &udp_encap_port, sizeof(udp_encap_port)); + } else { + if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + fprintf (stderr, "Creating PGM/IP socket: %s\n", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (sock, IPPROTO_PGM, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int recv_only = 1, + passive = 0, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_secs (2), + nak_rdata_ivl = pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + pgm_setsockopt (sock, IPPROTO_PGM, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_PASSIVE, &passive, sizeof(passive)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_MTU, &max_tpdu, sizeof(max_tpdu)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_RXW_SQNS, &sqns, sizeof(sqns)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + if (use_fec) { + struct pgm_fecinfo_t fecinfo; + fecinfo.block_size = rs_n; + fecinfo.proactive_packets = 0; + fecinfo.group_size = rs_k; + fecinfo.ondemand_parity_enabled = TRUE; + fecinfo.var_pktlen_enabled = TRUE; + pgm_setsockopt (sock, IPPROTO_PGM, PGM_USE_FEC, &fecinfo, sizeof(fecinfo)); + } + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = port ? port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + fprintf (stderr, "Creating GSI: %s\n", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + fprintf (stderr, "Binding PGM socket: %s\n", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (sock, IPPROTO_PGM, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int nonblocking = 1, + multicast_loop = use_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (sock, IPPROTO_PGM, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (sock, IPPROTO_PGM, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (sock, IPPROTO_PGM, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); + + if (!pgm_connect (sock, &pgm_err)) { + fprintf (stderr, "Connecting PGM socket: %s\n", pgm_err->message); + goto err_abort; + } + +/* wrap bound socket in asynchronous queue */ + if (0 != async_create (&async, sock)) { + fprintf (stderr, "Creating asynchronous queue failed: %s\n", strerror(errno)); + goto err_abort; + } + + puts ("Startup complete."); + return TRUE; + +err_abort: + if (NULL != sock) { + pgm_close (sock, FALSE); + sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +static +int +on_data ( + const void* restrict data, + const size_t len, + const struct pgm_sockaddr_t* restrict from + ) +{ +/* protect against non-null terminated strings */ + char buf[1024], tsi[PGM_TSISTRLEN]; + const size_t buflen = MIN(sizeof(buf) - 1, len); + strncpy (buf, (const char*)data, buflen); + buf[buflen] = '\0'; + pgm_tsi_print_r (&from->sa_addr, tsi, sizeof(tsi)); +#ifndef _MSC_VER + printf ("\"%s\" (%zu bytes from %s)\n", + buf, len, tsi); +#else +/* Microsoft CRT will crash on %zu */ + printf ("\"%s\" (%u bytes from %s)\n", + buf, (unsigned)len, tsi); +#endif + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/examples/snonblocksyncrecv.c b/3rdparty/openpgm-svn-r1135/pgm/examples/snonblocksyncrecv.c new file mode 100644 index 0000000..4d72b71 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/examples/snonblocksyncrecv.c @@ -0,0 +1,435 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Simple PGM receiver: select based non-blocking synchronous receiver. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef G_OS_UNIX +# include +# include +# include +# include +#endif +#include + +/* example dependencies */ +#include +#include + + +/* typedefs */ + +/* globals */ + +static int g_port = 0; +static const char* g_network = ""; +static gboolean g_multicast_loop = FALSE; +static int g_udp_encap_port = 0; + +static int g_max_tpdu = 1500; +static int g_sqns = 100; + +static pgm_sock_t* g_sock = NULL; +static gboolean g_quit; + +#ifdef G_OS_UNIX +static int g_quit_pipe[2]; +static void on_signal (int); +#else +static HANDLE g_quit_event; +static BOOL on_console_ctrl (DWORD); +#endif + +static gboolean on_startup (void); + +static int on_data (gconstpointer, size_t, struct pgm_sockaddr_t*); + + +G_GNUC_NORETURN static +void +usage ( + const char* bin + ) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + fprintf (stderr, " -p : Encapsulate PGM in UDP on IP port\n"); + fprintf (stderr, " -l : Enable multicast loopback and address sharing\n"); + exit (1); +} + +int +main ( + int argc, + char* argv[] + ) +{ + int e; + pgm_error_t* pgm_err = NULL; + + setlocale (LC_ALL, ""); + + log_init (); + g_message ("snonblocksyncrecv"); + + if (!pgm_init (&pgm_err)) { + g_error ("Unable to start PGM engine: %s", pgm_err->message); + pgm_error_free (pgm_err); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:p:lh")) != -1) + { + switch (c) { + case 'n': g_network = optarg; break; + case 's': g_port = atoi (optarg); break; + case 'p': g_udp_encap_port = atoi (optarg); break; + case 'l': g_multicast_loop = TRUE; break; + + case 'h': + case '?': usage (binary_name); + } + } + + g_quit = FALSE; + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); +#ifdef SIGHUP + signal (SIGHUP, SIG_IGN); +#endif +#ifdef G_OS_UNIX + e = pipe (g_quit_pipe); + g_assert (0 == e); + signal (SIGINT, on_signal); + signal (SIGTERM, on_signal); +#else + g_quit_event = CreateEvent (NULL, TRUE, FALSE, TEXT("QuitEvent")); + SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE); +#endif /* !G_OS_UNIX */ + + if (!on_startup()) { + g_error ("startup failed"); + exit(1); + } + +/* dispatch loop */ +#ifdef G_OS_UNIX + int fds; + fd_set readfds; +#else + int n_handles = 3; + HANDLE waitHandles[ n_handles ]; + DWORD dwTimeout, dwEvents; + WSAEVENT recvEvent, pendingEvent; + + recvEvent = WSACreateEvent (); + WSAEventSelect (pgm_transport_get_recv_fd (g_transport), recvEvent, FD_READ); + pendingEvent = WSACreateEvent (); + WSAEventSelect (pgm_transport_get_pending_fd (g_transport), pendingEvent, FD_READ); + + waitHandles[0] = g_quit_event; + waitHandles[1] = recvEvent; + waitHandles[2] = pendingEvent; +#endif /* !G_OS_UNIX */ + g_message ("entering PGM message loop ... "); + do { + struct timeval tv; + char buffer[4096]; + size_t len; + struct pgm_sockaddr_t from; + socklen_t fromlen = sizeof(from); + const int status = pgm_recvfrom (g_sock, + buffer, + sizeof(buffer), + 0, + &len, + &from, + &fromlen, + &pgm_err); + switch (status) { + case PGM_IO_STATUS_NORMAL: + on_data (buffer, len, &from); + break; + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (g_sock, IPPROTO_PGM, PGM_TIME_REMAIN, &tv, &optlen); + } + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (g_sock, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &optlen); + } +/* fall through */ + case PGM_IO_STATUS_WOULD_BLOCK: +/* select for next event */ +block: +#ifdef G_OS_UNIX + fds = g_quit_pipe[0] + 1; + FD_ZERO(&readfds); + FD_SET(g_quit_pipe[0], &readfds); + pgm_select_info (g_sock, &readfds, NULL, &fds); + fds = select (fds, &readfds, NULL, NULL, PGM_IO_STATUS_WOULD_BLOCK == status ? NULL : &tv); +#else + dwTimeout = PGM_IO_STATUS_WOULD_BLOCK == status ? INFINITE : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + dwEvents = WaitForMultipleObjects (n_handles, waitHandles, FALSE, dwTimeout); + switch (dwEvents) { + case WAIT_OBJECT_0+1: WSAResetEvent (recvEvent); break; + case WAIT_OBJECT_0+2: WSAResetEvent (pendingEvent); break; + default: break; + } +#endif /* !G_OS_UNIX */ + break; + + default: + if (pgm_err) { + g_warning ("%s", pgm_err->message); + pgm_error_free (pgm_err); + pgm_err = NULL; + } + if (PGM_IO_STATUS_ERROR == status) + break; + } + } while (!g_quit); + + g_message ("message loop terminated, cleaning up."); + +/* cleanup */ +#ifdef G_OS_UNIX + close (g_quit_pipe[0]); + close (g_quit_pipe[1]); +#else + WSACloseEvent (recvEvent); + WSACloseEvent (pendingEvent); + CloseHandle (g_quit_event); +#endif /* !G_OS_UNIX */ + + if (g_sock) { + g_message ("closing PGM socket."); + pgm_close (g_sock, TRUE); + g_sock = NULL; + } + + g_message ("PGM engine shutdown."); + pgm_shutdown (); + g_message ("finished."); + return EXIT_SUCCESS; +} + +#ifdef G_OS_UNIX +static +void +on_signal ( + int signum + ) +{ + g_message ("on_signal (signum:%d)", signum); + g_quit = TRUE; + const char one = '1'; + const size_t writelen = write (g_quit_pipe[1], &one, sizeof(one)); + g_assert (sizeof(one) == writelen); +} +#else +static +BOOL +on_console_ctrl ( + DWORD dwCtrlType + ) +{ + g_message ("on_console_ctrl (dwCtrlType:%lu)", (unsigned long)dwCtrlType); + SetEvent (g_quit_event); + return TRUE; +} +#endif /* !G_OS_UNIX */ + +static +gboolean +on_startup (void) +{ + struct pgm_addrinfo_t* res = NULL; + pgm_error_t* pgm_err = NULL; + sa_family_t sa_family = AF_UNSPEC; + + g_message ("startup."); + +/* parse network parameter into transport address structure */ + if (!pgm_getaddrinfo (g_network, NULL, &res, &pgm_err)) { + g_error ("parsing network parameter: %s", pgm_err->message); + goto err_abort; + } + + sa_family = res->ai_send_addrs[0].gsr_group.ss_family; + + if (g_udp_encap_port) { + g_message ("create PGM/UDP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_UDP_ENCAP_UCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_UDP_ENCAP_MCAST_PORT, &g_udp_encap_port, sizeof(g_udp_encap_port)); + } else { + g_message ("create PGM/IP socket."); + if (!pgm_socket (&g_sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + g_error ("socket: %s", pgm_err->message); + goto err_abort; + } + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist)); + + pgm_drop_superuser(); + +/* set PGM parameters */ + const int recv_only = 1, + passive = 0, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_secs (2), + nak_rdata_ivl = pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_PASSIVE, &passive, sizeof(passive)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MTU, &g_max_tpdu, sizeof(g_max_tpdu)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_RXW_SQNS, &g_sqns, sizeof(g_sqns)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = g_port ? g_port : DEFAULT_DATA_DESTINATION_PORT; + addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + g_error ("creating GSI: %s", pgm_err->message); + goto err_abort; + } + +/* assign socket to specified address */ + struct pgm_interface_req_t if_req; + memset (&if_req, 0, sizeof(if_req)); + if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface; + if_req.ir_scope_id = 0; + if (AF_INET6 == sa_family) { + struct sockaddr_in6 sa6; + memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6)); + if_req.ir_scope_id = sa6.sin6_scope_id; + } + if (!pgm_bind3 (g_sock, + &addr, sizeof(addr), + &if_req, sizeof(if_req), /* tx interface */ + &if_req, sizeof(if_req), /* rx interface */ + &pgm_err)) + { + g_error ("binding PGM socket: %s", pgm_err->message); + goto err_abort; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req)); + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int nonblocking = 1, + multicast_loop = g_multicast_loop ? 1 : 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); + if (AF_INET6 != sa_family) + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_TOS, &dscp, sizeof(dscp)); + pgm_setsockopt (g_sock, IPPROTO_PGM, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); + + if (!pgm_connect (g_sock, &pgm_err)) { + g_error ("connecting PGM socket: %s", pgm_err->message); + goto err_abort; + } + + g_message ("startup complete."); + return TRUE; + +err_abort: + if (NULL != g_sock) { + pgm_close (g_sock, FALSE); + g_sock = NULL; + } + if (NULL != res) { + pgm_freeaddrinfo (res); + res = NULL; + } + if (NULL != pgm_err) { + pgm_error_free (pgm_err); + pgm_err = NULL; + } + return FALSE; +} + +static +int +on_data ( + gconstpointer data, + size_t len, + struct pgm_sockaddr_t* from + ) +{ +/* protect against non-null terminated strings */ + char buf[1024], tsi[PGM_TSISTRLEN]; + const size_t buflen = MIN(sizeof(buf) - 1, len); + strncpy (buf, (const char*)data, buflen); + buf[buflen] = '\0'; + pgm_tsi_print_r (&from->sa_addr, tsi, sizeof(tsi)); + + g_message ("\"%s\" (%u bytes from %s)", + buf, + (unsigned)len, + tsi); + + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/fec-block.txt b/3rdparty/openpgm-svn-r1135/pgm/fec-block.txt new file mode 100644 index 0000000..8278fb6 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/fec-block.txt @@ -0,0 +1,66 @@ +from rfc5052 +------------ + + +step one: + + Input: + + B -- Maximum Source Block Length, i.e., the maximum number of source + symbols per source block + + L -- Transfer Length in octets + + E -- Encoding Symbol Length in octets + + Output: + + T -- the number of source symbols in the object. + + N -- the number of source blocks into which the object shall be + partitioned. + + Algorithm: + + 1. The number of source symbols in the transport object is computed + as T = ceil[L/E]. + + 2. The transport object shall be partitioned into N = ceil[T/B] + source blocks. + + +B = maximum TPDU - IP header ( - UDP header ) - PGM header +L = APDU length (pgm_transport_send length parameter). +E = 1 (fixed). + +T = ceil( L / E ) = ceil( L / 1 ) = L +N = ceil( T / B ) = ceil( L / B ) + +step two: + + Input: + + T -- the number of source symbols in the object. + + N -- the number of source blocks into which the object is + partitioned. + + Output: + + I -- the number of larger source blocks. + + A_large -- the length of each of the larger source blocks in + symbols. + + A_small -- the length of each of the smaller source blocks in + symbols. + + Algorithm: + + 1. A_large = ceil[T/N] + + 2. A_small = floor[T/N] + + 3. I = T - A_small * N + + diff --git a/3rdparty/openpgm-svn-r1135/pgm/fec.txt b/3rdparty/openpgm-svn-r1135/pgm/fec.txt new file mode 100644 index 0000000..b83590d --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/fec.txt @@ -0,0 +1,77 @@ +pkt:k=1 + +non-parity + +rs eqn: + +n = 255 +k = 2t +255 = 2k +k = 128 +=> 2t = 128 + +-------------------------------------------------------------------------------- + +1456, 1442 + +pkt:k=2 ( 2 data packets ) + +1 packet loss: + +pkt:h=1 +pkt:n=3 + +rs eqn: + +n = 255 +# reed-solomon codes = tsdu / n +k = 2 4 6 ... 254 +=> 2t = 1 2 3 127 (k/2) + +255 = k + (k/2) +510 = 2k + k +510 = 3k +170 = k +=> 2t = 85 + +2 packet loss: + +pkt:h=2 pkts +pkt:n=4 pkts + +-------------------------------------------------------------------------------- + +pkt:k=4 ( 4 data packets ) + +1 packet loss: + +255 = k + (k/4) +k = 51 +2t = 13 (rounded up) + +-------------------------------------------------------------------------------- + +pkt:k=8 ( 8 data packets ) + +1 packet loss: + +255 = k + (k/8) +k = 29 +2t = 4 (rounded up) + +-------------------------------------------------------------------------------- + +pkt:k=16 ( 16 data packets ) + +1 packet loss: + +255 = k + (k/16) +k = 15 +2t = 1 (invalid?) + +2 packet loss: + +255 = k + (2k/16) +k = 29 +2t = 4 + diff --git a/3rdparty/openpgm-svn-r1135/pgm/galois_generator.pl b/3rdparty/openpgm-svn-r1135/pgm/galois_generator.pl new file mode 100755 index 0000000..b3f531d --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/galois_generator.pl @@ -0,0 +1,139 @@ +#!/usr/bin/perl +# +# Galois field table generator. +# +# Copyright (c) 2006-2010 Miru Limited. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +use strict; + +my $GF_ELEMENT_BYTES = 1; +my $GF_ELEMENT_BITS = 8 * $GF_ELEMENT_BYTES; +my $GF_NO_ELEMENTS = 1 << $GF_ELEMENT_BITS; +my $GF_MAX = $GF_NO_ELEMENTS - 1; + +my $GF_GENERATOR = 0x11d; + +my @gflog; +my @gfantilog; + +my $j = 1; + +for (my $i = 0; $i < $GF_MAX; $i++) +{ + $gflog[ $j ] = $i; + $gfantilog[ $i ] = $j; + + $j <<= 1; + if ($j & $GF_NO_ELEMENTS) { + $j ^= $GF_GENERATOR; + } +} + +$gflog[ 0 ] = $GF_MAX; +$gfantilog[ $GF_MAX ] = 0; + +print< + + +/* globals */ + +const pgm_gf8_t pgm_gflog[PGM_GF_NO_ELEMENTS] = +{ +MOO + +# print out y = log₂(x) table +for (my $i = 0; $i < $GF_NO_ELEMENTS; $i++) +{ + print "\t" if ($i % 8 == 0); + print sprintf("0x%2.2x", $gflog[ $i ]); + print ',' unless ($i == $GF_MAX); + print ( (($i % 8) == 7) ? "\n" : ' ' ); +} + +print<= $GF_MAX) ? $gfantilog[ $sum - $GF_MAX ] : $gfantilog[ $sum ]; +} + +# print out multiplication table z = x • y +for (my $i = 0; $i < $GF_NO_ELEMENTS; $i++) +{ + for (my $j = 0; $j < $GF_NO_ELEMENTS; $j++) + { + print "\t" if ($j % 8 == 0); + print sprintf("0x%2.2x", gfmul( $i, $j )); + print ',' unless ($i == $GF_MAX && $j == $GF_MAX); + print ( (($j % 8) == 7) ? "\n" : ' ' ); + } +} + +print<) { + chomp; + if (/^(Function|File) '(.+)'/) { + $type = $1; + $target = $2; + } elsif (/^Lines executed:(\d+\.\d+)% of (\d+)/) { +# print "$type,$target,$1,$2\n"; + if ($type cmp 'File') { + $files{$target} = $1; + } else { + $functions{$target} = $1; + } + } +} + +#@sorted = sort { $files{$a} <=> $files{$b} } keys %files; +#foreach $name (@sorted) +#{ +# print "$name:$files{$name}\n"; +#} +@sorted = sort { $functions{$a} <=> $functions{$b} } keys %functions; +$total = 0; +$count = 0; +foreach $name (@sorted) +{ + next if $name =~ m#^/#; + next if $name =~ m#.+\.h$#; + next if $name =~ m#_unittest\.c$#; + print sprintf("%20s: %3.1f%%\n", $name, $functions{$name}); + $total += $functions{$name}; + $count++; +} +$total /= $count; +print "\n TOTAL: ~" . int($total) . "%\n\n"; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/gcov-seed.sh b/3rdparty/openpgm-svn-r1135/pgm/gcov-seed.sh new file mode 100755 index 0000000..853fbe6 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/gcov-seed.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +./ref/debug/async_unittest +./ref/debug/checksum_unittest +./ref/debug/getifaddrs_unittest +./ref/debug/getnodeaddr_unittest +./ref/debug/gsi_unittest +./ref/debug/http_unittest +./ref/debug/if_unittest +./ref/debug/indextoaddr_unittest +./ref/debug/inet_network_unittest +./ref/debug/md5_unittest +./ref/debug/net_unittest +./ref/debug/packet_unittest +./ref/debug/pgmMIB_unittest +./ref/debug/pgm_unittest +./ref/debug/rate_control_unittest +./ref/debug/receiver_unittest +./ref/debug/recv_unittest +./ref/debug/reed_solomon_unittest +./ref/debug/rxwi_unittest +./ref/debug/signal_unittest +./ref/debug/snmp_unittest +./ref/debug/source_unittest +./ref/debug/timer_unittest +./ref/debug/time_unittest +user=`id -nu` +group=`id -ng` +sudo execcap 'cap_net_raw=ep' /sbin/sucap $user $group ./ref/debug/transport_unittest +sudo find ref/debug/ -user 0 -exec chown $user:$group {} \; +./ref/debug/tsi_unittest +./ref/debug/txwi_unittest + diff --git a/3rdparty/openpgm-svn-r1135/pgm/gcov.sh b/3rdparty/openpgm-svn-r1135/pgm/gcov.sh new file mode 100755 index 0000000..ca17d62 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/gcov.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +gcov -fno ref/debug async_unittest.c +gcov -fno ref/debug checksum_unittest.c +gcov -fno ref/debug getifaddrs_unittest.c +gcov -fno ref/debug getnodeaddr_unittest.c +gcov -fno ref/debug gsi_unittest.c +gcov -fno ref/debug http_unittest.c +gcov -fno ref/debug if_unittest.c +gcov -fno ref/debug indextoaddr_unittest.c +gcov -fno ref/debug inet_network_unittest.c +gcov -fno ref/debug md5_unittest.c +gcov -fno ref/debug net_unittest.c +gcov -fno ref/debug packet_unittest.c +gcov -fno ref/debug pgmMIB_unittest.c +gcov -fno ref/debug pgm_unittest.c +gcov -fno ref/debug rate_control_unittest.c +gcov -fno ref/debug receiver_unittest.c +gcov -fno ref/debug recv_unittest.c +gcov -fno ref/debug reed_solomon_unittest.c +gcov -fno ref/debug rxwi_unittest.c +gcov -fno ref/debug signal_unittest.c +gcov -fno ref/debug snmp_unittest.c +gcov -fno ref/debug source_unittest.c +gcov -fno ref/debug timer_unittest.c +gcov -fno ref/debug time_unittest.c +gcov -fno ref/debug tsi_unittest.c +gcov -fno ref/debug txwi_unittest.c + diff --git a/3rdparty/openpgm-svn-r1135/pgm/getifaddrs.c b/3rdparty/openpgm-svn-r1135/pgm/getifaddrs.c new file mode 100644 index 0000000..9db1d86 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/getifaddrs.c @@ -0,0 +1,840 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable getifaddrs implementation. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#ifdef CONFIG_HAVE_GETIFADDRS +# include +# include +#endif +#if defined( sun ) +# include +#endif +#if defined( _WIN32 ) +# include +# include +#endif +#include +#include + + +//#define GETIFADDRS_DEBUG + +/* locals */ +struct _pgm_ifaddrs_t +{ + struct pgm_ifaddrs_t _ifa; + char _name[IF_NAMESIZE]; + struct sockaddr_storage _addr; + struct sockaddr_storage _netmask; +}; + +/* Number of attempts to try enumerating the interface list */ +#define MAX_TRIES 3 +#define DEFAULT_BUFFER_SIZE 4096 + +/* returns TRUE on success setting ifap to a linked list of system interfaces, + * returns FALSE on failure and sets error appropriately. + */ + +#ifdef SIOCGLIFCONF +static +bool +_pgm_getlifaddrs ( + struct pgm_ifaddrs_t** restrict ifap, + pgm_error_t** restrict error + ) +{ + const int sock = socket (AF_INET, SOCK_DGRAM, 0); + if (-1 == sock) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("Opening IPv4 datagram socket: %s"), + strerror (errno)); + return FALSE; + } + +/* process IPv6 interfaces */ + const int sock6 = socket (AF_INET6, SOCK_DGRAM, 0); + if (-1 == sock6) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("Opening IPv6 datagram socket: %s"), + strerror (errno)); + close (sock); + return FALSE; + } + + struct lifnum lifn; +again: + lifn.lifn_family = AF_INET; + lifn.lifn_flags = 0; + if (-1 == ioctl (sock, SIOCGLIFNUM, &lifn)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("SIOCGLIFNUM failed on IPv4 socket: %s"), + strerror (errno)); + close (sock); + close (sock6); + return FALSE; + } + unsigned if_count = lifn.lifn_count; + pgm_debug ("ioctl(AF_INET, SIOCGLIFNUM) = %d", lifn.lifn_count); + +/* nb: Sun and Apple code likes to pad the interface count here in case interfaces + * are coming up between calls, + */ + lifn.lifn_count += 4; + +/* process all interfaces with family-agnostic ioctl, unfortunately it still + * must be called on each family socket despite what if_tcp(7P) says. + */ + struct lifconf lifc, lifc6; + lifc.lifc_family = AF_INET; + lifc.lifc_flags = 0; + lifc.lifc_len = lifn.lifn_count * sizeof(struct lifreq); + lifc.lifc_buf = alloca (lifc.lifc_len); + if (-1 == ioctl (sock, SIOCGLIFCONF, &lifc)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("SIOCGLIFCONF failed on IPv4 socket: %s"), + strerror (errno)); + close (sock); + close (sock6); + return FALSE; + } + pgm_debug ("ioctl(AF_INET, SIOCGLIFCONF) = %d (%d)", lifc.lifc_len, (int)(lifc.lifc_len / sizeof(struct lifreq))); + +/* repeat everything for IPv6 */ + lifn.lifn_family = AF_INET6; + lifn.lifn_flags = 0; + if (-1 == ioctl (sock6, SIOCGLIFNUM, &lifn)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("SIOCGLIFNUM failed on IPv6 socket: %s"), + strerror (errno)); + close (sock); + close (sock6); + return FALSE; + } + if_count += lifn.lifn_count; + pgm_debug ("ioctl(AF_INET6, SIOCGLIFNUM) = %d", lifn.lifn_count); + + lifn.lifn_count += 4; + + lifc6.lifc_family = AF_INET6; + lifc6.lifc_flags = 0; + lifc6.lifc_len = lifn.lifn_count * sizeof(struct lifreq); + lifc6.lifc_buf = alloca (lifc6.lifc_len); + if (-1 == ioctl (sock6, SIOCGLIFCONF, &lifc6)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("SIOCGLIFCONF failed on IPv6 socket: %s"), + strerror (errno)); + close (sock); + close (sock6); + return FALSE; + } + pgm_debug ("ioctl(AF_INET6, SIOCGLIFCONF) = %d (%d)", lifc6.lifc_len, (int)(lifc6.lifc_len / sizeof(struct lifreq))); + + unsigned nlifr = (lifc.lifc_len + lifc6.lifc_len) / sizeof(struct lifreq); + if (nlifr > if_count) + goto again; + +/* alloc a contiguous block for entire list */ + struct _pgm_ifaddrs_t* ifa = calloc (nlifr, sizeof (struct _pgm_ifaddrs_t)); + pgm_assert (NULL != ifa); + + struct _pgm_ifaddrs_t* ift = ifa; + struct lifreq* lifr = lifc.lifc_req; + struct lifreq* lifr_end = (struct lifreq *)&lifc.lifc_buf[lifc.lifc_len]; + + pgm_assert (IF_NAMESIZE >= LIFNAMSIZ); + + while (lifr < lifr_end) + { +/* name */ + pgm_debug ("AF_INET/name: %s", lifr->lifr_name ? lifr->lifr_name : "(null)"); + ift->_ifa.ifa_name = ift->_name; + strncpy (ift->_ifa.ifa_name, lifr->lifr_name, LIFNAMSIZ); + ift->_ifa.ifa_name[LIFNAMSIZ - 1] = 0; + +/* flags */ + if (-1 != ioctl (sock, SIOCGLIFFLAGS, lifr)) { + ift->_ifa.ifa_flags = lifr->lifr_flags; + } else { + pgm_warn (_("SIOCGLIFFLAGS failed on interface %s%s%s"), + lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); + } + +/* address */ + if (-1 != ioctl (sock, SIOCGLIFADDR, lifr)) { + ift->_ifa.ifa_addr = (void*)&ift->_addr; + memcpy (ift->_ifa.ifa_addr, &lifr->lifr_addr, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_addr)); + } else { + pgm_warn (_("SIOCGLIFADDR failed on interface %s%s%s"), + lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); + } + +/* netmask */ + if (-1 != ioctl (sock, SIOCGLIFNETMASK, lifr)) { + ift->_ifa.ifa_netmask = (void*)&ift->_netmask; +# ifdef CONFIG_HAVE_IFR_NETMASK + memcpy (ift->_ifa.ifa_netmask, &lifr->lifr_netmask, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_netmask)); +# else + memcpy (ift->_ifa.ifa_netmask, &lifr->lifr_addr, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_addr)); +# endif + } else { + pgm_warn (_("SIOCGLIFNETMASK failed on interface %s%s%s"), + lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); + } + + ++lifr; + if (lifr < lifr_end) { + ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); + ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); + } + } + +/* repeat everything for IPv6 */ + lifr = lifc6.lifc_req; + lifr_end = (struct lifreq *)&lifc6.lifc_buf[lifc6.lifc_len]; + + while (lifr < lifr_end) + { + if (ift != ifa) { + ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); + ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); + } + +/* name */ + pgm_debug ("AF_INET6/name: %s", lifr->lifr_name ? lifr->lifr_name : "(null)"); + ift->_ifa.ifa_name = ift->_name; + strncpy (ift->_ifa.ifa_name, lifr->lifr_name, sizeof(lifr->lifr_name)); + ift->_ifa.ifa_name[sizeof(lifr->lifr_name) - 1] = 0; + +/* flags */ + if (-1 != ioctl (sock6, SIOCGLIFFLAGS, lifr)) { + ift->_ifa.ifa_flags = lifr->lifr_flags; + } else { + pgm_warn (_("SIOCGLIFFLAGS failed on interface %s%s%s"), + lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); + } + +/* address */ + if (-1 != ioctl (sock6, SIOCGLIFADDR, lifr)) { + ift->_ifa.ifa_addr = (void*)&ift->_addr; + memcpy (ift->_ifa.ifa_addr, &lifr->lifr_addr, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_addr)); + } else { + pgm_warn (_("SIOCGLIFADDR failed on interface %s%s%s"), + lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); + } + +/* netmask */ + if (ioctl (sock6, SIOCGLIFNETMASK, lifr) != -1) { + ift->_ifa.ifa_netmask = (void*)&ift->_netmask; +# ifdef CONFIG_HAVE_IFR_NETMASK + memcpy (ift->_ifa.ifa_netmask, &lifr->lifr_netmask, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_netmask)); +# else + memcpy (ift->_ifa.ifa_netmask, &lifr->lifr_addr, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_addr)); +# endif + } else { + pgm_warn (_("SIOCGLIFNETMASK failed on interface %s%s%s"), + lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); + } + + ++lifr; + } + + if (-1 == close (sock6)) + pgm_warn (_("Closing IPv6 socket failed: %s"), strerror(errno)); + if (-1 == close (sock)) + pgm_warn (_("Closing IPv4 socket failed: %s"), strerror(errno)); + + *ifap = (struct pgm_ifaddrs_t*)ifa; + return TRUE; +} +#endif /* SIOCGLIFCONF */ + +#ifdef SIOCGIFCONF +static +bool +_pgm_getifaddrs ( + struct pgm_ifaddrs_t** restrict ifap, + pgm_error_t** restrict error + ) +{ + const int sock = socket (AF_INET, SOCK_DGRAM, 0); + if (-1 == sock) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("Opening IPv4 datagram socket: %s"), + strerror (errno)); + return FALSE; + } + +/* process IPv4 interfaces */ + char buf[ DEFAULT_BUFFER_SIZE ]; + struct ifconf ifc; + ifc.ifc_buf = buf; + ifc.ifc_len = sizeof(buf); + if (-1 == ioctl (sock, SIOCGIFCONF, &ifc)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("SIOCGIFCONF failed on IPv4 socket: %s"), + strerror (errno)); + close (sock); + return FALSE; + } + int if_count = ifc.ifc_len / sizeof(struct ifreq); + +# ifdef CONFIG_HAVE_IPV6_SIOCGIFADDR +/* process IPv6 interfaces */ + const int sock6 = socket (AF_INET6, SOCK_DGRAM, 0); + if (-1 == sock6) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("Opening IPv6 datagram socket: %s"), + strerror (errno)); + close (sock); + return FALSE; + } + + char buf6[1024]; + struct ifconf ifc6; + ifc6.ifc_buf = buf6; + ifc6.ifc_len = sizeof(buf6); + if (-1 == ioctl (sock6, SIOCGIFCONF, &ifc6)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("SIOCGIFCONF failed on IPv6 socket: %s"), + strerror (errno)); + close (sock); + close (sock6); + return FALSE; + } + if_count += ifc6.ifc_len / sizeof(struct ifreq); +# endif /* CONFIG_HAVE_IPV6_SIOCGIFADDR */ + +/* alloc a contiguous block for entire list */ + struct _pgm_ifaddrs_t* ifa = pgm_new0 (struct _pgm_ifaddrs_t, if_count); + struct _pgm_ifaddrs_t* ift = ifa; + struct ifreq *ifr = ifc.ifc_req; + struct ifreq *ifr_end = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len]; + + pgm_assert (IF_NAMESIZE >= sizeof(ifr->ifr_name)); + + while (ifr < ifr_end) + { +/* name */ + pgm_debug ("AF_INET/name:%s", ifr->ifr_name ? ifr->ifr_name : "(null)"); + ift->_ifa.ifa_name = ift->_name; + strncpy (ift->_ifa.ifa_name, ifr->ifr_name, sizeof(ifr->ifr_name)); + ift->_ifa.ifa_name[sizeof(ifr->ifr_name) - 1] = 0; + +/* flags */ + if (-1 != ioctl (sock, SIOCGIFFLAGS, ifr)) { + ift->_ifa.ifa_flags = ifr->ifr_flags; + } else { + pgm_warn (_("SIOCGIFFLAGS failed on interface %s%s%s"), + ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); + } + +/* address */ + if (-1 != ioctl (sock, SIOCGIFADDR, ifr)) { + ift->_ifa.ifa_addr = (void*)&ift->_addr; + memcpy (ift->_ifa.ifa_addr, &ifr->ifr_addr, pgm_sockaddr_len(&ifr->ifr_addr)); + } else { + pgm_warn (_("SIOCGIFADDR failed on interface %s%s%s"), + ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); + } + +/* netmask */ + if (-1 != ioctl (sock, SIOCGIFNETMASK, ifr)) { + ift->_ifa.ifa_netmask = (void*)&ift->_netmask; +# ifdef CONFIG_HAVE_IFR_NETMASK + memcpy (ift->_ifa.ifa_netmask, &ifr->ifr_netmask, pgm_sockaddr_len(&ifr->ifr_netmask)); +# else + memcpy (ift->_ifa.ifa_netmask, &ifr->ifr_addr, pgm_sockaddr_len(&ifr->ifr_addr)); +# endif + } else { + pgm_warn (_("SIOCGIFNETMASK failed on interface %s%s%s"), + ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); + } + + ++ifr; + if (ifr < ifr_end) { + ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); + ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); + } + } + +# ifdef CONFIG_HAVE_IPV6_SIOCGIFADDR +/* repeat everything for IPv6 */ + ifr = ifc6.ifc_req; + ifr_end = (struct ifreq *)&ifc6.ifc_buf[ifc6.ifc_len]; + + while (ifr < ifr_end) + { + if (ift != ifa) { + ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); + ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); + } + +/* name */ + pgm_debug ("AF_INET6/name:%s", ifr->ifr_name ? ifr->ifr_name : "(null)"); + ift->_ifa.ifa_name = ift->_name; + strncpy (ift->_ifa.ifa_name, ifr->ifr_name, sizeof(ifr->ifr_name)); + ift->_ifa.ifa_name[sizeof(ifr->ifr_name) - 1] = 0; + +/* flags */ + if (-1 != ioctl (sock6, SIOCGIFFLAGS, ifr)) { + ift->_ifa.ifa_flags = ifr->ifr_flags; + } else { + pgm_warn (_("SIOCGIFFLAGS failed on interface %s%s%s"), + ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); + } + +/* address, note this does not work on Linux as struct ifreq is too small for an IPv6 address */ + if (-1 != ioctl (sock6, SIOCGIFADDR, ifr)) { + ift->_ifa.ifa_addr = (void*)&ift->_addr; + memcpy (ift->_ifa.ifa_addr, &ifr->ifr_addr, pgm_sockaddr_len(&ifr->ifr_addr)); + } else { + pgm_warn (_("SIOCGIFADDR failed on interface %s%s%s"), + ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); + } + +/* netmask */ + if (-1 != ioctl (sock6, SIOCGIFNETMASK, ifr)) { + ift->_ifa.ifa_netmask = (void*)&ift->_netmask; +# ifdef CONFIG_HAVE_IFR_NETMASK + memcpy (ift->_ifa.ifa_netmask, &ifr->ifr_netmask, pgm_sockaddr_len(&ifr->ifr_netmask)); +# else + memcpy (ift->_ifa.ifa_netmask, &ifr->ifr_addr, pgm_sockaddr_len(&ifr->ifr_addr)); +# endif + } else { + pgm_warn (_("SIOCGIFNETMASK failed on interface %s%s%s"), + ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); + } + + ++ifr; + } + + if (-1 == close (sock6)) + pgm_warn (_("Closing IPv6 socket failed: %s"), strerror(errno)); +# endif /* CONFIG_HAVE_IPV6_SIOCGIFADDR */ + + if (-1 == close (sock)) + pgm_warn (_("Closing IPv4 socket failed: %s"), strerror(errno)); + + *ifap = (struct pgm_ifaddrs_t*)ifa; + return TRUE; +} +#endif /* SIOCLIFCONF */ + +#if defined(_WIN32) +static inline +void* +_pgm_heap_alloc ( + const size_t n_bytes + ) +{ +# ifdef CONFIG_USE_HEAPALLOC +/* Does not appear very safe with re-entrant calls on XP */ + return HeapAlloc (GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, n_bytes); +# else + return pgm_malloc (n_bytes); +# endif +} + +static inline +void +_pgm_heap_free ( + void* mem + ) +{ +# ifdef CONFIG_USE_HEAPALLOC + HeapFree (GetProcessHeap(), 0, mem); +# else + pgm_free (mem); +# endif +} + +/* NB: IP_ADAPTER_INFO size varies size due to sizeof (time_t), the API assumes + * 4-byte datatype whilst compiler uses an 8-byte datatype. Size can be forced + * with -D_USE_32BIT_TIME_T with side effects to everything else. + */ + +static +bool +_pgm_getadaptersinfo ( + struct pgm_ifaddrs_t** restrict ifap, + pgm_error_t** restrict error + ) +{ + DWORD dwRet; + ULONG ulOutBufLen = DEFAULT_BUFFER_SIZE; + PIP_ADAPTER_INFO pAdapterInfo = NULL; + PIP_ADAPTER_INFO pAdapter = NULL; + +/* loop to handle interfaces coming online causing a buffer overflow + * between first call to list buffer length and second call to enumerate. + */ + for (unsigned i = MAX_TRIES; i; i--) + { + pgm_debug ("IP_ADAPTER_INFO buffer length %lu bytes.", ulOutBufLen); + pAdapterInfo = (IP_ADAPTER_INFO*)_pgm_heap_alloc (ulOutBufLen); + dwRet = GetAdaptersInfo (pAdapterInfo, &ulOutBufLen); + if (ERROR_BUFFER_OVERFLOW == dwRet) { + _pgm_heap_free (pAdapterInfo); + pAdapterInfo = NULL; + } else { + break; + } + } + + switch (dwRet) { + case ERROR_SUCCESS: /* NO_ERROR */ + break; + case ERROR_BUFFER_OVERFLOW: + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NOBUFS, + _("GetAdaptersInfo repeatedly failed with ERROR_BUFFER_OVERFLOW.")); + if (pAdapterInfo) + _pgm_heap_free (pAdapterInfo); + return FALSE; + default: + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_win_errno (dwRet), + _("GetAdaptersInfo failed: %s"), + pgm_adapter_strerror (dwRet)); + if (pAdapterInfo) + _pgm_heap_free (pAdapterInfo); + return FALSE; + } + +/* count valid adapters */ + int n = 0, k = 0; + for (pAdapter = pAdapterInfo; + pAdapter; + pAdapter = pAdapter->Next) + { + for (IP_ADDR_STRING *pIPAddr = &pAdapter->IpAddressList; + pIPAddr; + pIPAddr = pIPAddr->Next) + { +/* skip null adapters */ + if (strlen (pIPAddr->IpAddress.String) == 0) + continue; + ++n; + } + } + + pgm_debug ("GetAdaptersInfo() discovered %d interfaces.", n); + +/* contiguous block for adapter list */ + struct _pgm_ifaddrs_t* ifa = pgm_new0 (struct _pgm_ifaddrs_t, n); + struct _pgm_ifaddrs_t* ift = ifa; + +/* now populate list */ + for (pAdapter = pAdapterInfo; + pAdapter; + pAdapter = pAdapter->Next) + { + for (IP_ADDR_STRING *pIPAddr = &pAdapter->IpAddressList; + pIPAddr; + pIPAddr = pIPAddr->Next) + { +/* skip null adapters */ + if (strlen (pIPAddr->IpAddress.String) == 0) + continue; + +/* address */ + ift->_ifa.ifa_addr = (void*)&ift->_addr; + pgm_assert (pgm_sockaddr_pton (pIPAddr->IpAddress.String, ift->_ifa.ifa_addr)); + +/* name */ + pgm_debug ("name:%s IPv4 index:%lu", + pAdapter->AdapterName, pAdapter->Index); + ift->_ifa.ifa_name = ift->_name; + strncpy (ift->_ifa.ifa_name, pAdapter->AdapterName, IF_NAMESIZE); + ift->_ifa.ifa_name[IF_NAMESIZE - 1] = 0; + +/* flags: assume up, broadcast and multicast */ + ift->_ifa.ifa_flags = IFF_UP | IFF_BROADCAST | IFF_MULTICAST; + if (pAdapter->Type == MIB_IF_TYPE_LOOPBACK) + ift->_ifa.ifa_flags |= IFF_LOOPBACK; + +/* netmask */ + ift->_ifa.ifa_netmask = (void*)&ift->_netmask; + pgm_assert (pgm_sockaddr_pton (pIPAddr->IpMask.String, ift->_ifa.ifa_netmask)); + +/* next */ + if (k++ < (n - 1)) { + ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); + ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); + } + } + } + + if (pAdapterInfo) + free (pAdapterInfo); + *ifap = (struct pgm_ifaddrs_t*)ifa; + return TRUE; +} + +static +bool +_pgm_getadaptersaddresses ( + struct pgm_ifaddrs_t** restrict ifap, + pgm_error_t** restrict error + ) +{ + DWORD dwSize = DEFAULT_BUFFER_SIZE, dwRet; + IP_ADAPTER_ADDRESSES *pAdapterAddresses = NULL, *adapter; + +/* loop to handle interfaces coming online causing a buffer overflow + * between first call to list buffer length and second call to enumerate. + */ + for (unsigned i = MAX_TRIES; i; i--) + { + pgm_debug ("IP_ADAPTER_ADDRESSES buffer length %lu bytes.", dwSize); + pAdapterAddresses = (IP_ADAPTER_ADDRESSES*)_pgm_heap_alloc (dwSize); + dwRet = GetAdaptersAddresses (AF_UNSPEC, + GAA_FLAG_INCLUDE_PREFIX | + GAA_FLAG_SKIP_ANYCAST | + GAA_FLAG_SKIP_DNS_SERVER | + GAA_FLAG_SKIP_FRIENDLY_NAME | + GAA_FLAG_SKIP_MULTICAST, + NULL, + pAdapterAddresses, + &dwSize); + if (ERROR_BUFFER_OVERFLOW == dwRet) { + _pgm_heap_free (pAdapterAddresses); + pAdapterAddresses = NULL; + } else { + break; + } + } + + switch (dwRet) { + case ERROR_SUCCESS: + break; + case ERROR_BUFFER_OVERFLOW: + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NOBUFS, + _("GetAdaptersAddresses repeatedly failed with ERROR_BUFFER_OVERFLOW.")); + if (pAdapterAddresses) + free (pAdapterAddresses); + return FALSE; + default: + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_win_errno (dwRet), + _("GetAdaptersAddresses failed: %s"), + pgm_adapter_strerror (dwRet)); + if (pAdapterAddresses) + free (pAdapterAddresses); + return FALSE; + } + +/* count valid adapters */ + int n = 0, k = 0; + for (adapter = pAdapterAddresses; + adapter; + adapter = adapter->Next) + { + for (IP_ADAPTER_UNICAST_ADDRESS *unicast = adapter->FirstUnicastAddress; + unicast; + unicast = unicast->Next) + { +/* ensure IP adapter */ + if (AF_INET != unicast->Address.lpSockaddr->sa_family && + AF_INET6 != unicast->Address.lpSockaddr->sa_family) + { + continue; + } + + ++n; + } + } + +/* contiguous block for adapter list */ + struct _pgm_ifaddrs_t* ifa = pgm_new0 (struct _pgm_ifaddrs_t, n); + struct _pgm_ifaddrs_t* ift = ifa; + +/* now populate list */ + for (adapter = pAdapterAddresses; + adapter; + adapter = adapter->Next) + { + int unicastIndex = 0; + for (IP_ADAPTER_UNICAST_ADDRESS *unicast = adapter->FirstUnicastAddress; + unicast; + unicast = unicast->Next, ++unicastIndex) + { +/* ensure IP adapter */ + if (AF_INET != unicast->Address.lpSockaddr->sa_family && + AF_INET6 != unicast->Address.lpSockaddr->sa_family) + { + continue; + } + +/* address */ + ift->_ifa.ifa_addr = (void*)&ift->_addr; + memcpy (ift->_ifa.ifa_addr, unicast->Address.lpSockaddr, unicast->Address.iSockaddrLength); + +/* name */ + pgm_debug ("name:%s IPv4 index:%lu IPv6 index:%lu", + adapter->AdapterName, adapter->IfIndex, adapter->Ipv6IfIndex); + ift->_ifa.ifa_name = ift->_name; + strncpy (ift->_ifa.ifa_name, adapter->AdapterName, IF_NAMESIZE); + ift->_ifa.ifa_name[IF_NAMESIZE - 1] = 0; + +/* flags */ + ift->_ifa.ifa_flags = 0; + if (IfOperStatusUp == adapter->OperStatus) + ift->_ifa.ifa_flags |= IFF_UP; + if (IF_TYPE_SOFTWARE_LOOPBACK == adapter->IfType) + ift->_ifa.ifa_flags |= IFF_LOOPBACK; + if (!(adapter->Flags & IP_ADAPTER_NO_MULTICAST)) + ift->_ifa.ifa_flags |= IFF_MULTICAST; + +/* netmask */ + ift->_ifa.ifa_netmask = (void*)&ift->_netmask; + +/* pre-Vista must hunt for matching prefix in linked list, otherwise use OnLinkPrefixLength */ + int prefixIndex = 0; + ULONG prefixLength = 0; + for (IP_ADAPTER_PREFIX *prefix = adapter->FirstPrefix; + prefix; + prefix = prefix->Next, ++prefixIndex) + { + if (prefixIndex == unicastIndex) { + prefixLength = prefix->PrefixLength; + break; + } + } + +/* map prefix to netmask */ + ift->_ifa.ifa_netmask->sa_family = unicast->Address.lpSockaddr->sa_family; + switch (unicast->Address.lpSockaddr->sa_family) { + case AF_INET: + if (0 == prefixLength) { + pgm_warn (_("IPv4 adapter %s prefix length is 0, overriding to 32."), adapter->AdapterName); + prefixLength = 32; + } + ((struct sockaddr_in*)ift->_ifa.ifa_netmask)->sin_addr.s_addr = htonl( 0xffffffffU << ( 32 - prefixLength ) ); + break; + + case AF_INET6: + if (0 == prefixLength) { + pgm_warn (_("IPv6 adapter %s prefix length is 0, overriding to 128."), adapter->AdapterName); + prefixLength = 128; + } + for (ULONG i = prefixLength, j = 0; i > 0; i -= 8, ++j) + { + ((struct sockaddr_in6*)ift->_ifa.ifa_netmask)->sin6_addr.s6_addr[ j ] = i >= 8 ? 0xff : (ULONG)(( 0xffU << ( 8 - i ) ) & 0xffU ); + } + break; + } + +/* next */ + if (k++ < (n - 1)) { + ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); + ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); + } + } + } + + if (pAdapterAddresses) + free (pAdapterAddresses); + *ifap = (struct pgm_ifaddrs_t*)ifa; + return TRUE; +} +#endif /* _WIN32 */ + +/* returns TRUE on success setting ifap to a linked list of system interfaces, + * returns FALSE on failure and sets error appropriately. + */ + +bool +pgm_getifaddrs ( + struct pgm_ifaddrs_t** restrict ifap, + pgm_error_t** restrict error + ) +{ + pgm_assert (NULL != ifap); + + pgm_debug ("pgm_getifaddrs (ifap:%p error:%p)", + (void*)ifap, (void*)error); + +#ifdef CONFIG_HAVE_GETIFADDRS + const int e = getifaddrs ((struct ifaddrs**)ifap); + if (-1 == e) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("getifaddrs failed: %s"), + strerror (errno)); + return FALSE; + } + return TRUE; +#elif defined(CONFIG_TARGET_WINE) + return _pgm_getadaptersinfo (ifap, error); +#elif defined(_WIN32) + return _pgm_getadaptersaddresses (ifap, error); +#elif defined(SIOCGLIFCONF) + return _pgm_getlifaddrs (ifap, error); +#elif defined(SIOCGIFCONF) + return _pgm_getifaddrs (ifap, error); +#else +# error "Unsupported interface enumeration on this platform." +#endif /* !CONFIG_HAVE_GETIFADDRS */ +} + +void +pgm_freeifaddrs ( + struct pgm_ifaddrs_t* ifa + ) +{ + pgm_return_if_fail (NULL != ifa); + +#ifdef CONFIG_HAVE_GETIFADDRS + freeifaddrs ((struct ifaddrs*)ifa); +#else + pgm_free (ifa); +#endif +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/getifaddrs.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/getifaddrs.c.c89.patch new file mode 100644 index 0000000..d7e7c08 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/getifaddrs.c.c89.patch @@ -0,0 +1,218 @@ +--- getifaddrs.c 2010-07-03 20:31:27.000000000 +0800 ++++ getifaddrs.c89 2010-08-04 10:49:52.000000000 +0800 +@@ -507,7 +507,9 @@ + /* loop to handle interfaces coming online causing a buffer overflow + * between first call to list buffer length and second call to enumerate. + */ +- for (unsigned i = MAX_TRIES; i; i--) ++ { ++ unsigned i; ++ for (i = MAX_TRIES; i; i--) + { + pgm_debug ("IP_ADAPTER_INFO buffer length %lu bytes.", ulOutBufLen); + pAdapterInfo = (IP_ADAPTER_INFO*)_pgm_heap_alloc (ulOutBufLen); +@@ -519,6 +521,7 @@ + break; + } + } ++ } + + switch (dwRet) { + case ERROR_SUCCESS: /* NO_ERROR */ +@@ -543,12 +546,15 @@ + } + + /* count valid adapters */ ++ { + int n = 0, k = 0; + for (pAdapter = pAdapterInfo; + pAdapter; + pAdapter = pAdapter->Next) + { +- for (IP_ADDR_STRING *pIPAddr = &pAdapter->IpAddressList; ++ { ++ IP_ADDR_STRING *pIPAddr; ++ for (pIPAddr = &pAdapter->IpAddressList; + pIPAddr; + pIPAddr = pIPAddr->Next) + { +@@ -557,11 +563,13 @@ + continue; + ++n; + } ++ } + } + + pgm_debug ("GetAdaptersInfo() discovered %d interfaces.", n); + + /* contiguous block for adapter list */ ++ { + struct _pgm_ifaddrs_t* ifa = pgm_new0 (struct _pgm_ifaddrs_t, n); + struct _pgm_ifaddrs_t* ift = ifa; + +@@ -570,7 +578,9 @@ + pAdapter; + pAdapter = pAdapter->Next) + { +- for (IP_ADDR_STRING *pIPAddr = &pAdapter->IpAddressList; ++ { ++ IP_ADDR_STRING *pIPAddr; ++ for (pIPAddr = &pAdapter->IpAddressList; + pIPAddr; + pIPAddr = pIPAddr->Next) + { +@@ -586,8 +596,12 @@ + pgm_debug ("name:%s IPv4 index:%lu", + pAdapter->AdapterName, pAdapter->Index); + ift->_ifa.ifa_name = ift->_name; ++#ifdef _MSC_VER ++ strncpy_s (ift->_ifa.ifa_name, IF_NAMESIZE, pAdapter->AdapterName, _TRUNCATE); ++#else + strncpy (ift->_ifa.ifa_name, pAdapter->AdapterName, IF_NAMESIZE); + ift->_ifa.ifa_name[IF_NAMESIZE - 1] = 0; ++#endif + + /* flags: assume up, broadcast and multicast */ + ift->_ifa.ifa_flags = IFF_UP | IFF_BROADCAST | IFF_MULTICAST; +@@ -604,11 +618,14 @@ + ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); + } + } ++ } + } + + if (pAdapterInfo) + free (pAdapterInfo); + *ifap = (struct pgm_ifaddrs_t*)ifa; ++ } ++ } + return TRUE; + } + +@@ -625,7 +642,9 @@ + /* loop to handle interfaces coming online causing a buffer overflow + * between first call to list buffer length and second call to enumerate. + */ +- for (unsigned i = MAX_TRIES; i; i--) ++ { ++ unsigned i; ++ for (i = MAX_TRIES; i; i--) + { + pgm_debug ("IP_ADAPTER_ADDRESSES buffer length %lu bytes.", dwSize); + pAdapterAddresses = (IP_ADAPTER_ADDRESSES*)_pgm_heap_alloc (dwSize); +@@ -645,6 +664,7 @@ + break; + } + } ++ } + + switch (dwRet) { + case ERROR_SUCCESS: +@@ -669,12 +689,15 @@ + } + + /* count valid adapters */ ++ { + int n = 0, k = 0; + for (adapter = pAdapterAddresses; + adapter; + adapter = adapter->Next) + { +- for (IP_ADAPTER_UNICAST_ADDRESS *unicast = adapter->FirstUnicastAddress; ++ { ++ IP_ADAPTER_UNICAST_ADDRESS *unicast; ++ for (unicast = adapter->FirstUnicastAddress; + unicast; + unicast = unicast->Next) + { +@@ -687,9 +710,11 @@ + + ++n; + } ++ } + } + + /* contiguous block for adapter list */ ++ { + struct _pgm_ifaddrs_t* ifa = pgm_new0 (struct _pgm_ifaddrs_t, n); + struct _pgm_ifaddrs_t* ift = ifa; + +@@ -699,7 +724,9 @@ + adapter = adapter->Next) + { + int unicastIndex = 0; +- for (IP_ADAPTER_UNICAST_ADDRESS *unicast = adapter->FirstUnicastAddress; ++ { ++ IP_ADAPTER_UNICAST_ADDRESS *unicast; ++ for (unicast = adapter->FirstUnicastAddress; + unicast; + unicast = unicast->Next, ++unicastIndex) + { +@@ -718,8 +745,12 @@ + pgm_debug ("name:%s IPv4 index:%lu IPv6 index:%lu", + adapter->AdapterName, adapter->IfIndex, adapter->Ipv6IfIndex); + ift->_ifa.ifa_name = ift->_name; ++#ifdef _MSC_VER ++ strncpy_s (ift->_ifa.ifa_name, IF_NAMESIZE, adapter->AdapterName, _TRUNCATE); ++#else + strncpy (ift->_ifa.ifa_name, adapter->AdapterName, IF_NAMESIZE); + ift->_ifa.ifa_name[IF_NAMESIZE - 1] = 0; ++#endif + + /* flags */ + ift->_ifa.ifa_flags = 0; +@@ -734,9 +765,12 @@ + ift->_ifa.ifa_netmask = (void*)&ift->_netmask; + + /* pre-Vista must hunt for matching prefix in linked list, otherwise use OnLinkPrefixLength */ ++ { + int prefixIndex = 0; + ULONG prefixLength = 0; +- for (IP_ADAPTER_PREFIX *prefix = adapter->FirstPrefix; ++ { ++ IP_ADAPTER_PREFIX *prefix; ++ for (prefix = adapter->FirstPrefix; + prefix; + prefix = prefix->Next, ++prefixIndex) + { +@@ -745,6 +779,7 @@ + break; + } + } ++ } + + /* map prefix to netmask */ + ift->_ifa.ifa_netmask->sa_family = unicast->Address.lpSockaddr->sa_family; +@@ -762,12 +797,16 @@ + pgm_warn (_("IPv6 adapter %s prefix length is 0, overriding to 128."), adapter->AdapterName); + prefixLength = 128; + } +- for (ULONG i = prefixLength, j = 0; i > 0; i -= 8, ++j) ++ { ++ ULONG i, j; ++ for (i = prefixLength, j = 0; i > 0; i -= 8, ++j) + { + ((struct sockaddr_in6*)ift->_ifa.ifa_netmask)->sin6_addr.s6_addr[ j ] = i >= 8 ? 0xff : (ULONG)(( 0xffU << ( 8 - i ) ) & 0xffU ); + } ++ } + break; + } ++ } + + /* next */ + if (k++ < (n - 1)) { +@@ -775,11 +814,14 @@ + ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); + } + } ++ } + } + + if (pAdapterAddresses) + free (pAdapterAddresses); + *ifap = (struct pgm_ifaddrs_t*)ifa; ++ } ++ } + return TRUE; + } + #endif /* _WIN32 */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/getifaddrs_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/getifaddrs_unittest.c new file mode 100644 index 0000000..1ab7378 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/getifaddrs_unittest.c @@ -0,0 +1,262 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for portable getifaddrs implementation. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* IFF_UP */ +#define _BSD_SOURCE 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* mock state */ + +/* mock functions for external references */ + +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return 0; +} + + +#define GETIFADDRS_DEBUG +#include "getifaddrs.c" + + +char* +ifflags_string ( + unsigned int flags + ) +{ + static char s[1024]; + + s[0] = '\0'; + if (flags & IFF_UP) + strcat (s, "IFF_UP"); +#define IFF(flag) \ + do { \ + if (flags & flag) { \ + strcat (s, s[0] ? ("|" #flag) : (#flag)); \ + } \ + } while (0) +#ifdef IFF_BROADCAST + IFF(IFF_BROADCAST); +#endif +#ifdef IFF_DEBUG + IFF(IFF_DEBUG); +#endif +#ifdef IFF_LOOPBACK + IFF(IFF_LOOPBACK); +#endif +#ifdef IFF_POINTOPOINT + IFF(IFF_POINTOPOINT); +#endif +#ifdef IFF_RUNNING + IFF(IFF_RUNNING); +#endif +#ifdef IFF_NOARP + IFF(IFF_NOARP); +#endif +#ifdef IFF_PROMISC + IFF(IFF_PROMISC); +#endif +#ifdef IFF_NOTRAILERS + IFF(IFF_NOTRAILERS); +#endif +#ifdef IFF_ALLMULTI + IFF(IFF_ALLMULTI); +#endif +#ifdef IFF_MASTER + IFF(IFF_MASTER); +#endif +#ifdef IFF_SLAVE + IFF(IFF_SLAVE); +#endif +#ifdef IFF_MULTICAST + IFF(IFF_MULTICAST); +#endif +#ifdef IFF_PORTSEL + IFF(IFF_PORTSEL); +#endif +#ifdef IFF_AUTOMEDIA + IFF(IFF_AUTOMEDIA); +#endif +#ifdef IFF_DYNAMIC + IFF(IFF_DYNAMIC); +#endif +#ifdef IFF_LOWER_UP + IFF(IFF_LOWER_UP); +#endif +#ifdef IFF_DORMANT + IFF(IFF_DORMANT); +#endif +#ifdef IFF_ECHO + IFF(IFF_ECHO); +#endif + if (!s[0]) { + if (flags) + sprintf (s, "0x%x", flags); + else + strcpy (s, "(null)"); + } + return s; +} + +/* target: + * bool + * pgm_getifaddrs ( + * struct pgm_ifaddrs_t**restrict ifap, + * pgm_error_t**restrict error + * ) + */ + +START_TEST (test_getifaddrs_pass_001) +{ + char saddr[INET6_ADDRSTRLEN], snetmask[INET6_ADDRSTRLEN]; + struct pgm_ifaddrs_t *ifap = NULL, *ifa; + pgm_error_t* err = NULL; + fail_unless (TRUE == pgm_getifaddrs (&ifap, &err), "getifaddrs failed"); + for (ifa = ifap; ifa; ifa = ifa->ifa_next) + { + fail_unless (NULL != ifa, "invalid address"); + if (ifa->ifa_addr) { + if (AF_INET == ifa->ifa_addr->sa_family || + AF_INET6 == ifa->ifa_addr->sa_family) + pgm_sockaddr_ntop (ifa->ifa_addr, saddr, sizeof(saddr)); +#ifdef AF_PACKET + else if (AF_PACKET == ifa->ifa_addr->sa_family) + strcpy (saddr, "(AF_PACKET)"); +#endif + else + sprintf (saddr, "(AF = %d)", ifa->ifa_addr->sa_family); + } else + strcpy (saddr, "(null)"); + if (ifa->ifa_netmask) { + if (AF_INET == ifa->ifa_addr->sa_family || + AF_INET6 == ifa->ifa_addr->sa_family) + pgm_sockaddr_ntop (ifa->ifa_netmask, snetmask, sizeof(snetmask)); +#ifdef AF_PACKET + else if (AF_PACKET == ifa->ifa_addr->sa_family) + strcpy (snetmask, "(AF_PACKET)"); +#endif + else + sprintf (snetmask, "(AF = %d)", ifa->ifa_addr->sa_family); + } else + strcpy (snetmask, "(null)"); + g_message ("ifa = {" + "ifa_next = %p, " + "ifa_name = %s%s%s, " + "ifa_flags = %s, " + "ifa_addr = %s, " + "ifa_netmask = %s" + "}", + ifa->ifa_next, + ifa->ifa_name ? "\"" : "", ifa->ifa_name ? ifa->ifa_name : "(null)", ifa->ifa_name ? "\"" : "", + ifflags_string (ifa->ifa_flags), + saddr, + snetmask); + } +} +END_TEST + +START_TEST (test_getifaddrs_fail_001) +{ + fail_unless (FALSE == pgm_getifaddrs (NULL, NULL), "getifaddrs failed"); + g_message ("errno:%d", errno); +} +END_TEST + +/* target: + * void + * pgm_freeifaddrs ( + * struct pgm_ifaddrs* ifa + * ) + */ + +START_TEST (test_freeifaddrs_pass_001) +{ + struct pgm_ifaddrs_t* ifap = NULL; + pgm_error_t* err = NULL; + fail_unless (TRUE == pgm_getifaddrs (&ifap, &err), "getifaddrs failed"); + pgm_freeifaddrs (ifap); +} +END_TEST + +/* silent failure */ +START_TEST (test_freeifaddrs_pass_002) +{ + pgm_freeifaddrs (NULL); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_getifaddrs = tcase_create ("getifaddrs"); + suite_add_tcase (s, tc_getifaddrs); + tcase_add_test (tc_getifaddrs, test_getifaddrs_pass_001); + tcase_add_test_raise_signal (tc_getifaddrs, test_getifaddrs_fail_001, SIGABRT); + + TCase* tc_freeifaddrs = tcase_create ("freeifaddrs"); + suite_add_tcase (s, tc_freeifaddrs); + tcase_add_test (tc_freeifaddrs, test_freeifaddrs_pass_001); + tcase_add_test (tc_freeifaddrs, test_freeifaddrs_pass_002); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/getnodeaddr.c b/3rdparty/openpgm-svn-r1135/pgm/getnodeaddr.c new file mode 100644 index 0000000..0765c9f --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/getnodeaddr.c @@ -0,0 +1,196 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable function to return the nodes IP address. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#ifndef _WIN32 +# include +#endif +#include +#include + + +//#define GETNODEADDR_DEBUG + + +/* globals */ + +static const char* pgm_family_string (const sa_family_t); + + +/* return node primary address on multi-address family interfaces. + * + * returns TRUE on success, returns FALSE on failure. + */ + +bool +pgm_if_getnodeaddr ( + const sa_family_t family, /* requested address family, AF_INET, AF_INET6, or AF_UNSPEC */ + struct sockaddr* restrict addr, + const socklen_t cnt, /* size of address pointed to by addr */ + pgm_error_t** restrict error + ) +{ + pgm_return_val_if_fail (AF_INET == family || AF_INET6 == family || AF_UNSPEC == family, FALSE); + pgm_return_val_if_fail (NULL != addr, FALSE); + if (AF_INET == family || AF_UNSPEC == family) + pgm_return_val_if_fail (cnt >= sizeof(struct sockaddr_in), FALSE); + else + pgm_return_val_if_fail (cnt >= sizeof(struct sockaddr_in6), FALSE); + + pgm_debug ("pgm_if_getnodeaddr (family:%s addr:%p cnt:%d error:%p)", + pgm_family_string (family), (const void*)addr, cnt, (const void*)error); + + char hostname[NI_MAXHOST + 1]; + struct hostent* he; + + if (0 != gethostname (hostname, sizeof(hostname))) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("Resolving hostname: %s"), +#ifndef _WIN32 + strerror (errno) +#else + pgm_wsastrerror (WSAGetLastError()) +#endif + ); + return FALSE; + } + + addr->sa_family = family; + struct addrinfo hints = { + .ai_family = family, + .ai_socktype = SOCK_STREAM, /* not really */ + .ai_protocol = IPPROTO_TCP, /* not really */ + .ai_flags = AI_ADDRCONFIG, + }, *res; + + int e = getaddrinfo (hostname, NULL, &hints, &res); + if (0 == e) { + const socklen_t addrlen = res->ai_addrlen; + memcpy (addr, res->ai_addr, addrlen); + freeaddrinfo (res); + return TRUE; + } else if (EAI_NONAME != e) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_eai_errno (e, errno), + _("Resolving hostname address: %s"), + gai_strerror (e)); + return FALSE; + } else if (AF_UNSPEC == family) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NONAME, + _("Resolving hostname address family.")); + return FALSE; + } + +/* Common case a dual stack host has incorrect IPv6 configuration, i.e. + * hostname is only IPv4 and despite one or more IPv6 addresses. Workaround + * for this case is to resolve the IPv4 hostname, find the matching interface + * and from that interface find an active IPv6 address taking global scope as + * preference over link scoped addresses. + */ + he = gethostbyname (hostname); + if (NULL == he) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_h_errno (h_errno), +#ifndef _WIN32 + _("Resolving IPv4 hostname address: %s"), + hstrerror (h_errno) +#else + _("Resolving IPv4 hostname address: %s"), + pgm_wsastrerror (WSAGetLastError()) +#endif + ); + return FALSE; + } + + struct pgm_ifaddrs_t *ifap, *ifa, *ifa6; + if (!pgm_getifaddrs (&ifap, error)) { + pgm_prefix_error (error, + _("Enumerating network interfaces: ")); + return FALSE; + } + +/* hunt for IPv4 interface */ + for (ifa = ifap; ifa; ifa = ifa->ifa_next) + { + if (NULL == ifa->ifa_addr || + AF_INET != ifa->ifa_addr->sa_family) + continue; + if (((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr == ((struct in_addr*)(he->h_addr_list[0]))->s_addr) + { + goto ipv4_found; + } + } + pgm_freeifaddrs (ifap); + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NONET, + _("Discovering primary IPv4 network interface.")); + return FALSE; +ipv4_found: + +/* hunt for IPv6 interface */ + for (ifa6 = ifap; ifa6; ifa6 = ifa6->ifa_next) + { + if (AF_INET6 != ifa6->ifa_addr->sa_family) + continue; + if (0 == strcmp (ifa->ifa_name, ifa6->ifa_name)) + { + goto ipv6_found; + } + } + pgm_freeifaddrs (ifap); + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NONET, + _("Discovering primary IPv6 network interface.")); + return FALSE; +ipv6_found: + + memcpy (addr, ifa6->ifa_addr, pgm_sockaddr_len (ifa6->ifa_addr)); + pgm_freeifaddrs (ifap); + return TRUE; +} + +static +const char* +pgm_family_string ( + const sa_family_t family + ) +{ + const char* c; + + switch (family) { + case AF_UNSPEC: c = "AF_UNSPEC"; break; + case AF_INET: c = "AF_INET"; break; + case AF_INET6: c = "AF_INET6"; break; + default: c = "(unknown)"; break; + } + + return c; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/getnodeaddr.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/getnodeaddr.c.c89.patch new file mode 100644 index 0000000..06db807 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/getnodeaddr.c.c89.patch @@ -0,0 +1,59 @@ +--- getnodeaddr.c 2010-05-21 11:35:15.000000000 +0800 ++++ getnodeaddr.c89 2010-08-05 13:17:31.000000000 +0800 +@@ -58,6 +58,7 @@ + pgm_debug ("pgm_if_getnodeaddr (family:%s addr:%p cnt:%d error:%p)", + pgm_family_string (family), (const void*)addr, cnt, (const void*)error); + ++ { + char hostname[NI_MAXHOST + 1]; + struct hostent* he; + +@@ -75,15 +76,22 @@ + return FALSE; + } + ++/* sa_family_t may be ADDRESS_FAMILY on Vista+ which is a USHORT, whilst ULONG ++ * on earlier targets. ++ */ ++#pragma warning( disable : 4244 ) + addr->sa_family = family; +- struct addrinfo hints = { +- .ai_family = family, +- .ai_socktype = SOCK_STREAM, /* not really */ +- .ai_protocol = IPPROTO_TCP, /* not really */ +- .ai_flags = AI_ADDRCONFIG, +- }, *res; ++#pragma warning( default : 4244 ) ++ { ++ int e; ++ struct addrinfo hints, *res; ++ memset (&hints, 0, sizeof(hints)); ++ hints.ai_family = family, ++ hints.ai_socktype = SOCK_STREAM, /* not really */ ++ hints.ai_protocol = IPPROTO_TCP, /* not really */ ++ hints.ai_flags = AI_ADDRCONFIG, + +- int e = getaddrinfo (hostname, NULL, &hints, &res); ++ e = getaddrinfo (hostname, NULL, &hints, &res); + if (0 == e) { + const socklen_t addrlen = res->ai_addrlen; + memcpy (addr, res->ai_addr, addrlen); +@@ -125,7 +133,9 @@ + ); + return FALSE; + } ++ } + ++ { + struct pgm_ifaddrs_t *ifap, *ifa, *ifa6; + if (!pgm_getifaddrs (&ifap, error)) { + pgm_prefix_error (error, +@@ -172,6 +182,8 @@ + + memcpy (addr, ifa6->ifa_addr, pgm_sockaddr_len (ifa6->ifa_addr)); + pgm_freeifaddrs (ifap); ++ } ++ } + return TRUE; + } + diff --git a/3rdparty/openpgm-svn-r1135/pgm/getnodeaddr_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/getnodeaddr_unittest.c new file mode 100644 index 0000000..a226931 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/getnodeaddr_unittest.c @@ -0,0 +1,573 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for portable function to return the nodes IP address. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* IFF_UP */ +#define _BSD_SOURCE 1 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* mock state */ + +struct addrinfo; + +struct mock_host_t { + struct sockaddr_storage address; + char* canonical_hostname; + char* alias; +}; + +struct mock_interface_t { + unsigned int index; + char* name; + unsigned int flags; + struct sockaddr_storage addr; + struct sockaddr_storage netmask; +}; + +static GList *mock_hosts = NULL, *mock_interfaces = NULL; + +#define MOCK_HOSTNAME "kiku" +static char* mock_kiku = MOCK_HOSTNAME; +static char* mock_localhost = "localhost"; +static char* mock_invalid = "invalid.invalid"; /* RFC 2606 */ +static char* mock_toolong = "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij12345"; /* 65 */ +static char* mock_hostname = NULL; + +struct pgm_ifaddrs_t; +struct pgm_error_t; + +static bool mock_pgm_getifaddrs (struct pgm_ifaddrs_t**, struct pgm_error_t**); +static void mock_pgm_freeifaddrs (struct pgm_ifaddrs_t*); +static int mock_getaddrinfo (const char*, const char*, const struct addrinfo*, struct addrinfo**); +static void mock_freeaddrinfo (struct addrinfo*); +static int mock_gethostname (char*, size_t); +static struct hostent* mock_gethostbyname (const char*); + + +#define pgm_getifaddrs mock_pgm_getifaddrs +#define pgm_freeifaddrs mock_pgm_freeifaddrs +#define getaddrinfo mock_getaddrinfo +#define freeaddrinfo mock_freeaddrinfo +#define gethostname mock_gethostname +#define gethostbyname mock_gethostbyname + + +#define GETNODEADDR_DEBUG +#include "getnodeaddr.c" + + +static +gpointer +create_host ( + const char* address, + const char* canonical_hostname, + const char* alias + ) +{ + struct mock_host_t* new_host; + + g_assert (address); + g_assert (canonical_hostname); + + new_host = g_slice_alloc0 (sizeof(struct mock_host_t)); + g_assert (pgm_sockaddr_pton (address, (struct sockaddr*)&new_host->address)); + new_host->canonical_hostname = g_strdup (canonical_hostname); + new_host->alias = alias ? g_strdup (alias) : NULL; + + return new_host; +} + +static +gpointer +create_interface ( + const unsigned index, + const char* name, + const char* flags + ) +{ + struct mock_interface_t* new_interface; + + g_assert (name); + g_assert (flags); + + new_interface = g_slice_alloc0 (sizeof(struct mock_interface_t)); + new_interface->index = index; + new_interface->name = g_strdup (name); + + struct sockaddr_in* sin = (gpointer)&new_interface->addr; + struct sockaddr_in6* sin6 = (gpointer)&new_interface->addr; + + gchar** tokens = g_strsplit (flags, ",", 0); + for (guint i = 0; tokens[i]; i++) + { + if (strcmp (tokens[i], "up") == 0) + new_interface->flags |= IFF_UP; + else if (strcmp (tokens[i], "down") == 0) + new_interface->flags |= 0; + else if (strcmp (tokens[i], "loop") == 0) + new_interface->flags |= IFF_LOOPBACK; + else if (strcmp (tokens[i], "broadcast") == 0) + new_interface->flags |= IFF_BROADCAST; + else if (strcmp (tokens[i], "multicast") == 0) + new_interface->flags |= IFF_MULTICAST; + else if (strncmp (tokens[i], "ip=", strlen("ip=")) == 0) { + const char* addr = tokens[i] + strlen("ip="); + g_assert (pgm_sockaddr_pton (addr, (struct sockaddr*)&new_interface->addr)); + } + else if (strncmp (tokens[i], "netmask=", strlen("netmask=")) == 0) { + const char* addr = tokens[i] + strlen("netmask="); + g_assert (pgm_sockaddr_pton (addr, (struct sockaddr*)&new_interface->netmask)); + } + else if (strncmp (tokens[i], "scope=", strlen("scope=")) == 0) { + const char* scope = tokens[i] + strlen("scope="); + g_assert (AF_INET6 == ((struct sockaddr*)&new_interface->addr)->sa_family); + ((struct sockaddr_in6*)&new_interface->addr)->sin6_scope_id = atoi (scope); + } + else + g_error ("parsing failed for flag %s%s%s", + tokens[i] ? "\"" : "", tokens[i] ? tokens[i] : "(null)", tokens[i] ? "\"" : ""); + } + + g_strfreev (tokens); + return new_interface; +} + +#define APPEND_HOST2(a,b,c) \ + do { \ + gpointer data = create_host ((a), (b), (c)); \ + g_assert (data); \ + mock_hosts = g_list_append (mock_hosts, data); \ + g_assert (mock_hosts); g_assert (mock_hosts->data); \ + } while (0) +#define APPEND_HOST(a,b) APPEND_HOST2((a),(b),NULL) +#define APPEND_INTERFACE(a,b,c) \ + do { \ + gpointer data = create_interface ((a), (b), (c)); \ + g_assert (data); \ + mock_interfaces = g_list_append (mock_interfaces, data); \ + g_assert (mock_interfaces); g_assert (mock_interfaces->data); \ + } while (0) +static +void +mock_setup_net (void) +{ + mock_hostname = mock_kiku; + + APPEND_HOST ( "127.0.0.1", "localhost"); + APPEND_HOST2( "10.6.28.33", "kiku.hk.miru.hk", "kiku"); + APPEND_HOST2( "2002:dce8:d28e::33", "ip6-kiku", "kiku"); + APPEND_HOST2( "::1", "ip6-localhost", "ip6-loopback"); + + APPEND_INTERFACE( 1, "lo", "up,loop"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast"); + APPEND_INTERFACE( 3, "eth1", "down,broadcast,multicast"); + APPEND_INTERFACE( 1, "lo", "up,loop,ip=127.0.0.1,netmask=255.0.0.0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=10.6.28.33,netmask=255.255.255.0"); + APPEND_INTERFACE( 1, "lo", "up,loop,ip=::1,netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,scope=0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=2002:dce8:d28e::33,netmask=ffff:ffff:ffff:ffff::0,scope=0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=fe80::214:5eff:febd:6dda,netmask=ffff:ffff:ffff:ffff::0,scope=2"); +} + +/* with broken IPv6 hostname setup */ +static +void +mock_setup_net2 (void) +{ + mock_hostname = mock_kiku; + + APPEND_HOST ( "127.0.0.1", "localhost"); + APPEND_HOST2( "10.6.28.33", "kiku.hk.miru.hk", "kiku"); + APPEND_HOST( "2002:dce8:d28e::33", "ip6-kiku"); + APPEND_HOST2( "::1", "ip6-localhost", "ip6-loopback"); + + APPEND_INTERFACE( 1, "lo", "up,loop"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast"); + APPEND_INTERFACE( 3, "eth1", "down,broadcast,multicast"); + APPEND_INTERFACE( 1, "lo", "up,loop,ip=127.0.0.1,netmask=255.0.0.0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=10.6.28.33,netmask=255.255.255.0"); + APPEND_INTERFACE( 1, "lo", "up,loop,ip=::1,netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,scope=0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=2002:dce8:d28e::33,netmask=ffff:ffff:ffff:ffff::0,scope=0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=fe80::214:5eff:febd:6dda,netmask=ffff:ffff:ffff:ffff::0,scope=2"); +} + +static +void +mock_teardown_net (void) +{ + GList* list; + + list = mock_hosts; + while (list) { + struct mock_host_t* host = list->data; + g_free (host->canonical_hostname); + if (host->alias) + g_free (host->alias); + g_slice_free1 (sizeof(struct mock_host_t), host); + list = list->next; + } + g_list_free (mock_hosts); + + list = mock_interfaces; + while (list) { + struct mock_interface_t* interface = list->data; + g_free (interface->name); + g_slice_free1 (sizeof(struct mock_interface_t), interface); + list = list->next; + } + g_list_free (mock_interfaces); +} + + +/* mock functions for external references */ + +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return 0; +} + +static +bool +mock_pgm_getifaddrs ( + struct pgm_ifaddrs_t** ifap, + pgm_error_t** err + ) +{ + if (NULL == ifap) { + return FALSE; + } + + g_debug ("mock_getifaddrs (ifap:%p err:%p)", (gpointer)ifap, (gpointer)err); + + GList* list = mock_interfaces; + int n = g_list_length (list); + struct pgm_ifaddrs_t* ifa = malloc (n * sizeof(struct pgm_ifaddrs_t)); + memset (ifa, 0, n * sizeof(struct pgm_ifaddrs_t)); + struct pgm_ifaddrs_t* ift = ifa; + while (list) { + struct mock_interface_t* interface = list->data; + ift->ifa_addr = (gpointer)&interface->addr; + ift->ifa_name = interface->name; + ift->ifa_flags = interface->flags; + ift->ifa_netmask = (gpointer)&interface->netmask; + list = list->next; + if (list) { + ift->ifa_next = ift + 1; + ift = ift->ifa_next; + } + } + + *ifap = ifa; + return TRUE; +} + +static +void +mock_pgm_freeifaddrs ( + struct pgm_ifaddrs_t* ifa + ) +{ + g_debug ("mock_freeifaddrs (ifa:%p)", (gpointer)ifa); + free (ifa); +} + +static +struct hostent* +mock_gethostbyname ( + const char* name + ) +{ + static struct hostent he; + static char* aliases[2]; + static char* addr_list[2]; + +/* pre-conditions */ + g_assert (NULL != name); + + g_debug ("mock_gethostbyname (name:%s%s%s)", + name ? "\"" : "", name ? name : "(null)", name ? "\"" : ""); + + GList* list = mock_hosts; + while (list) { + struct mock_host_t* host = list->data; + const int host_family = ((struct sockaddr*)&host->address)->sa_family; + if (((strcmp (host->canonical_hostname, name) == 0) || + (host->alias && strcmp (host->alias, name) == 0))) + { + he.h_name = host->canonical_hostname; + aliases[0] = host->alias; + aliases[1] = NULL; + he.h_aliases = aliases; + he.h_addrtype = host_family; + switch (host->address.ss_family){ + case AF_INET: + he.h_length = sizeof (struct in_addr); + addr_list[0] = (char*)&host->address + G_STRUCT_OFFSET(struct sockaddr_in, sin_addr); + break; + case AF_INET6: + he.h_length = sizeof (struct in6_addr); + addr_list[0] = (char*)&host->address + G_STRUCT_OFFSET(struct sockaddr_in6, sin6_addr); + break; + default: + g_assert_not_reached(); + } + addr_list[1] = NULL; + he.h_addr_list = addr_list; + return &he; + } + list = list->next; + } + h_errno = HOST_NOT_FOUND; + return NULL; +} + +static +int +mock_getaddrinfo ( + const char* node, + const char* service, + const struct addrinfo* hints, + struct addrinfo** res + ) +{ + const int ai_flags = hints ? hints->ai_flags : (AI_V4MAPPED | AI_ADDRCONFIG); + const int ai_family = hints ? hints->ai_family : AF_UNSPEC; + GList* list; + struct sockaddr_storage addr; + + if (NULL == node && NULL == service) + return EAI_NONAME; + +/* pre-conditions */ + g_assert (NULL != node); + g_assert (NULL == service); + g_assert (!(ai_flags & AI_CANONNAME)); + g_assert (!(ai_flags & AI_NUMERICSERV)); + g_assert (!(ai_flags & AI_V4MAPPED)); + + g_debug ("mock_getaddrinfo (node:\"%s\" service:%s hints:%p res:%p)", + node ? node : "(null)", + service ? service : "(null)", + (gpointer)hints, + (gpointer)res); + + gboolean has_ip4_config; + gboolean has_ip6_config; + + if (hints && hints->ai_flags & AI_ADDRCONFIG) + { + has_ip4_config = has_ip6_config = FALSE; + list = mock_interfaces; + while (list) { + const struct mock_interface_t* interface = list->data; + if (AF_INET == ((struct sockaddr*)&interface->addr)->sa_family) + has_ip4_config = TRUE; + else if (AF_INET6 == ((struct sockaddr*)&interface->addr)->sa_family) + has_ip6_config = TRUE; + if (has_ip4_config && has_ip6_config) + break; + list = list->next; + } + } else { + has_ip4_config = has_ip6_config = TRUE; + } + + if (ai_flags & AI_NUMERICHOST) { + pgm_sockaddr_pton (node, (struct sockaddr*)&addr); + } + list = mock_hosts; + while (list) { + struct mock_host_t* host = list->data; + const int host_family = ((struct sockaddr*)&host->address)->sa_family; + if (((strcmp (host->canonical_hostname, node) == 0) || + (host->alias && strcmp (host->alias, node) == 0) || + (ai_flags & AI_NUMERICHOST && + 0 == pgm_sockaddr_cmp ((struct sockaddr*)&addr, (struct sockaddr*)&host->address))) + && + (host_family == ai_family || AF_UNSPEC == ai_family) && + ((AF_INET == host_family && has_ip4_config) || (AF_INET6 == host_family && has_ip6_config))) + { + struct addrinfo* ai = malloc (sizeof(struct addrinfo)); + memset (ai, 0, sizeof(struct addrinfo)); + ai->ai_family = host_family; + ai->ai_addrlen = AF_INET == host_family ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); + ai->ai_addr = (gpointer)&host->address; + *res = ai; + return 0; + } + list = list->next; + } + return EAI_NONAME; +} + +static +void +mock_freeaddrinfo ( + struct addrinfo* res + ) +{ + g_assert (NULL != res); + g_debug ("mock_freeaddrinfo (res:%p)", (gpointer)res); + free (res); +} + +static +int +mock_gethostname ( + char* name, + size_t len + ) +{ + g_debug ("mock_gethostname (name:%p len:%d)", + (gpointer)name, len); + + if (NULL == name) { + errno = EFAULT; + return -1; + } + if (len < 0) { + errno = EINVAL; + return -1; + } + if (len < (1 + strlen (mock_hostname))) { + errno = ENAMETOOLONG; + return -1; + } +/* force an error */ + if (mock_hostname == mock_toolong) { + errno = ENAMETOOLONG; + return -1; + } + strncpy (name, mock_hostname, len); + if (len > 0) + name[len - 1] = '\0'; + return 0; +} + + +/* target: + * bool + * pgm_if_getnodeaddr ( + * const sa_family_t family, + * struct sockaddr* addr, + * const socklen_t cnt, + * pgm_error_t** error + * ) + */ + +START_TEST (test_getnodeaddr_pass_001) +{ + struct sockaddr_storage addr; + char saddr[INET6_ADDRSTRLEN]; + pgm_error_t* err = NULL; + gboolean success = pgm_if_getnodeaddr (AF_UNSPEC, (struct sockaddr*)&addr, sizeof(addr), &err); + if (!success && err) { + g_error ("Resolving node address with AF_UNSPEC: %s", (err && err->message) ? err->message : "(null)"); + } + fail_unless (TRUE == success, "getnodeaddr failed"); + fail_unless (NULL == err, "error raised"); + pgm_sockaddr_ntop ((struct sockaddr*)&addr, saddr, sizeof(saddr)); + g_message ("AF_UNSPEC:%s", saddr ? saddr : "(null)"); + fail_unless (TRUE == pgm_if_getnodeaddr (AF_INET, (struct sockaddr*)&addr, sizeof(addr), &err), "getnodeaddr failed"); + fail_unless (NULL == err, "error raised"); + pgm_sockaddr_ntop ((struct sockaddr*)&addr, saddr, sizeof(saddr)); + g_message ("AF_INET:%s", saddr ? saddr : "(null)"); + fail_unless (TRUE == pgm_if_getnodeaddr (AF_INET6, (struct sockaddr*)&addr, sizeof(addr), &err), "getnodeaddr failed"); + fail_unless (NULL == err, "error raised"); + pgm_sockaddr_ntop ((struct sockaddr*)&addr, saddr, sizeof(saddr)); + g_message ("AF_INET6:%s", saddr ? saddr : "(null)"); +} +END_TEST + +START_TEST (test_getnodeaddr_fail_001) +{ + pgm_error_t* err = NULL; + fail_unless (FALSE == pgm_if_getnodeaddr (AF_UNSPEC, NULL, 0, &err), "getnodeaddr failed"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + +{ + mock_setup_net(); + + struct sockaddr_storage addr; + char saddr[INET6_ADDRSTRLEN]; + pgm_error_t* err = NULL; + gboolean success = pgm_if_getnodeaddr (AF_UNSPEC, (struct sockaddr*)&addr, sizeof(addr), &err); + if (!success && err) { + g_error ("Resolving node address with AF_UNSPEC: %s", (err && err->message) ? err->message : "(null)"); + } +} + s = suite_create (__FILE__); + + TCase* tc_getnodeaddr = tcase_create ("getnodeaddr"); + suite_add_tcase (s, tc_getnodeaddr); + tcase_add_checked_fixture (tc_getnodeaddr, mock_setup_net, mock_teardown_net); + tcase_add_test (tc_getnodeaddr, test_getnodeaddr_pass_001); + tcase_add_test (tc_getnodeaddr, test_getnodeaddr_fail_001); + + TCase* tc_getnodeaddr2 = tcase_create ("getnodeaddr/2"); + suite_add_tcase (s, tc_getnodeaddr2); + tcase_add_checked_fixture (tc_getnodeaddr2, mock_setup_net2, mock_teardown_net); + tcase_add_test (tc_getnodeaddr2, test_getnodeaddr_pass_001); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/gsi.c b/3rdparty/openpgm-svn-r1135/pgm/gsi.c new file mode 100644 index 0000000..c086cb9 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/gsi.c @@ -0,0 +1,232 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * global session ID helper functions. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#ifndef _WIN32 +# include +#endif +#include +#include + + +//#define GSI_DEBUG + + +/* create a GSI based on md5 of a user provided data block. + * + * returns TRUE on success, returns FALSE on error and sets error appropriately, + */ + +bool +pgm_gsi_create_from_data ( + pgm_gsi_t* restrict gsi, + const uint8_t* restrict data, + const size_t length + ) +{ + pgm_return_val_if_fail (NULL != gsi, FALSE); + pgm_return_val_if_fail (NULL != data, FALSE); + pgm_return_val_if_fail (length > 1, FALSE); + +#ifdef CONFIG_HAVE_GLIB_CHECKSUM + GChecksum* checksum = g_checksum_new (G_CHECKSUM_MD5); + pgm_return_val_if_fail (NULL != checksum, FALSE); + g_checksum_update (checksum, data, length); + memcpy (gsi, g_checksum_get_string (checksum) + 10, 6); + g_checksum_free (checksum); +#else + struct pgm_md5_t ctx; + char resblock[16]; + pgm_md5_init_ctx (&ctx); + pgm_md5_process_bytes (&ctx, data, length); + pgm_md5_finish_ctx (&ctx, resblock); + memcpy (gsi, resblock + 10, 6); +#endif + return TRUE; +} + +bool +pgm_gsi_create_from_string ( + pgm_gsi_t* restrict gsi, + const char* restrict str, + ssize_t length /* -1 for NULL terminated */ + ) +{ + pgm_return_val_if_fail (NULL != gsi, FALSE); + pgm_return_val_if_fail (NULL != str, FALSE); + + if (length < 0) + length = strlen (str); + + return pgm_gsi_create_from_data (gsi, (const uint8_t*)str, length); +} + +/* create a global session ID as recommended by the PGM draft specification using + * low order 48 bits of md5 of the hostname. + * + * returns TRUE on success, returns FALSE on error and sets error appropriately, + */ + +bool +pgm_gsi_create_from_hostname ( + pgm_gsi_t* restrict gsi, + pgm_error_t** restrict error + ) +{ + pgm_return_val_if_fail (NULL != gsi, FALSE); + + char hostname[NI_MAXHOST]; + int retval = gethostname (hostname, sizeof(hostname)); + if (0 != retval) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("Resolving hostname: %s"), +#ifndef _WIN32 + strerror (errno) +#else + pgm_wsastrerror (WSAGetLastError()) +#endif + ); + return FALSE; + } + + return pgm_gsi_create_from_string (gsi, hostname, -1); +} + +/* create a global session ID based on the IP address. + * + * returns TRUE on succcess, returns FALSE on error and sets error. + */ + +bool +pgm_gsi_create_from_addr ( + pgm_gsi_t* restrict gsi, + pgm_error_t** restrict error + ) +{ + char hostname[NI_MAXHOST]; + struct addrinfo hints, *res = NULL; + + pgm_return_val_if_fail (NULL != gsi, FALSE); + + int retval = gethostname (hostname, sizeof(hostname)); + if (0 != retval) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("Resolving hostname: %s"), +#ifndef _WIN32 + strerror (errno) +#else + pgm_wsastrerror (WSAGetLastError()) +#endif + ); + return FALSE; + } + memset (&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_flags = AI_ADDRCONFIG; + retval = getaddrinfo (hostname, NULL, &hints, &res); + if (0 != retval) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_eai_errno (retval, errno), + _("Resolving hostname address: %s"), + gai_strerror (retval)); + return FALSE; + } + memcpy (gsi, &((struct sockaddr_in*)(res->ai_addr))->sin_addr, sizeof(struct in_addr)); + freeaddrinfo (res); + const uint16_t random_val = pgm_random_int_range (0, UINT16_MAX); + memcpy ((uint8_t*)gsi + sizeof(struct in_addr), &random_val, sizeof(random_val)); + return TRUE; +} + +/* re-entrant form of pgm_gsi_print() + * + * returns number of bytes written to buffer on success, returns -1 on + * invalid parameters. + */ + +int +pgm_gsi_print_r ( + const pgm_gsi_t* restrict gsi, + char* restrict buf, + const size_t bufsize + ) +{ + const uint8_t* restrict src = (const uint8_t* restrict)gsi; + + pgm_return_val_if_fail (NULL != gsi, -1); + pgm_return_val_if_fail (NULL != buf, -1); + pgm_return_val_if_fail (bufsize > 0, -1); + +#ifdef _MSC_VER + return _snprintf_s (buf, bufsize, _TRUNCATE, "%i.%i.%i.%i.%i.%i", + src[0], src[1], src[2], src[3], src[4], src[5]); +#else + return snprintf (buf, bufsize, "%i.%i.%i.%i.%i.%i", + src[0], src[1], src[2], src[3], src[4], src[5]); +#endif +} + +/* transform GSI to ASCII string form. + * + * on success, returns pointer to ASCII string. on error, returns NULL. + */ + +char* +pgm_gsi_print ( + const pgm_gsi_t* gsi + ) +{ + static char buf[PGM_GSISTRLEN]; + + pgm_return_val_if_fail (NULL != gsi, NULL); + + pgm_gsi_print_r (gsi, buf, sizeof(buf)); + return buf; +} + +/* compare two global session identifier GSI values and return TRUE if they are equal + */ + +bool +pgm_gsi_equal ( + const void* restrict p1, + const void* restrict p2 + ) +{ + const union { + pgm_gsi_t gsi; + uint16_t s[3]; + } *u1 = p1, *u2 = p2; + +/* pre-conditions */ + pgm_assert (NULL != p1); + pgm_assert (NULL != p2); + + return (u1->s[0] == u2->s[0] && u1->s[1] == u2->s[1] && u1->s[2] == u2->s[2]); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/gsi.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/gsi.c.c89.patch new file mode 100644 index 0000000..b7edc28 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/gsi.c.c89.patch @@ -0,0 +1,53 @@ +--- gsi.c 2010-08-04 11:14:59.000000000 +0800 ++++ gsi.c89 2010-08-04 11:16:29.000000000 +0800 +@@ -54,12 +54,14 @@ + memcpy (gsi, g_checksum_get_string (checksum) + 10, 6); + g_checksum_free (checksum); + #else ++ { + struct pgm_md5_t ctx; + char resblock[16]; + pgm_md5_init_ctx (&ctx); + pgm_md5_process_bytes (&ctx, data, length); + pgm_md5_finish_ctx (&ctx, resblock); + memcpy (gsi, resblock + 10, 6); ++ } + #endif + return TRUE; + } +@@ -94,6 +96,7 @@ + { + pgm_return_val_if_fail (NULL != gsi, FALSE); + ++ { + char hostname[NI_MAXHOST]; + int retval = gethostname (hostname, sizeof(hostname)); + if (0 != retval) { +@@ -111,6 +114,7 @@ + } + + return pgm_gsi_create_from_string (gsi, hostname, -1); ++ } + } + + /* create a global session ID based on the IP address. +@@ -129,6 +133,7 @@ + + pgm_return_val_if_fail (NULL != gsi, FALSE); + ++ { + int retval = gethostname (hostname, sizeof(hostname)); + if (0 != retval) { + pgm_set_error (error, +@@ -157,8 +162,11 @@ + } + memcpy (gsi, &((struct sockaddr_in*)(res->ai_addr))->sin_addr, sizeof(struct in_addr)); + freeaddrinfo (res); ++ { + const uint16_t random_val = pgm_random_int_range (0, UINT16_MAX); + memcpy ((uint8_t*)gsi + sizeof(struct in_addr), &random_val, sizeof(random_val)); ++ } ++ } + return TRUE; + } + diff --git a/3rdparty/openpgm-svn-r1135/pgm/gsi_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/gsi_unittest.c new file mode 100644 index 0000000..dc4c244 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/gsi_unittest.c @@ -0,0 +1,350 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for global session ID helper functions. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* mock state */ + +static char* mock_localhost = "localhost"; +static char* mock_invalid = "invalid.invalid"; /* RFC 2606 */ +static char* mock_toolong = "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij12345"; /* 65 */ +static char* mock_hostname = NULL; + + +static +void +mock_setup_invalid (void) +{ + mock_hostname = mock_invalid; +} + +static +void +mock_setup_toolong (void) +{ + mock_hostname = mock_toolong; +} + +static +void +mock_setup_localhost (void) +{ + mock_hostname = mock_localhost; +} + +static +void +mock_teardown (void) +{ +// null +} + + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + +int +mock_gethostname ( + char* name, + size_t len + ) +{ + if (mock_hostname == mock_toolong) { + errno = EINVAL; + return -1; + } + strncpy (name, mock_hostname, len); + if (len > 0) + name[len - 1] = '\0'; + return 0; +} + + +#define gethostname mock_gethostname + +#define GSI_DEBUG +#include "gsi.c" + + +/* target: + * bool + * pgm_gsi_create_from_hostname ( + * pgm_gsi_t* gsi, + * pgm_error_t** err + * ) + */ + +START_TEST (test_create_from_hostname_pass_001) +{ + pgm_gsi_t gsi; + pgm_error_t* err = NULL; + fail_unless (pgm_gsi_create_from_hostname (&gsi, &err), "create_from_hostname failed"); + fail_if (err, "error raised"); + fail_unless (pgm_gsi_create_from_hostname (&gsi, NULL), "create_from_hostname failed"); +} +END_TEST + +START_TEST (test_create_from_hostname_pass_002) +{ + pgm_error_t* err = NULL; + fail_if (pgm_gsi_create_from_hostname (NULL, &err), "create_from_hostname failed"); + fail_if (err, "error raised"); + fail_if (pgm_gsi_create_from_hostname (NULL, NULL), "create_from_hostname failed"); +} +END_TEST + +/* hostname too long */ +START_TEST (test_create_from_hostname_pass_003) +{ + pgm_gsi_t gsi; + pgm_error_t* err = NULL; + fail_if (pgm_gsi_create_from_hostname (&gsi, &err), "create_from_hostname failed"); + fail_if (NULL == err, "error not raised"); + fail_if (NULL == err->message, "no error message"); + g_debug ("pgm_error_t: %s", err->message); + fail_if (pgm_gsi_create_from_hostname (&gsi, NULL), "create_from_hostname failed"); +} +END_TEST + +/* target: + * bool + * pgm_gsi_create_from_addr ( + * pgm_gsi_t* gsi, + * pgm_error_t** err + * ) + */ + +START_TEST (test_create_from_addr_pass_001) +{ + pgm_gsi_t gsi; + pgm_error_t* err = NULL; + fail_unless (pgm_gsi_create_from_addr (&gsi, &err), "create_from_addr failed"); + fail_if (err, "error raised"); + fail_unless (pgm_gsi_create_from_addr (&gsi, NULL), "create_from_addr failed"); +} +END_TEST + +START_TEST (test_create_from_addr_pass_002) +{ + pgm_error_t* err = NULL; + fail_if (pgm_gsi_create_from_addr (NULL, &err), "create_from_addr failed"); + fail_if (pgm_gsi_create_from_addr (NULL, NULL), "create_from_addr failed"); +} +END_TEST + +/* invalid hostname */ +START_TEST (test_create_from_addr_pass_003) +{ + pgm_gsi_t gsi; + pgm_error_t* err = NULL; + fail_if (pgm_gsi_create_from_addr (&gsi, &err), "create_from_addr failed"); + fail_if (NULL == err, "error not raised"); + fail_if (NULL == err->message, "no error message"); + g_debug ("pgm_error_t: %s", err->message); + fail_if (pgm_gsi_create_from_addr (&gsi, NULL), "create_from_addr failed"); +} +END_TEST + +/* target: + * char* + * pgm_gsi_print ( + * const pgm_gsi_t* gsi + * ) + */ + +START_TEST (test_print_pass_001) +{ + pgm_gsi_t gsi; + fail_unless (pgm_gsi_create_from_hostname (&gsi, NULL), "create_from_hostname failed"); + fail_if (NULL == pgm_gsi_print (&gsi), "print failed"); +} +END_TEST + +START_TEST (test_print_pass_002) +{ + fail_unless (NULL == pgm_gsi_print (NULL), "print failed"); +} +END_TEST + +/* target: + * int + * pgm_gsi_print_r ( + * const pgm_gsi_t* gsi, + * char* buf, + * size_t bufsize + * ) + */ + +START_TEST (test_print_r_pass_001) +{ + pgm_gsi_t gsi; + char buf[PGM_GSISTRLEN]; + fail_unless (pgm_gsi_create_from_hostname (&gsi, NULL), "create_from_hostname failed"); + fail_unless (pgm_gsi_print_r (&gsi, buf, sizeof(buf)) > 0, "print_r failed"); +} +END_TEST + +START_TEST (test_print_r_pass_002) +{ + pgm_gsi_t gsi; + char buf[PGM_GSISTRLEN]; + fail_unless (pgm_gsi_create_from_hostname (&gsi, NULL), "create_from_hostname failed"); + fail_unless (pgm_gsi_print_r (NULL, buf, sizeof(buf)) == -1, "print_r failed"); + fail_unless (pgm_gsi_print_r (&gsi, NULL, sizeof(buf)) == -1, "print_r failed"); + fail_unless (pgm_gsi_print_r (&gsi, buf, 0) == -1, "print_r failed"); +} +END_TEST + +/* target: + * bool + * pgm_gsi_equal ( + * const void* gsi1, + * const void* gsi2 + * ) + */ + +START_TEST (test_equal_pass_001) +{ + pgm_gsi_t gsi1, gsi2; + fail_unless (pgm_gsi_create_from_hostname (&gsi1, NULL), "create_from_hostname failed"); + fail_unless (pgm_gsi_create_from_hostname (&gsi2, NULL), "create_from_hostname failed"); + fail_unless (pgm_gsi_equal (&gsi1, &gsi2), "equal failed"); +} +END_TEST + +START_TEST (test_equal_pass_002) +{ + pgm_gsi_t gsi1, gsi2; + fail_unless (pgm_gsi_create_from_hostname (&gsi1, NULL), "create_from_hostname failed"); + fail_unless (pgm_gsi_create_from_addr (&gsi2, NULL), "create_from_addr failed"); + fail_if (pgm_gsi_equal (&gsi1, &gsi2), "equal failed"); +} +END_TEST + +START_TEST (test_equal_fail_001) +{ + pgm_gsi_t gsi; + fail_unless (pgm_gsi_create_from_hostname (&gsi, NULL), "create_from_hostname failed"); + gboolean retval = pgm_gsi_equal (NULL, &gsi); + fail ("reached"); +} +END_TEST + +START_TEST (test_equal_fail_002) +{ + pgm_gsi_t gsi; + fail_unless (pgm_gsi_create_from_hostname (&gsi, NULL), "create_from_hostname failed"); + gboolean retval = pgm_gsi_equal (&gsi, NULL); + fail ("reached"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_create_from_hostname = tcase_create ("create-from-hostname"); + suite_add_tcase (s, tc_create_from_hostname); + tcase_add_checked_fixture (tc_create_from_hostname, mock_setup_localhost, mock_teardown); + tcase_add_test (tc_create_from_hostname, test_create_from_hostname_pass_001); + tcase_add_test (tc_create_from_hostname, test_create_from_hostname_pass_002); + + TCase* tc_create_from_hostname2 = tcase_create ("create-from-hostname/2"); + suite_add_tcase (s, tc_create_from_hostname2); + tcase_add_checked_fixture (tc_create_from_hostname2, mock_setup_toolong, mock_teardown); + tcase_add_test (tc_create_from_hostname2, test_create_from_hostname_pass_003); + + TCase* tc_create_from_addr = tcase_create ("create-from-addr"); + suite_add_tcase (s, tc_create_from_addr); + tcase_add_checked_fixture (tc_create_from_addr, mock_setup_localhost, mock_teardown); + tcase_add_test (tc_create_from_addr, test_create_from_addr_pass_001); + tcase_add_test (tc_create_from_addr, test_create_from_addr_pass_002); + + TCase* tc_create_from_addr2 = tcase_create ("create-from-addr/2"); + suite_add_tcase (s, tc_create_from_addr2); + tcase_add_checked_fixture (tc_create_from_addr2, mock_setup_invalid, mock_teardown); + tcase_add_test (tc_create_from_addr2, test_create_from_addr_pass_003); + + TCase* tc_print = tcase_create ("print"); + suite_add_tcase (s, tc_print); + tcase_add_checked_fixture (tc_print, mock_setup_localhost, mock_teardown); + tcase_add_test (tc_print, test_print_pass_001); + tcase_add_test (tc_print, test_print_pass_002); + + TCase* tc_print_r = tcase_create ("print-r"); + suite_add_tcase (s, tc_print_r); + tcase_add_checked_fixture (tc_print_r, mock_setup_localhost, mock_teardown); + tcase_add_test (tc_print_r, test_print_r_pass_001); + tcase_add_test (tc_print_r, test_print_r_pass_002); + + TCase* tc_equal = tcase_create ("equal"); + suite_add_tcase (s, tc_equal); + tcase_add_checked_fixture (tc_equal, mock_setup_localhost, mock_teardown); + tcase_add_test (tc_equal, test_equal_pass_001); + tcase_add_test (tc_equal, test_equal_pass_002); + tcase_add_test_raise_signal (tc_equal, test_equal_fail_001, SIGABRT); + tcase_add_test_raise_signal (tc_equal, test_equal_fail_002, SIGABRT); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/hashtable.c b/3rdparty/openpgm-svn-r1135/pgm/hashtable.c new file mode 100644 index 0000000..7b99cda --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/hashtable.c @@ -0,0 +1,327 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable hashtable. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + + +//#define HASHTABLE_DEBUG + +#define HASHTABLE_MIN_SIZE 11 +#define HASHTABLE_MAX_SIZE 13845163 + +struct pgm_hashnode_t +{ + const void* key; + void* value; + struct pgm_hashnode_t* next; + uint_fast32_t key_hash; +}; + +typedef struct pgm_hashnode_t pgm_hashnode_t; + +struct pgm_hashtable_t +{ + unsigned size; + unsigned nnodes; + pgm_hashnode_t** nodes; + pgm_hashfunc_t hash_func; + pgm_equalfunc_t key_equal_func; +}; + +#define PGM_HASHTABLE_RESIZE(hash_table) \ + do { \ + if ( (hash_table->size >= 3 * hash_table->nnodes && hash_table->size > HASHTABLE_MIN_SIZE) || \ + (3 * hash_table->size <= hash_table->nnodes && hash_table->size < HASHTABLE_MAX_SIZE) ) \ + { \ + pgm_hashtable_resize (hash_table); \ + } \ + } while (0) + +static void pgm_hashtable_resize (pgm_hashtable_t*); +static pgm_hashnode_t** pgm_hashtable_lookup_node (const pgm_hashtable_t*restrict, const void*restrict, pgm_hash_t*restrict) PGM_GNUC_PURE; +static pgm_hashnode_t* pgm_hash_node_new (const void*restrict, void*restrict, const pgm_hash_t); +static void pgm_hash_node_destroy (pgm_hashnode_t*); +static void pgm_hash_nodes_destroy (pgm_hashnode_t*); + + +pgm_hashtable_t* +pgm_hashtable_new ( + pgm_hashfunc_t hash_func, + pgm_equalfunc_t key_equal_func + ) +{ + pgm_return_val_if_fail (NULL != hash_func, NULL); + pgm_return_val_if_fail (NULL != key_equal_func, NULL); + + pgm_hashtable_t *hash_table; + + hash_table = pgm_new (pgm_hashtable_t, 1); + hash_table->size = HASHTABLE_MIN_SIZE; + hash_table->nnodes = 0; + hash_table->hash_func = hash_func; + hash_table->key_equal_func = key_equal_func; + hash_table->nodes = pgm_new0 (pgm_hashnode_t*, hash_table->size); + + return hash_table; +} + +void +pgm_hashtable_unref ( + pgm_hashtable_t* hash_table + ) +{ + pgm_return_if_fail (hash_table != NULL); + + for (unsigned i = 0; i < hash_table->size; i++) + pgm_hash_nodes_destroy (hash_table->nodes[i]); + pgm_free (hash_table->nodes); + pgm_free (hash_table); +} + +void +pgm_hashtable_destroy ( + pgm_hashtable_t* hash_table + ) +{ + pgm_return_if_fail (hash_table != NULL); + + pgm_hashtable_remove_all (hash_table); + pgm_hashtable_unref (hash_table); +} + +static inline +pgm_hashnode_t** +pgm_hashtable_lookup_node ( + const pgm_hashtable_t* restrict hash_table, + const void* restrict key, + pgm_hash_t* restrict hash_return /* non-NULL to return hash value */ + ) +{ + const pgm_hash_t hash_value = (*hash_table->hash_func) (key); + pgm_hashnode_t** node = &hash_table->nodes[hash_value % hash_table->size]; + + if (hash_return) + *hash_return = hash_value; + + while (*node && (((*node)->key_hash != hash_value) || + !(*hash_table->key_equal_func) ((*node)->key, key))) + { + node = &(*node)->next; + } + + return node; +} + +void* +pgm_hashtable_lookup ( + const pgm_hashtable_t* restrict hash_table, + const void* restrict key + ) +{ + pgm_return_val_if_fail (hash_table != NULL, NULL); + + const pgm_hashnode_t* node = *pgm_hashtable_lookup_node (hash_table, key, NULL); + return node ? node->value : NULL; +} + +void* +pgm_hashtable_lookup_extended ( + const pgm_hashtable_t* restrict hash_table, + const void* restrict key, + void* restrict hash_return + ) +{ + pgm_return_val_if_fail (hash_table != NULL, NULL); + + const pgm_hashnode_t* node = *pgm_hashtable_lookup_node (hash_table, key, hash_return); + return node ? node->value : NULL; +} + +void +pgm_hashtable_insert ( + pgm_hashtable_t* restrict hash_table, + const void* restrict key, + void* restrict value + ) +{ + pgm_hashnode_t **node; + pgm_hash_t key_hash; + + pgm_return_if_fail (hash_table != NULL); + + node = pgm_hashtable_lookup_node (hash_table, key, &key_hash); + pgm_return_if_fail (NULL == *node); + + *node = pgm_hash_node_new (key, value, key_hash); + hash_table->nnodes++; + PGM_HASHTABLE_RESIZE (hash_table); +} + +bool +pgm_hashtable_remove ( + pgm_hashtable_t* restrict hash_table, + const void* restrict key + ) +{ + pgm_hashnode_t **node, *dest; + + pgm_return_val_if_fail (hash_table != NULL, FALSE); + + node = pgm_hashtable_lookup_node (hash_table, key, NULL); + if (*node) + { + dest = *node; + (*node) = dest->next; + pgm_hash_node_destroy (dest); + hash_table->nnodes--; + PGM_HASHTABLE_RESIZE (hash_table); + return TRUE; + } + return FALSE; +} + +void +pgm_hashtable_remove_all ( + pgm_hashtable_t* hash_table + ) +{ + pgm_return_if_fail (hash_table != NULL); + + for (unsigned i = 0; i < hash_table->size; i++) + { + pgm_hash_nodes_destroy (hash_table->nodes[i]); + hash_table->nodes[i] = NULL; + } + hash_table->nnodes = 0; + PGM_HASHTABLE_RESIZE (hash_table); +} + +static +void +pgm_hashtable_resize ( + pgm_hashtable_t* hash_table + ) +{ + const unsigned new_size = CLAMP (pgm_spaced_primes_closest (hash_table->nnodes), + HASHTABLE_MIN_SIZE, HASHTABLE_MAX_SIZE); + pgm_hashnode_t** new_nodes = pgm_new0 (pgm_hashnode_t*, new_size); + + for (unsigned i = 0; i < hash_table->size; i++) + for (pgm_hashnode_t *node = hash_table->nodes[i], *next; node; node = next) + { + next = node->next; + const pgm_hash_t hash_val = node->key_hash % new_size; + node->next = new_nodes[hash_val]; + new_nodes[hash_val] = node; + } + + pgm_free (hash_table->nodes); + hash_table->nodes = new_nodes; + hash_table->size = new_size; +} + +static +pgm_hashnode_t* +pgm_hash_node_new ( + const void* restrict key, + void* restrict value, + const pgm_hash_t key_hash + ) +{ + pgm_hashnode_t *hash_node = pgm_new (pgm_hashnode_t, 1); + hash_node->key = key; + hash_node->value = value; + hash_node->key_hash = key_hash; + hash_node->next = NULL; + return hash_node; +} + +static +void +pgm_hash_node_destroy ( + pgm_hashnode_t* hash_node + ) +{ + pgm_free (hash_node); +} + +static +void +pgm_hash_nodes_destroy ( + pgm_hashnode_t* hash_node + ) +{ + while (hash_node) { + pgm_hashnode_t *next = hash_node->next; + pgm_free (hash_node); + hash_node = next; + } +} + +/* common hash value compare and hash key generation functions */ + +bool +pgm_str_equal ( + const void* restrict p1, + const void* restrict p2 + ) +{ + const char *restrict s1 = p1, *restrict s2 = p2; + return (strcmp (s1, s2) == 0); +} + +/* 31 bit hash function */ + +pgm_hash_t +pgm_str_hash ( + const void* p + ) +{ + const char* s = p; + pgm_hash_t hash_val = *s; + + if (PGM_LIKELY (hash_val)) + for (s++; *s; s++) + hash_val = (hash_val << 5) - hash_val + *s; + return hash_val; +} + +bool +pgm_int_equal ( + const void* restrict p1, + const void* restrict p2 + ) +{ + const int i1 = *(const int*restrict)p1, i2 = *(const int*restrict)p2; + return (i1 == i2); +} + +pgm_hash_t +pgm_int_hash ( + const void* p + ) +{ + const int i = *(const int*)p; + return (pgm_hash_t)i; +} + + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/hashtable.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/hashtable.c.c89.patch new file mode 100644 index 0000000..d4d7406 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/hashtable.c.c89.patch @@ -0,0 +1,47 @@ +73a74 +> { +83a85 +> } +93c95,97 +< for (unsigned i = 0; i < hash_table->size; i++) +--- +> { +> unsigned i; +> for (i = 0; i < hash_table->size; i++) +94a99 +> } +140a146 +> { +142a149 +> } +153a161 +> { +155a164 +> } +208c217,219 +< for (unsigned i = 0; i < hash_table->size; i++) +--- +> { +> unsigned i; +> for (i = 0; i < hash_table->size; i++) +212a224 +> } +226,228c238,244 +< +< for (unsigned i = 0; i < hash_table->size; i++) +< for (pgm_hashnode_t *node = hash_table->nodes[i], *next; node; node = next) +--- +> +> { +> unsigned i; +> for (i = 0; i < hash_table->size; i++) +> { +> pgm_hashnode_t *node, *next; +> for (node = hash_table->nodes[i]; node; node = next) +230a247 +> { +233a251,252 +> } +> } +234a254 +> } diff --git a/3rdparty/openpgm-svn-r1135/pgm/histogram.c b/3rdparty/openpgm-svn-r1135/pgm/histogram.c new file mode 100644 index 0000000..3e5ad66 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/histogram.c @@ -0,0 +1,414 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Histograms. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + + +//#define HISTOGRAM_DEBUG + + +pgm_slist_t* pgm_histograms = NULL; + + +static void sample_set_accumulate (pgm_sample_set_t*, pgm_sample_t, pgm_count_t, unsigned); +static pgm_count_t sample_set_total_count (const pgm_sample_set_t*) PGM_GNUC_PURE; + +static void set_bucket_range (pgm_histogram_t*, unsigned, pgm_sample_t); +static void initialize_bucket_range (pgm_histogram_t*); +static unsigned bucket_index (const pgm_histogram_t*, const pgm_sample_t); +static void accumulate (pgm_histogram_t*, pgm_sample_t, pgm_count_t, unsigned); +static double get_peak_bucket_size (const pgm_histogram_t*restrict, const pgm_sample_set_t*restrict); +static double get_bucket_size (const pgm_histogram_t*, const pgm_count_t, const unsigned); + +static void pgm_histogram_write_html_graph (pgm_histogram_t*restrict, pgm_string_t*restrict); +static void write_ascii (pgm_histogram_t*restrict, const char*restrict, pgm_string_t*restrict); +static void write_ascii_header (pgm_histogram_t*restrict, pgm_sample_set_t*restrict, pgm_count_t, pgm_string_t*restrict); +static void write_ascii_bucket_graph (double, double, pgm_string_t*); +static void write_ascii_bucket_context (int64_t, pgm_count_t, int64_t, unsigned, pgm_string_t*); +static void write_ascii_bucket_value (pgm_count_t, double, pgm_string_t*); +static pgm_string_t* get_ascii_bucket_range (pgm_histogram_t*, unsigned); + + +void +pgm_histogram_add ( + pgm_histogram_t* histogram, + int value + ) +{ + if (value > INT_MAX) + value = INT_MAX - 1; + if (value < 0) + value = 0; + const unsigned i = bucket_index (histogram, value); + pgm_assert (value >= histogram->ranges[ i ]); + pgm_assert (value < histogram->ranges[ i + 1 ]); + accumulate (histogram, value, 1, i); +} + +void +pgm_histogram_write_html_graph_all ( + pgm_string_t* string + ) +{ + if (!pgm_histograms) + return; + pgm_slist_t* snapshot = pgm_histograms; + while (snapshot) { + pgm_histogram_t* histogram = snapshot->data; + pgm_histogram_write_html_graph (histogram, string); + snapshot = snapshot->next; + } +} + +static +void +pgm_histogram_write_html_graph ( + pgm_histogram_t* histogram, + pgm_string_t* string + ) +{ + pgm_string_append (string, "
");
+	write_ascii (histogram, "
", string); + pgm_string_append (string, "
"); +} + +static +void +sample_set_accumulate ( + pgm_sample_set_t* sample_set, + pgm_sample_t value, + pgm_count_t count, + unsigned i + ) +{ + pgm_assert (1 == count || -1 == count); + sample_set->counts[ i ] += count; + sample_set->sum += count * value; + sample_set->square_sum += (count * value) * (int64_t)value; + pgm_assert (sample_set->counts[ i ] >= 0); + pgm_assert (sample_set->sum >= 0); + pgm_assert (sample_set->square_sum >= 0); +} + +static +pgm_count_t +sample_set_total_count ( + const pgm_sample_set_t* sample_set + ) +{ + pgm_count_t total = 0; + for (unsigned i = 0; i < sample_set->counts_len; i++) + total += sample_set->counts[ i ]; + return total; +} + +void +pgm_histogram_init ( + pgm_histogram_t* histogram + ) +{ + if (histogram->declared_min <= 0) + histogram->declared_min = 1; + pgm_assert (histogram->declared_min > 0); + histogram->declared_max = INT_MAX - 1; + pgm_assert (histogram->declared_min <= histogram->declared_max); + pgm_assert (1 < histogram->bucket_count); + set_bucket_range (histogram, histogram->bucket_count, INT_MAX); + initialize_bucket_range (histogram); + +/* register with global list */ + histogram->histograms_link.data = histogram; + histogram->histograms_link.next = pgm_histograms; + pgm_histograms = &histogram->histograms_link; + histogram->is_registered = TRUE; +} + +static +void +set_bucket_range ( + pgm_histogram_t* histogram, + unsigned i, + pgm_sample_t value + ) +{ + histogram->ranges[ i ] = value; +} + +static +void +initialize_bucket_range ( + pgm_histogram_t* histogram + ) +{ + const double log_max = log(histogram->declared_max); + double log_ratio; + double log_next; + unsigned i = 1; + pgm_sample_t current = histogram->declared_min; + + set_bucket_range (histogram, i, current); + while (histogram->bucket_count > ++i) { + double log_current = log(current); + log_ratio = (log_max - log_current) / (histogram->bucket_count - i); + log_next = log_current + log_ratio; + int next = floor(exp(log_next) + 0.5); + if (next > current) + current = next; + else + current++; + set_bucket_range (histogram, i, current); + } + pgm_assert (histogram->bucket_count == i); +} + +static +unsigned +bucket_index ( + const pgm_histogram_t* histogram, + const pgm_sample_t value + ) +{ + pgm_assert (histogram->ranges[0] <= value); + pgm_assert (histogram->ranges[ histogram->bucket_count ] > value); + unsigned under = 0; + unsigned over = histogram->bucket_count; + unsigned mid; + + do { + pgm_assert (over >= under); + mid = ((unsigned)under + (unsigned)over) >> 1; + if (mid == under) + break; + if (histogram->ranges[ mid ] <= value) + under = mid; + else + over = mid; + } while (TRUE); + pgm_assert (histogram->ranges[ mid ] <= value && + histogram->ranges[ mid + 1] > value); + return mid; +} + +static +void +accumulate ( + pgm_histogram_t* histogram, + pgm_sample_t value, + pgm_count_t count, + unsigned i + ) +{ + sample_set_accumulate (&histogram->sample, value, count, i); +} + +static +void +write_ascii ( + pgm_histogram_t* restrict histogram, + const char* restrict newline, + pgm_string_t* restrict output + ) +{ + pgm_count_t snapshot_counts[ histogram->sample.counts_len ]; + pgm_sample_set_t snapshot = { + .counts = snapshot_counts, + .counts_len = histogram->sample.counts_len, + .sum = histogram->sample.sum, + .square_sum = histogram->sample.square_sum + }; + memcpy (snapshot_counts, histogram->sample.counts, sizeof(pgm_count_t) * histogram->sample.counts_len); + + pgm_count_t sample_count = sample_set_total_count (&snapshot); + write_ascii_header (histogram, &snapshot, sample_count, output); + pgm_string_append (output, newline); + + double max_size = get_peak_bucket_size (histogram, &snapshot); + unsigned largest_non_empty_bucket = histogram->bucket_count - 1; + while (0 == snapshot.counts[ largest_non_empty_bucket ]) + { + if (0 == largest_non_empty_bucket) + break; + largest_non_empty_bucket--; + } + + int print_width = 1; + for (unsigned i = 0; i < histogram->bucket_count; ++i) + { + if (snapshot.counts[ i ]) { + pgm_string_t* bucket_range = get_ascii_bucket_range (histogram, i); + const int width = bucket_range->len + 1; + pgm_string_free (bucket_range, TRUE); + if (width > print_width) + print_width = width; + } + } + + int64_t remaining = sample_count; + int64_t past = 0; + for (unsigned i = 0; i < histogram->bucket_count; ++i) + { + pgm_count_t current = snapshot.counts[ i ]; + remaining -= current; + pgm_string_t* bucket_range = get_ascii_bucket_range (histogram, i); + pgm_string_append_printf (output, "%*s ", print_width, bucket_range->str); + pgm_string_free (bucket_range, TRUE); + if (0 == current && + i < histogram->bucket_count - 1 && + 0 == snapshot.counts[ i + 1 ]) + { + while (i < histogram->bucket_count - 1 && + 0 == snapshot.counts[ i + 1 ]) + { + i++; + } + pgm_string_append (output, "... "); + pgm_string_append (output, newline); + continue; + } + + const double current_size = get_bucket_size (histogram, current, i); + write_ascii_bucket_graph (current_size, max_size, output); + write_ascii_bucket_context (past, current, remaining, i, output); + pgm_string_append (output, newline); + past += current; + } +} + +static +void +write_ascii_header ( + pgm_histogram_t* restrict histogram, + pgm_sample_set_t* restrict sample_set, + pgm_count_t sample_count, + pgm_string_t* restrict output + ) +{ + pgm_string_append_printf (output, + "Histogram: %s recorded %d samples", + histogram->histogram_name ? histogram->histogram_name : "(null)", + sample_count); + if (sample_count > 0) { + const double average = sample_set->sum / sample_count; + const double variance = sample_set->square_sum / sample_count + - average * average; + const double standard_deviation = sqrt (variance); + pgm_string_append_printf (output, + ", average = %.1f, standard deviation = %.1f", + average, standard_deviation); + } +} + +static +void +write_ascii_bucket_graph ( + double current_size, + double max_size, + pgm_string_t* output + ) +{ + static const int k_line_length = 72; + int x_count = (k_line_length * (current_size / max_size) + 0.5); + int x_remainder = k_line_length - x_count; + while (0 < x_count--) + pgm_string_append_c (output, '-'); + pgm_string_append_c (output, 'O'); + while (0 < x_remainder--) + pgm_string_append_c (output, ' '); +} + +static +void +write_ascii_bucket_context ( + int64_t past, + pgm_count_t current, + int64_t remaining, + unsigned i, + pgm_string_t* output + ) +{ + const double scaled_sum = (past + current + remaining) / 100.0; + write_ascii_bucket_value (current, scaled_sum, output); + if (0 < i) { + const double percentage = past / scaled_sum; + pgm_string_append_printf (output, " {%3.1f%%}", percentage); + } +} + +static +void +write_ascii_bucket_value ( + pgm_count_t current, + double scaled_sum, + pgm_string_t* output + ) +{ + pgm_string_append_printf (output, " (%d = %3.1f%%)", current, current/scaled_sum); +} + +static +double +get_peak_bucket_size ( + const pgm_histogram_t* restrict histogram, + const pgm_sample_set_t* restrict sample_set + ) +{ + double max_size = 0; + for (unsigned i = 0; i < histogram->bucket_count; i++) { + const double current_size = get_bucket_size (histogram, sample_set->counts[ i ], i); + if (current_size > max_size) + max_size = current_size; + } + return max_size; +} + +static +double +get_bucket_size ( + const pgm_histogram_t* histogram, + const pgm_count_t current, + const unsigned i + ) +{ + pgm_assert (histogram->ranges[ i + 1 ] > histogram->ranges[ i ]); + static const double kTransitionWidth = 5; + double denominator = histogram->ranges[ i + 1 ] - histogram->ranges[ i ]; + if (denominator > kTransitionWidth) + denominator = kTransitionWidth; + return current / denominator; +} + +static +pgm_string_t* +get_ascii_bucket_range ( + pgm_histogram_t* histogram, + unsigned i + ) +{ + pgm_string_t* result = pgm_string_new (NULL); + pgm_string_printf (result, "%d", histogram->ranges[ i ]); + return result; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/histogram.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/histogram.c.c89.patch new file mode 100644 index 0000000..a5826fd --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/histogram.c.c89.patch @@ -0,0 +1,212 @@ +--- histogram.c 2010-05-21 11:35:50.000000000 +0800 ++++ histogram.c89 2010-08-04 11:32:46.000000000 +0800 +@@ -61,10 +61,12 @@ + value = INT_MAX - 1; + if (value < 0) + value = 0; ++ { + const unsigned i = bucket_index (histogram, value); + pgm_assert (value >= histogram->ranges[ i ]); + pgm_assert (value < histogram->ranges[ i + 1 ]); + accumulate (histogram, value, 1, i); ++ } + } + + void +@@ -74,12 +76,14 @@ + { + if (!pgm_histograms) + return; ++ { + pgm_slist_t* snapshot = pgm_histograms; + while (snapshot) { + pgm_histogram_t* histogram = snapshot->data; + pgm_histogram_write_html_graph (histogram, string); + snapshot = snapshot->next; + } ++ } + } + + static +@@ -119,8 +123,11 @@ + ) + { + pgm_count_t total = 0; +- for (unsigned i = 0; i < sample_set->counts_len; i++) ++ { ++ unsigned i; ++ for (i = 0; i < sample_set->counts_len; i++) + total += sample_set->counts[ i ]; ++ } + return total; + } + +@@ -173,12 +180,15 @@ + double log_current = log(current); + log_ratio = (log_max - log_current) / (histogram->bucket_count - i); + log_next = log_current + log_ratio; +- int next = floor(exp(log_next) + 0.5); ++ { ++/* force cast to ignore float to int conversion warning */ ++ int next = (int)floor(exp(log_next) + 0.5); + if (next > current) + current = next; + else + current++; + set_bucket_range (histogram, i, current); ++ } + } + pgm_assert (histogram->bucket_count == i); + } +@@ -192,6 +202,7 @@ + { + pgm_assert (histogram->ranges[0] <= value); + pgm_assert (histogram->ranges[ histogram->bucket_count ] > value); ++ { + unsigned under = 0; + unsigned over = histogram->bucket_count; + unsigned mid; +@@ -209,6 +220,7 @@ + pgm_assert (histogram->ranges[ mid ] <= value && + histogram->ranges[ mid + 1] > value); + return mid; ++ } + } + + static +@@ -231,19 +243,21 @@ + pgm_string_t* restrict output + ) + { +- pgm_count_t snapshot_counts[ histogram->sample.counts_len ]; +- pgm_sample_set_t snapshot = { +- .counts = snapshot_counts, +- .counts_len = histogram->sample.counts_len, +- .sum = histogram->sample.sum, +- .square_sum = histogram->sample.square_sum +- }; ++ pgm_count_t* snapshot_counts = pgm_newa (pgm_count_t, histogram->sample.counts_len); ++ pgm_sample_set_t snapshot; ++ snapshot.counts = snapshot_counts; ++ snapshot.counts_len = histogram->sample.counts_len; ++ snapshot.sum = histogram->sample.sum; ++ snapshot.square_sum = histogram->sample.square_sum; ++ + memcpy (snapshot_counts, histogram->sample.counts, sizeof(pgm_count_t) * histogram->sample.counts_len); + ++ { + pgm_count_t sample_count = sample_set_total_count (&snapshot); + write_ascii_header (histogram, &snapshot, sample_count, output); + pgm_string_append (output, newline); + ++ { + double max_size = get_peak_bucket_size (histogram, &snapshot); + unsigned largest_non_empty_bucket = histogram->bucket_count - 1; + while (0 == snapshot.counts[ largest_non_empty_bucket ]) +@@ -253,8 +267,11 @@ + largest_non_empty_bucket--; + } + ++ { + int print_width = 1; +- for (unsigned i = 0; i < histogram->bucket_count; ++i) ++ { ++ unsigned i; ++ for (i = 0; i < histogram->bucket_count; ++i) + { + if (snapshot.counts[ i ]) { + pgm_string_t* bucket_range = get_ascii_bucket_range (histogram, i); +@@ -264,16 +281,22 @@ + print_width = width; + } + } ++ } + ++ { + int64_t remaining = sample_count; + int64_t past = 0; +- for (unsigned i = 0; i < histogram->bucket_count; ++i) ++ { ++ unsigned i; ++ for (i = 0; i < histogram->bucket_count; ++i) + { + pgm_count_t current = snapshot.counts[ i ]; + remaining -= current; ++ { + pgm_string_t* bucket_range = get_ascii_bucket_range (histogram, i); + pgm_string_append_printf (output, "%*s ", print_width, bucket_range->str); + pgm_string_free (bucket_range, TRUE); ++ } + if (0 == current && + i < histogram->bucket_count - 1 && + 0 == snapshot.counts[ i + 1 ]) +@@ -288,12 +311,19 @@ + continue; + } + ++ { + const double current_size = get_bucket_size (histogram, current, i); + write_ascii_bucket_graph (current_size, max_size, output); ++ } + write_ascii_bucket_context (past, current, remaining, i, output); + pgm_string_append (output, newline); + past += current; + } ++ } ++ } ++ } ++ } ++ } + } + + static +@@ -310,8 +340,8 @@ + histogram->histogram_name ? histogram->histogram_name : "(null)", + sample_count); + if (sample_count > 0) { +- const double average = sample_set->sum / sample_count; +- const double variance = sample_set->square_sum / sample_count ++ const double average = (double)sample_set->sum / sample_count; ++ const double variance = (double)sample_set->square_sum / sample_count + - average * average; + const double standard_deviation = sqrt (variance); + pgm_string_append_printf (output, +@@ -329,7 +359,7 @@ + ) + { + static const int k_line_length = 72; +- int x_count = (k_line_length * (current_size / max_size) + 0.5); ++ int x_count = (int)(k_line_length * (current_size / max_size) + 0.5); + int x_remainder = k_line_length - x_count; + while (0 < x_count--) + pgm_string_append_c (output, '-'); +@@ -375,11 +405,14 @@ + ) + { + double max_size = 0; +- for (unsigned i = 0; i < histogram->bucket_count; i++) { ++ { ++ unsigned i; ++ for (i = 0; i < histogram->bucket_count; i++) { + const double current_size = get_bucket_size (histogram, sample_set->counts[ i ], i); + if (current_size > max_size) + max_size = current_size; + } ++ } + return max_size; + } + +@@ -392,11 +425,13 @@ + ) + { + pgm_assert (histogram->ranges[ i + 1 ] > histogram->ranges[ i ]); ++ { + static const double kTransitionWidth = 5; + double denominator = histogram->ranges[ i + 1 ] - histogram->ranges[ i ]; + if (denominator > kTransitionWidth) + denominator = kTransitionWidth; + return current / denominator; ++ } + } + + static diff --git a/3rdparty/openpgm-svn-r1135/pgm/htdocs/404.html b/3rdparty/openpgm-svn-r1135/pgm/htdocs/404.html new file mode 100644 index 0000000..538c90a --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/htdocs/404.html @@ -0,0 +1,11 @@ + + + + OpenPGM - Page Not Found + + + +

Lah, page not found.

+

Return to main page

+ + diff --git a/3rdparty/openpgm-svn-r1135/pgm/htdocs/base.css b/3rdparty/openpgm-svn-r1135/pgm/htdocs/base.css new file mode 100644 index 0000000..5aba236 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/htdocs/base.css @@ -0,0 +1,136 @@ +html { + background-color: white; + font-family: Verdana; + font-size: 12px; + color: black; +} + +a, a:link, a:visited { + color: #0033cc; + text-decoration: none; +} + +#header { + text-align: right; +} + +#header #hostname { + font-weight: bold; +} + +#header a { + color: black; +} + +#header a:hover { + text-decoration: underline; +} + +#footer { + clear: both; + margin-top: 3.5em; + margin-bottom: 1em; + padding-top: 20px; + text-align: center; +} + +#navigation a { + color: black; +} + +#navigation .tab { + -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; + padding: 4px 1em 2px; + margin-right: 8px; + float: left; + font-weight: bold; +} + +#navigation #tabtop,#tabline { + background-color: #fb879c; +} + +#navigation #tabbottom { + background-color: #fbc1a9; +} + +#navigation #tabline { + clear: left; + -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; + height: 4px; +} + +#content { + margin-top: 6px; + padding: 3px; +} + +#content a:hover { + background: #ffffaa; +} + +.heading { + -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; + background-color: #fb879c; + padding: 6px; + margin-bottom: 3px; +} + +table { + border-collapse: separate; +} + +th { + text-align: left; +} + +#information { + float: right; +} + +.rounded { + background-color: #fbc1a9; + -moz-border-radius: 4px; -webkit-border-radius: 4px; + padding: 5px; + margin-top: 6px; + margin-bottom: 6px; + width: 25em; +} + +.break { + border-top: 3px solid white; + margin-top: 1em; + padding-top: 6px; +} + +.bubbly { + background-color: #fb879c; + -moz-border-radius: 4px; -webkit-border-radius: 4px; + padding: 4px; +} + +.bubbly table { + width: 100%; +} + +.bubbly th,.bubbly td { + border-bottom: 1px solid #bbbbbb; +} + +.bubbly th { + background-color: #fbc1a9; + border-left: 1px solid #bbbbbb; + padding: 2px 1px 2px 2px; +} + +.bubbly td { + background-color: white; + padding: 4px; +} + +.bubbly .empty { + padding: 3em; + text-align: center; +} diff --git a/3rdparty/openpgm-svn-r1135/pgm/htdocs/convert_to_macro.pl b/3rdparty/openpgm-svn-r1135/pgm/htdocs/convert_to_macro.pl new file mode 100755 index 0000000..bea44af --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/htdocs/convert_to_macro.pl @@ -0,0 +1,20 @@ +#!/usr/bin/perl + +use strict; +use File::Basename; + +die "usage: $0 [text file]\n" unless ($ARGV[0]); +open(MOO, $ARGV[0]) or die "cannot open $ARGV[0]: $!"; +my $all = do { local $/; }; +close(MOO); +$all =~ s/"/\\"/g; +$all =~ s/\n/\\n/mg; +$all =~ s/\r/\\r/mg; + +my $var = uc (basename($ARGV[0])); +$var =~ s/\s+/_/g; +$var =~ s/\./_/g; + +print< + + diff --git a/3rdparty/openpgm-svn-r1135/pgm/http.c b/3rdparty/openpgm-svn-r1135/pgm/http.c new file mode 100644 index 0000000..442f6f3 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/http.c @@ -0,0 +1,1735 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * HTTP administrative interface + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#ifndef _WIN32 +# include +#else +# include +# include +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pgm/http.h" +#include "htdocs/404.html.h" +#include "htdocs/base.css.h" +#include "htdocs/robots.txt.h" +#include "htdocs/xhtml10_strict.doctype.h" + + +/* OpenSolaris */ +#ifndef LOGIN_NAME_MAX +# ifdef _WIN32 +# define LOGIN_NAME_MAX (UNLEN + 1) +# else +# define LOGIN_NAME_MAX 256 +# endif +#endif + +#ifdef _WIN32 +# define getpid _getpid +# define read _read +# define write _write +# define SHUT_WR SD_SEND +#endif + +#ifdef CONFIG_HAVE_SPRINTF_GROUPING +# define GROUP_FORMAT "'" +#else +# define GROUP_FORMAT "" +#endif + +#define HTTP_BACKLOG 10 /* connections */ +#define HTTP_TIMEOUT 60 /* seconds */ + + +/* locals */ + +struct http_connection_t { + pgm_list_t link_; + int sock; + enum { + HTTP_STATE_READ, + HTTP_STATE_WRITE, + HTTP_STATE_FINWAIT + } state; + + char* buf; + size_t buflen; + size_t bufoff; + unsigned status_code; + const char* status_text; + const char* content_type; +}; + +enum { + HTTP_MEMORY_STATIC, + HTTP_MEMORY_TAKE +}; + +static char http_hostname[NI_MAXHOST + 1]; +static char http_address[INET6_ADDRSTRLEN]; +static char http_username[LOGIN_NAME_MAX + 1]; +static int http_pid; + +#ifndef _WIN32 +static int http_sock = -1; +static pthread_t http_thread; +static void* http_routine (void*); +#else +static int http_sock = INVALID_SOCKET; +static HANDLE http_thread; +static unsigned __stdcall http_routine (void*); +#endif +static int http_max_sock = -1; +static fd_set http_readfds, http_writefds, http_exceptfds; +static pgm_list_t* http_socks = NULL; +static pgm_notify_t http_notify = PGM_NOTIFY_INIT; +static volatile uint32_t http_ref_count = 0; + + +static int http_tsi_response (struct http_connection_t*, pgm_tsi_t*); +static void http_each_receiver (pgm_peer_t*, pgm_string_t*); +static int http_receiver_response (struct http_connection_t*, pgm_peer_t*); + +static void default_callback (struct http_connection_t*, const char*); +static void robots_callback (struct http_connection_t*, const char*); +static void css_callback (struct http_connection_t*, const char*); +static void index_callback (struct http_connection_t*, const char*); +static void interfaces_callback (struct http_connection_t*, const char*); +static void transports_callback (struct http_connection_t*, const char*); +static void histograms_callback (struct http_connection_t*, const char*); + +static struct { + const char* path; + void (*callback) (struct http_connection_t*, const char*); +} http_directory[] = { + { "/robots.txt", robots_callback }, + { "/base.css", css_callback }, + { "/", index_callback }, + { "/interfaces", interfaces_callback }, + { "/transports", transports_callback } +#ifdef CONFIG_HISTOGRAMS + ,{ "/histograms", histograms_callback } +#endif +}; + + +static +int +http_sock_rcvtimeo ( + int sock, + int seconds + ) +{ +#if defined( sun ) + return 0; +#elif !defined( _WIN32 ) + const struct timeval timeout = { .tv_sec = seconds, .tv_usec = 0 }; + return setsockopt (sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)); +#else + const int optval = seconds * 1000; + return setsockopt (sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&optval, sizeof(optval)); +#endif +} + +static +int +http_sock_sndtimeo ( + int sock, + int seconds + ) +{ +#if defined( sun ) + return 0; +#elif !defined( _WIN32 ) + const struct timeval timeout = { .tv_sec = seconds, .tv_usec = 0 }; + return setsockopt (sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout)); +#else + const int optval = seconds * 1000; + return setsockopt (sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&optval, sizeof(optval)); +#endif +} + +bool +pgm_http_init ( + uint16_t http_port, + pgm_error_t** error + ) +{ + int e; + + if (pgm_atomic_exchange_and_add32 (&http_ref_count, 1) > 0) + return TRUE; + +/* resolve and store relatively constant runtime information */ + if (0 != gethostname (http_hostname, sizeof(http_hostname))) { + const int save_errno = errno; + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_errno (save_errno), + _("Resolving hostname: %s"), + strerror (save_errno)); + goto err_cleanup; + } + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, + .ai_protocol = IPPROTO_TCP, + .ai_flags = AI_ADDRCONFIG + }, *res = NULL; + e = getaddrinfo (http_hostname, NULL, &hints, &res); + if (0 != e) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_eai_errno (e, errno), + _("Resolving hostname address: %s"), + gai_strerror (e)); + goto err_cleanup; + } + e = getnameinfo (res->ai_addr, res->ai_addrlen, + http_address, sizeof(http_address), + NULL, 0, + NI_NUMERICHOST); + if (0 != e) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_eai_errno (e, errno), + _("Resolving numeric hostname: %s"), + gai_strerror (e)); + goto err_cleanup; + } + freeaddrinfo (res); +#ifndef _WIN32 + e = getlogin_r (http_username, sizeof(http_username)); + if (0 != e) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_errno (errno), + _("Retrieving user name: %s"), + strerror (errno)); + goto err_cleanup; + } +#else + wchar_t wusername[UNLEN + 1]; + DWORD nSize = PGM_N_ELEMENTS( wusername ); + if (!GetUserNameW (wusername, &nSize)) { + const DWORD save_errno = GetLastError(); + char winstr[1024]; + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_win_errno (save_errno), + _("Retrieving user name: %s"), + pgm_win_strerror (winstr, sizeof(winstr), save_errno)); + goto err_cleanup; + } + WideCharToMultiByte (CP_UTF8, 0, wusername, nSize + 1, http_username, sizeof(http_username), NULL, NULL); +#endif /* _WIN32 */ + http_pid = getpid(); + +/* create HTTP listen socket */ + if ((http_sock = socket (AF_INET, SOCK_STREAM, 0)) < 0) { +#ifndef _WIN32 + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_errno (errno), + _("Creating HTTP socket: %s"), + strerror (errno)); +#else + const int save_errno = WSAGetLastError(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_wsa_errno (save_errno), + _("Creating HTTP socket: %s"), + pgm_wsastrerror (save_errno)); +#endif + goto err_cleanup; + } + const int v = 1; + if (0 != setsockopt (http_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&v, sizeof(v))) { +#ifndef _WIN32 + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_errno (errno), + _("Enabling reuse of socket local address: %s"), + strerror (errno)); +#else + const int save_errno = WSAGetLastError(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_wsa_errno (save_errno), + _("Enabling reuse of socket local address: %s"), + pgm_wsastrerror (save_errno)); +#endif + goto err_cleanup; + } + if (0 != http_sock_rcvtimeo (http_sock, HTTP_TIMEOUT) || + 0 != http_sock_sndtimeo (http_sock, HTTP_TIMEOUT)) { +#ifndef _WIN32 + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_errno (errno), + _("Setting socket timeout: %s"), + strerror (errno)); +#else + const int save_errno = WSAGetLastError(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_wsa_errno (save_errno), + _("Setting socket timeout: %s"), + pgm_wsastrerror (save_errno)); +#endif + goto err_cleanup; + } + struct sockaddr_in http_addr; + memset (&http_addr, 0, sizeof(http_addr)); + http_addr.sin_family = AF_INET; + http_addr.sin_addr.s_addr = INADDR_ANY; + http_addr.sin_port = htons (http_port); + if (0 != bind (http_sock, (struct sockaddr*)&http_addr, sizeof(http_addr))) { + char addr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&http_addr, addr, sizeof(addr)); +#ifndef _WIN32 + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_errno (errno), + _("Binding HTTP socket to address %s: %s"), + addr, + strerror (errno)); +#else + const int save_errno = WSAGetLastError(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_wsa_errno (save_errno), + _("Binding HTTP socket to address %s: %s"), + addr, + pgm_wsastrerror (save_errno)); +#endif + goto err_cleanup; + } + if (listen (http_sock, HTTP_BACKLOG) < 0) { +#ifndef _WIN32 + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_errno (errno), + _("Listening to HTTP socket: %s"), + strerror (errno)); +#else + const int save_errno = WSAGetLastError(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_wsa_errno (save_errno), + _("Listening to HTTP socket: %s"), + pgm_wsastrerror (save_errno)); +#endif + goto err_cleanup; + } + +/* non-blocking notification of new connections */ + pgm_sockaddr_nonblocking (http_sock, TRUE); + +/* create notification channel */ + if (0 != pgm_notify_init (&http_notify)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_errno (errno), + _("Creating HTTP notification channel: %s"), + strerror (errno)); + goto err_cleanup; + } + +/* spawn thread to handle HTTP requests */ +#ifndef _WIN32 + const int status = pthread_create (&http_thread, NULL, &http_routine, NULL); + if (0 != status) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_errno (errno), + _("Creating HTTP thread: %s"), + strerror (errno)); + goto err_cleanup; + } +#else + http_thread = (HANDLE)_beginthreadex (NULL, 0, &http_routine, NULL, 0, NULL); + const int save_errno = errno; + if (0 == http_thread) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_HTTP, + pgm_error_from_errno (save_errno), + _("Creating HTTP thread: %s"), + strerror (save_errno)); + goto err_cleanup; + } +#endif /* _WIN32 */ + pgm_minor (_("Web interface: http://%s:%i"), + http_hostname, + http_port); + return TRUE; + +err_cleanup: +#ifndef _WIN32 + if (-1 != http_sock) { + close (http_sock); + http_sock = -1; + } +#else + if (INVALID_SOCKET != http_sock) { + closesocket (http_sock); + http_sock = INVALID_SOCKET; + } +#endif /* _WIN32 */ + if (pgm_notify_is_valid (&http_notify)) { + pgm_notify_destroy (&http_notify); + } + pgm_atomic_dec32 (&http_ref_count); + return FALSE; +} + +/* notify HTTP thread to shutdown, wait for shutdown and cleanup. + */ + +bool +pgm_http_shutdown (void) +{ + pgm_return_val_if_fail (pgm_atomic_read32 (&http_ref_count) > 0, FALSE); + + if (pgm_atomic_exchange_and_add32 (&http_ref_count, (uint32_t)-1) != 1) + return TRUE; + + pgm_notify_send (&http_notify); +#ifndef _WIN32 + pthread_join (http_thread, NULL); +#else + CloseHandle (http_thread); +#endif +#ifndef _WIN32 + if (-1 != http_sock) { + close (http_sock); + http_sock = -1; + } +#else + if (INVALID_SOCKET != http_sock) { + closesocket (http_sock); + http_sock = INVALID_SOCKET; + } +#endif /* _WIN32 */ + pgm_notify_destroy (&http_notify); + return TRUE; +} + +/* accept a new incoming HTTP connection. + */ + +static +void +http_accept ( + int listen_sock + ) +{ +/* new connection */ + struct sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + int new_sock = accept (listen_sock, (struct sockaddr*)&addr, &addrlen); + if (-1 == new_sock) { + if (EAGAIN == errno) + return; +#ifndef _WIN32 + pgm_warn (_("HTTP accept: %s"), strerror (errno)); +#else + const int save_errno = WSAGetLastError(); + pgm_warn (_("HTTP accept: %s"), pgm_wsastrerror (save_errno)); +#endif + return; + } + +#ifndef _WIN32 +/* out of bounds file descriptor for select() */ + if (new_sock >= FD_SETSIZE) { + close (new_sock); + pgm_warn (_("Rejected new HTTP client socket due to out of bounds file descriptor.")); + return; + } +#endif + + pgm_sockaddr_nonblocking (new_sock, TRUE); + + struct http_connection_t* connection = pgm_new0 (struct http_connection_t, 1); + connection->sock = new_sock; + connection->state = HTTP_STATE_READ; + http_socks = pgm_list_prepend_link (http_socks, &connection->link_); + FD_SET( new_sock, &http_readfds ); + FD_SET( new_sock, &http_exceptfds ); + if (new_sock > http_max_sock) + http_max_sock = new_sock; +} + +static +void +http_close ( + struct http_connection_t* connection + ) +{ +#ifndef _WIN32 + if (0 != close (connection->sock)) { + pgm_warn (_("Close HTTP client socket: %s"), strerror (errno)); + } +#else + if (0 != closesocket (connection->sock)) { + const int save_errno = WSAGetLastError(); + pgm_warn (_("Close HTTP client socket: %s"), pgm_wsastrerror (save_errno)); + } +#endif + switch (connection->state) { + case HTTP_STATE_READ: + case HTTP_STATE_FINWAIT: + FD_CLR( connection->sock, &http_readfds ); + break; + case HTTP_STATE_WRITE: + FD_CLR( connection->sock, &http_writefds ); + break; + } + FD_CLR( connection->sock, &http_exceptfds ); + http_socks = pgm_list_remove_link (http_socks, &connection->link_); + if (connection->buflen > 0) { + pgm_free (connection->buf); + connection->buf = NULL; + connection->buflen = 0; + } +/* find new highest fd */ + if (connection->sock == http_max_sock) + { + http_max_sock = -1; + for (pgm_list_t* list = http_socks; list; list = list->next) + { + struct http_connection_t* c = (void*)list; + if (c->sock > http_max_sock) + http_max_sock = c->sock; + } + } + pgm_free (connection); +} + +/* non-blocking read an incoming HTTP request + */ + +static +void +http_read ( + struct http_connection_t* connection + ) +{ + for (;;) + { +/* grow buffer as needed */ + if (connection->bufoff + 1024 > connection->buflen) { + connection->buf = pgm_realloc (connection->buf, connection->buflen + 1024); + connection->buflen += 1024; + } + const ssize_t bytes_read = recv (connection->sock, &connection->buf[ connection->bufoff ], connection->buflen - connection->bufoff, 0); + if (bytes_read < 0) { + if (EINTR == errno || EAGAIN == errno) + return; +#ifndef _WIN32 + pgm_warn (_("HTTP client read: %s"), strerror (errno)); +#else + const int save_errno = WSAGetLastError(); + pgm_warn (_("HTTP client read: %s"), pgm_wsastrerror (save_errno)); +#endif + http_close (connection); + return; + } + +/* complete */ + if (strstr (connection->buf, "\r\n\r\n")) + break; + } + +/* process request, e.g. GET /index.html HTTP/1.1\r\n + */ + connection->buf[ connection->buflen - 1 ] = '\0'; + if (0 != memcmp (connection->buf, "GET ", strlen("GET "))) { +/* 501 (not implemented) */ + http_close (connection); + return; + } + + char* request_uri = connection->buf + strlen("GET "); + char* p = request_uri; + do { + if (*p == '?' || *p == ' ') { + *p = '\0'; + break; + } + } while (*(++p)); + + connection->status_code = 200; /* OK */ + connection->status_text = "OK"; + connection->content_type = "text/html"; + connection->bufoff = 0; + for (unsigned i = 0; i < PGM_N_ELEMENTS(http_directory); i++) + { + if (0 == strcmp (request_uri, http_directory[i].path)) + { + http_directory[i].callback (connection, request_uri); + goto complete; + } + } + default_callback (connection, request_uri); + +complete: + connection->state = HTTP_STATE_WRITE; + FD_CLR( connection->sock, &http_readfds ); + FD_SET( connection->sock, &http_writefds ); +} + +/* non-blocking write a HTTP response + */ + +static +void +http_write ( + struct http_connection_t* connection + ) +{ + do { + const ssize_t bytes_written = send (connection->sock, &connection->buf[ connection->bufoff ], connection->buflen - connection->bufoff, 0); + if (bytes_written < 0) { + if (EINTR == errno || EAGAIN == errno) + return; +#ifndef _WIN32 + pgm_warn (_("HTTP client write: %s"), strerror (errno)); +#else + const int save_errno = WSAGetLastError(); + pgm_warn (_("HTTP client write: %s"), pgm_wsastrerror (save_errno)); +#endif + http_close (connection); + return; + } + connection->bufoff += bytes_written; + } while (connection->bufoff < connection->buflen); + + if (0 == shutdown (connection->sock, SHUT_WR)) { + http_close (connection); + } else { + pgm_debug ("HTTP socket entering finwait state."); + connection->state = HTTP_STATE_FINWAIT; + FD_CLR( connection->sock, &http_writefds ); + FD_SET( connection->sock, &http_readfds ); + } +} + +/* read and discard pending data waiting for FIN + */ + +static +void +http_finwait ( + struct http_connection_t* connection + ) +{ + char buf[1024]; + const ssize_t bytes_read = read (connection->sock, buf, sizeof(buf)); + if (bytes_read < 0 && (EINTR == errno || EAGAIN == errno)) + return; + http_close (connection); +} + +static +void +http_process ( + struct http_connection_t* connection + ) +{ + switch (connection->state) { + case HTTP_STATE_READ: http_read (connection); break; + case HTTP_STATE_WRITE: http_write (connection); break; + case HTTP_STATE_FINWAIT: http_finwait (connection); break; + } +} + +static +void +http_set_status ( + struct http_connection_t* connection, + int status_code, + const char* status_text + ) +{ + connection->status_code = status_code; + connection->status_text = status_text; +} + +static +void +http_set_content_type ( + struct http_connection_t* connection, + const char* content_type + ) +{ + connection->content_type = content_type; +} + +/* finalise response buffer with headers and content */ + +static +void +http_set_static_response ( + struct http_connection_t* connection, + const char* content, + size_t content_length + ) +{ + pgm_string_t* response = pgm_string_new (NULL); + pgm_string_printf (response, "HTTP/1.0 %d %s\r\n" + "Server: OpenPGM HTTP Server %u.%u.%u\r\n" + "Last-Modified: Fri, 1 Jan 2010, 00:00:01 GMT\r\n" +#ifndef _MSC_VER + "Content-Length: %zd\r\n" +#else + "Content-Length: %d\r\n" +#endif + "Content-Type: %s\r\n" + "Connection: close\r\n" + "\r\n", + connection->status_code, + connection->status_text, + pgm_major_version, pgm_minor_version, pgm_micro_version, +#ifndef _MSC_VER + content_length, +#else + (int)content_length, +#endif + connection->content_type + ); + pgm_string_append (response, content); + if (connection->buflen) + pgm_free (connection->buf); + connection->buflen = response->len; + connection->buf = pgm_string_free (response, FALSE); +} + +static +void +http_set_response ( + struct http_connection_t* connection, + char* content, + size_t content_length + ) +{ + pgm_string_t* response = pgm_string_new (NULL); + pgm_string_printf (response, "HTTP/1.0 %d %s\r\n" + "Server: OpenPGM HTTP Server %u.%u.%u\r\n" +#ifndef _MSC_VER + "Content-Length: %zd\r\n" +#else + "Content-Length: %d\r\n" +#endif + "Content-Type: %s\r\n" + "Connection: close\r\n" + "\r\n", + connection->status_code, + connection->status_text, + pgm_major_version, pgm_minor_version, pgm_micro_version, +#ifndef _MSC_VER + content_length, +#else + (int)content_length, +#endif + connection->content_type + ); + pgm_string_append (response, content); + pgm_free (content); + if (connection->buflen) + pgm_free (connection->buf); + connection->buflen = response->len; + connection->buf = pgm_string_free (response, FALSE); +} + +/* Thread routine for processing HTTP requests + */ + +static +#ifndef _WIN32 +void* +#else +unsigned +__stdcall +#endif +http_routine ( + PGM_GNUC_UNUSED void* arg + ) +{ + const int notify_fd = pgm_notify_get_fd (&http_notify); + const int max_fd = MAX( notify_fd, http_sock ); + + FD_ZERO( &http_readfds ); + FD_ZERO( &http_writefds ); + FD_ZERO( &http_exceptfds ); + FD_SET( notify_fd, &http_readfds ); + FD_SET( http_sock, &http_readfds ); + + for (;;) + { + int fds = MAX( http_max_sock, max_fd ) + 1; + fd_set readfds = http_readfds, writefds = http_writefds, exceptfds = http_exceptfds; + + fds = select (fds, &readfds, &writefds, &exceptfds, NULL); +/* signal interrupt */ + if (PGM_UNLIKELY(fds < 0 && EINTR == errno)) + continue; +/* terminate */ + if (PGM_UNLIKELY(FD_ISSET( notify_fd, &readfds ))) + break; +/* new connection */ + if (FD_ISSET( http_sock, &readfds )) { + http_accept (http_sock); + continue; + } +/* existing connection */ + for (pgm_list_t* list = http_socks; list;) + { + struct http_connection_t* c = (void*)list; + list = list->next; + if ((FD_ISSET( c->sock, &readfds ) && HTTP_STATE_READ == c->state) || + (FD_ISSET( c->sock, &writefds ) && HTTP_STATE_WRITE == c->state) || + (FD_ISSET( c->sock, &exceptfds ))) + { + http_process (c); + } + } + } + +/* cleanup */ +#ifndef _WIN32 + return NULL; +#else + _endthread(); + return 0; +#endif /* WIN32 */ +} + +/* add xhtml doctype and head, populate with runtime values + */ + +typedef enum { + HTTP_TAB_GENERAL_INFORMATION, + HTTP_TAB_INTERFACES, + HTTP_TAB_TRANSPORTS, + HTTP_TAB_HISTOGRAMS +} http_tab_e; + +static +pgm_string_t* +http_create_response ( + const char* subtitle, + http_tab_e tab + ) +{ + pgm_assert (NULL != subtitle); + pgm_assert (tab == HTTP_TAB_GENERAL_INFORMATION || + tab == HTTP_TAB_INTERFACES || + tab == HTTP_TAB_TRANSPORTS || + tab == HTTP_TAB_HISTOGRAMS); + +/* surprising deficiency of GLib is no support of display locale time */ + char timestamp[100]; + time_t now; + time (&now); + const struct tm* time_ptr = localtime (&now); +#ifndef _WIN32 + strftime (timestamp, sizeof(timestamp), "%c", time_ptr); +#else + wchar_t wtimestamp[100]; + const size_t slen = strftime (timestamp, sizeof(timestamp), "%c", time_ptr); + const size_t wslen = MultiByteToWideChar (CP_ACP, 0, timestamp, slen, wtimestamp, 100); + WideCharToMultiByte (CP_UTF8, 0, wtimestamp, wslen + 1, timestamp, sizeof(timestamp), NULL, NULL); +#endif + + pgm_string_t* response = pgm_string_new (WWW_XHTML10_STRICT_DOCTYPE); + pgm_string_append_printf (response, "\n" + "%s - %s" + "" + "\n" + "" + "
" + "%s" + " | OpenPGM %u.%u.%u" + " | %s" + "
" + "
" + "General Information" + "Interfaces" + "Transports" +#ifdef CONFIG_HISTOGRAMS + "Histograms" +#endif + "
" + "
" + "
", + http_hostname, + subtitle, + http_hostname, + pgm_major_version, pgm_minor_version, pgm_micro_version, + timestamp, + tab == HTTP_TAB_GENERAL_INFORMATION ? "top" : "bottom", + tab == HTTP_TAB_INTERFACES ? "top" : "bottom", + tab == HTTP_TAB_TRANSPORTS ? "top" : "bottom" +#ifdef CONFIG_HISTOGRAMS + ,tab == HTTP_TAB_HISTOGRAMS ? "top" : "bottom" +#endif + ); + + return response; +} + +static +void +http_finalize_response ( + struct http_connection_t* connection, + pgm_string_t* response + ) +{ + pgm_string_append (response, "
" + "
" + "©2010 Miru" + "
" + "\n" + ""); + + char* buf = pgm_string_free (response, FALSE); + http_set_response (connection, buf, strlen (buf)); +} + +static +void +robots_callback ( + struct http_connection_t* connection, + PGM_GNUC_UNUSED const char* path + ) +{ + http_set_content_type (connection, "text/plain"); + http_set_static_response (connection, WWW_ROBOTS_TXT, strlen(WWW_ROBOTS_TXT)); +} + +static +void +css_callback ( + struct http_connection_t* connection, + PGM_GNUC_UNUSED const char* path + ) +{ + http_set_content_type (connection, "text/css"); + http_set_static_response (connection, WWW_BASE_CSS, strlen(WWW_BASE_CSS)); +} + +static +void +index_callback ( + struct http_connection_t* connection, + const char* path + ) +{ + if (strlen (path) > 1) { + default_callback (connection, path); + return; + } + pgm_rwlock_reader_lock (&pgm_sock_list_lock); + const unsigned transport_count = pgm_slist_length (pgm_sock_list); + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + + pgm_string_t* response = http_create_response ("OpenPGM", HTTP_TAB_GENERAL_INFORMATION); + pgm_string_append_printf (response, "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
host name:%s
user name:%s
IP address:%s
transports:%i
process ID:%i
\n", + http_hostname, + http_username, + http_address, + transport_count, + http_pid); + http_finalize_response (connection, response); +} + +static +void +interfaces_callback ( + struct http_connection_t* connection, + PGM_GNUC_UNUSED const char* path + ) +{ + pgm_string_t* response = http_create_response ("Interfaces", HTTP_TAB_INTERFACES); + pgm_string_append (response, "
");
+	struct pgm_ifaddrs_t *ifap, *ifa;
+	pgm_error_t* err = NULL;
+	if (!pgm_getifaddrs (&ifap, &err)) {
+		pgm_string_append_printf (response, "pgm_getifaddrs(): %s", (err && err->message) ? err->message : "(null)");
+		http_finalize_response (connection, response);
+		return;
+	}
+	for (ifa = ifap; ifa; ifa = ifa->ifa_next)
+	{
+		int i = NULL == ifa->ifa_addr ? 0 : pgm_if_nametoindex (ifa->ifa_addr->sa_family, ifa->ifa_name);
+		char rname[IF_NAMESIZE * 2 + 3];
+		char b[IF_NAMESIZE * 2 + 3];
+
+		pgm_if_indextoname (i, rname);
+		sprintf (b, "%s (%s)", ifa->ifa_name, rname);
+
+		 if (NULL == ifa->ifa_addr ||
+		      (ifa->ifa_addr->sa_family != AF_INET &&
+		       ifa->ifa_addr->sa_family != AF_INET6) )
+		{
+			pgm_string_append_printf (response,
+				"#%d name %-15.15s ---- %-46.46s scope 0 status %s loop %s b/c %s m/c %s
\n", + i, + b, + "", + ifa->ifa_flags & IFF_UP ? "UP " : "DOWN", + ifa->ifa_flags & IFF_LOOPBACK ? "YES" : "NO ", + ifa->ifa_flags & IFF_BROADCAST ? "YES" : "NO ", + ifa->ifa_flags & IFF_MULTICAST ? "YES" : "NO " + ); + continue; + } + + char s[INET6_ADDRSTRLEN]; + getnameinfo (ifa->ifa_addr, pgm_sockaddr_len(ifa->ifa_addr), + s, sizeof(s), + NULL, 0, + NI_NUMERICHOST); + pgm_string_append_printf (response, + "#%d name %-15.15s IPv%i %-46.46s scope %u status %s loop %s b/c %s m/c %s
\n", + i, + b, + ifa->ifa_addr->sa_family == AF_INET ? 4 : 6, + s, + (unsigned)pgm_sockaddr_scope_id(ifa->ifa_addr), + ifa->ifa_flags & IFF_UP ? "UP " : "DOWN", + ifa->ifa_flags & IFF_LOOPBACK ? "YES" : "NO ", + ifa->ifa_flags & IFF_BROADCAST ? "YES" : "NO ", + ifa->ifa_flags & IFF_MULTICAST ? "YES" : "NO " + ); + } + pgm_freeifaddrs (ifap); + pgm_string_append (response, "
\n"); + http_finalize_response (connection, response); +} + +static +void +transports_callback ( + struct http_connection_t* connection, + PGM_GNUC_UNUSED const char* path + ) +{ + pgm_string_t* response = http_create_response ("Transports", HTTP_TAB_TRANSPORTS); + pgm_string_append (response, "
" + "\n" + "" + "" + "" + "" + "" + "" + ); + + if (pgm_sock_list) + { + pgm_rwlock_reader_lock (&pgm_sock_list_lock); + + pgm_slist_t* list = pgm_sock_list; + while (list) + { + pgm_slist_t* next = list->next; + pgm_sock_t* sock = list->data; + + char group_address[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&sock->send_gsr.gsr_group, pgm_sockaddr_len ((struct sockaddr*)&sock->send_gsr.gsr_group), + group_address, sizeof(group_address), + NULL, 0, + NI_NUMERICHOST); + char gsi[ PGM_GSISTRLEN ]; + pgm_gsi_print_r (&sock->tsi.gsi, gsi, sizeof(gsi)); + const uint16_t sport = ntohs (sock->tsi.sport); + const uint16_t dport = ntohs (sock->dport); + pgm_string_append_printf (response, "" + "" + "" + "" + "" + "", + group_address, + dport, + gsi, sport, + gsi, + gsi, sport, + sport); + list = next; + } + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + } + else + { +/* no transports */ + pgm_string_append (response, "" + "" + "" + ); + } + + pgm_string_append (response, "
Group addressDest portSource GSISource port
%s%i%s%u
This transport has no peers.
\n" + "
"); + http_finalize_response (connection, response); +} + +static +void +histograms_callback ( + struct http_connection_t* connection, + PGM_GNUC_UNUSED const char* path + ) +{ + pgm_string_t* response = http_create_response ("Histograms", HTTP_TAB_HISTOGRAMS); + pgm_histogram_write_html_graph_all (response); + http_finalize_response (connection, response); +} + +static +void +default_callback ( + struct http_connection_t* connection, + const char* path + ) +{ + pgm_tsi_t tsi; + const int count = sscanf (path, "/%hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hu", + (unsigned char*)&tsi.gsi.identifier[0], + (unsigned char*)&tsi.gsi.identifier[1], + (unsigned char*)&tsi.gsi.identifier[2], + (unsigned char*)&tsi.gsi.identifier[3], + (unsigned char*)&tsi.gsi.identifier[4], + (unsigned char*)&tsi.gsi.identifier[5], + &tsi.sport); + tsi.sport = htons (tsi.sport); + if (count == 7) + { + int retval = http_tsi_response (connection, &tsi); + if (!retval) return; + } + + http_set_status (connection, 404, "Not Found"); + http_set_static_response (connection, WWW_404_HTML, strlen(WWW_404_HTML)); +} + +static +int +http_tsi_response ( + struct http_connection_t* connection, + pgm_tsi_t* tsi + ) +{ +/* first verify this is a valid TSI */ + pgm_rwlock_reader_lock (&pgm_sock_list_lock); + + pgm_sock_t* sock = NULL; + pgm_slist_t* list = pgm_sock_list; + while (list) + { + pgm_sock_t* list_sock = (pgm_sock_t*)list->data; + pgm_slist_t* next = list->next; + +/* check source */ + if (pgm_tsi_equal (tsi, &list_sock->tsi)) + { + sock = list_sock; + break; + } + +/* check receivers */ + pgm_rwlock_reader_lock (&list_sock->peers_lock); + pgm_peer_t* receiver = pgm_hashtable_lookup (list_sock->peers_hashtable, tsi); + if (receiver) { + int retval = http_receiver_response (connection, receiver); + pgm_rwlock_reader_unlock (&list_sock->peers_lock); + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + return retval; + } + pgm_rwlock_reader_unlock (&list_sock->peers_lock); + + list = next; + } + + if (!sock) { + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + return -1; + } + +/* transport now contains valid matching TSI */ + char gsi[ PGM_GSISTRLEN ]; + pgm_gsi_print_r (&sock->tsi.gsi, gsi, sizeof(gsi)); + + char title[ sizeof("Transport .00000") + PGM_GSISTRLEN ]; + sprintf (title, "Transport %s.%hu", + gsi, + ntohs (sock->tsi.sport)); + + char source_address[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&sock->send_gsr.gsr_source, pgm_sockaddr_len ((struct sockaddr*)&sock->send_gsr.gsr_source), + source_address, sizeof(source_address), + NULL, 0, + NI_NUMERICHOST); + + char group_address[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&sock->send_gsr.gsr_group, pgm_sockaddr_len ((struct sockaddr*)&sock->send_gsr.gsr_group), + group_address, sizeof(group_address), + NULL, 0, + NI_NUMERICHOST); + + const uint16_t dport = ntohs (sock->dport); + const uint16_t sport = ntohs (sock->tsi.sport); + + const pgm_time_t ihb_min = sock->spm_heartbeat_len ? sock->spm_heartbeat_interval[ 1 ] : 0; + const pgm_time_t ihb_max = sock->spm_heartbeat_len ? sock->spm_heartbeat_interval[ sock->spm_heartbeat_len - 1 ] : 0; + + char spm_path[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&sock->recv_gsr[0].gsr_source, pgm_sockaddr_len ((struct sockaddr*)&sock->recv_gsr[0].gsr_source), + spm_path, sizeof(spm_path), + NULL, 0, + NI_NUMERICHOST); + + pgm_string_t* response = http_create_response (title, HTTP_TAB_TRANSPORTS); + pgm_string_append_printf (response, "
" + "Transport: " + "%s.%hu" + "
", + gsi, sport); + +/* peers */ + + pgm_string_append (response, "
" + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + ); + + if (sock->peers_list) + { + pgm_rwlock_reader_lock (&sock->peers_lock); + pgm_list_t* peers_list = sock->peers_list; + while (peers_list) { + pgm_list_t* next = peers_list->next; + http_each_receiver (peers_list->data, response); + peers_list = next; + } + pgm_rwlock_reader_unlock (&sock->peers_lock); + } + else + { +/* no peers */ + + pgm_string_append (response, "" + "" + "" + ); + + } + + pgm_string_append (response, "
Group addressDest portSource addressLast hopSource GSISource port
This transport has no peers.
\n" + "
"); + +/* source and configuration information */ + + pgm_string_append_printf (response, "
" + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "", + source_address, + group_address, + dport, + gsi, + sport); + +/* continue with source information */ + + pgm_string_append_printf (response, "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
Source address%s
Group address%s
Dest port%u
Source GSI%s
Source port%u
Ttl%u
Adv Mode%s
Late joindisable(2)
TXW_MAX_RTE%" GROUP_FORMAT "zd
TXW_SECS%" GROUP_FORMAT "u
TXW_ADV_SECS0
Ambient SPM interval%" GROUP_FORMAT PGM_TIME_FORMAT " ms
IHB_MIN%" GROUP_FORMAT PGM_TIME_FORMAT " ms
IHB_MAX%" GROUP_FORMAT PGM_TIME_FORMAT " ms
NAK_BO_IVL%" GROUP_FORMAT PGM_TIME_FORMAT " ms
FECdisabled(1)
Source Path Address%s
\n" + "
", + sock->hops, + 0 == sock->adv_mode ? "time(0)" : "data(1)", + sock->txw_max_rte, + sock->txw_secs, + pgm_to_msecs(sock->spm_ambient_interval), + ihb_min, + ihb_max, + pgm_to_msecs(sock->nak_bo_ivl), + spm_path); + +/* performance information */ + + const pgm_txw_t* window = sock->window; + pgm_string_append_printf (response, "\n

Performance information

" + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
Data bytes sent%" GROUP_FORMAT PRIu32 "
Data packets sent%" GROUP_FORMAT PRIu32 "
Bytes buffered%" GROUP_FORMAT PRIu32 "
Packets buffered%" GROUP_FORMAT PRIu32 "
Bytes sent%" GROUP_FORMAT PRIu32 "
Raw NAKs received%" GROUP_FORMAT PRIu32 "
Checksum errors%" GROUP_FORMAT PRIu32 "
Malformed NAKs%" GROUP_FORMAT PRIu32 "
Packets discarded%" GROUP_FORMAT PRIu32 "
Bytes retransmitted%" GROUP_FORMAT PRIu32 "
Packets retransmitted%" GROUP_FORMAT PRIu32 "
NAKs received%" GROUP_FORMAT PRIu32 "
NAKs ignored%" GROUP_FORMAT PRIu32 "
Transmission rate%" GROUP_FORMAT PRIu32 " bps
NNAK packets received%" GROUP_FORMAT PRIu32 "
NNAKs received%" GROUP_FORMAT PRIu32 "
Malformed NNAKs%" GROUP_FORMAT PRIu32 "
\n", + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT], + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT], + window ? pgm_txw_size (window) : 0, /* minus IP & any UDP header */ + window ? pgm_txw_length (window) : 0, + sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED], + sock->cumulative_stats[PGM_PC_SOURCE_CKSUM_ERRORS], + sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS], + sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED], + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_BYTES_RETRANSMITTED], + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED], + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED], + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_IGNORED], + sock->cumulative_stats[PGM_PC_SOURCE_TRANSMISSION_CURRENT_RATE], + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAK_PACKETS_RECEIVED], + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAKS_RECEIVED], + sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]); + + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + http_finalize_response (connection, response); + return 0; +} + +static +void +http_each_receiver ( + pgm_peer_t* peer, + pgm_string_t* response + ) +{ + char group_address[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&peer->group_nla, pgm_sockaddr_len ((struct sockaddr*)&peer->group_nla), + group_address, sizeof(group_address), + NULL, 0, + NI_NUMERICHOST); + + char source_address[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&peer->nla, pgm_sockaddr_len ((struct sockaddr*)&peer->nla), + source_address, sizeof(source_address), + NULL, 0, + NI_NUMERICHOST); + + char last_hop[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&peer->local_nla, pgm_sockaddr_len ((struct sockaddr*)&peer->local_nla), + last_hop, sizeof(last_hop), + NULL, 0, + NI_NUMERICHOST); + + char gsi[ PGM_GSISTRLEN + sizeof(".00000") ]; + pgm_gsi_print_r (&peer->tsi.gsi, gsi, sizeof(gsi)); + + const int sport = ntohs (peer->tsi.sport); + const int dport = ntohs (peer->sock->dport); /* by definition must be the same */ + pgm_string_append_printf (response, "" + "%s" + "%u" + "%s" + "%s" + "%s" + "%u" + "", + group_address, + dport, + source_address, + last_hop, + gsi, sport, gsi, + gsi, sport, sport + ); +} + +static +int +http_time_summary ( + const time_t* activity_time, + char* sz + ) +{ + time_t now_time = time (NULL); + + if (*activity_time > now_time) { + return sprintf (sz, "clock skew"); + } + + struct tm* activity_tm = localtime (activity_time); + + now_time -= *activity_time; + + if (now_time < (24 * 60 * 60)) + { + char hourmin[6]; + strftime (hourmin, sizeof(hourmin), "%H:%M", activity_tm); + + if (now_time < 60) { + return sprintf (sz, "%s (%li second%s ago)", + hourmin, now_time, now_time > 1 ? "s" : ""); + } + now_time /= 60; + if (now_time < 60) { + return sprintf (sz, "%s (%li minute%s ago)", + hourmin, now_time, now_time > 1 ? "s" : ""); + } + now_time /= 60; + return sprintf (sz, "%s (%li hour%s ago)", + hourmin, now_time, now_time > 1 ? "s" : ""); + } + else + { + char daymonth[32]; +#ifndef _WIN32 + strftime (daymonth, sizeof(daymonth), "%d %b", activity_tm); +#else + wchar_t wdaymonth[32]; + const size_t slen = strftime (daymonth, sizeof(daymonth), "%d %b", &activity_tm); + const size_t wslen = MultiByteToWideChar (CP_ACP, 0, daymonth, slen, wdaymonth, 32); + WideCharToMultiByte (CP_UTF8, 0, wdaymonth, wslen + 1, daymonth, sizeof(daymonth), NULL, NULL); +#endif + now_time /= 24; + if (now_time < 14) { + return sprintf (sz, "%s (%li day%s ago)", + daymonth, now_time, now_time > 1 ? "s" : ""); + } else { + return sprintf (sz, "%s", daymonth); + } + } +} + +static +int +http_receiver_response ( + struct http_connection_t* connection, + pgm_peer_t* peer + ) +{ + char gsi[ PGM_GSISTRLEN ]; + pgm_gsi_print_r (&peer->tsi.gsi, gsi, sizeof(gsi)); + char title[ sizeof("Peer .00000") + PGM_GSISTRLEN ]; + sprintf (title, "Peer %s.%u", + gsi, + ntohs (peer->tsi.sport)); + + char group_address[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&peer->group_nla, pgm_sockaddr_len ((struct sockaddr*)&peer->group_nla), + group_address, sizeof(group_address), + NULL, 0, + NI_NUMERICHOST); + + char source_address[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&peer->nla, pgm_sockaddr_len ((struct sockaddr*)&peer->nla), + source_address, sizeof(source_address), + NULL, 0, + NI_NUMERICHOST); + + char last_hop[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&peer->local_nla, pgm_sockaddr_len ((struct sockaddr*)&peer->local_nla), + last_hop, sizeof(last_hop), + NULL, 0, + NI_NUMERICHOST); + + const uint16_t sport = ntohs (peer->tsi.sport); + const uint16_t dport = ntohs (peer->sock->dport); /* by definition must be the same */ + const pgm_rxw_t* window = peer->window; + const uint32_t outstanding_naks = window->nak_backoff_queue.length + + window->wait_ncf_queue.length + + window->wait_data_queue.length; + + time_t last_activity_time; + pgm_time_since_epoch (&peer->last_packet, &last_activity_time); + + char last_activity[100]; + http_time_summary (&last_activity_time, last_activity); + + pgm_string_t* response = http_create_response (title, HTTP_TAB_TRANSPORTS); + pgm_string_append_printf (response, "
" + "Peer: " + "%s.%u" + "
", + gsi, sport); + + +/* peer information */ + pgm_string_append_printf (response, "
" + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "", + group_address, + dport, + source_address, + last_hop, + gsi, + sport); + + pgm_string_append_printf (response, "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
Group address%s
Dest port%u
Source address%s
Last hop%s
Source GSI%s
Source port%u
NAK_BO_IVL%" GROUP_FORMAT PGM_TIME_FORMAT " ms
NAK_RPT_IVL%" GROUP_FORMAT PGM_TIME_FORMAT " ms
NAK_NCF_RETRIES%" GROUP_FORMAT "u
NAK_RDATA_IVL%" GROUP_FORMAT PGM_TIME_FORMAT " ms
NAK_DATA_RETRIES%" GROUP_FORMAT "u
Send NAKsenabled(1)
Late joindisabled(2)
NAK TTL%u
Delivery orderordered(2)
Multicast NAKsdisabled(2)
\n" + "
", + pgm_to_msecs(peer->sock->nak_bo_ivl), + pgm_to_msecs(peer->sock->nak_rpt_ivl), + peer->sock->nak_ncf_retries, + pgm_to_msecs(peer->sock->nak_rdata_ivl), + peer->sock->nak_data_retries, + peer->sock->hops); + + pgm_string_append_printf (response, "\n

Performance information

" + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" /* detected missed packets */ + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
Data bytes received%" GROUP_FORMAT PRIu32 "
Data packets received%" GROUP_FORMAT PRIu32 "
NAK failures%" GROUP_FORMAT PRIu32 "
Bytes received%" GROUP_FORMAT PRIu32 "
Checksum errors%" GROUP_FORMAT PRIu32 "
Malformed SPMs%" GROUP_FORMAT PRIu32 "
Malformed ODATA%" GROUP_FORMAT PRIu32 "
Malformed RDATA%" GROUP_FORMAT PRIu32 "
Malformed NCFs%" GROUP_FORMAT PRIu32 "
Packets discarded%" GROUP_FORMAT PRIu32 "
Losses%" GROUP_FORMAT PRIu32 "
Bytes delivered to app%" GROUP_FORMAT PRIu32 "
Packets delivered to app%" GROUP_FORMAT PRIu32 "
Duplicate SPMs%" GROUP_FORMAT PRIu32 "
Duplicate ODATA/RDATA%" GROUP_FORMAT PRIu32 "
NAK packets sent%" GROUP_FORMAT PRIu32 "
NAKs sent%" GROUP_FORMAT PRIu32 "
NAKs retransmitted%" GROUP_FORMAT PRIu32 "
NAKs failed%" GROUP_FORMAT PRIu32 "
NAKs failed due to RXW advance%" GROUP_FORMAT PRIu32 "
NAKs failed due to NCF retries%" GROUP_FORMAT PRIu32 "
NAKs failed due to DATA retries%" GROUP_FORMAT PRIu32 "
NAK failures delivered to app%" GROUP_FORMAT PRIu32 "
NAKs suppressed%" GROUP_FORMAT PRIu32 "
Malformed NAKs%" GROUP_FORMAT PRIu32 "
Outstanding NAKs%" GROUP_FORMAT PRIu32 "
Last activity%s
NAK repair min time%" GROUP_FORMAT PRIu32 " μs
NAK repair mean time%" GROUP_FORMAT PRIu32 " μs
NAK repair max time%" GROUP_FORMAT PRIu32 " μs
NAK fail min time%" GROUP_FORMAT PRIu32 " μs
NAK fail mean time%" GROUP_FORMAT PRIu32 " μs
NAK fail max time%" GROUP_FORMAT PRIu32 " μs
NAK min retransmit count%" GROUP_FORMAT PRIu32 "
NAK mean retransmit count%" GROUP_FORMAT PRIu32 "
NAK max retransmit count%" GROUP_FORMAT PRIu32 "
\n", + peer->cumulative_stats[PGM_PC_RECEIVER_DATA_BYTES_RECEIVED], + peer->cumulative_stats[PGM_PC_RECEIVER_DATA_MSGS_RECEIVED], + peer->cumulative_stats[PGM_PC_RECEIVER_NAK_FAILURES], + peer->cumulative_stats[PGM_PC_RECEIVER_BYTES_RECEIVED], + peer->sock->cumulative_stats[PGM_PC_SOURCE_CKSUM_ERRORS], + peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_SPMS], + peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_ODATA], + peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_RDATA], + peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_NCFS], + peer->cumulative_stats[PGM_PC_RECEIVER_PACKETS_DISCARDED], + window->cumulative_losses, + window->bytes_delivered, + window->msgs_delivered, + peer->cumulative_stats[PGM_PC_RECEIVER_DUP_SPMS], + peer->cumulative_stats[PGM_PC_RECEIVER_DUP_DATAS], + peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAK_PACKETS_SENT], + peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SENT], + peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_RETRANSMITTED], + peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_FAILED], + peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_RXW_ADVANCED], + peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_NCF_RETRIES_EXCEEDED], + peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_DATA_RETRIES_EXCEEDED], + peer->cumulative_stats[PGM_PC_RECEIVER_NAK_FAILURES_DELIVERED], + peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED], + peer->cumulative_stats[PGM_PC_RECEIVER_NAK_ERRORS], + outstanding_naks, + last_activity, + window->min_fill_time, + peer->cumulative_stats[PGM_PC_RECEIVER_NAK_SVC_TIME_MEAN], + window->max_fill_time, + peer->min_fail_time, + peer->cumulative_stats[PGM_PC_RECEIVER_NAK_FAIL_TIME_MEAN], + peer->max_fail_time, + window->min_nak_transmit_count, + peer->cumulative_stats[PGM_PC_RECEIVER_TRANSMIT_MEAN], + window->max_nak_transmit_count); + http_finalize_response (connection, response); + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/http_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/http_unittest.c new file mode 100644 index 0000000..32ba11b --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/http_unittest.c @@ -0,0 +1,186 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for the HTTP administration interface. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include + +#include "pgm/transport.h" + + +/* mock state */ +static const guint mock_pgm_major_version = 0; +static const guint mock_pgm_minor_version = 0; +static const guint mock_pgm_micro_version = 0; +static GStaticRWLock mock_pgm_transport_list_lock = G_STATIC_RW_LOCK_INIT; +static GSList* mock_pgm_transport_list = NULL; + +static +gboolean +mock_pgm_tsi_equal ( + gconstpointer v1, + gconstpointer v2 + ) +{ + return memcmp (v1, v2, sizeof(struct pgm_tsi_t)) == 0; +} + +static +void +mock_pgm_time_since_epoch ( + pgm_time_t* pgm_time_t_time, + time_t* time_t_time + ) +{ + *time_t_time = pgm_to_secs (*pgm_time_t_time + 0); +} + +static +void +mock_pgm_histogram_write_html_graph_all + ( + GString* string + ) +{ +} + + +/* mock functions for external references */ + +#define pgm_major_version mock_pgm_major_version +#define pgm_minor_version mock_pgm_minor_version +#define pgm_micro_version mock_pgm_micro_version +#define pgm_transport_list_lock mock_pgm_transport_list_lock +#define pgm_transport_list mock_pgm_transport_list +#define pgm_tsi_equal mock_pgm_tsi_equal +#define pgm_time_since_epoch mock_pgm_time_since_epoch +#define pgm_histogram_write_html_graph_all mock_pgm_histogram_write_html_graph_all + +#define HTTP_DEBUG +#include "http.c" + + +/* target: + * gboolean + * pgm_http_init ( + * guint16* http_port, + * GError** error + * ) + */ + +START_TEST (test_init_pass_001) +{ + GError* err = NULL; + fail_unless (TRUE == pgm_http_init (8080, &err)); + fail_unless (NULL == err); +} +END_TEST + +/* duplicate servers */ +START_TEST (test_init_fail_001) +{ + GError* err = NULL; + fail_unless (TRUE == pgm_http_init (8080, &err)); + fail_unless (FALSE == pgm_http_init (8080, &err)); +} +END_TEST + +/* target: + * gboolean + * pgm_http_shutdown (void) + */ + +START_TEST (test_shutdown_pass_001) +{ + GError* err = NULL; + fail_unless (TRUE == pgm_http_init (8080, &err)); + fail_unless (NULL == err); + fail_unless (TRUE == pgm_http_shutdown ()); +} +END_TEST + +/* repeatability + */ +START_TEST (test_shutdown_pass_002) +{ + GError* err = NULL; + fail_unless (TRUE == pgm_http_init (8080, &err)); + fail_unless (NULL == err); + fail_unless (TRUE == pgm_http_shutdown ()); + fail_unless (TRUE == pgm_http_init (8080, &err)); + fail_unless (NULL == err); + fail_unless (TRUE == pgm_http_shutdown ()); +} +END_TEST + +/* no running server */ +START_TEST (test_shutdown_fail_001) +{ + fail_unless (FALSE == pgm_http_shutdown ()); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_init = tcase_create ("init"); + suite_add_tcase (s, tc_init); + tcase_add_test (tc_init, test_init_pass_001); + tcase_add_test (tc_init, test_init_fail_001); + + TCase* tc_shutdown = tcase_create ("shutdown"); + suite_add_tcase (s, tc_shutdown); + tcase_add_test (tc_shutdown, test_shutdown_pass_001); + tcase_add_test (tc_shutdown, test_shutdown_pass_002); + tcase_add_test (tc_shutdown, test_shutdown_fail_001); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/if.c b/3rdparty/openpgm-svn-r1135/pgm/if.c new file mode 100644 index 0000000..d149608 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/if.c @@ -0,0 +1,1598 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * network interface handling. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include +#include +#include +#ifndef _WIN32 +# include +# include +# include /* _GNU_SOURCE for EAI_NODATA */ +#endif +#include +#include +#include + + +//#define IF_DEBUG + +/* temporary structure to contain interface name whilst address family + * has not been resolved. + */ +struct interface_req { + char ir_name[IF_NAMESIZE]; + unsigned int ir_flags; /* from SIOCGIFFLAGS */ + unsigned int ir_interface; /* interface index */ + struct sockaddr_storage ir_addr; /* interface address */ +}; + + +/* locals */ + +#ifndef _WIN32 +# define IF_DEFAULT_GROUP ((in_addr_t)0xefc00001) /* 239.192.0.1 */ +#else +# define IF_DEFAULT_GROUP ((u_long)0xefc00001) +#endif + +/* ff08::1 */ +#define IF6_DEFAULT_INIT { { { 0xff,8,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } } +const struct in6_addr if6_default_group_addr = IF6_DEFAULT_INIT; + + +static inline bool is_in_net (const struct in_addr*restrict, const struct in_addr*restrict, const struct in_addr*restrict) PGM_GNUC_WARN_UNUSED_RESULT; +static inline bool is_in_net6 (const struct in6_addr*restrict, const struct in6_addr*restrict, const struct in6_addr*restrict) PGM_GNUC_WARN_UNUSED_RESULT; +static inline bool is_network_char (const int, const char) PGM_GNUC_CONST; +static const char* pgm_family_string (const int) PGM_GNUC_CONST; + + +/* recommended address space for multicast: + * rfc4607, rfc3180, rfc2365 + * + * avoid 5 high-order bit overlap. + * + * loopback: ffx1::/16 + * segment: ffx2::/16 + * glop: 238/8 + * mysterious admin: 239/8, ffx6::/16 + * site: 239.252-255/16, ffx5::/16 + * org: 239.192/14, ffx8::/16 + * + * internets: 224.0.1.0-238.255.255.255, ffxe::/16 + */ + + +/* dump all interfaces to console. + * + * note that interface indexes are only in regard to the link layer and hence + * no 1-1 mapping between adapter name to index back to address. + */ + +void +pgm_if_print_all (void) +{ + struct pgm_ifaddrs_t *ifap, *ifa; + + if (!pgm_getifaddrs (&ifap, NULL)) + return; + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) + { + const unsigned int i = NULL == ifa->ifa_addr ? 0 : pgm_if_nametoindex (ifa->ifa_addr->sa_family, ifa->ifa_name); + char rname[IF_NAMESIZE * 2 + 3]; + char b[IF_NAMESIZE * 2 + 3]; + + pgm_if_indextoname (i, rname); + sprintf (b, "%s (%s)", + ifa->ifa_name ? ifa->ifa_name : "(null)", rname); + + if (NULL == ifa->ifa_addr || + (ifa->ifa_addr->sa_family != AF_INET && + ifa->ifa_addr->sa_family != AF_INET6) ) + { + pgm_info (_("#%d name %-15.15s ---- %-46.46s scope 0 status %s loop %s b/c %s m/c %s"), + i, + b, + "", + ifa->ifa_flags & IFF_UP ? "UP " : "DOWN", + ifa->ifa_flags & IFF_LOOPBACK ? "YES" : "NO ", + ifa->ifa_flags & IFF_BROADCAST ? "YES" : "NO ", + ifa->ifa_flags & IFF_MULTICAST ? "YES" : "NO " + ); + continue; + } + + char s[INET6_ADDRSTRLEN]; + getnameinfo (ifa->ifa_addr, pgm_sockaddr_len(ifa->ifa_addr), + s, sizeof(s), + NULL, 0, + NI_NUMERICHOST); + pgm_info (_("#%d name %-15.15s IPv%i %-46.46s scope %u status %s loop %s b/c %s m/c %s"), + i, + b, + ifa->ifa_addr->sa_family == AF_INET ? 4 : 6, + s, + (unsigned)pgm_sockaddr_scope_id(ifa->ifa_addr), + ifa->ifa_flags & IFF_UP ? "UP " : "DOWN", + ifa->ifa_flags & IFF_LOOPBACK ? "YES" : "NO ", + ifa->ifa_flags & IFF_BROADCAST ? "YES" : "NO ", + ifa->ifa_flags & IFF_MULTICAST ? "YES" : "NO " + ); + } + + pgm_freeifaddrs (ifap); +} + +static inline +bool +is_in_net ( + const struct in_addr* restrict addr, /* host byte order */ + const struct in_addr* restrict netaddr, + const struct in_addr* restrict netmask + ) +{ + pgm_assert (NULL != addr); + pgm_assert (NULL != netaddr); + pgm_assert (NULL != netmask); + +#ifdef IF_DEBUG + const struct in_addr taddr = { .s_addr = htonl (addr->s_addr) }; + const struct in_addr tnetaddr = { .s_addr = htonl (netaddr->s_addr) }; + const struct in_addr tnetmask = { .s_addr = htonl (netmask->s_addr) }; + char saddr[INET_ADDRSTRLEN], snetaddr[INET_ADDRSTRLEN], snetmask[INET_ADDRSTRLEN]; + pgm_debug ("is_in_net (addr:%s netaddr:%s netmask:%s)", + pgm_inet_ntop (AF_INET, &taddr, saddr, sizeof(saddr)), + pgm_inet_ntop (AF_INET, &tnetaddr, snetaddr, sizeof(snetaddr)), + pgm_inet_ntop (AF_INET, &tnetmask, snetmask, sizeof(snetmask))); +#endif + + if ((addr->s_addr & netmask->s_addr) == (netaddr->s_addr & netmask->s_addr)) + return TRUE; + return FALSE; +} + +static +bool +is_in_net6 ( + const struct in6_addr* restrict addr, + const struct in6_addr* restrict netaddr, + const struct in6_addr* restrict netmask + ) +{ + pgm_assert (NULL != addr); + pgm_assert (NULL != netaddr); + pgm_assert (NULL != netmask); + +#ifdef IF_DEBUG + char saddr[INET6_ADDRSTRLEN], snetaddr[INET6_ADDRSTRLEN], snetmask[INET6_ADDRSTRLEN]; + pgm_debug ("is_in_net6 (addr:%s netaddr:%s netmask:%s)", + pgm_inet_ntop (AF_INET6, addr, saddr, sizeof(saddr)), + pgm_inet_ntop (AF_INET6, netaddr, snetaddr, sizeof(snetaddr)), + pgm_inet_ntop (AF_INET6, netmask, snetmask, sizeof(snetmask))); +#endif + + for (unsigned i = 0; i < 16; i++) + if ((addr->s6_addr[i] & netmask->s6_addr[i]) != (netaddr->s6_addr[i] & netmask->s6_addr[i])) + return FALSE; + return TRUE; +} + +/* parse interface entity into an interface-request structure. + * + * e.g. eth0 + * 1.2.3.4 + * 1.2 + * abcd:: + * [abcd::] + * + * + * + * special addresses should be ignored: + * + * local physical link: 169.254.0.0/16, fe80::/64 + * broadcast: 255.255.255.255 + * multicast: 224.0.0.0/4 (224.0.0.0 to 239.255.255.255), ff00::/8 + * + * We could use if_nametoindex() but we might as well check that the interface is + * actually UP and capable of multicast traffic. + * + * returns TRUE on success, FALSE on error and sets error appropriately. + */ + +static +bool +parse_interface ( + int family, /* AF_UNSPEC | AF_INET | AF_INET6 */ + const char* restrict ifname, /* NULL terminated */ + struct interface_req* restrict ir, /* location to write interface details to */ + pgm_error_t** restrict error + ) +{ + bool check_inet_network = FALSE, check_inet6_network = FALSE; + bool check_addr = FALSE; + bool check_ifname = FALSE; + char literal[1024]; + struct in_addr in_addr; + struct in6_addr in6_addr; + struct pgm_ifaddrs_t *ifap, *ifa; + struct sockaddr_storage addr; + unsigned interface_matches = 0; + +/* pre-conditions */ + pgm_assert (AF_INET == family || AF_INET6 == family || AF_UNSPEC == family); + pgm_assert (NULL != ifname); + pgm_assert (NULL != ir); + + pgm_debug ("parse_interface (family:%s ifname:%s%s%s ir:%p error:%p)", + pgm_family_string (family), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : "", + (const void*)ir, + (const void*)error); + +/* strip any square brackets for IPv6 early evaluation */ + if (AF_INET != family && + '[' == ifname[0]) + { + const size_t ifnamelen = strlen(ifname); + if (']' == ifname[ ifnamelen - 1 ]) { + strncpy (literal, ifname + 1, ifnamelen - 2); + literal[ ifnamelen - 2 ] = 0; + family = AF_INET6; /* force IPv6 evaluation */ + check_inet6_network = TRUE; /* may be a network IP or CIDR block */ + check_addr = TRUE; /* cannot be not a name */ + ifname = literal; + } + } + +/* network address: in_addr in host byte order */ + if (AF_INET6 != family && 0 == pgm_inet_network (ifname, &in_addr)) + { +#ifdef IF_DEBUG + struct in_addr t = { .s_addr = htonl (in_addr.s_addr) }; + pgm_debug ("IPv4 network address: %s", inet_ntoa (t)); +#endif + if (IN_MULTICAST(in_addr.s_addr)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_XDEV, + _("Expecting network interface address, found IPv4 multicast network %s%s%s"), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + return FALSE; + } + struct sockaddr_in s4; + memset (&s4, 0, sizeof(s4)); + s4.sin_family = AF_INET; + s4.sin_addr.s_addr = htonl (in_addr.s_addr); + memcpy (&addr, &s4, sizeof(s4)); + + check_inet_network = TRUE; + check_addr = TRUE; + } + if (AF_INET != family && 0 == pgm_inet6_network (ifname, &in6_addr)) + { + if (IN6_IS_ADDR_MULTICAST(&in6_addr)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_XDEV, + _("Expecting network interface address, found IPv6 multicast network %s%s%s"), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + return FALSE; + } + struct sockaddr_in6 s6; + memset (&s6, 0, sizeof(s6)); + s6.sin6_family = AF_INET6; + s6.sin6_addr = in6_addr; + memcpy (&addr, &s6, sizeof(s6)); + + check_inet6_network = TRUE; + check_addr = TRUE; + } + +/* numeric host with scope id */ + if (!check_addr) + { + struct addrinfo hints = { + .ai_family = family, + .ai_socktype = SOCK_STREAM, /* not really, SOCK_RAW */ + .ai_protocol = IPPROTO_TCP, /* not really, IPPROTO_PGM */ + .ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST /* AI_V4MAPPED is unhelpful */ + }, *res; + const int eai = getaddrinfo (ifname, NULL, &hints, &res); + switch (eai) { + case 0: + if (AF_INET == res->ai_family && + IN_MULTICAST(ntohl (((struct sockaddr_in*)(res->ai_addr))->sin_addr.s_addr))) + { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_XDEV, + _("Expecting interface address, found IPv4 multicast address %s%s%s"), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + freeaddrinfo (res); + return FALSE; + } + else if (AF_INET6 == res->ai_family && + IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)res->ai_addr)->sin6_addr)) + { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_XDEV, + _("Expecting interface address, found IPv6 multicast address %s%s%s"), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + freeaddrinfo (res); + return FALSE; + } + + memcpy (&addr, res->ai_addr, pgm_sockaddr_len (res->ai_addr)); + freeaddrinfo (res); + check_addr = TRUE; + break; + +#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME + case EAI_NODATA: +#endif + case EAI_NONAME: + break; + + default: + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_eai_errno (eai, errno), + _("Numeric host resolution: %s"), + gai_strerror (eai)); + return FALSE; + } + } + +#ifndef _WIN32 +/* network name into network address, can be expensive with NSS network lookup + * + * Only Class A, B or C networks are supported, partitioned networks + * (i.e. network/26 or network/28) are not supported by this facility. + */ + if (!(check_inet_network || check_inet6_network)) + { + const struct netent* ne = getnetbyname (ifname); +/* ne::n_net in host byte order */ + + if (ne) { + switch (ne->n_addrtype) { + case AF_INET: + if (AF_INET6 == family) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("IP address family conflict when resolving network name %s%s%s, found AF_INET when AF_INET6 expected."), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + return FALSE; + } +/* ne->n_net in network order */ + in_addr.s_addr = ne->n_net; + if (IN_MULTICAST(in_addr.s_addr)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_XDEV, + _("Network name %s%s%s resolves to IPv4 mulicast address."), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + return FALSE; + } + check_inet_network = TRUE; + break; + case AF_INET6: +#ifndef CONFIG_HAVE_IP6_NETWORKS + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("Not configured for IPv6 network name support, %s%s%s is an IPv6 network name."), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + return FALSE; +#else + if (AF_INET == family) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("IP address family conflict when resolving network name %s%s%s, found AF_INET6 when AF_INET expected."), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + return FALSE; + } + if (IN6_IS_ADDR_MULTICAST(&ne->n_net)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_XDEV, + _("Network name resolves to IPv6 mulicast address %s%s%s"), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + return FALSE; + } + in6_addr = *(const struct in6_addr*)&ne->n_net; + check_inet6_network = TRUE; + break; +#endif + default: + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("Network name resolves to non-internet protocol address family %s%s%s"), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + return FALSE; + } + } + } +#endif /* _WIN32 */ + +/* hostname lookup with potential DNS delay or error */ + if (!check_addr) + { + struct addrinfo hints = { + .ai_family = family, + .ai_socktype = SOCK_STREAM, /* not really, SOCK_RAW */ + .ai_protocol = IPPROTO_TCP, /* not really, IPPROTO_PGM */ + .ai_flags = AI_ADDRCONFIG, /* AI_V4MAPPED is unhelpful */ + }, *res; + + const int eai = getaddrinfo (ifname, NULL, &hints, &res); + switch (eai) { + case 0: + if (AF_INET == res->ai_family && + IN_MULTICAST(ntohl (((struct sockaddr_in*)(res->ai_addr))->sin_addr.s_addr))) + { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_XDEV, + _("Expecting interface address, found IPv4 multicast name %s%s%s"), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + freeaddrinfo (res); + return FALSE; + } + else if (AF_INET6 == res->ai_family && + IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)res->ai_addr)->sin6_addr)) + { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_XDEV, + _("Expecting interface address, found IPv6 multicast name %s%s%s"), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + freeaddrinfo (res); + return FALSE; + } + memcpy (&addr, res->ai_addr, pgm_sockaddr_len (res->ai_addr)); + freeaddrinfo (res); + check_addr = TRUE; + break; + +#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME + case EAI_NODATA: +#endif + case EAI_NONAME: + check_ifname = TRUE; + break; + + default: + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_eai_errno (eai, errno), + _("Internet host resolution: %s(%d)"), + gai_strerror (eai), eai); + return FALSE; + } + } + +/* iterate through interface list and match device name, ip or net address */ + if (!pgm_getifaddrs (&ifap, error)) { + pgm_prefix_error (error, + _("Enumerating network interfaces: ")); + return FALSE; + } + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) + { + if (NULL == ifa->ifa_addr) + continue; + + switch (ifa->ifa_addr->sa_family) { +/* ignore raw entries on Linux */ +#ifdef AF_PACKET + case AF_PACKET: + continue; +#endif + case AF_INET: + if (AF_INET6 == family) + continue; + break; + case AF_INET6: + if (AF_INET == family) + continue; + break; + default: + continue; + } + + const unsigned ifindex = pgm_if_nametoindex (ifa->ifa_addr->sa_family, ifa->ifa_name); + pgm_assert (0 != ifindex); + +/* check numeric host */ + if (check_addr && + (0 == pgm_sockaddr_cmp (ifa->ifa_addr, (const struct sockaddr*)&addr))) + { + strcpy (ir->ir_name, ifa->ifa_name); + ir->ir_flags = ifa->ifa_flags; + if (ir->ir_flags & IFF_LOOPBACK) + pgm_warn (_("Interface %s reports as a loopback device."), ir->ir_name); + if (!(ir->ir_flags & IFF_MULTICAST)) + pgm_warn (_("Interface %s reports as a non-multicast capable device."), ir->ir_name); + ir->ir_interface = ifindex; + memcpy (&ir->ir_addr, ifa->ifa_addr, pgm_sockaddr_len (ifa->ifa_addr)); + pgm_freeifaddrs (ifap); + return TRUE; + } + +/* check network address */ + if (check_inet_network && + AF_INET == ifa->ifa_addr->sa_family) + { + const struct in_addr ifaddr = { .s_addr = ntohl (((struct sockaddr_in*)ifa->ifa_addr)->sin_addr.s_addr) }; + const struct in_addr netmask = { .s_addr = ntohl (((struct sockaddr_in*)ifa->ifa_netmask)->sin_addr.s_addr) }; + if (is_in_net (&ifaddr, &in_addr, &netmask)) { + strcpy (ir->ir_name, ifa->ifa_name); + ir->ir_flags = ifa->ifa_flags; + if (ir->ir_flags & IFF_LOOPBACK) { + pgm_warn (_("Skipping matching loopback network device %s."), ir->ir_name); + goto skip_inet_network; + } + if (!(ir->ir_flags & IFF_MULTICAST)) { + pgm_warn (_("Skipping matching non-multicast capable network device %s."), ir->ir_name); + goto skip_inet_network; + } + ir->ir_interface = ifindex; + memcpy (&ir->ir_addr, ifa->ifa_addr, pgm_sockaddr_len (ifa->ifa_addr)); + pgm_freeifaddrs (ifap); + return TRUE; + } + } + if (check_inet6_network && + AF_INET6 == ifa->ifa_addr->sa_family) + { + const struct in6_addr ifaddr = ((struct sockaddr_in6*)ifa->ifa_addr)->sin6_addr; + const struct in6_addr netmask = ((struct sockaddr_in6*)ifa->ifa_netmask)->sin6_addr; + if (is_in_net6 (&ifaddr, &in6_addr, &netmask)) { + strcpy (ir->ir_name, ifa->ifa_name); + ir->ir_flags = ifa->ifa_flags; + if (ir->ir_flags & IFF_LOOPBACK) { + pgm_warn (_("Skipping matching loopback network device %s."), ir->ir_name); + goto skip_inet_network; + } + if (!(ir->ir_flags & IFF_MULTICAST)) { + pgm_warn (_("Skipping matching non-multicast capable network device %s."), ir->ir_name); + goto skip_inet_network; + } + ir->ir_interface = ifindex; + memcpy (&ir->ir_addr, ifa->ifa_addr, pgm_sockaddr_len (ifa->ifa_addr)); + pgm_freeifaddrs (ifap); + return TRUE; + } + } +skip_inet_network: + +/* check interface name */ + if (check_ifname) + { + if (0 != strcmp (ifname, ifa->ifa_name)) + continue; + + ir->ir_flags = ifa->ifa_flags; +/* skip loopback and non-multicast capable devices */ + if ((ir->ir_flags & IFF_LOOPBACK) || !(ir->ir_flags & IFF_MULTICAST)) + continue; + +/* check for multiple interfaces */ + if (interface_matches++) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NOTUNIQ, + _("Network interface name not unique %s%s%s"), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + pgm_freeifaddrs (ifap); + return FALSE; + } + + ir->ir_interface = ifindex; + strcpy (ir->ir_name, ifa->ifa_name); + memcpy (&ir->ir_addr, ifa->ifa_addr, pgm_sockaddr_len (ifa->ifa_addr)); + continue; + } + + } + + if (0 == interface_matches) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("No matching non-loopback and multicast capable network interface %s%s%s"), + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + pgm_freeifaddrs (ifap); + return FALSE; + } + + pgm_freeifaddrs (ifap); + return TRUE; +} + +/* parse one multicast address, conflict resolution of multiple address families of DNS multicast names is + * deferred to libc. + * + * Zone indices are ignored as interface specification is already available. + * + * reserved addresses may flag warnings: + * + * 224.0.0.0/24 for local network control + * 224.0.1/24 for internetwork control + * 169.254.255.255, ff02::1 all local nodes on segment + * ff02::2 all routers + * ff05::1 all nodes + * ff0x::fb multicast DNS + * ff0x::108 NIS + * ff05::1:3 DHCP + * + * returns TRUE on success, FALSE on error and sets error appropriately. + */ + +static +bool +parse_group ( + const int family, /* AF_UNSPEC | AF_INET | AF_INET6 */ + const char* restrict group, /* NULL terminated */ + struct sockaddr* restrict addr, /* pointer to sockaddr_storage for writing */ + pgm_error_t** restrict error + ) +{ +/* pre-conditions */ + pgm_assert (AF_INET == family || AF_INET6 == family || AF_UNSPEC == family); + pgm_assert (NULL != group); + pgm_assert (NULL != addr); + + pgm_debug ("parse_group (family:%s group:%s%s%s addr:%p error:%p)", + pgm_family_string (family), + group ? "\"" : "", group ? group : "(null)", group ? "\"" : "", + (const void*)addr, + (const void*)error); + +/* strip any square brackets for early IPv6 literal evaluation */ + if (AF_INET != family && + '[' == group[0]) + { + const size_t grouplen = strlen(group); + if (']' == group[ grouplen - 1 ]) { + char literal[1024]; + strncpy (literal, group + 1, grouplen - 2); + literal[ grouplen - 2 ] = 0; + if (pgm_inet_pton (AF_INET6, literal, &((struct sockaddr_in6*)addr)->sin6_addr) && + IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)addr)->sin6_addr)) + { + addr->sa_family = AF_INET6; + ((struct sockaddr_in6*)addr)->sin6_port = 0; + ((struct sockaddr_in6*)addr)->sin6_flowinfo = 0; + ((struct sockaddr_in6*)addr)->sin6_scope_id = 0; + return TRUE; + } + } + } + +/* IPv4 address */ + if (AF_INET6 != family && + pgm_inet_pton (AF_INET, group, &((struct sockaddr_in*)addr)->sin_addr) && + IN_MULTICAST(ntohl (((struct sockaddr_in*)addr)->sin_addr.s_addr))) + { + addr->sa_family = AF_INET; + return TRUE; + } + if (AF_INET != family && + pgm_inet_pton (AF_INET6, group, &((struct sockaddr_in6*)addr)->sin6_addr) && + IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)addr)->sin6_addr)) + { + addr->sa_family = AF_INET6; + ((struct sockaddr_in6*)addr)->sin6_port = 0; + ((struct sockaddr_in6*)addr)->sin6_flowinfo = 0; + ((struct sockaddr_in6*)addr)->sin6_scope_id = 0; + return TRUE; + } + +#ifndef _WIN32 +/* NSS network */ + const struct netent* ne = getnetbyname (group); +/* ne::n_net in host byte order */ + if (ne) { + switch (ne->n_addrtype) { + case AF_INET: + if (AF_INET6 == family) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("IP address family conflict when resolving network name %s%s%s, found IPv4 when IPv6 expected."), + group ? "\"" : "", group ? group : "(null)", group ? "\"" : ""); + return FALSE; + } + if (IN_MULTICAST(ne->n_net)) { + addr->sa_family = AF_INET; + ((struct sockaddr_in*)addr)->sin_addr.s_addr = htonl (ne->n_net); + return TRUE; + } + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("IP address class conflict when resolving network name %s%s%s, expected IPv4 multicast."), + group ? "\"" : "", group ? group : "(null)", group ? "\"" : ""); + return FALSE; + case AF_INET6: +#ifndef CONFIG_HAVE_IP6_NETWORKS + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("Not configured for IPv6 network name support, %s%s%s is an IPv6 network name."), + group ? "\"" : "", group ? group : "(null)", group ? "\"" : ""); + return FALSE; +#else + if (AF_INET == family) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("IP address family conflict when resolving network name %s%s%s, found IPv6 when IPv4 expected."), + group ? "\"" : "", group ? group : "(null)", group ? "\"" : ""); + return FALSE; + } + if (IN6_IS_ADDR_MULTICAST(&ne->n_net)) { + addr->sa_family = AF_INET6; + ((struct sockaddr_in6*)addr)->sin6_addr = *(const struct in6_addr*)ne->n_net; + ((struct sockaddr_in6*)&addr)->sin6_port = 0; + ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; + ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; + return TRUE; + } + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("IP address class conflict when resolving network name %s%s%s, expected IPv6 multicast."), + group ? "\"" : "", group ? group : "(null)", group ? "\"" : ""); + return FALSE; +#endif /* CONFIG_HAVE_IP6_NETWORKS */ + default: + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("Network name resolves to non-internet protocol address family %s%s%s"), + group ? "\"" : "", group ? group : "(null)", group ? "\"" : ""); + return FALSE; + } + } +#endif /* _WIN32 */ + +/* lookup group through name service */ + struct addrinfo hints = { + .ai_family = family, + .ai_socktype = SOCK_STREAM, /* not really, SOCK_RAW */ + .ai_protocol = IPPROTO_TCP, /* not really, IPPROTO_PGM */ + .ai_flags = AI_ADDRCONFIG, /* AI_V4MAPPED is unhelpful */ + }, *res; + + const int eai = getaddrinfo (group, NULL, &hints, &res); + if (0 != eai) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_eai_errno (eai, errno), + _("Resolving receive group: %s"), + gai_strerror (eai)); + return FALSE; + } + + if ((AF_INET6 != family && IN_MULTICAST(ntohl (((struct sockaddr_in*)res->ai_addr)->sin_addr.s_addr))) || + (AF_INET != family && IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)res->ai_addr)->sin6_addr))) + { + memcpy (addr, res->ai_addr, res->ai_addrlen); + freeaddrinfo (res); + return TRUE; + } + + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_INVAL, + _("Unresolvable receive group %s%s%s"), + group ? "\"" : "", group ? group : "(null)", group ? "\"" : ""); + freeaddrinfo (res); + return FALSE; +} + +/* parse an interface entity from a network parameter. + * + * family can be unspecified - AF_UNSPEC, can return interfaces with the unspecified + * address family + * + * examples: "eth0" + * "hme0,hme1" + * "qe0,qe1,qe2" + * "qe0,qe2,qe2" => valid even though duplicate interface name + * + * returns TRUE on success with device_list containing double linked list of devices as + * sockaddr/idx pairs. returns FALSE on error, including multiple matching adapters. + * + * memory ownership of linked list is passed to caller and must be freed with pgm_free + * and the pgm_list_free* api. + */ + +static +bool +parse_interface_entity ( + int family, /* AF_UNSPEC | AF_INET | AF_INET6 */ + const char* restrict entity, /* NULL terminated */ + pgm_list_t** restrict interface_list, /* */ + pgm_error_t** restrict error + ) +{ + struct interface_req* ir; + pgm_list_t* source_list = NULL; + +/* pre-conditions */ + pgm_assert (AF_INET == family || AF_INET6 == family || AF_UNSPEC == family); + pgm_assert (NULL != interface_list); + pgm_assert (NULL == *interface_list); + pgm_assert (NULL != error); + + pgm_debug ("parse_interface_entity (family:%s entity:%s%s%s interface_list:%p error:%p)", + pgm_family_string (family), + entity ? "\"":"", entity ? entity : "(null)", entity ? "\"":"", + (const void*)interface_list, + (const void*)error); + +/* the empty entity, returns in_addr_any for both receive and send interfaces */ + if (NULL == entity) + { + ir = pgm_new0 (struct interface_req, 1); + ir->ir_addr.ss_family = family; + *interface_list = pgm_list_append (*interface_list, ir); + return TRUE; + } + +/* check interface name length limit */ + char** tokens = pgm_strsplit (entity, ",", 10); + int j = 0; + while (tokens && tokens[j]) + { + pgm_error_t* sub_error = NULL; + ir = pgm_new (struct interface_req, 1); + if (!parse_interface (family, tokens[j], ir, &sub_error)) + { +/* mark multiple interfaces for later decision based on group families */ + if (sub_error && PGM_ERROR_NOTUNIQ == sub_error->code) + { + ir->ir_addr.ss_family = AF_UNSPEC; + pgm_error_free (sub_error); + } +/* bail out on first interface with an error */ + else + { + pgm_propagate_error (error, sub_error); + pgm_free (ir); + pgm_strfreev (tokens); + while (source_list) { + pgm_free (source_list->data); + source_list = pgm_list_delete_link (source_list, source_list); + } + return FALSE; + } + } + + source_list = pgm_list_append (source_list, ir); + ++j; + } + + pgm_strfreev (tokens); + *interface_list = source_list; + return TRUE; +} + +/* parse a receive multicast group entity. can contain more than one multicast group to + * support asymmetric fan-out. + * + * if group is ambiguous, i.e. empty or a name mapping then the address family of the matching + * interface is queried. if the interface is also ambiguous, i.e. empty interface and receive group + * then the hostname will be used to determine the default node address family. if the hosts + * node name resolves both IPv4 and IPv6 address families then the first matching value is taken. + * + * e.g. "239.192.0.1" + * "239.192.0.100,239.192.0.101" + * + * unspecified address family interfaces are forced to AF_INET or AF_INET6. + * + * returns TRUE on success, returns FALSE on error and sets error appropriately. + */ + +static +bool +parse_receive_entity ( + int family, /* AF_UNSPEC | AF_INET | AF_INET6 */ + const char* restrict entity, /* NULL terminated */ + pgm_list_t** restrict interface_list, /* */ + pgm_list_t** restrict recv_list, /* */ + pgm_error_t** restrict error + ) +{ +/* pre-conditions */ + pgm_assert (AF_INET == family || AF_INET6 == family || AF_UNSPEC == family); + pgm_assert (NULL != recv_list); + pgm_assert (NULL == *recv_list); + pgm_assert (NULL != error); + + pgm_debug ("parse_receive_entity (family:%s entity:%s%s%s interface_list:%p recv_list:%p error:%p)", + pgm_family_string (family), + entity ? "\"":"", entity ? entity : "(null)", entity ? "\"":"", + (const void*)interface_list, + (const void*)recv_list, + (const void*)error); + + struct group_source_req* recv_gsr; + struct interface_req* primary_interface = (struct interface_req*)pgm_memdup ((*interface_list)->data, sizeof(struct interface_req)); + +/* the empty entity */ + if (NULL == entity) + { +/* default receive object */ + recv_gsr = pgm_new0 (struct group_source_req, 1); + recv_gsr->gsr_interface = primary_interface->ir_interface; + recv_gsr->gsr_group.ss_family = family; + +/* track IPv6 scope from any resolved interface */ + unsigned scope_id = 0; + +/* if using unspec default group check the interface for address family + */ + if (AF_UNSPEC == recv_gsr->gsr_group.ss_family) + { + if (AF_UNSPEC == primary_interface->ir_addr.ss_family) + { + struct sockaddr_storage addr; + if (!pgm_if_getnodeaddr (AF_UNSPEC, (struct sockaddr*)&addr, sizeof(addr), error)) + { + pgm_prefix_error (error, + _("Node primary address family cannot be determined: ")); + pgm_free (recv_gsr); + pgm_free (primary_interface); + return FALSE; + } + recv_gsr->gsr_group.ss_family = addr.ss_family; + scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&addr); + +/* was an interface actually specified */ + if (primary_interface->ir_name[0] != '\0') + { + struct interface_req ir; + if (!parse_interface (recv_gsr->gsr_group.ss_family, primary_interface->ir_name, &ir, error)) + { + pgm_prefix_error (error, + _("Unique address cannot be determined for interface %s%s%s: "), + primary_interface->ir_name ? "\"" : "", primary_interface->ir_name ? primary_interface->ir_name : "(null)", primary_interface->ir_name ? "\"" : ""); + pgm_free (recv_gsr); + pgm_free (primary_interface); + return FALSE; + } + + recv_gsr->gsr_interface = ir.ir_interface; + memcpy (&primary_interface->ir_addr, &ir.ir_addr, pgm_sockaddr_len ((struct sockaddr*)&ir.ir_addr)); + scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&ir.ir_addr); + } + } + else + { +/* use interface address family for multicast group */ + recv_gsr->gsr_group.ss_family = primary_interface->ir_addr.ss_family; + scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&primary_interface->ir_addr); + } + } + + + pgm_assert (AF_UNSPEC != recv_gsr->gsr_group.ss_family); + if (AF_UNSPEC != primary_interface->ir_addr.ss_family) + { + pgm_assert (recv_gsr->gsr_group.ss_family == primary_interface->ir_addr.ss_family); + } + else + { +/* check if we can now resolve the interface by address family of the receive group */ + if (primary_interface->ir_name[0] != '\0') + { + struct interface_req ir; + if (!parse_interface (recv_gsr->gsr_group.ss_family, primary_interface->ir_name, &ir, error)) + { + pgm_prefix_error (error, + _("Unique address cannot be determined for interface %s%s%s: "), + primary_interface->ir_name ? "\"" : "", primary_interface->ir_name ? primary_interface->ir_name : "(null)", primary_interface->ir_name ? "\"" : ""); + pgm_free (recv_gsr); + pgm_free (primary_interface); + return FALSE; + } + + recv_gsr->gsr_interface = ir.ir_interface; + scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&ir.ir_addr); + } + } + +/* copy default PGM multicast group */ + switch (recv_gsr->gsr_group.ss_family) { + case AF_INET6: + memcpy (&((struct sockaddr_in6*)&recv_gsr->gsr_group)->sin6_addr, + &if6_default_group_addr, + sizeof(if6_default_group_addr)); + ((struct sockaddr_in6*)&recv_gsr->gsr_group)->sin6_scope_id = scope_id; + break; + + case AF_INET: + ((struct sockaddr_in*)&recv_gsr->gsr_group)->sin_addr.s_addr = htonl(IF_DEFAULT_GROUP); + break; + + default: + pgm_assert_not_reached(); + } + +/* ASM: source = group */ + memcpy (&recv_gsr->gsr_source, &recv_gsr->gsr_group, pgm_sockaddr_len ((struct sockaddr*)&recv_gsr->gsr_group)); + *recv_list = pgm_list_append (*recv_list, recv_gsr); + pgm_free (primary_interface); + return TRUE; + } + +/* parse one or more multicast receive groups. + */ + + int j = 0; + char** tokens = pgm_strsplit (entity, ",", 10); + while (tokens && tokens[j]) + { +/* default receive object */ + recv_gsr = pgm_new0 (struct group_source_req, 1); + recv_gsr->gsr_interface = primary_interface->ir_interface; + recv_gsr->gsr_group.ss_family = family; + + if (AF_UNSPEC == recv_gsr->gsr_group.ss_family) + { + if (AF_UNSPEC == primary_interface->ir_addr.ss_family) + { + pgm_debug ("Address family of receive group cannot be determined from interface."); + } + else + { + recv_gsr->gsr_group.ss_family = primary_interface->ir_addr.ss_family; + ((struct sockaddr_in6*)&recv_gsr->gsr_group)->sin6_scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&primary_interface->ir_addr); + } + } + + if (!parse_group (recv_gsr->gsr_group.ss_family, tokens[j], (struct sockaddr*)&recv_gsr->gsr_group, error)) + { + pgm_prefix_error (error, + _("Unresolvable receive entity %s%s%s: "), + tokens[j] ? "\"" : "", tokens[j] ? tokens[j] : "(null)", tokens[j] ? "\"" : ""); + pgm_free (recv_gsr); + pgm_strfreev (tokens); + pgm_free (primary_interface); + return FALSE; + } + +/* check if we can now resolve the source interface by address family of the receive group */ + if (AF_UNSPEC == primary_interface->ir_addr.ss_family) + { + if (primary_interface->ir_name[0] != '\0') + { + struct interface_req ir; + if (!parse_interface (recv_gsr->gsr_group.ss_family, primary_interface->ir_name, &ir, error)) + { + pgm_prefix_error (error, + _("Unique address cannot be determined for interface %s%s%s: "), + primary_interface->ir_name ? "\"" : "", primary_interface->ir_name ? primary_interface->ir_name : "(null)", primary_interface->ir_name ? "\"" : ""); + pgm_free (recv_gsr); + pgm_free (primary_interface); + return FALSE; + } + + recv_gsr->gsr_interface = ir.ir_interface; + ((struct sockaddr_in6*)&recv_gsr->gsr_group)->sin6_scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&ir.ir_addr); + } + } + else + { +/* keep interface scope */ + ((struct sockaddr_in6*)&recv_gsr->gsr_group)->sin6_scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&primary_interface->ir_addr); + } + +/* ASM: source = group */ + memcpy (&recv_gsr->gsr_source, &recv_gsr->gsr_group, pgm_sockaddr_len ((struct sockaddr*)&recv_gsr->gsr_group)); + *recv_list = pgm_list_append (*recv_list, recv_gsr); + ++j; + } + + pgm_strfreev (tokens); + pgm_free (primary_interface); + return TRUE; +} + +static +bool +parse_send_entity ( + int family, /* AF_UNSPEC | AF_INET | AF_INET6 */ + const char* restrict entity, /* null = empty entity */ + pgm_list_t** restrict interface_list, /* */ + pgm_list_t** restrict recv_list, /* */ + pgm_list_t** restrict send_list, /* */ + pgm_error_t** restrict error + ) +{ +/* pre-conditions */ + pgm_assert (AF_INET == family || AF_INET6 == family || AF_UNSPEC == family); + pgm_assert (NULL != recv_list); + pgm_assert (NULL != *recv_list); + pgm_assert (NULL != send_list); + pgm_assert (NULL == *send_list); + pgm_assert (NULL != error); + + pgm_debug ("parse_send_entity (family:%s entity:%s%s%s interface_list:%p recv_list:%p send_list:%p error:%p)", + pgm_family_string (family), + entity ? "\"":"", entity ? entity : "(null)", entity ? "\"":"", + (const void*)interface_list, + (const void*)recv_list, + (const void*)send_list, + (const void*)error); + + struct group_source_req* send_gsr; + const struct interface_req* primary_interface = (struct interface_req*)(*interface_list)->data; + + if (entity == NULL) + { + send_gsr = pgm_memdup ((*recv_list)->data, sizeof(struct group_source_req)); + *send_list = pgm_list_append (*send_list, send_gsr); + return TRUE; + } + +/* default send object */ + send_gsr = pgm_new0 (struct group_source_req, 1); + send_gsr->gsr_interface = primary_interface->ir_interface; + if (!parse_group (family, entity, (struct sockaddr*)&send_gsr->gsr_group, error)) + { + pgm_prefix_error (error, + _("Unresolvable send entity %s%s%s: "), + entity ? "\"":"", entity ? entity : "(null)", entity ? "\"":""); + pgm_free (send_gsr); + return FALSE; + } + +/* check if we can now resolve the source interface by address family of the send group */ + if (AF_UNSPEC == primary_interface->ir_addr.ss_family) + { + if (primary_interface->ir_name[0] != '\0') + { + struct interface_req ir; + if (!parse_interface (send_gsr->gsr_group.ss_family, primary_interface->ir_name, &ir, error)) + { + pgm_prefix_error (error, + _("Unique address cannot be determined for interface %s%s%s: "), + primary_interface->ir_name ? "\"":"", primary_interface->ir_name ? primary_interface->ir_name : "(null)", primary_interface->ir_name ? "\"":""); + pgm_free (send_gsr); + return FALSE; + } + + send_gsr->gsr_interface = ir.ir_interface; + ((struct sockaddr_in6*)&send_gsr->gsr_group)->sin6_scope_id = pgm_sockaddr_scope_id ((struct sockaddr*)&ir.ir_addr); + } + } + +/* ASM: source = group */ + memcpy (&send_gsr->gsr_source, &send_gsr->gsr_group, pgm_sockaddr_len ((struct sockaddr*)&send_gsr->gsr_group)); + *send_list = pgm_list_append (*send_list, send_gsr); + return TRUE; +} + +/* parse network parameter + * + * interface list; receive multicast group list; send multicast group + */ + +#define IS_HOSTNAME(x) ( /* RFC 952 */ \ + isalnum(x) || \ + ((x) == '-') || \ + ((x) == '.') \ + ) +#define IS_IP(x) ( \ + isdigit(x) || \ + ((x) == '.') || \ + ((x) == '/') \ + ) +#define IS_IP6(x) ( \ + isxdigit(x) || \ + ((x) == ':') || \ + ((x) == '/') || \ + ((x) == '.') || \ + ((x) == '[') || \ + ((x) == ']') \ + ) +/* e.g. fe80::1%eth0.620 vlan tag, + * fe80::1%eth0:0 IP alias + * fe80::1%qe0_0 Solaris link name + * + * The Linux kernel generally doesn't care too much, but everything else falls apart with + * random characters in interface names. Hyphen is a popular problematic character. + */ +#define IS_IP6_WITH_ZONE(x) ( \ + IS_IP6(x) || \ + ((x) == '%') || \ + isalpha(x) || \ + ((x) == '_') \ + ) +#define IS_NETPARAM(x) ( \ + ((x) == ',') || \ + ((x) == ';') \ + ) + +static inline +bool +is_network_char ( + const int family, + const char c + ) +{ + if (IS_HOSTNAME(c) || + (AF_INET == family && IS_IP(c)) || + ((AF_INET6 == family || AF_UNSPEC == family) && IS_IP6_WITH_ZONE(c)) || + IS_NETPARAM(c)) + return TRUE; + else + return FALSE; +} + +static +bool +network_parse ( + const char* restrict network, /* NULL terminated */ + int family, /* AF_UNSPEC | AF_INET | AF_INET6 */ + pgm_list_t** restrict recv_list, /* */ + pgm_list_t** restrict send_list, /* */ + pgm_error_t** restrict error + ) +{ + bool retval = FALSE; + const char *p = network; + const char *e = p + strlen(network); + enum { ENTITY_INTERFACE, ENTITY_RECEIVE, ENTITY_SEND, ENTITY_ERROR } ec = ENTITY_INTERFACE; + const char *b = p; /* begin of entity */ + pgm_list_t* source_list = NULL; + pgm_error_t* sub_error = NULL; + +/* pre-conditions */ + pgm_assert (NULL != network); + pgm_assert (AF_UNSPEC == family || AF_INET == family || AF_INET6 == family); + pgm_assert (NULL != recv_list); + pgm_assert (NULL != send_list); + + pgm_debug ("network_parse (network:%s%s%s family:%s recv_list:%p send_list:%p error:%p)", + network ? "\"" : "", network ? network : "(null)", network ? "\"" : "", + pgm_family_string (family), + (const void*)recv_list, + (const void*)send_list, + (const void*)error); + + while (p < e) + { + if (!is_network_char (family, *p)) + { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_INVAL, + _("'%c' is not a valid character."), + *p); + goto free_lists; + } + + if (*p == ';') /* end of entity */ + { + if (b == p) /* empty entity */ + { + switch (ec++) { + case ENTITY_INTERFACE: + retval = parse_interface_entity (family, NULL, &source_list, error); + break; + + case ENTITY_RECEIVE: + retval = parse_receive_entity (family, NULL, &source_list, recv_list, error); + break; + + case ENTITY_SEND: + retval = parse_send_entity (family, NULL, &source_list, recv_list, send_list, error); + break; + + default: + pgm_assert_not_reached(); + break; + } + + if (!retval) + goto free_lists; + + b = ++p; + continue; + } + +/* entity from b to p-1 */ + char entity[1024]; + strncpy (entity, b, sizeof(entity)); + entity[p - b] = 0; + + switch (ec++) { + case ENTITY_INTERFACE: + if (parse_interface_entity (family, entity, &source_list, &sub_error)) + break; + if (!(sub_error && PGM_ERROR_XDEV == sub_error->code)) + { +/* fall through on multicast */ + if (!(sub_error && PGM_ERROR_NOTUNIQ == sub_error->code)) + { + pgm_propagate_error (error, sub_error); + goto free_lists; + } + pgm_clear_error (&sub_error); +/* FIXME: too many interfaces */ + if (pgm_list_length (source_list) > 1) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_INVAL, + _("Send group list contains more than one entity.")); + goto free_lists; + } + break; + } + pgm_clear_error (&sub_error); + while (source_list) { + pgm_free (source_list->data); + source_list = pgm_list_delete_link (source_list, source_list); + } + if (!parse_interface_entity (family, NULL, &source_list, &sub_error) && + !(sub_error && PGM_ERROR_NOTUNIQ == sub_error->code)) + { + pgm_propagate_error (error, sub_error); + goto free_lists; + } + pgm_clear_error (&sub_error); + ec++; + + case ENTITY_RECEIVE: + if (!parse_receive_entity (family, entity, &source_list, recv_list, error)) + goto free_lists; + break; + + case ENTITY_SEND: + if (!parse_send_entity (family, entity, &source_list, recv_list, send_list, error)) + goto free_lists; + break; + + default: + pgm_assert_not_reached(); + break; + } + + b = ++p; + continue; + } + + p++; + } + + if (b < e) { + switch (ec++) { + case ENTITY_INTERFACE: + if (parse_interface_entity (family, b, &source_list, &sub_error)) + break; + if (!(sub_error && PGM_ERROR_XDEV == sub_error->code)) + { +/* fall through on multicast */ + if (!(sub_error && PGM_ERROR_NOTUNIQ == sub_error->code)) + { + pgm_propagate_error (error, sub_error); + goto free_lists; + } + pgm_clear_error (&sub_error); + +/* FIXME: too many interfaces */ + if (pgm_list_length (source_list) > 1) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_INVAL, + _("Send group list contains more than one entity.")); + goto free_lists; + } + break; + } + pgm_clear_error (&sub_error); + while (source_list) { + pgm_free (source_list->data); + source_list = pgm_list_delete_link (source_list, source_list); + } + if (!parse_interface_entity (family, NULL, &source_list, &sub_error) && + !(sub_error && PGM_ERROR_NOTUNIQ == sub_error->code)) + { + pgm_propagate_error (error, sub_error); + goto free_lists; + } + ec++; + + case ENTITY_RECEIVE: + if (!parse_receive_entity (family, b, &source_list, recv_list, error)) + goto free_lists; + break; + + case ENTITY_SEND: + if (!parse_send_entity (family, b, &source_list, recv_list, send_list, error)) + goto free_lists; + break; + + default: + pgm_assert_not_reached(); + break; + } + } + + while (ec <= ENTITY_SEND) + { + switch (ec++) { + case ENTITY_INTERFACE: + if (!parse_interface_entity (family, NULL, &source_list, error)) + goto free_lists; + break; + + case ENTITY_RECEIVE: + if (!parse_receive_entity (family, NULL, &source_list, recv_list, error)) + goto free_lists; + break; + + case ENTITY_SEND: + if (!parse_send_entity (family, NULL, &source_list, recv_list, send_list, error)) + goto free_lists; + break; + + default: + pgm_assert_not_reached(); + break; + } + } + + if (pgm_list_length (source_list) > 1) + goto free_lists; + +/* cleanup source interface list */ + while (source_list) { + pgm_free (source_list->data); + source_list = pgm_list_delete_link (source_list, source_list); + } + + return TRUE; + +free_lists: + while (source_list) { + pgm_free (source_list->data); + source_list = pgm_list_delete_link (source_list, source_list); + } + while (*recv_list) { + pgm_free ((*recv_list)->data); + *recv_list = pgm_list_delete_link (*recv_list, *recv_list); + } + while (*send_list) { + pgm_free ((*send_list)->data); + *send_list = pgm_list_delete_link (*send_list, *send_list); + } + return FALSE; +} + +/* create group_source_req as used by pgm_transport_create which specify port, address & interface. + * gsr_source is copied from gsr_group for ASM, caller needs to populate gsr_source for SSM. + * + * returns TRUE on success, returns FALSE on error and sets error appropriately. + */ + +bool +pgm_getaddrinfo ( + const char* restrict network, + const struct pgm_addrinfo_t* const restrict hints, + struct pgm_addrinfo_t** restrict res, + pgm_error_t** restrict error + ) +{ + struct pgm_addrinfo_t* ai; + const int family = hints ? hints->ai_family : AF_UNSPEC; + pgm_list_t* recv_list = NULL; /* */ + pgm_list_t* send_list = NULL; /* */ + + pgm_return_val_if_fail (NULL != network, FALSE); + pgm_return_val_if_fail (AF_UNSPEC == family || AF_INET == family || AF_INET6 == family, FALSE); + pgm_return_val_if_fail (NULL != res, FALSE); + + if (hints) { + pgm_debug ("pgm_getaddrinfo (network:%s%s%s hints: {family:%s} res:%p error:%p)", + network ? "\"" : "", network ? network : "(null)", network ? "\"" : "", + pgm_family_string (family), + (const void*)res, + (const void*)error); + } else { + pgm_debug ("pgm_getaddrinfo (network:%s%s%s hints:%p res:%p error:%p)", + network ? "\"" : "", network ? network : "(null)", network ? "\"" : "", + (const void*)hints, + (const void*)res, + (const void*)error); + } + + if (!network_parse (network, family, &recv_list, &send_list, error)) + return FALSE; + const size_t recv_list_len = pgm_list_length (recv_list); + const size_t send_list_len = pgm_list_length (send_list); + ai = pgm_malloc0 (sizeof(struct pgm_addrinfo_t) + + (recv_list_len + send_list_len) * sizeof(struct group_source_req)); + ai->ai_recv_addrs_len = recv_list_len; + ai->ai_recv_addrs = (void*)((char*)ai + sizeof(struct pgm_addrinfo_t)); + ai->ai_send_addrs_len = send_list_len; + ai->ai_send_addrs = (void*)((char*)ai->ai_recv_addrs + recv_list_len * sizeof(struct group_source_req)); + + size_t i = 0; + while (recv_list) { + memcpy (&ai->ai_recv_addrs[i++], recv_list->data, sizeof(struct group_source_req)); + pgm_free (recv_list->data); + recv_list = pgm_list_delete_link (recv_list, recv_list); + } + i = 0; + while (send_list) { + memcpy (&ai->ai_send_addrs[i++], send_list->data, sizeof(struct group_source_req)); + pgm_free (send_list->data); + send_list = pgm_list_delete_link (send_list, send_list); + } + *res = ai; + return TRUE; +} + +void +pgm_freeaddrinfo ( + struct pgm_addrinfo_t* res + ) +{ + pgm_free (res); +} + + +static +const char* +pgm_family_string ( + const int family + ) +{ + const char* c; + + switch (family) { + case AF_UNSPEC: c = "AF_UNSPEC"; break; + case AF_INET: c = "AF_INET"; break; + case AF_INET6: c = "AF_INET6"; break; + default: c = "(unknown)"; break; + } + + return c; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/if.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/if.c.c89.patch new file mode 100644 index 0000000..245479a --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/if.c.c89.patch @@ -0,0 +1,376 @@ +--- if.c 2010-05-29 17:36:46.000000000 +0800 ++++ if.c89 2010-08-05 13:19:04.000000000 +0800 +@@ -102,8 +102,13 @@ + char b[IF_NAMESIZE * 2 + 3]; + + pgm_if_indextoname (i, rname); ++#ifdef _MSC_VER ++ _snprintf_s (b, sizeof(b), _TRUNCATE, "%s (%s)", ++ ifa->ifa_name ? ifa->ifa_name : "(null)", rname); ++#else + sprintf (b, "%s (%s)", + ifa->ifa_name ? ifa->ifa_name : "(null)", rname); ++#endif + + if (NULL == ifa->ifa_addr || + (ifa->ifa_addr->sa_family != AF_INET && +@@ -121,6 +126,7 @@ + continue; + } + ++ { + char s[INET6_ADDRSTRLEN]; + getnameinfo (ifa->ifa_addr, pgm_sockaddr_len(ifa->ifa_addr), + s, sizeof(s), +@@ -137,6 +143,7 @@ + ifa->ifa_flags & IFF_BROADCAST ? "YES" : "NO ", + ifa->ifa_flags & IFF_MULTICAST ? "YES" : "NO " + ); ++ } + } + + pgm_freeifaddrs (ifap); +@@ -190,9 +197,12 @@ + pgm_inet_ntop (AF_INET6, netmask, snetmask, sizeof(snetmask))); + #endif + +- for (unsigned i = 0; i < 16; i++) ++ { ++ unsigned i; ++ for (i = 0; i < 16; i++) + if ((addr->s6_addr[i] & netmask->s6_addr[i]) != (netaddr->s6_addr[i] & netmask->s6_addr[i])) + return FALSE; ++ } + return TRUE; + } + +@@ -254,8 +264,12 @@ + { + const size_t ifnamelen = strlen(ifname); + if (']' == ifname[ ifnamelen - 1 ]) { ++#ifdef _MSC_VER ++ strncpy_s (literal, sizeof(literal), ifname + 1, ifnamelen - 2); ++#else + strncpy (literal, ifname + 1, ifnamelen - 2); + literal[ ifnamelen - 2 ] = 0; ++#endif + family = AF_INET6; /* force IPv6 evaluation */ + check_inet6_network = TRUE; /* may be a network IP or CIDR block */ + check_addr = TRUE; /* cannot be not a name */ +@@ -278,11 +292,13 @@ + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + return FALSE; + } ++ { + struct sockaddr_in s4; + memset (&s4, 0, sizeof(s4)); + s4.sin_family = AF_INET; + s4.sin_addr.s_addr = htonl (in_addr.s_addr); + memcpy (&addr, &s4, sizeof(s4)); ++ } + + check_inet_network = TRUE; + check_addr = TRUE; +@@ -297,11 +313,13 @@ + ifname ? "\"" : "", ifname ? ifname : "(null)", ifname ? "\"" : ""); + return FALSE; + } ++ { + struct sockaddr_in6 s6; + memset (&s6, 0, sizeof(s6)); + s6.sin6_family = AF_INET6; + s6.sin6_addr = in6_addr; + memcpy (&addr, &s6, sizeof(s6)); ++ } + + check_inet6_network = TRUE; + check_addr = TRUE; +@@ -310,12 +328,13 @@ + /* numeric host with scope id */ + if (!check_addr) + { +- struct addrinfo hints = { +- .ai_family = family, +- .ai_socktype = SOCK_STREAM, /* not really, SOCK_RAW */ +- .ai_protocol = IPPROTO_TCP, /* not really, IPPROTO_PGM */ +- .ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST /* AI_V4MAPPED is unhelpful */ +- }, *res; ++ struct addrinfo hints, *res; ++ memset (&hints, 0, sizeof(hints)); ++ hints.ai_family = family; ++ hints.ai_socktype = SOCK_STREAM; /* not really, SOCK_RAW */ ++ hints.ai_protocol = IPPROTO_TCP; /* not really, IPPROTO_PGM */ ++ hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST; /* AI_V4MAPPED is unhelpful */ ++ { + const int eai = getaddrinfo (ifname, NULL, &hints, &res); + switch (eai) { + case 0: +@@ -361,6 +380,7 @@ + gai_strerror (eai)); + return FALSE; + } ++ } + } + + #ifndef _WIN32 +@@ -441,13 +461,13 @@ + /* hostname lookup with potential DNS delay or error */ + if (!check_addr) + { +- struct addrinfo hints = { +- .ai_family = family, +- .ai_socktype = SOCK_STREAM, /* not really, SOCK_RAW */ +- .ai_protocol = IPPROTO_TCP, /* not really, IPPROTO_PGM */ +- .ai_flags = AI_ADDRCONFIG, /* AI_V4MAPPED is unhelpful */ +- }, *res; +- ++ struct addrinfo hints, *res; ++ memset (&hints, 0, sizeof(hints)); ++ hints.ai_family = family; ++ hints.ai_socktype = SOCK_STREAM; /* not really, SOCK_RAW */ ++ hints.ai_protocol = IPPROTO_TCP; /* not really, IPPROTO_PGM */ ++ hints.ai_flags = AI_ADDRCONFIG; /* AI_V4MAPPED is unhelpful */ ++ { + const int eai = getaddrinfo (ifname, NULL, &hints, &res); + switch (eai) { + case 0: +@@ -493,6 +513,7 @@ + gai_strerror (eai), eai); + return FALSE; + } ++ } + } + + /* iterate through interface list and match device name, ip or net address */ +@@ -525,6 +546,7 @@ + continue; + } + ++ { + const unsigned ifindex = pgm_if_nametoindex (ifa->ifa_addr->sa_family, ifa->ifa_name); + pgm_assert (0 != ifindex); + +@@ -532,7 +554,11 @@ + if (check_addr && + (0 == pgm_sockaddr_cmp (ifa->ifa_addr, (const struct sockaddr*)&addr))) + { ++#ifdef _MSC_VER ++ strcpy_s (ir->ir_name, IF_NAMESIZE, ifa->ifa_name); ++#else + strcpy (ir->ir_name, ifa->ifa_name); ++#endif + ir->ir_flags = ifa->ifa_flags; + if (ir->ir_flags & IFF_LOOPBACK) + pgm_warn (_("Interface %s reports as a loopback device."), ir->ir_name); +@@ -548,10 +574,15 @@ + if (check_inet_network && + AF_INET == ifa->ifa_addr->sa_family) + { +- const struct in_addr ifaddr = { .s_addr = ntohl (((struct sockaddr_in*)ifa->ifa_addr)->sin_addr.s_addr) }; +- const struct in_addr netmask = { .s_addr = ntohl (((struct sockaddr_in*)ifa->ifa_netmask)->sin_addr.s_addr) }; ++ struct in_addr ifaddr, netmask; ++ ifaddr.s_addr = ntohl (((struct sockaddr_in*)ifa->ifa_addr)->sin_addr.s_addr); ++ netmask.s_addr = ntohl (((struct sockaddr_in*)ifa->ifa_netmask)->sin_addr.s_addr); + if (is_in_net (&ifaddr, &in_addr, &netmask)) { ++#ifdef _MSC_VER ++ strcpy_s (ir->ir_name, IF_NAMESIZE, ifa->ifa_name); ++#else + strcpy (ir->ir_name, ifa->ifa_name); ++#endif + ir->ir_flags = ifa->ifa_flags; + if (ir->ir_flags & IFF_LOOPBACK) { + pgm_warn (_("Skipping matching loopback network device %s."), ir->ir_name); +@@ -573,7 +604,11 @@ + const struct in6_addr ifaddr = ((struct sockaddr_in6*)ifa->ifa_addr)->sin6_addr; + const struct in6_addr netmask = ((struct sockaddr_in6*)ifa->ifa_netmask)->sin6_addr; + if (is_in_net6 (&ifaddr, &in6_addr, &netmask)) { ++#ifdef _MSC_VER ++ strcpy_s (ir->ir_name, IF_NAMESIZE, ifa->ifa_name); ++#else + strcpy (ir->ir_name, ifa->ifa_name); ++#endif + ir->ir_flags = ifa->ifa_flags; + if (ir->ir_flags & IFF_LOOPBACK) { + pgm_warn (_("Skipping matching loopback network device %s."), ir->ir_name); +@@ -614,11 +649,15 @@ + } + + ir->ir_interface = ifindex; ++#ifdef _MSC_VER ++ strcpy_s (ir->ir_name, IF_NAMESIZE, ifa->ifa_name); ++#else + strcpy (ir->ir_name, ifa->ifa_name); ++#endif + memcpy (&ir->ir_addr, ifa->ifa_addr, pgm_sockaddr_len (ifa->ifa_addr)); + continue; + } +- ++ } + } + + if (0 == interface_matches) { +@@ -681,8 +720,12 @@ + const size_t grouplen = strlen(group); + if (']' == group[ grouplen - 1 ]) { + char literal[1024]; ++#ifdef _MSC_VER ++ strncpy_s (literal, sizeof(literal), group + 1, grouplen - 2); ++#else + strncpy (literal, group + 1, grouplen - 2); + literal[ grouplen - 2 ] = 0; ++#endif + if (pgm_inet_pton (AF_INET6, literal, &((struct sockaddr_in6*)addr)->sin6_addr) && + IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)addr)->sin6_addr)) + { +@@ -784,13 +827,14 @@ + #endif /* _WIN32 */ + + /* lookup group through name service */ +- struct addrinfo hints = { +- .ai_family = family, +- .ai_socktype = SOCK_STREAM, /* not really, SOCK_RAW */ +- .ai_protocol = IPPROTO_TCP, /* not really, IPPROTO_PGM */ +- .ai_flags = AI_ADDRCONFIG, /* AI_V4MAPPED is unhelpful */ +- }, *res; +- ++ { ++ struct addrinfo hints, *res; ++ memset (&hints, 0, sizeof(hints)); ++ hints.ai_family = family; ++ hints.ai_socktype = SOCK_STREAM; /* not really, SOCK_RAW */ ++ hints.ai_protocol = IPPROTO_TCP; /* not really, IPPROTO_PGM */ ++ hints.ai_flags = AI_ADDRCONFIG; /* AI_V4MAPPED is unhelpful */ ++ { + const int eai = getaddrinfo (group, NULL, &hints, &res); + if (0 != eai) { + pgm_set_error (error, +@@ -816,6 +860,8 @@ + group ? "\"" : "", group ? group : "(null)", group ? "\"" : ""); + freeaddrinfo (res); + return FALSE; ++ } ++ } + } + + /* parse an interface entity from a network parameter. +@@ -869,6 +915,7 @@ + } + + /* check interface name length limit */ ++ { + char** tokens = pgm_strsplit (entity, ",", 10); + int j = 0; + while (tokens && tokens[j]) +@@ -903,6 +950,7 @@ + + pgm_strfreev (tokens); + *interface_list = source_list; ++ } + return TRUE; + } + +@@ -945,6 +993,7 @@ + (const void*)recv_list, + (const void*)error); + ++ { + struct group_source_req* recv_gsr; + struct interface_req* primary_interface = (struct interface_req*)pgm_memdup ((*interface_list)->data, sizeof(struct interface_req)); + +@@ -957,6 +1006,7 @@ + recv_gsr->gsr_group.ss_family = family; + + /* track IPv6 scope from any resolved interface */ ++ { + unsigned scope_id = 0; + + /* if using unspec default group check the interface for address family +@@ -1053,11 +1103,13 @@ + *recv_list = pgm_list_append (*recv_list, recv_gsr); + pgm_free (primary_interface); + return TRUE; ++ } + } + + /* parse one or more multicast receive groups. + */ + ++ { + int j = 0; + char** tokens = pgm_strsplit (entity, ",", 10); + while (tokens && tokens[j]) +@@ -1126,6 +1178,8 @@ + pgm_strfreev (tokens); + pgm_free (primary_interface); + return TRUE; ++ } ++ } + } + + static +@@ -1155,6 +1209,7 @@ + (const void*)send_list, + (const void*)error); + ++ { + struct group_source_req* send_gsr; + const struct interface_req* primary_interface = (struct interface_req*)(*interface_list)->data; + +@@ -1201,6 +1256,7 @@ + memcpy (&send_gsr->gsr_source, &send_gsr->gsr_group, pgm_sockaddr_len ((struct sockaddr*)&send_gsr->gsr_group)); + *send_list = pgm_list_append (*send_list, send_gsr); + return TRUE; ++ } + } + + /* parse network parameter +@@ -1333,9 +1389,14 @@ + } + + /* entity from b to p-1 */ ++ { + char entity[1024]; ++#ifdef _MSC_VER ++ strncpy_s (entity, sizeof(entity), b, p - b); ++#else + strncpy (entity, b, sizeof(entity)); + entity[p - b] = 0; ++#endif + + switch (ec++) { + case ENTITY_INTERFACE: +@@ -1391,6 +1452,7 @@ + + b = ++p; + continue; ++ } + } + + p++; +@@ -1540,6 +1602,7 @@ + + if (!network_parse (network, family, &recv_list, &send_list, error)) + return FALSE; ++ { + const size_t recv_list_len = pgm_list_length (recv_list); + const size_t send_list_len = pgm_list_length (send_list); + ai = pgm_malloc0 (sizeof(struct pgm_addrinfo_t) + +@@ -1548,7 +1611,8 @@ + ai->ai_recv_addrs = (void*)((char*)ai + sizeof(struct pgm_addrinfo_t)); + ai->ai_send_addrs_len = send_list_len; + ai->ai_send_addrs = (void*)((char*)ai->ai_recv_addrs + recv_list_len * sizeof(struct group_source_req)); +- ++ ++ { + size_t i = 0; + while (recv_list) { + memcpy (&ai->ai_recv_addrs[i++], recv_list->data, sizeof(struct group_source_req)); +@@ -1563,6 +1627,8 @@ + } + *res = ai; + return TRUE; ++ } ++ } + } + + void diff --git a/3rdparty/openpgm-svn-r1135/pgm/if_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/if_unittest.c new file mode 100644 index 0000000..ada444a --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/if_unittest.c @@ -0,0 +1,1497 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for network interface declaration parsing. + * + * CAUTION: Assumes host is IPv4 by default for AF_UNSPEC + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* IFF_UP */ +#ifndef _BSD_SOURCE +# define _BSD_SOURCE 1 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* mock state */ + +struct mock_host_t { + struct sockaddr_storage address; + char* canonical_hostname; + char* alias; +}; + +struct mock_network_t { + char* name; + struct sockaddr_storage number; + char** aliases; +}; + +struct mock_interface_t { + unsigned int index; + char* name; + unsigned int flags; + struct sockaddr_storage addr; + struct sockaddr_storage netmask; +}; + +static GList *mock_hosts = NULL, *mock_networks = NULL, *mock_interfaces = NULL; + +#define MOCK_HOSTNAME "kiku" +#define MOCK_HOSTNAME6 "ip6-kiku" /* ping6 doesn't work on fe80:: */ +#define MOCK_NETWORK "private" /* /etc/networks */ +#define MOCK_NETWORK6 "ip6-private" +#define MOCK_PGM_NETWORK "pgm-private" +#define MOCK_PGM_NETWORK6 "pgm-ip6-private" +#define MOCK_INTERFACE "eth0" +#define MOCK_INTERFACE_INDEX 2 +#define MOCK_ADDRESS "10.6.28.33" +#define MOCK_GROUP ((in_addr_t) 0xefc00001) /* 239.192.0.1 */ +#define MOCK_GROUP6_INIT { { { 0xff,8,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } } /* ff08::1 */ +static const struct in6_addr mock_group6_addr = MOCK_GROUP6_INIT; +#define MOCK_ADDRESS6 "2002:dce8:d28e::33" +#define MOCK_ADDRESS6_INIT { { { 0x20,2,0xdc,0xe8,0xd2,0x8e,0,0,0,0,0,0,0,0,0,0x33 } } } +static const struct in6_addr mock_address6_addr = MOCK_ADDRESS6_INIT; + +static int mock_family = 0; +static char* mock_kiku = MOCK_HOSTNAME; +static char* mock_localhost = "localhost"; +static char* mock_invalid = "invalid.invalid"; /* RFC 2606 */ +static char* mock_toolong = "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij12345"; /* 65 */ +static char* mock_hostname = NULL; + +struct pgm_ifaddrs_t; +struct pgm_error_t; + +bool mock_pgm_getifaddrs (struct pgm_ifaddrs_t**, struct pgm_error_t**); +void mock_pgm_freeifaddrs (struct pgm_ifaddrs_t*); +unsigned mock_pgm_if_nametoindex (const sa_family_t, const char*); +char* mock_if_indextoname (unsigned int, char*); +int mock_getnameinfo (const struct sockaddr*, socklen_t, char*, size_t, char*, size_t, int); +int mock_getaddrinfo (const char*, const char*, const struct addrinfo*, struct addrinfo**); +void mock_freeaddrinfo (struct addrinfo*); +int mock_gethostname (char*, size_t); +struct netent* mock_getnetbyname (const char*); +bool mock_pgm_if_getnodeaddr (const sa_family_t, struct sockaddr*, const socklen_t, struct pgm_error_t**); + +#define pgm_getifaddrs mock_pgm_getifaddrs +#define pgm_freeifaddrs mock_pgm_freeifaddrs +#define pgm_if_nametoindex mock_pgm_if_nametoindex +#define if_indextoname mock_if_indextoname +#define getnameinfo mock_getnameinfo +#define getaddrinfo mock_getaddrinfo +#define freeaddrinfo mock_freeaddrinfo +#define gethostname mock_gethostname +#define getnetbyname mock_getnetbyname +#define pgm_if_getnodeaddr mock_pgm_if_getnodeaddr + + +#define IF_DEBUG +#include "if.c" + + +static +gpointer +create_host ( + const char* address, + const char* canonical_hostname, + const char* alias + ) +{ + struct mock_host_t* new_host; + + g_assert (address); + g_assert (canonical_hostname); + + new_host = g_slice_alloc0 (sizeof(struct mock_host_t)); + g_assert (pgm_sockaddr_pton (address, (struct sockaddr*)&new_host->address)); + new_host->canonical_hostname = g_strdup (canonical_hostname); + new_host->alias = alias ? g_strdup (alias) : NULL; + + return new_host; +} + +static +gpointer +create_network ( + const char* name, + const char* number + ) +{ + struct mock_network_t* new_network; + + g_assert (name); + g_assert (number); + + new_network = g_slice_alloc0 (sizeof(struct mock_network_t)); + new_network->name = g_strdup (name); + g_assert (pgm_sockaddr_pton (number, (struct sockaddr*)&new_network->number)); + + return new_network; +} + +static +gpointer +create_interface ( + const unsigned index, + const char* name, + const char* flags + ) +{ + struct mock_interface_t* new_interface; + + g_assert (name); + g_assert (flags); + + new_interface = g_slice_alloc0 (sizeof(struct mock_interface_t)); + new_interface->index = index; + new_interface->name = g_strdup (name); + + struct sockaddr_in* sin = (gpointer)&new_interface->addr; + struct sockaddr_in6* sin6 = (gpointer)&new_interface->addr; + + gchar** tokens = g_strsplit (flags, ",", 0); + for (guint i = 0; tokens[i]; i++) + { + if (strcmp (tokens[i], "up") == 0) + new_interface->flags |= IFF_UP; + else if (strcmp (tokens[i], "down") == 0) + new_interface->flags |= 0; + else if (strcmp (tokens[i], "loop") == 0) + new_interface->flags |= IFF_LOOPBACK; + else if (strcmp (tokens[i], "broadcast") == 0) + new_interface->flags |= IFF_BROADCAST; + else if (strcmp (tokens[i], "multicast") == 0) + new_interface->flags |= IFF_MULTICAST; + else if (strncmp (tokens[i], "ip=", strlen("ip=")) == 0) { + const char* addr = tokens[i] + strlen("ip="); + g_assert (pgm_sockaddr_pton (addr, (struct sockaddr*)&new_interface->addr)); + } + else if (strncmp (tokens[i], "netmask=", strlen("netmask=")) == 0) { + const char* addr = tokens[i] + strlen("netmask="); + g_assert (pgm_sockaddr_pton (addr, (struct sockaddr*)&new_interface->netmask)); + } + else if (strncmp (tokens[i], "scope=", strlen("scope=")) == 0) { + const char* scope = tokens[i] + strlen("scope="); + g_assert (AF_INET6 == ((struct sockaddr*)&new_interface->addr)->sa_family); + ((struct sockaddr_in6*)&new_interface->addr)->sin6_scope_id = atoi (scope); + } + else + g_error ("parsing failed for flag %s%s%s", + tokens[i] ? "\"" : "", tokens[i] ? tokens[i] : "(null)", tokens[i] ? "\"" : ""); + } + + g_strfreev (tokens); + return new_interface; +} + +#define APPEND_HOST2(a,b,c) \ + do { \ + gpointer data = create_host ((a), (b), (c)); \ + g_assert (data); \ + mock_hosts = g_list_append (mock_hosts, data); \ + g_assert (mock_hosts); g_assert (mock_hosts->data); \ + } while (0) +#define APPEND_HOST(a,b) APPEND_HOST2((a),(b),NULL) +#define APPEND_NETWORK(a,b) \ + do { \ + gpointer data = create_network ((a), (b)); \ + g_assert (data); \ + mock_networks = g_list_append (mock_networks, data); \ + g_assert (mock_networks); g_assert (mock_networks->data); \ + } while (0) +#define APPEND_INTERFACE(a,b,c) \ + do { \ + gpointer data = create_interface ((a), (b), (c)); \ + g_assert (data); \ + mock_interfaces = g_list_append (mock_interfaces, data); \ + g_assert (mock_interfaces); g_assert (mock_interfaces->data); \ + } while (0) +static +void +mock_setup_net (void) +{ + mock_hostname = mock_kiku; + + APPEND_HOST ( "127.0.0.1", "localhost"); + APPEND_HOST2( "10.6.28.33", "kiku.hk.miru.hk", "kiku"); + APPEND_HOST2( "2002:dce8:d28e::33", "ip6-kiku", "kiku"); + APPEND_HOST2( "172.12.90.1", "mi-hee.ko.miru.hk", "mi-hee"); + APPEND_HOST2( "::1", "ip6-localhost", "ip6-loopback"); + APPEND_HOST ( "239.192.0.1", "PGM.MCAST.NET"); + APPEND_HOST ( "ff08::1", "IP6-PGM.MCAST.NET"); + + APPEND_NETWORK( "loopback", "127.0.0.0"); + APPEND_NETWORK( "private", "10.6.28.0"); + APPEND_NETWORK( "private2", "172.16.90.0"); + APPEND_NETWORK( "pgm-private", "239.192.0.1"); +#ifdef CONFIG_HAVE_IP6_NETWORKS + APPEND_NETWORK( "ip6-private", "2002:dce8:d28e:0:0:0"); + APPEND_NETWORK( "ip6-pgm-private","ff08::1"); +#endif + + APPEND_INTERFACE( 1, "lo", "up,loop"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast"); + APPEND_INTERFACE( 3, "eth1", "down,broadcast,multicast"); + APPEND_INTERFACE( 1, "lo", "up,loop,ip=127.0.0.1,netmask=255.0.0.0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=10.6.28.33,netmask=255.255.255.0"); + APPEND_INTERFACE( 1, "lo", "up,loop,ip=::1,netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,scope=0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=2002:dce8:d28e::33,netmask=ffff:ffff:ffff:ffff::0,scope=0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=fe80::214:5eff:febd:6dda,netmask=ffff:ffff:ffff:ffff::0,scope=2"); +} + +static +void +mock_teardown_net (void) +{ + GList* list; + + list = mock_hosts; + while (list) { + struct mock_host_t* host = list->data; + g_free (host->canonical_hostname); + if (host->alias) + g_free (host->alias); + g_slice_free1 (sizeof(struct mock_host_t), host); + list = list->next; + } + g_list_free (mock_hosts); + + list = mock_networks; + while (list) { + struct mock_network_t* network = list->data; + g_free (network->name); + g_slice_free1 (sizeof(struct mock_network_t), network); + list = list->next; + } + g_list_free (mock_networks); + + list = mock_interfaces; + while (list) { + struct mock_interface_t* interface = list->data; + g_free (interface->name); + g_slice_free1 (sizeof(struct mock_interface_t), interface); + list = list->next; + } + g_list_free (mock_interfaces); +} + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + +bool +mock_pgm_getifaddrs ( + struct pgm_ifaddrs_t** ifap, + pgm_error_t** err + ) +{ + if (NULL == ifap) { + return -1; + } + + g_debug ("mock_getifaddrs (ifap:%p err:%p)", (gpointer)ifap, (gpointer)err); + + GList* list = mock_interfaces; + int n = g_list_length (list); + struct pgm_ifaddrs_t* ifa = calloc (n, sizeof(struct pgm_ifaddrs_t)); + struct pgm_ifaddrs_t* ift = ifa; + while (list) { + struct mock_interface_t* interface = list->data; + ift->ifa_addr = (gpointer)&interface->addr; + ift->ifa_name = interface->name; + ift->ifa_flags = interface->flags; + ift->ifa_netmask = (gpointer)&interface->netmask; + list = list->next; + if (list) { + ift->ifa_next = ift + 1; + ift = ift->ifa_next; + } + } + + *ifap = ifa; + return TRUE; +} + +void +mock_pgm_freeifaddrs ( + struct pgm_ifaddrs_t* ifa + ) +{ + free (ifa); +} + +unsigned +mock_pgm_if_nametoindex ( + const sa_family_t iffamily, + const char* ifname + ) +{ + GList* list = mock_interfaces; + while (list) { + const struct mock_interface_t* interface = list->data; + if (0 == strcmp (ifname, interface->name)) + return interface->index; + list = list->next; + } + return 0; +} + +char* +mock_if_indextoname ( + unsigned ifindex, + char* ifname + ) +{ + GList* list = mock_interfaces; + while (list) { + const struct mock_interface_t* interface = list->data; + if (interface->index == ifindex) { + strcpy (ifname, interface->name); + return ifname; + } + list = list->next; + } + errno = ENXIO; + return NULL; +} + +int +mock_getnameinfo ( + const struct sockaddr* sa, + socklen_t salen, + char* host, + size_t hostlen, + char* serv, + size_t servlen, + int flags + ) +{ + if ((0 == hostlen && 0 == servlen) || + (NULL == host && NULL == serv)) + return EAI_NONAME; + + if (flags & NI_NUMERICHOST && flags & NI_NAMEREQD) + return EAI_BADFLAGS; + +/* pre-conditions */ + g_assert (NULL != host); + g_assert (hostlen > 0); + g_assert (NULL == serv); + g_assert (0 == servlen); + + const int sa_family = sa->sa_family; + + if (AF_INET == sa_family) + g_assert (sizeof(struct sockaddr_in) == salen); + else { + g_assert (AF_INET6 == sa_family); + g_assert (sizeof(struct sockaddr_in6) == salen); + } + + if (!(flags & NI_NUMERICHOST)) + { + GList* list = mock_hosts; + while (list) { + const struct mock_host_t* _host = list->data; + const int host_family = ((struct sockaddr*)&_host->address)->sa_family; + const size_t host_len = AF_INET == host_family ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); + + if (host_family == sa_family && + host_len == salen && + 0 == memcmp (sa, &_host->address, salen)) + { + if (hostlen < (1 + strlen(_host->canonical_hostname))) + return EAI_OVERFLOW; + strncpy (host, _host->canonical_hostname, hostlen); + return 0; + } + list = list->next; + } + + if (flags & NI_NAMEREQD) + return EAI_NONAME; + } + + if (AF_INET == sa_family) + pgm_inet_ntop (sa_family, &((const struct sockaddr_in*)sa)->sin_addr, host, hostlen); + else { + const unsigned scope = ((const struct sockaddr_in6*)sa)->sin6_scope_id; + pgm_inet_ntop (sa_family, &((const struct sockaddr_in6*)sa)->sin6_addr, host, hostlen); + if (scope) { + char buffer[1+IF_NAMESIZE]; + strcat (host, "%"); + strcat (host, mock_if_indextoname (scope, buffer)); + } + } + return 0; +} + +int +mock_getaddrinfo ( + const char* node, + const char* service, + const struct addrinfo* hints, + struct addrinfo** res + ) +{ + const int ai_flags = hints ? hints->ai_flags : (AI_V4MAPPED | AI_ADDRCONFIG); + const int ai_family = hints ? hints->ai_family : AF_UNSPEC; + GList* list; + struct sockaddr_storage addr; + + if (NULL == node && NULL == service) + return EAI_NONAME; + +/* pre-conditions */ + g_assert (NULL != node); + g_assert (NULL == service); + g_assert (!(ai_flags & AI_CANONNAME)); + g_assert (!(ai_flags & AI_NUMERICSERV)); + g_assert (!(ai_flags & AI_V4MAPPED)); + + g_message ("mock_getaddrinfo (node:%s%s%s service:%s%s%s hints:%p res:%p)", + node ? "\"" : "", node ? node : "(null)", node ? "\"" : "", + service ? "\"" : "", service ? service : "(null)", service ? "\"" : "", + (gpointer)hints, + (gpointer)res); + + gboolean has_ip4_config; + gboolean has_ip6_config; + + if (hints && hints->ai_flags & AI_ADDRCONFIG) + { + has_ip4_config = has_ip6_config = FALSE; + list = mock_interfaces; + while (list) { + const struct mock_interface_t* interface = list->data; + if (AF_INET == ((struct sockaddr*)&interface->addr)->sa_family) + has_ip4_config = TRUE; + else if (AF_INET6 == ((struct sockaddr*)&interface->addr)->sa_family) + has_ip6_config = TRUE; + if (has_ip4_config && has_ip6_config) + break; + list = list->next; + } + } else { + has_ip4_config = has_ip6_config = TRUE; + } + + if (ai_flags & AI_NUMERICHOST) { + pgm_sockaddr_pton (node, (struct sockaddr*)&addr); + } + list = mock_hosts; + while (list) { + struct mock_host_t* host = list->data; + const int host_family = ((struct sockaddr*)&host->address)->sa_family; + if (((strcmp (host->canonical_hostname, node) == 0) || + (host->alias && strcmp (host->alias, node) == 0) || + (ai_flags & AI_NUMERICHOST && + 0 == pgm_sockaddr_cmp ((struct sockaddr*)&addr, (struct sockaddr*)&host->address))) + && + (host_family == ai_family || AF_UNSPEC == ai_family) && + ((AF_INET == host_family && has_ip4_config) || (AF_INET6 == host_family && has_ip6_config))) + { + struct addrinfo* ai = malloc (sizeof(struct addrinfo)); + memset (ai, 0, sizeof(struct addrinfo)); + ai->ai_family = host_family; + ai->ai_addrlen = AF_INET == host_family ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); + ai->ai_addr = (gpointer)&host->address; + *res = ai; + return 0; + } + list = list->next; + } + return EAI_NONAME; +} + +void +mock_freeaddrinfo ( + struct addrinfo* res + ) +{ + free (res); +} + +int +mock_gethostname ( + char* name, + size_t len + ) +{ + if (NULL == name) { + errno = EFAULT; + return -1; + } + if (len < 0) { + errno = EINVAL; + return -1; + } + if (len < (1 + strlen (mock_hostname))) { + errno = ENAMETOOLONG; + return -1; + } +/* force an error */ + if (mock_hostname == mock_toolong) { + errno = ENAMETOOLONG; + return -1; + } + strncpy (name, mock_hostname, len); + if (len > 0) + name[len - 1] = '\0'; + return 0; +} + +struct netent* +mock_getnetbyname ( + const char* name + ) +{ + static struct netent ne; + GList* list = mock_networks; + + if (NULL == name) + return NULL; + + while (list) { + const struct mock_network_t* network = list->data; + if (strcmp (network->name, name) == 0) { + ne.n_name = network->name; + ne.n_aliases = network->aliases; + ne.n_addrtype = AF_INET; + ne.n_net = g_ntohl (((struct sockaddr_in*)&network->number)->sin_addr.s_addr); + return ≠ + } + list = list->next; + } + return NULL; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_if_getnodeaddr ( + const sa_family_t family, + struct sockaddr* addr, + const socklen_t cnt, + pgm_error_t** error + ) +{ + switch (family) { + case AF_UNSPEC: + case AF_INET: + ((struct sockaddr*)addr)->sa_family = AF_INET; + ((struct sockaddr_in*)addr)->sin_addr.s_addr = inet_addr(MOCK_ADDRESS); + break; + case AF_INET6: + ((struct sockaddr*)addr)->sa_family = AF_INET6; + ((struct sockaddr_in6*)addr)->sin6_addr = mock_address6_addr; + break; + default: + g_assert_not_reached(); + } + return TRUE; +} + +/* following tests will use AF_UNSPEC address family */ + +static +void +mock_setup_unspec (void) +{ + mock_family = AF_UNSPEC; +} + +/* following tests will use AF_INET address family */ + +static +void +mock_setup_ip4 (void) +{ + mock_family = AF_INET; +} + +/* following tests will use AF_INET6 address family */ + +static +void +mock_setup_ip6 (void) +{ + mock_family = AF_INET6; +} + + +/* return 0 if gsr multicast group does not match the default PGM group for + * the address family, return -1 on no match. + */ + +static +gboolean +match_default_group ( + const int ai_family, + const struct group_source_req* gsr + ) +{ + const struct sockaddr_in sa_default = { + .sin_family = AF_INET, + .sin_addr.s_addr = g_htonl (MOCK_GROUP) + }; + const struct sockaddr_in6 sa6_default = { + .sin6_family = AF_INET6, + .sin6_addr = MOCK_GROUP6_INIT + }; + gboolean is_match = FALSE; + + switch (ai_family) { + case AF_UNSPEC: + case AF_INET: + is_match = (0 == pgm_sockaddr_cmp ((struct sockaddr*)&gsr->gsr_group, (const struct sockaddr*)&sa_default)); + if (!is_match) { + char addr1[INET6_ADDRSTRLEN], addr2[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&gsr->gsr_group, addr1, sizeof(addr1)); + pgm_sockaddr_ntop ((struct sockaddr*)&sa_default, addr2, sizeof(addr2)); + g_message ("FALSE == cmp(%s%s%s, default-group %s%s%s)", + addr1 ? "\"" : "", addr1 ? addr1 : "(null)", addr1 ? "\"" : "", + addr2 ? "\"" : "", addr2 ? addr2 : "(null)", addr2 ? "\"" : ""); + } + break; + case AF_INET6: + is_match = (0 == pgm_sockaddr_cmp ((struct sockaddr*)&gsr->gsr_group, (const struct sockaddr*)&sa6_default)); + if (!is_match) { + char addr1[INET6_ADDRSTRLEN], addr2[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&gsr->gsr_group, addr1, sizeof(addr1)); + pgm_sockaddr_ntop ((struct sockaddr*)&sa6_default, addr2, sizeof(addr2)); + g_message ("FALSE == cmp(%s%s%s, default-group %s%s%s)", + addr1 ? "\"" : "", addr1 ? addr1 : "(null)", addr1 ? "\"" : "", + addr2 ? "\"" : "", addr2 ? addr2 : "(null)", addr2 ? "\"" : ""); + } + default: + break; + } + return is_match; +} + +/* return 0 if gsr source inteface does not match the INADDR_ANY reserved + * address, return -1 on no match. + */ + +static +int +match_default_source ( + const int ai_family, + const struct group_source_req* gsr + ) +{ + if (0 != gsr->gsr_interface) + return FALSE; + +/* ASM: source == group */ + return (0 == pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_group, (const struct sockaddr*)&gsr->gsr_source)); +} + +/* return 0 if gsr source interface does not match the hosts default interface, + * return -1 on mismatch + */ + +static +int +match_default_interface ( + const int ai_family, + const struct group_source_req* gsr + ) +{ + if (MOCK_INTERFACE_INDEX != gsr->gsr_interface) + return FALSE; + +/* ASM: source == group */ + return (0 == pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_group, (const struct sockaddr*)&gsr->gsr_source)); +} + +/* target: + * bool + * pgm_getaddrinfo ( + * const char* s, + * const struct pgm_addrinfo_t* const hints, + * struct pgm_addrinfo_t** res, + * pgm_error_t** err + * ) + */ + +struct test_case_t { + const char* ip4; + const char* ip6; +}; + +#define IP4_AND_IP6(x) x, x + +static const struct test_case_t cases_001[] = { + { IP4_AND_IP6("") }, + { IP4_AND_IP6(";") }, + { IP4_AND_IP6(";;") }, + { "239.192.0.1", "ff08::1" }, + { "239.192.0.1", "[ff08::1]" }, + { ";239.192.0.1", ";ff08::1" }, + { ";239.192.0.1", ";[ff08::1]" }, + { ";239.192.0.1;239.192.0.1", ";ff08::1;ff08::1" }, + { ";239.192.0.1;239.192.0.1", ";[ff08::1];[ff08::1]" }, + { "PGM.MCAST.NET", "IP6-PGM.MCAST.NET" }, + { ";PGM.MCAST.NET", ";IP6-PGM.MCAST.NET" }, + { ";PGM.MCAST.NET;PGM.MCAST.NET", ";IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { ";239.192.0.1;PGM.MCAST.NET", ";ff08::1;IP6-PGM.MCAST.NET" }, + { ";239.192.0.1;PGM.MCAST.NET", ";[ff08::1];IP6-PGM.MCAST.NET" }, + { ";PGM.MCAST.NET;239.192.0.1", ";IP6-PGM.MCAST.NET;ff08::1" }, + { ";PGM.MCAST.NET;239.192.0.1", ";IP6-PGM.MCAST.NET;[ff08::1]" }, + { "pgm-private", /* ‡ */ "pgm-ip6-private" }, + { ";pgm-private", /* ‡ */ ";pgm-ip6-private" }, + { ";pgm-private;pgm-private", /* ‡ */ ";pgm-ip6-private;pgm-ip6-private" }, + { ";PGM.MCAST.NET;pgm-private", /* ‡ */ ";IP6-PGM.MCAST.NET;pgm-ip6-private" }, + { ";pgm-private;PGM.MCAST.NET", /* ‡ */ ";pgm-ip6-private;IP6-PGM.MCAST.NET" }, + { ";239.192.0.1;pgm-private", /* ‡ */ ";ff08::1;pgm-ip6-private" }, + { ";239.192.0.1;pgm-private", /* ‡ */ ";[ff08::1];pgm-ip6-private" }, + { ";pgm-private;239.192.0.1", /* ‡ */ ";pgm-ip6-private;ff08::1" }, + { ";pgm-private;239.192.0.1", /* ‡ */ ";pgm-ip6-private;[ff08::1]" }, +}; + +START_TEST (test_parse_transport_pass_001) +{ + fail_unless (mock_family == AF_UNSPEC || mock_family == AF_INET || mock_family == AF_INET6, "invalid mock address family"); + + const char* s = (mock_family == AF_INET6) ? cases_001[_i].ip6 : cases_001[_i].ip4; + struct pgm_addrinfo_t hints = { + .ai_family = mock_family + }, *res = NULL; + pgm_error_t* err = NULL; + + g_message ("%i: test_parse_transport_001(%s, %s%s%s)", + _i, + (mock_family == AF_INET6) ? "AF_INET6" : ( (mock_family == AF_INET) ? "AF_INET" : "AF_UNSPEC" ), + s ? "\"" : "", s ? s : "(null)", s ? "\"" : ""); + +/* ‡ Linux does not support IPv6 /etc/networks so IPv6 entries appear as 255.255.255.255 and + * pgm_if_parse_transport will fail. + */ +#ifndef CONFIG_HAVE_IP6_NETWORKS + if (NULL != strstr (s, MOCK_NETWORK6) || NULL != strstr (s, MOCK_PGM_NETWORK6)) + { + g_message ("IPv6 exception, /etc/networks not supported on this platform."); + return; + } +#endif + + gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); + if (!retval) { + g_message ("pgm_getaddrinfo: %s", + (err && err->message) ? err->message : "(null)"); + } + fail_unless (TRUE == retval, "pgm_getaddrinfo failed"); + fail_if (NULL == res, "no result"); + fail_unless (NULL == err, "error raised"); + + fail_unless (1 == res->ai_recv_addrs_len, "not exactly one receive address"); + fail_unless (match_default_group (mock_family, &res->ai_recv_addrs[0]), "receive address not match default group"); + fail_unless (match_default_source (mock_family, &res->ai_recv_addrs[0]), "receive address not match default source"); + fail_unless (1 == res->ai_send_addrs_len, "not exactly one send address"); + fail_unless (match_default_group (mock_family, &res->ai_send_addrs[0]), "send address not match default group"); + fail_unless (match_default_source (mock_family, &res->ai_send_addrs[0]), "send address not match default source"); +} +END_TEST + +/* interface name + * + * pre-condition: interface defined to match running host + * ipv4 and ipv6 hostnames are different, otherwise "" tests might go unexpected. + */ + +static const struct test_case_t cases_002[] = { + { MOCK_INTERFACE, /* † */ MOCK_INTERFACE }, + { MOCK_INTERFACE ";", /* † */ MOCK_INTERFACE ";" }, + { MOCK_INTERFACE ";;", /* † */ MOCK_INTERFACE ";;" }, + { MOCK_INTERFACE ";239.192.0.1", /* † */ MOCK_INTERFACE ";ff08::1" }, + { MOCK_INTERFACE ";239.192.0.1", /* † */ MOCK_INTERFACE ";[ff08::1]" }, + { MOCK_INTERFACE ";239.192.0.1;239.192.0.1", /* † */ MOCK_INTERFACE ";ff08::1;ff08::1" }, + { MOCK_INTERFACE ";239.192.0.1;239.192.0.1", /* † */ MOCK_INTERFACE ";[ff08::1];[ff08::1]" }, + { MOCK_INTERFACE ";PGM.MCAST.NET", /* † */ MOCK_INTERFACE ";IP6-PGM.MCAST.NET" }, + { MOCK_INTERFACE ";PGM.MCAST.NET;PGM.MCAST.NET",/* † */ MOCK_INTERFACE ";IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { MOCK_INTERFACE ";239.192.0.1;PGM.MCAST.NET", /* † */ MOCK_INTERFACE ";ff08::1;IP6-PGM.MCAST.NET" }, + { MOCK_INTERFACE ";239.192.0.1;PGM.MCAST.NET", /* † */ MOCK_INTERFACE ";[ff08::1];IP6-PGM.MCAST.NET" }, + { MOCK_INTERFACE ";PGM.MCAST.NET;239.192.0.1", /* † */ MOCK_INTERFACE ";IP6-PGM.MCAST.NET;ff08::1" }, + { MOCK_INTERFACE ";PGM.MCAST.NET;239.192.0.1", /* † */ MOCK_INTERFACE ";IP6-PGM.MCAST.NET;[ff08::1]" }, + { MOCK_INTERFACE ";pgm-private", /* ‡ */ MOCK_INTERFACE ";pgm-ip6-private" }, + { MOCK_INTERFACE ";pgm-private;pgm-private", /* ‡ */ MOCK_INTERFACE ";pgm-ip6-private;pgm-ip6-private" }, + { MOCK_ADDRESS, MOCK_ADDRESS6 }, + { MOCK_ADDRESS, "[" MOCK_ADDRESS6 "]" }, + { MOCK_ADDRESS ";", MOCK_ADDRESS6 ";" }, + { MOCK_ADDRESS ";", "[" MOCK_ADDRESS6 "];" }, + { MOCK_ADDRESS ";;", MOCK_ADDRESS6 ";;" }, + { MOCK_ADDRESS ";;", "[" MOCK_ADDRESS6 "];;" }, + { MOCK_ADDRESS ";239.192.0.1", MOCK_ADDRESS6 ";ff08::1" }, + { MOCK_ADDRESS ";239.192.0.1", "[" MOCK_ADDRESS6 "];[ff08::1]" }, + { MOCK_ADDRESS ";239.192.0.1;239.192.0.1", MOCK_ADDRESS6 ";ff08::1;ff08::1" }, + { MOCK_ADDRESS ";239.192.0.1;239.192.0.1", "[" MOCK_ADDRESS6 "];[ff08::1];[ff08::1]" }, + { MOCK_ADDRESS ";PGM.MCAST.NET", MOCK_ADDRESS6 ";IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS ";PGM.MCAST.NET", "[" MOCK_ADDRESS6 "];IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS ";PGM.MCAST.NET;PGM.MCAST.NET", MOCK_ADDRESS6 ";IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS ";PGM.MCAST.NET;PGM.MCAST.NET", "[" MOCK_ADDRESS6 "];IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS ";239.192.0.1;PGM.MCAST.NET", MOCK_ADDRESS6 ";ff08::1;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS ";239.192.0.1;PGM.MCAST.NET", "[" MOCK_ADDRESS6 "];[ff08::1];IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS ";PGM.MCAST.NET;239.192.0.1", MOCK_ADDRESS6 ";IP6-PGM.MCAST.NET;ff08::1" }, + { MOCK_ADDRESS ";PGM.MCAST.NET;239.192.0.1", "[" MOCK_ADDRESS6 "];IP6-PGM.MCAST.NET;[ff08::1]" }, + { MOCK_ADDRESS ";pgm-private", MOCK_ADDRESS6 ";pgm-ip6-private" }, + { MOCK_ADDRESS ";pgm-private", "[" MOCK_ADDRESS6 "];pgm-ip6-private" }, + { MOCK_ADDRESS ";pgm-private;pgm-private", MOCK_ADDRESS6 ";pgm-ip6-private;pgm-ip6-private" }, + { MOCK_ADDRESS ";pgm-private;pgm-private", "[" MOCK_ADDRESS6 "];pgm-ip6-private;pgm-ip6-private" }, + { MOCK_NETWORK, /* ‡ */ MOCK_NETWORK6 }, + { MOCK_NETWORK ";", /* ‡ */ MOCK_NETWORK6 ";" }, + { MOCK_NETWORK ";;", /* ‡ */ MOCK_NETWORK6 ";;" }, + { MOCK_NETWORK ";239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";ff08::1" }, + { MOCK_NETWORK ";239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";[ff08::1]" }, + { MOCK_NETWORK ";239.192.0.1;239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";ff08::1;ff08::1" }, + { MOCK_NETWORK ";239.192.0.1;239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";[ff08::1];[ff08::1]" }, + { MOCK_NETWORK ";PGM.MCAST.NET", /* ‡ */ MOCK_NETWORK6 ";IP6-PGM.MCAST.NET" }, + { MOCK_NETWORK ";PGM.MCAST.NET;PGM.MCAST.NET", /* ‡ */ MOCK_NETWORK6 ";IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { MOCK_NETWORK ";239.192.0.1;PGM.MCAST.NET", /* ‡ */ MOCK_NETWORK6 ";ff08::1;IP6-PGM.MCAST.NET" }, + { MOCK_NETWORK ";239.192.0.1;PGM.MCAST.NET", /* ‡ */ MOCK_NETWORK6 ";[ff08::1];IP6-PGM.MCAST.NET" }, + { MOCK_NETWORK ";PGM.MCAST.NET;239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";IP6-PGM.MCAST.NET;ff08::1" }, + { MOCK_NETWORK ";PGM.MCAST.NET;239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";IP6-PGM.MCAST.NET;[ff08::1]" }, + { MOCK_NETWORK ";pgm-private", /* ‡ */ MOCK_NETWORK6 ";pgm-ip6-private" }, + { MOCK_NETWORK ";pgm-private;pgm-private", /* ‡ */ MOCK_NETWORK6 ";pgm-ip6-private;pgm-ip6-private" }, + { MOCK_HOSTNAME, MOCK_HOSTNAME6 }, + { MOCK_HOSTNAME ";", MOCK_HOSTNAME6 ";" }, + { MOCK_HOSTNAME ";;", MOCK_HOSTNAME6 ";;" }, + { MOCK_HOSTNAME ";239.192.0.1", MOCK_HOSTNAME6 ";ff08::1" }, + { MOCK_HOSTNAME ";239.192.0.1", MOCK_HOSTNAME6 ";[ff08::1]" }, + { MOCK_HOSTNAME ";239.192.0.1;239.192.0.1", MOCK_HOSTNAME6 ";ff08::1;ff08::1" }, + { MOCK_HOSTNAME ";239.192.0.1;239.192.0.1", MOCK_HOSTNAME6 ";[ff08::1];[ff08::1]" }, + { MOCK_HOSTNAME ";PGM.MCAST.NET", MOCK_HOSTNAME6 ";IP6-PGM.MCAST.NET" }, + { MOCK_HOSTNAME ";PGM.MCAST.NET;PGM.MCAST.NET", MOCK_HOSTNAME6 ";IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { MOCK_HOSTNAME ";239.192.0.1;PGM.MCAST.NET", MOCK_HOSTNAME6 ";ff08::1;IP6-PGM.MCAST.NET" }, + { MOCK_HOSTNAME ";239.192.0.1;PGM.MCAST.NET", MOCK_HOSTNAME6 ";[ff08::1];IP6-PGM.MCAST.NET" }, + { MOCK_HOSTNAME ";PGM.MCAST.NET;239.192.0.1", MOCK_HOSTNAME6 ";IP6-PGM.MCAST.NET;ff08::1" }, + { MOCK_HOSTNAME ";PGM.MCAST.NET;239.192.0.1", MOCK_HOSTNAME6 ";IP6-PGM.MCAST.NET;[ff08::1]" }, + { MOCK_HOSTNAME ";pgm-private", MOCK_HOSTNAME6 ";pgm-ip6-private" }, + { MOCK_HOSTNAME ";pgm-private;pgm-private", MOCK_HOSTNAME6 ";pgm-ip6-private;pgm-ip6-private" }, +}; + +START_TEST (test_parse_transport_pass_002) +{ + fail_unless (mock_family == AF_UNSPEC || mock_family == AF_INET || mock_family == AF_INET6, "invalid mock address family"); + + const char* s = (mock_family == AF_INET6) ? cases_002[_i].ip6 : cases_002[_i].ip4; + struct pgm_addrinfo_t hints = { + .ai_family = mock_family + }, *res = NULL; + pgm_error_t* err = NULL; + + g_message ("%i: test_parse_transport_002(%s, %s%s%s)", + _i, + (mock_family == AF_INET6) ? "AF_INET6" : ( (mock_family == AF_INET) ? "AF_INET" : "AF_UNSPEC" ), + s ? "\"" : "", s ? s : "(null)", s ? "\"" : ""); + +/* ‡ Linux does not support IPv6 /etc/networks so IPv6 entries appear as 255.255.255.255 and + * pgm_if_parse_transport will fail. + */ +#ifndef CONFIG_HAVE_IP6_NETWORKS + if (NULL != strstr (s, MOCK_NETWORK6) || NULL != strstr (s, MOCK_PGM_NETWORK6)) + { + g_message ("IPv6 exception, /etc/networks not supported on this platform."); + return; + } +#endif + +/* † Multiple scoped IPv6 interfaces match a simple interface name network parameter and so + * pgm-if_parse_transport will fail finding multiple matching interfaces + */ + if (AF_INET6 == mock_family && 0 == strncmp (s, MOCK_INTERFACE, strlen (MOCK_INTERFACE))) + { + g_message ("IPv6 exception, multiple scoped addresses on one interface"); + fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); + fail_if (NULL == err, "error not raised"); + fail_unless (PGM_ERROR_NOTUNIQ == err->code, "interfaces not found unique"); + return; + } + + fail_unless (TRUE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (1 == res->ai_recv_addrs_len, "not exactly one receive address"); + fail_unless (match_default_group (mock_family, &res->ai_recv_addrs[0]), "receive address not match default group"); + fail_unless (match_default_interface (mock_family, &res->ai_recv_addrs[0]), "receive address not match default interface"); + fail_unless (1 == res->ai_send_addrs_len, "not exactly one send address"); + fail_unless (match_default_group (mock_family, &res->ai_send_addrs[0]), "send address not match default group"); + fail_unless (match_default_interface (mock_family, &res->ai_send_addrs[0]), "send address not match default interface"); +} +END_TEST + +/* network to node address in bits, 8-32 + * + * e.g. 127.0.0.1/16 + */ + +static const struct test_case_t cases_003[] = { + { MOCK_ADDRESS "/24", MOCK_ADDRESS6 "/64" }, + { MOCK_ADDRESS "/24;", MOCK_ADDRESS6 "/64;" }, + { MOCK_ADDRESS "/24;;", MOCK_ADDRESS6 "/64;;" }, + { MOCK_ADDRESS "/24;239.192.0.1", MOCK_ADDRESS6 "/64;ff08::1" }, + { MOCK_ADDRESS "/24;239.192.0.1", MOCK_ADDRESS6 "/64;[ff08::1]" }, + { MOCK_ADDRESS "/24;239.192.0.1;239.192.0.1", MOCK_ADDRESS6 "/64;ff08::1;ff08::1" }, + { MOCK_ADDRESS "/24;239.192.0.1;239.192.0.1", MOCK_ADDRESS6 "/64;[ff08::1];[ff08::1]" }, + { MOCK_ADDRESS "/24;PGM.MCAST.NET", MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS "/24;PGM.MCAST.NET;PGM.MCAST.NET",MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS "/24;239.192.0.1;PGM.MCAST.NET", MOCK_ADDRESS6 "/64;ff08::1;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS "/24;239.192.0.1;PGM.MCAST.NET", MOCK_ADDRESS6 "/64;[ff08::1];IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS "/24;PGM.MCAST.NET;239.192.0.1", MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET;ff08::1" }, + { MOCK_ADDRESS "/24;PGM.MCAST.NET;239.192.0.1", MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET;[ff08::1]" }, + { MOCK_ADDRESS "/24;PGM.MCAST.NET", MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS "/24;PGM.MCAST.NET;PGM.MCAST.NET",MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS "/24;pgm-private", /* ‡ */ MOCK_ADDRESS6 "/64;pgm-ip6-private" }, + { MOCK_ADDRESS "/24;pgm-private;pgm-private", /* ‡ */ MOCK_ADDRESS6 "/64;pgm-ip6-private;pgm-ip6-private" }, + { MOCK_ADDRESS "/24;239.192.0.1;pgm-private", /* ‡ */ MOCK_ADDRESS6 "/64;ff08::1;pgm-ip6-private" }, + { MOCK_ADDRESS "/24;239.192.0.1;pgm-private", /* ‡ */ MOCK_ADDRESS6 "/64;[ff08::1];pgm-ip6-private" }, + { MOCK_ADDRESS "/24;pgm-private;239.192.0.1", /* ‡ */ MOCK_ADDRESS6 "/64;pgm-ip6-private;ff08::1" }, + { MOCK_ADDRESS "/24;pgm-private;239.192.0.1", /* ‡ */ MOCK_ADDRESS6 "/64;pgm-ip6-private;[ff08::1]" }, + { MOCK_ADDRESS "/24;PGM.MCAST.NET;pgm-private", /* ‡ */ MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET;pgm-ip6-private" }, + { MOCK_ADDRESS "/24;pgm-private;PGM.MCAST.NET", /* ‡ */ MOCK_ADDRESS6 "/64;pgm-ip6-private;IP6-PGM.MCAST.NET" }, +}; + +START_TEST (test_parse_transport_pass_003) +{ + fail_unless (mock_family == AF_UNSPEC || mock_family == AF_INET || mock_family == AF_INET6, "invalid mock address family"); + + const char* s = (mock_family == AF_INET6) ? cases_003[_i].ip6 : cases_003[_i].ip4; + struct pgm_addrinfo_t hints = { + .ai_family = mock_family + }, *res = NULL; + pgm_error_t* err = NULL; + + g_message ("%i: test_parse_transport_003(%s, %s%s%s)", + _i, + (mock_family == AF_INET6) ? "AF_INET6" : ( (mock_family == AF_INET) ? "AF_INET" : "AF_UNSPEC" ), + s ? "\"" : "", s ? s : "(null)", s ? "\"" : ""); + +/* ‡ Linux does not support IPv6 /etc/networks so IPv6 entries appear as 255.255.255.255 and + * pgm_if_parse_transport will fail. + */ +#ifndef CONFIG_HAVE_IP6_NETWORKS + if (NULL != strstr (s, MOCK_NETWORK6) || NULL != strstr (s, MOCK_PGM_NETWORK6)) + { + g_message ("IPv6 exception, /etc/networks not supported on this platform."); + return; + } +#endif + + gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); + if (!retval) { + g_message ("pgm_getaddrinfo: %s", + (err && err->message) ? err->message : "(null)"); + } + fail_unless (TRUE == retval, "pgm_getaddrinfo failed"); + fail_unless (1 == res->ai_recv_addrs_len, "not exactly one receive address"); + fail_unless (match_default_group (mock_family, &res->ai_recv_addrs[0]), "receive address not match default group"); + fail_unless (match_default_interface (mock_family, &res->ai_recv_addrs[0]), "receive address not match default interface"); + fail_unless (1 == res->ai_send_addrs_len, "not exactly one send address"); + fail_unless (match_default_group (mock_family, &res->ai_send_addrs[0]), "send address not match default group"); + fail_unless (match_default_interface (mock_family, &res->ai_send_addrs[0]), "send address not match default interface"); +} +END_TEST + +/* asymmetric groups + */ + +START_TEST (test_parse_transport_pass_004) +{ + fail_unless (mock_family == AF_UNSPEC || mock_family == AF_INET || mock_family == AF_INET6, "invalid mock address family"); + + const char* s = (mock_family == AF_INET6) ? ";ff08::1;ff08::2" + /* AF_INET */: ";239.192.56.1;239.192.56.2"; + struct pgm_addrinfo_t hints = { + .ai_family = mock_family + }, *res = NULL; + pgm_error_t* err = NULL; + struct sockaddr_storage addr; + + fail_unless (TRUE == pgm_getaddrinfo (s, &hints, &res, &err), "get_transport_info failed"); + fail_unless (1 == res->ai_recv_addrs_len, "not exactly one receive address"); + fail_unless (1 == res->ai_send_addrs_len, "not exactly one send address"); + if (mock_family == AF_INET6) + { + inet_pton (AF_INET6, "ff08::1", &((struct sockaddr_in6*)&addr)->sin6_addr); + ((struct sockaddr*)&addr)->sa_family = mock_family; + ((struct sockaddr_in6*)&addr)->sin6_port = 0; + ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; + ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + inet_pton (AF_INET6, "ff08::2", &((struct sockaddr_in6*)&addr)->sin6_addr); + ((struct sockaddr*)&addr)->sa_family = mock_family; + ((struct sockaddr_in6*)&addr)->sin6_port = 0; + ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; + ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_send_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + } else { + inet_pton (AF_INET, "239.192.56.1", &((struct sockaddr_in*)&addr)->sin_addr); + ((struct sockaddr*)&addr)->sa_family = AF_INET; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + inet_pton (AF_INET, "239.192.56.2", &((struct sockaddr_in*)&addr)->sin_addr); + ((struct sockaddr*)&addr)->sa_family = AF_INET; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_send_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + } + fail_unless (match_default_source (mock_family, &res->ai_recv_addrs[0]), "source not match"); + fail_unless (match_default_source (mock_family, &res->ai_send_addrs[0]), "source not match"); +} +END_TEST + +/* multiple receive groups and asymmetric sending + */ + +START_TEST (test_parse_transport_pass_005) +{ + fail_unless (mock_family == AF_UNSPEC || mock_family == AF_INET || mock_family == AF_INET6, "invalid mock address family"); + + const char* s = (mock_family == AF_INET6) ? ";ff08::1,ff08::2;ff08::3" + /* AF_INET */: ";239.192.56.1,239.192.56.2;239.192.56.3"; + struct pgm_addrinfo_t hints = { + .ai_family = mock_family + }, *res = NULL; + pgm_error_t* err = NULL; + struct sockaddr_storage addr; + + fail_unless (TRUE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (2 == res->ai_recv_addrs_len, "not exactly one receive address"); + fail_unless (1 == res->ai_send_addrs_len, "not exactly one send address"); + if (mock_family == AF_INET6) + { + inet_pton (AF_INET6, "ff08::1", &((struct sockaddr_in6*)&addr)->sin6_addr); + ((struct sockaddr*)&addr)->sa_family = mock_family; + ((struct sockaddr_in6*)&addr)->sin6_port = 0; + ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; + ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + inet_pton (AF_INET6, "ff08::2", &((struct sockaddr_in6*)&addr)->sin6_addr); + ((struct sockaddr*)&addr)->sa_family = mock_family; + ((struct sockaddr_in6*)&addr)->sin6_port = 0; + ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; + ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[1].gsr_group, (struct sockaddr*)&addr), "group not match"); + inet_pton (AF_INET6, "ff08::3", &((struct sockaddr_in6*)&addr)->sin6_addr); + ((struct sockaddr*)&addr)->sa_family = mock_family; + ((struct sockaddr_in6*)&addr)->sin6_port = 0; + ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; + ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_send_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + } else { + inet_pton (AF_INET, "239.192.56.1", &((struct sockaddr_in*)&addr)->sin_addr); + ((struct sockaddr*)&addr)->sa_family = AF_INET; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + inet_pton (AF_INET, "239.192.56.2", &((struct sockaddr_in*)&addr)->sin_addr); + ((struct sockaddr*)&addr)->sa_family = AF_INET; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[1].gsr_group, (struct sockaddr*)&addr), "group not match"); + inet_pton (AF_INET, "239.192.56.3", &((struct sockaddr_in*)&addr)->sin_addr); + ((struct sockaddr*)&addr)->sa_family = AF_INET; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_send_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + } + fail_unless (match_default_source (mock_family, &res->ai_recv_addrs[0]), "source not match"); + fail_unless (match_default_source (mock_family, &res->ai_send_addrs[0]), "source not match"); +} +END_TEST + + +/* too many interfaces + */ +START_TEST (test_parse_transport_fail_001) +{ + const char* s = "eth0,lo;;;"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* invalid characters, or simply just bogus + */ +START_TEST (test_parse_transport_fail_002) +{ + const char* s = "!@#$%^&*()"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* too many groups + */ +START_TEST (test_parse_transport_fail_003) +{ + const char* s = ";239.192.0.1,239.192.0.2,239.192.0.3,239.192.0.4,239.192.0.5,239.192.0.6,239.192.0.7,239.192.0.8,239.192.0.9,239.192.0.10,239.192.0.11,239.192.0.12,239.192.0.13,239.192.0.14,239.192.0.15,239.192.0.16,239.192.0.17,239.192.0.18,239.192.0.19,239.192.0.20;239.192.0.21"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* too many receiver groups in asymmetric pairing + */ +START_TEST (test_parse_transport_fail_004) +{ + const char* s = ";239.192.0.1,239.192.0.2,239.192.0.3,239.192.0.4,239.192.0.5,239.192.0.6,239.192.0.7,239.192.0.8,239.192.0.9,239.192.0.10,239.192.0.11,239.192.0.12,239.192.0.13,239.192.0.14,239.192.0.15,239.192.0.16,239.192.0.17,239.192.0.18,239.192.0.19,239.192.0.20,239.192.0.21;239.192.0.22"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* null string + */ +START_TEST (test_parse_transport_fail_005) +{ + const char* s = NULL; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* invalid address family + */ +START_TEST (test_parse_transport_fail_006) +{ + const char* s = ";"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_IPX + }, *res = NULL; + pgm_error_t* err = NULL; + + fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* invalid transport info pointer + */ +START_TEST (test_parse_transport_fail_007) +{ + const char* s = ";"; + pgm_error_t* err = NULL; + + fail_unless (FALSE == pgm_getaddrinfo (s, NULL, NULL, &err), "pgm_getaddrinfo failed"); +} +END_TEST + +/* invalid interface + */ +START_TEST (test_parse_transport_fail_008) +{ + const char* s = "qe0;"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); + if (!retval) { + g_message ("pgm_getaddrinfo: %s", err ? err->message : "(null)"); + } + fail_unless (FALSE == retval, "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* non-existing interface IP address + */ +START_TEST (test_parse_transport_fail_009) +{ + const char* s = "172.16.90.1;"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); + if (!retval) { + g_message ("pgm_getaddrinfo: %s", + (err && err->message) ? err->message : "(null)"); + } + fail_unless (FALSE == retval, "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* non-existing network name address + */ +START_TEST (test_parse_transport_fail_010) +{ + const char* s = "private2;"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); + if (!retval) { + g_message ("pgm_getaddrinfo: %s", + (err && err->message) ? err->message : "(null)"); + } + fail_unless (FALSE == retval, "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* non-existing host name interface + */ +START_TEST (test_parse_transport_fail_011) +{ + const char* s = "mi-hee.ko.miru.hk;"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); + if (!retval) { + g_message ("pgm_getaddrinfo: %s", + (err && err->message) ? err->message : "(null)"); + } + fail_unless (FALSE == retval, "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* target: + * pgm_if_print_all (void) + */ + +START_TEST (test_print_all_pass_001) +{ + pgm_if_print_all (); +} +END_TEST + + +/* target: + * bool + * is_in_net ( + * const struct in_addr* addr, -- in host byte order + * const struct in_addr* netaddr, + * const struct in_addr* netmask + * ) + */ + +struct test_case_net_t { + const char* addr; + const char* netaddr; + const char* netmask; + const gboolean answer; +}; + +static const struct test_case_net_t cases_004[] = { + { "127.0.0.1", "127.0.0.1", "255.0.0.0", TRUE }, + { "127.0.0.1", "127.0.0.1", "255.255.0.0", TRUE }, + { "127.0.0.1", "127.0.0.1", "255.255.255.0", TRUE }, + { "127.0.0.1", "127.0.0.1", "255.255.255.255", TRUE }, + { "127.0.0.1", "127.0.0.0", "255.0.0.0", TRUE }, + { "127.0.0.1", "127.0.0.0", "255.255.0.0", TRUE }, + { "127.0.0.1", "127.0.0.0", "255.255.255.0", TRUE }, + { "127.0.0.1", "127.0.0.0", "255.255.255.255", FALSE }, + { "172.15.1.1", "172.16.0.0", "255.240.0.0", FALSE }, + { "172.16.1.1", "172.16.0.0", "255.240.0.0", TRUE }, + { "172.18.1.1", "172.16.0.0", "255.240.0.0", TRUE }, + { "172.31.1.1", "172.16.0.0", "255.240.0.0", TRUE }, + { "172.32.1.1", "172.16.0.0", "255.240.0.0", FALSE }, +}; + +START_TEST (test_is_in_net_pass_001) +{ + struct in_addr addr, netaddr, netmask; + fail_unless (pgm_inet_pton (AF_INET, cases_004[_i].addr, &addr)); + fail_unless (pgm_inet_pton (AF_INET, cases_004[_i].netaddr, &netaddr)); + fail_unless (pgm_inet_pton (AF_INET, cases_004[_i].netmask, &netmask)); + const gboolean answer = cases_004[_i].answer; + + addr.s_addr = g_ntohl (addr.s_addr); + netaddr.s_addr = g_ntohl (netaddr.s_addr); + netmask.s_addr = g_ntohl (netmask.s_addr); + gboolean result = is_in_net (&addr, &netaddr, &netmask); + + g_message ("result %s (%s)", + result ? "TRUE" : "FALSE", + answer ? "TRUE" : "FALSE"); + + fail_unless (answer == result); +} +END_TEST + +static const struct test_case_net_t cases_005[] = { + { "::1", "::1", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", TRUE }, + { "fe80::203:baff:fe4e:6cc8", "fe80::", "ffff:0000:0000:0000:0000:0000:0000:0000", TRUE }, + { "2002:dec8:d28e::36", "2002:dec8:d28e::", "ffff:ffff:ffff:0000:0000:0000:0000:0000", TRUE }, + { "2002:dec8:d28e::36", "2002:dafa:939:0::", "ffff:ffff:ffff:ffff:0000:0000:0000:0000", FALSE }, +}; + +START_TEST (test_is_in_net6_pass_001) +{ + struct in6_addr addr, netaddr, netmask; + fail_unless (pgm_inet_pton (AF_INET6, cases_005[_i].addr, &addr)); + fail_unless (pgm_inet_pton (AF_INET6, cases_005[_i].netaddr, &netaddr)); + fail_unless (pgm_inet_pton (AF_INET6, cases_005[_i].netmask, &netmask)); + const gboolean answer = cases_005[_i].answer; + + gboolean result = is_in_net6 (&addr, &netaddr, &netmask); + + g_message ("result %s (%s)", + result ? "TRUE" : "FALSE", + answer ? "TRUE" : "FALSE"); + + fail_unless (answer == result); +} +END_TEST + + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_is_in_net = tcase_create ("is_in_net"); + suite_add_tcase (s, tc_is_in_net); + tcase_add_checked_fixture (tc_is_in_net, mock_setup_net, mock_teardown_net); + tcase_add_checked_fixture (tc_is_in_net, mock_setup_unspec, NULL); + tcase_add_loop_test (tc_is_in_net, test_is_in_net_pass_001, 0, G_N_ELEMENTS(cases_004)); + + TCase* tc_is_in_net6 = tcase_create ("is_in_net6"); + suite_add_tcase (s, tc_is_in_net6); + tcase_add_checked_fixture (tc_is_in_net6, mock_setup_net, mock_teardown_net); + tcase_add_checked_fixture (tc_is_in_net6, mock_setup_unspec, NULL); + tcase_add_loop_test (tc_is_in_net6, test_is_in_net6_pass_001, 0, G_N_ELEMENTS(cases_005)); + +/* three variations of all parse-transport tests, one for each valid + * address family value: AF_UNSPEC, AF_INET, AF_INET6. + */ + +/* unspecified address family, ai_family == AF_UNSPEC */ + TCase* tc_parse_transport_unspec = tcase_create ("parse_transport/unspec"); + suite_add_tcase (s, tc_parse_transport_unspec); + tcase_add_checked_fixture (tc_parse_transport_unspec, mock_setup_net, mock_teardown_net); + tcase_add_checked_fixture (tc_parse_transport_unspec, mock_setup_unspec, NULL); + tcase_add_loop_test (tc_parse_transport_unspec, test_parse_transport_pass_001, 0, G_N_ELEMENTS(cases_001)); + tcase_add_loop_test (tc_parse_transport_unspec, test_parse_transport_pass_002, 0, G_N_ELEMENTS(cases_002)); + tcase_add_loop_test (tc_parse_transport_unspec, test_parse_transport_pass_003, 0, G_N_ELEMENTS(cases_003)); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_pass_004); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_pass_005); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_001); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_002); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_003); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_004); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_005); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_006); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_007); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_008); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_009); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_010); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_011); + +/* IP version 4, ai_family = AF_INET */ + TCase* tc_parse_transport_ip4 = tcase_create ("parse_transport/af_inet"); + suite_add_tcase (s, tc_parse_transport_ip4); + tcase_add_checked_fixture (tc_parse_transport_ip4, mock_setup_net, mock_teardown_net); + tcase_add_checked_fixture (tc_parse_transport_ip4, mock_setup_ip4, NULL); + tcase_add_loop_test (tc_parse_transport_ip4, test_parse_transport_pass_001, 0, G_N_ELEMENTS(cases_001)); + tcase_add_loop_test (tc_parse_transport_ip4, test_parse_transport_pass_002, 0, G_N_ELEMENTS(cases_002)); + tcase_add_loop_test (tc_parse_transport_ip4, test_parse_transport_pass_003, 0, G_N_ELEMENTS(cases_003)); + tcase_add_test (tc_parse_transport_ip4, test_parse_transport_pass_004); + tcase_add_test (tc_parse_transport_ip4, test_parse_transport_pass_005); + +/* IP version 6, ai_family = AF_INET6 */ + TCase* tc_parse_transport_ip6 = tcase_create ("parse_transport/af_inet6"); + suite_add_tcase (s, tc_parse_transport_ip6); + tcase_add_checked_fixture (tc_parse_transport_ip6, mock_setup_net, mock_teardown_net); + tcase_add_checked_fixture (tc_parse_transport_ip6, mock_setup_ip6, NULL); + tcase_add_loop_test (tc_parse_transport_ip6, test_parse_transport_pass_001, 0, G_N_ELEMENTS(cases_001)); + tcase_add_loop_test (tc_parse_transport_ip6, test_parse_transport_pass_002, 0, G_N_ELEMENTS(cases_002)); + tcase_add_loop_test (tc_parse_transport_ip6, test_parse_transport_pass_003, 0, G_N_ELEMENTS(cases_003)); + tcase_add_test (tc_parse_transport_ip6, test_parse_transport_pass_004); + tcase_add_test (tc_parse_transport_ip6, test_parse_transport_pass_005); + + TCase* tc_print_all = tcase_create ("print-all"); + tcase_add_checked_fixture (tc_print_all, mock_setup_net, mock_teardown_net); + suite_add_tcase (s, tc_print_all); + tcase_add_test (tc_print_all, test_print_all_pass_001); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/checksum.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/checksum.h new file mode 100644 index 0000000..67f71a6 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/checksum.h @@ -0,0 +1,75 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM checksum routines + * + * Copyright (c) 2006-2008 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_CHECKSUM_H__ +#define __PGM_IMPL_CHECKSUM_H__ + +#include + +PGM_BEGIN_DECLS + +uint16_t pgm_inet_checksum (const void*, uint16_t, uint16_t); +uint16_t pgm_csum_fold (uint32_t) PGM_GNUC_CONST; +uint32_t pgm_csum_block_add (uint32_t, uint32_t, const uint16_t) PGM_GNUC_CONST; +uint32_t pgm_compat_csum_partial (const void*, uint16_t, uint32_t); +uint32_t pgm_compat_csum_partial_copy (const void*restrict, void*restrict, uint16_t, uint32_t); + +static inline uint32_t add32_with_carry (uint32_t, uint32_t) PGM_GNUC_CONST; + +#if defined(__x86_64__) || defined(__i386__) || defined(__i386) || defined(__amd64) +static inline uint32_t add32_with_carry (uint32_t a, uint32_t b) +{ + asm("addl %2, %0 \n\t" + "adcl $0, %0" + : "=r" (a) /* output operands */ + : "0" (a), "r" (b)); /* input operands */ + return a; +} +#elif defined(__sparc__) || defined(__sparc) || defined(__sparcv9) +static inline uint32_t add32_with_carry (uint32_t a, uint32_t b) +{ + asm("addcc %2, %0, %0 \n\t" + "addx %0, %%g0, %0" + : "=r" (a) /* output operands */ + : "0" (a), "r" (b) /* input operands */ + : "cc"); /* list of clobbered registers */ + return a; +} +#else +static inline uint32_t add32_with_carry (uint32_t a, uint32_t b) +{ + a += b; + a = (a >> 16) + (a & 0xffff); + return a; +} +#endif + +# define pgm_csum_partial pgm_compat_csum_partial +# define pgm_csum_partial_copy pgm_compat_csum_partial_copy + +PGM_END_DECLS + +#endif /* __PGM_IMPL_CHECKSUM_H__ */ + diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/engine.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/engine.h new file mode 100644 index 0000000..eeacbe7 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/engine.h @@ -0,0 +1,43 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM engine. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_ENGINE_H__ +#define __PGM_IMPL_ENGINE_H__ + +#ifdef _WIN32 +# include +# include +#endif +#include + +PGM_BEGIN_DECLS + +#ifdef _WIN32 +extern LPFN_WSARECVMSG pgm_WSARecvMsg; +#endif + +#ifdef PGM_DEBUG +extern unsigned pgm_loss_rate; +#endif + +PGM_END_DECLS + +#endif /* __PGM_IMPL_ENGINE_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/features.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/features.h new file mode 100644 index 0000000..a8dadf7 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/features.h @@ -0,0 +1,42 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Compiler feature flags. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_FEATURES_H__ +#define __PGM_IMPL_FEATURES_H__ + +#if defined(_POSIX_C_SOURCE) || defined(__POSIX_VISIBLE) +# if (_POSIX_C_SOURCE - 0) >= 200112L || (__POSIX_VISIBLE - 0) >= 200112L +# define CONFIG_HAVE_FTIME 1 +# define CONFIG_HAVE_GETTIMEOFDAY 1 +# endif +# if (_POSIX_C_SOURCE - 0) >= 199309L || (__POSIX_VISIBLE - 0) >= 199309L +# define CONFIG_HAVE_CLOCK_GETTIME 1 +# endif +#endif +#if defined(_WIN32) +# define CONFIG_HAVE_FTIME +#endif + +#endif /* __PGM_IMPL_FEATURES_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/fixed.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/fixed.h new file mode 100644 index 0000000..f864989 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/fixed.h @@ -0,0 +1,156 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * 8-bit and 16-bit shift fixed point math + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_FIXED_H__ +#define __PGM_IMPL_FIXED_H__ + +#include + +PGM_BEGIN_DECLS + +static inline uint_fast32_t pgm_fp8 (unsigned) PGM_GNUC_CONST; +static inline uint_fast32_t pgm_fp16 (unsigned) PGM_GNUC_CONST; +static inline unsigned pgm_fp8tou (uint_fast32_t) PGM_GNUC_CONST; +static inline unsigned pgm_fp16tou (uint_fast32_t) PGM_GNUC_CONST; +static inline uint_fast32_t pgm_fp8mul (uint_fast32_t, uint_fast32_t) PGM_GNUC_CONST; +static inline uint_fast32_t pgm_fp16mul (uint_fast32_t, uint_fast32_t) PGM_GNUC_CONST; +static inline uint_fast32_t pgm_fp8div (uint_fast32_t, uint_fast32_t) PGM_GNUC_CONST; +static inline uint_fast32_t pgm_fp16div (uint_fast32_t, uint_fast32_t) PGM_GNUC_CONST; +static inline uint_fast32_t pgm_fp16pow (uint_fast32_t, uint_fast32_t) PGM_GNUC_CONST; + +static inline +uint_fast32_t +pgm_fp8 ( + unsigned v + ) +{ + return (uint32_t)(v << 8); +} + +static inline +uint_fast32_t +pgm_fp16 ( + unsigned v + ) +{ + return (uint_fast32_t)(v << 16); +} + +static inline +unsigned +pgm_fp8tou ( + uint_fast32_t f + ) +{ + return (f + (1 << 7)) >> 8; +} + +static inline +unsigned +pgm_fp16tou ( + uint_fast32_t f + ) +{ + return (f + (1 << 15)) >> 16; +} + +static inline +uint_fast32_t +pgm_fp8mul ( + uint_fast32_t a, + uint_fast32_t b + ) +{ + return ( a * b + 128 ) >> 8; +} + +static inline +uint_fast32_t +pgm_fp16mul ( + uint_fast32_t a, + uint_fast32_t b + ) +{ + return ( a * b + 32768 ) >> 16; +} + +static inline +uint_fast32_t +pgm_fp8div ( + uint_fast32_t a, + uint_fast32_t b + ) +{ + return ( ( (a << 9) / b ) + 1 ) / 2; +} + +static inline +uint_fast32_t +pgm_fp16div ( + uint_fast32_t a, + uint_fast32_t b + ) +{ + return ( ( (a << 17) / b ) + 1 ) / 2; +} + +static inline +uint_fast32_t +pgm_fp16pow ( + uint_fast32_t x, + uint_fast32_t y + ) +{ + uint_fast32_t result = pgm_fp16 (1); +#if defined(__STDC_VERSION__) && (__STDC_VERSION >= 199901L) +/* C99 version */ + for (uint_fast32_t i = x; + y; + y >>= 1) + { + if (y & 1) + result = (result * i + 32768) >> 16; + i = (i * i + 32768) >> 16; + } +#else +/* C89 version */ + { + uint_fast32_t i; + for (i = x; + y; + y >>= 1) + { + if (y & 1) + result = (result * i + 32768) >> 16; + i = (i * i + 32768) >> 16; + } + } +#endif + return result; +} + +PGM_END_DECLS + +#endif /* __PGM_IMPL_FIXED_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/framework.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/framework.h new file mode 100644 index 0000000..951ad26 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/framework.h @@ -0,0 +1,76 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Framework collection. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_FRAMEWORK_H__ +#define __PGM_IMPL_FRAMEWORK_H__ + +#define __PGM_IMPL_FRAMEWORK_H_INSIDE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef __PGM_IMPL_FRAMEWORK_H_INSIDE__ + +#endif /* __PGM_IMPL_FRAMEWORK_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/galois.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/galois.h new file mode 100644 index 0000000..e90e66b --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/galois.h @@ -0,0 +1,153 @@ +/* + * Galois field maths. + * + * Copyright (c) 2006-2008 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_GALOIS_H__ +#define __PGM_IMPL_GALOIS_H__ + +#include + +PGM_BEGIN_DECLS + +/* 8 bit wide galois field integer: GF(2⁸) */ +#ifdef _MSC_VER +typedef uint8_t pgm_gf8_t; +#else +typedef uint8_t __attribute__((__may_alias__)) pgm_gf8_t; +#endif + +/* E denotes the encoding symbol length in bytes. + * S denotes the symbol size in units of m-bit elements. When m = 8, + * then S and E are equal. + */ +#define PGM_GF_ELEMENT_BYTES sizeof(pgm_gf8_t) + +/* m defines the length of the elements in the finite field, in bits. + * m belongs to {2..16}. + */ +#define PGM_GF_ELEMENT_BITS ( 8 * PGM_GF_ELEMENT_BYTES ) + +/* q defines the number of elements in the finite field. + */ +#define PGM_GF_NO_ELEMENTS ( 1 << PGM_GF_ELEMENT_BITS ) +#define PGM_GF_MAX ( PGM_GF_NO_ELEMENTS - 1 ) + + +extern const pgm_gf8_t pgm_gflog[PGM_GF_NO_ELEMENTS]; +extern const pgm_gf8_t pgm_gfantilog[PGM_GF_NO_ELEMENTS]; + +#ifdef CONFIG_GALOIS_MUL_LUT +extern const pgm_gf8_t pgm_gftable[PGM_GF_NO_ELEMENTS * PGM_GF_NO_ELEMENTS]; +#endif + +/* In a finite field with characteristic 2, addition and subtraction are + * identical, and are accomplished using the XOR operator. + */ +static inline +pgm_gf8_t +pgm_gfadd ( + pgm_gf8_t a, + pgm_gf8_t b + ) +{ + return a ^ b; +} + +static inline +pgm_gf8_t +pgm_gfadd_equals ( + pgm_gf8_t a, + pgm_gf8_t b + ) +{ + return a ^= b; +} + +static inline +pgm_gf8_t +pgm_gfsub ( + pgm_gf8_t a, + pgm_gf8_t b + ) +{ + return pgm_gfadd (a, b); +} + +static inline +pgm_gf8_t +pgm_gfsub_equals ( + pgm_gf8_t a, + pgm_gf8_t b + ) +{ + return pgm_gfadd_equals (a, b); +} + +static inline +pgm_gf8_t +pgm_gfmul ( + pgm_gf8_t a, + pgm_gf8_t b + ) +{ + if (PGM_UNLIKELY( !(a && b) )) { + return 0; + } + +#ifdef CONFIG_GALOIS_MUL_LUT + return pgm_gftable[ (uint16_t)a << 8 | (uint16_t)b ]; +#else + unsigned sum = pgm_gflog[ a ] + pgm_gflog[ b ]; + return sum >= PGM_GF_MAX ? pgm_gfantilog[ sum - PGM_GF_MAX ] : pgm_gfantilog[ sum ]; +#endif +} + +static inline +pgm_gf8_t +pgm_gfdiv ( + pgm_gf8_t a, + pgm_gf8_t b + ) +{ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +/* C99 version */ + if (PGM_UNLIKELY( !a )) { + return 0; + } + + const int sum = pgm_gflog[ a ] - pgm_gflog[ b ]; + return sum < 0 ? pgm_gfantilog[ sum + PGM_GF_MAX ] : pgm_gfantilog[ sum ]; +#else +/* C89 version */ + const int sum = pgm_gflog[ a ] - pgm_gflog[ b ]; + if (PGM_UNLIKELY( !a )) { + return 0; + } + + return sum < 0 ? pgm_gfantilog[ sum + PGM_GF_MAX ] : pgm_gfantilog[ sum ]; +#endif +} + +PGM_END_DECLS + +#endif /* __PGM_IMPL_GALOIS_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/getifaddrs.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/getifaddrs.h new file mode 100644 index 0000000..eddb37a --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/getifaddrs.h @@ -0,0 +1,76 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable getifaddrs + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_GETIFADDRS_H__ +#define __PGM_IMPL_GETIFADDRS_H__ + +#ifndef _WIN32 +# include +# include +# include +#else +# include +#endif + +struct pgm_ifaddrs_t; + +#include +#include + +PGM_BEGIN_DECLS + +#ifndef IF_NAMESIZE +# ifdef IFNAMSIZ +# define IF_NAMESIZE IFNAMSIZ +# elif defined(MAX_INTERFACE_NAME_LEN) +# define IF_NAMESIZE MAX_INTERFACE_NAME_LEN +# elif defined(_WIN32) +/* 40 for UUID, 256 for device path */ +# define IF_NAMESIZE 256 +# else +# define IF_NAMESIZE 16 +# endif +#endif + +struct pgm_ifaddrs_t +{ + struct pgm_ifaddrs_t* ifa_next; /* Pointer to the next structure. */ + + char* ifa_name; /* Name of this network interface. */ + unsigned int ifa_flags; /* Flags as from SIOCGIFFLAGS ioctl. */ + +#ifdef ifa_addr +# undef ifa_addr +#endif + struct sockaddr* ifa_addr; /* Network address of this interface. */ + struct sockaddr* ifa_netmask; /* Netmask of this interface. */ +}; + +bool pgm_getifaddrs (struct pgm_ifaddrs_t**restrict, pgm_error_t**restrict); +void pgm_freeifaddrs (struct pgm_ifaddrs_t*); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_GETIFADDRS_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/getnodeaddr.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/getnodeaddr.h new file mode 100644 index 0000000..befcf35 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/getnodeaddr.h @@ -0,0 +1,41 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable function to return node IP address. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_GETNODEADDR_H__ +#define __PGM_IMPL_GETNODEADDR_H__ + +#ifndef _WIN32 +# include +#endif +#include +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL bool pgm_if_getnodeaddr (const sa_family_t, struct sockaddr*restrict, const socklen_t, pgm_error_t**restrict); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_GETNODEADDR_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/hashtable.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/hashtable.h new file mode 100644 index 0000000..4271cb6 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/hashtable.h @@ -0,0 +1,58 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable hash table. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_HASHTABLE_H__ +#define __PGM_IMPL_HASHTABLE_H__ + +#include + +PGM_BEGIN_DECLS + +typedef struct pgm_hashtable_t pgm_hashtable_t; +typedef uint_fast32_t pgm_hash_t; + +typedef pgm_hash_t (*pgm_hashfunc_t) (const void*); +typedef bool (*pgm_equalfunc_t) (const void*restrict, const void*restrict); + +PGM_GNUC_INTERNAL pgm_hashtable_t* pgm_hashtable_new (pgm_hashfunc_t, pgm_equalfunc_t); +PGM_GNUC_INTERNAL void pgm_hashtable_destroy (pgm_hashtable_t*); +PGM_GNUC_INTERNAL void pgm_hashtable_insert (pgm_hashtable_t*restrict, const void*restrict, void*restrict); +PGM_GNUC_INTERNAL bool pgm_hashtable_remove (pgm_hashtable_t*restrict, const void*restrict); +PGM_GNUC_INTERNAL void pgm_hashtable_remove_all (pgm_hashtable_t*); +PGM_GNUC_INTERNAL void* pgm_hashtable_lookup (const pgm_hashtable_t*restrict, const void*restrict); +PGM_GNUC_INTERNAL void* pgm_hashtable_lookup_extended (const pgm_hashtable_t*restrict, const void*restrict, void*restrict); +PGM_GNUC_INTERNAL void pgm_hashtable_unref (pgm_hashtable_t*); + +/* Hash Functions + */ + +PGM_GNUC_INTERNAL bool pgm_str_equal (const void*restrict, const void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL pgm_hash_t pgm_str_hash (const void*) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_int_equal (const void*restrict, const void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL pgm_hash_t pgm_int_hash (const void*) PGM_GNUC_WARN_UNUSED_RESULT; + +PGM_END_DECLS + +#endif /* __PGM_IMPL_HASHTABLE_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/histogram.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/histogram.h new file mode 100644 index 0000000..92ddeb9 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/histogram.h @@ -0,0 +1,129 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * histograms. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_HISTOGRAM_H__ +#define __PGM_IMPL_HISTOGRAM_H__ + +#include +#include +#include +#include + +PGM_BEGIN_DECLS + +typedef int pgm_sample_t; +typedef int pgm_count_t; + +struct pgm_sample_set_t { + pgm_count_t* counts; + unsigned counts_len; + int64_t sum; + int64_t square_sum; +}; + +typedef struct pgm_sample_set_t pgm_sample_set_t; + +struct pgm_histogram_t { + const char* restrict histogram_name; + unsigned bucket_count; + pgm_sample_t declared_min; + pgm_sample_t declared_max; + pgm_sample_t* restrict ranges; + pgm_sample_set_t sample; + bool is_registered; + pgm_slist_t histograms_link; +}; + +typedef struct pgm_histogram_t pgm_histogram_t; + +#define PGM_HISTOGRAM_DEFINE(name, minimum, maximum, count) \ + static pgm_count_t counts[ (count) ]; \ + static pgm_sample_t ranges[ (count) + 1 ]; \ + static pgm_histogram_t counter = { \ + .histogram_name = (name), \ + .bucket_count = (count), \ + .declared_min = (minimum), \ + .declared_max = (maximum), \ + .ranges = ranges, \ + .sample = { \ + .counts = counts, \ + .counts_len = (count), \ + .sum = 0, \ + .square_sum = 0 \ + }, \ + .is_registered = FALSE \ + } + +#ifdef CONFIG_HISTOGRAMS + +# define PGM_HISTOGRAM_TIMES(name, sample) do { \ + PGM_HISTOGRAM_DEFINE(name, pgm_msecs(1), pgm_secs(10), 50); \ + if (!counter.is_registered) { \ + memset (counts, 0, sizeof(counts)); \ + memset (ranges, 0, sizeof(ranges)); \ + pgm_histogram_init (&counter); \ + } \ + pgm_histogram_add_time (&counter, sample); \ + } while (0) + +# define PGM_HISTOGRAM_COUNTS(name, sample) do { \ + PGM_HISTOGRAM_DEFINE(name, 1, 1000000, 50); \ + if (!counter.is_registered) { \ + memset (counts, 0, sizeof(counts)); \ + memset (ranges, 0, sizeof(ranges)); \ + pgm_histogram_init (&counter); \ + } \ + pgm_histogram_add (&counter, (sample)); \ + } while (0) + +#else /* !CONFIG_HISTOGRAMS */ + +# define PGM_HISTOGRAM_TIMES(name, sample) +# define PGM_HISTOGRAM_COUNTS(name, sample) + +#endif /* !CONFIG_HISTOGRAMS */ + + +extern pgm_slist_t* pgm_histograms; + +void pgm_histogram_init (pgm_histogram_t*); +void pgm_histogram_add (pgm_histogram_t*, int); +void pgm_histogram_write_html_graph_all (pgm_string_t*); + +static inline +void +pgm_histogram_add_time ( + pgm_histogram_t*const histogram, + pgm_time_t sample_time + ) +{ + pgm_histogram_add (histogram, (int)pgm_to_msecs (sample_time)); +} + +PGM_END_DECLS + +#endif /* __PGM_IMPL_HISTOGRAM_H__ */ + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/i18n.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/i18n.h new file mode 100644 index 0000000..c97f3de --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/i18n.h @@ -0,0 +1,32 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * i18n & l10n support + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_I18N_H__ +#define __PGM_IMPL_I18N_H__ + +#ifdef CONFIG_HAVE_GETTEXT +# include +# define _(String) dgettext (GETTEXT_PACKAGE, String) +#else +# define _(String) (String) +#endif + +#endif /* __PGM_IMPL_I18N_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/indextoaddr.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/indextoaddr.h new file mode 100644 index 0000000..df2b2ef --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/indextoaddr.h @@ -0,0 +1,41 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable interface index to socket address function. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_INDEXTOADDR_H__ +#define __PGM_IMPL_INDEXTOADDR_H__ + +#ifndef _WIN32 +# include +#endif +#include +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL bool pgm_if_indextoaddr (const unsigned, const sa_family_t, const uint32_t, struct sockaddr*restrict, pgm_error_t**restrict); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_INDEXTOADDR_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/indextoname.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/indextoname.h new file mode 100644 index 0000000..d5d7964 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/indextoname.h @@ -0,0 +1,37 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Windows interface index to interface name function. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_INDEXTONAME_H__ +#define __PGM_IMPL_INDEXTONAME_H__ + +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL char* pgm_if_indextoname (unsigned, char*); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_INDEXTONAME_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/inet_network.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/inet_network.h new file mode 100644 index 0000000..64c43fb --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/inet_network.h @@ -0,0 +1,41 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable implementations of inet_network and inet_network6. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_INET_NETWORK_H__ +#define __PGM_IMPL_INET_NETWORK_H__ + +#ifndef _WIN32 +# include +#endif +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL int pgm_inet_network (const char*restrict, struct in_addr*restrict); +PGM_GNUC_INTERNAL int pgm_inet6_network (const char*restrict, struct in6_addr*restrict); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_INET_NETWORK_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/ip.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/ip.h new file mode 100644 index 0000000..2db402e --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/ip.h @@ -0,0 +1,150 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Internet header for protocol version 4, RFC 791. + * + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_IP_H__ +#define __PGM_IMPL_IP_H__ + +#ifndef _WIN32 +# include +# include +#endif +#include + +PGM_BEGIN_DECLS + +/* Byte alignment for packet memory maps. + * NB: Solaris and OpenSolaris don't support #pragma pack(push) even on x86. + */ +#if defined( __GNUC__ ) && !defined( sun ) +# pragma pack(push) +#endif +#pragma pack(1) + +/* RFC 791 */ + +/* nb: first four bytes are forced bitfields for win32 "feature" */ +struct pgm_ip +{ +#if (defined( sun ) && defined( _BIT_FIELDS_LTOH )) || (!defined( sun ) && __BYTE_ORDER == __LITTLE_ENDIAN) + unsigned ip_hl:4; /* header length */ + unsigned ip_v:4; /* version */ +#else + unsigned ip_v:4; /* version */ + unsigned ip_hl:4; /* header length */ +#endif + unsigned ip_tos:8; /* type of service */ + unsigned ip_len:16; /* total length */ + uint16_t ip_id; /* identification */ + uint16_t ip_off; /* fragment offset field */ + uint8_t ip_ttl; /* time to live */ + uint8_t ip_p; /* protocol */ + uint16_t ip_sum; /* checksum */ + struct in_addr ip_src, ip_dst; /* source and dest address */ +}; + +PGM_STATIC_ASSERT(sizeof(struct pgm_ip) == 20); + +/* RFC 2460 */ +#ifdef ip6_vfc +# undef ip6_vfc +#endif +#ifdef ip6_plen +# undef ip6_plen +#endif +#ifdef ip6_nxt +# undef ip6_nxt +#endif +#ifdef ip6_hops +# undef ip6_hops +#endif +struct pgm_ip6_hdr +{ + uint32_t ip6_vfc; /* version:4, traffic class:8, flow label:20 */ + uint16_t ip6_plen; /* payload length: packet length - 40 */ + uint8_t ip6_nxt; /* next header type */ + uint8_t ip6_hops; /* hop limit */ + struct in6_addr ip6_src, ip6_dst; /* source and dest address */ +}; + +PGM_STATIC_ASSERT(sizeof(struct pgm_ip6_hdr) == 40); + +#define PGM_IPOPT_EOL 0 /* end of option list */ +#define PGM_IPOPT_NOP 1 /* no operation */ +#define PGM_IPOPT_RR 7 /* record packet route */ +#define PGM_IPOPT_TS 68 /* timestamp */ +#define PGM_IPOPT_SECURITY 130 /* provide s, c, h, tcc */ +#define PGM_IPOPT_LSRR 131 /* loose source route */ +#define PGM_IPOPT_ESO 133 +#define PGM_IPOPT_CIPSO 134 +#define PGM_IPOPT_SATID 136 /* satnet id */ +#define PGM_IPOPT_SSRR 137 /* strict source route */ +#define PGM_IPOPT_RA 148 /* router alert */ + +/* RFC 768 */ +struct pgm_udphdr +{ + uint16_t uh_sport; /* source port */ + uint16_t uh_dport; /* destination port */ + uint16_t uh_ulen; /* udp length */ + uint16_t uh_sum; /* udp checksum */ +}; + +PGM_STATIC_ASSERT(sizeof(struct pgm_udphdr) == 8); + +#if defined( __GNUC__ ) && !defined( sun ) +# pragma pack(pop) +#else +# pragma pack() +#endif + +PGM_END_DECLS + +#endif /* __PGM_IMPL_IP_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/list.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/list.h new file mode 100644 index 0000000..91a3ed3 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/list.h @@ -0,0 +1,43 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable doubly-linked list. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_LIST_H__ +#define __PGM_IMPL_LIST_H__ + +#include +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL pgm_list_t* pgm_list_append (pgm_list_t*restrict, void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL pgm_list_t* pgm_list_prepend_link (pgm_list_t*restrict, pgm_list_t*restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL pgm_list_t* pgm_list_remove_link (pgm_list_t*, pgm_list_t*) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL pgm_list_t* pgm_list_delete_link (pgm_list_t*, pgm_list_t*) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL pgm_list_t* pgm_list_last (pgm_list_t*) PGM_GNUC_PURE PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL unsigned pgm_list_length (pgm_list_t*) PGM_GNUC_PURE PGM_GNUC_WARN_UNUSED_RESULT; + +PGM_END_DECLS + +#endif /* __PGM_IMPL_LIST_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/math.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/math.h new file mode 100644 index 0000000..00eb808 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/math.h @@ -0,0 +1,86 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Shared math routines. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_MATH_H__ +#define __PGM_IMPL_MATH_H__ + +#include + +PGM_BEGIN_DECLS + +/* fast log base 2 of power of 2 + */ + +static inline unsigned pgm_power2_log2 (unsigned) PGM_GNUC_CONST; + +static inline +unsigned +pgm_power2_log2 ( + unsigned v + ) +{ + static const unsigned int b[] = { 0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0, 0xFF00FF00, 0xFFFF0000 }; + unsigned int r = (v & b[0]) != 0; +#if defined(__STDC_VERSION__) && (__STDC_VERSION >= 199901L) +/* C99 version */ + for (unsigned i = 4; i > 0; i--) { + r |= ((v & b[i]) != 0) << i; + } +#else +/* C89 version */ + { + unsigned i; + for (i = 4; i > 0; i--) { + r |= ((v & b[i]) != 0) << i; + } + } +#endif + return r; +} + +/* nearest power of 2 + */ + +static inline size_t pgm_nearest_power (size_t, size_t) PGM_GNUC_CONST; + +static inline +size_t +pgm_nearest_power ( + size_t b, + size_t v + ) +{ + if (v > (SIZE_MAX/2)) + return SIZE_MAX; + while (b < v) + b <<= 1; + return b; +} + +unsigned pgm_spaced_primes_closest (unsigned) PGM_GNUC_PURE; + +PGM_END_DECLS + +#endif /* __PGM_IMPL_MATH_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/md5.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/md5.h new file mode 100644 index 0000000..b28ab7d --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/md5.h @@ -0,0 +1,61 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * MD5 hashing algorithm. + * + * MD5 original source GNU C Library: + * Includes functions to compute MD5 message digest of files or memory blocks + * according to the definition of MD5 in RFC 1321 from April 1992. + * + * Copyright (C) 1995, 1996, 2001, 2003 Free Software Foundation, Inc. + * + * This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_MD5_H__ +#define __PGM_IMPL_MD5_H__ + +struct pgm_md5_t; + +#include + +PGM_BEGIN_DECLS + +struct pgm_md5_t +{ + uint32_t A; + uint32_t B; + uint32_t C; + uint32_t D; + + uint32_t total[2]; + uint32_t buflen; + char buffer[128] +#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) + __attribute__ ((__aligned__ (__alignof__ (uint32_t)))) +#endif + ; +}; + +PGM_GNUC_INTERNAL void pgm_md5_init_ctx (struct pgm_md5_t*); +PGM_GNUC_INTERNAL void pgm_md5_process_bytes (struct pgm_md5_t*restrict, const void*restrict, size_t); +PGM_GNUC_INTERNAL void* pgm_md5_finish_ctx (struct pgm_md5_t*, void*); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_MD5_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/mem.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/mem.h new file mode 100644 index 0000000..9377ced --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/mem.h @@ -0,0 +1,34 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable fail fast memory allocation. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_MEM_H__ +#define __PGM_IMPL_MEM_H__ + +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL void pgm_mem_init (void); +PGM_GNUC_INTERNAL void pgm_mem_shutdown (void); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_MEM_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/messages.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/messages.h new file mode 100644 index 0000000..e7065b0 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/messages.h @@ -0,0 +1,352 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * basic message reporting. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_MESSAGES_H__ +#define __PGM_IMPL_MESSAGES_H__ + +#include +#include +#include +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL void pgm__log (const int, const char*, ...) PGM_GNUC_PRINTF (2, 3); +PGM_GNUC_INTERNAL void pgm__logv (const int, const char*, va_list) PGM_GNUC_PRINTF (2, 0); + +#ifdef CONFIG_HAVE_ISO_VARARGS + +/* debug trace level only valid in debug mode */ +# ifdef PGM_DEBUG +# define pgm_debug(...) \ + do { \ + if (pgm_min_log_level == PGM_LOG_LEVEL_DEBUG) \ + pgm__log (PGM_LOG_LEVEL_DEBUG, __VA_ARGS__); \ + } while (0) +# else +# define pgm_debug(...) while (0) +# endif /* !PGM_DEBUG */ + +# define pgm_trace(r,...) \ + do { \ + if (pgm_min_log_level <= PGM_LOG_LEVEL_TRACE && pgm_log_mask & (r)) \ + pgm__log (PGM_LOG_LEVEL_TRACE, __VA_ARGS__); \ + } while (0) +# define pgm_minor(...) \ + do { \ + if (pgm_min_log_level <= PGM_LOG_LEVEL_MINOR) \ + pgm__log (PGM_LOG_LEVEL_MINOR, __VA_ARGS__); \ + } while (0) +# define pgm_info(...) \ + do { \ + if (pgm_min_log_level <= PGM_LOG_LEVEL_NORMAL) \ + pgm__log (PGM_LOG_LEVEL_NORMAL, __VA_ARGS__); \ + } while (0) +# define pgm_warn(...) \ + do { \ + if (pgm_min_log_level <= PGM_LOG_LEVEL_WARNING) \ + pgm__log (PGM_LOG_LEVEL_WARNING, __VA_ARGS__); \ + } while (0) +# define pgm_error(...) \ + do { \ + if (pgm_min_log_level <= PGM_LOG_LEVEL_ERROR) \ + pgm__log (PGM_LOG_LEVEL_ERROR, __VA_ARGS__); \ + } while (0) +# define pgm_fatal(...) \ + do { \ + pgm__log (PGM_LOG_LEVEL_FATAL, __VA_ARGS__); \ + } while (0) + +#elif defined(CONFIG_HAVE_GNUC_VARARGS) + +# ifdef PGM_DEBUG +# define pgm_debug(f...) \ + do { \ + if (pgm_min_log_level == PGM_LOG_LEVEL_DEBUG) \ + pgm__log (PGM_LOG_LEVEL_DEBUG, f); \ + } while (0) +# else +# define pgm_debug(f...) while (0) +# endif /* !PGM_DEBUG */ + +# define pgm_trace(r,f...) if (pgm_min_log_level <= PGM_LOG_LEVEL_TRACE && pgm_log_mask & (r)) \ + pgm__log (PGM_LOG_LEVEL_TRACE, f) +# define pgm_minor(f...) if (pgm_min_log_level <= PGM_LOG_LEVEL_MINOR) pgm__log (PGM_LOG_LEVEL_MINOR, f) +# define pgm_info(f...) if (pgm_min_log_level <= PGM_LOG_LEVEL_NORMAL) pgm__log (PGM_LOG_LEVEL_NORMAL, f) +# define pgm_warn(f...) if (pgm_min_log_level <= PGM_LOG_LEVEL_WARNING) pgm__log (PGM_LOG_LEVEL_WARNING, f) +# define pgm_error(f...) if (pgm_min_log_level <= PGM_LOG_LEVEL_ERROR) pgm__log (PGM_LOG_LEVEL_ERROR, f) +# define pgm_fatal(f...) pgm__log (PGM_LOG_LEVEL_FATAL, f) + +#else /* no varargs macros */ + +/* declare for GCC attributes */ +static inline void pgm_debug (const char*, ...) PGM_GNUC_PRINTF (1, 2); +static inline void pgm_trace (const int, const char*, ...) PGM_GNUC_PRINTF (2, 3); +static inline void pgm_minor (const char*, ...) PGM_GNUC_PRINTF (1, 2); +static inline void pgm_info (const char*, ...) PGM_GNUC_PRINTF (1, 2); +static inline void pgm_warn (const char*, ...) PGM_GNUC_PRINTF (1, 2); +static inline void pgm_error (const char*, ...) PGM_GNUC_PRINTF (1, 2); +static inline void pgm_fatal (const char*, ...) PGM_GNUC_PRINTF (1, 2); + +static inline void pgm_debug (const char* format, ...) { + if (PGM_LOG_LEVEL_DEBUG == pgm_min_log_level) { + va_list args; + va_start (args, format); + pgm__logv (PGM_LOG_LEVEL_DEBUG, format, args); + va_end (args); + } +} + +static inline void pgm_trace (const int role, const char* format, ...) { + if (PGM_LOG_LEVEL_TRACE >= pgm_min_log_level && pgm_log_mask & role) { + va_list args; + va_start (args, format); + pgm__logv (PGM_LOG_LEVEL_TRACE, format, args); + va_end (args); + } +} + +static inline void pgm_minor (const char* format, ...) { + if (PGM_LOG_LEVEL_MINOR >= pgm_min_log_level) { + va_list args; + va_start (args, format); + pgm__logv (PGM_LOG_LEVEL_MINOR, format, args); + va_end (args); + } +} + +static inline void pgm_info (const char* format, ...) { + if (PGM_LOG_LEVEL_NORMAL >= pgm_min_log_level) { + va_list args; + va_start (args, format); + pgm__logv (PGM_LOG_LEVEL_NORMAL, format, args); + va_end (args); + } +} + +static inline void pgm_warn (const char* format, ...) { + if (PGM_LOG_LEVEL_WARNING >= pgm_min_log_level) { + va_list args; + va_start (args, format); + pgm__logv (PGM_LOG_LEVEL_WARNING, format, args); + va_end (args); + } +} + +static inline void pgm_error (const char* format, ...) { + if (PGM_LOG_LEVEL_ERROR >= pgm_min_log_level) { + va_list args; + va_start (args, format); + pgm__logv (PGM_LOG_LEVEL_WARNING, format, args); + va_end (args); + } +} + +static inline void pgm_fatal (const char* format, ...) { + va_list args; + va_start (args, format); + pgm__logv (PGM_LOG_LEVEL_FATAL, format, args); + va_end (args); +} + +#endif /* varargs */ + +#define pgm_warn_if_reached() \ + do { \ + pgm_warn ("file %s: line %d (%s): code should not be reached", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__); \ + } while (0) +#define pgm_warn_if_fail(expr) \ + do { \ + if (PGM_LIKELY (expr)); \ + else \ + pgm_warn ("file %s: line %d (%s): runtime check failed: (%s)", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, #expr); \ + } while (0) + + +#ifdef PGM_DISABLE_ASSERT + +# define pgm_assert(expr) while (0) +# define pgm_assert_not_reached() while (0) +# define pgm_assert_cmpint(n1, cmp, n2) while (0) +# define pgm_assert_cmpuint(n1, cmp, n2) while (0) + +#elif defined(__GNUC__) + +# define pgm_assert(expr) \ + do { \ + if (PGM_LIKELY(expr)); \ + else { \ + pgm_fatal ("file %s: line %d (%s): assertion failed: (%s)", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, #expr); \ + abort (); \ + } \ + } while (0) +# define pgm_assert_not_reached() \ + do { \ + pgm_fatal ("file %s: line %d (%s): should not be reached", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__); \ + abort (); \ + } while (0) +# define pgm_assert_cmpint(n1, cmp, n2) \ + do { \ + const int _n1 = (n1), _n2 = (n2); \ + if (PGM_LIKELY(_n1 cmp _n2)); \ + else { \ + pgm_fatal ("file %s: line %d (%s): assertion failed (%s): (%u %s %u)", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, #n1 " " #cmp " " #n2, _n1, #cmp, _n2); \ + abort (); \ + } \ + } while (0) +# define pgm_assert_cmpuint(n1, cmp, n2) \ + do { \ + const unsigned _n1 = (n1), _n2 = (n2); \ + if (PGM_LIKELY(_n1 cmp _n2)); \ + else { \ + pgm_fatal ("file %s: line %d (%s): assertion failed (%s): (%u %s %u)", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, #n1 " " #cmp " " #n2, _n1, #cmp, _n2); \ + abort (); \ + } \ + } while (0) + +#else + +# define pgm_assert(expr) \ + do { \ + if (PGM_LIKELY(expr)); \ + else { \ + pgm_fatal ("file %s: line %d: assertion failed: (%s)", \ + __FILE__, __LINE__, #expr); \ + abort (); \ + } \ + } while (0) +# define pgm_assert_not_reached() \ + do { \ + pgm_fatal ("file %s: line %d: assertion failed: (%s)", \ + __FILE__, __LINE__); \ + abort (); \ + } while (0) +# define pgm_assert_cmpint(n1, cmp, n2) \ + do { \ + const int _n1 = (n1), _n2 = (n2); \ + if (PGM_LIKELY(_n1 cmp _n2)); \ + else { \ + pgm_fatal ("file %s: line %d: assertion failed (%s): (%u %s %u)", \ + __FILE__, __LINE__, #n1 " " #cmp " " #n2, _n1, #cmp, _n2); \ + abort (); \ + } \ + } while (0) +# define pgm_assert_cmpuint(n1, cmp, n2) \ + do { \ + const unsigned _n1 = (n1), _n2 = (n2); \ + if (PGM_LIKELY(_n1 cmp _n2)); \ + else { \ + pgm_fatal ("file %s: line %d: assertion failed (%s): (%u %s %u)", \ + __FILE__, __LINE__, #n1 " " #cmp " " #n2, _n1, #cmp, _n2); \ + abort (); \ + } \ + } while (0) + +#endif /* !PGM_DISABLE_ASSERT */ + +#ifdef PGM_DISABLE_CHECKS + +# define pgm_return_if_fail(expr) while (0) +# define pgm_return_val_if_fail(expr, val) while (0) +# define pgm_return_if_reached() return +# define pgm_return_val_if_reached(val) return (val) + +#elif defined(__GNUC__) + +# define pgm_return_if_fail(expr) \ + do { \ + if (PGM_LIKELY(expr)); \ + else { \ + pgm_warn ("file %s: line %d (%s): assertion `%s' failed", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, #expr); \ + return; \ + } \ + } while (0) +# define pgm_return_val_if_fail(expr, val) \ + do { \ + if (PGM_LIKELY(expr)); \ + else { \ + pgm_warn ("file %s: line %d (%s): assertion `%s' failed", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, #expr); \ + return (val); \ + } \ + } while (0) +# define pgm_return_if_reached() \ + do { \ + pgm_warn ("file %s: line %d (%s): should not be reached", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__); \ + return; \ + } while (0) +# define pgm_return_val_if_reached(val) \ + do { \ + pgm_warn ("file %s: line %d (%s): should not be reached", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__); \ + return (val); \ + } while (0) + +#else + +# define pgm_return_if_fail(expr) \ + do { \ + if (PGM_LIKELY(expr)); \ + else { \ + pgm_warn ("file %s: line %d: assertion `%s' failed", \ + __FILE__, __LINE__, #expr); \ + return; \ + } \ + } while (0) +# define pgm_return_val_if_fail(expr, val) \ + do { \ + if (PGM_LIKELY(expr)); \ + else { \ + pgm_warn ("file %s: line %d: assertion `%s' failed", \ + __FILE__, __LINE__, #expr); \ + return (val); \ + } \ + } while (0) +# define pgm_return_if_reached() \ + do { \ + pgm_warn ("file %s: line %d): should not be reached", \ + __FILE__, __LINE__); \ + return; \ + } while (0) +# define pgm_return_val_if_reached(val) \ + do { \ + pgm_warn ("file %s: line %d: should not be reached", \ + __FILE__, __LINE__); \ + return (val); \ + } while (0) + +#endif /* !PGM_DISABLE_CHECKS */ + +PGM_END_DECLS + +#endif /* __PGM_IMPL_MESSAGES_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/nametoindex.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/nametoindex.h new file mode 100644 index 0000000..a25bd89 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/nametoindex.h @@ -0,0 +1,40 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Windows interface name to interface index function. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_NAMETOINDEX_H__ +#define __PGM_IMPL_NAMETOINDEX_H__ + +#ifndef _WIN32 +# include +#endif +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL unsigned pgm_if_nametoindex (const sa_family_t, const char*); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_NAMETOINDEX_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/net.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/net.h new file mode 100644 index 0000000..fd30c1c --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/net.h @@ -0,0 +1,53 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * network send wrapper. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_NET_H__ +#define __PGM_IMPL_NET_H__ + +#ifndef _WIN32 +# include +#endif +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL ssize_t pgm_sendto_hops (pgm_sock_t*restrict, bool, bool, int, const void*restrict, size_t, const struct sockaddr*restrict, socklen_t); +PGM_GNUC_INTERNAL int pgm_set_nonblocking (int fd[2]); + +static inline +ssize_t +pgm_sendto ( + pgm_sock_t*restrict sock, + bool use_rate_limit, + bool use_router_alert, + const void*restrict buf, + size_t len, + const struct sockaddr*restrict to, + socklen_t tolen + ) +{ + return pgm_sendto_hops (sock, use_rate_limit, use_router_alert, -1, buf, len, to, tolen); +} + +PGM_END_DECLS + +#endif /* __PGM_IMPL_NET_H__ */ + diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/notify.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/notify.h new file mode 100644 index 0000000..47f9e1c --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/notify.h @@ -0,0 +1,305 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Low kernel overhead event notify mechanism, or standard pipes. + * + * Copyright (c) 2008 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_NOTIFY_H__ +#define __PGM_IMPL_NOTIFY_H__ + +typedef struct pgm_notify_t pgm_notify_t; + +#ifndef _WIN32 +# include +# include +# ifdef CONFIG_HAVE_EVENTFD +# include +# endif +#else /* _WIN32 */ +# include +# include +#endif +#include +#include + +PGM_BEGIN_DECLS + +struct pgm_notify_t { +#if defined(CONFIG_HAVE_EVENTFD) + int eventfd; +#elif !defined(_WIN32) + int pipefd[2]; +#else + SOCKET s[2]; +#endif /* _WIN32 */ +}; + +#if defined(CONFIG_HAVE_EVENTFD) +# define PGM_NOTIFY_INIT { -1 } +#elif !defined(_WIN32) +# define PGM_NOTIFY_INIT { { -1, -1 } } +#else +# define PGM_NOTIFY_INIT { { INVALID_SOCKET, INVALID_SOCKET } } +#endif + + +static inline +bool +pgm_notify_is_valid ( + pgm_notify_t* notify + ) +{ + if (PGM_UNLIKELY(NULL == notify)) + return FALSE; +#if defined(CONFIG_HAVE_EVENTFD) + if (PGM_UNLIKELY(-1 == notify->eventfd)) + return FALSE; +#elif !defined(_WIN32) + if (PGM_UNLIKELY(-1 == notify->pipefd[0] || -1 == notify->pipefd[1])) + return FALSE; +#else + if (PGM_UNLIKELY(INVALID_SOCKET == notify->s[0] || INVALID_SOCKET == notify->s[1])) + return FALSE; +#endif /* _WIN32 */ + return TRUE; +} + +static inline +int +pgm_notify_init ( + pgm_notify_t* notify + ) +{ +#if defined(CONFIG_HAVE_EVENTFD) + pgm_assert (NULL != notify); + notify->eventfd = -1; + int retval = eventfd (0, 0); + if (-1 == retval) + return retval; + notify->eventfd = retval; + const int fd_flags = fcntl (notify->eventfd, F_GETFL); + if (-1 != fd_flags) + retval = fcntl (notify->eventfd, F_SETFL, fd_flags | O_NONBLOCK); + return 0; +#elif !defined(_WIN32) + pgm_assert (NULL != notify); + notify->pipefd[0] = notify->pipefd[1] = -1; + int retval = pipe (notify->pipefd); + pgm_assert (0 == retval); +/* set non-blocking */ +/* write-end */ + int fd_flags = fcntl (notify->pipefd[1], F_GETFL); + if (fd_flags != -1) + retval = fcntl (notify->pipefd[1], F_SETFL, fd_flags | O_NONBLOCK); + pgm_assert (notify->pipefd[1]); +/* read-end */ + fd_flags = fcntl (notify->pipefd[0], F_GETFL); + if (fd_flags != -1) + retval = fcntl (notify->pipefd[0], F_SETFL, fd_flags | O_NONBLOCK); + pgm_assert (notify->pipefd[0]); + return retval; +#else +/* use loopback sockets to simulate a pipe suitable for win32/select() */ + struct sockaddr_in addr; + SOCKET listener; + int sockerr; + int addrlen = sizeof (addr); + const unsigned long one = 1; + + pgm_assert (NULL != notify); + notify->s[0] = notify->s[1] = INVALID_SOCKET; + + listener = socket (AF_INET, SOCK_STREAM, 0); + pgm_assert (listener != INVALID_SOCKET); + + memset (&addr, 0, sizeof (addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr ("127.0.0.1"); + pgm_assert (addr.sin_addr.s_addr != INADDR_NONE); + + sockerr = bind (listener, (const struct sockaddr*)&addr, sizeof (addr)); + pgm_assert (sockerr != SOCKET_ERROR); + + sockerr = getsockname (listener, (struct sockaddr*)&addr, &addrlen); + pgm_assert (sockerr != SOCKET_ERROR); + +// Listen for incoming connections. + sockerr = listen (listener, 1); + pgm_assert (sockerr != SOCKET_ERROR); + +// Create the socket. + notify->s[1] = WSASocket (AF_INET, SOCK_STREAM, 0, NULL, 0, 0); + pgm_assert (notify->s[1] != INVALID_SOCKET); + +// Connect to the remote peer. + sockerr = connect (notify->s[1], (struct sockaddr*)&addr, addrlen); + pgm_assert (sockerr != SOCKET_ERROR); + +// Accept connection. + notify->s[0] = accept (listener, NULL, NULL); + pgm_assert (notify->s[0] != INVALID_SOCKET); + +// Set read-end to non-blocking mode +#pragma warning( disable : 4090 ) + sockerr = ioctlsocket (notify->s[0], FIONBIO, &one); +#pragma warning( default : 4090 ) + pgm_assert (sockerr != SOCKET_ERROR); + +// We don't need the listening socket anymore. Close it. + sockerr = closesocket (listener); + pgm_assert (sockerr != SOCKET_ERROR); + + return 0; +#endif +} + +static inline +int +pgm_notify_destroy ( + pgm_notify_t* notify + ) +{ + pgm_assert (NULL != notify); + +#if defined(CONFIG_HAVE_EVENTFD) + if (-1 != notify->eventfd) { + close (notify->eventfd); + notify->eventfd = -1; + } +#elif !defined(_WIN32) + if (-1 != notify->pipefd[0]) { + close (notify->pipefd[0]); + notify->pipefd[0] = -1; + } + if (-1 != notify->pipefd[1]) { + close (notify->pipefd[1]); + notify->pipefd[1] = -1; + } +#else + if (INVALID_SOCKET != notify->s[0]) { + closesocket (notify->s[0]); + notify->s[0] = INVALID_SOCKET; + } + if (INVALID_SOCKET != notify->s[1]) { + closesocket (notify->s[1]); + notify->s[1] = INVALID_SOCKET; + } +#endif + return 0; +} + +static inline +int +pgm_notify_send ( + pgm_notify_t* notify + ) +{ +#if defined(CONFIG_HAVE_EVENTFD) + uint64_t u = 1; + pgm_assert (NULL != notify); + pgm_assert (-1 != notify->eventfd); + ssize_t s = write (notify->eventfd, &u, sizeof(u)); + return (s == sizeof(u)); +#elif !defined(_WIN32) + const char one = '1'; + pgm_assert (NULL != notify); + pgm_assert (-1 != notify->pipefd[1]); + return (1 == write (notify->pipefd[1], &one, sizeof(one))); +#else + const char one = '1'; + pgm_assert (NULL != notify); + pgm_assert (INVALID_SOCKET != notify->s[1]); + return (1 == send (notify->s[1], &one, sizeof(one), 0)); +#endif +} + +static inline +int +pgm_notify_read ( + pgm_notify_t* notify + ) +{ +#if defined(CONFIG_HAVE_EVENTFD) + uint64_t u; + pgm_assert (NULL != notify); + pgm_assert (-1 != notify->eventfd); + return (sizeof(u) == read (notify->eventfd, &u, sizeof(u))); +#elif !defined(_WIN32) + char buf; + pgm_assert (NULL != notify); + pgm_assert (-1 != notify->pipefd[0]); + return (sizeof(buf) == read (notify->pipefd[0], &buf, sizeof(buf))); +#else + char buf; + pgm_assert (NULL != notify); + pgm_assert (INVALID_SOCKET != notify->s[0]); + return (sizeof(buf) == recv (notify->s[0], &buf, sizeof(buf), 0)); +#endif +} + +static inline +void +pgm_notify_clear ( + pgm_notify_t* notify + ) +{ +#if defined(CONFIG_HAVE_EVENTFD) + uint64_t u; + pgm_assert (NULL != notify); + pgm_assert (-1 != notify->eventfd); + while (sizeof(u) == read (notify->eventfd, &u, sizeof(u))); +#elif !defined(_WIN32) + char buf; + pgm_assert (NULL != notify); + pgm_assert (-1 != notify->pipefd[0]); + while (sizeof(buf) == read (notify->pipefd[0], &buf, sizeof(buf))); +#else + char buf; + pgm_assert (NULL != notify); + pgm_assert (INVALID_SOCKET != notify->s[0]); + while (sizeof(buf) == recv (notify->s[0], &buf, sizeof(buf), 0)); +#endif +} + +static inline +int +pgm_notify_get_fd ( + pgm_notify_t* notify + ) +{ + pgm_assert (NULL != notify); + +#if defined(CONFIG_HAVE_EVENTFD) + pgm_assert (-1 != notify->eventfd); + return notify->eventfd; +#elif !defined(_WIN32) + pgm_assert (-1 != notify->pipefd[0]); + return notify->pipefd[0]; +#else + pgm_assert (INVALID_SOCKET != notify->s[0]); + return notify->s[0]; +#endif +} + +PGM_END_DECLS + +#endif /* __PGM_IMPL_NOTIFY_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/packet_parse.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/packet_parse.h new file mode 100644 index 0000000..4416e21 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/packet_parse.h @@ -0,0 +1,45 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM packet formats, RFC 3208. + * + * Copyright (c) 2006 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_PACKET_PARSE_H__ +#define __PGM_IMPL_PACKET_PARSE_H__ + +#ifndef _WIN32 +# include +#endif +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL bool pgm_parse_raw (struct pgm_sk_buff_t*const restrict, struct sockaddr*const restrict, pgm_error_t**restrict); +PGM_GNUC_INTERNAL bool pgm_parse_udp_encap (struct pgm_sk_buff_t*const restrict, pgm_error_t**restrict); +PGM_GNUC_INTERNAL bool pgm_verify_spm (const struct pgm_sk_buff_t* const); +PGM_GNUC_INTERNAL bool pgm_verify_spmr (const struct pgm_sk_buff_t* const); +PGM_GNUC_INTERNAL bool pgm_verify_nak (const struct pgm_sk_buff_t* const); +PGM_GNUC_INTERNAL bool pgm_verify_nnak (const struct pgm_sk_buff_t* const); +PGM_GNUC_INTERNAL bool pgm_verify_ncf (const struct pgm_sk_buff_t* const); +PGM_GNUC_INTERNAL bool pgm_verify_poll (const struct pgm_sk_buff_t* const); +PGM_GNUC_INTERNAL bool pgm_verify_polr (const struct pgm_sk_buff_t* const); +PGM_GNUC_INTERNAL bool pgm_verify_ack (const struct pgm_sk_buff_t* const); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_PACKET_PARSE_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/packet_test.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/packet_test.h new file mode 100644 index 0000000..1b39006 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/packet_test.h @@ -0,0 +1,40 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM packet formats, RFC 3208. + * + * Copyright (c) 2006 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_PACKET_TEST_H__ +#define __PGM_IMPL_PACKET_TEST_H__ + +#ifndef _WIN32 +# include +#endif +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL bool pgm_print_packet (const void*, size_t); +PGM_GNUC_INTERNAL const char* pgm_type_string (uint8_t) PGM_GNUC_WARN_UNUSED_RESULT PGM_GNUC_CONST; +PGM_GNUC_INTERNAL const char* pgm_udpport_string (uint16_t) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL const char* pgm_gethostbyaddr (const struct in_addr*) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_ipopt_print (const void*, size_t); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_PACKET_TEST_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/pgmMIB.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/pgmMIB.h new file mode 100644 index 0000000..6f80271 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/pgmMIB.h @@ -0,0 +1,28 @@ +/* + * Note: this file originally auto-generated by mib2c using + * : mib2c.notify.conf,v 5.3 2004/04/15 12:29:19 dts12 Exp $ + */ + +#ifndef __PGM_IMPL_MIB_H__ +#define __PGM_IMPL_MIB_H__ + +#include + +PGM_BEGIN_DECLS + +/* function declarations */ +PGM_GNUC_INTERNAL bool pgm_mib_init (pgm_error_t**); + +PGM_GNUC_INTERNAL int send_pgmStart_trap(void); +PGM_GNUC_INTERNAL int send_pgmStop_trap(void); +PGM_GNUC_INTERNAL int send_pgmNewSourceTrap_trap(void); +PGM_GNUC_INTERNAL int send_pgmClosedSourceTrap_trap(void); +PGM_GNUC_INTERNAL int send_pgmNewReceiverTrap_trap(void); +PGM_GNUC_INTERNAL int send_pgmClosedReceiverTrap_trap(void); +PGM_GNUC_INTERNAL int send_pgmNakFailuresTrap_trap(void); +PGM_GNUC_INTERNAL int send_pgmNewDlrSourceTrap_trap(void); +PGM_GNUC_INTERNAL int send_pgmClosedDlrSourceTrap_trap(void); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_MIB_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/pgmMIB_columns.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/pgmMIB_columns.h new file mode 100644 index 0000000..545e519 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/pgmMIB_columns.h @@ -0,0 +1,372 @@ +/* + * Note: this file originally auto-generated by mib2c using + * : mib2c.column_defines.conf,v 5.1 2002/05/08 05:42:47 hardaker Exp $ + */ +#ifndef PGMMIB_COLUMNS_H +#define PGMMIB_COLUMNS_H + +/* column number definitions for table pgmNeIfConfigTable */ + #define COLUMN_PGMNEIFCONFIGINDEX 1 + #define COLUMN_PGMNEIFPGMENABLE 2 + #define COLUMN_PGMNEIFNAKRPTINTERVAL 3 + #define COLUMN_PGMNEIFNAKRPTRATE 4 + #define COLUMN_PGMNEIFNAKRDATAINTERVAL 5 + #define COLUMN_PGMNEIFNAKELIMINATEINTERVAL 6 + +/* column number definitions for table pgmNeIfPerformanceTable */ + #define COLUMN_PGMNEIFPERFORMANCEINDEX 1 + #define COLUMN_PGMNEIFREXMITSTATES 2 + #define COLUMN_PGMNEIFREXMITTIMEDOUT 3 + #define COLUMN_PGMNEIFINSPMS 4 + #define COLUMN_PGMNEIFOUTSPMS 5 + #define COLUMN_PGMNEIFINPARITYSPMS 6 + #define COLUMN_PGMNEIFOUTPARITYSPMS 7 + #define COLUMN_PGMNEIFINRDATA 8 + #define COLUMN_PGMNEIFOUTRDATA 9 + #define COLUMN_PGMNEIFINPARITYRDATA 10 + #define COLUMN_PGMNEIFOUTPARITYRDATA 11 + #define COLUMN_PGMNEIFINRDATANOSESSIONERRORS 12 + #define COLUMN_PGMNEIFUNIQUENAKS 13 + #define COLUMN_PGMNEIFINNAKS 14 + #define COLUMN_PGMNEIFOUTNAKS 15 + #define COLUMN_PGMNEIFUNIQUEPARITYNAKS 16 + #define COLUMN_PGMNEIFINPARITYNAKS 17 + #define COLUMN_PGMNEIFOUTPARITYNAKS 18 + #define COLUMN_PGMNEIFINNAKNOSESSIONERRORS 19 + #define COLUMN_PGMNEIFINNAKSEQERRORS 20 + #define COLUMN_PGMNEIFINPARITYNAKTGERRORS 21 + #define COLUMN_PGMNEIFINNNAKS 22 + #define COLUMN_PGMNEIFOUTNNAKS 23 + #define COLUMN_PGMNEIFINPARITYNNAKS 24 + #define COLUMN_PGMNEIFOUTPARITYNNAKS 25 + #define COLUMN_PGMNEIFINNNAKNOSESSIONERRORS 26 + #define COLUMN_PGMNEIFINNCFS 27 + #define COLUMN_PGMNEIFOUTNCFS 28 + #define COLUMN_PGMNEIFINPARITYNCFS 29 + #define COLUMN_PGMNEIFOUTPARITYNCFS 30 + #define COLUMN_PGMNEIFINNCFNOSESSIONERRORS 31 + #define COLUMN_PGMNEIFINREDIRECTNCFS 32 + #define COLUMN_PGMNEIFMALFORMED 33 + #define COLUMN_PGMNEIFSPMFROMSOURCE 34 + #define COLUMN_PGMNEIFSPMBADSQN 35 + #define COLUMN_PGMNEIFSPMERROR 36 + #define COLUMN_PGMNEIFPOLLRANDOMIGNORE 37 + #define COLUMN_PGMNEIFPOLLTSISTATEERROR 38 + #define COLUMN_PGMNEIFPOLLPARENTERROR 39 + #define COLUMN_PGMNEIFPOLLTYPEERROR 40 + #define COLUMN_PGMNEIFPOLLERROR 41 + #define COLUMN_PGMNEIFPOLLSUCCESS 42 + #define COLUMN_PGMNEIFPOLLORIGINATED 43 + #define COLUMN_PGMNEIFPOLRNOSTATE 44 + #define COLUMN_PGMNEIFPOLRERROR 45 + #define COLUMN_PGMNEIFPOLRPARITYERROR 46 + #define COLUMN_PGMNEIFPOLRSUCCESS 47 + #define COLUMN_PGMNEIFPOLRORIGINATED 48 + #define COLUMN_PGMNEIFNCFERROR 49 + #define COLUMN_PGMNEIFNCFPARITYERROR 50 + #define COLUMN_PGMNEIFNCFPARTIALPARITY 51 + #define COLUMN_PGMNEIFNCFRECEIVED 52 + #define COLUMN_PGMNEIFNCFANTICIPATED 53 + #define COLUMN_PGMNEIFNCFREDIRECTING 54 + #define COLUMN_PGMNEIFNAKELIMINATED 55 + #define COLUMN_PGMNEIFNAKERROR 56 + #define COLUMN_PGMNEIFNAKPARITYERROR 57 + #define COLUMN_PGMNEIFNNAKELIMINATED 58 + #define COLUMN_PGMNEIFNNAKERROR 59 + #define COLUMN_PGMNEIFNNAKPARITYERROR 60 + #define COLUMN_PGMNEIFNNAKCONGESTIONREPORTS 61 + #define COLUMN_PGMNEIFNAKRETRYEXPIRED 62 + #define COLUMN_PGMNEIFNAKRETRYEXPIREDDLR 63 + #define COLUMN_PGMNEIFNAKFORWARDEDDLR 64 + #define COLUMN_PGMNEIFNAKRETRANSMITTED 65 + #define COLUMN_PGMNEIFRDATAELIMINATEDOIF 66 + #define COLUMN_PGMNEIFRDATAELIMINATEDSQN 67 + #define COLUMN_PGMNEIFINRDATAFRAGMENTS 68 + #define COLUMN_PGMNEIFRDATAFRAGMENTSNOSESSIONERRORS 69 + #define COLUMN_PGMNEIFRDATAFRAGMENTSELIMINATEDOIF 70 + #define COLUMN_PGMNEIFRDATAFRAGMENTSELIMINATEDSQN 71 + #define COLUMN_PGMNEIFOUTRDATAFRAGMENTS 72 + +/* column number definitions for table pgmNeTsiTable */ + #define COLUMN_PGMNETSIGLOBALID 1 + #define COLUMN_PGMNETSIDATASOURCEPORT 2 + #define COLUMN_PGMNETSISTATEBITS 3 + #define COLUMN_PGMNETSIDATADESTINATIONPORT 4 + #define COLUMN_PGMNETSISOURCEADDRESS 5 + #define COLUMN_PGMNETSIGROUPADDRESS 6 + #define COLUMN_PGMNETSIUPSTREAMADDRESS 7 + #define COLUMN_PGMNETSIUPSTREAMIFINDEX 8 + #define COLUMN_PGMNETSIDLRADDRESS 9 + +/* column number definitions for table pgmNeTsiPerformanceTable */ + #define COLUMN_PGMNETSIPERFORMANCEGLOBALID 1 + #define COLUMN_PGMNETSIPERFORMANCEDATASOURCEPORT 2 + #define COLUMN_PGMNETSISESSIONTRAILEDGESEQ 3 + #define COLUMN_PGMNETSISESSIONINCRSEQ 4 + #define COLUMN_PGMNETSILEADEDGESEQ 5 + #define COLUMN_PGMNETSIINSPMS 6 + #define COLUMN_PGMNETSIOUTSPMS 7 + #define COLUMN_PGMNETSIINPARITYSPMS 8 + #define COLUMN_PGMNETSIOUTPARITYSPMS 9 + #define COLUMN_PGMNETSITOTALREXMITSTATES 10 + #define COLUMN_PGMNETSITOTALREXMITTIMEDOUT 11 + #define COLUMN_PGMNETSIINRDATA 12 + #define COLUMN_PGMNETSIOUTRDATA 13 + #define COLUMN_PGMNETSIINPARITYRDATA 14 + #define COLUMN_PGMNETSIOUTPARITYRDATA 15 + #define COLUMN_PGMNETSIINRDATANOSTATEERRORS 16 + #define COLUMN_PGMNETSIUNIQUENAKS 17 + #define COLUMN_PGMNETSIINNAKS 18 + #define COLUMN_PGMNETSIOUTNAKS 19 + #define COLUMN_PGMNETSIUNIQUEPARITYNAKS 20 + #define COLUMN_PGMNETSIINPARITYNAKS 21 + #define COLUMN_PGMNETSIOUTPARITYNAKS 22 + #define COLUMN_PGMNETSIINNAKSEQERRORS 23 + #define COLUMN_PGMNETSIINNNAKS 24 + #define COLUMN_PGMNETSIOUTNNAKS 25 + #define COLUMN_PGMNETSIINPARITYNNAKS 26 + #define COLUMN_PGMNETSIOUTPARITYNNAKS 27 + #define COLUMN_PGMNETSIINNCFS 28 + #define COLUMN_PGMNETSIOUTNCFS 29 + #define COLUMN_PGMNETSIINPARITYNCFS 30 + #define COLUMN_PGMNETSIOUTPARITYNCFS 31 + #define COLUMN_PGMNETSISPMSEQUENCENUMBER 32 + #define COLUMN_PGMNETSITRANSMISSIONGROUPSIZE 33 + #define COLUMN_PGMNETSITIMEOUT 34 + #define COLUMN_PGMNETSILASTTTL 35 + #define COLUMN_PGMNETSILINKLOSSRATE 36 + #define COLUMN_PGMNETSIPATHLOSSRATE 37 + #define COLUMN_PGMNETSIRECEIVERLOSSRATE 38 + #define COLUMN_PGMNETSICONGESTIONREPORTLEAD 39 + #define COLUMN_PGMNETSICONGESTIONREPORTWORSTRECEIVER 40 + +/* column number definitions for table pgmNeTsiRtxTable */ + #define COLUMN_PGMNETSIRTXSEQUENCENUMBER 1 + #define COLUMN_PGMNETSIRTXSEQUENCENUMBERTYPE 2 + #define COLUMN_PGMNETSIRTXREQPARITYTGCOUNT 4 + #define COLUMN_PGMNETSIRTXTIMEOUT 5 + #define COLUMN_PGMNETSIRTXSTATEBITS 6 + +/* column number definitions for table pgmNeTsiRtxIfTable */ + #define COLUMN_PGMNETSIRTXIFINDEX 1 + #define COLUMN_PGMNETSIRTXIFPACKETCOUNT 2 + +/* column number definitions for table pgmNeTsiPolrTable */ + #define COLUMN_PGMNETSIPOLRSOURCE 1 + #define COLUMN_PGMNETSIPOLRSEQUENCENUMBER 2 + +/* column number definitions for table pgmNeTsiPollTable */ + #define COLUMN_PGMNETSIPOLLTYPE 1 + #define COLUMN_PGMNETSIPOLLSEQUENCE 2 + #define COLUMN_PGMNETSIPOLLCHILDBACKOFF 3 + #define COLUMN_PGMNETSIPOLLMASK 4 + #define COLUMN_PGMNETSIPOLLPERIOD 5 + #define COLUMN_PGMNETSIPOLLCOUNT 6 + #define COLUMN_PGMNETSIPOLLTIMEOUT 7 + +/* column number definitions for table pgmSourceTable */ + #define COLUMN_PGMSOURCEGLOBALID 1 + #define COLUMN_PGMSOURCESOURCEPORT 2 + #define COLUMN_PGMSOURCESOURCEADDRESS 3 + #define COLUMN_PGMSOURCEGROUPADDRESS 4 + #define COLUMN_PGMSOURCEDESTPORT 5 + #define COLUMN_PGMSOURCESOURCEGSI 6 + #define COLUMN_PGMSOURCESOURCEPORTNUMBER 7 + +/* column number definitions for table pgmSourceConfigTable */ + #define COLUMN_PGMSOURCECONFIGGLOBALID 1 + #define COLUMN_PGMSOURCECONFIGSOURCEPORT 2 + #define COLUMN_PGMSOURCETTL 3 + #define COLUMN_PGMSOURCEADVMODE 4 + #define COLUMN_PGMSOURCELATEJOIN 5 + #define COLUMN_PGMSOURCETXWMAXRTE 6 + #define COLUMN_PGMSOURCETXWSECS 7 + #define COLUMN_PGMSOURCETXWADVSECS 8 + #define COLUMN_PGMSOURCEADVIVL 9 + #define COLUMN_PGMSOURCESPMIVL 10 + #define COLUMN_PGMSOURCESPMHEARTBEATIVLMIN 11 + #define COLUMN_PGMSOURCESPMHEARTBEATIVLMAX 12 + #define COLUMN_PGMSOURCERDATABACKOFFIVL 13 + #define COLUMN_PGMSOURCEFEC 14 + #define COLUMN_PGMSOURCEFECTRANSMISSIONGRPSIZE 15 + #define COLUMN_PGMSOURCEFECPROACTIVEPARITYSIZE 16 + #define COLUMN_PGMSOURCESPMPATHADDRESS 17 + +/* column number definitions for table pgmSourcePerformanceTable */ + #define COLUMN_PGMSOURCEPERFORMANCEGLOBALID 1 + #define COLUMN_PGMSOURCEPERFORMANCESOURCEPORT 2 + #define COLUMN_PGMSOURCEDATABYTESSENT 3 + #define COLUMN_PGMSOURCEDATAMSGSSENT 4 + #define COLUMN_PGMSOURCEBYTESBUFFERED 5 + #define COLUMN_PGMSOURCEMSGSBUFFERED 6 + #define COLUMN_PGMSOURCEBYTESRETRANSMITTED 7 + #define COLUMN_PGMSOURCEMSGSRETRANSMITTED 8 + #define COLUMN_PGMSOURCEBYTESSENT 9 + #define COLUMN_PGMSOURCERAWNAKSRECEIVED 10 + #define COLUMN_PGMSOURCENAKSIGNORED 11 + #define COLUMN_PGMSOURCECKSUMERRORS 12 + #define COLUMN_PGMSOURCEMALFORMEDNAKS 13 + #define COLUMN_PGMSOURCEPACKETSDISCARDED 14 + #define COLUMN_PGMSOURCENAKSRCVD 15 + #define COLUMN_PGMSOURCEPARITYBYTESRETRANSMITTED 16 + #define COLUMN_PGMSOURCESELECTIVEBYTESRETRANSMITED 17 + #define COLUMN_PGMSOURCEPARITYMSGSRETRANSMITTED 18 + #define COLUMN_PGMSOURCESELECTIVEMSGSRETRANSMITTED 19 + #define COLUMN_PGMSOURCEBYTESADMIT 20 + #define COLUMN_PGMSOURCEMSGSADMIT 21 + #define COLUMN_PGMSOURCEPARITYNAKPACKETSRECEIVED 22 + #define COLUMN_PGMSOURCESELECTIVENAKPACKETSRECEIVED 23 + #define COLUMN_PGMSOURCEPARITYNAKSRECEIVED 24 + #define COLUMN_PGMSOURCESELECTIVENAKSRECEIVED 25 + #define COLUMN_PGMSOURCEPARITYNAKSIGNORED 26 + #define COLUMN_PGMSOURCESELECTIVENAKSIGNORED 27 + #define COLUMN_PGMSOURCEACKERRORS 28 + #define COLUMN_PGMSOURCEPGMCCACKER 29 + #define COLUMN_PGMSOURCETRANSMISSIONCURRENTRATE 30 + #define COLUMN_PGMSOURCEACKPACKETSRECEIVED 31 + #define COLUMN_PGMSOURCENNAKPACKETSRECEIVED 32 + #define COLUMN_PGMSOURCEPARITYNNAKPACKETSRECEIVED 33 + #define COLUMN_PGMSOURCESELECTIVENNAKPACKETSRECEIVED 34 + #define COLUMN_PGMSOURCENNAKSRECEIVED 35 + #define COLUMN_PGMSOURCEPARITYNNAKSRECEIVED 36 + #define COLUMN_PGMSOURCESELECTIVENNAKSRECEIVED 37 + #define COLUMN_PGMSOURCENNAKERRORS 38 + +/* column number definitions for table pgmReceiverTable */ + #define COLUMN_PGMRECEIVERGLOBALID 1 + #define COLUMN_PGMRECEIVERSOURCEPORT 2 + #define COLUMN_PGMRECEIVERINSTANCE 3 + #define COLUMN_PGMRECEIVERGROUPADDRESS 4 + #define COLUMN_PGMRECEIVERDESTPORT 5 + #define COLUMN_PGMRECEIVERSOURCEADDRESS 6 + #define COLUMN_PGMRECEIVERLASTHOP 7 + #define COLUMN_PGMRECEIVERSOURCEGSI 8 + #define COLUMN_PGMRECEIVERSOURCEPORTNUMBER 9 + #define COLUMN_PGMRECEIVERUNIQUEINSTANCE 10 + +/* column number definitions for table pgmReceiverConfigTable */ + #define COLUMN_PGMRECEIVERCONFIGGLOBALID 1 + #define COLUMN_PGMRECEIVERCONFIGSOURCEPORT 2 + #define COLUMN_PGMRECEIVERCONFIGINSTANCE 3 + #define COLUMN_PGMRECEIVERNAKBACKOFFIVL 4 + #define COLUMN_PGMRECEIVERNAKREPEATIVL 5 + #define COLUMN_PGMRECEIVERNAKNCFRETRIES 6 + #define COLUMN_PGMRECEIVERNAKRDATAIVL 7 + #define COLUMN_PGMRECEIVERNAKDATARETRIES 8 + #define COLUMN_PGMRECEIVERSENDNAKS 9 + #define COLUMN_PGMRECEIVERLATEJOIN 10 + #define COLUMN_PGMRECEIVERNAKTTL 11 + #define COLUMN_PGMRECEIVERDELIVERYORDER 12 + #define COLUMN_PGMRECEIVERMCASTNAKS 13 + #define COLUMN_PGMRECEIVERNAKFAILURETHRESHOLDTIMER 14 + #define COLUMN_PGMRECEIVERNAKFAILURETHRESHOLD 15 + +/* column number definitions for table pgmReceiverPerformanceTable */ + #define COLUMN_PGMRECEIVERPERFORMANCEGLOBALID 1 + #define COLUMN_PGMRECEIVERPERFORMANCESOURCEPORT 2 + #define COLUMN_PGMRECEIVERPERFORMANCEINSTANCE 3 + #define COLUMN_PGMRECEIVERDATABYTESRECEIVED 4 + #define COLUMN_PGMRECEIVERDATAMSGSRECEIVED 5 + #define COLUMN_PGMRECEIVERNAKSSENT 6 + #define COLUMN_PGMRECEIVERNAKSRETRANSMITTED 7 + #define COLUMN_PGMRECEIVERNAKFAILURES 8 + #define COLUMN_PGMRECEIVERBYTESRECEIVED 9 + #define COLUMN_PGMRECEIVERNAKSSUPPRESSED 10 + #define COLUMN_PGMRECEIVERCKSUMERRORS 11 + #define COLUMN_PGMRECEIVERMALFORMEDSPMS 12 + #define COLUMN_PGMRECEIVERMALFORMEDODATA 13 + #define COLUMN_PGMRECEIVERMALFORMEDRDATA 14 + #define COLUMN_PGMRECEIVERMALFORMEDNCFS 15 + #define COLUMN_PGMRECEIVERPACKETSDISCARDED 16 + #define COLUMN_PGMRECEIVERLOSSES 17 + #define COLUMN_PGMRECEIVERBYTESDELIVEREDTOAPP 18 + #define COLUMN_PGMRECEIVERMSGSDELIVEREDTOAPP 19 + #define COLUMN_PGMRECEIVERDUPSPMS 20 + #define COLUMN_PGMRECEIVERDUPDATAS 21 + #define COLUMN_PGMRECEIVERDUPPARITIES 22 + #define COLUMN_PGMRECEIVERNAKPACKETSSENT 23 + #define COLUMN_PGMRECEIVERPARITYNAKPACKETSSENT 24 + #define COLUMN_PGMRECEIVERSELECTIVENAKPACKETSSENT 25 + #define COLUMN_PGMRECEIVERPARITYNAKSSENT 26 + #define COLUMN_PGMRECEIVERSELECTIVENAKSSENT 27 + #define COLUMN_PGMRECEIVERPARITYNAKSRETRANSMITTED 28 + #define COLUMN_PGMRECEIVERSELECTIVENAKSRETRANSMITTED 29 + #define COLUMN_PGMRECEIVERNAKSFAILED 30 + #define COLUMN_PGMRECEIVERPARITYNAKSFAILED 31 + #define COLUMN_PGMRECEIVERSELECTIVENAKSFAILED 32 + #define COLUMN_PGMRECEIVERNAKSFAILEDRXWADVANCED 33 + #define COLUMN_PGMRECEIVERNAKSFALEDNCFRETRIESEXCEEDED 34 + #define COLUMN_PGMRECEIVERNAKSFAILEDDATARETRIESEXCEEDED 35 + #define COLUMN_PGMRECEIVERNAKSFAILEDGENEXPIRED 36 + #define COLUMN_PGMRECEIVERNAKFAILURESDELIVERED 37 + #define COLUMN_PGMRECEIVERPARITYNAKSSUPPRESSED 38 + #define COLUMN_PGMRECEIVERSELECTIVENAKSSUPPRESSED 39 + #define COLUMN_PGMRECEIVERNAKERRORS 40 + #define COLUMN_PGMRECEIVEROUTSTANDINGPARITYNAKS 41 + #define COLUMN_PGMRECEIVEROUTSTANDINGSELECTIVENAKS 42 + #define COLUMN_PGMRECEIVERLASTACTIVITY 43 + #define COLUMN_PGMRECEIVERNAKSVCTIMEMIN 44 + #define COLUMN_PGMRECEIVERNAKSVCTIMEMEAN 45 + #define COLUMN_PGMRECEIVERNAKSVCTIMEMAX 46 + #define COLUMN_PGMRECEIVERNAKFAILTIMEMIN 47 + #define COLUMN_PGMRECEIVERNAKFAILTIMEMEAN 48 + #define COLUMN_PGMRECEIVERNAKFAILTIMEMAX 49 + #define COLUMN_PGMRECEIVERNAKTRANSMITMIN 50 + #define COLUMN_PGMRECEIVERNAKTRANSMITMEAN 51 + #define COLUMN_PGMRECEIVERNAKTRANSMITMAX 52 + #define COLUMN_PGMRECEIVERACKSSENT 53 + #define COLUMN_PGMRECEIVERRXWTRAIL 54 + #define COLUMN_PGMRECEIVERRXWLEAD 55 + #define COLUMN_PGMRECEIVERNAKFAILURESLASTINTERVAL 56 + #define COLUMN_PGMRECEIVERLASTINTERVALNAKFAILURES 57 + +/* column number definitions for table pgmDlrSourceTable */ + #define COLUMN_PGMDLRSOURCEGLOBALID 1 + #define COLUMN_PGMDLRSOURCESOURCEPORT 2 + #define COLUMN_PGMDLRSOURCEGROUPADDRESS 3 + #define COLUMN_PGMDLRSOURCESOURCEGSI 4 + #define COLUMN_PGMDLRSOURCESOURCEPORTNUMBER 5 + +/* column number definitions for table pgmDlrSourceConfigTable */ + #define COLUMN_PGMDLRSOURCECONFIGGLOBALID 1 + #define COLUMN_PGMDLRSOURCECONFIGSOURCEPORT 2 + #define COLUMN_PGMDLRSOURCEGROUPTTL 3 + #define COLUMN_PGMDLRSOURCERDATABACKOFFIVL 4 + +/* column number definitions for table pgmDlrSourcePerformanceTable */ + #define COLUMN_PGMDLRSOURCEPERFORMANCEGLOBALID 1 + #define COLUMN_PGMDLRSOURCEPERFORMANCESOURCEPORT 2 + #define COLUMN_PGMDLRSOURCERDATAMSGSSENT 3 + #define COLUMN_PGMDLRSOURCERDATABYTESSENT 4 + #define COLUMN_PGMDLRSOURCEBYTESSENT 5 + #define COLUMN_PGMDLRSOURCENAKSRCVD 6 + #define COLUMN_PGMDLRSOURCENAKSIGNORED 7 + #define COLUMN_PGMDLRSOURCENAKERRORS 8 + #define COLUMN_PGMDLRSOURCEDISCARDS 9 + #define COLUMN_PGMDLRSOURCECKSUMERRORS 10 + #define COLUMN_PGMDLRSOURCENNAKSSENT 11 + #define COLUMN_PGMDLRSOURCEBYTESBUFFERED 12 + #define COLUMN_PGMDLRSOURCEMSGSBUFFERED 13 + #define COLUMN_PGMDLRSOURCEPARITYBYTESRETRANSMITTED 14 + #define COLUMN_PGMDLRSOURCESELECTIVEBYTESRETRANSMITED 15 + #define COLUMN_PGMDLRSOURCEPARITYMSGSRETRANSMITTED 16 + #define COLUMN_PGMDLRSOURCESELECTIVEMSGSRETRANSMITTED 17 + #define COLUMN_PGMDLRSOURCEBYTESADMIT 18 + #define COLUMN_PGMDLRSOURCEMSGSADMIT 19 + #define COLUMN_PGMDLRSOURCENAKPACKETSRECEIVED 20 + #define COLUMN_PGMDLRSOURCEPARITYNAKPACKETSRECEIVED 21 + #define COLUMN_PGMDLRSOURCESELECTIVENAKPACKETSRECEIVED 22 + #define COLUMN_PGMDLRSOURCEPARITYNAKSRECEIVED 23 + #define COLUMN_PGMDLRSOURCESELECTIVENAKSRECEIVED 24 + #define COLUMN_PGMDLRSOURCEPARITYNAKSIGNORED 25 + #define COLUMN_PGMDLRSOURCESELECTIVENAKSIGNORED 26 + #define COLUMN_PGMDLRSOURCEACKERRORS 27 + #define COLUMN_PGMDLRSOURCENNAKERRORS 28 + #define COLUMN_PGMDLRSOURCEACKPACKETSRECEIVED 29 + #define COLUMN_PGMDLRSOURCENNAKPACKETSRECEIVED 30 + #define COLUMN_PGMDLRSOURCEPARITYNNAKPACKETSRECEIVED 31 + #define COLUMN_PGMDLRSOURCESELECTIVENNAKPACKETSRECEIVED 32 + #define COLUMN_PGMDLRSOURCENNAKSRECEIVED 33 + #define COLUMN_PGMDLRSOURCEPARITYNNAKSRECEIVED 34 + #define COLUMN_PGMDLRSOURCESELECTIVENNAKSRECEIVED 35 +#endif /* PGMMIB_COLUMNS_H */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/pgmMIB_enums.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/pgmMIB_enums.h new file mode 100644 index 0000000..9ae5760 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/pgmMIB_enums.h @@ -0,0 +1,64 @@ +/* + * Note: this file originally auto-generated by mib2c using + * : mib2c.column_enums.conf,v 5.2 2003/02/22 04:09:25 hardaker Exp $ + */ +#ifndef PGMMIB_ENUMS_H +#define PGMMIB_ENUMS_H + +/* enums for column pgmNeIfPgmEnable */ + #define PGMNEIFPGMENABLE_ENABLE 1 + #define PGMNEIFPGMENABLE_DISABLE 2 + +/* enums for column pgmNeTsiStateBits */ + #define PGMNETSISTATEBITS_INITIALISING 0 + #define PGMNETSISTATEBITS_SPMSQNSTATEVALID 1 + #define PGMNETSISTATEBITS_DLRCANPROVIDEPARITY 2 + +/* enums for column pgmNeTsiRtxSequenceNumberType */ + #define PGMNETSIRTXSEQUENCENUMBERTYPE_SELECTIVE 1 + #define PGMNETSIRTXSEQUENCENUMBERTYPE_TG 2 + +/* enums for column pgmNeTsiRtxStateBits */ + #define PGMNETSIRTXSTATEBITS_INITIALISING 0 + #define PGMNETSIRTXSTATEBITS_ELIMINATING 1 + #define PGMNETSIRTXSTATEBITS_REDIRECTING 2 + #define PGMNETSIRTXSTATEBITS_STATECREATEDBYNULLNAK 3 + #define PGMNETSIRTXSTATEBITS_LISTNAKENTRY 4 + #define PGMNETSIRTXSTATEBITS_PARITYSTATE 5 + +/* enums for column pgmNeTsiPollType */ + #define PGMNETSIPOLLTYPE_GENERAL 1 + #define PGMNETSIPOLLTYPE_DLR 2 + +/* enums for column pgmSourceAdvMode */ + #define PGMSOURCEADVMODE_DATA 1 + #define PGMSOURCEADVMODE_TIME 2 + #define PGMSOURCEADVMODE_APPLCTRL 3 + #define PGMSOURCEADVMODE_OTHER 4 + +/* enums for column pgmSourceLateJoin */ + #define PGMSOURCELATEJOIN_ENABLE 1 + #define PGMSOURCELATEJOIN_DISABLE 2 + +/* enums for column pgmSourceFEC */ + #define PGMSOURCEFEC_DISABLED 1 + #define PGMSOURCEFEC_ENABLEDFIXEDPACKETSIZE 2 + #define PGMSOURCEFEC_ENABLEDVARIABLEPACKETSIZE 3 + +/* enums for column pgmReceiverSendNaks */ + #define PGMRECEIVERSENDNAKS_ENABLED 1 + #define PGMRECEIVERSENDNAKS_DISABLED 2 + +/* enums for column pgmReceiverLateJoin */ + #define PGMRECEIVERLATEJOIN_ENABLED 1 + #define PGMRECEIVERLATEJOIN_DISABLED 2 + +/* enums for column pgmReceiverDeliveryOrder */ + #define PGMRECEIVERDELIVERYORDER_UNORDERED 1 + #define PGMRECEIVERDELIVERYORDER_ORDERED 2 + +/* enums for column pgmReceiverMcastNaks */ + #define PGMRECEIVERMCASTNAKS_ENABLED 1 + #define PGMRECEIVERMCASTNAKS_DISABLED 2 + +#endif /* PGMMIB_ENUMS_H */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/processor.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/processor.h new file mode 100644 index 0000000..f7ee31a --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/processor.h @@ -0,0 +1,61 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Processor macros for cross-platform, cross-compiler froyo. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_PROCESSOR_H__ +#define __PGM_IMPL_PROCESSOR_H__ + +/* Memory prefetch */ +#if defined( sun ) +static inline void pgm_prefetch (const void *x) +{ + asm volatile ( "prefetch [%0], #one_write" + : /* nil */ + : "r" (x)); +} +static inline void pgm_prefetchw (const void *x) +{ + asm volatile ( "prefetch [%0], #n_writes" + : /* nil */ + : "r" (x)); +} +#elif defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) +static inline void pgm_prefetch (const void *x) +{ + __builtin_prefetch (x, 0 /* read */, 0 /* no temporal locality */); +} +static inline void pgm_prefetchw (const void *x) +{ + __builtin_prefetch (x, 1 /* write */, 3 /* high temporal */); +} +#else +static inline void pgm_prefetch (PGM_GNUC_UNUSED const void *x) +{ +} +static inline void pgm_prefetchw (PGM_GNUC_UNUSED const void *x) +{ +} +#endif + +#endif /* __PGM_IMPL_PROCESSOR_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/queue.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/queue.h new file mode 100644 index 0000000..c640e52 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/queue.h @@ -0,0 +1,51 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable double-ended queue. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_QUEUE_H__ +#define __PGM_IMPL_QUEUE_H__ + +typedef struct pgm_queue_t pgm_queue_t; + +#include +#include + +PGM_BEGIN_DECLS + +struct pgm_queue_t +{ + pgm_list_t* head; /* head & tail equal on 1 element */ + pgm_list_t* tail; + unsigned length; +}; + +PGM_GNUC_INTERNAL bool pgm_queue_is_empty (const pgm_queue_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_queue_push_head_link (pgm_queue_t*restrict, pgm_list_t*restrict); +PGM_GNUC_INTERNAL pgm_list_t* pgm_queue_pop_tail_link (pgm_queue_t*); +PGM_GNUC_INTERNAL pgm_list_t* pgm_queue_peek_tail_link (pgm_queue_t*) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_queue_unlink (pgm_queue_t*restrict, pgm_list_t*restrict); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_QUEUE_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/rand.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/rand.h new file mode 100644 index 0000000..0adfd78 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/rand.h @@ -0,0 +1,50 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable weak pseudo-random generator. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_RAND_H__ +#define __PGM_IMPL_RAND_H__ + +typedef struct pgm_rand_t pgm_rand_t; + +#include + +PGM_BEGIN_DECLS + +struct pgm_rand_t { + uint32_t seed; +}; + +PGM_GNUC_INTERNAL void pgm_rand_create (pgm_rand_t*); +PGM_GNUC_INTERNAL uint32_t pgm_rand_int (pgm_rand_t*); +PGM_GNUC_INTERNAL int32_t pgm_rand_int_range (pgm_rand_t*, int32_t, int32_t); +PGM_GNUC_INTERNAL uint32_t pgm_random_int (void); +PGM_GNUC_INTERNAL int32_t pgm_random_int_range (int32_t, int32_t); + +PGM_GNUC_INTERNAL void pgm_rand_init (void); +PGM_GNUC_INTERNAL void pgm_rand_shutdown (void); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_RAND_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/rate_control.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/rate_control.h new file mode 100644 index 0000000..b27b266 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/rate_control.h @@ -0,0 +1,54 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Rate regulation. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_RATE_CONTROL_H__ +#define __PGM_IMPL_RATE_CONTROL_H__ + +typedef struct pgm_rate_t pgm_rate_t; + +#include +#include +#include + +PGM_BEGIN_DECLS + +struct pgm_rate_t { + ssize_t rate_per_sec; + ssize_t rate_per_msec; + size_t iphdr_len; + + ssize_t rate_limit; /* signed for math */ + pgm_time_t last_rate_check; + pgm_spinlock_t spinlock; +}; + +PGM_GNUC_INTERNAL void pgm_rate_create (pgm_rate_t*, const ssize_t, const size_t, const uint16_t); +PGM_GNUC_INTERNAL void pgm_rate_destroy (pgm_rate_t*); +PGM_GNUC_INTERNAL bool pgm_rate_check (pgm_rate_t*, const size_t, const bool); +PGM_GNUC_INTERNAL pgm_time_t pgm_rate_remaining (pgm_rate_t*, const size_t); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_RATE_CONTROL_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/receiver.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/receiver.h new file mode 100644 index 0000000..e9c3a75 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/receiver.h @@ -0,0 +1,142 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM receiver socket. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_RECEIVER_H__ +#define __PGM_IMPL_RECEIVER_H__ + +typedef struct pgm_peer_t pgm_peer_t; + +#ifndef _WIN32 +# include +#endif +#include +#include + +PGM_BEGIN_DECLS + +/* Performance Counters */ + +enum { + PGM_PC_RECEIVER_DATA_BYTES_RECEIVED, + PGM_PC_RECEIVER_DATA_MSGS_RECEIVED, + PGM_PC_RECEIVER_NAK_FAILURES, + PGM_PC_RECEIVER_BYTES_RECEIVED, +/* PGM_PC_RECEIVER_CKSUM_ERRORS, */ /* inherently same as source */ + PGM_PC_RECEIVER_MALFORMED_SPMS, + PGM_PC_RECEIVER_MALFORMED_ODATA, + PGM_PC_RECEIVER_MALFORMED_RDATA, + PGM_PC_RECEIVER_MALFORMED_NCFS, + PGM_PC_RECEIVER_PACKETS_DISCARDED, + PGM_PC_RECEIVER_LOSSES, +/* PGM_PC_RECEIVER_BYTES_DELIVERED_TO_APP, */ +/* PGM_PC_RECEIVER_MSGS_DELIVERED_TO_APP, */ + PGM_PC_RECEIVER_DUP_SPMS, + PGM_PC_RECEIVER_DUP_DATAS, + PGM_PC_RECEIVER_PARITY_NAK_PACKETS_SENT, + PGM_PC_RECEIVER_SELECTIVE_NAK_PACKETS_SENT, + PGM_PC_RECEIVER_PARITY_NAKS_SENT, + PGM_PC_RECEIVER_SELECTIVE_NAKS_SENT, + PGM_PC_RECEIVER_PARITY_NAKS_RETRANSMITTED, + PGM_PC_RECEIVER_SELECTIVE_NAKS_RETRANSMITTED, + PGM_PC_RECEIVER_PARITY_NAKS_FAILED, + PGM_PC_RECEIVER_SELECTIVE_NAKS_FAILED, + PGM_PC_RECEIVER_NAKS_FAILED_RXW_ADVANCED, + PGM_PC_RECEIVER_NAKS_FAILED_NCF_RETRIES_EXCEEDED, + PGM_PC_RECEIVER_NAKS_FAILED_DATA_RETRIES_EXCEEDED, +/* PGM_PC_RECEIVER_NAKS_FAILED_GEN_EXPIRED */ + PGM_PC_RECEIVER_NAK_FAILURES_DELIVERED, + PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED, + PGM_PC_RECEIVER_NAK_ERRORS, +/* PGM_PC_RECEIVER_LAST_ACTIVITY, */ +/* PGM_PC_RECEIVER_NAK_SVC_TIME_MIN, */ + PGM_PC_RECEIVER_NAK_SVC_TIME_MEAN, +/* PGM_PC_RECEIVER_NAK_SVC_TIME_MAX, */ +/* PGM_PC_RECEIVER_NAK_FAIL_TIME_MIN, */ + PGM_PC_RECEIVER_NAK_FAIL_TIME_MEAN, +/* PGM_PC_RECEIVER_NAK_FAIL_TIME_MAX, */ +/* PGM_PC_RECEIVER_TRANSMIT_MIN, */ + PGM_PC_RECEIVER_TRANSMIT_MEAN, +/* PGM_PC_RECEIVER_TRANSMIT_MAX, */ + PGM_PC_RECEIVER_ACKS_SENT, + +/* marker */ + PGM_PC_RECEIVER_MAX +}; + +struct pgm_peer_t { + volatile uint32_t ref_count; /* atomic integer */ + + pgm_tsi_t tsi; + struct sockaddr_storage group_nla; + struct sockaddr_storage nla, local_nla; /* nla = advertised, local_nla = from packet */ + struct sockaddr_storage poll_nla; /* from parent to direct poll-response */ + struct sockaddr_storage redirect_nla; /* from dlr */ + pgm_time_t polr_expiry; + pgm_time_t spmr_expiry; + pgm_time_t spmr_tstamp; + + pgm_rxw_t* restrict window; + pgm_sock_t* restrict sock; + pgm_list_t peers_link; + pgm_slist_t pending_link; + + unsigned is_fec_enabled:1; + unsigned has_proactive_parity:1; /* indicating availability from this source */ + unsigned has_ondemand_parity:1; + + uint32_t spm_sqn; + pgm_time_t expiry; + + pgm_time_t ack_rb_expiry; /* 0 = no ACK pending */ + pgm_time_t ack_last_tstamp; /* in source time reference */ + pgm_list_t ack_link; + + uint32_t last_poll_sqn; + uint16_t last_poll_round; + pgm_time_t last_packet; + pgm_time_t last_data_tstamp; /* local timestamp of ack_last_tstamp */ + unsigned last_commit; + uint32_t lost_count; + uint32_t last_cumulative_losses; + volatile uint32_t cumulative_stats[PGM_PC_RECEIVER_MAX]; + uint32_t snap_stats[PGM_PC_RECEIVER_MAX]; + + uint32_t min_fail_time; + uint32_t max_fail_time; +}; + +PGM_GNUC_INTERNAL pgm_peer_t* pgm_new_peer (pgm_sock_t*const restrict, const pgm_tsi_t*const restrict, const struct sockaddr*const restrict, const socklen_t, const struct sockaddr*const restrict, const socklen_t, const pgm_time_t); +PGM_GNUC_INTERNAL void pgm_peer_unref (pgm_peer_t*); +PGM_GNUC_INTERNAL int pgm_flush_peers_pending (pgm_sock_t*const restrict, struct pgm_msgv_t**restrict, const struct pgm_msgv_t*const, size_t*const restrict, unsigned*const restrict); +PGM_GNUC_INTERNAL bool pgm_peer_has_pending (pgm_peer_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_peer_set_pending (pgm_sock_t*const, pgm_peer_t*const); +PGM_GNUC_INTERNAL bool pgm_check_peer_state (pgm_sock_t*const, const pgm_time_t); +PGM_GNUC_INTERNAL void pgm_set_reset_error (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_msgv_t*const restrict); +PGM_GNUC_INTERNAL pgm_time_t pgm_min_receiver_expiry (pgm_time_t, pgm_sock_t*) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_on_peer_nak (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_on_data (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_on_ncf (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_on_spm (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_on_poll (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; + +PGM_END_DECLS + +#endif /* __PGM_IMPL_RECEIVER_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/reed_solomon.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/reed_solomon.h new file mode 100644 index 0000000..98f6734 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/reed_solomon.h @@ -0,0 +1,51 @@ +/* + * Reed-Solomon forward error correction based on Vandermonde matrices + * + * Copyright (c) 2006-2008 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_REED_SOLOMON_H__ +#define __PGM_IMPL_REED_SOLOMON_H__ + +typedef struct pgm_rs_t pgm_rs_t; + +#include +#include + +PGM_BEGIN_DECLS + +struct pgm_rs_t { + uint8_t n, k; /* RS(n, k) */ + pgm_gf8_t* GM; + pgm_gf8_t* RM; +}; + +#define PGM_RS_DEFAULT_N 255 + +PGM_GNUC_INTERNAL void pgm_rs_create (pgm_rs_t*, const uint8_t, const uint8_t); +PGM_GNUC_INTERNAL void pgm_rs_destroy (pgm_rs_t*); +PGM_GNUC_INTERNAL void pgm_rs_encode (pgm_rs_t*restrict, const pgm_gf8_t**restrict, const uint8_t, pgm_gf8_t*restrict, const uint16_t); +PGM_GNUC_INTERNAL void pgm_rs_decode_parity_inline (pgm_rs_t*restrict, pgm_gf8_t**restrict, const uint8_t*restrict, const uint16_t); +PGM_GNUC_INTERNAL void pgm_rs_decode_parity_appended (pgm_rs_t*restrict, pgm_gf8_t**restrict, const uint8_t*restrict, const uint16_t); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_REED_SOLOMON_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/rxw.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/rxw.h new file mode 100644 index 0000000..6eaa9f0 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/rxw.h @@ -0,0 +1,223 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * basic receive window. + * + * Copyright (c) 2006-2008 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_RXW_H__ +#define __PGM_IMPL_RXW_H__ + +typedef struct pgm_rxw_state_t pgm_rxw_state_t; +typedef struct pgm_rxw_t pgm_rxw_t; + +#include + +PGM_BEGIN_DECLS + +enum +{ + PGM_PKT_STATE_ERROR = 0, + PGM_PKT_STATE_BACK_OFF, /* PGM protocol recovery states */ + PGM_PKT_STATE_WAIT_NCF, + PGM_PKT_STATE_WAIT_DATA, + PGM_PKT_STATE_HAVE_DATA, /* data received waiting to commit to application layer */ + PGM_PKT_STATE_HAVE_PARITY, /* contains parity information not original data */ + PGM_PKT_STATE_COMMIT_DATA, /* commited data waiting for purging */ + PGM_PKT_STATE_LOST_DATA /* if recovery fails, but packet has not yet been commited */ +}; + +enum +{ + PGM_RXW_OK = 0, + PGM_RXW_INSERTED, + PGM_RXW_APPENDED, + PGM_RXW_UPDATED, + PGM_RXW_MISSING, + PGM_RXW_DUPLICATE, + PGM_RXW_MALFORMED, + PGM_RXW_BOUNDS, + PGM_RXW_SLOW_CONSUMER, + PGM_RXW_UNKNOWN +}; + +/* must be smaller than PGM skbuff control buffer */ +struct pgm_rxw_state_t { + pgm_time_t timer_expiry; + int pkt_state; + + uint8_t nak_transmit_count; /* 8-bit for size constraints */ + uint8_t ncf_retry_count; + uint8_t data_retry_count; + +/* only valid on tg_sqn::pkt_sqn = 0 */ + unsigned is_contiguous:1; /* transmission group */ +}; + +struct pgm_rxw_t { + const pgm_tsi_t* tsi; + + pgm_queue_t ack_backoff_queue; + pgm_queue_t nak_backoff_queue; + pgm_queue_t wait_ncf_queue; + pgm_queue_t wait_data_queue; +/* window context counters */ + uint32_t lost_count; /* failed to repair */ + uint32_t fragment_count; /* incomplete apdu */ + uint32_t parity_count; /* parity for repairs */ + uint32_t committed_count; /* but still in window */ + + uint16_t max_tpdu; /* maximum packet size */ + uint32_t lead, trail; + uint32_t rxw_trail, rxw_trail_init; + uint32_t commit_lead; + unsigned is_constrained:1; + unsigned is_defined:1; + unsigned has_event:1; /* edge triggered */ + unsigned is_fec_available:1; + pgm_rs_t rs; + uint32_t tg_size; /* transmission group size for parity recovery */ + uint8_t tg_sqn_shift; + + uint32_t bitmap; /* receive status of last 32 packets */ + uint32_t data_loss; /* p */ + uint32_t ack_c_p; /* constant Cᵨ */ + +/* counters all guint32 */ + uint32_t min_fill_time; /* restricted from pgm_time_t */ + uint32_t max_fill_time; + uint32_t min_nak_transmit_count; + uint32_t max_nak_transmit_count; + uint32_t cumulative_losses; + uint32_t bytes_delivered; + uint32_t msgs_delivered; + + size_t size; /* in bytes */ + unsigned alloc; /* in pkts */ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +/* C99 flexible array, sizeof() invalid */ + struct pgm_sk_buff_t* pdata[]; +#elif !defined(__STDC_VERSION__) || defined(__cplusplus) +/* C90 and older */ + struct pgm_sk_buff_t* pdata[1]; +#else +/* GNU C variable-length object */ + struct pgm_sk_buff_t* pdata[0]; +#endif +}; + + +PGM_GNUC_INTERNAL pgm_rxw_t* pgm_rxw_create (const pgm_tsi_t*const, const uint16_t, const unsigned, const unsigned, const ssize_t, const uint32_t) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_rxw_destroy (pgm_rxw_t*const); +PGM_GNUC_INTERNAL int pgm_rxw_add (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*const restrict, const pgm_time_t, const pgm_time_t) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_rxw_add_ack (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*const restrict, const pgm_time_t); +PGM_GNUC_INTERNAL void pgm_rxw_remove_ack (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*const restrict); +PGM_GNUC_INTERNAL void pgm_rxw_remove_commit (pgm_rxw_t*const); +PGM_GNUC_INTERNAL ssize_t pgm_rxw_readv (pgm_rxw_t*const restrict, struct pgm_msgv_t** restrict, const unsigned) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL unsigned pgm_rxw_remove_trail (pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL unsigned pgm_rxw_update (pgm_rxw_t*const, const uint32_t, const uint32_t, const pgm_time_t, const pgm_time_t) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_rxw_update_fec (pgm_rxw_t*const, const uint8_t); +PGM_GNUC_INTERNAL int pgm_rxw_confirm (pgm_rxw_t*const, const uint32_t, const pgm_time_t, const pgm_time_t, const pgm_time_t) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_rxw_lost (pgm_rxw_t*const, const uint32_t); +PGM_GNUC_INTERNAL void pgm_rxw_state (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*const restrict, const int); +PGM_GNUC_INTERNAL struct pgm_sk_buff_t* pgm_rxw_peek (pgm_rxw_t*const, const uint32_t) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL const char* pgm_pkt_state_string (const int) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL const char* pgm_rxw_returns_string (const int) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_rxw_dump (const pgm_rxw_t*const); + +/* declare for GCC attributes */ +static inline unsigned pgm_rxw_max_length (const pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline uint32_t pgm_rxw_length (const pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline size_t pgm_rxw_size (const pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline bool pgm_rxw_is_empty (const pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline bool pgm_rxw_is_full (const pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline uint32_t pgm_rxw_lead (const pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline uint32_t pgm_rxw_next_lead (const pgm_rxw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; + +static inline +unsigned +pgm_rxw_max_length ( + const pgm_rxw_t* const window + ) +{ + pgm_assert (NULL != window); + return window->alloc; +} + +static inline +uint32_t +pgm_rxw_length ( + const pgm_rxw_t* const window + ) +{ + pgm_assert (NULL != window); + return ( 1 + window->lead ) - window->trail; +} + +static inline +size_t +pgm_rxw_size ( + const pgm_rxw_t* const window + ) +{ + pgm_assert (NULL != window); + return window->size; +} + +static inline +bool +pgm_rxw_is_empty ( + const pgm_rxw_t* const window + ) +{ + pgm_assert (NULL != window); + return pgm_rxw_length (window) == 0; +} + +static inline +bool +pgm_rxw_is_full ( + const pgm_rxw_t* const window + ) +{ + pgm_assert (NULL != window); + return pgm_rxw_length (window) == pgm_rxw_max_length (window); +} + +static inline +uint32_t +pgm_rxw_lead ( + const pgm_rxw_t* const window + ) +{ + pgm_assert (NULL != window); + return window->lead; +} + +static inline +uint32_t +pgm_rxw_next_lead ( + const pgm_rxw_t* const window + ) +{ + pgm_assert (NULL != window); + return (uint32_t)(pgm_rxw_lead (window) + 1); +} + +PGM_END_DECLS + +#endif /* __PGM_IMPL_RXW_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/slist.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/slist.h new file mode 100644 index 0000000..e71b15d --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/slist.h @@ -0,0 +1,52 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable singly-linked list. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_SLIST_H__ +#define __PGM_IMPL_SLIST_H__ + +typedef struct pgm_slist_t pgm_slist_t; + +#include + +PGM_BEGIN_DECLS + +struct pgm_slist_t +{ + void* restrict data; + struct pgm_slist_t* restrict next; +}; + +PGM_GNUC_INTERNAL pgm_slist_t* pgm_slist_append (pgm_slist_t*restrict, void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL pgm_slist_t* pgm_slist_prepend (pgm_slist_t*restrict, void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL pgm_slist_t* pgm_slist_prepend_link (pgm_slist_t*restrict, pgm_slist_t*restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL pgm_slist_t* pgm_slist_remove (pgm_slist_t*restrict, const void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL pgm_slist_t* pgm_slist_remove_first (pgm_slist_t*) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_slist_free (pgm_slist_t*); +PGM_GNUC_INTERNAL pgm_slist_t* pgm_slist_last (pgm_slist_t*) PGM_GNUC_PURE PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL unsigned pgm_slist_length (pgm_slist_t*) PGM_GNUC_PURE PGM_GNUC_WARN_UNUSED_RESULT; + +PGM_END_DECLS + +#endif /* __PGM_IMPL_SLIST_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/sn.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/sn.h new file mode 100644 index 0000000..a8cd3da --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/sn.h @@ -0,0 +1,174 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * serial number arithmetic: rfc 1982 + * + * Copyright (c) 2006-2007 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_SN_H__ +#define __PGM_IMPL_SN_H__ + +#include +#include + +PGM_BEGIN_DECLS + +#define PGM_UINT32_SIGN_BIT (1UL<<31) +#define PGM_UINT64_SIGN_BIT (1ULL<<63) + +/* declare for GCC attributes */ +static inline bool pgm_uint32_lt (const uint32_t, const uint32_t) PGM_GNUC_CONST; +static inline bool pgm_uint32_lte (const uint32_t, const uint32_t) PGM_GNUC_CONST; +static inline bool pgm_uint32_gt (const uint32_t, const uint32_t) PGM_GNUC_CONST; +static inline bool pgm_uint32_gte (const uint32_t, const uint32_t) PGM_GNUC_CONST; +static inline bool pgm_uint64_lt (const uint64_t, const uint64_t) PGM_GNUC_CONST; +static inline bool pgm_uint64_lte (const uint64_t, const uint64_t) PGM_GNUC_CONST; +static inline bool pgm_uint64_gt (const uint64_t, const uint64_t) PGM_GNUC_CONST; +static inline bool pgm_uint64_gte (const uint64_t, const uint64_t) PGM_GNUC_CONST; + +/* 32 bit */ +static inline +bool pgm_uint32_lt ( + const uint32_t s, + const uint32_t t + ) +{ + pgm_assert (sizeof(int) >= 4); + return ((s) - (t)) & PGM_UINT32_SIGN_BIT; +} + +static inline +bool +pgm_uint32_lte ( + const uint32_t s, + const uint32_t t + ) +{ + pgm_assert (sizeof(int) >= 4); + return ((s) == (t)) || ( ((s) - (t)) & PGM_UINT32_SIGN_BIT ); +} + +static inline +bool +pgm_uint32_gt ( + const uint32_t s, + const uint32_t t + ) +{ + pgm_assert (sizeof(int) >= 4); + return ((t) - (s)) & PGM_UINT32_SIGN_BIT; +} + +static inline +bool +pgm_uint32_gte ( + const uint32_t s, + const uint32_t t + ) +{ + pgm_assert (sizeof(int) >= 4); + return ((s) == (t)) || ( ((t) - (s)) & PGM_UINT32_SIGN_BIT ); +} + +/* 64 bit */ +static inline +bool +pgm_uint64_lt ( + const uint64_t s, + const uint64_t t + ) +{ + if (sizeof(int) == 4) + { +/* need to force boolean conversion when int = 32bits */ + return ( ((s) - (t)) & PGM_UINT64_SIGN_BIT ) != 0; + } + else + { + pgm_assert (sizeof(int) >= 8); + return ( ((s) - (t)) & PGM_UINT64_SIGN_BIT ) != 0; + } +} + +static inline +bool +pgm_uint64_lte ( + const uint64_t s, + const uint64_t t + ) +{ + if (sizeof(int) == 4) + { +/* need to force boolean conversion when int = 32bits */ + return ( (s) == (t) ) + || + ( ( ((s) - (t)) & PGM_UINT64_SIGN_BIT ) != 0 ); + } + else + { + pgm_assert (sizeof(int) >= 8); + return ( ((s) == (t)) || ( ((s) - (t)) & PGM_UINT64_SIGN_BIT ) ) != 0; + } +} + +static inline +bool +pgm_uint64_gt ( + const uint64_t s, + const uint64_t t + ) +{ + if (sizeof(int) == 4) + { +/* need to force boolean conversion when int = 32bits */ + return ( ((t) - (s)) & PGM_UINT64_SIGN_BIT ) != 0; + } + else + { + pgm_assert (sizeof(int) >= 8); + return ( ((t) - (s)) & PGM_UINT64_SIGN_BIT ) != 0; + } +} + +static inline +bool +pgm_uint64_gte ( + const uint64_t s, + const uint64_t t + ) +{ + if (sizeof(int) == 4) + { +/* need to force boolean conversion when int = 32bits */ + return ( (s) == (t) ) + || + ( ( ((t) - (s)) & PGM_UINT64_SIGN_BIT ) != 0 ); + } + else + { + pgm_assert (sizeof(int) >= 8); + return ( ((s) == (t)) || ( ((t) - (s)) & PGM_UINT64_SIGN_BIT ) ) != 0; + } +} + +PGM_END_DECLS + +#endif /* __PGM_IMPL_SN_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/sockaddr.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/sockaddr.h new file mode 100644 index 0000000..715c63f --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/sockaddr.h @@ -0,0 +1,105 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * struct sockaddr functions independent of in or in6. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_SOCKADDR_H__ +#define __PGM_IMPL_SOCKADDR_H__ + +#ifndef _WIN32 +# include +#endif +#include + +PGM_BEGIN_DECLS + +/* fallback values where not directly supported */ +#ifndef MSG_DONTWAIT +# define MSG_DONTWAIT 0 +#endif +#ifndef MSG_ERRQUEUE +# define MSG_ERRQUEUE 0x2000 +#endif +#if !defined(EAFNOSUPPORT) && defined(WSAEAFNOSUPPORT) +# define EAFNOSUPPORT WSAEAFNOSUPPORT +#endif + +#ifndef _WIN32 +# define PGM_INVALID_SOCKET -1 +# define PGM_SOCKET_ERROR -1 +# define pgm_closesocket close +# define pgm_sock_errno() (errno) +# define pgm_sock_strerror(e) strerror(e) +# define pgm_error_from_sock_errno pgm_error_from_errno +#else +# define PGM_INVALID_SOCKET (int)INVALID_SOCKET +# define PGM_SOCKET_ERROR (int)SOCKET_ERROR +# define pgm_closesocket closesocket +# define pgm_sock_errno() WSAGetLastError() +# define pgm_sock_strerror(e) pgm_wsastrerror(e) +# define pgm_error_from_sock_errno pgm_error_from_wsa_errno +#endif + +PGM_GNUC_INTERNAL sa_family_t pgm_sockaddr_family (const struct sockaddr* sa); +PGM_GNUC_INTERNAL uint16_t pgm_sockaddr_port (const struct sockaddr* sa); +PGM_GNUC_INTERNAL socklen_t pgm_sockaddr_len (const struct sockaddr* sa); +PGM_GNUC_INTERNAL socklen_t pgm_sockaddr_storage_len (const struct sockaddr_storage* ss); +PGM_GNUC_INTERNAL uint32_t pgm_sockaddr_scope_id (const struct sockaddr* sa); +PGM_GNUC_INTERNAL int pgm_sockaddr_ntop (const struct sockaddr*restrict sa, char*restrict dst, size_t ulen); +PGM_GNUC_INTERNAL int pgm_sockaddr_pton (const char*restrict src, struct sockaddr*restrict dst); +PGM_GNUC_INTERNAL int pgm_sockaddr_is_addr_multicast (const struct sockaddr* sa); +PGM_GNUC_INTERNAL int pgm_sockaddr_is_addr_unspecified (const struct sockaddr* sa); +PGM_GNUC_INTERNAL int pgm_sockaddr_cmp (const struct sockaddr*restrict sa1, const struct sockaddr*restrict sa2); +PGM_GNUC_INTERNAL int pgm_sockaddr_hdrincl (const int s, const sa_family_t sa_family, const bool v); +PGM_GNUC_INTERNAL int pgm_sockaddr_pktinfo (const int s, const sa_family_t sa_family, const bool v); +PGM_GNUC_INTERNAL int pgm_sockaddr_router_alert (const int s, const sa_family_t sa_family, const bool v); +PGM_GNUC_INTERNAL int pgm_sockaddr_tos (const int s, const sa_family_t sa_family, const int tos); +PGM_GNUC_INTERNAL int pgm_sockaddr_join_group (const int s, const sa_family_t sa_family, const struct group_req* gr); +PGM_GNUC_INTERNAL int pgm_sockaddr_leave_group (const int s, const sa_family_t sa_family, const struct group_req* gr); +PGM_GNUC_INTERNAL int pgm_sockaddr_block_source (const int s, const sa_family_t sa_family, const struct group_source_req* gsr); +PGM_GNUC_INTERNAL int pgm_sockaddr_unblock_source (const int s, const sa_family_t sa_family, const struct group_source_req* gsr); +PGM_GNUC_INTERNAL int pgm_sockaddr_join_source_group (const int s, const sa_family_t sa_family, const struct group_source_req* gsr); +PGM_GNUC_INTERNAL int pgm_sockaddr_leave_source_group (const int s, const sa_family_t sa_family, const struct group_source_req* gsr); +#if defined(MCAST_MSFILTER) || defined(SIOCSMSFILTER) +# ifndef GROUP_FILTER_SIZE +# define GROUP_FILTER_SIZE(numsrc) (sizeof (struct group_filter) \ + - sizeof (struct sockaddr_storage) \ + + ((numsrc) \ + * sizeof (struct sockaddr_storage))) +# endif +PGM_GNUC_INTERNAL int pgm_sockaddr_msfilter (const int s, const sa_family_t sa_family, const struct group_filter* gf_list); +#endif +PGM_GNUC_INTERNAL int pgm_sockaddr_multicast_if (int s, const struct sockaddr* address, unsigned ifindex); +PGM_GNUC_INTERNAL int pgm_sockaddr_multicast_loop (const int s, const sa_family_t sa_family, const bool v); +PGM_GNUC_INTERNAL int pgm_sockaddr_multicast_hops (const int s, const sa_family_t sa_family, const unsigned hops); +PGM_GNUC_INTERNAL void pgm_sockaddr_nonblocking (const int s, const bool v); + +PGM_GNUC_INTERNAL const char* pgm_inet_ntop (int af, const void*restrict src, char*restrict dst, socklen_t size); +PGM_GNUC_INTERNAL int pgm_inet_pton (int af, const char*restrict src, void*restrict dst); + +PGM_GNUC_INTERNAL int pgm_nla_to_sockaddr (const void*restrict nla, struct sockaddr*restrict sa); +PGM_GNUC_INTERNAL int pgm_sockaddr_to_nla (const struct sockaddr*restrict sa, void*restrict nla); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_SOCKADDR_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/socket.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/socket.h new file mode 100644 index 0000000..ee175d8 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/socket.h @@ -0,0 +1,182 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM socket. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_SOCKET_H__ +#define __PGM_IMPL_SOCKET_H__ + +struct pgm_sock_t; + +#include +#include +#include + +PGM_BEGIN_DECLS + +#ifndef IP_MAX_MEMBERSHIPS +# define IP_MAX_MEMBERSHIPS 20 +#endif + +struct pgm_sock_t { + sa_family_t family; /* communications domain */ + int socket_type; + int protocol; + pgm_tsi_t tsi; + uint16_t dport; + uint16_t udp_encap_ucast_port; + uint16_t udp_encap_mcast_port; + uint32_t rand_node_id; /* node identifier */ + + pgm_rwlock_t lock; /* running / destroyed */ + pgm_mutex_t receiver_mutex; /* receiver API */ + pgm_mutex_t source_mutex; /* source API */ + pgm_spinlock_t txw_spinlock; /* transmit window */ + pgm_mutex_t send_mutex; /* non-router alert socket */ + pgm_mutex_t timer_mutex; /* next timer expiration */ + + bool is_bound; + bool is_connected; + bool is_destroyed; + bool is_reset; + bool is_abort_on_reset; + + bool can_send_data; /* and SPMs */ + bool can_send_nak; /* muted receiver */ + bool can_recv_data; /* send-only */ + bool is_edge_triggered_recv; + bool is_nonblocking; + + struct group_source_req send_gsr; /* multicast */ + struct sockaddr_storage send_addr; /* unicast nla */ + int send_sock; + int send_with_router_alert_sock; + struct group_source_req recv_gsr[IP_MAX_MEMBERSHIPS]; /* sa_family = 0 terminated */ + unsigned recv_gsr_len; + int recv_sock; + + size_t max_apdu; + uint16_t max_tpdu; + uint16_t max_tsdu; /* excluding optional var_pktlen word */ + uint16_t max_tsdu_fragment; + size_t iphdr_len; + bool use_multicast_loop; /* and reuseaddr for UDP encapsulation */ + unsigned hops; + unsigned txw_sqns, txw_secs; + unsigned rxw_sqns, rxw_secs; + ssize_t txw_max_rte, rxw_max_rte; + size_t sndbuf, rcvbuf; /* setsockopt (SO_SNDBUF/SO_RCVBUF) */ + + pgm_txw_t* restrict window; + pgm_rate_t rate_control; + pgm_time_t adv_ivl; /* advancing with data */ + unsigned adv_mode; /* 0 = time, 1 = data */ + bool is_controlled_spm; + bool is_controlled_odata; + bool is_controlled_rdata; + + bool use_cr; /* congestion reports */ + bool use_pgmcc; /* congestion control */ + bool is_pending_crqst; + unsigned ack_c; /* constant C */ + unsigned ack_c_p; /* constant Cᵨ */ + uint32_t ssthresh; /* slow-start threshold */ + uint32_t tokens; + uint32_t cwnd_size; /* congestion window size */ + uint32_t ack_rx_max; + uint32_t ack_bitmap; + uint32_t acks_after_loss; + uint32_t suspended_sqn; + bool is_congested; + pgm_time_t ack_expiry; + pgm_time_t ack_expiry_ivl; + pgm_time_t next_crqst; + pgm_time_t crqst_ivl; + pgm_time_t ack_bo_ivl; + struct sockaddr_storage acker_nla; + uint64_t acker_loss; + + pgm_notify_t ack_notify; + pgm_notify_t rdata_notify; + + pgm_hash_t last_hash_key; + void* restrict last_hash_value; + unsigned last_commit; + size_t blocklen; /* length of buffer blocked */ + bool is_apdu_eagain; /* writer-lock on window_lock exists as send would block */ + bool is_spm_eagain; /* writer-lock in receiver */ + + struct { + size_t data_pkt_offset; + size_t data_bytes_offset; + uint32_t first_sqn; + struct pgm_sk_buff_t* skb; /* references external buffer */ + size_t tsdu_length; + uint32_t unfolded_odata; + size_t apdu_length; + unsigned vector_index; + size_t vector_offset; + bool is_rate_limited; + } pkt_dontwait_state; + + uint32_t spm_sqn; + unsigned spm_ambient_interval; /* microseconds */ + unsigned* restrict spm_heartbeat_interval; /* zero terminated, zero lead-pad */ + unsigned spm_heartbeat_state; /* indexof spm_heartbeat_interval */ + unsigned spm_heartbeat_len; + unsigned peer_expiry; /* from absence of SPMs */ + unsigned spmr_expiry; /* waiting for peer SPMRs */ + + pgm_rand_t rand_; /* for calculating nak_rb_ivl from nak_bo_ivl */ + unsigned nak_data_retries, nak_ncf_retries; + pgm_time_t nak_bo_ivl, nak_rpt_ivl, nak_rdata_ivl; + pgm_time_t next_heartbeat_spm, next_ambient_spm; + + bool use_proactive_parity; + bool use_ondemand_parity; + bool use_var_pktlen; + uint8_t rs_n; + uint8_t rs_k; + uint8_t rs_proactive_h; /* 0 <= proactive-h <= ( n - k ) */ + uint8_t tg_sqn_shift; + struct pgm_sk_buff_t* restrict rx_buffer; + + pgm_rwlock_t peers_lock; + pgm_hashtable_t* restrict peers_hashtable; /* fast lookup */ + pgm_list_t* restrict peers_list; /* easy iteration */ + pgm_slist_t* restrict peers_pending; /* rxw: have or lost data */ + pgm_notify_t pending_notify; /* timer to rx */ + bool is_pending_read; + pgm_time_t next_poll; + + uint32_t cumulative_stats[PGM_PC_SOURCE_MAX]; + uint32_t snap_stats[PGM_PC_SOURCE_MAX]; + pgm_time_t snap_time; +}; + + +/* global variables */ +extern pgm_rwlock_t pgm_sock_list_lock; +extern pgm_slist_t* pgm_sock_list; + +size_t pgm_pkt_offset (bool, sa_family_t); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_SOCKET_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/source.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/source.h new file mode 100644 index 0000000..5349046 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/source.h @@ -0,0 +1,75 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM source socket. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_SOURCE_H__ +#define __PGM_IMPL_SOURCE_H__ + +#include +#include + +PGM_BEGIN_DECLS + +/* Performance Counters */ +enum { + PGM_PC_SOURCE_DATA_BYTES_SENT, + PGM_PC_SOURCE_DATA_MSGS_SENT, /* msgs = packets not APDUs */ +/* PGM_PC_SOURCE_BYTES_BUFFERED, */ /* tx window contents in bytes */ +/* PGM_PC_SOURCE_MSGS_BUFFERED, */ + PGM_PC_SOURCE_BYTES_SENT, +/* PGM_PC_SOURCE_RAW_NAKS_RECEIVED, */ + PGM_PC_SOURCE_CKSUM_ERRORS, + PGM_PC_SOURCE_MALFORMED_NAKS, + PGM_PC_SOURCE_PACKETS_DISCARDED, + PGM_PC_SOURCE_PARITY_BYTES_RETRANSMITTED, + PGM_PC_SOURCE_SELECTIVE_BYTES_RETRANSMITTED, + PGM_PC_SOURCE_PARITY_MSGS_RETRANSMITTED, + PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED, + PGM_PC_SOURCE_PARITY_NAK_PACKETS_RECEIVED, + PGM_PC_SOURCE_SELECTIVE_NAK_PACKETS_RECEIVED, /* total packets */ + PGM_PC_SOURCE_PARITY_NAKS_RECEIVED, + PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED, /* serial numbers */ + PGM_PC_SOURCE_PARITY_NAKS_IGNORED, + PGM_PC_SOURCE_SELECTIVE_NAKS_IGNORED, + PGM_PC_SOURCE_ACK_ERRORS, +/* PGM_PC_SOURCE_PGMCC_ACKER, */ + PGM_PC_SOURCE_TRANSMISSION_CURRENT_RATE, + PGM_PC_SOURCE_ACK_PACKETS_RECEIVED, + PGM_PC_SOURCE_PARITY_NNAK_PACKETS_RECEIVED, + PGM_PC_SOURCE_SELECTIVE_NNAK_PACKETS_RECEIVED, + PGM_PC_SOURCE_PARITY_NNAKS_RECEIVED, + PGM_PC_SOURCE_SELECTIVE_NNAKS_RECEIVED, + PGM_PC_SOURCE_NNAK_ERRORS, + +/* marker */ + PGM_PC_SOURCE_MAX +}; + +PGM_GNUC_INTERNAL bool pgm_send_spm (pgm_sock_t*const, const int) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_on_deferred_nak (pgm_sock_t*const); +PGM_GNUC_INTERNAL bool pgm_on_spmr (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_on_nak (pgm_sock_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_on_nnak (pgm_sock_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_on_ack (pgm_sock_t*const restrict, struct pgm_sk_buff_t*const restrict) PGM_GNUC_WARN_UNUSED_RESULT; + +PGM_END_DECLS + +#endif /* __PGM_IMPL_SOURCE_H__ */ + diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/sqn_list.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/sqn_list.h new file mode 100644 index 0000000..4d216ae --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/sqn_list.h @@ -0,0 +1,38 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM sequence list. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_SQN_LIST_H__ +#define __PGM_IMPL_SQN_LIST_H__ + +struct pgm_sqn_list_t; + +#include + +PGM_BEGIN_DECLS + +struct pgm_sqn_list_t { + uint8_t len; + uint32_t sqn[63]; /* list of sequence numbers */ +}; + +PGM_END_DECLS + +#endif /* __PGM_IMPL_SQN_LIST_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/string.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/string.h new file mode 100644 index 0000000..8357c2a --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/string.h @@ -0,0 +1,59 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable string manipulation functions. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_STRING_H__ +#define __PGM_IMPL_STRING_H__ + +typedef struct pgm_string_t pgm_string_t; + +#include +#include + +PGM_BEGIN_DECLS + +struct pgm_string_t { + char* str; + size_t len; + size_t allocated_len; +}; + +PGM_GNUC_INTERNAL char* pgm_strdup (const char*) PGM_GNUC_MALLOC; +PGM_GNUC_INTERNAL int pgm_printf_string_upper_bound (const char*, va_list) PGM_GNUC_PRINTF(1, 0); +PGM_GNUC_INTERNAL int pgm_vasprintf (char**restrict, char const*restrict, va_list args) PGM_GNUC_PRINTF(2, 0); +PGM_GNUC_INTERNAL char* pgm_strdup_vprintf (const char*, va_list) PGM_GNUC_PRINTF(1, 0) PGM_GNUC_MALLOC; +PGM_GNUC_INTERNAL char* pgm_strconcat (const char*, ...) PGM_GNUC_MALLOC PGM_GNUC_NULL_TERMINATED; +PGM_GNUC_INTERNAL char** pgm_strsplit (const char*restrict, const char*restrict, int) PGM_GNUC_MALLOC; +PGM_GNUC_INTERNAL void pgm_strfreev (char**); + +PGM_GNUC_INTERNAL pgm_string_t* pgm_string_new (const char*); +PGM_GNUC_INTERNAL char* pgm_string_free (pgm_string_t*, bool); +PGM_GNUC_INTERNAL void pgm_string_printf (pgm_string_t*restrict, const char*restrict, ...) PGM_GNUC_PRINTF(2, 3); +PGM_GNUC_INTERNAL pgm_string_t* pgm_string_append (pgm_string_t*restrict, const char*restrict); +PGM_GNUC_INTERNAL pgm_string_t* pgm_string_append_c (pgm_string_t*, char); +PGM_GNUC_INTERNAL void pgm_string_append_printf (pgm_string_t*restrict, const char*restrict, ...) PGM_GNUC_PRINTF(2, 3); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_STRING_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/thread.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/thread.h new file mode 100644 index 0000000..105e9ce --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/thread.h @@ -0,0 +1,211 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * mutexes and locks. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_THREAD_H__ +#define __PGM_IMPL_THREAD_H__ + +typedef struct pgm_mutex_t pgm_mutex_t; +typedef struct pgm_spinlock_t pgm_spinlock_t; +typedef struct pgm_cond_t pgm_cond_t; +typedef struct pgm_rwlock_t pgm_rwlock_t; + +#ifndef _WIN32 +# include +# include +#else +# define WIN32_LEAN_AND_MEAN +# include +#endif +#include + +PGM_BEGIN_DECLS + +struct pgm_mutex_t { +#ifndef _WIN32 + pthread_mutex_t pthread_mutex; +#else + HANDLE win32_mutex; +#endif /* !_WIN32 */ +}; + +struct pgm_spinlock_t { +#ifndef _WIN32 + pthread_spinlock_t pthread_spinlock; +#else + CRITICAL_SECTION win32_spinlock; +#endif +}; + +struct pgm_cond_t { +#ifndef _WIN32 + pthread_cond_t pthread_cond; +#elif defined(CONFIG_HAVE_WIN_COND) + CONDITION_VARIABLE win32_cond; +#else + CRITICAL_SECTION win32_spinlock; + size_t len; + size_t allocated_len; + HANDLE* phandle; +#endif /* !_WIN32 */ +}; + +struct pgm_rwlock_t { +#ifndef _WIN32 + pthread_rwlock_t pthread_rwlock; +#elif CONFIG_HAVE_WIN_SRW_LOCK + SRWLOCK win32_lock; + pthread_rwlock_t pthread_rwlock; +#else + CRITICAL_SECTION win32_spinlock; + pgm_cond_t read_cond; + pgm_cond_t write_cond; + unsigned read_counter; + bool have_writer; + unsigned want_to_read; + unsigned want_to_write; +#endif /* !CONFIG_HAVE_WIN_SRW_LOCK */ +}; + +PGM_GNUC_INTERNAL void pgm_mutex_init (pgm_mutex_t*); +PGM_GNUC_INTERNAL void pgm_mutex_free (pgm_mutex_t*); +PGM_GNUC_INTERNAL bool pgm_mutex_trylock (pgm_mutex_t*); + +static inline void pgm_mutex_lock (pgm_mutex_t* mutex) { +#ifndef _WIN32 + pthread_mutex_lock (&mutex->pthread_mutex); +#else + WaitForSingleObject (mutex->win32_mutex, INFINITE); +#endif /* !_WIN32 */ +} + +static inline void pgm_mutex_unlock (pgm_mutex_t* mutex) { +#ifndef _WIN32 + pthread_mutex_unlock (&mutex->pthread_mutex); +#else + ReleaseMutex (mutex->win32_mutex); +#endif /* !_WIN32 */ +} + +PGM_GNUC_INTERNAL void pgm_spinlock_init (pgm_spinlock_t*); +PGM_GNUC_INTERNAL void pgm_spinlock_free (pgm_spinlock_t*); +PGM_GNUC_INTERNAL bool pgm_spinlock_trylock (pgm_spinlock_t*); + +static inline void pgm_spinlock_lock (pgm_spinlock_t* spinlock) { +#ifndef _WIN32 + pthread_spin_lock (&spinlock->pthread_spinlock); +#else + EnterCriticalSection (&spinlock->win32_spinlock); +#endif /* !_WIN32 */ +} + +static inline void pgm_spinlock_unlock (pgm_spinlock_t* spinlock) { +#ifndef _WIN32 + pthread_spin_unlock (&spinlock->pthread_spinlock); +#else + LeaveCriticalSection (&spinlock->win32_spinlock); +#endif /* !_WIN32 */ +} + +PGM_GNUC_INTERNAL void pgm_cond_init (pgm_cond_t*); +PGM_GNUC_INTERNAL void pgm_cond_signal (pgm_cond_t*); +PGM_GNUC_INTERNAL void pgm_cond_broadcast (pgm_cond_t*); +#ifndef _WIN32 +PGM_GNUC_INTERNAL void pgm_cond_wait (pgm_cond_t*, pthread_mutex_t*); +#else +PGM_GNUC_INTERNAL void pgm_cond_wait (pgm_cond_t*, CRITICAL_SECTION*); +#endif +PGM_GNUC_INTERNAL void pgm_cond_free (pgm_cond_t*); + +#ifndef _WIN32 +static inline void pgm_rwlock_reader_lock (pgm_rwlock_t* rwlock) { + pthread_rwlock_rdlock (&rwlock->pthread_rwlock); +} +static inline bool pgm_rwlock_reader_trylock (pgm_rwlock_t* rwlock) { + return !pthread_rwlock_tryrdlock (&rwlock->pthread_rwlock); +} +static inline void pgm_rwlock_reader_unlock(pgm_rwlock_t* rwlock) { + pthread_rwlock_unlock (&rwlock->pthread_rwlock); +} +static inline void pgm_rwlock_writer_lock (pgm_rwlock_t* rwlock) { + pthread_rwlock_wrlock (&rwlock->pthread_rwlock); +} +static inline bool pgm_rwlock_writer_trylock (pgm_rwlock_t* rwlock) { + return !pthread_rwlock_trywrlock (&rwlock->pthread_rwlock); +} +static inline void pgm_rwlock_writer_unlock (pgm_rwlock_t* rwlock) { + pthread_rwlock_unlock (&rwlock->pthread_rwlock); +} +#elif defined(CONFIG_HAVE_WIN_SRW_LOCK) +static inline void pgm_rwlock_reader_lock (pgm_rwlock_t* rwlock) { + AcquireSRWLockShared (&rwlock->win32_lock); +} +static inline bool pgm_rwlock_reader_trylock (pgm_rwlock_t* rwlock) { + return TryAcquireSRWLockShared (&rwlock->win32_lock); +} +static inline void pgm_rwlock_reader_unlock(pgm_rwlock_t* rwlock) { + ReleaseSRWLockShared (&rwlock->win32_lock); +} +static inline void pgm_rwlock_writer_lock (pgm_rwlock_t* rwlock) { + AcquireSRWLockExclusive (&rwlock->win32_lock); +} +static inline bool pgm_rwlock_writer_trylock (pgm_rwlock_t* rwlock) { + return AcquireSRWLockExclusive (&rwlock->win32_lock); +} +static inline void pgm_rwlock_writer_unlock (pgm_rwlock_t* rwlock) { + ReleaseSRWLockExclusive (&rwlock->win32_lock); +} +#else +PGM_GNUC_INTERNAL void pgm_rwlock_reader_lock (pgm_rwlock_t*); +PGM_GNUC_INTERNAL bool pgm_rwlock_reader_trylock (pgm_rwlock_t*); +PGM_GNUC_INTERNAL void pgm_rwlock_reader_unlock(pgm_rwlock_t*); +PGM_GNUC_INTERNAL void pgm_rwlock_writer_lock (pgm_rwlock_t*); +PGM_GNUC_INTERNAL bool pgm_rwlock_writer_trylock (pgm_rwlock_t*); +PGM_GNUC_INTERNAL void pgm_rwlock_writer_unlock (pgm_rwlock_t*); +#endif + +PGM_GNUC_INTERNAL void pgm_rwlock_init (pgm_rwlock_t*); +PGM_GNUC_INTERNAL void pgm_rwlock_free (pgm_rwlock_t*); + +PGM_GNUC_INTERNAL void pgm_thread_init (void); +PGM_GNUC_INTERNAL void pgm_thread_shutdown (void); + +static inline +void +pgm_thread_yield (void) +{ +#ifndef _WIN32 +# ifdef _POSIX_PRIORITY_SCHEDULING + sched_yield(); +# else + pthread_yield(); /* requires _GNU */ +# endif +#else + Sleep (0); /* If you specify 0 milliseconds, the thread will relinquish + * the remainder of its time slice but remain ready. + */ +#endif /* _WIN32 */ +} + +PGM_END_DECLS + +#endif /* __PGM_IMPL_THREAD_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/time.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/time.h new file mode 100644 index 0000000..70c0d37 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/time.h @@ -0,0 +1,51 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * high resolution timers. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_TIME_H__ +#define __PGM_IMPL_TIME_H__ + +#include +#include +#include + +PGM_BEGIN_DECLS + +typedef pgm_time_t (*pgm_time_update_func)(void); + +#define pgm_time_after(a,b) ( (a) > (b) ) +#define pgm_time_before(a,b) ( pgm_time_after((b),(a)) ) + +#define pgm_time_after_eq(a,b) ( (a) >= (b) ) +#define pgm_time_before_eq(a,b) ( pgm_time_after_eq((b),(a)) ) + +extern pgm_time_update_func pgm_time_update_now; + +PGM_GNUC_INTERNAL bool pgm_time_init (pgm_error_t**) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_time_shutdown (void); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_TIME_H__ */ + diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/timer.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/timer.h new file mode 100644 index 0000000..4f900e4 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/timer.h @@ -0,0 +1,58 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM timer thread. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_TIMER_H__ +#define __PGM_IMPL_TIMER_H__ + +#include +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL bool pgm_timer_prepare (pgm_sock_t*const); +PGM_GNUC_INTERNAL bool pgm_timer_check (pgm_sock_t*const); +PGM_GNUC_INTERNAL pgm_time_t pgm_timer_expiration (pgm_sock_t*const); +PGM_GNUC_INTERNAL bool pgm_timer_dispatch (pgm_sock_t*const); + +static inline +void +pgm_timer_lock ( + pgm_sock_t* const sock + ) +{ + if (sock->can_send_data) + pgm_mutex_lock (&sock->timer_mutex); +} + +static inline +void +pgm_timer_unlock ( + pgm_sock_t* const sock + ) +{ + if (sock->can_send_data) + pgm_mutex_unlock (&sock->timer_mutex); +} + +PGM_END_DECLS + +#endif /* __PGM_IMPL_TIMER_H__ */ + diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/tsi.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/tsi.h new file mode 100644 index 0000000..be93e62 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/tsi.h @@ -0,0 +1,39 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * transport session ID helper functions + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_TSI_H__ +#define __PGM_IMPL_TSI_H__ + +#include +#include +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_INTERNAL pgm_hash_t pgm_tsi_hash (const void*) PGM_GNUC_WARN_UNUSED_RESULT; + +PGM_END_DECLS + +#endif /* __PGM_IMPL_TSI_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/txw.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/txw.h new file mode 100644 index 0000000..650cd6a --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/txw.h @@ -0,0 +1,207 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * basic transmit window. + * + * Copyright (c) 2006 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IMPL_TXW_H__ +#define __PGM_IMPL_TXW_H__ + +typedef struct pgm_txw_state_t pgm_txw_state_t; +typedef struct pgm_txw_t pgm_txw_t; + +#include + +PGM_BEGIN_DECLS + +/* must be smaller than PGM skbuff control buffer */ +struct pgm_txw_state_t { + uint32_t unfolded_checksum; /* first 32-bit word must be checksum */ + + unsigned waiting_retransmit:1; /* in retransmit queue */ + unsigned retransmit_count:15; + unsigned nak_elimination_count:16; + + uint8_t pkt_cnt_requested; /* # parity packets to send */ + uint8_t pkt_cnt_sent; /* # parity packets already sent */ +}; + +struct pgm_txw_t { + const pgm_tsi_t* restrict tsi; + +/* option: lockless atomics */ + volatile uint32_t lead; + volatile uint32_t trail; + + pgm_queue_t retransmit_queue; + + pgm_rs_t rs; + uint8_t tg_sqn_shift; + struct pgm_sk_buff_t* restrict parity_buffer; + +/* Advance with data */ + pgm_time_t adv_ivl_expiry; + unsigned increment_window_naks; + unsigned adv_secs; /* TXW_ADV_SECS */ + unsigned adv_sqns; /* TXW_ADV_SECS in sequences */ + + unsigned is_fec_enabled:1; + unsigned adv_mode:1; /* 0 = advance by time, 1 = advance by data */ + + size_t size; /* window content size in bytes */ + unsigned alloc; /* length of pdata[] */ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +/* C99 flexible array, sizeof() invalid */ + struct pgm_sk_buff_t* pdata[]; +#elif !defined(__STDC_VERSION__) || defined(__cplusplus) +/* C90 and older */ + struct pgm_sk_buff_t* pdata[1]; +#else +/* GNU C variable-length object */ + struct pgm_sk_buff_t* pdata[0]; +#endif +}; + +PGM_GNUC_INTERNAL pgm_txw_t* pgm_txw_create (const pgm_tsi_t*const, const uint16_t, const uint32_t, const unsigned, const ssize_t, const bool, const uint8_t, const uint8_t) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_txw_shutdown (pgm_txw_t*const); +PGM_GNUC_INTERNAL void pgm_txw_add (pgm_txw_t*const restrict, struct pgm_sk_buff_t*const restrict); +PGM_GNUC_INTERNAL struct pgm_sk_buff_t* pgm_txw_peek (const pgm_txw_t*const, const uint32_t) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL bool pgm_txw_retransmit_push (pgm_txw_t*const, const uint32_t, const bool, const uint8_t) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL struct pgm_sk_buff_t* pgm_txw_retransmit_try_peek (pgm_txw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +PGM_GNUC_INTERNAL void pgm_txw_retransmit_remove_head (pgm_txw_t*const); +PGM_GNUC_INTERNAL uint32_t pgm_txw_get_unfolded_checksum (const struct pgm_sk_buff_t*const) PGM_GNUC_PURE; +PGM_GNUC_INTERNAL void pgm_txw_set_unfolded_checksum (struct pgm_sk_buff_t*const, const uint32_t); +PGM_GNUC_INTERNAL void pgm_txw_inc_retransmit_count (struct pgm_sk_buff_t*const); +PGM_GNUC_INTERNAL bool pgm_txw_retransmit_is_empty (const pgm_txw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; + +/* declare for GCC attributes */ +static inline size_t pgm_txw_max_length (const pgm_txw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline uint32_t pgm_txw_length (const pgm_txw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline size_t pgm_txw_size (const pgm_txw_t*const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline bool pgm_txw_is_empty (const pgm_txw_t* const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline bool pgm_txw_is_full (const pgm_txw_t* const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline uint32_t pgm_txw_lead (const pgm_txw_t* const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline uint32_t pgm_txw_lead_atomic (const pgm_txw_t* const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline uint32_t pgm_txw_next_lead (const pgm_txw_t* const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline uint32_t pgm_txw_trail (const pgm_txw_t* const) PGM_GNUC_WARN_UNUSED_RESULT; +static inline uint32_t pgm_txw_trail_atomic (const pgm_txw_t* const) PGM_GNUC_WARN_UNUSED_RESULT; + +static inline +size_t +pgm_txw_max_length ( + const pgm_txw_t*const window + ) +{ + pgm_assert (NULL != window); + return window->alloc; +} + +static inline +uint32_t +pgm_txw_length ( + const pgm_txw_t*const window + ) +{ + pgm_assert (NULL != window); + return ( 1 + window->lead ) - window->trail; +} + +static inline +size_t +pgm_txw_size ( + const pgm_txw_t*const window + ) +{ + pgm_assert (NULL != window); + return window->size; +} + +static inline +bool +pgm_txw_is_empty ( + const pgm_txw_t*const window + ) +{ + pgm_assert (NULL != window); + return (0 == pgm_txw_length (window)); +} + +static inline +bool +pgm_txw_is_full ( + const pgm_txw_t*const window + ) +{ + pgm_assert (NULL != window); + return (pgm_txw_length (window) == pgm_txw_max_length (window)); +} + +static inline +uint32_t +pgm_txw_lead ( + const pgm_txw_t*const window + ) +{ + pgm_assert (NULL != window); + return window->lead; +} + +/* atomics may rely on global variables and so cannot be defined __pure__ */ +static inline +uint32_t +pgm_txw_lead_atomic ( + const pgm_txw_t*const window + ) +{ + pgm_assert (NULL != window); + return pgm_atomic_read32 (&window->lead); +} + +static inline +uint32_t +pgm_txw_next_lead ( + const pgm_txw_t*const window + ) +{ + pgm_assert (NULL != window); + return (uint32_t)(pgm_txw_lead (window) + 1); +} + +static inline +uint32_t +pgm_txw_trail ( + const pgm_txw_t*const window + ) +{ + pgm_assert (NULL != window); + return window->trail; +} + +static inline +uint32_t +pgm_txw_trail_atomic ( + const pgm_txw_t*const window + ) +{ + pgm_assert (NULL != window); + return pgm_atomic_read32 (&window->trail); +} + +PGM_END_DECLS + +#endif /* __PGM_IMPL_TXW_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/impl/wsastrerror.h b/3rdparty/openpgm-svn-r1135/pgm/include/impl/wsastrerror.h new file mode 100644 index 0000000..1be4ef2 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/impl/wsastrerror.h @@ -0,0 +1,37 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Winsock Error strings. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined (__PGM_IMPL_FRAMEWORK_H_INSIDE__) && !defined (PGM_COMPILATION) +# error "Only can be included directly." +#endif + +#ifndef __PGM_IMPL_WSASTRERROR_H__ +#define __PGM_IMPL_WSASTRERROR_H__ + +#include + +PGM_BEGIN_DECLS + +char* pgm_wsastrerror (const int); +char* pgm_adapter_strerror (const int); +char* pgm_win_strerror (char*, size_t, const int); + +PGM_END_DECLS + +#endif /* __PGM_IMPL_WSASTRERROR_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/atomic.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/atomic.h new file mode 100644 index 0000000..60349f7 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/atomic.h @@ -0,0 +1,140 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * 32-bit atomic operations. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_ATOMIC_H__ +#define __PGM_ATOMIC_H__ + +#ifdef sun +# include +#endif +#include + +static inline +uint32_t +pgm_atomic_exchange_and_add32 ( + volatile uint32_t* atomic, + const uint32_t val + ) +{ +#if defined( __GNUC__ ) && ( defined( __i386__ ) || defined( __x86_64__ ) ) + uint32_t result; + asm volatile ( "lock\n\t" + "xaddl %0, %1" + : "=r" (result), "=m" (*atomic) + : "0" (val), "m" (*atomic) + : "memory", "cc" ); + return result; +#elif (defined( __SUNPRO_C ) || defined( __SUNPRO_CC )) && (defined( __i386 ) || defined( __amd64 )) + uint32_t result = val; + asm volatile ( "lock\n\t" + "xaddl %0, %1" + :: "r" (result), "m" (*atomic) ); + return result; +#elif defined( sun ) + const uint32_t nv = atomic_add_32_nv (atomic, (int32_t)val); + return nv - val; +#elif defined( __GNUC__ ) && ( __GNUC__ * 100 + __GNUC_MINOR__ >= 401 ) + return __sync_fetch_and_add (atomic, val); +#elif defined( _WIN32 ) + return InterlockedExchangeAdd ((volatile LONG*)atomic, val); +#else +# error "No supported atomic operations for this platform." +#endif +} + +static inline +void +pgm_atomic_add32 ( + volatile uint32_t* atomic, + const uint32_t val + ) +{ +#if defined( __GNUC__ ) && ( defined( __i386__ ) || defined( __x86_64__ ) ) + asm volatile ( "lock\n\t" + "addl %1, %0" + : "=m" (*atomic) + : "ir" (val), "m" (*atomic) + : "memory", "cc" ); +#elif (defined( __SUNPRO_C ) || defined( __SUNPRO_CC )) && (defined( __i386 ) || defined( __amd64 )) + asm volatile ( "lock\n\t" + "addl %1, %0" + :: "r" (val), "m" (*atomic) ); +#elif defined( sun ) + atomic_add_32 (atomic, (int32_t)val); +#elif defined( __GNUC__ ) && ( __GNUC__ * 100 + __GNUC_MINOR__ >= 401 ) + __sync_fetch_and_add (atomic, val); +#elif defined( _WIN32 ) + InterlockedExchangeAdd ((volatile LONG*)atomic, val); +#endif +} + +static inline +void +pgm_atomic_inc32 ( + volatile uint32_t* atomic + ) +{ +#if (defined( __GNUC__ ) && (defined( __i386__ ) || defined( __x86_64__ ))) || ((defined( __SUNPRO_C ) || defined( __SUNPRO_CC )) && (defined( __i386 ) || defined( __amd64 ))) + pgm_atomic_add32 (atomic, 1); +#elif defined( sun ) + atomic_inc_32 (atomic); +#elif defined( __GNUC__ ) && ( __GNUC__ * 100 + __GNUC_MINOR__ >= 401 ) + pgm_atomic_add32 (atomic, 1); +#elif defined( _WIN32 ) + InterlockedIncrement ((volatile LONG*)atomic); +#endif +} + +static inline +void +pgm_atomic_dec32 ( + volatile uint32_t* atomic + ) +{ +#if (defined( __GNUC__ ) && (defined( __i386__ ) || defined( __x86_64__ ))) || ((defined( __SUNPRO_C ) || defined( __SUNPRO_CC )) && (defined( __i386 ) || defined( __amd64 ))) + pgm_atomic_add32 (atomic, (uint32_t)-1); +#elif defined( sun ) + atomic_dec_32 (atomic); +#elif defined( __GNUC__ ) && ( __GNUC__ * 100 + __GNUC_MINOR__ >= 401 ) + pgm_atomic_add32 (atomic, (uint32_t)-1); +#elif defined( _WIN32 ) + InterlockedDecrement ((volatile LONG*)atomic); +#endif +} + +static inline +uint32_t +pgm_atomic_read32 ( + const volatile uint32_t* atomic + ) +{ + return *atomic; +} + +static inline +void +pgm_atomic_write32 ( + volatile uint32_t* atomic, + const uint32_t val + ) +{ + *atomic = val; +} + +#endif /* __PGM_ATOMIC_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/backtrace.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/backtrace.h new file mode 100644 index 0000000..24f8469 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/backtrace.h @@ -0,0 +1,33 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Dump back trace to stderr and try gdb. + * + * Copyright (c) 2006 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_BACKTRACE_H__ +#define __PGM_BACKTRACE_H__ + +#include + +PGM_BEGIN_DECLS + +PGM_GNUC_NORETURN void on_sigsegv (int); + +PGM_END_DECLS + +#endif /* __PGM_BACKTRACE_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/engine.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/engine.h new file mode 100644 index 0000000..43115e8 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/engine.h @@ -0,0 +1,37 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM engine. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_ENGINE_H__ +#define __PGM_ENGINE_H__ + +#include +#include + +PGM_BEGIN_DECLS + +bool pgm_init (pgm_error_t**); +bool pgm_supported (void) PGM_GNUC_WARN_UNUSED_RESULT PGM_GNUC_PURE; +bool pgm_shutdown (void); +void pgm_drop_superuser (void); + +PGM_END_DECLS + +#endif /* __PGM_ENGINE_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/error.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/error.h new file mode 100644 index 0000000..3d77290 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/error.h @@ -0,0 +1,109 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable error reporting. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_ERROR_H__ +#define __PGM_ERROR_H__ + +typedef struct pgm_error_t pgm_error_t; + +#include + +PGM_BEGIN_DECLS + +/* error domains */ +enum +{ + PGM_ERROR_DOMAIN_IF, /* interface and host */ + PGM_ERROR_DOMAIN_PACKET, + PGM_ERROR_DOMAIN_RECV, + PGM_ERROR_DOMAIN_TIME, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_DOMAIN_ENGINE, + PGM_ERROR_DOMAIN_HTTP, + PGM_ERROR_DOMAIN_SNMP +}; + +/* error codes */ +enum +{ + /* Derived from errno, eai_errno, etc */ + PGM_ERROR_ADDRFAMILY, /* EAI_ADDRFAMILY */ + PGM_ERROR_AFNOSUPPORT, /* EAI_FAMILY */ + PGM_ERROR_AGAIN, + PGM_ERROR_BADE, /* ERROR_INVALID_DATA */ + PGM_ERROR_BADF, + PGM_ERROR_BOUNDS, /* sequence out-of-bounds */ + PGM_ERROR_CKSUM, /* pkt cksum incorrect */ + PGM_ERROR_CONNRESET, + PGM_ERROR_FAIL, /* EAI_FAIL */ + PGM_ERROR_FAULT, + PGM_ERROR_INPROGRESS, /* WSAEINPROGRESS */ + PGM_ERROR_INTR, + PGM_ERROR_INVAL, + PGM_ERROR_MFILE, + PGM_ERROR_NFILE, + PGM_ERROR_NOBUFS, /* ERROR_BUFFER_OVERFLOW */ + PGM_ERROR_NODATA, /* EAI_NODATA */ + PGM_ERROR_NODEV, + PGM_ERROR_NOENT, + PGM_ERROR_NOMEM, + PGM_ERROR_NONAME, /* EAI_NONAME */ + PGM_ERROR_NONET, + PGM_ERROR_NOPROTOOPT, + PGM_ERROR_NOSYS, /* ERROR_NOT_SUPPORTED */ + PGM_ERROR_NOTUNIQ, + PGM_ERROR_NXIO, + PGM_ERROR_PERM, + PGM_ERROR_PROCLIM, /* WSAEPROCLIM */ + PGM_ERROR_PROTO, + PGM_ERROR_RANGE, + PGM_ERROR_SERVICE, /* EAI_SERVICE */ + PGM_ERROR_SOCKTNOSUPPORT, /* EAI_SOCKTYPE */ + PGM_ERROR_SYSNOTAREADY, /* WSASYSNOTAREADY */ + PGM_ERROR_SYSTEM, /* EAI_SYSTEM */ + PGM_ERROR_VERNOTSUPPORTED, /* WSAVERNOTSUPPORTED */ + PGM_ERROR_XDEV, + + PGM_ERROR_FAILED /* generic error */ +}; + +struct pgm_error_t +{ + int domain; + int code; + char* message; +}; + +void pgm_error_free (pgm_error_t*); +void pgm_set_error (pgm_error_t**restrict, const int, const int, const char*restrict, ...) PGM_GNUC_PRINTF (4, 5); +void pgm_propagate_error (pgm_error_t**restrict, pgm_error_t*restrict); +void pgm_clear_error (pgm_error_t**); +void pgm_prefix_error (pgm_error_t**restrict, const char*restrict, ...) PGM_GNUC_PRINTF (2, 3); + +int pgm_error_from_errno (const int) PGM_GNUC_CONST; +int pgm_error_from_h_errno (const int) PGM_GNUC_CONST; +int pgm_error_from_eai_errno (const int, const int) PGM_GNUC_CONST; +int pgm_error_from_wsa_errno (const int) PGM_GNUC_CONST; +int pgm_error_from_win_errno (const int) PGM_GNUC_CONST; + +PGM_END_DECLS + +#endif /* __PGM_ERROR_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/gsi.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/gsi.h new file mode 100644 index 0000000..86d2921 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/gsi.h @@ -0,0 +1,51 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * global session ID helper functions + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_GSI_H__ +#define __PGM_GSI_H__ + +typedef struct pgm_gsi_t pgm_gsi_t; + +#include +#include + +PGM_BEGIN_DECLS + +#define PGM_GSISTRLEN (sizeof("000.000.000.000.000.000")) +#define PGM_GSI_INIT {{ 0, 0, 0, 0, 0, 0 }} + +struct pgm_gsi_t { + uint8_t identifier[6]; +}; + +PGM_STATIC_ASSERT(sizeof(struct pgm_gsi_t) == 6); + +bool pgm_gsi_create_from_hostname (pgm_gsi_t*restrict, pgm_error_t**restrict); +bool pgm_gsi_create_from_addr (pgm_gsi_t*restrict, pgm_error_t**restrict); +bool pgm_gsi_create_from_data (pgm_gsi_t*restrict, const uint8_t*restrict, const size_t); +bool pgm_gsi_create_from_string (pgm_gsi_t*restrict, const char*restrict, ssize_t); +int pgm_gsi_print_r (const pgm_gsi_t*restrict, char*restrict, const size_t); +char* pgm_gsi_print (const pgm_gsi_t*); +bool pgm_gsi_equal (const void*restrict, const void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; + +PGM_END_DECLS + +#endif /* __PGM_GSI_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/http.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/http.h new file mode 100644 index 0000000..5c65d15 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/http.h @@ -0,0 +1,37 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * HTTP administrative interface + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_HTTP_H__ +#define __PGM_HTTP_H__ + +#include +#include + +PGM_BEGIN_DECLS + +#define PGM_HTTP_DEFAULT_SERVER_PORT 4968 + +bool pgm_http_init (uint16_t, pgm_error_t**) PGM_GNUC_WARN_UNUSED_RESULT; +bool pgm_http_shutdown (void); + +PGM_END_DECLS + +#endif /* __PGM_HTTP_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/if.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/if.h new file mode 100644 index 0000000..814d235 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/if.h @@ -0,0 +1,33 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * network interface handling. + * + * Copyright (c) 2006 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IF_H__ +#define __PGM_IF_H__ + +#include + +PGM_BEGIN_DECLS + +void pgm_if_print_all (void); + +PGM_END_DECLS + +#endif /* __PGM_IF_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/ip/pgm.hh b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/ip/pgm.hh new file mode 100644 index 0000000..a5e260f --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/ip/pgm.hh @@ -0,0 +1,100 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM protocol + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IP_PGM_HH__ +#define __PGM_IP_PGM_HH__ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +namespace pgm { +#define restrict +#include +} + +namespace ip { + +class pgm +{ +public: + /// The type of a PGM endpoint. + typedef pgm_endpoint endpoint; + + /// Construct to represent PGM over IPv4. + static pgm v4() + { + return pgm (PF_INET); + } + + /// Construct to represent PGM over IPv6. + static pgm v6() + { + return pgm (PF_INET6); + } + + /// Obtain an identifier for the type of the protocol. + int type() const + { + return SOCK_SEQPACKET; + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return IPPROTO_PGM; + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return family_; + } + + /// The PGM socket type. + typedef pgm_socket socket; + + /// Compare two protocols for equality. + friend bool operator== (const pgm& p1, const pgm& p2) + { + return p1.family_ == p2.family_; + } + + /// Compare two protocols for inequality. + friend bool operator!= (const pgm& p1, const pgm& p2) + { + return p1.family_ != p2.family_; + } + +private: + // Construct with a specific family. + explicit pgm (int family) + : family_ (family) + { + } + + int family_; +}; + +} // namespace ip + + +#endif /* __PGM_IP_PGM_HH__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/ip/pgm_endpoint.hh b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/ip/pgm_endpoint.hh new file mode 100644 index 0000000..1114719 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/ip/pgm_endpoint.hh @@ -0,0 +1,171 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM endpoint + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_IP_PGM_ENDPOINT_HH__ +#define __PGM_IP_PGM_ENDPOINT_HH__ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +namespace pgm { +#define restrict +#include +} + +namespace ip { + +template +class pgm_endpoint +{ +public: + /// The protocol type associated with the endpoint. + typedef InternetProtocol protocol_type; + + typedef struct cpgm::pgm_sockaddr_t data_type; + + /// Default constructor. + pgm_endpoint() + : data_() + { + data_.sa_port = 0; + cpgm::pgm_tsi_t tmp_addr = PGM_TSI_INIT; + data_.sa_addr = tmp_addr; + } + + /// Construct an endpoint using a port number, specified in host byte + /// order. The GSI will be generated from the node name. + /** + * @par examples + * To initialise a PGM endpoint for port 7500, use: + * @code + * ip::pgm::endpoint ep (7500); + * @endcode + */ + pgm_endpoint (unsigned short port_num) + : data_() + { + data_.sa_port = port_num; + data_.sa_addr.sport = 0; + pgm_gsi_create_from_hostname (&data_.sa_addr.gsi, NULL); + } + + /// Construct an endpoint using a port number and a TSI. + pgm_endpoint (const cpgm::pgm_tsi_t& tsi, unsigned short port_num) + : data_() + { + data_.sa_port = port_num; + data_.sa_addr = tsi; + } + + /// Construct an endpoint using a port number and a memory area. + pgm_endpoint (const void* src, std::size_t len, unsigned short port_num) + : data_() + { + data_.sa_port = port_num; + data_.sa_addr.sport = 0; + pgm_gsi_create_from_data (&data_.sa_addr.gsi, src, len); + } + + /// Copy constructor. + pgm_endpoint (const pgm_endpoint& other) + : data_ (other.data_) + { + } + + /// Assign from another endpoint. + pgm_endpoint& operator= (const pgm_endpoint& other) + { + data_ = other.data_; + return *this; + } + + /// Get the underlying endpoint in the native type. + const data_type* data() const + { + return &data_; + } + + /// Get the underlying size of the endpoint in the native type. + std::size_t size() const + { + return sizeof(data_type); + } + + /// Get the port associated with the endpoint. The port number is always in + /// the host's byte order. + unsigned short port() const + { + return data_.sa_port; + } + + /// Set the port associated with the endpoint. The port number is always in + /// the host's byte order. + void port (unsigned short port_num) + { + data_.sa_port = port_num; + } + + /// Get the TSI associated with the endpoint. + const cpgm::pgm_tsi_t* address() const + { + return &data_.sa_addr; + } + + /// Set the TSI associated with the endpoint. + void address (cpgm::pgm_tsi_t& addr) + { + data_.sa_addr = addr; + } + + /// Compare two endpoints for equality. + friend bool operator== (const pgm_endpoint& e1, + const pgm_endpoint& e2) + { + return e1.address() == e2.address() && e1.port() == e2.port(); + } + + /// Compare two endpoints for inequality. + friend bool operator!= (const pgm_endpoint& e1, + const pgm_endpoint& e2) + { + return e1.address() != e2.address() || e1.port() != e2.port(); + } + + /// Compare endpoints for ordering. + friend bool operator<(const pgm_endpoint& e1, + const pgm_endpoint& e2) + { + if (e1.address() < e2.address()) + return true; + if (e1.address() != e2.address()) + return false; + return e1.port() < e2.port(); + } + +private: + // The underlying PGM socket address. + data_type data_; +}; + +} // namespace ip + +#endif /* __PGM_IP_PGM_ENDPOINT_HH__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/list.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/list.h new file mode 100644 index 0000000..b91abc9 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/list.h @@ -0,0 +1,40 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable doubly-linked list. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_LIST_H__ +#define __PGM_LIST_H__ + +typedef struct pgm_list_t pgm_list_t; + +#include + +PGM_BEGIN_DECLS + +struct pgm_list_t +{ + void* data; + struct pgm_list_t* next; + struct pgm_list_t* prev; +}; + +PGM_END_DECLS + +#endif /* __PGM_LIST_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/log.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/log.h new file mode 100644 index 0000000..2b6c036 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/log.h @@ -0,0 +1,33 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * basic logging. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_LOG_H__ +#define __PGM_LOG_H__ + +#include + +PGM_BEGIN_DECLS + +bool log_init (void); + +PGM_END_DECLS + +#endif /* __PGM_LOG_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/macros.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/macros.h new file mode 100644 index 0000000..da7e4d7 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/macros.h @@ -0,0 +1,171 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Pre-processor macros for cross-platform, cross-compiler ice cream. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_MACROS_H__ +#define __PGM_MACROS_H__ + +/* NULL, ptrdiff_t, and size_t + */ + +#include + + +/* GCC function attributes + * http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html + */ + +#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96) + +/* No side-effects except return value, may follow pointers and read globals */ +# define PGM_GNUC_PURE __attribute__((__pure__)) + +/* Returns new memory like malloc() */ +# define PGM_GNUC_MALLOC __attribute__((__malloc__)) + +# define PGM_GNUC_CACHELINE_ALIGNED __attribute__((__aligned__(SMP_CACHE_BYTES), \ + __section__((".data.cacheline_aligned"))) +# define PGM_GNUC_READ_MOSTLY __attribute__((__section__(".data.read_mostly"))) + +#else +# define PGM_GNUC_PURE +# define PGM_GNUC_MALLOC +# define PGM_GNUC_CACHELINE_ALIGNED +# define PGM_GNUC_READ_MOSTLY +#endif + +#if (__GNUC__ >= 4) + +/* Variable argument function with NULL terminated list */ +# define PGM_GNUC_NULL_TERMINATED __attribute__((__sentinel__)) +#else +# define PGM_GNUC_NULL_TERMINATED +#endif + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) + +/* malloc() with xth parameter being size */ +# define PGM_GNUC_ALLOC_SIZE(x) __attribute__((__alloc_size__(x))) + +/* malloc() with xth*yth parameters being size */ +# define PGM_GNUC_ALLOC_SIZE2(x,y) __attribute__((__alloc_size__(x,y))) +#else +# define PGM_GNUC_ALLOC_SIZE(x) +# define PGM_GNUC_ALLOC_SIZE2(x,y) +#endif + +#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) + +/* printf() like function */ +# define PGM_GNUC_PRINTF(format, args) __attribute__((__format__ (__printf__, format, args))) +# define PGM_GNUC_FORMAT(format) __attribute__((__format_arg__ (format))) + +/* Function will never return */ +# define PGM_GNUC_NORETURN __attribute__((__noreturn__)) + +/* No side-effects except return value, must not follow pointers or read globals */ +# define PGM_GNUC_CONST __attribute__((__const__)) + +/* Unused function */ +# define PGM_GNUC_UNUSED __attribute__((__unused__)) + +#else /* !__GNUC__ */ +# define PGM_GNUC_PRINTF(format, args) +# define PGM_GNUC_NORETURN +# define PGM_GNUC_CONST +# define PGM_GNUC_UNUSED +#endif /* !__GNUC__ */ + +#if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) + +/* Raise compiler warning if caller ignores return value */ +# define PGM_GNUC_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) + +# ifdef CONFIG_HAVE_DSO_VISIBILITY +/* Hidden visibility */ +# define PGM_GNUC_INTERNAL __attribute__((visibility("hidden"))) +# else +# define PGM_GNUC_INTERNAL +# endif +#else /* !__GNUC__ */ +# define PGM_GNUC_WARN_UNUSED_RESULT +# if ((defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550)) || (defined(__SUNPRO_CC) && (__SUNPRO_CC >= 0x550))) && defined(CONFIG_HAVE_DSO_VISIBILITY) +# define PGM_GNUC_INTERNAL __hidden +# else +# define PGM_GNUC_INTERNAL +# endif +#endif /* __GNUC__ */ + + +/* Compiler time assertions, must be on unique lines in the project */ +#define PGM_PASTE_ARGS(identifier1,identifier2) identifier1 ## identifier2 +#define PGM_PASTE(identifier1,identifier2) PGM_PASTE_ARGS (identifier1, identifier2) +#define PGM_STATIC_ASSERT(expr) typedef struct { char compile_time_assertion[(expr) ? 1 : -1]; } PGM_PASTE (_pgm_static_assert_, __LINE__) + +/* Function declaration wrappers for C++ */ +#ifdef __cplusplus +# define PGM_BEGIN_DECLS extern "C" { +# define PGM_END_DECLS } +#else +# define PGM_BEGIN_DECLS +# define PGM_END_DECLS +#endif + +/* Surprisingly still not defined in C99 */ +#ifndef FALSE +# define FALSE (0) +#endif + +#ifndef TRUE +# define TRUE (!FALSE) +#endif + +#undef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#undef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +#undef ABS +#define ABS(a) (((a) < 0) ? -(a) : (a)) + +#undef CLAMP +#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) + +/* Number of elements */ +#define PGM_N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0])) + +/* Structure offsets */ +#if defined(__GNUC__) && __GNUC__ >= 4 +# define PGM_OFFSETOF(struct_type, member) (offsetof (struct_type, member)) +#else +# define PGM_OFFSETOF(struct_type, member) ((size_t)((char*)&((struct_type*) 0)->member)) +#endif + +/* Branch prediction hint */ +#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) +# define PGM_LIKELY(expr) __builtin_expect ((expr), 1) +# define PGM_UNLIKELY(expr) __builtin_expect ((expr), 0) +#else +# define PGM_LIKELY(expr) (expr) +# define PGM_UNLIKELY(expr) (expr) +#endif + +#endif /* __PGM_MACROS_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/mem.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/mem.h new file mode 100644 index 0000000..eea31fe --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/mem.h @@ -0,0 +1,60 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable fail fast memory allocation. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_MEM_H__ +#define __PGM_MEM_H__ + +#ifdef CONFIG_HAVE_ALLOCA_H +# include +#elif defined(_WIN32) +# include +#else +# include +#endif +#include + +PGM_BEGIN_DECLS + +extern bool pgm_mem_gc_friendly; + +void* pgm_malloc (const size_t) PGM_GNUC_MALLOC PGM_GNUC_ALLOC_SIZE(1); +void* pgm_malloc_n (const size_t, const size_t) PGM_GNUC_MALLOC PGM_GNUC_ALLOC_SIZE2(1, 2); +void* pgm_malloc0 (const size_t) PGM_GNUC_MALLOC PGM_GNUC_ALLOC_SIZE(1); +void* pgm_malloc0_n (const size_t, const size_t) PGM_GNUC_MALLOC PGM_GNUC_ALLOC_SIZE2(1, 2); +void* pgm_memdup (const void*, const size_t) PGM_GNUC_MALLOC; +void* pgm_realloc (void*, const size_t) PGM_GNUC_WARN_UNUSED_RESULT; +void pgm_free (void*); + +/* Convenience memory allocators that wont work well above 32-bit sizes + */ +#define pgm_new(struct_type, n_structs) \ + ((struct_type*)pgm_malloc_n ((size_t)sizeof(struct_type), (size_t)(n_structs))) +#define pgm_new0(struct_type, n_structs) \ + ((struct_type*)pgm_malloc0_n ((size_t)sizeof(struct_type), (size_t)(n_structs))) + +#define pgm_alloca(size) \ + alloca (size) +#define pgm_newa(struct_type, n_structs) \ + ((struct_type*) pgm_alloca (sizeof(struct_type) * (size_t)(n_structs))) + +PGM_END_DECLS + +#endif /* __PGM_MEM_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/messages.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/messages.h new file mode 100644 index 0000000..6a00b8f --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/messages.h @@ -0,0 +1,66 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * basic message reporting. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_MESSAGES_H__ +#define __PGM_MESSAGES_H__ + +#include + +PGM_BEGIN_DECLS + +/* Set bitmask of log roles in environmental variable PGM_LOG_MASK, + * borrowed from SmartPGM. + */ +enum { + PGM_LOG_ROLE_MEMORY = 0x001, + PGM_LOG_ROLE_NETWORK = 0x002, + PGM_LOG_ROLE_CONFIGURATION = 0x004, + PGM_LOG_ROLE_SESSION = 0x010, + PGM_LOG_ROLE_NAK = 0x020, + PGM_LOG_ROLE_RATE_CONTROL = 0x040, + PGM_LOG_ROLE_TX_WINDOW = 0x080, + PGM_LOG_ROLE_RX_WINDOW = 0x100, + PGM_LOG_ROLE_FEC = 0x400, + PGM_LOG_ROLE_CONGESTION_CONTROL = 0x800 +}; + +enum { + PGM_LOG_LEVEL_DEBUG = 0, + PGM_LOG_LEVEL_TRACE = 1, + PGM_LOG_LEVEL_MINOR = 2, + PGM_LOG_LEVEL_NORMAL = 3, + PGM_LOG_LEVEL_WARNING = 4, + PGM_LOG_LEVEL_ERROR = 5, + PGM_LOG_LEVEL_FATAL = 6 +}; + +extern int pgm_log_mask; +extern int pgm_min_log_level; + +typedef void (*pgm_log_func_t) (const int, const char*restrict, void*restrict); + +pgm_log_func_t pgm_log_set_handler (pgm_log_func_t, void*); +void pgm_messages_init (void); +void pgm_messages_shutdown (void); + +PGM_END_DECLS + +#endif /* __PGM_MESSAGES_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/msgv.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/msgv.h new file mode 100644 index 0000000..f5effcb --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/msgv.h @@ -0,0 +1,54 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Vector message container + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_MSGV_H__ +#define __PGM_MSGV_H__ + +struct pgm_iovec; +struct pgm_msgv_t; + +#include +#include +#include + +PGM_BEGIN_DECLS + +/* struct for scatter/gather I/O */ +struct pgm_iovec { +#ifndef _WIN32 +/* match struct iovec */ + void* iov_base; + size_t iov_len; /* size of iov_base */ +#else +/* match WSABUF */ + u_long iov_len; + char* iov_base; +#endif /* _WIN32 */ +}; + +struct pgm_msgv_t { + uint32_t msgv_len; /* number of elements in skb */ + struct pgm_sk_buff_t* msgv_skb[PGM_MAX_FRAGMENTS]; /* PGM socket buffer array */ +}; + +PGM_END_DECLS + +#endif /* __PGM_MSGV_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/packet.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/packet.h new file mode 100644 index 0000000..fb1c13a --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/packet.h @@ -0,0 +1,475 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM packet formats, RFC 3208. + * + * Copyright (c) 2006 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_PACKET_H__ +#define __PGM_PACKET_H__ + +#ifndef _WIN32 +# include +# include +# include +#endif +#include + +PGM_BEGIN_DECLS + +/* protocol number assigned by IANA */ +#ifndef IPPROTO_PGM +# define IPPROTO_PGM 113 +#endif + +/* read from /etc/protocols if available */ +extern int pgm_ipproto_pgm; + + +/* address family indicator, rfc 1700 (ADDRESS FAMILY NUMBERS) */ +#ifndef AFI_IP +# define AFI_IP 1 /* IP (IP version 4) */ +# define AFI_IP6 2 /* IP6 (IP version 6) */ +#endif + +/* UDP ports for UDP encapsulation, as per IBM WebSphere MQ */ +#define DEFAULT_UDP_ENCAP_UCAST_PORT 3055 +#define DEFAULT_UDP_ENCAP_MCAST_PORT 3056 + +/* PGM default ports */ +#define DEFAULT_DATA_DESTINATION_PORT 7500 +#define DEFAULT_DATA_SOURCE_PORT 0 /* random */ + +/* DoS limitation to protocol (MS08-036, KB950762) */ +#ifndef PGM_MAX_APDU +# define PGM_MAX_APDU UINT16_MAX +#endif + +/* Cisco default: 24 (max 8200), Juniper & H3C default: 16, SmartPGM: 64 */ +#ifndef PGM_MAX_FRAGMENTS +# define PGM_MAX_FRAGMENTS 16 +#endif + + +enum pgm_type_e { + PGM_SPM = 0x00, /* 8.1: source path message */ + PGM_POLL = 0x01, /* 14.7.1: poll request */ + PGM_POLR = 0x02, /* 14.7.2: poll response */ + PGM_ODATA = 0x04, /* 8.2: original data */ + PGM_RDATA = 0x05, /* 8.2: repair data */ + PGM_NAK = 0x08, /* 8.3: NAK or negative acknowledgement */ + PGM_NNAK = 0x09, /* 8.3: N-NAK or null negative acknowledgement */ + PGM_NCF = 0x0a, /* 8.3: NCF or NAK confirmation */ + PGM_SPMR = 0x0c, /* 13.6: SPM request */ + PGM_ACK = 0x0d, /* PGMCC: congestion control ACK */ + PGM_MAX = 0xff +}; + +#define PGM_OPT_LENGTH 0x00 /* options length */ +#define PGM_OPT_FRAGMENT 0x01 /* fragmentation */ +#define PGM_OPT_NAK_LIST 0x02 /* list of nak entries */ +#define PGM_OPT_JOIN 0x03 /* late joining */ +#define PGM_OPT_REDIRECT 0x07 /* redirect */ +#define PGM_OPT_SYN 0x0d /* synchronisation */ +#define PGM_OPT_FIN 0x0e /* session end */ +#define PGM_OPT_RST 0x0f /* session reset */ + +#define PGM_OPT_PARITY_PRM 0x08 /* forward error correction parameters */ +#define PGM_OPT_PARITY_GRP 0x09 /* group number */ +#define PGM_OPT_CURR_TGSIZE 0x0a /* group size */ + +#define PGM_OPT_CR 0x10 /* congestion report */ +#define PGM_OPT_CRQST 0x11 /* congestion report request */ + +#define PGM_OPT_PGMCC_DATA 0x12 +#define PGM_OPT_PGMCC_FEEDBACK 0x13 + +#define PGM_OPT_NAK_BO_IVL 0x04 /* nak back-off interval */ +#define PGM_OPT_NAK_BO_RNG 0x05 /* nak back-off range */ +#define PGM_OPT_NBR_UNREACH 0x0b /* neighbour unreachable */ +#define PGM_OPT_PATH_NLA 0x0c /* path nla */ + +#define PGM_OPT_INVALID 0x7f /* option invalidated */ + +/* byte alignment for packet memory maps */ +#if defined( __GNUC__ ) && !defined( sun ) +# pragma pack(push) +#endif +#pragma pack(1) + +/* 8. PGM header */ +struct pgm_header { + uint16_t pgm_sport; /* source port: tsi::sport or UDP port depending on direction */ + uint16_t pgm_dport; /* destination port */ + uint8_t pgm_type; /* version / packet type */ + uint8_t pgm_options; /* options */ +#define PGM_OPT_PARITY 0x80 /* parity packet */ +#define PGM_OPT_VAR_PKTLEN 0x40 /* + variable sized packets */ +#define PGM_OPT_NETWORK 0x02 /* network-significant: must be interpreted by network elements */ +#define PGM_OPT_PRESENT 0x01 /* option extension are present */ + uint16_t pgm_checksum; /* checksum */ + uint8_t pgm_gsi[6]; /* global source id */ + uint16_t pgm_tsdu_length; /* tsdu length */ + /* tpdu length = th length (header + options) + tsdu length */ +}; + +/* 8.1. Source Path Messages (SPM) */ +struct pgm_spm { + uint32_t spm_sqn; /* spm sequence number */ + uint32_t spm_trail; /* trailing edge sequence number */ + uint32_t spm_lead; /* leading edge sequence number */ + uint16_t spm_nla_afi; /* nla afi */ + uint16_t spm_reserved; /* reserved */ + struct in_addr spm_nla; /* path nla */ + /* ... option extensions */ +}; + +struct pgm_spm6 { + uint32_t spm6_sqn; /* spm sequence number */ + uint32_t spm6_trail; /* trailing edge sequence number */ + uint32_t spm6_lead; /* leading edge sequence number */ + uint16_t spm6_nla_afi; /* nla afi */ + uint16_t spm6_reserved; /* reserved */ + struct in6_addr spm6_nla; /* path nla */ + /* ... option extensions */ +}; + +/* 8.2. Data Packet */ +struct pgm_data { + uint32_t data_sqn; /* data packet sequence number */ + uint32_t data_trail; /* trailing edge sequence number */ + /* ... option extensions */ + /* ... data */ +}; + +/* 8.3. Negative Acknowledgments and Confirmations (NAK, N-NAK, & NCF) */ +struct pgm_nak { + uint32_t nak_sqn; /* requested sequence number */ + uint16_t nak_src_nla_afi; /* nla afi */ + uint16_t nak_reserved; /* reserved */ + struct in_addr nak_src_nla; /* source nla */ + uint16_t nak_grp_nla_afi; /* nla afi */ + uint16_t nak_reserved2; /* reserved */ + struct in_addr nak_grp_nla; /* multicast group nla */ + /* ... option extension */ +}; + +struct pgm_nak6 { + uint32_t nak6_sqn; /* requested sequence number */ + uint16_t nak6_src_nla_afi; /* nla afi */ + uint16_t nak6_reserved; /* reserved */ + struct in6_addr nak6_src_nla; /* source nla */ + uint16_t nak6_grp_nla_afi; /* nla afi */ + uint16_t nak6_reserved2; /* reserved */ + struct in6_addr nak6_grp_nla; /* multicast group nla */ + /* ... option extension */ +}; + +/* 9. Option header (max 16 per packet) */ +struct pgm_opt_header { + uint8_t opt_type; /* option type */ +#define PGM_OPT_MASK 0x7f +#define PGM_OPT_END 0x80 /* end of options flag */ + uint8_t opt_length; /* option length */ + uint8_t opt_reserved; +#define PGM_OP_ENCODED 0x8 /* F-bit */ +#define PGM_OPX_MASK 0x3 +#define PGM_OPX_IGNORE 0x0 /* extensibility bits */ +#define PGM_OPX_INVALIDATE 0x1 +#define PGM_OPX_DISCARD 0x2 +#define PGM_OP_ENCODED_NULL 0x80 /* U-bit */ +}; + +/* 9.1. Option extension length - OPT_LENGTH */ +struct pgm_opt_length { + uint8_t opt_type; /* include header as total length overwrites reserved/OPX bits */ + uint8_t opt_length; + uint16_t opt_total_length; /* total length of all options */ +}; + +/* 9.2. Option fragment - OPT_FRAGMENT */ +struct pgm_opt_fragment { + uint8_t opt_reserved; /* reserved */ + uint32_t opt_sqn; /* first sequence number */ + uint32_t opt_frag_off; /* offset */ + uint32_t opt_frag_len; /* length */ +}; + +/* 9.3.5. Option NAK List - OPT_NAK_LIST + * + * GNU C allows opt_sqn[0], ISO C89 requireqs opt_sqn[1], ISO C99 permits opt_sqn[] + */ +struct pgm_opt_nak_list { + uint8_t opt_reserved; /* reserved */ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +/* C99 flexible array, sizeof() invalid */ + uint32_t opt_sqn[]; /* requested sequence number [62] */ +#elif !defined(__STDC_VERSION__) || defined(__cplusplus) +/* C90 and older */ + uint32_t opt_sqn[1]; +#else +/* GNU C variable-length object */ + uint32_t opt_sqn[0]; +#endif +}; + +/* 9.4.2. Option Join - OPT_JOIN */ +struct pgm_opt_join { + uint8_t opt_reserved; /* reserved */ + uint32_t opt_join_min; /* minimum sequence number */ +}; + +/* 9.5.5. Option Redirect - OPT_REDIRECT */ +struct pgm_opt_redirect { + uint8_t opt_reserved; /* reserved */ + uint16_t opt_nla_afi; /* nla afi */ + uint16_t opt_reserved2; /* reserved */ + struct in_addr opt_nla; /* dlr nla */ +}; + +struct pgm_opt6_redirect { + uint8_t opt6_reserved; /* reserved */ + uint16_t opt6_nla_afi; /* nla afi */ + uint16_t opt6_reserved2; /* reserved */ + struct in6_addr opt6_nla; /* dlr nla */ +}; + +/* 9.6.2. Option Sources - OPT_SYN */ +struct pgm_opt_syn { + uint8_t opt_reserved; /* reserved */ +}; + +/* 9.7.4. Option End Session - OPT_FIN */ +struct pgm_opt_fin { + uint8_t opt_reserved; /* reserved */ +}; + +/* 9.8.4. Option Reset - OPT_RST */ +struct pgm_opt_rst { + uint8_t opt_reserved; /* reserved */ +}; + + +/* + * Forward Error Correction - FEC + */ + +/* 11.8.1. Option Parity - OPT_PARITY_PRM */ +struct pgm_opt_parity_prm { + uint8_t opt_reserved; /* reserved */ +#define PGM_PARITY_PRM_MASK 0x3 +#define PGM_PARITY_PRM_PRO 0x1 /* source provides pro-active parity packets */ +#define PGM_PARITY_PRM_OND 0x2 /* on-demand parity packets */ + uint32_t parity_prm_tgs; /* transmission group size */ +}; + +/* 11.8.2. Option Parity Group - OPT_PARITY_GRP */ +struct pgm_opt_parity_grp { + uint8_t opt_reserved; /* reserved */ + uint32_t prm_group; /* parity group number */ +}; + +/* 11.8.3. Option Current Transmission Group Size - OPT_CURR_TGSIZE */ +struct pgm_opt_curr_tgsize { + uint8_t opt_reserved; /* reserved */ + uint32_t prm_atgsize; /* actual transmission group size */ +}; + +/* + * Congestion Control + */ + +/* 12.7.1. Option Congestion Report - OPT_CR */ +struct pgm_opt_cr { + uint8_t opt_reserved; /* reserved */ +#define PGM_OPT_CR_NEL 0x0 /* OPT_CR_NE_WL report */ +#define PGM_OPT_CR_NEP 0x1 /* OPT_CR_NE_WP report */ +#define PGM_OPT_CR_RXP 0x2 /* OPT_CR_RX_WP report */ + uint32_t opt_cr_lead; /* congestion report reference sqn */ + uint16_t opt_cr_ne_wl; /* ne worst link */ + uint16_t opt_cr_ne_wp; /* ne worst path */ + uint16_t opt_cr_rx_wp; /* rcvr worst path */ + uint16_t opt_reserved2; /* reserved */ + uint16_t opt_nla_afi; /* nla afi */ + uint16_t opt_reserved3; /* reserved */ + uint32_t opt_cr_rcvr; /* worst receivers nla */ +}; + +/* 12.7.2. Option Congestion Report Request - OPT_CRQST */ +struct pgm_opt_crqst { + uint8_t opt_reserved; /* reserved */ +#define PGM_OPT_CRQST_NEL 0x0 /* request OPT_CR_NE_WL report */ +#define PGM_OPT_CRQST_NEP 0x1 /* request OPT_CR_NE_WP report */ +#define PGM_OPT_CRQST_RXP 0x2 /* request OPT_CR_RX_WP report */ +}; + +/* PGMCC. ACK Packet */ +struct pgm_ack { + uint32_t ack_rx_max; /* RX_MAX */ + uint32_t ack_bitmap; /* received packets */ + /* ... option extensions */ +}; + +/* PGMCC Options */ +struct pgm_opt_pgmcc_data { + uint8_t opt_reserved; /* reserved */ + uint32_t opt_tstamp; /* timestamp */ + uint16_t opt_nla_afi; /* nla afi */ + uint16_t opt_reserved2; /* reserved */ + struct in_addr opt_nla; /* ACKER nla */ +}; + +struct pgm_opt6_pgmcc_data { + uint8_t opt6_reserved; /* reserved */ + uint32_t opt6_tstamp; /* timestamp */ + uint16_t opt6_nla_afi; /* nla afi */ + uint16_t opt6_reserved2; /* reserved */ + struct in6_addr opt6_nla; /* ACKER nla */ +}; + +struct pgm_opt_pgmcc_feedback { + uint8_t opt_reserved; /* reserved */ + uint32_t opt_tstamp; /* timestamp */ + uint16_t opt_nla_afi; /* nla afi */ + uint16_t opt_loss_rate; /* loss rate */ + struct in_addr opt_nla; /* ACKER nla */ +}; + +struct pgm_opt6_pgmcc_feedback { + uint8_t opt6_reserved; /* reserved */ + uint32_t opt6_tstamp; /* timestamp */ + uint16_t opt6_nla_afi; /* nla afi */ + uint16_t opt6_loss_rate; /* loss rate */ + struct in6_addr opt6_nla; /* ACKER nla */ +}; + + +/* + * SPM Requests + */ + +/* 13.6. SPM Requests */ +#if 0 +struct pgm_spmr { + /* ... option extensions */ +}; +#endif + + +/* + * Poll Mechanism + */ + +/* 14.7.1. Poll Request */ +struct pgm_poll { + uint32_t poll_sqn; /* poll sequence number */ + uint16_t poll_round; /* poll round */ + uint16_t poll_s_type; /* poll sub-type */ +#define PGM_POLL_GENERAL 0x0 /* general poll */ +#define PGM_POLL_DLR 0x1 /* DLR poll */ + uint16_t poll_nla_afi; /* nla afi */ + uint16_t poll_reserved; /* reserved */ + struct in_addr poll_nla; /* path nla */ + uint32_t poll_bo_ivl; /* poll back-off interval */ + char poll_rand[4]; /* random string */ + uint32_t poll_mask; /* matching bit-mask */ + /* ... option extensions */ +}; + +struct pgm_poll6 { + uint32_t poll6_sqn; /* poll sequence number */ + uint16_t poll6_round; /* poll round */ + uint16_t poll6_s_type; /* poll sub-type */ + uint16_t poll6_nla_afi; /* nla afi */ + uint16_t poll6_reserved; /* reserved */ + struct in6_addr poll6_nla; /* path nla */ + uint32_t poll6_bo_ivl; /* poll back-off interval */ + char poll6_rand[4]; /* random string */ + uint32_t poll6_mask; /* matching bit-mask */ + /* ... option extensions */ +}; + +/* 14.7.2. Poll Response */ +struct pgm_polr { + uint32_t polr_sqn; /* polr sequence number */ + uint16_t polr_round; /* polr round */ + uint16_t polr_reserved; /* reserved */ + /* ... option extensions */ +}; + + +/* + * Implosion Prevention + */ + +/* 15.4.1. Option NAK Back-Off Interval - OPT_NAK_BO_IVL */ +struct pgm_opt_nak_bo_ivl { + uint8_t opt_reserved; /* reserved */ + uint32_t opt_nak_bo_ivl; /* nak back-off interval */ + uint32_t opt_nak_bo_ivl_sqn; /* nak back-off interval sqn */ +}; + +/* 15.4.2. Option NAK Back-Off Range - OPT_NAK_BO_RNG */ +struct pgm_opt_nak_bo_rng { + uint8_t opt_reserved; /* reserved */ + uint32_t opt_nak_max_bo_ivl; /* maximum nak back-off interval */ + uint32_t opt_nak_min_bo_ivl; /* minimum nak back-off interval */ +}; + +/* 15.4.3. Option Neighbour Unreachable - OPT_NBR_UNREACH */ +struct pgm_opt_nbr_unreach { + uint8_t opt_reserved; /* reserved */ +}; + +/* 15.4.4. Option Path - OPT_PATH_NLA */ +struct pgm_opt_path_nla { + uint8_t opt_reserved; /* reserved */ + struct in_addr opt_path_nla; /* path nla */ +}; + +struct pgm_opt6_path_nla { + uint8_t opt6_reserved; /* reserved */ + struct in6_addr opt6_path_nla; /* path nla */ +}; + + +#if defined( __GNUC__ ) && !defined( sun ) +# pragma pack(pop) +#else +# pragma pack() +#endif + +#define PGM_IS_UPSTREAM(t) \ + ((t) == PGM_NAK /* unicast */ \ + || (t) == PGM_NNAK /* unicast */ \ + || (t) == PGM_SPMR /* multicast + unicast */ \ + || (t) == PGM_POLR /* unicast */ \ + || (t) == PGM_ACK) /* unicast */ + +#define PGM_IS_PEER(t) \ + ((t) == PGM_SPMR) /* multicast */ + +#define PGM_IS_DOWNSTREAM(t) \ + ((t) == PGM_SPM /* all types are multicast */ \ + || (t) == PGM_ODATA \ + || (t) == PGM_RDATA \ + || (t) == PGM_POLL \ + || (t) == PGM_NCF) + +PGM_END_DECLS + +#endif /* __PGM_PACKET_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/pgm.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/pgm.h new file mode 100644 index 0000000..a122f48 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/pgm.h @@ -0,0 +1,42 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * OpenPGM, an implementation of the PGM network protocol. + * + * Copyright (c) 2006-2008 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_H__ +#define __PGM_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* __PGM_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/pgm.hh b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/pgm.hh new file mode 100644 index 0000000..fc3476f --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/pgm.hh @@ -0,0 +1,33 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * OpenPGM C++ wrapper. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_HH__ +#define __PGM_HH__ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include +#include + +#endif /* __PGM_HH__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/pgm_socket.hh b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/pgm_socket.hh new file mode 100644 index 0000000..87a5609 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/pgm_socket.hh @@ -0,0 +1,157 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM socket + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_SOCKET_HH__ +#define __PGM_SOCKET_HH__ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#ifndef _WIN32 +# include +# include +#else +# include +#endif + +namespace cpgm { +#define restrict +#include +}; + +template +class pgm_socket +{ +public: + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// The native socket type. + typedef struct cpgm::pgm_sock_t* native_type; + + /// Construct a pgm_socket without opening it. + pgm_socket() + { + } + + // Open a new PGM socket implementation. + bool open (::sa_family_t family, int sock_type, int protocol, cpgm::pgm_error_t** error) + { + return cpgm::pgm_socket (&this->native_type_, family, sock_type, protocol, error); + } + + /// Close a PGM socket implementation. + bool close (bool flush) + { + return pgm_close (this->native_type_, flush); + } + + /// Get the native socket implementation. + native_type native (void) + { + return this->native_type_; + } + + // Bind the datagram socket to the specified local endpoint. + bool bind (const endpoint_type& addr, cpgm::pgm_error_t** error) + { + return pgm_bind (this->native_type_, addr.data(), sizeof(addr.data()), error); + } + + /// Connect the PGM socket to the specified endpoint. + bool connect (cpgm::pgm_error_t** error) + { + return pgm_connect (this->native_type_, error); + } + + /// Set a socket option. + bool set_option (int level, int optname, const void* optval, ::socklen_t optlen) + { + return pgm_setsockopt (this->native_type_, level, optname, optval, optlen); + } + + /// Get a socket option. + bool get_option (int level, int optname, void* optval, ::socklen_t* optlen) + { + return pgm_getsockopt (this->native_type_, level, optname, optval, optlen); + } + + /// Get the local endpoint. + endpoint_type local_endpoint() const + { + endpoint_type endpoint; + pgm_getsockname (this->native_type_, &endpoint); + return endpoint; + } + + /// Disable sends or receives on the socket. + bool shutdown (int what) + { + int optname, v = 1; +#ifndef _WIN32 + if (SHUT_RD == what) optname = cpgm::PGM_SEND_ONLY; + else if (SHUT_WR == what) optname = cpgm::PGM_RECV_ONLY; +#else + if (SD_RECEIVE == what) optname = cpgm::PGM_SEND_ONLY; + else if (SD_SEND == what) optname = cpgm::PGM_RECV_ONLY; +#endif + else { + errno = EINVAL; + return false; + } + return pgm_setsockopt (this->native_type_, optname, v, sizeof(v)); + } + + /// Send some data on a connected socket. + int send (const void* buf, std::size_t len, std::size_t* bytes_sent) + { + return pgm_send (this->native_type_, buf, len, bytes_sent); + } + + /// Receive some data from the peer. + int receive (void* buf, std::size_t len, int flags, std::size_t* bytes_read, cpgm::pgm_error_t** error) + { + return pgm_recv (this->native_type_, buf, len, flags, bytes_read, error); + } + + /// Receive a datagram with the endpoint of the sender. + int receive_from (void* buf, std::size_t len, int flags, std::size_t* bytes_read, endpoint_type* from, cpgm::pgm_error_t** error) + { + int ec; + struct cpgm::pgm_sockaddr_t addr; + socklen_t addrlen = sizeof (addr); + ec = pgm_recvfrom (this->native_type_, buf, len, flags, bytes_read, &addr, &addrlen, error); + from->port (addr.sa_port); + from->address (addr.sa_addr); +/* TODO: set data-destination port */ + return ec; + } + +private: + native_type native_type_; +}; + +#endif /* __PGM_SOCKET_HH__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/signal.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/signal.h new file mode 100644 index 0000000..546fb7e --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/signal.h @@ -0,0 +1,36 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Re-entrant safe signal handling. + * + * Copyright (c) 2006-2007 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_SIGNAL_H__ +#define __PGM_SIGNAL_H__ + +#include +#include + +typedef void (*pgm_sighandler_t)(int, gpointer); + +G_BEGIN_DECLS + +gboolean pgm_signal_install (int, pgm_sighandler_t, gpointer); + +G_END_DECLS + +#endif /* __PGM_SIGNAL_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/skbuff.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/skbuff.h new file mode 100644 index 0000000..06f1264 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/skbuff.h @@ -0,0 +1,258 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM socket buffers + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_SKBUFF_H__ +#define __PGM_SKBUFF_H__ + +#include + +struct pgm_sk_buff_t; + +#include +#include +#include +#include +#include +#include +#include +#include + +PGM_BEGIN_DECLS + +struct pgm_sk_buff_t { + pgm_list_t link_; + + pgm_sock_t* restrict sock; + pgm_time_t tstamp; + pgm_tsi_t tsi; + + uint32_t sequence; + uint32_t __padding; /* push alignment of pgm_sk_buff_t::cb to 8 bytes */ + + char cb[48]; /* control buffer */ + + uint16_t len; /* actual data */ + unsigned zero_padded:1; + + struct pgm_header* pgm_header; + struct pgm_opt_fragment* pgm_opt_fragment; +#define of_apdu_first_sqn pgm_opt_fragment->opt_sqn +#define of_frag_offset pgm_opt_fragment->opt_frag_off +#define of_apdu_len pgm_opt_fragment->opt_frag_len + struct pgm_opt_pgmcc_data* pgm_opt_pgmcc_data; + struct pgm_data* pgm_data; + + void *head, /* all may-alias */ + *data, + *tail, + *end; + uint32_t truesize; + volatile uint32_t users; /* atomic */ +}; + +void pgm_skb_over_panic (const struct pgm_sk_buff_t*const, const uint16_t) PGM_GNUC_NORETURN; +void pgm_skb_under_panic (const struct pgm_sk_buff_t*const, const uint16_t) PGM_GNUC_NORETURN; +bool pgm_skb_is_valid (const struct pgm_sk_buff_t*const) PGM_GNUC_PURE PGM_GNUC_WARN_UNUSED_RESULT; + +/* attribute __pure__ only valid for platforms with atomic ops. + * attribute __malloc__ not used as only part of the memory should be aliased. + * attribute __alloc_size__ does not allow headroom. + */ +static inline struct pgm_sk_buff_t* pgm_alloc_skb (const uint16_t) PGM_GNUC_WARN_UNUSED_RESULT; + +static inline +struct pgm_sk_buff_t* +pgm_alloc_skb ( + const uint16_t size + ) +{ + struct pgm_sk_buff_t* skb; + + skb = (struct pgm_sk_buff_t*)pgm_malloc (size + sizeof(struct pgm_sk_buff_t)); +/* Requires fast FSB to test + pgm_prefetchw (skb); + */ + if (PGM_UNLIKELY(pgm_mem_gc_friendly)) { + memset (skb, 0, size + sizeof(struct pgm_sk_buff_t)); + skb->zero_padded = 1; + } else { + memset (skb, 0, sizeof(struct pgm_sk_buff_t)); + } + skb->truesize = size + sizeof(struct pgm_sk_buff_t); + pgm_atomic_write32 (&skb->users, 1); + skb->head = skb + 1; + skb->data = skb->tail = skb->head; + skb->end = (char*)skb->data + size; + return skb; +} + +/* increase reference count */ +static inline +struct pgm_sk_buff_t* +pgm_skb_get ( + struct pgm_sk_buff_t*const skb + ) +{ + pgm_atomic_inc32 (&skb->users); + return skb; +} + +static inline +void +pgm_free_skb ( + struct pgm_sk_buff_t*const skb + ) +{ + if (pgm_atomic_exchange_and_add32 (&skb->users, (uint32_t)-1) == 1) + pgm_free (skb); +} + +/* add data */ +static inline +void* +pgm_skb_put ( + struct pgm_sk_buff_t* const skb, + const uint16_t len + ) +{ + void* tmp = skb->tail; + skb->tail = (char*)skb->tail + len; + skb->len += len; + if (PGM_UNLIKELY(skb->tail > skb->end)) + pgm_skb_over_panic (skb, len); + return tmp; +} + +static inline +void* +__pgm_skb_pull ( + struct pgm_sk_buff_t*const skb, + const uint16_t len + ) +{ + skb->len -= len; + return skb->data = (char*)skb->data + len; +} + +/* remove data from start of buffer */ +static inline +void* +pgm_skb_pull ( + struct pgm_sk_buff_t*const skb, + const uint16_t len + ) +{ + return PGM_UNLIKELY(len > skb->len) ? NULL : __pgm_skb_pull (skb, len); +} + +static inline uint16_t pgm_skb_headroom (const struct pgm_sk_buff_t*const) PGM_GNUC_PURE PGM_GNUC_WARN_UNUSED_RESULT; +static inline uint16_t pgm_skb_tailroom (const struct pgm_sk_buff_t*const) PGM_GNUC_PURE PGM_GNUC_WARN_UNUSED_RESULT; + +static inline +uint16_t +pgm_skb_headroom ( + const struct pgm_sk_buff_t*const skb + ) +{ + return (char*)skb->data - (char*)skb->head; +} + +static inline +uint16_t +pgm_skb_tailroom ( + const struct pgm_sk_buff_t*const skb + ) +{ + return (char*)skb->end - (char*)skb->tail; +} + +/* reserve space to add data */ +static inline +void +pgm_skb_reserve ( + struct pgm_sk_buff_t*const skb, + const uint16_t len + ) +{ + skb->data = (char*)skb->data + len; + skb->tail = (char*)skb->tail + len; + if (PGM_UNLIKELY(skb->tail > skb->end)) + pgm_skb_over_panic (skb, len); + if (PGM_UNLIKELY(skb->data < skb->head)) + pgm_skb_under_panic (skb, len); +} + +static inline struct pgm_sk_buff_t* pgm_skb_copy (const struct pgm_sk_buff_t* const) PGM_GNUC_WARN_UNUSED_RESULT; + +static inline +struct pgm_sk_buff_t* +pgm_skb_copy ( + const struct pgm_sk_buff_t* const skb + ) +{ + struct pgm_sk_buff_t* newskb; + newskb = (struct pgm_sk_buff_t*)pgm_malloc (skb->truesize); + memcpy (newskb, skb, PGM_OFFSETOF(struct pgm_sk_buff_t, pgm_header)); + newskb->zero_padded = 0; + newskb->truesize = skb->truesize; + pgm_atomic_write32 (&newskb->users, 1); + newskb->head = newskb + 1; + newskb->end = (char*)newskb->head + ((char*)skb->end - (char*)skb->head); + newskb->data = (char*)newskb->head + ((char*)skb->data - (char*)skb->head); + newskb->tail = (char*)newskb->head + ((char*)skb->tail - (char*)skb->head); + newskb->pgm_header = skb->pgm_header ? (struct pgm_header*)((char*)newskb->head + ((char*)skb->pgm_header - (char*)skb->head)) : skb->pgm_header; + newskb->pgm_opt_fragment = skb->pgm_opt_fragment ? (struct pgm_opt_fragment*)((char*)newskb->head + ((char*)skb->pgm_opt_fragment - (char*)skb->head)) : skb->pgm_opt_fragment; + newskb->pgm_data = skb->pgm_data ? (struct pgm_data*)((char*)newskb->head + ((char*)skb->pgm_data - (char*)skb->head)) : skb->pgm_data; + memcpy (newskb->head, skb->head, (char*)skb->end - (char*)skb->head); + return newskb; +} + +static inline +void +pgm_skb_zero_pad ( + struct pgm_sk_buff_t* const skb, + const uint16_t len + ) +{ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +/* C99 version */ + if (skb->zero_padded) + return; + + const uint16_t tailroom = MIN(pgm_skb_tailroom (skb), len); + if (tailroom > 0) + memset (skb->tail, 0, tailroom); + skb->zero_padded = 1; +#else +/* C89 version */ + const uint16_t tailroom = MIN(pgm_skb_tailroom (skb), len); + if (skb->zero_padded) + return; + + if (tailroom > 0) + memset (skb->tail, 0, tailroom); + skb->zero_padded = 1; +#endif +} + +PGM_END_DECLS + +#endif /* __PGM_SKBUFF_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/snmp.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/snmp.h new file mode 100644 index 0000000..922f4e2 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/snmp.h @@ -0,0 +1,35 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * SNMP + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_SNMP_H__ +#define __PGM_SNMP_H__ + +#include +#include + +PGM_BEGIN_DECLS + +bool pgm_snmp_init (pgm_error_t**) PGM_GNUC_WARN_UNUSED_RESULT; +bool pgm_snmp_shutdown (void); + +PGM_END_DECLS + +#endif /* __PGM_SNMP_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/socket.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/socket.h new file mode 100644 index 0000000..1e32af7 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/socket.h @@ -0,0 +1,172 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * PGM socket. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_SOCKET_H__ +#define __PGM_SOCKET_H__ + +typedef struct pgm_sock_t pgm_sock_t; +struct pgm_sockaddr_t; +struct pgm_addrinfo_t; +struct pgm_fecinto_t; + +#ifdef CONFIG_HAVE_POLL +# include +#endif +#ifdef CONFIG_HAVE_EPOLL +# include +#endif +#ifndef _WIN32 +# include +# include +#endif +#include +#include +#include +#include + +PGM_BEGIN_DECLS + +struct pgm_sockaddr_t { + uint16_t sa_port; /* data-destination port */ + pgm_tsi_t sa_addr; +}; + +struct pgm_addrinfo_t { + sa_family_t ai_family; + uint32_t ai_recv_addrs_len; + struct group_source_req* restrict ai_recv_addrs; + uint32_t ai_send_addrs_len; + struct group_source_req* restrict ai_send_addrs; +}; + +struct pgm_interface_req_t { + uint32_t ir_interface; + uint32_t ir_scope_id; +}; + +struct pgm_fecinfo_t { + uint8_t block_size; + uint8_t proactive_packets; + uint8_t group_size; + bool ondemand_parity_enabled; + bool var_pktlen_enabled; +}; + +struct pgm_pgmccinfo_t { + uint32_t ack_bo_ivl; + uint32_t ack_c; + uint32_t ack_c_p; +}; + +/* socket options */ +enum { + PGM_SEND_SOCK = 0x2000, + PGM_RECV_SOCK, + PGM_REPAIR_SOCK, + PGM_PENDING_SOCK, + PGM_ACK_SOCK, + PGM_TIME_REMAIN, + PGM_RATE_REMAIN, + PGM_IP_ROUTER_ALERT, + PGM_MTU, + PGM_MSSS, + PGM_MSS, + PGM_PDU, + PGM_MULTICAST_LOOP, + PGM_MULTICAST_HOPS, + PGM_TOS, + PGM_AMBIENT_SPM, + PGM_HEARTBEAT_SPM, + PGM_TXW_SQNS, + PGM_TXW_SECS, + PGM_TXW_MAX_RTE, + PGM_PEER_EXPIRY, + PGM_SPMR_EXPIRY, + PGM_RXW_SQNS, + PGM_RXW_SECS, + PGM_RXW_MAX_RTE, + PGM_NAK_BO_IVL, + PGM_NAK_RPT_IVL, + PGM_NAK_RDATA_IVL, + PGM_NAK_DATA_RETRIES, + PGM_NAK_NCF_RETRIES, + PGM_USE_FEC, + PGM_USE_CR, + PGM_USE_PGMCC, + PGM_SEND_ONLY, + PGM_RECV_ONLY, + PGM_PASSIVE, + PGM_ABORT_ON_RESET, + PGM_NOBLOCK, + PGM_SEND_GROUP, + PGM_JOIN_GROUP, + PGM_LEAVE_GROUP, + PGM_BLOCK_SOURCE, + PGM_UNBLOCK_SOURCE, + PGM_JOIN_SOURCE_GROUP, + PGM_LEAVE_SOURCE_GROUP, + PGM_MSFILTER, + PGM_UDP_ENCAP_UCAST_PORT, + PGM_UDP_ENCAP_MCAST_PORT +}; + +/* IO status */ +enum { + PGM_IO_STATUS_ERROR, /* an error occurred */ + PGM_IO_STATUS_NORMAL, /* success */ + PGM_IO_STATUS_RESET, /* session reset */ + PGM_IO_STATUS_FIN, /* session finished */ + PGM_IO_STATUS_EOF, /* socket closed */ + PGM_IO_STATUS_WOULD_BLOCK, /* resource temporarily unavailable */ + PGM_IO_STATUS_RATE_LIMITED, /* would-block on rate limit, check timer */ + PGM_IO_STATUS_TIMER_PENDING, /* would-block with pending timer */ + PGM_IO_STATUS_CONGESTION /* would-block waiting on ACK or timeout */ +}; + +bool pgm_socket (pgm_sock_t**restrict, const sa_family_t, const int, const int, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; +bool pgm_bind (pgm_sock_t*restrict, const struct pgm_sockaddr_t*const restrict, const socklen_t, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; +bool pgm_bind3 (pgm_sock_t*restrict, const struct pgm_sockaddr_t*const restrict, const socklen_t, const struct pgm_interface_req_t*const, const socklen_t, const struct pgm_interface_req_t*const, const socklen_t, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; +bool pgm_connect (pgm_sock_t*restrict, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; +bool pgm_close (pgm_sock_t*, bool); +bool pgm_setsockopt (pgm_sock_t*const restrict, const int, const int, const void*restrict, const socklen_t); +bool pgm_getsockopt (pgm_sock_t*const restrict, const int, const int, void*restrict, socklen_t*restrict); +bool pgm_getaddrinfo (const char*restrict, const struct pgm_addrinfo_t*const restrict, struct pgm_addrinfo_t**restrict, pgm_error_t**restrict); +void pgm_freeaddrinfo (struct pgm_addrinfo_t*); +int pgm_send (pgm_sock_t*const restrict, const void*restrict, const size_t, size_t*restrict); +int pgm_sendv (pgm_sock_t*const restrict, const struct pgm_iovec*const restrict, const unsigned, const bool, size_t*restrict); +int pgm_send_skbv (pgm_sock_t*const restrict, struct pgm_sk_buff_t**const restrict, const unsigned, const bool, size_t*restrict); +int pgm_recvmsg (pgm_sock_t*const restrict, struct pgm_msgv_t*const restrict, const int, size_t*restrict, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; +int pgm_recvmsgv (pgm_sock_t*const restrict, struct pgm_msgv_t*const restrict, const size_t, const int, size_t*restrict, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; +int pgm_recv (pgm_sock_t*const restrict, void*restrict, const size_t, const int, size_t*const restrict, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; +int pgm_recvfrom (pgm_sock_t*const restrict, void*restrict, const size_t, const int, size_t*restrict, struct pgm_sockaddr_t*restrict, socklen_t*restrict, pgm_error_t**restrict) PGM_GNUC_WARN_UNUSED_RESULT; + +bool pgm_getsockname (pgm_sock_t*const restrict, struct pgm_sockaddr_t*restrict, socklen_t*restrict); +int pgm_select_info (pgm_sock_t*const restrict, fd_set*const restrict, fd_set*const restrict, int*const restrict); +#ifdef CONFIG_HAVE_POLL +int pgm_poll_info (pgm_sock_t*const restrict, struct pollfd*const restrict, int*const restrict, const int); +#endif +#ifdef CONFIG_HAVE_EPOLL +int pgm_epoll_ctl (pgm_sock_t*const, const int, const int, const int); +#endif + +PGM_END_DECLS + +#endif /* __PGM_SOCKET_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/time.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/time.h new file mode 100644 index 0000000..2fd3898 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/time.h @@ -0,0 +1,54 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * high resolution timers. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_TIME_H__ +#define __PGM_TIME_H__ + +#include + +PGM_BEGIN_DECLS + +typedef uint64_t pgm_time_t; +typedef void (*pgm_time_since_epoch_func)(const pgm_time_t*const restrict, time_t*restrict); + +#define pgm_to_secs(t) ((uint64_t)( (t) / 1000000UL )) +#define pgm_to_msecs(t) ((uint64_t)( (t) / 1000UL )) +#define pgm_to_usecs(t) ( (t) ) +#define pgm_to_nsecs(t) ((uint64_t)( (t) * 1000UL )) + +#define pgm_to_secsf(t) ( (double)(t) / 1000000.0 ) +#define pgm_to_msecsf(t) ( (double)(t) / 1000.0 ) +#define pgm_to_usecsf(t) ( (double)(t) ) +#define pgm_to_nsecsf(t) ( (double)(t) * 1000.0 ) + +#define pgm_secs(t) ((uint64_t)( (uint64_t)(t) * 1000000UL )) +#define pgm_msecs(t) ((uint64_t)( (uint64_t)(t) * 1000UL )) +#define pgm_usecs(t) ((uint64_t)( (t) )) +#define pgm_nsecs(t) ((uint64_t)( (t) / 1000UL )) + +#define PGM_TIME_FORMAT PRIu64 + +extern pgm_time_since_epoch_func pgm_time_since_epoch; + +PGM_END_DECLS + +#endif /* __PGM_TIME_H__ */ + diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/tsi.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/tsi.h new file mode 100644 index 0000000..d99f090 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/tsi.h @@ -0,0 +1,49 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * transport session ID helper functions + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_TSI_H__ +#define __PGM_TSI_H__ + +typedef struct pgm_tsi_t pgm_tsi_t; + +#include +#include + +PGM_BEGIN_DECLS + +/* maximum length of TSI as a string */ +#define PGM_TSISTRLEN (sizeof("000.000.000.000.000.000.00000")) +#define PGM_TSI_INIT { PGM_GSI_INIT, 0 } + +struct pgm_tsi_t { + pgm_gsi_t gsi; /* global session identifier */ + uint16_t sport; /* source port: a random number to help detect session re-starts */ +}; + +PGM_STATIC_ASSERT(sizeof(struct pgm_tsi_t) == 8); + +char* pgm_tsi_print (const pgm_tsi_t*) PGM_GNUC_WARN_UNUSED_RESULT; +int pgm_tsi_print_r (const pgm_tsi_t*restrict, char*restrict, size_t); +bool pgm_tsi_equal (const void*restrict, const void*restrict) PGM_GNUC_WARN_UNUSED_RESULT; + +PGM_END_DECLS + +#endif /* __PGM_TSI_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/types.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/types.h new file mode 100644 index 0000000..e8b2d82 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/types.h @@ -0,0 +1,66 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Cross-platform data types. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_TYPES_H__ +#define __PGM_TYPES_H__ + +#ifndef _MSC_VER +# include +#endif +#include + +#ifdef _WIN32 +# include +# include +# ifdef _MSC_VER +# define sa_family_t ADDRESS_FAMILY +# else +# define sa_family_t USHORT +# endif +#endif + +#ifdef _MSC_VER +# include +# define bool BOOL +# define ssize_t SSIZE_T +# define inline __inline +#elif !defined(__cplusplus) || (__GNUC__ >= 4) +/* g++ v4 handles C99 headers without complaints */ +# include +# include +#else +/* g++ v3 and other ancient compilers */ +# define bool int +# include +#endif + +#if !defined(restrict) || (__STDC_VERSION__ < 199901L) +/* C89 ANSI standard */ +# define restrict +#endif + +PGM_BEGIN_DECLS + +/* nc */ + +PGM_END_DECLS + +#endif /* __PGM_TYPES_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/version.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/version.h new file mode 100644 index 0000000..7928450 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/version.h @@ -0,0 +1,41 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * OpenPGM version. + * + * Copyright (c) 2006-2008 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_VERSION_H__ +#define __PGM_VERSION_H__ + +#include + +PGM_BEGIN_DECLS + +extern const unsigned pgm_major_version; +extern const unsigned pgm_minor_version; +extern const unsigned pgm_micro_version; + +extern const char* pgm_build_date; +extern const char* pgm_build_time; +extern const char* pgm_build_system; +extern const char* pgm_build_machine; +extern const char* pgm_build_revision; + +PGM_END_DECLS + +#endif /* __PGM_VERSION_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/winint.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/winint.h new file mode 100644 index 0000000..0205cdf --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/winint.h @@ -0,0 +1,198 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * stdint.h for Win32 & Win64 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_WININT_H__ +#define __PGM_WININT_H__ + +#include + +/* 7.18.1.1 Exact-width integer types */ +typedef signed __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef signed __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef signed __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + +/* 7.18.1.2 Minimum-width integer types */ +typedef int8_t int_least8_t; +typedef uint8_t uint_least8_t; +typedef int16_t int_least16_t; +typedef uint16_t uint_least16_t; +typedef int32_t int_least32_t; +typedef uint32_t uint_least32_t; +typedef int64_t int_least64_t; +typedef uint64_t uint_least64_t; + +/* 7.18.1.3 Fastest minimum-width integer types + * Not actually guaranteed to be fastest for all purposes + * Here we use the exact-width types for 8 and 16-bit ints. + */ +typedef int8_t int_fast8_t; +typedef uint8_t uint_fast8_t; +typedef int16_t int_fast16_t; +typedef uint16_t uint_fast16_t; +typedef int32_t int_fast32_t; +typedef uint32_t uint_fast32_t; +typedef int64_t int_fast64_t; +typedef uint64_t uint_fast64_t; + +/* 7.18.1.5 Greatest-width integer types */ +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + +/* 7.18.2 Limits of specified-width integer types */ +#if !defined ( __cplusplus) || defined (__STDC_LIMIT_MACROS) + +/* 7.18.2.1 Limits of exact-width integer types */ +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +/* 7.18.2.2 Limits of minimum-width integer types */ +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST64_MIN INT64_MIN + +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MAX INT64_MAX + +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +/* 7.18.2.3 Limits of fastest minimum-width integer types */ +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST64_MIN INT64_MIN + +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MAX INT64_MAX + +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +/* 7.18.2.4 Limits of integer types capable of holding + object pointers */ +#ifdef _WIN64 +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif + +/* 7.18.2.5 Limits of greatest-width integer types */ +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +/* 7.18.3 Limits of other integer types */ +#ifdef _WIN64 +# define PTRDIFF_MIN INT64_MIN +# define PTRDIFF_MAX INT64_MAX +#else +# define PTRDIFF_MIN INT32_MIN +# define PTRDIFF_MAX INT32_MAX +#endif + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX +# ifdef _WIN64 +# define SIZE_MAX UINT64_MAX +# else +# define SIZE_MAX UINT32_MAX +# endif +#endif + +#ifndef WCHAR_MIN /* also in wchar.h */ +# define WCHAR_MIN 0U +# define WCHAR_MAX UINT16_MAX +#endif + +/* + * wint_t is unsigned short for compatibility with MS runtime + */ +#define WINT_MIN 0U +#define WINT_MAX UINT16_MAX + +#endif /* !defined ( __cplusplus) || defined __STDC_LIMIT_MACROS */ + + +/* 7.18.4 Macros for integer constants */ +#if !defined ( __cplusplus) || defined (__STDC_CONSTANT_MACROS) + +/* 7.18.4.1 Macros for minimum-width integer constants + + Accoding to Douglas Gwyn : + "This spec was changed in ISO/IEC 9899:1999 TC1; in ISO/IEC + 9899:1999 as initially published, the expansion was required + to be an integer constant of precisely matching type, which + is impossible to accomplish for the shorter types on most + platforms, because C99 provides no standard way to designate + an integer constant with width less than that of type int. + TC1 changed this to require just an integer constant + *expression* with *promoted* type." + + The trick used here is from Clive D W Feather. +*/ + +#define INT8_C(val) (INT_LEAST8_MAX-INT_LEAST8_MAX+(val)) +#define INT16_C(val) (INT_LEAST16_MAX-INT_LEAST16_MAX+(val)) +#define INT32_C(val) (INT_LEAST32_MAX-INT_LEAST32_MAX+(val)) +/* The 'trick' doesn't work in C89 for long long because, without + suffix, (val) will be evaluated as int, not intmax_t */ +#define INT64_C(val) val##LL + +#define UINT8_C(val) (val) +#define UINT16_C(val) (val) +#define UINT32_C(val) (val##U) +#define UINT64_C(val) val##ULL + +/* 7.18.4.2 Macros for greatest-width integer constants */ +#define INTMAX_C(val) val##LL +#define UINTMAX_C(val) val##ULL + +#endif /* !defined ( __cplusplus) || defined __STDC_CONSTANT_MACROS */ + +#endif /* __PGM_WININT_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/include/pgm/wininttypes.h b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/wininttypes.h new file mode 100644 index 0000000..5daa7f1 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/include/pgm/wininttypes.h @@ -0,0 +1,254 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * inttypes.h for Win32 & Win64 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_WININTTYPES_H__ +#define __PGM_WININTTYPES_H__ + +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) + +/* 7.8.1 Macros for format specifiers + * + * MS runtime does not yet understand C9x standard "ll" + * length specifier. It appears to treat "ll" as "l". + * The non-standard I64 length specifier causes warning in GCC, + * but understood by MS runtime functions. + */ + +/* fprintf macros for signed types */ +#define PRId8 "d" +#define PRId16 "hd" +#define PRId32 "I32d" +#define PRId64 "I64d" + +#define PRIdLEAST8 "d" +#define PRIdLEAST16 "hd" +#define PRIdLEAST32 "I32d" +#define PRIdLEAST64 "I64d" + +#define PRIdFAST8 "d" +#define PRIdFAST16 "hd" +#define PRIdFAST32 "I32d" +#define PRIdFAST64 "I64d" + +#define PRIdMAX "I64d" + +#define PRIi8 "i" +#define PRIi16 "hi" +#define PRIi32 "i" +#define PRIi64 "I64i" + +#define PRIiLEAST8 "i" +#define PRIiLEAST16 "hi" +#define PRIiLEAST32 "I32i" +#define PRIiLEAST64 "I64i" + +#define PRIiFAST8 "i" +#define PRIiFAST16 "hi" +#define PRIiFAST32 "I32i" +#define PRIiFAST64 "I64i" + +#define PRIiMAX "I64i" + +#define PRIo8 "o" +#define PRIo16 "ho" +#define PRIo32 "I32o" +#define PRIo64 "I64o" + +#define PRIoLEAST8 "o" +#define PRIoLEAST16 "ho" +#define PRIoLEAST32 "I32o" +#define PRIoLEAST64 "I64o" + +#define PRIoFAST8 "o" +#define PRIoFAST16 "ho" +#define PRIoFAST32 "o" +#define PRIoFAST64 "I64o" + +#define PRIoMAX "I64o" + +/* fprintf macros for unsigned types */ +#define PRIu8 "u" +#define PRIu16 "hu" +#define PRIu32 "I32u" +#define PRIu64 "I64u" + +#define PRIuLEAST8 "u" +#define PRIuLEAST16 "hu" +#define PRIuLEAST32 "I32u" +#define PRIuLEAST64 "I64u" + +#define PRIuFAST8 "u" +#define PRIuFAST16 "hu" +#define PRIuFAST32 "I32u" +#define PRIuFAST64 "I64u" + +#define PRIuMAX "I64u" + +#define PRIx8 "x" +#define PRIx16 "hx" +#define PRIx32 "I32x" +#define PRIx64 "I64x" + +#define PRIxLEAST8 "x" +#define PRIxLEAST16 "hx" +#define PRIxLEAST32 "I32x" +#define PRIxLEAST64 "I64x" + +#define PRIxFAST8 "x" +#define PRIxFAST16 "hx" +#define PRIxFAST32 "I32x" +#define PRIxFAST64 "I64x" + +#define PRIxMAX "I64x" + +#define PRIX8 "X" +#define PRIX16 "hX" +#define PRIX32 "I32X" +#define PRIX64 "I64X" + +#define PRIXLEAST8 "X" +#define PRIXLEAST16 "hX" +#define PRIXLEAST32 "I32X" +#define PRIXLEAST64 "I64X" + +#define PRIXFAST8 "X" +#define PRIXFAST16 "hX" +#define PRIXFAST32 "I32X" +#define PRIXFAST64 "I64X" + +#define PRIXMAX "I64X" + +/* fscanf macros for signed int types */ + +#define SCNd8 "hhd" +#define SCNd16 "hd" +#define SCNd32 "ld" +#define SCNd64 "I64d" + +#define SCNdLEAST8 "hhd" +#define SCNdLEAST16 "hd" +#define SCNdLEAST32 "ld" +#define SCNdLEAST64 "I64d" + +#define SCNdFAST8 "hhd" +#define SCNdFAST16 "hd" +#define SCNdFAST32 "ld" +#define SCNdFAST64 "I64d" + +#define SCNdMAX "I64d" + +#define SCNi8 "hhi" +#define SCNi16 "hi" +#define SCNi32 "li" +#define SCNi64 "I64i" + +#define SCNiLEAST8 "hhi" +#define SCNiLEAST16 "hi" +#define SCNiLEAST32 "li" +#define SCNiLEAST64 "I64i" + +#define SCNiFAST8 "hhi" +#define SCNiFAST16 "hi" +#define SCNiFAST32 "li" +#define SCNiFAST64 "I64i" + +#define SCNiMAX "I64i" + +#define SCNo8 "hho" +#define SCNo16 "ho" +#define SCNo32 "lo" +#define SCNo64 "I64o" + +#define SCNoLEAST8 "hho" +#define SCNoLEAST16 "ho" +#define SCNoLEAST32 "lo" +#define SCNoLEAST64 "I64o" + +#define SCNoFAST8 "hho" +#define SCNoFAST16 "ho" +#define SCNoFAST32 "lo" +#define SCNoFAST64 "I64o" + +#define SCNoMAX "I64o" + +#define SCNx8 "hhx" +#define SCNx16 "hx" +#define SCNx32 "lx" +#define SCNx64 "I64x" + +#define SCNxLEAST8 "hhx" +#define SCNxLEAST16 "hx" +#define SCNxLEAST32 "lx" +#define SCNxLEAST64 "I64x" + +#define SCNxFAST8 "hhx" +#define SCNxFAST16 "hx" +#define SCNxFAST32 "lx" +#define SCNxFAST64 "I64x" + +#define SCNxMAX "I64x" + +/* fscanf macros for unsigned int types */ + +#define SCNu8 "hhu" +#define SCNu16 "hu" +#define SCNu32 "lu" +#define SCNu64 "I64u" + +#define SCNuLEAST8 "hhu" +#define SCNuLEAST16 "hu" +#define SCNuLEAST32 "lu" +#define SCNuLEAST64 "I64u" + +#define SCNuFAST8 "hhu" +#define SCNuFAST16 "hu" +#define SCNuFAST32 "lu" +#define SCNuFAST64 "I64u" + +#define SCNuMAX "I64u" + +#ifdef _WIN64 +# define PRIdPTR "I64d" +# define PRIiPTR "I64i" +# define PRIoPTR "I64o" +# define PRIuPTR "I64u" +# define PRIxPTR "I64x" +# define PRIXPTR "I64X" +# define SCNdPTR "I64d" +# define SCNiPTR "I64i" +# define SCNoPTR "I64o" +# define SCNxPTR "I64x" +# define SCNuPTR "I64u" +#else +# define PRIdPTR "ld" +# define PRIiPTR "li" +# define PRIoPTR "lo" +# define PRIuPTR "lu" +# define PRIxPTR "lx" +# define PRIXPTR "lX" +# define SCNdPTR "ld" +# define SCNiPTR "li" +# define SCNoPTR "lo" +# define SCNxPTR "lx" +# define SCNuPTR "lu" +#endif + +#endif /* !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) */ + +#endif /* __PGM_WININTTYPES_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/indextoaddr.c b/3rdparty/openpgm-svn-r1135/pgm/indextoaddr.c new file mode 100644 index 0000000..479dffd --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/indextoaddr.c @@ -0,0 +1,98 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable interface index to socket address function. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + + +//#define INDEXTOADDR_DEBUG + + +/* interfaces indexes refer to the link layer, we want to find the internet layer address. + * the big problem is that multiple IPv6 addresses can be bound to one link - called scopes. + * we can just pick the first scope and let IP routing handle the rest. + */ + +bool +pgm_if_indextoaddr ( + const unsigned ifindex, + const sa_family_t iffamily, + const uint32_t ifscope, + struct sockaddr* restrict ifsa, + pgm_error_t** restrict error + ) +{ + pgm_return_val_if_fail (NULL != ifsa, FALSE); + + if (0 == ifindex) /* any interface or address */ + { + ifsa->sa_family = iffamily; + switch (iffamily) { + case AF_INET: + ((struct sockaddr_in*)ifsa)->sin_addr.s_addr = INADDR_ANY; + break; + + case AF_INET6: + ((struct sockaddr_in6*)ifsa)->sin6_addr = in6addr_any; + break; + + default: + pgm_return_val_if_reached (FALSE); + break; + } + return TRUE; + } + + struct pgm_ifaddrs_t *ifap, *ifa; + if (!pgm_getifaddrs (&ifap, error)) { + pgm_prefix_error (error, + _("Enumerating network interfaces: ")); + return FALSE; + } + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) + { + if (NULL == ifa->ifa_addr || + ifa->ifa_addr->sa_family != iffamily) + continue; + + const unsigned i = pgm_if_nametoindex (iffamily, ifa->ifa_name); + pgm_assert (0 != i); + if (i == ifindex) + { + if (ifscope && ifscope != pgm_sockaddr_scope_id (ifa->ifa_addr)) + continue; + memcpy (ifsa, ifa->ifa_addr, pgm_sockaddr_len(ifa->ifa_addr)); + pgm_freeifaddrs (ifap); + return TRUE; + } + } + + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NODEV, + _("No matching network interface index: %i"), + ifindex); + pgm_freeifaddrs (ifap); + return FALSE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/indextoaddr.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/indextoaddr.c.c89.patch new file mode 100644 index 0000000..4965077 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/indextoaddr.c.c89.patch @@ -0,0 +1,44 @@ +--- indextoaddr.c 2010-05-21 11:34:27.000000000 +0800 ++++ indextoaddr.c89 2010-08-04 12:06:20.000000000 +0800 +@@ -44,7 +44,9 @@ + + if (0 == ifindex) /* any interface or address */ + { ++#pragma warning( disable : 4244 ) + ifsa->sa_family = iffamily; ++#pragma warning( default : 4244 ) + switch (iffamily) { + case AF_INET: + ((struct sockaddr_in*)ifsa)->sin_addr.s_addr = INADDR_ANY; +@@ -61,6 +63,7 @@ + return TRUE; + } + ++ { + struct pgm_ifaddrs_t *ifap, *ifa; + if (!pgm_getifaddrs (&ifap, error)) { + pgm_prefix_error (error, +@@ -74,6 +77,7 @@ + ifa->ifa_addr->sa_family != iffamily) + continue; + ++ { + const unsigned i = pgm_if_nametoindex (iffamily, ifa->ifa_name); + pgm_assert (0 != i); + if (i == ifindex) +@@ -84,6 +88,7 @@ + pgm_freeifaddrs (ifap); + return TRUE; + } ++ } + } + + pgm_set_error (error, +@@ -93,6 +98,7 @@ + ifindex); + pgm_freeifaddrs (ifap); + return FALSE; ++ } + } + + /* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/indextoaddr_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/indextoaddr_unittest.c new file mode 100644 index 0000000..7699f9c --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/indextoaddr_unittest.c @@ -0,0 +1,302 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for portable interface index to socket address function. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* IFF_UP */ +#define _BSD_SOURCE 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* mock state */ + +struct mock_interface_t { + unsigned int index; + char* name; + unsigned int flags; + struct sockaddr_storage addr; + struct sockaddr_storage netmask; +}; + +static GList *mock_interfaces = NULL; + +struct pgm_ifaddrs_t; +struct pgm_error_t; + +static bool mock_pgm_getifaddrs (struct pgm_ifaddrs_t**, struct pgm_error_t**); +static void mock_pgm_freeifaddrs (struct pgm_ifaddrs_t*); +static unsigned mock_pgm_if_nametoindex (const sa_family_t, const char*); + + +#define pgm_getifaddrs mock_pgm_getifaddrs +#define pgm_freeifaddrs mock_pgm_freeifaddrs +#define pgm_if_nametoindex mock_pgm_if_nametoindex + + +#define INDEXTOADDR_DEBUG +#include "indextoaddr.c" + + +static +gpointer +create_interface ( + const unsigned index, + const char* name, + const char* flags + ) +{ + struct mock_interface_t* new_interface; + + g_assert (name); + g_assert (flags); + + new_interface = g_slice_alloc0 (sizeof(struct mock_interface_t)); + new_interface->index = index; + new_interface->name = g_strdup (name); + + struct sockaddr_in* sin = (gpointer)&new_interface->addr; + struct sockaddr_in6* sin6 = (gpointer)&new_interface->addr; + + gchar** tokens = g_strsplit (flags, ",", 0); + for (guint i = 0; tokens[i]; i++) + { + if (strcmp (tokens[i], "up") == 0) + new_interface->flags |= IFF_UP; + else if (strcmp (tokens[i], "down") == 0) + new_interface->flags |= 0; + else if (strcmp (tokens[i], "loop") == 0) + new_interface->flags |= IFF_LOOPBACK; + else if (strcmp (tokens[i], "broadcast") == 0) + new_interface->flags |= IFF_BROADCAST; + else if (strcmp (tokens[i], "multicast") == 0) + new_interface->flags |= IFF_MULTICAST; + else if (strncmp (tokens[i], "ip=", strlen("ip=")) == 0) { + const char* addr = tokens[i] + strlen("ip="); + g_assert (pgm_sockaddr_pton (addr, (struct sockaddr*)&new_interface->addr)); + } + else if (strncmp (tokens[i], "netmask=", strlen("netmask=")) == 0) { + const char* addr = tokens[i] + strlen("netmask="); + g_assert (pgm_sockaddr_pton (addr, (struct sockaddr*)&new_interface->netmask)); + } + else if (strncmp (tokens[i], "scope=", strlen("scope=")) == 0) { + const char* scope = tokens[i] + strlen("scope="); + g_assert (AF_INET6 == ((struct sockaddr*)&new_interface->addr)->sa_family); + ((struct sockaddr_in6*)&new_interface->addr)->sin6_scope_id = atoi (scope); + } + else + g_error ("parsing failed for flag \"%s\"", tokens[i]); + } + + g_strfreev (tokens); + return new_interface; +} + +#define APPEND_INTERFACE(a,b,c) \ + do { \ + gpointer data = create_interface ((a), (b), (c)); \ + g_assert (data); \ + mock_interfaces = g_list_append (mock_interfaces, data); \ + g_assert (mock_interfaces); g_assert (mock_interfaces->data); \ + } while (0) +static +void +mock_setup_net (void) +{ + APPEND_INTERFACE( 1, "lo", "up,loop"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast"); + APPEND_INTERFACE( 3, "eth1", "down,broadcast,multicast"); + APPEND_INTERFACE( 1, "lo", "up,loop,ip=127.0.0.1,netmask=255.0.0.0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=10.6.28.33,netmask=255.255.255.0"); + APPEND_INTERFACE( 1, "lo", "up,loop,ip=::1,netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,scope=0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=2002:dce8:d28e::33,netmask=ffff:ffff:ffff:ffff::0,scope=0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=fe80::214:5eff:febd:6dda,netmask=ffff:ffff:ffff:ffff::0,scope=2"); +} + +static +void +mock_teardown_net (void) +{ + GList* list; + + list = mock_interfaces; + while (list) { + struct mock_interface_t* interface = list->data; + g_free (interface->name); + g_slice_free1 (sizeof(struct mock_interface_t), interface); + list = list->next; + } + g_list_free (mock_interfaces); +} + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + +bool +mock_pgm_getifaddrs ( + struct pgm_ifaddrs_t** ifap, + pgm_error_t** err + ) +{ + if (NULL == ifap) { + return FALSE; + } + + g_debug ("mock_getifaddrs (ifap:%p err:%p)", (gpointer)ifap, (gpointer)err); + + GList* list = mock_interfaces; + int n = g_list_length (list); + struct pgm_ifaddrs_t* ifa = calloc (n, sizeof(struct pgm_ifaddrs_t)); + struct pgm_ifaddrs_t* ift = ifa; + while (list) { + struct mock_interface_t* interface = list->data; + ift->ifa_addr = (gpointer)&interface->addr; + ift->ifa_name = interface->name; + ift->ifa_flags = interface->flags; + ift->ifa_netmask = (gpointer)&interface->netmask; + list = list->next; + if (list) { + ift->ifa_next = ift + 1; + ift = ift->ifa_next; + } + } + + *ifap = ifa; + return TRUE; +} + +static +void +mock_pgm_freeifaddrs ( + struct pgm_ifaddrs_t* ifa + ) +{ + g_debug ("mock_freeifaddrs (ifa:%p)", (gpointer)ifa); + free (ifa); +} + +unsigned +mock_pgm_if_nametoindex ( + const sa_family_t iffamily, + const char* ifname + ) +{ + GList* list = mock_interfaces; + while (list) { + const struct mock_interface_t* interface = list->data; + if (0 == strcmp (ifname, interface->name)) + return interface->index; + list = list->next; + } + return 0; +} + + +/* target: + * bool + * pgm_if_indextoaddr ( + * const unsigned ifindex, + * const sa_family_t iffamily, + * const uint32_t ifscope, + * struct sockaddr* ifsa, + * pgm_error_t** error + * ) + */ + +START_TEST (test_indextoaddr_pass_001) +{ + char saddr[INET6_ADDRSTRLEN]; + struct sockaddr_storage addr; + pgm_error_t* err = NULL; + const unsigned int ifindex = 2; + fail_unless (TRUE == pgm_if_indextoaddr (ifindex, AF_INET, 0, (struct sockaddr*)&addr, &err)); + pgm_sockaddr_ntop ((struct sockaddr*)&addr, saddr, sizeof(saddr)); + g_message ("index:%d -> %s", + ifindex, saddr); + fail_unless (TRUE == pgm_if_indextoaddr (ifindex, AF_INET6, 0, (struct sockaddr*)&addr, &err)); + pgm_sockaddr_ntop ((struct sockaddr*)&addr, saddr, sizeof(saddr)); + g_message ("index:%d -> %s", + ifindex, saddr); +} +END_TEST + +START_TEST (test_indextoaddr_fail_001) +{ + pgm_error_t* err = NULL; + fail_unless (FALSE == pgm_if_indextoaddr (0, 0, 0, NULL, &err)); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_indextoaddr = tcase_create ("init-ctx"); + suite_add_tcase (s, tc_indextoaddr); + tcase_add_checked_fixture (tc_indextoaddr, mock_setup_net, mock_teardown_net); + tcase_add_test (tc_indextoaddr, test_indextoaddr_pass_001); + tcase_add_test (tc_indextoaddr, test_indextoaddr_fail_001); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/indextoname.c b/3rdparty/openpgm-svn-r1135/pgm/indextoname.c new file mode 100644 index 0000000..c4043ef --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/indextoname.c @@ -0,0 +1,52 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Windows interface index to interface name function. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef _WIN32 +# include +# include +#endif +#include + + +//#define INDEXTONAME_DEBUG + + +char* +pgm_if_indextoname ( + unsigned int ifindex, + char* ifname + ) +{ +#ifndef _WIN32 + return if_indextoname (ifindex, ifname); +#else + pgm_return_val_if_fail (NULL != ifname, NULL); + + MIB_IFROW ifRow = { .dwIndex = ifindex }; + const DWORD dwRetval = GetIfEntry (&ifRow); + if (NO_ERROR != dwRetval) + return NULL; + strcpy (ifname, (char*)ifRow.wszName); + return ifname; +#endif /* _WIN32 */ +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/indextoname.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/indextoname.c.c89.patch new file mode 100644 index 0000000..307ec77 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/indextoname.c.c89.patch @@ -0,0 +1,29 @@ +--- indextoname.c 2010-05-21 11:35:23.000000000 +0800 ++++ indextoname.c89 2010-08-05 14:12:26.000000000 +0800 +@@ -39,13 +39,25 @@ + return if_indextoname (ifindex, ifname); + #else + pgm_return_val_if_fail (NULL != ifname, NULL); ++ { + +- MIB_IFROW ifRow = { .dwIndex = ifindex }; ++ MIB_IFROW ifRow; ++ ifRow.dwIndex = ifindex; ++ { + const DWORD dwRetval = GetIfEntry (&ifRow); + if (NO_ERROR != dwRetval) + return NULL; ++#ifdef _MSC_VER ++ { ++ int i; ++ wcstombs_s (&i, ifname, IF_NAMESIZE, ifRow.wszName, _TRUNCATE); ++ } ++#else + strcpy (ifname, (char*)ifRow.wszName); ++#endif + return ifname; ++ } ++ } + #endif /* _WIN32 */ + } + diff --git a/3rdparty/openpgm-svn-r1135/pgm/inet_network.c b/3rdparty/openpgm-svn-r1135/pgm/inet_network.c new file mode 100644 index 0000000..8cac34b --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/inet_network.c @@ -0,0 +1,237 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable implementations of inet_network and inet_network6. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + + +//#define INET_NETWORK_DEBUG + +/* locals */ + +static uint32_t cidr_to_netmask (const unsigned) PGM_GNUC_CONST; + + +/* calculate IPv4 netmask from network size, returns address in + * host byte order. + */ + +static +uint32_t +cidr_to_netmask ( + const unsigned cidr + ) +{ + return (cidr == 0) ? 0 : (0xffffffff - (1 << (32 - cidr)) + 1); +} + + +/* Converts a numbers-and-dots notation string into a network number in + * host order. + * Note parameters and return value differs from inet_network(). This + * function will not interpret octal numbers, preceeded with a 0, or + * hexadecimal numbers, preceeded by 0x. + * + * 127 => 127.0.0.0 + * 127.1/8 => 127.0.0.0 -- 127.1.0.0 + * inet_addr() would be 127.0.0.1 + * inet_network() would be 0.0.127.1 + * + * returns 0 on success, returns -1 on invalid address. + */ + +int /* return type to match inet_network() */ +pgm_inet_network ( + const char* restrict s, + struct in_addr* restrict in + ) +{ + pgm_return_val_if_fail (NULL != s, -1); + pgm_return_val_if_fail (NULL != in, -1); + + pgm_debug ("pgm_inet_network (s:\"%s\" in:%p)", + s, (const void*)in); + + const char *p = s; + unsigned val = 0; + int shift = 24; + + in->s_addr = INADDR_ANY; + + while (*p) + { + if (isdigit (*p)) { + val = 10 * val + (*p - '0'); + } else if (*p == '.' || *p == 0) { + if (val > 0xff) { + in->s_addr = INADDR_NONE; + return -1; + } + +//g_trace ("elem %i", val); + + in->s_addr |= val << shift; + val = 0; + shift -= 8; + if (shift < 0 && *p != 0) { + in->s_addr = INADDR_NONE; + return -1; + } + + } else if (*p == '/') { + if (val > 0xff) { + in->s_addr = INADDR_NONE; + return -1; + } +//g_trace ("elem %i", val); + in->s_addr |= val << shift; + p++; val = 0; + while (*p) + { + if (isdigit (*p)) { + val = 10 * val + (*p - '0'); + } else { + in->s_addr = INADDR_NONE; + return -1; + } + p++; + } + if (val == 0 || val > 32) { + in->s_addr = INADDR_NONE; + return -1; + } +//g_trace ("bit mask %i", val); + +/* zero out host bits */ + const struct in_addr netaddr = { .s_addr = cidr_to_netmask (val) }; +#ifdef INET_NETWORK_DEBUG +{ +g_debug ("netaddr %s", inet_ntoa (netaddr)); +} +#endif + in->s_addr &= netaddr.s_addr; + return 0; + + } else if (*p == 'x' || *p == 'X') { /* skip number, e.g. 1.x.x.x */ + if (val > 0) { + in->s_addr = INADDR_NONE; + return -1; + } + + } else { + in->s_addr = INADDR_NONE; + return -1; + } + p++; + } + + in->s_addr |= val << shift; + return 0; +} + +/* Converts a numbers-and-dots notation string into an IPv6 network number. + * + * ::1/128 => 0:0:0:0:0:0:0:1 + * ::1 => 0:0:0:0:0:0:0:1 + * ::1.2.3.4 => 0:0:0:0:1.2.3.4 + * + * returns 0 on success, returns -1 on invalid address. + */ + +int +pgm_inet6_network ( + const char* restrict s, /* NULL terminated */ + struct in6_addr* restrict in6 + ) +{ + pgm_return_val_if_fail (NULL != s, -1); + pgm_return_val_if_fail (NULL != in6, -1); + + pgm_debug ("pgm_inet6_network (s:\"%s\" in6:%p)", + s, (const void*)in6); + +/* inet_pton cannot parse IPv6 addresses with subnet declarations, so + * chop them off. + * + * as we are dealing with network addresses IPv6 zone indices are not important + * so we can use the inet_xtoy functions. + */ + char s2[INET6_ADDRSTRLEN]; + const char *p = s; + char* p2 = s2; + while (*p) { + if (*p == '/') break; + *p2++ = *p++; + } + if (*p == 0) { + if (pgm_inet_pton (AF_INET6, s, in6)) return 0; + pgm_debug ("pgm_inet_pton(AF_INET6) failed on '%s'", s); + memcpy (in6, &in6addr_any, sizeof(in6addr_any)); + return -1; + } + + *p2 = 0; + pgm_debug ("net part %s", s2); + if (!pgm_inet_pton (AF_INET6, s2, in6)) { + pgm_debug ("pgm_inet_pton(AF_INET) failed parsing network part '%s'", s2); + memcpy (in6, &in6addr_any, sizeof(in6addr_any)); + return -1; + } + +#ifdef INET_NETWORK_DEBUG + char sdebug[INET6_ADDRSTRLEN]; + pgm_debug ("IPv6 network address: %s", pgm_inet_ntop(AF_INET6, in6, sdebug, sizeof(sdebug))); +#endif + + p++; + unsigned val = 0; + while (*p) + { + if (isdigit(*p)) { + val = 10 * val + (*p - '0'); + } else { + pgm_debug ("failed parsing subnet size due to character '%c'", *p); + memcpy (in6, &in6addr_any, sizeof(in6addr_any)); + return -1; + } + p++; + } + if (val == 0 || val > 128) { + pgm_debug ("subnet size invalid (%d)", val); + memcpy (in6, &in6addr_any, sizeof(in6addr_any)); + return -1; + } + pgm_debug ("subnet size %i", val); + +/* zero out host bits */ + const unsigned suffix_length = 128 - val; + for (int i = suffix_length, j = 15; i > 0; i -= 8, --j) + { + in6->s6_addr[ j ] &= i >= 8 ? 0x00 : (unsigned)(( 0xffU << i ) & 0xffU ); + } + + pgm_debug ("effective IPv6 network address after subnet mask: %s", pgm_inet_ntop(AF_INET6, in6, s2, sizeof(s2))); + + return 0; +} + +/* eof */ + diff --git a/3rdparty/openpgm-svn-r1135/pgm/inet_network.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/inet_network.c.c89.patch new file mode 100644 index 0000000..3dbe298 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/inet_network.c.c89.patch @@ -0,0 +1,76 @@ +--- inet_network.c 2010-05-21 11:32:20.000000000 +0800 ++++ inet_network.c89 2010-08-04 12:18:21.000000000 +0800 +@@ -70,6 +70,7 @@ + pgm_debug ("pgm_inet_network (s:\"%s\" in:%p)", + s, (const void*)in); + ++ { + const char *p = s; + unsigned val = 0; + int shift = 24; +@@ -121,13 +122,16 @@ + //g_trace ("bit mask %i", val); + + /* zero out host bits */ +- const struct in_addr netaddr = { .s_addr = cidr_to_netmask (val) }; ++ { ++ struct in_addr netaddr; ++ netaddr.s_addr = cidr_to_netmask (val); + #ifdef INET_NETWORK_DEBUG + { + g_debug ("netaddr %s", inet_ntoa (netaddr)); + } + #endif + in->s_addr &= netaddr.s_addr; ++ } + return 0; + + } else if (*p == 'x' || *p == 'X') { /* skip number, e.g. 1.x.x.x */ +@@ -145,6 +149,7 @@ + + in->s_addr |= val << shift; + return 0; ++ } + } + + /* Converts a numbers-and-dots notation string into an IPv6 network number. +@@ -174,6 +179,7 @@ + * as we are dealing with network addresses IPv6 zone indices are not important + * so we can use the inet_xtoy functions. + */ ++ { + char s2[INET6_ADDRSTRLEN]; + const char *p = s; + char* p2 = s2; +@@ -202,6 +208,7 @@ + #endif + + p++; ++ { + unsigned val = 0; + while (*p) + { +@@ -222,15 +229,22 @@ + pgm_debug ("subnet size %i", val); + + /* zero out host bits */ ++ { + const unsigned suffix_length = 128 - val; +- for (int i = suffix_length, j = 15; i > 0; i -= 8, --j) ++ { ++ int i, j; ++ for (i = suffix_length, j = 15; i > 0; i -= 8, --j) + { + in6->s6_addr[ j ] &= i >= 8 ? 0x00 : (unsigned)(( 0xffU << i ) & 0xffU ); + } ++ } + + pgm_debug ("effective IPv6 network address after subnet mask: %s", pgm_inet_ntop(AF_INET6, in6, s2, sizeof(s2))); + + return 0; ++ } ++ } ++ } + } + + /* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/inet_network_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/inet_network_unittest.c new file mode 100644 index 0000000..2739215 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/inet_network_unittest.c @@ -0,0 +1,203 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for portable implementations of inet_network and inet_network6. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* mock state */ + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + + +#define INET_NETWORK_DEBUG +#include "inet_network.c" + + +/* target: + * int + * pgm_inet_network ( + * const char* s, + * struct in_addr* in -- in host byte order + * ) + */ + +struct test_case_t { + const char* network; + const char* answer; +}; + +static const struct test_case_t cases_001[] = { + { "127", "127.0.0.0" }, /* different to inet_addr/inet_network */ + { "127/8", "127.0.0.0" }, + { "127.1/8", "127.0.0.0" }, + { "127.1", "127.1.0.0" }, /* different to inet_addr/inet_network */ + { "127.x.x.x", "127.0.0.0" }, + { "127.X.X.X", "127.0.0.0" }, + { "127.0.0.0", "127.0.0.0" }, + { "127.0.0.1/8", "127.0.0.0" }, + { "127.0.0.1/32", "127.0.0.1" }, + { "10.0.0.0/8", "10.0.0.0" }, /* RFC1918 class A */ + { "10.255.255.255/8", "10.0.0.0" }, + { "172.16.0.0/12", "172.16.0.0" }, /* RFC1918 class B */ + { "172.31.255.255/12", "172.16.0.0" }, + { "192.168.0.0/16", "192.168.0.0" }, /* RFC1918 class C */ + { "192.168.255.255/16", "192.168.0.0" }, + { "169.254.0.0/16", "169.254.0.0" }, /* RFC3927 link-local */ + { "192.88.99.0/24", "192.88.99.0" }, /* RFC3068 6to4 relay anycast */ + { "224.0.0.0/4", "224.0.0.0" }, /* RFC3171 multicast */ + { "0.0.0.0", "0.0.0.0" }, + { "255.255.255.255", "255.255.255.255" }, +}; + +START_TEST (test_inet_network_pass_001) +{ + const char* network = cases_001[_i].network; + const char* answer = cases_001[_i].answer; + + struct in_addr host_order, network_order; + fail_unless (0 == pgm_inet_network (network, &host_order)); + network_order.s_addr = g_htonl (host_order.s_addr); + + g_message ("Resolved \"%s\" to \"%s\"", + network, inet_ntoa (network_order)); + +{ +struct in_addr t = { .s_addr = g_htonl (inet_network (network)) }; +g_message ("inet_network (%s) = %s", network, inet_ntoa (t)); +} + + fail_unless (0 == strcmp (answer, inet_ntoa (network_order))); +} +END_TEST + +START_TEST (test_inet_network_fail_001) +{ + fail_unless (-1 == pgm_inet_network (NULL, NULL)); +} +END_TEST + +START_TEST (test_inet_network_fail_002) +{ + const char* network = "192.168.0.1/0"; + + struct in_addr host_order; + fail_unless (-1 == pgm_inet_network (network, &host_order)); +} +END_TEST + +/* target: + * int + * pgm_inet6_network ( + * const char* s, + * struct in6_addr* in6 + * ) + */ + +static const struct test_case_t cases6_001[] = { + { "::1/128", "::1" }, + { "2002:dec8:d28e::36/64", "2002:dec8:d28e::" }, /* 6to4 */ + { "fe80::203:baff:fe4e:6cc8/10", "fe80::" }, /* link-local */ + { "ff02::1/8", "ff00::" }, /* multicast */ + { "fc00:6::61/7", "fc00::" }, /* ULA */ +}; + +START_TEST (test_inet6_network_pass_001) +{ + const char* network = cases6_001[_i].network; + const char* answer = cases6_001[_i].answer; + + char snetwork[INET6_ADDRSTRLEN]; + struct in6_addr addr; + fail_unless (0 == pgm_inet6_network (network, &addr)); + g_message ("Resolved \"%s\" to \"%s\"", + network, pgm_inet_ntop (AF_INET6, &addr, snetwork, sizeof(snetwork))); + + fail_unless (0 == strcmp (answer, snetwork)); +} +END_TEST + +START_TEST (test_inet6_network_fail_001) +{ + fail_unless (-1 == pgm_inet6_network (NULL, NULL)); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_inet_network = tcase_create ("inet-network"); + suite_add_tcase (s, tc_inet_network); + tcase_add_loop_test (tc_inet_network, test_inet_network_pass_001, 0, G_N_ELEMENTS(cases_001)); + tcase_add_test (tc_inet_network, test_inet_network_fail_001); + + TCase* tc_inet6_network = tcase_create ("inet6-network"); + suite_add_tcase (s, tc_inet6_network); + tcase_add_loop_test (tc_inet6_network, test_inet6_network_pass_001, 0, G_N_ELEMENTS(cases6_001)); + tcase_add_test (tc_inet6_network, test_inet6_network_fail_001); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/ip_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/ip_unittest.c new file mode 100644 index 0000000..692fb55 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/ip_unittest.c @@ -0,0 +1,367 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for ip stack. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* getsockopt(3SOCKET) + * level is the protocol number of the protocl that controls the option. + */ +#ifndef SOL_IP +# define SOL_IP IPPROTO_IP +#endif +#ifndef SOL_IPV6 +# define SOL_IPV6 IPPROTO_IPV6 +#endif + +/* mock state */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + + +#define PGM_COMPILATION +#include "impl/sockaddr.h" +#include "impl/indextoaddr.h" +#include "impl/ip.h" + + +/* target: + * testing platform capability to loop send multicast packets to a listening + * receive socket. + */ + +START_TEST (test_multicast_loop_pass_001) +{ + struct sockaddr_in addr; + memset (&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr ("239.192.0.1"); + + int recv_sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + fail_if (-1 == recv_sock, "socket failed"); + struct sockaddr_in recv_addr; + memcpy (&recv_addr, &addr, sizeof(addr)); + recv_addr.sin_port = 7500; + fail_unless (0 == bind (recv_sock, (struct sockaddr*)&recv_addr, pgm_sockaddr_len ((struct sockaddr*)&recv_addr)), "bind failed"); + struct group_req gr; + memset (&gr, 0, sizeof(gr)); + ((struct sockaddr*)&gr.gr_group)->sa_family = addr.sin_family; + ((struct sockaddr_in*)&gr.gr_group)->sin_addr.s_addr = addr.sin_addr.s_addr; + fail_unless (0 == setsockopt (recv_sock, SOL_IP, MCAST_JOIN_GROUP, (const char*)&gr, sizeof(gr)), "setsockopt failed"); + fail_unless (0 == pgm_sockaddr_multicast_loop (recv_sock, AF_INET, FALSE), "multicast_loop failed"); + + int send_sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + fail_if (-1 == send_sock, "socket failed"); + struct sockaddr_in send_addr; + memcpy (&send_addr, &addr, sizeof(addr)); + fail_unless (0 == bind (send_sock, (struct sockaddr*)&send_addr, pgm_sockaddr_len ((struct sockaddr*)&send_addr)), "bind failed"); + struct sockaddr_in if_addr; + fail_unless (TRUE == pgm_if_indextoaddr (0, AF_INET, 0, (struct sockaddr*)&if_addr, NULL), "if_indextoaddr failed"); + fail_unless (0 == pgm_sockaddr_multicast_if (send_sock, (struct sockaddr*)&if_addr, 0), "multicast_if failed"); + fail_unless (0 == pgm_sockaddr_multicast_loop (send_sock, AF_INET, TRUE), "multicast_loop failed"); + + const char data[] = "apple pie"; + addr.sin_port = 7500; + ssize_t bytes_sent = sendto (send_sock, data, sizeof(data), 0, (struct sockaddr*)&addr, pgm_sockaddr_len ((struct sockaddr*)&addr)); + if (-1 == bytes_sent) + g_message ("sendto: %s", strerror (errno)); + fail_unless (sizeof(data) == bytes_sent, "sendto underrun"); + + char recv_data[1024]; + ssize_t bytes_read = recv (recv_sock, recv_data, sizeof(recv_data), MSG_DONTWAIT); + if (-1 == bytes_read) + g_message ("sendto: %s", strerror (errno)); + fail_unless (sizeof(data) == bytes_read, "recv underrun"); + + fail_unless (0 == close (recv_sock), "close failed"); + fail_unless (0 == close (send_sock), "close failed"); +} +END_TEST + +/* target: + * testing whether unicast bind accepts packets to multicast join on a + * different port. + */ + +START_TEST (test_port_bind_pass_001) +{ + struct sockaddr_in addr; + memset (&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr ("239.192.0.1"); + + int recv_sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + fail_if (-1 == recv_sock, "socket failed"); + struct sockaddr_in recv_addr; + memcpy (&recv_addr, &addr, sizeof(addr)); + recv_addr.sin_port = 3056; + fail_unless (0 == bind (recv_sock, (struct sockaddr*)&recv_addr, pgm_sockaddr_len ((struct sockaddr*)&recv_addr)), "bind failed"); + struct group_req gr; + memset (&gr, 0, sizeof(gr)); + ((struct sockaddr*)&gr.gr_group)->sa_family = addr.sin_family; + ((struct sockaddr_in*)&gr.gr_group)->sin_addr.s_addr = addr.sin_addr.s_addr; + ((struct sockaddr_in*)&gr.gr_group)->sin_port = 3055; + fail_unless (0 == setsockopt (recv_sock, SOL_IP, MCAST_JOIN_GROUP, (const char*)&gr, sizeof(gr)), "setsockopt failed"); + fail_unless (0 == pgm_sockaddr_multicast_loop (recv_sock, AF_INET, FALSE), "multicast_loop failed"); + + int send_sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + fail_if (-1 == send_sock, "socket failed"); + struct sockaddr_in send_addr; + memcpy (&send_addr, &addr, sizeof(addr)); + fail_unless (0 == bind (send_sock, (struct sockaddr*)&send_addr, pgm_sockaddr_len ((struct sockaddr*)&send_addr)), "bind failed"); + struct sockaddr_in if_addr; + fail_unless (TRUE == pgm_if_indextoaddr (0, AF_INET, 0, (struct sockaddr*)&if_addr, NULL), "if_indextoaddr failed"); + fail_unless (0 == pgm_sockaddr_multicast_if (send_sock, (struct sockaddr*)&if_addr, 0), "multicast_if failed"); + fail_unless (0 == pgm_sockaddr_multicast_loop (send_sock, AF_INET, TRUE), "multicast_loop failed"); + + const char data[] = "apple pie"; + addr.sin_port = 3056; + ssize_t bytes_sent = sendto (send_sock, data, sizeof(data), 0, (struct sockaddr*)&addr, pgm_sockaddr_len ((struct sockaddr*)&addr)); + if (-1 == bytes_sent) + g_message ("sendto: %s", strerror (errno)); + fail_unless (sizeof(data) == bytes_sent, "sendto underrun"); + + char recv_data[1024]; + ssize_t bytes_read = recv (recv_sock, recv_data, sizeof(recv_data), MSG_DONTWAIT); + if (-1 == bytes_read) + g_message ("recv: %s", strerror (errno)); + if (sizeof(data) != bytes_read) + g_message ("recv returned %d bytes expected %d.", bytes_read, sizeof(data)); + fail_unless (sizeof(data) == bytes_read, "recv underrun"); + + fail_unless (0 == close (recv_sock), "close failed"); + fail_unless (0 == close (send_sock), "close failed"); +} +END_TEST + +/* target: + * test setting hop limit, aka time-to-live. + * + * NB: whilst convenient, we cannot use SOCK_RAW & IPPROTO_UDP on Solaris 10 + * as it crashes the IP stack. + */ + +START_TEST (test_hop_limit_pass_001) +{ + struct sockaddr_in addr; + memset (&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr ("239.192.0.1"); + + int recv_sock = socket (AF_INET, SOCK_RAW, 113); + fail_if (-1 == recv_sock, "socket failed"); + struct sockaddr_in recv_addr; + memcpy (&recv_addr, &addr, sizeof(addr)); + recv_addr.sin_port = 7500; + fail_unless (0 == bind (recv_sock, (struct sockaddr*)&recv_addr, pgm_sockaddr_len ((struct sockaddr*)&recv_addr)), "bind failed"); + struct group_req gr; + memset (&gr, 0, sizeof(gr)); + ((struct sockaddr*)&gr.gr_group)->sa_family = addr.sin_family; + ((struct sockaddr_in*)&gr.gr_group)->sin_addr.s_addr = addr.sin_addr.s_addr; + fail_unless (0 == setsockopt (recv_sock, SOL_IP, MCAST_JOIN_GROUP, (const char*)&gr, sizeof(gr)), "setsockopt failed"); + fail_unless (0 == pgm_sockaddr_multicast_loop (recv_sock, AF_INET, FALSE), "multicast_loop failed"); + fail_unless (0 == pgm_sockaddr_hdrincl (recv_sock, AF_INET, TRUE), "hdrincl failed"); + + int send_sock = socket (AF_INET, SOCK_RAW, 113); + fail_if (-1 == send_sock, "socket failed"); + struct sockaddr_in send_addr; + memcpy (&send_addr, &addr, sizeof(addr)); + fail_unless (0 == bind (send_sock, (struct sockaddr*)&send_addr, pgm_sockaddr_len ((struct sockaddr*)&send_addr)), "bind failed"); + struct sockaddr_in if_addr; + fail_unless (TRUE == pgm_if_indextoaddr (0, AF_INET, 0, (struct sockaddr*)&if_addr, NULL), "if_indextoaddr failed"); + fail_unless (0 == pgm_sockaddr_multicast_if (send_sock, (struct sockaddr*)&if_addr, 0), "multicast_if failed"); + fail_unless (0 == pgm_sockaddr_multicast_loop (send_sock, AF_INET, TRUE), "multicast_loop failed"); + fail_unless (0 == pgm_sockaddr_multicast_hops (send_sock, AF_INET, 16), "multicast_hops failed"); + + const char data[] = "apple pie"; + addr.sin_port = 7500; + ssize_t bytes_sent = sendto (send_sock, data, sizeof(data), 0, (struct sockaddr*)&addr, pgm_sockaddr_len ((struct sockaddr*)&addr)); + if (-1 == bytes_sent) + g_message ("sendto: %s", strerror (errno)); + fail_unless (sizeof(data) == bytes_sent, "sendto underrun"); + + char recv_data[1024]; + ssize_t bytes_read = recv (recv_sock, recv_data, sizeof(recv_data), MSG_DONTWAIT); + if (-1 == bytes_read) + g_message ("recv: %s", strerror (errno)); + const size_t pkt_len = sizeof(struct pgm_ip) + sizeof(data); + if (pkt_len != bytes_read) + g_message ("recv returned %zd bytes expected %zu.", bytes_read, pkt_len); + fail_unless (pkt_len == bytes_read, "recv underrun"); + const struct pgm_ip* iphdr = (void*)recv_data; + fail_unless (4 == iphdr->ip_v, "Incorrect IP version, found %u expecting 4.", iphdr->ip_v); + fail_unless (16 == iphdr->ip_ttl, "hop count mismatch, found %u expecting 16.", iphdr->ip_ttl); + + fail_unless (0 == close (recv_sock), "close failed"); + fail_unless (0 == close (send_sock), "close failed"); +} +END_TEST + +/* target: + * router alert. + */ + +START_TEST (test_router_alert_pass_001) +{ + struct sockaddr_in addr; + memset (&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr ("239.192.0.1"); + + int recv_sock = socket (AF_INET, SOCK_RAW, 113); + fail_if (-1 == recv_sock, "socket failed"); + struct sockaddr_in recv_addr; + memcpy (&recv_addr, &addr, sizeof(addr)); + recv_addr.sin_port = 7500; + fail_unless (0 == bind (recv_sock, (struct sockaddr*)&recv_addr, pgm_sockaddr_len ((struct sockaddr*)&recv_addr)), "bind failed"); + struct group_req gr; + memset (&gr, 0, sizeof(gr)); + ((struct sockaddr*)&gr.gr_group)->sa_family = addr.sin_family; + ((struct sockaddr_in*)&gr.gr_group)->sin_addr.s_addr = addr.sin_addr.s_addr; + fail_unless (0 == setsockopt (recv_sock, SOL_IP, MCAST_JOIN_GROUP, (const char*)&gr, sizeof(gr)), "setsockopt failed"); + fail_unless (0 == pgm_sockaddr_multicast_loop (recv_sock, AF_INET, FALSE), "multicast_loop failed"); + fail_unless (0 == pgm_sockaddr_hdrincl (recv_sock, AF_INET, TRUE), "hdrincl failed"); + + int send_sock = socket (AF_INET, SOCK_RAW, 113); + fail_if (-1 == send_sock, "socket failed"); + struct sockaddr_in send_addr; + memcpy (&send_addr, &addr, sizeof(addr)); + fail_unless (0 == bind (send_sock, (struct sockaddr*)&send_addr, pgm_sockaddr_len ((struct sockaddr*)&send_addr)), "bind failed"); + struct sockaddr_in if_addr; + fail_unless (TRUE == pgm_if_indextoaddr (0, AF_INET, 0, (struct sockaddr*)&if_addr, NULL), "if_indextoaddr failed"); + fail_unless (0 == pgm_sockaddr_multicast_if (send_sock, (struct sockaddr*)&if_addr, 0), "multicast_if failed"); + fail_unless (0 == pgm_sockaddr_multicast_loop (send_sock, AF_INET, TRUE), "multicast_loop failed"); + fail_unless (0 == pgm_sockaddr_router_alert (send_sock, AF_INET, TRUE), "router_alert failed"); + + const char data[] = "apple pie"; + addr.sin_port = 7500; + ssize_t bytes_sent = sendto (send_sock, data, sizeof(data), 0, (struct sockaddr*)&addr, pgm_sockaddr_len ((struct sockaddr*)&addr)); + if (-1 == bytes_sent) + g_message ("sendto: %s", strerror (errno)); + fail_unless (sizeof(data) == bytes_sent, "sendto underrun"); + + char recv_data[1024]; + ssize_t bytes_read = recv (recv_sock, recv_data, sizeof(recv_data), MSG_DONTWAIT); + if (-1 == bytes_read) + g_message ("recv: %s", strerror (errno)); + const size_t ra_iphdr_len = sizeof(uint32_t) + sizeof(struct pgm_ip); + const size_t ra_pkt_len = ra_iphdr_len + sizeof(data); + if (ra_pkt_len != bytes_read) + g_message ("recv returned %zd bytes expected %zu.", bytes_read, ra_pkt_len); + fail_unless (ra_pkt_len == bytes_read, "recv underrun"); + const struct pgm_ip* iphdr = (void*)recv_data; + fail_unless (4 == iphdr->ip_v, "Incorrect IP version, found %u expecting 4.", iphdr->ip_v); + if (ra_iphdr_len != (iphdr->ip_hl << 2)) { + g_message ("IP header length mismatch, found %zu expecting %zu.", + (size_t)(iphdr->ip_hl << 2), ra_iphdr_len); + } + g_message ("IP header length = %zu", (size_t)(iphdr->ip_hl << 2)); + const uint32_t* ipopt = (const void*)&recv_data[ iphdr->ip_hl << 2 ]; + const uint32_t ipopt_ra = ((uint32_t)PGM_IPOPT_RA << 24) | (0x04 << 16); + const uint32_t router_alert = htonl(ipopt_ra); + if (router_alert == *ipopt) { + g_message ("IP option router alert found after IP header length."); + ipopt += sizeof(uint32_t); + } else { + ipopt = (const void*)&recv_data[ sizeof(struct pgm_ip) ]; + fail_unless (router_alert == *ipopt, "IP router alert option not found."); + g_message ("IP option router alert found before end of IP header length."); + } + g_message ("Final IP header length = %zu", (size_t)((const char*)ipopt - (const char*)recv_data)); + + fail_unless (0 == close (recv_sock), "close failed"); + fail_unless (0 == close (send_sock), "close failed"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_multicast_loop = tcase_create ("multicast loop"); + suite_add_tcase (s, tc_multicast_loop); + tcase_add_test (tc_multicast_loop, test_multicast_loop_pass_001); + + TCase* tc_port_bind = tcase_create ("port bind"); + suite_add_tcase (s, tc_port_bind); + tcase_add_test (tc_port_bind, test_port_bind_pass_001); + + TCase* tc_hop_limit = tcase_create ("hop limit"); + suite_add_tcase (s, tc_hop_limit); + tcase_add_test (tc_hop_limit, test_hop_limit_pass_001); + + TCase* tc_router_alert = tcase_create ("router alert"); + suite_add_tcase (s, tc_router_alert); + tcase_add_test (tc_router_alert, test_router_alert_pass_001); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + if (0 != getuid()) { + fprintf (stderr, "This test requires super-user privileges to run.\n"); + return EXIT_FAILURE; + } + + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/list.c b/3rdparty/openpgm-svn-r1135/pgm/list.c new file mode 100644 index 0000000..7b22f17 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/list.c @@ -0,0 +1,146 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable doubly-linked list. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + + +//#define LIST_DEBUG + +pgm_list_t* +pgm_list_append ( + pgm_list_t* restrict list, + void* restrict data + ) +{ + pgm_list_t* new_list; + pgm_list_t* last; + + new_list = pgm_new (pgm_list_t, 1); + new_list->data = data; + new_list->next = NULL; + + if (list) + { + last = pgm_list_last (list); + last->next = new_list; + new_list->prev = last; + return list; + } + else + { + new_list->prev = NULL; + return new_list; + } +} + +pgm_list_t* +pgm_list_prepend_link ( + pgm_list_t* restrict list, + pgm_list_t* restrict link_ + ) +{ + pgm_list_t* new_list = link_; + + pgm_return_val_if_fail (NULL != link_, list); + + new_list->next = list; + new_list->prev = NULL; + + if (list) + list->prev = new_list; + return new_list; +} + +static inline +pgm_list_t* +_pgm_list_remove_link ( + pgm_list_t* list, /* list and link_ may be the same */ + pgm_list_t* link_ + ) +{ + if (PGM_LIKELY (NULL != link_)) + { + if (link_->prev) + link_->prev->next = link_->next; + if (link_->next) + link_->next->prev = link_->prev; + + if (link_ == list) + list = list->next; + + link_->next = link_->prev = NULL; + } + return list; +} + +pgm_list_t* +pgm_list_remove_link ( + pgm_list_t* list, /* list and link_ may be the same */ + pgm_list_t* link_ + ) +{ + return _pgm_list_remove_link (list, link_); +} + +pgm_list_t* +pgm_list_delete_link ( + pgm_list_t* list, /* list and link_ may be the same */ + pgm_list_t* link_ + ) +{ + pgm_list_t* new_list = _pgm_list_remove_link (list, link_); + pgm_free (link_); + + return new_list; +} + +/* Has pure attribute as NULL is a valid list + */ + +pgm_list_t* +pgm_list_last ( + pgm_list_t* list + ) +{ + if (PGM_LIKELY (NULL != list)) { + while (list->next) + list = list->next; + } + return list; +} + +unsigned +pgm_list_length ( + pgm_list_t* list + ) +{ + unsigned length = 0; + + while (list) + { + length++; + list = list->next; + } + + return length; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/log.c b/3rdparty/openpgm-svn-r1135/pgm/log.c new file mode 100644 index 0000000..af2aec5 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/log.c @@ -0,0 +1,151 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * basic logging. + * + * Copyright (c) 2006-2007 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#ifndef G_OS_WIN32 +# include +#endif +#include +#include "pgm/log.h" + + +/* globals */ + +#define TIME_FORMAT "%Y-%m-%d %H:%M:%S " + +static int log_timezone PGM_GNUC_READ_MOSTLY = 0; +static char log_hostname[NI_MAXHOST + 1] PGM_GNUC_READ_MOSTLY; + +static void glib_log_handler (const gchar*, GLogLevelFlags, const gchar*, gpointer); +static void pgm_log_handler (const int, const char*, void*); + + +/* calculate time zone offset in seconds + */ + +bool +log_init ( void ) +{ +/* time zone offset */ + time_t t = time(NULL); + struct tm sgmt, *gmt = &sgmt; + *gmt = *gmtime(&t); + struct tm* loc = localtime(&t); + log_timezone = (loc->tm_hour - gmt->tm_hour) * 60 * 60 + + (loc->tm_min - gmt->tm_min) * 60; + int dir = loc->tm_year - gmt->tm_year; + if (!dir) dir = loc->tm_yday - gmt->tm_yday; + log_timezone += dir * 24 * 60 * 60; +// printf ("timezone offset %u seconds.\n", log_timezone); + gethostname (log_hostname, sizeof(log_hostname)); + g_log_set_handler ("Pgm", G_LOG_LEVEL_MASK, glib_log_handler, NULL); + g_log_set_handler ("Pgm-Http", G_LOG_LEVEL_MASK, glib_log_handler, NULL); + g_log_set_handler ("Pgm-Snmp", G_LOG_LEVEL_MASK, glib_log_handler, NULL); + g_log_set_handler (NULL, G_LOG_LEVEL_MASK, glib_log_handler, NULL); + pgm_log_set_handler (pgm_log_handler, NULL); + return 0; +} + +/* log callback + */ +static void +glib_log_handler ( + const gchar* log_domain, + G_GNUC_UNUSED GLogLevelFlags log_level, + const gchar* message, + G_GNUC_UNUSED gpointer unused_data + ) +{ +#ifdef G_OS_UNIX + struct iovec iov[7]; + struct iovec* v = iov; + time_t now; + time (&now); + const struct tm* time_ptr = localtime(&now); + char tbuf[1024]; + strftime(tbuf, sizeof(tbuf), TIME_FORMAT, time_ptr); + v->iov_base = tbuf; + v->iov_len = strlen(tbuf); + v++; + v->iov_base = log_hostname; + v->iov_len = strlen(log_hostname); + v++; + if (log_domain) { + v->iov_base = " "; + v->iov_len = 1; + v++; + v->iov_base = log_domain; + v->iov_len = strlen(log_domain); + v++; + } + v->iov_base = ": "; + v->iov_len = 2; + v++; + v->iov_base = message; + v->iov_len = strlen(message); + v++; + v->iov_base = "\n"; + v->iov_len = 1; + v++; + writev (STDOUT_FILENO, iov, v - iov); +#else + time_t now; + time (&now); + const struct tm* time_ptr = localtime(&now); + char s[1024]; + strftime(s, sizeof(s), TIME_FORMAT, time_ptr); + write (STDOUT_FILENO, s, strlen(s)); + write (STDOUT_FILENO, log_hostname, strlen(log_hostname)); + if (log_domain) { + write (STDOUT_FILENO, " ", 1); + write (STDOUT_FILENO, log_domain, strlen(log_domain)); + } + write (STDOUT_FILENO, ": ", 2); + write (STDOUT_FILENO, message, strlen(message)); + write (STDOUT_FILENO, "\n", 1); +#endif +} + +static void +pgm_log_handler ( + const int pgm_log_level, + const char* message, + G_GNUC_UNUSED void* closure + ) +{ + GLogLevelFlags glib_log_level; + + switch (pgm_log_level) { + case PGM_LOG_LEVEL_DEBUG: glib_log_level = G_LOG_LEVEL_DEBUG; break; + case PGM_LOG_LEVEL_TRACE: glib_log_level = G_LOG_LEVEL_DEBUG; break; + case PGM_LOG_LEVEL_MINOR: glib_log_level = G_LOG_LEVEL_INFO; break; + case PGM_LOG_LEVEL_NORMAL: glib_log_level = G_LOG_LEVEL_MESSAGE; break; + case PGM_LOG_LEVEL_WARNING: glib_log_level = G_LOG_LEVEL_WARNING; break; + case PGM_LOG_LEVEL_ERROR: glib_log_level = G_LOG_LEVEL_CRITICAL; break; + case PGM_LOG_LEVEL_FATAL: glib_log_level = G_LOG_LEVEL_ERROR; break; + } + + g_log ("Pgm", glib_log_level, message, NULL); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/math.c b/3rdparty/openpgm-svn-r1135/pgm/math.c new file mode 100644 index 0000000..c2a1b4e --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/math.c @@ -0,0 +1,75 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable math. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + + +//#define MATH_DEBUG + + +static const unsigned primes[] = +{ + 11, + 19, + 37, + 73, + 109, + 163, + 251, + 367, + 557, + 823, + 1237, + 1861, + 2777, + 4177, + 6247, + 9371, + 14057, + 21089, + 31627, + 47431, + 71143, + 106721, + 160073, + 240101, + 360163, + 540217, + 810343, + 1215497, + 1823231, + 2734867, + 4102283, + 6153409, + 9230113, + 13845163, +}; + +unsigned +pgm_spaced_primes_closest (unsigned num) +{ + for (unsigned i = 0; i < PGM_N_ELEMENTS(primes); i++) + if (primes[i] > num) + return primes[i]; + return primes[PGM_N_ELEMENTS(primes) - 1]; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/math.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/math.c.c89.patch new file mode 100644 index 0000000..f6746fc --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/math.c.c89.patch @@ -0,0 +1,16 @@ +--- math.c 2010-05-21 11:32:22.000000000 +0800 ++++ math.c89 2010-08-03 17:25:54.000000000 +0800 +@@ -66,9 +66,12 @@ + unsigned + pgm_spaced_primes_closest (unsigned num) + { +- for (unsigned i = 0; i < PGM_N_ELEMENTS(primes); i++) ++ { ++ unsigned i; ++ for (i = 0; i < PGM_N_ELEMENTS(primes); i++) + if (primes[i] > num) + return primes[i]; ++ } + return primes[PGM_N_ELEMENTS(primes) - 1]; + } + diff --git a/3rdparty/openpgm-svn-r1135/pgm/md5.c b/3rdparty/openpgm-svn-r1135/pgm/md5.c new file mode 100644 index 0000000..9761113 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/md5.c @@ -0,0 +1,368 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * MD5 hashing algorithm. + * + * MD5 original source GNU C Library: + * Includes functions to compute MD5 message digest of files or memory blocks + * according to the definition of MD5 in RFC 1321 from April 1992. + * + * Copyright (C) 1995, 1996, 2001, 2003 Free Software Foundation, Inc. + * + * This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + + +//#define MD5_DEBUG + + +/* locals */ + +static void _pgm_md5_process_block (struct pgm_md5_t*restrict, const void*restrict, size_t); +static void* _pgm_md5_read_ctx (const struct pgm_md5_t*, void*restrict); + + +/* This array contains the bytes used to pad the buffer to the next + * 64-byte boundary. (RFC 1321, 3.1: Step 1) */ +static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; + + +#if __BYTE_ORDER == __BIG_ENDIAN +# define SWAP(n) \ + (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) +#else +# define SWAP(n) (n) +#endif + + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ + +void +pgm_md5_init_ctx ( + struct pgm_md5_t* ctx + ) +{ +/* pre-conditions */ + pgm_assert (NULL != ctx); + + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; +} + +/* These are the four functions used in the four steps of the MD5 algorithm + and defined in the RFC 1321. The first function is a little bit optimized + (as found in Colin Plumbs public domain implementation). */ +#define FF(b, c, d) (d ^ (b & (c ^ d))) +#define FG(b, c, d) FF (d, b, c) +#define FH(b, c, d) (b ^ c ^ d) +#define FI(b, c, d) (c ^ (b | ~d)) + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. */ + +static +void +_pgm_md5_process_block ( + struct pgm_md5_t* restrict ctx, + const void* restrict buffer, + size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != buffer); + pgm_assert (len > 0); + pgm_assert (NULL != ctx); + + uint32_t correct_words[16]; + const uint32_t *words = buffer; + const size_t nwords = len / sizeof (uint32_t); + const uint32_t *endp = words + nwords; + uint32_t A = ctx->A; + uint32_t B = ctx->B; + uint32_t C = ctx->C; + uint32_t D = ctx->D; + +/* First increment the byte count. RFC 1321 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. Do a double word increment. */ + ctx->total[0] += len; + if (ctx->total[0] < len) + ++ctx->total[1]; + +/* Process all bytes in the buffer with 64 bytes in each round of + the loop. */ + while (words < endp) + { + uint32_t *cwp = correct_words; + uint32_t A_save = A; + uint32_t B_save = B; + uint32_t C_save = C; + uint32_t D_save = D; + +/* First round: using the given function, the context and a constant + the next context is computed. Because the algorithms processing + unit is a 32-bit word and it is determined to work on words in + little endian byte order we perhaps have to change the byte order + before the computation. To reduce the work for the next steps + we store the swapped words in the array CORRECT_WORDS. */ + +#define OP(a, b, c, d, s, T) \ + do \ + { \ + a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \ + ++words; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + +/* It is unfortunate that C does not provide an operator for + * cyclic rotation. Hope the C compiler is smart enough. */ +#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) + +/* Before we start, one word to the strange constants. + They are defined in RFC 1321 as + + T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64, or + perl -e 'foreach(1..64){printf "0x%08x\n", int (4294967296 * abs (sin $_))}' + */ + +/* Round 1. */ + OP (A, B, C, D, 7, 0xd76aa478); + OP (D, A, B, C, 12, 0xe8c7b756); + OP (C, D, A, B, 17, 0x242070db); + OP (B, C, D, A, 22, 0xc1bdceee); + OP (A, B, C, D, 7, 0xf57c0faf); + OP (D, A, B, C, 12, 0x4787c62a); + OP (C, D, A, B, 17, 0xa8304613); + OP (B, C, D, A, 22, 0xfd469501); + OP (A, B, C, D, 7, 0x698098d8); + OP (D, A, B, C, 12, 0x8b44f7af); + OP (C, D, A, B, 17, 0xffff5bb1); + OP (B, C, D, A, 22, 0x895cd7be); + OP (A, B, C, D, 7, 0x6b901122); + OP (D, A, B, C, 12, 0xfd987193); + OP (C, D, A, B, 17, 0xa679438e); + OP (B, C, D, A, 22, 0x49b40821); + +/* For the second to fourth round we have the possibly swapped words + in CORRECT_WORDS. Redefine the macro to take an additional first + argument specifying the function to use. */ +#undef OP +#define OP(f, a, b, c, d, k, s, T) \ + do \ + { \ + a += f (b, c, d) + correct_words[k] + T; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + +/* Round 2. */ + OP (FG, A, B, C, D, 1, 5, 0xf61e2562); + OP (FG, D, A, B, C, 6, 9, 0xc040b340); + OP (FG, C, D, A, B, 11, 14, 0x265e5a51); + OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); + OP (FG, A, B, C, D, 5, 5, 0xd62f105d); + OP (FG, D, A, B, C, 10, 9, 0x02441453); + OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); + OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); + OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); + OP (FG, D, A, B, C, 14, 9, 0xc33707d6); + OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); + OP (FG, B, C, D, A, 8, 20, 0x455a14ed); + OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); + OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); + OP (FG, C, D, A, B, 7, 14, 0x676f02d9); + OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); + +/* Round 3. */ + OP (FH, A, B, C, D, 5, 4, 0xfffa3942); + OP (FH, D, A, B, C, 8, 11, 0x8771f681); + OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); + OP (FH, B, C, D, A, 14, 23, 0xfde5380c); + OP (FH, A, B, C, D, 1, 4, 0xa4beea44); + OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); + OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); + OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); + OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); + OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); + OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); + OP (FH, B, C, D, A, 6, 23, 0x04881d05); + OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); + OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); + OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); + OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); + +/* Round 4. */ + OP (FI, A, B, C, D, 0, 6, 0xf4292244); + OP (FI, D, A, B, C, 7, 10, 0x432aff97); + OP (FI, C, D, A, B, 14, 15, 0xab9423a7); + OP (FI, B, C, D, A, 5, 21, 0xfc93a039); + OP (FI, A, B, C, D, 12, 6, 0x655b59c3); + OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); + OP (FI, C, D, A, B, 10, 15, 0xffeff47d); + OP (FI, B, C, D, A, 1, 21, 0x85845dd1); + OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); + OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); + OP (FI, C, D, A, B, 6, 15, 0xa3014314); + OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); + OP (FI, A, B, C, D, 4, 6, 0xf7537e82); + OP (FI, D, A, B, C, 11, 10, 0xbd3af235); + OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); + OP (FI, B, C, D, A, 9, 21, 0xeb86d391); + +/* Add the starting values of the context. */ + A += A_save; + B += B_save; + C += C_save; + D += D_save; + } + +/* Put checksum in context given as argument. */ + ctx->A = A; + ctx->B = B; + ctx->C = C; + ctx->D = D; +} + +void +pgm_md5_process_bytes ( + struct pgm_md5_t* restrict ctx, + const void* restrict buffer, + size_t len + ) +{ +/* pre-conditions */ + if (len > 0) { + pgm_assert (NULL != buffer); + } + pgm_assert (NULL != ctx); + + if (len >= 64) + { +#ifndef _STRING_ARCH_unaligned +/* To check alignment gcc has an appropriate operator. Other + compilers don't. */ +# if __GNUC__ >= 2 +# define UNALIGNED_P(p) (((uintptr_t)p) % __alignof__ (uint32_t) != 0) +# else +# define UNALIGNED_P(p) (((uintptr_t)p) % sizeof(uint32_t) != 0) +# endif + if (UNALIGNED_P (buffer)) + while (len > 64) + { + _pgm_md5_process_block (ctx, memcpy (ctx->buffer, buffer, 64), 64); + buffer = (const char*)buffer + 64; + len -= 64; + } + else +#endif + { + _pgm_md5_process_block (ctx, buffer, len & ~63); + buffer = (const char*)buffer + (len & ~63); + len &= 63; + } + } + +/* Move remaining bytes in internal buffer. */ + if (len > 0) + { + size_t left_over = ctx->buflen; + + memcpy (&ctx->buffer[left_over], buffer, len); + left_over += len; + if (left_over >= 64) + { + _pgm_md5_process_block (ctx, ctx->buffer, 64); + left_over -= 64; + memcpy (ctx->buffer, &ctx->buffer[64], left_over); + } + ctx->buflen = left_over; + } +} + +/* Put result from CTX in first 16 bytes following RESBUF. The result + must be in little endian byte order. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ + +static +void* +_pgm_md5_read_ctx ( + const struct pgm_md5_t* restrict ctx, + void* restrict resbuf + ) +{ +/* pre-conditions */ + pgm_assert (NULL != ctx); + pgm_assert (NULL != resbuf); + + ((uint32_t*)resbuf)[0] = SWAP (ctx->A); + ((uint32_t*)resbuf)[1] = SWAP (ctx->B); + ((uint32_t*)resbuf)[2] = SWAP (ctx->C); + ((uint32_t*)resbuf)[3] = SWAP (ctx->D); + + return resbuf; +} + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ + +void* +pgm_md5_finish_ctx ( + struct pgm_md5_t* restrict ctx, + void* restrict resbuf + ) +{ +/* pre-conditions */ + pgm_assert (NULL != ctx); + pgm_assert (NULL != resbuf); + +/* Take yet unprocessed bytes into account. */ + const uint32_t bytes = ctx->buflen; + size_t pad; + +/* Now count remaining bytes. */ + ctx->total[0] += bytes; + if (ctx->total[0] < bytes) + ++ctx->total[1]; + + pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; + memcpy (&ctx->buffer[bytes], fillbuf, pad); + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + *(uint32_t*) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3); + *(uint32_t*) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) | + (ctx->total[0] >> 29)); + +/* Process last bytes. */ + _pgm_md5_process_block (ctx, ctx->buffer, bytes + pad + 8); + + return _pgm_md5_read_ctx (ctx, resbuf); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/md5.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/md5.c.c89.patch new file mode 100644 index 0000000..14d8971 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/md5.c.c89.patch @@ -0,0 +1,34 @@ +--- md5.c 2010-05-21 11:32:21.000000000 +0800 ++++ md5.c89 2010-08-04 12:19:52.000000000 +0800 +@@ -92,6 +92,7 @@ + pgm_assert (len > 0); + pgm_assert (NULL != ctx); + ++ { + uint32_t correct_words[16]; + const uint32_t *words = buffer; + const size_t nwords = len / sizeof (uint32_t); +@@ -243,6 +244,7 @@ + ctx->B = B; + ctx->C = C; + ctx->D = D; ++ } + } + + void +@@ -343,6 +345,7 @@ + pgm_assert (NULL != resbuf); + + /* Take yet unprocessed bytes into account. */ ++ { + const uint32_t bytes = ctx->buflen; + size_t pad; + +@@ -363,6 +366,7 @@ + _pgm_md5_process_block (ctx, ctx->buffer, bytes + pad + 8); + + return _pgm_md5_read_ctx (ctx, resbuf); ++ } + } + + /* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/md5_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/md5_unittest.c new file mode 100644 index 0000000..836af1f --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/md5_unittest.c @@ -0,0 +1,189 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for MD5 hashing (not actual algorithm). + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* mock state */ + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + + +#define MD5_DEBUG +#include "md5.c" + + +/* target: + * void + * pgm_md5_init_ctx ( + * struct pgm_md5_t* ctx + * ) + */ + +START_TEST (test_init_ctx_pass_001) +{ + struct pgm_md5_t ctx; + memset (&ctx, 0, sizeof(ctx)); + pgm_md5_init_ctx (&ctx); +} +END_TEST + +START_TEST (test_init_ctx_fail_001) +{ + pgm_md5_init_ctx (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_md5_process_bytes ( + * struct pgm_md5_t* ctx, + * const void* buffer, + * size_t len + * ) + */ + +START_TEST (test_process_bytes_pass_001) +{ + const char buffer[] = "i am not a string."; + struct pgm_md5_t ctx; + memset (&ctx, 0, sizeof(ctx)); + pgm_md5_init_ctx (&ctx); + pgm_md5_process_bytes (&ctx, buffer, sizeof(buffer)); +} +END_TEST + +START_TEST (test_process_bytes_fail_001) +{ + const char buffer[] = "i am not a string."; + pgm_md5_process_bytes (NULL, buffer, sizeof(buffer)); +} +END_TEST + +/* target: + * void* + * pgm_md5_finish_ctx ( + * struct pgm_md5_t* ctx, + * void* resbuf + * ) + */ + +START_TEST (test_finish_ctx_pass_001) +{ + const char* buffer = "i am not a string."; + const char* answer = "ef71-1617-4eef-9737-5e2b-5d7a-d015-b064"; + + char md5[1024]; + char resblock[16]; + struct pgm_md5_t ctx; + memset (&ctx, 0, sizeof(ctx)); + memset (resblock, 0, sizeof(resblock)); + pgm_md5_init_ctx (&ctx); + pgm_md5_process_bytes (&ctx, buffer, strlen(buffer)+1); + pgm_md5_finish_ctx (&ctx, resblock); + sprintf (md5, "%02.2hhx%02.2hhx-%02.2hhx%02.2hhx-%02.2hhx%02.2hhx-%02.2hhx%02.2hhx-%02.2hhx%02.2hhx-%02.2hhx%02.2hhx-%02.2hhx%02.2hhx-%02.2hhx%02.2hhx", + resblock[0], resblock[1], + resblock[2], resblock[3], + resblock[4], resblock[5], + resblock[6], resblock[7], + resblock[8], resblock[9], + resblock[10], resblock[11], + resblock[12], resblock[13], + resblock[14], resblock[15]); + g_message ("md5: %s", md5); + + fail_unless (0 == strcmp (md5, answer), "md5 mismatch"); +} +END_TEST + +START_TEST (test_finish_ctx_fail_001) +{ + char resblock[16]; + pgm_md5_finish_ctx (NULL, resblock); + fail ("reached"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_init_ctx = tcase_create ("init-ctx"); + suite_add_tcase (s, tc_init_ctx); + tcase_add_test (tc_init_ctx, test_init_ctx_pass_001); + tcase_add_test_raise_signal (tc_init_ctx, test_init_ctx_fail_001, SIGABRT); + + TCase* tc_process_bytes = tcase_create ("process_bytes"); + suite_add_tcase (s, tc_process_bytes); + tcase_add_test (tc_process_bytes, test_process_bytes_pass_001); + tcase_add_test_raise_signal (tc_process_bytes, test_process_bytes_fail_001, SIGABRT); + + TCase* tc_finish_ctx = tcase_create ("finish-ctx"); + suite_add_tcase (s, tc_finish_ctx); + tcase_add_test (tc_finish_ctx, test_finish_ctx_pass_001); + tcase_add_test_raise_signal (tc_finish_ctx, test_finish_ctx_fail_001, SIGABRT); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/mem.c b/3rdparty/openpgm-svn-r1135/pgm/mem.c new file mode 100644 index 0000000..ac98fe6 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/mem.c @@ -0,0 +1,250 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable fail fast memory allocation. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#ifdef _WIN32 +# define strcasecmp stricmp +#endif +#include +#include + + +//#define MEM_DEBUG + + +/* globals */ + +bool pgm_mem_gc_friendly PGM_GNUC_READ_MOSTLY = FALSE; + + +/* locals */ + +struct pgm_debug_key_t { + const char* key; + unsigned value; +}; +typedef struct pgm_debug_key_t pgm_debug_key_t; + +static volatile uint32_t mem_ref_count = 0; + + +static +bool +debug_key_matches ( + const char* restrict key, + const char* restrict token, + unsigned length + ) +{ + for (; length; length--, key++, token++) + { + const char k = (*key == '_') ? '-' : tolower (*key ); + const char t = (*token == '_') ? '-' : tolower (*token); + if (k != t) + return FALSE; + } + return *key == '\0'; +} + +static +unsigned +pgm_parse_debug_string ( + const char* restrict string, + const pgm_debug_key_t* restrict keys, + const unsigned nkeys + ) +{ + unsigned result = 0; + + if (NULL == string) + return result; + + if (!strcasecmp (string, "all")) + { + for (unsigned i = 0; i < nkeys; i++) + result |= keys[i].value; + } + else if (!strcasecmp (string, "help")) + { + fprintf (stderr, "Supported debug values:"); + for (unsigned i = 0; i < nkeys; i++) + fprintf (stderr, " %s", keys[i].key); + fprintf (stderr, "\n"); + } + else + { + while (string) { + const char* q = strpbrk (string, ":;, \t"); + if (!q) + q = string + strlen (string); + for (unsigned i = 0; i < nkeys; i++) + if (debug_key_matches (keys[i].key, string, q - string)) + result |= keys[i].value; + string = q; + if (*string) + string++; + } + } + return result; +} + +void +pgm_mem_init (void) +{ + static const pgm_debug_key_t keys[] = { + { "gc-friendly", 1 }, + }; + + if (pgm_atomic_exchange_and_add32 (&mem_ref_count, 1) > 0) + return; + + const char *val = getenv ("PGM_DEBUG"); + const unsigned flags = !val ? 0 : pgm_parse_debug_string (val, keys, PGM_N_ELEMENTS (keys)); + if (flags & 1) + pgm_mem_gc_friendly = TRUE; +} + +void +pgm_mem_shutdown (void) +{ + pgm_return_if_fail (pgm_atomic_read32 (&mem_ref_count) > 0); + + if (pgm_atomic_exchange_and_add32 (&mem_ref_count, (uint32_t)-1) != 1) + return; + + /* nop */ +} + +/* malloc wrappers to hard fail */ +void* +pgm_malloc ( + const size_t n_bytes + ) +{ + if (PGM_LIKELY (n_bytes)) + { + void* mem = malloc (n_bytes); + if (mem) + return mem; + + pgm_fatal ("file %s: line %d (%s): failed to allocate %zu bytes", + __FILE__, __LINE__, __PRETTY_FUNCTION__, + n_bytes); + abort (); + } + return NULL; +} + +#define SIZE_OVERFLOWS(a,b) (PGM_UNLIKELY ((a) > SIZE_MAX / (b))) + +void* +pgm_malloc_n ( + const size_t n_blocks, + const size_t block_bytes + ) +{ + if (SIZE_OVERFLOWS (n_blocks, block_bytes)) { + pgm_fatal ("file %s: line %d (%s): overflow allocating %zu*%zu bytes", + __FILE__, __LINE__, __PRETTY_FUNCTION__, + n_blocks, block_bytes); + } + return pgm_malloc (n_blocks * block_bytes); +} + +void* +pgm_malloc0 ( + const size_t n_bytes + ) +{ + if (PGM_LIKELY (n_bytes)) + { + void* mem = calloc (1, n_bytes); + if (mem) + return mem; + + pgm_fatal ("file %s: line %d (%s): failed to allocate %zu bytes", + __FILE__, __LINE__, __PRETTY_FUNCTION__, + n_bytes); + abort (); + } + return NULL; +} + +void* +pgm_malloc0_n ( + const size_t n_blocks, + const size_t block_bytes + ) +{ + if (PGM_LIKELY (n_blocks && block_bytes)) + { + void* mem = calloc (n_blocks, block_bytes); + if (mem) + return mem; + + pgm_fatal ("file %s: line %d (%s): failed to allocate %zu*%zu bytes", + __FILE__, __LINE__, __PRETTY_FUNCTION__, + n_blocks, block_bytes); + abort (); + } + return NULL; +} + +void* +pgm_memdup ( + const void* mem, + const size_t n_bytes + ) +{ + void* new_mem; + + if (PGM_LIKELY (NULL != mem)) + { + new_mem = pgm_malloc (n_bytes); + memcpy (new_mem, mem, n_bytes); + } + else + new_mem = NULL; + + return new_mem; +} + +void* +pgm_realloc ( + void* mem, + const size_t n_bytes + ) +{ + return realloc (mem, n_bytes); +} + +void +pgm_free ( + void* mem + ) +{ + if (PGM_LIKELY (NULL != mem)) + free (mem); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/mem.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/mem.c.c89.patch new file mode 100644 index 0000000..5673af3 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/mem.c.c89.patch @@ -0,0 +1,145 @@ +--- mem.c 2010-05-23 16:49:21.000000000 +0800 ++++ mem.c89 2010-08-05 12:19:31.000000000 +0800 +@@ -78,16 +78,24 @@ + if (NULL == string) + return result; + ++/* C++ warning completely irrelevant for C */ ++#pragma warning( disable : 4996 ) + if (!strcasecmp (string, "all")) + { +- for (unsigned i = 0; i < nkeys; i++) ++ { ++ unsigned i; ++ for (i = 0; i < nkeys; i++) + result |= keys[i].value; ++ } + } + else if (!strcasecmp (string, "help")) + { + fprintf (stderr, "Supported debug values:"); +- for (unsigned i = 0; i < nkeys; i++) ++ { ++ unsigned i; ++ for (i = 0; i < nkeys; i++) + fprintf (stderr, " %s", keys[i].key); ++ } + fprintf (stderr, "\n"); + } + else +@@ -96,14 +104,18 @@ + const char* q = strpbrk (string, ":;, \t"); + if (!q) + q = string + strlen (string); +- for (unsigned i = 0; i < nkeys; i++) ++ { ++ unsigned i; ++ for (i = 0; i < nkeys; i++) + if (debug_key_matches (keys[i].key, string, q - string)) + result |= keys[i].value; ++ } + string = q; + if (*string) + string++; + } + } ++#pragma warning( default : 4996 ) + return result; + } + +@@ -117,10 +129,27 @@ + if (pgm_atomic_exchange_and_add32 (&mem_ref_count, 1) > 0) + return; + ++ { ++#ifdef _MSC_VER ++/* _dupenv_s is recommended replacement for getenv which is not thread-safe */ ++ char *val; ++ size_t len; ++ errno_t err; ++ unsigned flags; ++ err = _dupenv_s (&val, &len, "PGM_DEBUG"); ++ if (err || 0 == len) { ++ flags = 0; ++ } else { ++ flags = pgm_parse_debug_string (val, keys, PGM_N_ELEMENTS (keys)); ++ free (val); ++ } ++#else + const char *val = getenv ("PGM_DEBUG"); + const unsigned flags = !val ? 0 : pgm_parse_debug_string (val, keys, PGM_N_ELEMENTS (keys)); ++#endif + if (flags & 1) + pgm_mem_gc_friendly = TRUE; ++ } + } + + void +@@ -146,9 +175,15 @@ + if (mem) + return mem; + +- pgm_fatal ("file %s: line %d (%s): failed to allocate %zu bytes", ++#ifdef __PRETTY_FUNCTION__ ++ pgm_fatal ("file %s: line %d (%s): failed to allocate %lu bytes", + __FILE__, __LINE__, __PRETTY_FUNCTION__, + n_bytes); ++#else ++ pgm_fatal ("file %s: line %d): failed to allocate %lu bytes", ++ __FILE__, __LINE__, ++ n_bytes); ++#endif + abort (); + } + return NULL; +@@ -163,9 +198,15 @@ + ) + { + if (SIZE_OVERFLOWS (n_blocks, block_bytes)) { +- pgm_fatal ("file %s: line %d (%s): overflow allocating %zu*%zu bytes", ++#ifdef __PRETTY_FUNCTION__ ++ pgm_fatal ("file %s: line %d (%s): overflow allocating %lu*%lu bytes", + __FILE__, __LINE__, __PRETTY_FUNCTION__, + n_blocks, block_bytes); ++#else ++ pgm_fatal ("file %s: line %d: overflow allocating %lu*%lu bytes", ++ __FILE__, __LINE__, ++ n_blocks, block_bytes); ++#endif + } + return pgm_malloc (n_blocks * block_bytes); + } +@@ -181,9 +222,15 @@ + if (mem) + return mem; + +- pgm_fatal ("file %s: line %d (%s): failed to allocate %zu bytes", ++#ifdef __PRETTY_FUNCTION__ ++ pgm_fatal ("file %s: line %d (%s): failed to allocate %lu bytes", + __FILE__, __LINE__, __PRETTY_FUNCTION__, + n_bytes); ++#else ++ pgm_fatal ("file %s: line %d: failed to allocate %lu bytes", ++ __FILE__, __LINE__, ++ n_bytes); ++#endif + abort (); + } + return NULL; +@@ -201,9 +248,15 @@ + if (mem) + return mem; + +- pgm_fatal ("file %s: line %d (%s): failed to allocate %zu*%zu bytes", ++#ifdef __PRETTY_FUNCTION__ ++ pgm_fatal ("file %s: line %d (%s): failed to allocate %lu*%lu bytes", + __FILE__, __LINE__, __PRETTY_FUNCTION__, + n_blocks, block_bytes); ++#else ++ pgm_fatal ("file %s: line %d: failed to allocate %lu*%lu bytes", ++ __FILE__, __LINE__, ++ n_blocks, block_bytes); ++#endif + abort (); + } + return NULL; diff --git a/3rdparty/openpgm-svn-r1135/pgm/memcheck b/3rdparty/openpgm-svn-r1135/pgm/memcheck new file mode 100755 index 0000000..fbfe59c --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/memcheck @@ -0,0 +1,13 @@ +#!/bin/sh + +G_SLICE=always-malloc \ +G_DEBUG=gc-friendly \ + valgrind \ + -v \ + --tool=memcheck \ + --leak-check=full \ + --num-callers=40 \ + --gen-suppressions=no \ + --show-reachable=yes \ + --suppressions=valgrind.supp \ + $* diff --git a/3rdparty/openpgm-svn-r1135/pgm/messages.c b/3rdparty/openpgm-svn-r1135/pgm/messages.c new file mode 100644 index 0000000..2c13662 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/messages.c @@ -0,0 +1,173 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * basic message reporting. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + + +/* globals */ + +/* bit mask for trace role modules */ +int pgm_log_mask PGM_GNUC_READ_MOSTLY = 0xffff; +int pgm_min_log_level PGM_GNUC_READ_MOSTLY = PGM_LOG_LEVEL_NORMAL; + + +/* locals */ + +static const char log_levels[8][7] = { + "Uknown", + "Debug", + "Trace", + "Minor", + "Info", + "Warn", + "Error", + "Fatal" +}; + +static volatile uint32_t messages_ref_count = 0; +static pgm_mutex_t messages_mutex; +static pgm_log_func_t log_handler PGM_GNUC_READ_MOSTLY = NULL; +static void* log_handler_closure PGM_GNUC_READ_MOSTLY = NULL; + +static inline const char* log_level_text (const int) PGM_GNUC_PURE; + + +static inline +const char* +log_level_text ( + const int log_level + ) +{ + switch (log_level) { + default: return log_levels[0]; + case PGM_LOG_LEVEL_DEBUG: return log_levels[1]; + case PGM_LOG_LEVEL_TRACE: return log_levels[2]; + case PGM_LOG_LEVEL_MINOR: return log_levels[3]; + case PGM_LOG_LEVEL_NORMAL: return log_levels[4]; + case PGM_LOG_LEVEL_WARNING: return log_levels[5]; + case PGM_LOG_LEVEL_ERROR: return log_levels[6]; + case PGM_LOG_LEVEL_FATAL: return log_levels[7]; + } +} + +/* reference counted init and shutdown + */ + +void +pgm_messages_init (void) +{ + if (pgm_atomic_exchange_and_add32 (&messages_ref_count, 1) > 0) + return; + + pgm_mutex_init (&messages_mutex); + + const char* log_mask = getenv ("PGM_LOG_MASK"); + if (NULL != log_mask) { + unsigned int value = 0; + if (1 == sscanf (log_mask, "0x%4x", &value)) + pgm_log_mask = value; + } + const char *min_log_level = getenv ("PGM_MIN_LOG_LEVEL"); + if (NULL != min_log_level) { + switch (min_log_level[0]) { + case 'D': pgm_min_log_level = PGM_LOG_LEVEL_DEBUG; break; + case 'T': pgm_min_log_level = PGM_LOG_LEVEL_TRACE; break; + case 'M': pgm_min_log_level = PGM_LOG_LEVEL_MINOR; break; + case 'N': pgm_min_log_level = PGM_LOG_LEVEL_NORMAL; break; + case 'W': pgm_min_log_level = PGM_LOG_LEVEL_WARNING; break; + case 'E': pgm_min_log_level = PGM_LOG_LEVEL_ERROR; break; + case 'F': pgm_min_log_level = PGM_LOG_LEVEL_FATAL; break; + default: break; + } + } +} + +void +pgm_messages_shutdown (void) +{ + pgm_return_if_fail (pgm_atomic_read32 (&messages_ref_count) > 0); + + if (pgm_atomic_exchange_and_add32 (&messages_ref_count, (uint32_t)-1) != 1) + return; + + pgm_mutex_free (&messages_mutex); +} + +/* set application handler for log messages, returns previous value, + * default handler value is NULL. + */ + +pgm_log_func_t +pgm_log_set_handler ( + pgm_log_func_t handler, + void* closure + ) +{ + pgm_log_func_t previous_handler; + pgm_mutex_lock (&messages_mutex); + previous_handler = log_handler; + log_handler = handler; + log_handler_closure = closure; + pgm_mutex_unlock (&messages_mutex); + return previous_handler; +} + +void +pgm__log ( + const int log_level, + const char* format, + ... + ) +{ + va_list args; + + va_start (args, format); + pgm__logv (log_level, format, args); + va_end (args); +} + +void +pgm__logv ( + const int log_level, + const char* format, + va_list args + ) +{ + char tbuf[ 1024 ]; + + pgm_mutex_lock (&messages_mutex); + const int offset = sprintf (tbuf, "%s: ", log_level_text (log_level)); + vsnprintf (tbuf+offset, sizeof(tbuf)-offset, format, args); + tbuf[ sizeof(tbuf) ] = '\0'; + if (log_handler) + log_handler (log_level, tbuf, log_handler_closure); + else { +/* ignore return value */ + write (STDOUT_FILENO, tbuf, strlen (tbuf)); + write (STDOUT_FILENO, "\n", 1); + } + + pgm_mutex_unlock (&messages_mutex); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/messages.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/messages.c.c89.patch new file mode 100644 index 0000000..157bd40 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/messages.c.c89.patch @@ -0,0 +1,91 @@ +--- messages.c 2010-05-23 16:50:35.000000000 +0800 ++++ messages.c89 2010-08-05 12:18:49.000000000 +0800 +@@ -19,6 +19,9 @@ + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + ++#ifdef _MSC_VER ++# include ++#endif + #include + #include + #include +@@ -81,14 +84,37 @@ + + pgm_mutex_init (&messages_mutex); + ++ { ++#ifdef _MSC_VER ++ char* log_mask; ++ size_t len; ++ errno_t err = _dupenv_s (&log_mask, &len, "PGM_LOG_MASK"); ++ if (!err && len > 0) { ++ unsigned int value = 0; ++ if (1 == sscanf_s (log_mask, "0x%4x", &value)) ++ pgm_log_mask = value; ++ free (log_mask); ++ } ++#else + const char* log_mask = getenv ("PGM_LOG_MASK"); + if (NULL != log_mask) { + unsigned int value = 0; + if (1 == sscanf (log_mask, "0x%4x", &value)) + pgm_log_mask = value; + } ++#endif ++ } ++ { ++#ifdef _MSC_VER ++ char *min_log_level; ++ size_t len; ++ errno_t err = _dupenv_s (&min_log_level, &len, "PGM_MIN_LOG_LEVEL"); ++ if (!err && len > 0) ++#else + const char *min_log_level = getenv ("PGM_MIN_LOG_LEVEL"); +- if (NULL != min_log_level) { ++ if (NULL != min_log_level) ++#endif ++ { + switch (min_log_level[0]) { + case 'D': pgm_min_log_level = PGM_LOG_LEVEL_DEBUG; break; + case 'T': pgm_min_log_level = PGM_LOG_LEVEL_TRACE; break; +@@ -99,6 +125,10 @@ + case 'F': pgm_min_log_level = PGM_LOG_LEVEL_FATAL; break; + default: break; + } ++#ifdef _MSC_VER ++ free (min_log_level); ++#endif ++ } + } + } + +@@ -156,15 +186,28 @@ + char tbuf[ 1024 ]; + + pgm_mutex_lock (&messages_mutex); ++ { ++#ifdef _MSC_VER ++ const int offset = sprintf_s (tbuf, sizeof(tbuf), "%s: ", log_level_text (log_level)); ++ vsnprintf_s (tbuf+offset, sizeof(tbuf)-offset, _TRUNCATE, format, args); ++#else + const int offset = sprintf (tbuf, "%s: ", log_level_text (log_level)); + vsnprintf (tbuf+offset, sizeof(tbuf)-offset, format, args); + tbuf[ sizeof(tbuf) ] = '\0'; ++#endif ++ } + if (log_handler) + log_handler (log_level, tbuf, log_handler_closure); + else { + /* ignore return value */ ++#ifdef _MSC_VER ++ const int stdoutfd = _fileno (stdout); ++ _write (stdoutfd, tbuf, strlen (tbuf)); ++ _write (stdoutfd, "\n", 1); ++#else + write (STDOUT_FILENO, tbuf, strlen (tbuf)); + write (STDOUT_FILENO, "\n", 1); ++#endif + } + + pgm_mutex_unlock (&messages_mutex); diff --git a/3rdparty/openpgm-svn-r1135/pgm/mibs/PGM-MIB-petrova-01.txt b/3rdparty/openpgm-svn-r1135/pgm/mibs/PGM-MIB-petrova-01.txt new file mode 100644 index 0000000..28ff72c --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/mibs/PGM-MIB-petrova-01.txt @@ -0,0 +1,5459 @@ +---------------------------------------------------------------- +-- +-- Pragmatic General Multicast (PGM) MIB +-- +---------------------------------------------------------------- +-- +-- +-- Full MIB for the PGM protocol incorporating Network Element +-- (router), source, receiver and DLR functionality +-- +-- extracted from draft-petrova-pgmmib-01.txt + +PGM-MIB DEFINITIONS ::= BEGIN +IMPORTS + OBJECT-TYPE, Counter32, Integer32, Unsigned32, NOTIFICATION-TYPE, + MODULE-IDENTITY, IpAddress, TimeTicks, experimental, BITS + FROM SNMPv2-SMI + MODULE-COMPLIANCE, OBJECT-GROUP, NOTIFICATION-GROUP + FROM SNMPv2-CONF + InterfaceIndex + FROM IF-MIB; + +pgmMIB MODULE-IDENTITY + LAST-UPDATED "200205010000Z" + ORGANIZATION + "Cisco Systems + Tibco Software Inc + Nortel Networks" + CONTACT-INFO + " Richard Edmonstone + redmonst@cisco.com + +44 131 561 3621 + Cisco Systems, Inc. + 170 West Tasman Drive, + San Jose, CA 95134 + USA + + Rajiv Raghunarayan + raraghun@cisco.com + +91 80 532 1300 + Cisco Systems, Inc. + 170 West Tasman Drive, + San Jose, CA 95134 + USA + + Devendra Raut + draut@nortelnetworks.com + (408)495-2859 + Nortel Networks + 4401 Great America Parkway, + Santa Clara, CA 95052 + + Moses Sun + mosun@nortelnetworks.com + (979)694-7156 + Nortel Networks + 4401 Great America Parkway + Santa Clara, CA, + USA + + Todd L. Montgomery + tmontgomery@tibco.com + (304)291-5972 + Tibco Software Inc. + 29W110 Butterfield Rd, Suite 205 + Warrenville, IL 60555 + USA + + Michael Garwood + mgarwood@tibco.com + (630)393-7363 ext.275 + Tibco Software Inc. + 29W110 Butterfield Rd, Suite 205 + Warrenville, IL 60555 + USA + + Luna Petrova + lpetrova@tibco.com + (630)393-7363 ext.330 + Tibco Software Inc. + 29W110 Butterfield Rd, Suite 205 + Warrenville, IL 60555 + USA" + DESCRIPTION + "The MIB module for managing PGM implementations." + REVISION "200205010000Z" + DESCRIPTION + "Rev 2.0: SNMP Notifications added to the MIB." + ::= { experimental 112 } -- assigned by IANA. + +pgm OBJECT IDENTIFIER ::= { pgmMIB 1 } +pgmNetworkElement OBJECT IDENTIFIER ::= { pgm 1 } +pgmSource OBJECT IDENTIFIER ::= { pgm 2 } +pgmReceiver OBJECT IDENTIFIER ::= { pgm 3 } +pgmDLR OBJECT IDENTIFIER ::= { pgm 4 } +pgmNotificationPrefix OBJECT IDENTIFIER ::= { pgmMIB 2 } + +-- PGM Network Element + +pgmNeEnable OBJECT-TYPE + SYNTAX INTEGER { + enable(1), + disable(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Enable/Disable Parameter indicates whether + this PGM operation is enabled or disabled." + DEFVAL { enable } + ::= { pgmNetworkElement 1 } + +pgmNeSessionLifeTime OBJECT-TYPE + SYNTAX Unsigned32(0..2147483647) + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The length of the idle time (seconds) following + which a PGM session will be aged out. An idle PGM + session means there is no SPM message received + from the upstream. + Value of 0 indicates no timeout." + DEFVAL { 300 } + ::= { pgmNetworkElement 2 } + +pgmNeMaxReXmitStates OBJECT-TYPE + SYNTAX Integer32(-2..2147483647) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The Maximum number of retransmission state entries. + The value of -1 means network element has no + limitation. + The value of -2 means not supported by this + implementation." + ::= { pgmNetworkElement 3 } + +pgmNeMaxSessions OBJECT-TYPE + SYNTAX Integer32(-2..2147483647) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The maximum number of state sessions supported. + The value of -1 means network element has no + limitation. + The value of -2 means not supported by this + implementation." + ::= { pgmNetworkElement 4 } + +-- The PGM NE Network Interface + +-- The PGM NE Network Interface tables contain +-- per-interface information about the PGM protocol. +-- The information is grouped into three major categories: +-- fault, configuration and performance management. + +pgmNeInterface OBJECT IDENTIFIER ::= { pgmNetworkElement 100 } + +pgmNeTotalInterfacesNumberOfEntries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of entries in the PGM Interface + table." + ::= { pgmNeInterface 1 } + +-- The PGM NE Network Interface configuration table + +pgmNeIfConfigTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmNeIfConfigEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per interface configuration + information relating to PGM Network Element + operation." + ::= {pgmNeInterface 3} + +pgmNeIfConfigEntry OBJECT-TYPE + SYNTAX PgmNeIfConfigEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per Interface Configuration Information." + INDEX { pgmNeIfConfigIndex } + ::= { pgmNeIfConfigTable 1 } + +PgmNeIfConfigEntry ::= SEQUENCE { + pgmNeIfConfigIndex + InterfaceIndex, + pgmNeIfPgmEnable + INTEGER, + pgmNeIfNakRptInterval + Unsigned32, + pgmNeIfNakRptRate + Unsigned32, + pgmNeIfNakRdataInterval + Unsigned32, + pgmNeIfNakEliminateInterval + Unsigned32 + } + +pgmNeIfConfigIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A unique value for each interface. Its value + ranges between 1 and the value of ifNumber. The + value for each interface must remain constant at + least from one re-initialization of the entity's + network management system to the next + re-initialization." + ::= { pgmNeIfConfigEntry 1 } + +pgmNeIfPgmEnable OBJECT-TYPE + SYNTAX INTEGER { + enable(1), + disable(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Allows PGM to be enabled and disabled per + Network Interface. + + PGM can be enabled or disabled per Network + Interface, only if PGM is enabled for this + Network Element." + ::= { pgmNeIfConfigEntry 2 } + +pgmNeIfNakRptInterval OBJECT-TYPE + SYNTAX Unsigned32(1..4294967295) + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The length of time (milliseconds) for which a + network element will repeat a NAK while waiting + for a corresponding NCF. This interval is counted + down from the transmission of a NAK." + DEFVAL { 100 } + ::= { pgmNeIfConfigEntry 3 } + +pgmNeIfNakRptRate OBJECT-TYPE + SYNTAX Unsigned32(1..4294967295) + UNITS "number of NAKs per second" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The rate at which NAKs are repeated." + DEFVAL { 2 } + ::= { pgmNeIfConfigEntry 4 } + +pgmNeIfNakRdataInterval OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The length of time (milliseconds) for which + a network element will wait for the + corresponding RDATA. This interval is counted + down from the time a matching NCF is received. + This value must be greater than the + pgmNeIfNakEliminateInterval." + DEFVAL { 10000 } + ::= { pgmNeIfConfigEntry 5 } + +pgmNeIfNakEliminateInterval OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The length of time (milliseconds) for which + a network element will eliminate NAKs for + a specific TSI/SQN. This interval is counted + down from the time the first NAK is + established. This value must + be smaller than pgmNeIfNakRdataInterval." + DEFVAL { 5000 } + ::= { pgmNeIfConfigEntry 6 } + +-- The PGM NE Interface performance table. +-- This is primarily statistical information +-- about packets received and sent on the interface + +pgmNeIfPerformanceTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmNeIfPerformanceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per interface performance + information related to PGM Network Element + operation." + ::= {pgmNeInterface 4} + +pgmNeIfPerformanceEntry OBJECT-TYPE + SYNTAX PgmNeIfPerformanceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per Interface Information for Network Elements." + INDEX { pgmNeIfPerformanceIndex } + ::= { pgmNeIfPerformanceTable 1 } + +PgmNeIfPerformanceEntry ::= SEQUENCE { + pgmNeIfPerformanceIndex + InterfaceIndex, + pgmNeIfReXmitStates + Counter32, + pgmNeIfReXmitTimedOut + Counter32, + pgmNeIfInSpms + Counter32, + pgmNeIfOutSpms + Counter32, + pgmNeIfInParitySpms + Counter32, + pgmNeIfOutParitySpms + Counter32, + pgmNeIfInRdata + Counter32, + pgmNeIfOutRdata + Counter32, + pgmNeIfInParityRdata + Counter32, + pgmNeIfOutParityRdata + Counter32, + pgmNeIfInRdataNoSessionErrors + Counter32, + pgmNeIfUniqueNaks + Counter32, + pgmNeIfInNaks + Counter32, + pgmNeIfOutNaks + Counter32, + pgmNeIfUniqueParityNaks + Counter32, + pgmNeIfInParityNaks + Counter32, + pgmNeIfOutParityNaks + Counter32, + pgmNeIfInNakNoSessionErrors + Counter32, + pgmNeIfInNakSeqErrors + Counter32, + pgmNeIfInParityNakTgErrors + Counter32, + pgmNeIfInNnaks + Counter32, + pgmNeIfOutNnaks + Counter32, + pgmNeIfInParityNnaks + Counter32, + pgmNeIfOutParityNnaks + Counter32, + pgmNeIfInNnakNoSessionErrors + Counter32, + pgmNeIfInNcfs + Counter32, + pgmNeIfOutNcfs + Counter32, + pgmNeIfInParityNcfs + Counter32, + pgmNeIfOutParityNcfs + Counter32, + pgmNeIfInNcfNoSessionErrors + Counter32, + pgmNeIfInRedirectNcfs + Counter32, + pgmNeIfMalformed + Counter32, + pgmNeIfSpmFromSource + Counter32, + pgmNeIfSpmBadSqn + Counter32, + pgmNeIfSpmError + Counter32, + pgmNeIfPollRandomIgnore + Counter32, + pgmNeIfPollTsiStateError + Counter32, + pgmNeIfPollParentError + Counter32, + pgmNeIfPollTypeError + Counter32, + pgmNeIfPollError + Counter32, + pgmNeIfPollSuccess + Counter32, + pgmNeIfPollOriginated + Counter32, + pgmNeIfPolrNoState + Counter32, + pgmNeIfPolrError + Counter32, + pgmNeIfPolrParityError + Counter32, + pgmNeIfPolrSuccess + Counter32, + pgmNeIfPolrOriginated + Counter32, + pgmNeIfNcfError + Counter32, + pgmNeIfNcfParityError + Counter32, + pgmNeIfNcfPartialParity + Counter32, + pgmNeIfNcfReceived + Counter32, + pgmNeIfNcfAnticipated + Counter32, + pgmNeIfNcfRedirecting + Counter32, + pgmNeIfNakEliminated + Counter32, + pgmNeIfNakError + Counter32, + pgmNeIfNakParityError + Counter32, + pgmNeIfNNakEliminated + Counter32, + pgmNeIfNNakError + Counter32, + pgmNeIfNNakParityError + Counter32, + pgmNeIfNNakCongestionReports + Counter32, + pgmNeIfNakRetryExpired + Counter32, + pgmNeIfNakRetryExpiredDLR + Counter32, + pgmNeIfNakForwardedDLR + Counter32, + pgmNeIfNakRetransmitted + Counter32, + pgmNeIfRdataEliminatedOIF + Counter32, + pgmNeIfRdataEliminatedSqn + Counter32, + pgmNeIfInRdataFragments + Counter32, + pgmNeIfRdataFragmentsNoSessionErrors + Counter32, + pgmNeIfRdataFragmentsEliminatedOIF + Counter32, + pgmNeIfRdataFragmentsEliminatedSqn + Counter32, + pgmNeIfOutRdataFragments + Counter32 +} + +pgmNeIfPerformanceIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A unique value for each interface. Its value + ranges between 1 and the value of ifNumber. + The value for each interface must remain + constant at least from one re-initialization + of the entity's network management system + to the next re-initialization." + ::= { pgmNeIfPerformanceEntry 1 } + +pgmNeIfReXmitStates OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total retransmit state entries for this + interface." + ::= { pgmNeIfPerformanceEntry 2 } + +pgmNeIfReXmitTimedOut OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of timed-out retransmit state + entries for this interface." + ::= { pgmNeIfPerformanceEntry 3 } + +pgmNeIfInSpms OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of SPMs received on the PGM + interface." + ::= { pgmNeIfPerformanceEntry 4 } + +pgmNeIfOutSpms OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of SPMs sent out from the PGM + interface." + ::= { pgmNeIfPerformanceEntry 5 } + +pgmNeIfInParitySpms OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity SPMs received on the + PGM interface." + ::= { pgmNeIfPerformanceEntry 6 } + +pgmNeIfOutParitySpms OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity SPMs sent out from the + PGM interface." + ::= { pgmNeIfPerformanceEntry 7 } + +pgmNeIfInRdata OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of RDATA received on the PGM + interface." + ::= { pgmNeIfPerformanceEntry 8 } + +pgmNeIfOutRdata OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of RDATA sent out from the + PGM interface." + ::= { pgmNeIfPerformanceEntry 9 } + +pgmNeIfInParityRdata OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity RDATA received on the + PGM interface." + ::= { pgmNeIfPerformanceEntry 10 } + +pgmNeIfOutParityRdata OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity RDATA sent out from + the PGM interface." + ::= { pgmNeIfPerformanceEntry 11 } + +pgmNeIfInRdataNoSessionErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received RDATA discarded because + of no session." + ::= { pgmNeIfPerformanceEntry 12 } + +pgmNeIfUniqueNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of unique NAKs received on + this interface." + ::= { pgmNeIfPerformanceEntry 13 } + +pgmNeIfInNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NAKs received on the PGM + interface." + ::= { pgmNeIfPerformanceEntry 14 } + +pgmNeIfOutNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NAKs sent out from the + PGM interface." + ::= { pgmNeIfPerformanceEntry 15 } + +pgmNeIfUniqueParityNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of unique parity NAKs received + on this interface." + ::= { pgmNeIfPerformanceEntry 16 } + +pgmNeIfInParityNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity NAKs received on the + PGM interface." + ::= { pgmNeIfPerformanceEntry 17 } + +pgmNeIfOutParityNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity NAKs sent out from + the PGM interface." + ::= { pgmNeIfPerformanceEntry 18 } + +pgmNeIfInNakNoSessionErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received NAKs discarded because of + no session." + ::= { pgmNeIfPerformanceEntry 19 } + +pgmNeIfInNakSeqErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received NAKs discarded because + of out of sequence (out of retransmit window)." + ::= { pgmNeIfPerformanceEntry 20 } + +pgmNeIfInParityNakTgErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received parity NAKs discarded + because out of parity TG window." + ::= { pgmNeIfPerformanceEntry 21 } + +pgmNeIfInNnaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NNAKs received on the PGM + interface." + ::= { pgmNeIfPerformanceEntry 22 } + +pgmNeIfOutNnaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NNAKs sent out from the + PGM interface." + ::= { pgmNeIfPerformanceEntry 23 } + +pgmNeIfInParityNnaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity NNAKs received on + the PGM interface." + ::= { pgmNeIfPerformanceEntry 24 } + +pgmNeIfOutParityNnaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity NNAKs sent out from + the PGM interface." + ::= { pgmNeIfPerformanceEntry 25 } + +pgmNeIfInNnakNoSessionErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received NNAKs discarded because + of no session." + ::= { pgmNeIfPerformanceEntry 26 } + +pgmNeIfInNcfs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NCFs received on the PGM + interface." + ::= { pgmNeIfPerformanceEntry 27 } + +pgmNeIfOutNcfs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NCFs sent out from the PGM + interface." + ::= { pgmNeIfPerformanceEntry 28 } + +pgmNeIfInParityNcfs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity NCFs received on the + PGM interface." + ::= { pgmNeIfPerformanceEntry 29 } + +pgmNeIfOutParityNcfs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity NCFs sent out from + the PGM interface." + ::= { pgmNeIfPerformanceEntry 30 } + +pgmNeIfInNcfNoSessionErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received NCFs discarded because + of no session." + ::= { pgmNeIfPerformanceEntry 31 } + +pgmNeIfInRedirectNcfs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of redirected NCFs received on the + PGM interface." + ::= { pgmNeIfPerformanceEntry 32 } + +pgmNeIfMalformed OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of malformed PGM packets." + ::= { pgmNeIfPerformanceEntry 33 } + +pgmNeIfSpmFromSource OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of SPM packets received from source." + ::= { pgmNeIfPerformanceEntry 34 } + +pgmNeIfSpmBadSqn OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of SPM packets discarded due to bad + SQN." + ::= { pgmNeIfPerformanceEntry 35 } + +pgmNeIfSpmError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of SPM packets discarded due to + operational error. Some examples of operational + errors are failure to create TSI state for SPM, + parity SPM for a TSI with no parity." + ::= { pgmNeIfPerformanceEntry 36 } + +pgmNeIfPollRandomIgnore OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of POLL packets not replied due to random + condition failing." + ::= { pgmNeIfPerformanceEntry 37 } + +pgmNeIfPollTsiStateError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of POLL packets discarded due to no + matching TSI state." + ::= { pgmNeIfPerformanceEntry 38 } + +pgmNeIfPollParentError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of POLL packets discarded due to + unknown parent." + ::= { pgmNeIfPerformanceEntry 39 } + +pgmNeIfPollTypeError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of POLL packets discarded due to failed + type matching." + ::= { pgmNeIfPerformanceEntry 40 } + +pgmNeIfPollError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of POLL packets discarded due to + operational error." + ::= { pgmNeIfPerformanceEntry 41 } + +pgmNeIfPollSuccess OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of successfully scheduled POLRs." + ::= { pgmNeIfPerformanceEntry 42 } + +pgmNeIfPollOriginated OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of polls originated on this interface." + ::= { pgmNeIfPerformanceEntry 43 } + +pgmNeIfPolrNoState OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of POLRs discarded due to no matching + state." + ::= { pgmNeIfPerformanceEntry 44 } + +pgmNeIfPolrError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of POLRs discarded due to operational + error." + ::= { pgmNeIfPerformanceEntry 45 } + +pgmNeIfPolrParityError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of parity POLRs received for non-parity + TSI." + ::= { pgmNeIfPerformanceEntry 46 } + +pgmNeIfPolrSuccess OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of POLRs recorded successfully." + ::= { pgmNeIfPerformanceEntry 47 } + +pgmNeIfPolrOriginated OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of POLRs originated by this interface." + ::= { pgmNeIfPerformanceEntry 48 } + +pgmNeIfNcfError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NCFs ignored due to no packet memory, + due to packet processing errors." + ::= { pgmNeIfPerformanceEntry 49 } + +pgmNeIfNcfParityError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NCFs ignored. Incremented when a parity + NCF is received on a session for which no parity + capability has been advertised in the session's + SPMs." + ::= { pgmNeIfPerformanceEntry 50 } + +pgmNeIfNcfPartialParity OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NCFs ignored due to not enough parity + blocks acknowledged." + ::= { pgmNeIfPerformanceEntry 51 } + +pgmNeIfNcfReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NCFs that confirm an outstanding NAK." + ::= { pgmNeIfPerformanceEntry 52 } + +pgmNeIfNcfAnticipated OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NCFs that cause NAK anticipation." + ::= { pgmNeIfPerformanceEntry 53 } + +pgmNeIfNcfRedirecting OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NCFs received as consequence of + redirected NAK." + ::= { pgmNeIfPerformanceEntry 54 } + +pgmNeIfNakEliminated OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAKs eliminated by retransmission + state." + ::= { pgmNeIfPerformanceEntry 55 } + +pgmNeIfNakError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of errors creating retransmission state + or NAK, due to NAK packet processing." + ::= { pgmNeIfPerformanceEntry 56 } + +pgmNeIfNakParityError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAKs ignored, due to no parity + available. Incremented when parity NAK is + received on this session, for which no parity + capability has been advartised." + ::= { pgmNeIfPerformanceEntry 57 } + +pgmNeIfNNakEliminated OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NNAKs eliminated by retransmission + state." + ::= { pgmNeIfPerformanceEntry 58 } + +pgmNeIfNNakError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of errors encountered creating + retransmission state OR nak." + ::= { pgmNeIfPerformanceEntry 59 } + +pgmNeIfNNakParityError OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of Null NAKs ignored, due to no parity + available. Incremented when parity NNAK is + received on this session, for which no parity + capability has been advartised." + ::= { pgmNeIfPerformanceEntry 60 } + +pgmNeIfNNakCongestionReports OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAKs forwarded as NNAK as congestion + report only." + ::= { pgmNeIfPerformanceEntry 61 } + +pgmNeIfNakRetryExpired OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NAKs timed out after + retrying." + ::= { pgmNeIfPerformanceEntry 62 } + +pgmNeIfNakRetryExpiredDLR OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAKs unconfirmed by DLR." + ::= { pgmNeIfPerformanceEntry 63 } + +pgmNeIfNakForwardedDLR OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAKs forwarded out this i/f to DLR + with retransmission state." + ::= { pgmNeIfPerformanceEntry 64 } + +pgmNeIfNakRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Total number of NAKs retransmitted out this i/f." + ::= { pgmNeIfPerformanceEntry 65 } + +pgmNeIfRdataEliminatedOIF OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of RDATA packets eliminated by lack of + OIF's." + ::= { pgmNeIfPerformanceEntry 66 } + +pgmNeIfRdataEliminatedSqn OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of RDATA packets eliminated by lack of + SQN." + ::= { pgmNeIfPerformanceEntry 67 } + +pgmNeIfInRdataFragments OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Total number of RDATA fragments received." + ::= { pgmNeIfPerformanceEntry 68 } + +pgmNeIfRdataFragmentsNoSessionErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of RDATA fragments eliminated by lack of + GSI." + ::= { pgmNeIfPerformanceEntry 69 } + +pgmNeIfRdataFragmentsEliminatedOIF OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of RDATA fragments eliminated by lack of + OIFs." + ::= { pgmNeIfPerformanceEntry 70 } + +pgmNeIfRdataFragmentsEliminatedSqn OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of RDATA fragments eliminated by lack of + SQN." + ::= { pgmNeIfPerformanceEntry 71 } + +pgmNeIfOutRdataFragments OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Total number of RDATA fragments forwarded." + ::= { pgmNeIfPerformanceEntry 72 } + +-- +-- PGM Network Element Transport Session Identifier +-- +pgmNeTsi OBJECT IDENTIFIER ::= { pgmNetworkElement 101 } + +pgmNeTotalTsiNumberOfEntries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of sessions in the PGM NE TSI + table." + ::= { pgmNeTsi 1 } + +-- The PGM Transport Session Identifier (TSI) table +-- The TSI information is grouped into three major categories: +-- fault, configuration and performance management. + +-- The PGM NE TSI fault management table +-- This table contains state and some general +-- per TSI information + +pgmNeTsiTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmNeTsiEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per-tsi state information." + ::= {pgmNeTsi 2} + +pgmNeTsiEntry OBJECT-TYPE + SYNTAX PgmNeTsiEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Transport Session is identified by Global ID and + Source Port." + INDEX { pgmNeTsiGlobalId, pgmNeTsiDataSourcePort } + ::= { pgmNeTsiTable 1 } + +PgmNeTsiEntry ::= SEQUENCE { + pgmNeTsiGlobalId + OCTET STRING, + pgmNeTsiDataSourcePort + Unsigned32, + pgmNeTsiStateBits + BITS, + pgmNeTsiDataDestinationPort + Unsigned32, + pgmNeTsiSourceAddress + IpAddress, + pgmNeTsiGroupAddress + IpAddress, + pgmNeTsiUpstreamAddress + IpAddress, + pgmNeTsiUpstreamIfIndex + InterfaceIndex, + pgmNeTsiDlrAddress + IpAddress + } + +pgmNeTsiGlobalId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The Globally unique source identifier for this + transport session." + ::= {pgmNeTsiEntry 1 } + +pgmNeTsiDataSourcePort OBJECT-TYPE + SYNTAX Unsigned32 (0..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Data source port." + ::= {pgmNeTsiEntry 2} + +pgmNeTsiStateBits OBJECT-TYPE + SYNTAX BITS { initialising(0), + spmSqnStateValid(1), + dlrCanProvideParity(2) } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "State associated with the TSI." + ::= {pgmNeTsiEntry 3 } + +pgmNeTsiDataDestinationPort OBJECT-TYPE + SYNTAX Unsigned32 (0..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Data destination port." + ::= {pgmNeTsiEntry 4 } + +pgmNeTsiSourceAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "IP address of the source." + ::= {pgmNeTsiEntry 5 } + +pgmNeTsiGroupAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Multicast group destination address." + ::= {pgmNeTsiEntry 6 } + +pgmNeTsiUpstreamAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The IP address of the upstream PGM neighbouring + element for this TSI." + ::= { pgmNeTsiEntry 7 } + +pgmNeTsiUpstreamIfIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The index of the upstream PGM element for the + entry." + ::= { pgmNeTsiEntry 8 } + +pgmNeTsiDlrAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "IP Address of a known DLR that will be used if + required." + ::= {pgmNeTsiEntry 9 } + + +-- PGM Network Element TSI Configuration Management Table +-- Since the Network Element cannot be configured +-- per TSI, configuration table is not implemented + + +-- PGM Network Element TSI Performance Management Table + +pgmNeTsiPerformanceTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmNeTsiPerformanceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding details of every transport + flow known by the Network Element." + ::= {pgmNeTsi 4} + +pgmNeTsiPerformanceEntry OBJECT-TYPE + SYNTAX PgmNeTsiPerformanceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Transport session description." + INDEX { pgmNeTsiPerformanceGlobalId, + pgmNeTsiPerformanceDataSourcePort } + ::= { pgmNeTsiPerformanceTable 1 } + +PgmNeTsiPerformanceEntry ::= SEQUENCE { + pgmNeTsiPerformanceGlobalId + OCTET STRING, + pgmNeTsiPerformanceDataSourcePort + Unsigned32, + pgmNeTsiSessionTrailEdgeSeq + Counter32, + pgmNeTsiSessionIncrSeq + Counter32, + pgmNeTsiLeadEdgeSeq + Counter32, + pgmNeTsiInSpms + Counter32, + pgmNeTsiOutSpms + Counter32, + pgmNeTsiInParitySpms + Counter32, + pgmNeTsiOutParitySpms + Counter32, + pgmNeTsiTotalReXmitStates + Counter32, + pgmNeTsiTotalReXmitTimedOut + Counter32, + pgmNeTsiInRdata + Counter32, + pgmNeTsiOutRdata + Counter32, + pgmNeTsiInParityRdata + Counter32, + pgmNeTsiOutParityRdata + Counter32, + pgmNeTsiInRdataNoStateErrors + Counter32, + pgmNeTsiUniqueNaks + Counter32, + pgmNeTsiInNaks + Counter32, + pgmNeTsiOutNaks + Counter32, + pgmNeTsiUniqueParityNaks + Counter32, + pgmNeTsiInParityNaks + Counter32, + pgmNeTsiOutParityNaks + Counter32, + pgmNeTsiInNakSeqErrors + Counter32, + pgmNeTsiInNnaks + Counter32, + pgmNeTsiOutNnaks + Counter32, + pgmNeTsiInParityNnaks + Counter32, + pgmNeTsiOutParityNnaks + Counter32, + pgmNeTsiInNcfs + Counter32, + pgmNeTsiOutNcfs + Counter32, + pgmNeTsiInParityNcfs + Counter32, + pgmNeTsiOutParityNcfs + Counter32, + pgmNeTsiSpmSequenceNumber + Unsigned32, + pgmNeTsiTransmissionGroupSize + Unsigned32, + pgmNeTsiTimeout + TimeTicks, + pgmNeTsiLastTtl + Unsigned32, + pgmNeTsiLinkLossRate + Unsigned32, + pgmNeTsiPathLossRate + Unsigned32, + pgmNeTsiReceiverLossRate + Unsigned32, + pgmNeTsiCongestionReportLead + Unsigned32, + pgmNeTsiCongestionReportWorstReceiver + IpAddress +} + +pgmNeTsiPerformanceGlobalId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The Globally unique source identifier for this + transport session." + ::= {pgmNeTsiPerformanceEntry 1 } + +pgmNeTsiPerformanceDataSourcePort OBJECT-TYPE + SYNTAX Unsigned32 (0..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Data source port." + ::= {pgmNeTsiPerformanceEntry 2} + +pgmNeTsiSessionTrailEdgeSeq OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The trailing edge sequence of the transmit + window." + ::= { pgmNeTsiPerformanceEntry 3 } + +pgmNeTsiSessionIncrSeq OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The sequence number defining the leading edge of + the increment window." + ::= { pgmNeTsiPerformanceEntry 4 } + +pgmNeTsiLeadEdgeSeq OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The leading edge sequence of the transmit + window." + ::= { pgmNeTsiPerformanceEntry 5 } + +pgmNeTsiInSpms OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of SPMs received for this + session." + ::= { pgmNeTsiPerformanceEntry 6 } + +pgmNeTsiOutSpms OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of SPMs sent out for this + session." + ::= { pgmNeTsiPerformanceEntry 7 } + +pgmNeTsiInParitySpms OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of Parity SPMs received for + this session." + ::= { pgmNeTsiPerformanceEntry 8 } + +pgmNeTsiOutParitySpms OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of Parity SPMs sent out for + this session." + ::= { pgmNeTsiPerformanceEntry 9 } + +pgmNeTsiTotalReXmitStates OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total retransmit states for this session." + ::= { pgmNeTsiPerformanceEntry 10 } + +pgmNeTsiTotalReXmitTimedOut OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total timed-out retransmit state entries for + this session." + ::= { pgmNeTsiPerformanceEntry 11 } + +pgmNeTsiInRdata OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of RDATAs received for this + session." + ::= { pgmNeTsiPerformanceEntry 12 } + +pgmNeTsiOutRdata OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of RDATAs sent out from this + session." + ::= { pgmNeTsiPerformanceEntry 13 } + +pgmNeTsiInParityRdata OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity RDATAs received for + this session." + ::= { pgmNeTsiPerformanceEntry 14 } + +pgmNeTsiOutParityRdata OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity RDATAs sent out from + this session." + ::= { pgmNeTsiPerformanceEntry 15 } + +pgmNeTsiInRdataNoStateErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of received RDATA discarded + due to no retransmit state." + ::= { pgmNeTsiPerformanceEntry 16 } + +pgmNeTsiUniqueNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of unique NAKs received for + this session." + ::= { pgmNeTsiPerformanceEntry 17 } + +pgmNeTsiInNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NAKs received for this + session." + ::= { pgmNeTsiPerformanceEntry 18 } + +pgmNeTsiOutNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NAKs sent out from this + session." + ::= { pgmNeTsiPerformanceEntry 19 } + +pgmNeTsiUniqueParityNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of unique parity NAKs received + for this session." + ::= { pgmNeTsiPerformanceEntry 20 } + +pgmNeTsiInParityNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity NAKs received for + this session." + ::= { pgmNeTsiPerformanceEntry 21 } + +pgmNeTsiOutParityNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity NAKs sent out from + this session." + ::= { pgmNeTsiPerformanceEntry 22 } + +pgmNeTsiInNakSeqErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of received NAKs discarded + because of out of sequence (out of retransmit + window)." + ::= { pgmNeTsiPerformanceEntry 23 } + +pgmNeTsiInNnaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NNAKs received for this + session." + ::= { pgmNeTsiPerformanceEntry 24 } + +pgmNeTsiOutNnaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NNAKs sent out from this + session." + ::= { pgmNeTsiPerformanceEntry 25 } + +pgmNeTsiInParityNnaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity NNAKs received for + this session." + ::= { pgmNeTsiPerformanceEntry 26 } + +pgmNeTsiOutParityNnaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity NNAKs sent out from + this session." + ::= { pgmNeTsiPerformanceEntry 27 } + +pgmNeTsiInNcfs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NCFs received for this + session." + ::= { pgmNeTsiPerformanceEntry 28 } + +pgmNeTsiOutNcfs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of NCFs sent out from this + session." + ::= { pgmNeTsiPerformanceEntry 29 } + +pgmNeTsiInParityNcfs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of parity NCFs received for + this session." + ::= { pgmNeTsiPerformanceEntry 30 } + +pgmNeTsiOutParityNcfs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of Parity NCFs sent out from + this session." + ::= { pgmNeTsiPerformanceEntry 31 } + +pgmNeTsiSpmSequenceNumber OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Sequence number of the last seen SPM." + ::= {pgmNeTsiPerformanceEntry 32 } + +pgmNeTsiTransmissionGroupSize OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Advertised size of the transmission group for + this transport session." + ::= {pgmNeTsiPerformanceEntry 33 } + +pgmNeTsiTimeout OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Time left for this entry to expire." + ::= {pgmNeTsiPerformanceEntry 34 } + +pgmNeTsiLastTtl OBJECT-TYPE + SYNTAX Unsigned32(1..255) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "IP TTL of last seen valid SPM." + ::= {pgmNeTsiPerformanceEntry 35 } + +pgmNeTsiLinkLossRate OBJECT-TYPE + SYNTAX Unsigned32(0..100) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Worst reported link loss rate for congestion + control. This is reported as a percentage." + ::= {pgmNeTsiPerformanceEntry 36 } + +pgmNeTsiPathLossRate OBJECT-TYPE + SYNTAX Unsigned32 (0..100) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Worst reported path loss rate for congestion + control. This is reported as a percentage." + ::= {pgmNeTsiPerformanceEntry 37 } + +pgmNeTsiReceiverLossRate OBJECT-TYPE + SYNTAX Unsigned32 (0..100) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Worst reported receiver loss rate for congestion + control. This is reported as a percentage." + ::= {pgmNeTsiPerformanceEntry 38 } + +pgmNeTsiCongestionReportLead OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Data lead sequence number associated with the + worst reported receiver loss rate." + ::= {pgmNeTsiPerformanceEntry 39 } + +pgmNeTsiCongestionReportWorstReceiver OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "IP address of the receiver that reported the + worst receiver loss rate." + ::= {pgmNeTsiPerformanceEntry 40 } + +-- The PGM Retransmission table + +-- The PGM Retransmission table contains +-- information about current retransmission requests. +-- This information is held per sequence number, or in +-- the case of FEC, every transmission group, for which +-- retransmission has been requested. + +pgmNeTsiRtxNumberOfEntries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of entries in the retransmission table." + ::= { pgmNeTsi 5 } + +pgmNeTsiRtxTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmNeTsiRtxEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding information for every sequence + number, or in the case of FEC, every + transmission group, for which retransmission has + been requested." + ::= {pgmNeTsi 6 } + +pgmNeTsiRtxEntry OBJECT-TYPE + SYNTAX PgmNeTsiRtxEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per sequence number / transmission group + information." + INDEX { pgmNeTsiGlobalId, + pgmNeTsiDataSourcePort, + pgmNeTsiRtxSequenceNumber, + pgmNeTsiRtxSequenceNumberType } + ::= { pgmNeTsiRtxTable 1 } + +PgmNeTsiRtxEntry ::= SEQUENCE { + pgmNeTsiRtxSequenceNumber + Unsigned32, + pgmNeTsiRtxSequenceNumberType + INTEGER, + pgmNeTsiRtxReqParityTgCount + Counter32, + pgmNeTsiRtxTimeout + TimeTicks, + pgmNeTsiRtxStateBits + BITS +} + +pgmNeTsiRtxSequenceNumber OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "For non-parity retransmission, a sequence number. + For parity retransmission, a transmission group + and packet count." + ::= {pgmNeTsiRtxEntry 1 } + +pgmNeTsiRtxSequenceNumberType OBJECT-TYPE + SYNTAX INTEGER { + selective(1), + tg(2) + } + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Selective Sequence Number and TG Sequence + Number." + ::= {pgmNeTsiRtxEntry 2 } + +pgmNeTsiRtxReqParityTgCount OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Requested number of missing parity packets + of specific Tg. The largest counter of the + received NAK will be stored in this mib. This + variable is valid for parity packets only." + ::= { pgmNeTsiRtxEntry 4 } + +pgmNeTsiRtxTimeout OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "When this state will expire." + ::= {pgmNeTsiRtxEntry 5 } + +pgmNeTsiRtxStateBits OBJECT-TYPE + SYNTAX BITS { + initialising(0), + eliminating(1), + redirecting(2), + stateCreatedByNullNAK(3), + listNAKentry(4), + parityState(5) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "State associated with retransmission entry." + ::= {pgmNeTsiRtxEntry 6 } + +-- The PGM Retransmission interfaces table + +-- The PGM Retransmission interfaces table contains +-- information about what interfaces will be sent +-- retransmitted data for a particular +-- retransmission entry + +pgmNeTsiRtxIfNumberOfEntries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of entries in the retransmission + interfaces table." + ::= { pgmNeTsi 7 } + +pgmNeTsiRtxIfTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmNeTsiRtxIfEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding information of every + interface for which retransmit state for + a particular sequence number or transmission + group has to be sent." + ::= {pgmNeTsi 8} + +pgmNeTsiRtxIfEntry OBJECT-TYPE + SYNTAX PgmNeTsiRtxIfEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Destination interfaces for a particular + retransmit state." + INDEX { pgmNeTsiGlobalId, + pgmNeTsiDataSourcePort, + pgmNeTsiRtxSequenceNumber, + pgmNeTsiRtxSequenceNumberType, + pgmNeTsiRtxIfIndex } + ::= { pgmNeTsiRtxIfTable 1 } + +PgmNeTsiRtxIfEntry ::= SEQUENCE { + pgmNeTsiRtxIfIndex + InterfaceIndex, + pgmNeTsiRtxIfPacketCount + Counter32 +} + +pgmNeTsiRtxIfIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A unique value for each interface. Its value + ranges between 1 and the value of ifNumber. + The value for each interface must remain + constant at least from one re-initialization + of the entity's network management system to + the next re-initialization." + ::= { pgmNeTsiRtxIfEntry 1 } + +pgmNeTsiRtxIfPacketCount OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of repair data packets still to be + retransmitted on this interface. For non-parity + retransmission this will never have a value + greater than 1. For parity retransmission, + any number can be present." + ::= { pgmNeTsiRtxIfEntry 2 } + +-- The PGM Poll Response table + +-- The PGM Poll Response table contains information +-- about PGM parent's of this network element who are +-- currently polling it. + +pgmNeTsiPolrNumberOfEntries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of entries in the poll response table." + ::= { pgmNeTsi 9 } + +pgmNeTsiPolrTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmNeTsiPolrEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding state information about what + PGM parents are polling this Network Element." + ::= { pgmNeTsi 10 } + +pgmNeTsiPolrEntry OBJECT-TYPE + SYNTAX PgmNeTsiPolrEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "State information for a Network Element that + is being polled by its parents" + INDEX { pgmNeTsiGlobalId, + pgmNeTsiDataSourcePort, + pgmNeTsiPolrSource } + ::= { pgmNeTsiPolrTable 1 } + +PgmNeTsiPolrEntry ::= SEQUENCE { + pgmNeTsiPolrSource + IpAddress, + pgmNeTsiPolrSequenceNumber + Unsigned32 +} + +pgmNeTsiPolrSource OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "IP Address of parent who is polling this + device." + ::= { pgmNeTsiPolrEntry 1 } + +pgmNeTsiPolrSequenceNumber OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Sequence number of last POLR from the source." + ::= { pgmNeTsiPolrEntry 2 } + +-- The PGM Poll table + +-- The PGM Poll table contains information related to +-- polling that this Network Element is doing for +-- its children + +pgmNeTsiPollNumberOfEntries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of entries in the poll table." + ::= { pgmNeTsi 11 } + +pgmNeTsiPollTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmNeTsiPollEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding state information related + to polling that this Network Element is doing + for its children." + ::= { pgmNeTsi 12 } + +pgmNeTsiPollEntry OBJECT-TYPE + SYNTAX PgmNeTsiPollEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "State information for a Network Element that + is polling its children." + INDEX { pgmNeTsiGlobalId, + pgmNeTsiDataSourcePort, + pgmNeTsiPollType } + ::= { pgmNeTsiPollTable 1 } + +PgmNeTsiPollEntry ::= SEQUENCE { + pgmNeTsiPollType + INTEGER, + pgmNeTsiPollSequence + Unsigned32, + pgmNeTsiPollChildBackoff + Unsigned32, + pgmNeTsiPollMask + Unsigned32, + pgmNeTsiPollPeriod + Unsigned32, + pgmNeTsiPollCount + Counter32, + pgmNeTsiPollTimeout + TimeTicks +} + +pgmNeTsiPollType OBJECT-TYPE + SYNTAX INTEGER { + general(1), + dlr(2) + } + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Type of Poll." + ::= { pgmNeTsiPollEntry 1 } + +pgmNeTsiPollSequence OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Sequence number of the most recent POLL packet + that we sent." + ::= { pgmNeTsiPollEntry 2 } + +pgmNeTsiPollChildBackoff OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Backoff advertised to be used by child of poll." + ::= { pgmNeTsiPollEntry 3 } + +pgmNeTsiPollMask OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Mask being used in poll." + ::= { pgmNeTsiPollEntry 4 } + +pgmNeTsiPollPeriod OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Period of poll." + ::= { pgmNeTsiPollEntry 5 } + +pgmNeTsiPollCount OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of Poll responses (POLRs) received." + ::= { pgmNeTsiPollEntry 6 } + +pgmNeTsiPollTimeout OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Remaining Time Ticks to next poll." + ::= { pgmNeTsiPollEntry 7 } + + +-- +-- PGM Source +-- + +-- PGM Source general management information + + +pgmSourceSaveDefaults OBJECT-TYPE + SYNTAX INTEGER { initial (1), + save (2), + pending (3), + success (4), + failure (5) } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Flag used to initiate the storing + of all default variable values to + non-volatile storage and to report the + result of the operation. + The following values can only be read, + never written : + initial(1) - returned prior to any requests + for saving the default configuration + pending(3) - saving in progress + success(4) - returned when a save(2) request + is successful + failure(5) - returned when a save(2) request + is unsuccessful + + The following values can only be written, + never read : + save(2) - to indicate that the default + configuration should be saved." + ::= { pgmSource 1 } + +pgmSourceLastUpdateTime OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of TimeTicks since the last update + of the non-volatile storage." + ::= { pgmSource 2 } + +pgmSourceDefaultTtl OBJECT-TYPE + SYNTAX Unsigned32(1..255) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Default TTL used by the PGM Source." + ::= { pgmSource 3 } + +pgmSourceDefaultAdvMode OBJECT-TYPE + SYNTAX INTEGER { data(1), + time(2), + applctrl(3), + other(4) } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Flag to indicate that the transmit window is + advanced with data, by time, under application + control, or any other method." + ::= { pgmSource 4 } + +pgmSourceDefaultLateJoin OBJECT-TYPE + SYNTAX INTEGER { + enable(1), + disable(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Flag to indicate whether or not the sender will + accept late joiners." + ::= { pgmSource 5 } + +pgmSourceDefaultTxwMaxRte OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Maximum transmit rate in bytes/second." + ::= { pgmSource 6 } + +pgmSourceDefaultTxwSecs OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Transmit window size in seconds." + ::= { pgmSource 7 } + +pgmSourceDefaultTxwAdvSecs OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Transmit window advance in seconds. This value + should always be set to a value smaller than + the pgmSourceTxwSecs." + ::= { pgmSource 8 } + +pgmSourceDefaultAdvIvl OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Advance interval in milliseconds. Always a + valid parameter when advancing with time. + Valid only in cases of absence of lost data + when advancing with data." + ::= { pgmSource 9 } + +pgmSourceDefaultSpmIvl OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "SPM interval in milliseconds." + ::= { pgmSource 10 } + +pgmSourceDefaultSpmHeartBeatIvlMin OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "SPM heartbeat interval in milliseconds." + ::= { pgmSource 11 } + +pgmSourceDefaultSpmHeartBeatIvlMax OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Maximum SPM heartbeat interval in milliseconds." + ::= { pgmSource 12 } + +pgmSourceDefaultRdataBackoffIvl OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "RDATA backoff interval in milliseconds." + ::= { pgmSource 13 } + +pgmSourceDefaultFECProactiveParitySize OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Number of proactive parity messages per FEC + block." + ::= { pgmSource 14 } + +pgmSourceDefaultGroupAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The default IP Multicast group address + used by the sender." + ::= { pgmSource 15 } + +pgmSourceUpdateSinceLastSave OBJECT-TYPE + SYNTAX INTEGER + { + notUpdated(1), + updated(2) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Specifies if any of the Source Default + variables have been updated or not, + since the last successful pgmSourceSaveDefaults. + notUpdated - none of the default Source + variables were set after the last + successful save to a non-volatile + storage. + updated - at least one of the default Source + variables were set after the last + successful save to a non-volatile + storage." + ::= { pgmSource 16 } + +-- PGM Source per TSI management information + +pgmSourceTsi OBJECT IDENTIFIER ::= { pgmSource 100 } + +pgmSourceNumberOfEntries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of PGM Source sessions." + ::= { pgmSourceTsi 1 } + +-- PGM Source Fault Management Table + +pgmSourceTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmSourceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per TSI fault + management and general information + related to PGM Source." + ::= {pgmSourceTsi 2} + +pgmSourceEntry OBJECT-TYPE + SYNTAX PgmSourceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per PGM sender information." + INDEX { pgmSourceGlobalId, + pgmSourceSourcePort } + ::= { pgmSourceTable 1 } + +PgmSourceEntry ::= SEQUENCE { + pgmSourceGlobalId + OCTET STRING, + pgmSourceSourcePort + Unsigned32, + pgmSourceSourceAddress + IpAddress, + pgmSourceGroupAddress + IpAddress, + pgmSourceDestPort + Unsigned32, + pgmSourceSourceGsi + OCTET STRING, + pgmSourceSourcePortNumber + Unsigned32 + } + +pgmSourceGlobalId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Globally unique session identifier (GSI)." + ::= { pgmSourceEntry 1 } + +pgmSourceSourcePort OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmSourceEntry 2 } + +pgmSourceSourceAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Source IP address." + ::= { pgmSourceEntry 3 } + +pgmSourceGroupAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "IP Multicast group address used by the + sender." + ::= { pgmSourceEntry 4 } + +pgmSourceDestPort OBJECT-TYPE + SYNTAX Unsigned32 (0..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Destination port number." + ::= { pgmSourceEntry 5 } + +pgmSourceSourceGsi OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Globally unique session identifier (GSI)." + ::= { pgmSourceEntry 6 } + +pgmSourceSourcePortNumber OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmSourceEntry 7 } + + +-- PGM Source Configuration Management Table + +pgmSourceConfigTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmSourceConfigEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per TSI + configuration information + related to the PGM Source." + ::= {pgmSourceTsi 3} + +pgmSourceConfigEntry OBJECT-TYPE + SYNTAX PgmSourceConfigEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per PGM sender information." + INDEX { pgmSourceConfigGlobalId, + pgmSourceConfigSourcePort } + ::= { pgmSourceConfigTable 1 } + +PgmSourceConfigEntry ::= SEQUENCE { + pgmSourceConfigGlobalId + OCTET STRING, + pgmSourceConfigSourcePort + Unsigned32, + pgmSourceTtl + Unsigned32, + pgmSourceAdvMode + INTEGER, + pgmSourceLateJoin + INTEGER, + pgmSourceTxwMaxRte + Unsigned32, + pgmSourceTxwSecs + Unsigned32, + pgmSourceTxwAdvSecs + Unsigned32, + pgmSourceAdvIvl + Unsigned32, + pgmSourceSpmIvl + Unsigned32, + pgmSourceSpmHeartBeatIvlMin + Unsigned32, + pgmSourceSpmHeartBeatIvlMax + Unsigned32, + pgmSourceRdataBackoffIvl + Unsigned32, + pgmSourceFEC + INTEGER, + pgmSourceFECTransmissionGrpSize + Unsigned32, + pgmSourceFECProactiveParitySize + Unsigned32, + pgmSourceSpmPathAddress + IpAddress + } + +pgmSourceConfigGlobalId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Globally unique session identifier (GSI)." + ::= { pgmSourceConfigEntry 1 } + +pgmSourceConfigSourcePort OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmSourceConfigEntry 2 } + +pgmSourceTtl OBJECT-TYPE + SYNTAX Unsigned32(1..255) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "TTL used by sender." + ::= { pgmSourceConfigEntry 3 } + +pgmSourceAdvMode OBJECT-TYPE + SYNTAX INTEGER { data(1), + time(2), + applctrl(3), + other(4) } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Flag to indicate that the transmit window is + advanced with data, by time, under application + control, or any other method." + ::= { pgmSourceConfigEntry 4 } + +pgmSourceLateJoin OBJECT-TYPE + SYNTAX INTEGER { + enable(1), + disable(2) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Flag to indicate whether or not the sender will + accept late joiners." + ::= { pgmSourceConfigEntry 5 } + +pgmSourceTxwMaxRte OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Maximum transmit rate in bytes/second." + ::= { pgmSourceConfigEntry 6 } + +pgmSourceTxwSecs OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Transmit window size in seconds." + ::= { pgmSourceConfigEntry 7 } + +pgmSourceTxwAdvSecs OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Transmit window advance in seconds. This value + should always be set to a value smaller than + the pgmSourceTxwSecs." + ::= { pgmSourceConfigEntry 8 } + +pgmSourceAdvIvl OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Advance interval in milliseconds. Always a + valid parameter when advancing with time. + Valid only in cases of absence of lost data + when advancing with data." + ::= { pgmSourceConfigEntry 9 } + +pgmSourceSpmIvl OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "SPM interval in milliseconds." + ::= { pgmSourceConfigEntry 10 } + +pgmSourceSpmHeartBeatIvlMin OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "SPM heartbeat interval in milliseconds." + ::= { pgmSourceConfigEntry 11 } + +pgmSourceSpmHeartBeatIvlMax OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Maximum SPM heartbeat interval in milliseconds." + ::= { pgmSourceConfigEntry 12 } + +pgmSourceRdataBackoffIvl OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "RDATA backoff interval in milliseconds." + ::= { pgmSourceConfigEntry 13 } + +pgmSourceFEC OBJECT-TYPE + SYNTAX INTEGER { disabled(1), + enabledFixedPacketSize(2), + enabledVariablePacketSize(3) } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Flag to indicate whether or not FEC is enabled + and whether it supports variable or fixed size + messages." + ::= { pgmSourceConfigEntry 14 } + +pgmSourceFECTransmissionGrpSize OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "FEC transmission group size." + ::= { pgmSourceConfigEntry 15 } + +pgmSourceFECProactiveParitySize OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Number of proactive parity messages per FEC + block." + ::= { pgmSourceConfigEntry 16 } + +pgmSourceSpmPathAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Ip Address for the NAKs to be sent, + in case that NE is not set." + ::= { pgmSourceConfigEntry 17 } + +-- PGM Source Performance Management Table + +pgmSourcePerformanceTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmSourcePerformanceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per TSI performance + information related to the PGM Source." + ::= {pgmSourceTsi 4} + +pgmSourcePerformanceEntry OBJECT-TYPE + SYNTAX PgmSourcePerformanceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per PGM sender information." + INDEX { pgmSourcePerformanceGlobalId, + pgmSourcePerformanceSourcePort } + ::= { pgmSourcePerformanceTable 1 } + +PgmSourcePerformanceEntry ::= SEQUENCE { + pgmSourcePerformanceGlobalId + OCTET STRING, + pgmSourcePerformanceSourcePort + Unsigned32, + pgmSourceDataBytesSent + Counter32, + pgmSourceDataMsgsSent + Counter32, + pgmSourceBytesBuffered + Counter32, + pgmSourceMsgsBuffered + Counter32, + pgmSourceBytesRetransmitted + Counter32, + pgmSourceMsgsRetransmitted + Counter32, + pgmSourceBytesSent + Counter32, + pgmSourceRawNaksReceived + Counter32, + pgmSourceNaksIgnored + Counter32, + pgmSourceCksumErrors + Counter32, + pgmSourceMalformedNaks + Counter32, + pgmSourcePacketsDiscarded + Counter32, + pgmSourceNaksRcvd + Counter32, + pgmSourceParityBytesRetransmitted + Counter32, + pgmSourceSelectiveBytesRetransmited + Counter32, + pgmSourceParityMsgsRetransmitted + Counter32, + pgmSourceSelectiveMsgsRetransmitted + Counter32, + pgmSourceBytesAdmit + Counter32, + pgmSourceMsgsAdmit + Counter32, + pgmSourceParityNakPacketsReceived + Counter32, + pgmSourceSelectiveNakPacketsReceived + Counter32, + pgmSourceParityNaksReceived + Counter32, + pgmSourceSelectiveNaksReceived + Counter32, + pgmSourceParityNaksIgnored + Counter32, + pgmSourceSelectiveNaksIgnored + Counter32, + pgmSourceAckErrors + Counter32, + pgmSourcePgmCCAcker + IpAddress, + pgmSourceTransmissionCurrentRate + Counter32, + pgmSourceAckPacketsReceived + Counter32, + pgmSourceNNakPacketsReceived + Counter32, + pgmSourceParityNNakPacketsReceived + Counter32, + pgmSourceSelectiveNNakPacketsReceived + Counter32, + pgmSourceNNaksReceived + Counter32, + pgmSourceParityNNaksReceived + Counter32, + pgmSourceSelectiveNNaksReceived + Counter32, + pgmSourceNNakErrors + Counter32 +} + +pgmSourcePerformanceGlobalId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Globally unique source identifier (GSI)." + ::= { pgmSourcePerformanceEntry 1 } + +pgmSourcePerformanceSourcePort OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmSourcePerformanceEntry 2 } + +pgmSourceDataBytesSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of data bytes sent for this TSI." + ::= { pgmSourcePerformanceEntry 3 } + +pgmSourceDataMsgsSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of data messages sent for this TSI." + ::= { pgmSourcePerformanceEntry 4 } + +pgmSourceBytesBuffered OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes currently buffered for this + TSI." + ::= { pgmSourcePerformanceEntry 5 } + +pgmSourceMsgsBuffered OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of messages currently buffered for + this TSI." + ::= { pgmSourcePerformanceEntry 6 } + +pgmSourceBytesRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes retransmitted for this TSI." + ::= { pgmSourcePerformanceEntry 7 } + +pgmSourceMsgsRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of messages retransmitted for this TSI." + ::= { pgmSourcePerformanceEntry 8 } + +pgmSourceBytesSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of bytes send for this TSI. Includes + IP header and non-data messages." + ::= { pgmSourcePerformanceEntry 9 } + +pgmSourceRawNaksReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Raw number of NAK packets received." + ::= { pgmSourcePerformanceEntry 10 } + +pgmSourceNaksIgnored OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of ignored Naks for this TSI, due to + duplicate NAKs reception." + ::= { pgmSourcePerformanceEntry 11 } + +pgmSourceCksumErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of checksum errors for this TSI." + ::= { pgmSourcePerformanceEntry 12 } + +pgmSourceMalformedNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of malformed NAK packets." + ::= { pgmSourcePerformanceEntry 13 } + +pgmSourcePacketsDiscarded OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of discarded data packets. This counter + is used to count all discarded incoming packets + per TSI in cases of duplicates, header and + packet errors, etc." + ::= { pgmSourcePerformanceEntry 14 } + +pgmSourceNaksRcvd OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of Sequence Numbers NAKed." + ::= { pgmSourcePerformanceEntry 15 } + +pgmSourceParityBytesRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes sent in parity retransmissions." + ::= { pgmSourcePerformanceEntry 16 } + +pgmSourceSelectiveBytesRetransmited OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes sent in selective retransmissions." + ::= { pgmSourcePerformanceEntry 17 } + +pgmSourceParityMsgsRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of parity retransmissions sent." + ::= { pgmSourcePerformanceEntry 18 } + +pgmSourceSelectiveMsgsRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of selective retransmissions sent." + ::= { pgmSourcePerformanceEntry 19 } + +pgmSourceBytesAdmit OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes currently in the rate controled + admit queue. Includes IP header, UDP header if + encapsulated, PGM header, and data." + ::= { pgmSourcePerformanceEntry 20 } + +pgmSourceMsgsAdmit OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of messages currently in the rate controled + admit queue. Includes data messages, retransmissions, + and SPMs." + ::= { pgmSourcePerformanceEntry 21 } + +pgmSourceParityNakPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of parity NAK packets received." + ::= { pgmSourcePerformanceEntry 22 } + +pgmSourceSelectiveNakPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of selective NAK packets received." + ::= { pgmSourcePerformanceEntry 23 } + +pgmSourceParityNaksReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual parity NAKs received." + ::= { pgmSourcePerformanceEntry 24 } + +pgmSourceSelectiveNaksReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual selective NAKs received." + ::= { pgmSourcePerformanceEntry 25 } + +pgmSourceParityNaksIgnored OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual parity NAKs ignored." + ::= { pgmSourcePerformanceEntry 26 } + +pgmSourceSelectiveNaksIgnored OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual selective NAKs ignored." + ::= { pgmSourcePerformanceEntry 27 } + +pgmSourceAckErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of ACK packets received with error in + them, different than checksum error." + ::= { pgmSourcePerformanceEntry 28 } + +pgmSourcePgmCCAcker OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Ip Address of the currently designated pgm + congestion control ACKER." + ::= { pgmSourcePerformanceEntry 29 } + +pgmSourceTransmissionCurrentRate OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Current transmission rate." + ::= { pgmSourcePerformanceEntry 30 } + +pgmSourceAckPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of ACK packets received." + ::= { pgmSourcePerformanceEntry 31 } + +pgmSourceNNakPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of Null NAKs received." + ::= { pgmSourcePerformanceEntry 32 } + +pgmSourceParityNNakPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of parity Null NAK packets received." + ::= { pgmSourcePerformanceEntry 33 } + +pgmSourceSelectiveNNakPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of selective Null NAK packets received." + ::= { pgmSourcePerformanceEntry 34 } + +pgmSourceNNaksReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual NAKs, received in Null + NAK packets." + ::= { pgmSourcePerformanceEntry 35 } + +pgmSourceParityNNaksReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual parity NAKs, + received in Null NAK packets." + ::= { pgmSourcePerformanceEntry 36 } + +pgmSourceSelectiveNNaksReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual NAKs, + received in Null NAK packets." + ::= { pgmSourcePerformanceEntry 37 } + +pgmSourceNNakErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of Null NAK packets received that contain + error, different than checksum error." + ::= { pgmSourcePerformanceEntry 38 } + +-- +-- PGM Receiver +-- + +-- PGM Receiver general management information + +pgmReceiverSaveDefaults OBJECT-TYPE + SYNTAX INTEGER { initial (1), + save (2), + pending (3), + success (4), + failure (5) } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Flag used to initiate the storing + of all default variable values to + non-volatile storage and to report the + result of the operation. + The following values can only be read, + never written : + initial(1) - returned prior to any requests + for saving the default configuration + pending(3) - saving in progress + success(4) - returned when a save(2) request + is successful + failure(5) - returned when a save(2) request + is unsuccessful + + The following values can only be written, + never read : + save(2) - to indicate that the default + configuration should be saved." + ::= { pgmReceiver 1 } + +pgmReceiverLastUpdateTime OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of TimeTicks since the last update + of the non-volatile storage." + ::= { pgmReceiver 2 } + +pgmReceiverDefaultNakBackoffIvl OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "NAK random backoff interval." + ::= { pgmReceiver 3 } + +pgmReceiverDefaultNakRepeatIvl OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "NAK repeat interval." + ::= { pgmReceiver 4 } + +pgmReceiverDefaultNakNcfRetries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Max NAK retries while witing for matching NCF." + ::= { pgmReceiver 5 } + +pgmReceiverDefaultNakRdataIvl OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Default NAK RDATA interval, i.e the amount of + time to cease NAKs for a particular piece of + data after a corresponding NCF has been received." + ::= { pgmReceiver 6 } + +pgmReceiverDefaultNakDataRetries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Max NAK retries while waiting for missing data." + ::= { pgmReceiver 7 } + +pgmReceiverDefaultSendNaks OBJECT-TYPE + SYNTAX INTEGER { + enabled(1), + disabled(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Flag to indicate whether or not receiver should + send NAKs or be totally passive." + ::= { pgmReceiver 8 } + +pgmReceiverDefaultLateJoin OBJECT-TYPE + SYNTAX INTEGER { + enabled(1), + disabled(2) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Flag to indicate whether or not the receiver + should wait for a OPT_JOIN SPM before + attempting to late join." + ::= { pgmReceiver 9 } + +pgmReceiverDefaultNakTtl OBJECT-TYPE + SYNTAX Unsigned32(1..255) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "TTL on NAK packets sent for loss." + ::= { pgmReceiver 10 } + +pgmReceiverDefaultDeliveryOrder OBJECT-TYPE + SYNTAX INTEGER { + unordered(1), + ordered(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Packet Delivery Order for the receiving + application." + ::= { pgmReceiver 11 } + +pgmReceiverDefaultNextPgmHop OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Next hop PGM router address. This option + sets the default address to send NAKs to, + instead of sending to the last hop address." + ::= { pgmReceiver 12 } + +pgmReceiverDefaultGroupAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Default IP Multicast group address + used by the sender." + ::= { pgmReceiver 13 } + +pgmReceiverUpdateSinceLastSave OBJECT-TYPE + SYNTAX INTEGER + { + notUpdated(1), + updated(2) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Specifies if any of the Receiver Default + variables have been updated or not, + since the last successful pgmSourceSaveDefaults. + notUpdated - none of the default Receiver + variables were set after the last + successful save to a non-volatile + storage. + updated - at least one of the default Receiver + variables were set after the last + successful save to a non-volatile + storage." + ::= { pgmReceiver 14 } + +pgmReceiverDefaultNakFailureThresholdTimer OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Timer that defines the default + interval of time during which unrecoverable + lost packets are monitored + for purposes of SNMP trap generation." + ::= { pgmReceiver 15 } + +pgmReceiverDefaultNakFailureThreshold OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The default number of unrecoverable + lost packets within the defined interval + after which an SNMP trap is generated." + ::= { pgmReceiver 16 } + + +-- PGM Receiver per Receiver management information + +pgmReceiverTsi OBJECT IDENTIFIER ::= { pgmReceiver 100 } + +pgmReceiverNumberOfEntries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of PGM Receivers." + ::= { pgmReceiverTsi 1 } + +-- PGM Receiver Fault Management Table + +pgmReceiverTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmReceiverEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per TSI fault + management and general information + related to the PGM Receiver." + ::= {pgmReceiverTsi 2} + +pgmReceiverEntry OBJECT-TYPE + SYNTAX PgmReceiverEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per PGM receiver fault management + and general information." + INDEX { pgmReceiverGlobalId, + pgmReceiverSourcePort, + pgmReceiverInstance } + ::= { pgmReceiverTable 1 } + +PgmReceiverEntry ::= SEQUENCE { + pgmReceiverGlobalId + OCTET STRING, + pgmReceiverSourcePort + Unsigned32, + pgmReceiverInstance + Unsigned32, + pgmReceiverGroupAddress + IpAddress, + pgmReceiverDestPort + Unsigned32, + pgmReceiverSourceAddress + IpAddress, + pgmReceiverLastHop + IpAddress, + pgmReceiverSourceGsi + OCTET STRING, + pgmReceiverSourcePortNumber + Unsigned32, + pgmReceiverUniqueInstance + Unsigned32 + } + +pgmReceiverGlobalId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Globally unique source identifier (GSI)." + ::= { pgmReceiverEntry 1 } + +pgmReceiverSourcePort OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmReceiverEntry 2 } + +pgmReceiverInstance OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Positive number, uniquely identifying + a Receiver." + ::= { pgmReceiverEntry 3 } + +pgmReceiverGroupAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "IP Multicast group address used by the sender." + ::= { pgmReceiverEntry 4 } + +pgmReceiverDestPort OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Destination port number." + ::= { pgmReceiverEntry 5 } + +pgmReceiverSourceAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Source IP address number." + ::= { pgmReceiverEntry 6 } + +pgmReceiverLastHop OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Last hop PGM router address." + ::= { pgmReceiverEntry 7 } + +pgmReceiverSourceGsi OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Globally unique source identifier (GSI)." + ::= { pgmReceiverEntry 8 } + +pgmReceiverSourcePortNumber OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmReceiverEntry 9 } + +pgmReceiverUniqueInstance OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Positive number, uniquely identifying + a Receiver." + ::= { pgmReceiverEntry 10 } + +-- PGM Receiver Configuration Management Table + +pgmReceiverConfigTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmReceiverConfigEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per TSI configuration + management information related + to the PGM Receiver." + ::= {pgmReceiverTsi 3 } + +pgmReceiverConfigEntry OBJECT-TYPE + SYNTAX PgmReceiverConfigEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per PGM receiver configuration management + information." + INDEX { pgmReceiverConfigGlobalId, + pgmReceiverConfigSourcePort, + pgmReceiverConfigInstance } + ::= { pgmReceiverConfigTable 1 } + +PgmReceiverConfigEntry ::= SEQUENCE { + pgmReceiverConfigGlobalId + OCTET STRING, + pgmReceiverConfigSourcePort + Unsigned32, + pgmReceiverConfigInstance + Unsigned32, + pgmReceiverNakBackoffIvl + Unsigned32, + pgmReceiverNakRepeatIvl + Unsigned32, + pgmReceiverNakNcfRetries + Unsigned32, + pgmReceiverNakRdataIvl + Unsigned32, + pgmReceiverNakDataRetries + Unsigned32, + pgmReceiverSendNaks + INTEGER, + pgmReceiverLateJoin + INTEGER, + pgmReceiverNakTtl + Unsigned32, + pgmReceiverDeliveryOrder + INTEGER, + pgmReceiverMcastNaks + INTEGER, + pgmReceiverNakFailureThresholdTimer + Unsigned32, + pgmReceiverNakFailureThreshold + Unsigned32 + } + +pgmReceiverConfigGlobalId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Globally unique source identifier (GSI)." + ::= { pgmReceiverConfigEntry 1 } + +pgmReceiverConfigSourcePort OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmReceiverConfigEntry 2 } + +pgmReceiverConfigInstance OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Positive number, uniquely identifying + a Receiver." + ::= { pgmReceiverConfigEntry 3 } + +pgmReceiverNakBackoffIvl OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "NAK random backoff interval." + ::= { pgmReceiverConfigEntry 4 } + +pgmReceiverNakRepeatIvl OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "NAK repeat interval." + ::= { pgmReceiverConfigEntry 5 } + +pgmReceiverNakNcfRetries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Max NAK retries while witing for matching NCF." + ::= { pgmReceiverConfigEntry 6 } + +pgmReceiverNakRdataIvl OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "NAK RDATA interval." + ::= { pgmReceiverConfigEntry 7 } + +pgmReceiverNakDataRetries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Max NAK retries while waiting for missing data." + ::= { pgmReceiverConfigEntry 8 } + +pgmReceiverSendNaks OBJECT-TYPE + SYNTAX INTEGER { + enabled(1), + disabled(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Flag to indicate whether or not receiver should + send NAKs or be totally passive." + ::= { pgmReceiverConfigEntry 9 } + +pgmReceiverLateJoin OBJECT-TYPE + SYNTAX INTEGER { + enabled(1), + disabled(2) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Flag to indicate whether or not the receiver + should wait for a OPT_JOIN SPM before + attempting to late join." + ::= { pgmReceiverConfigEntry 10 } + +pgmReceiverNakTtl OBJECT-TYPE + SYNTAX Unsigned32(1..255) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "TTL on NAK packets sent for loss." + ::= { pgmReceiverConfigEntry 11 } + +pgmReceiverDeliveryOrder OBJECT-TYPE + SYNTAX INTEGER { + unordered(1), + ordered(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Packet Delivery Order for the receiving + application." + ::= { pgmReceiverConfigEntry 12 } + +pgmReceiverMcastNaks OBJECT-TYPE + SYNTAX INTEGER { + enabled(1), + disabled(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Flag to indicate whether or not receiver should + send multicast NAKs." + ::= { pgmReceiverConfigEntry 13 } + +pgmReceiverNakFailureThresholdTimer OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Timer that defines per receiver + interval of time during which unrecoverable + lost packets are monitored + for purposes of SNMP trap generation." + ::= { pgmReceiverConfigEntry 14 } + +pgmReceiverNakFailureThreshold OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The number of unrecoverable lost packets + within the defined interval + after which an SNMP trap is generated." + ::= { pgmReceiverConfigEntry 15 } + + +-- PGM Receiver Performance Management Table + +pgmReceiverPerformanceTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmReceiverPerformanceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per TSI + performance management information + related to the PGM Receiver." + ::= {pgmReceiverTsi 4} + +pgmReceiverPerformanceEntry OBJECT-TYPE + SYNTAX PgmReceiverPerformanceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per PGM Receiver session performance + management information." + INDEX { pgmReceiverPerformanceGlobalId, + pgmReceiverPerformanceSourcePort, + pgmReceiverPerformanceInstance } + ::= { pgmReceiverPerformanceTable 1 } + +PgmReceiverPerformanceEntry ::= SEQUENCE { + pgmReceiverPerformanceGlobalId + OCTET STRING, + pgmReceiverPerformanceSourcePort + Unsigned32, + pgmReceiverPerformanceInstance + Unsigned32, + pgmReceiverDataBytesReceived + Counter32, + pgmReceiverDataMsgsReceived + Counter32, + pgmReceiverNaksSent + Counter32, + pgmReceiverNaksRetransmitted + Counter32, + pgmReceiverNakFailures + Counter32, + pgmReceiverBytesReceived + Counter32, + pgmReceiverNaksSuppressed + Counter32, + pgmReceiverCksumErrors + Counter32, + pgmReceiverMalformedSpms + Counter32, + pgmReceiverMalformedOdata + Counter32, + pgmReceiverMalformedRdata + Counter32, + pgmReceiverMalformedNcfs + Counter32, + pgmReceiverPacketsDiscarded + Counter32, + pgmReceiverLosses + Counter32, + pgmReceiverBytesDeliveredToApp + Counter32, + pgmReceiverMsgsDeliveredToApp + Counter32, + pgmReceiverDupSpms + Counter32, + pgmReceiverDupDatas + Counter32, + pgmReceiverDupParities + Counter32, + pgmReceiverNakPacketsSent + Counter32, + pgmReceiverParityNakPacketsSent + Counter32, + pgmReceiverSelectiveNakPacketsSent + Counter32, + pgmReceiverParityNaksSent + Counter32, + pgmReceiverSelectiveNaksSent + Counter32, + pgmReceiverParityNaksRetransmitted + Counter32, + pgmReceiverSelectiveNaksRetransmitted + Counter32, + pgmReceiverNaksFailed + Counter32, + pgmReceiverParityNaksFailed + Counter32, + pgmReceiverSelectiveNaksFailed + Counter32, + pgmReceiverNaksFailedRxwAdvanced + Counter32, + pgmReceiverNaksFaledNcfRetriesExceeded + Counter32, + pgmReceiverNaksFailedDataRetriesExceeded + Counter32, + pgmReceiverNaksFailedGenExpired + Counter32, + pgmReceiverNakFailuresDelivered + Counter32, + pgmReceiverParityNaksSuppressed + Counter32, + pgmReceiverSelectiveNaksSuppressed + Counter32, + pgmReceiverNakErrors + Counter32, + pgmReceiverOutstandingParityNaks + Counter32, + pgmReceiverOutstandingSelectiveNaks + Counter32, + pgmReceiverLastActivity + Counter32, + pgmReceiverNakSvcTimeMin + Counter32, + pgmReceiverNakSvcTimeMean + Counter32, + pgmReceiverNakSvcTimeMax + Counter32, + pgmReceiverNakFailTimeMin + Counter32, + pgmReceiverNakFailTimeMean + Counter32, + pgmReceiverNakFailTimeMax + Counter32, + pgmReceiverNakTransmitMin + Counter32, + pgmReceiverNakTransmitMean + Counter32, + pgmReceiverNakTransmitMax + Counter32, + pgmReceiverAcksSent + Counter32, + pgmReceiverRxwTrail + Counter32, + pgmReceiverRxwLead + Counter32, + pgmReceiverNakFailuresLastInterval + Counter32, + pgmReceiverLastIntervalNakFailures + Counter32 +} + +pgmReceiverPerformanceGlobalId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Globally unique source identifier (GSI)." + ::= { pgmReceiverPerformanceEntry 1 } + +pgmReceiverPerformanceSourcePort OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmReceiverPerformanceEntry 2 } + +pgmReceiverPerformanceInstance OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Positive number, uniquely identifying + a Receiver." + ::= { pgmReceiverPerformanceEntry 3 } + +pgmReceiverDataBytesReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of data bytes received for this PGM + Receiver session." + ::= { pgmReceiverPerformanceEntry 4 } + +pgmReceiverDataMsgsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of data messages received for this + PGM Receiver session." + ::= { pgmReceiverPerformanceEntry 5 } + +pgmReceiverNaksSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAKs sent for this session." + ::= { pgmReceiverPerformanceEntry 6 } + +pgmReceiverNaksRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAKs retransmitted for this + session." + ::= { pgmReceiverPerformanceEntry 7 } + +pgmReceiverNakFailures OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAK failures for this session. + This counter represents the number of + unrecoverable/unrepairable data packets." + ::= { pgmReceiverPerformanceEntry 8 } + +pgmReceiverBytesReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes received for this session. + It counts all bytes received, including IP + and PGM header and non-data messages." + ::= { pgmReceiverPerformanceEntry 9 } + +pgmReceiverNaksSuppressed OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of suppressed NAKs." + ::= { pgmReceiverPerformanceEntry 10 } + +pgmReceiverCksumErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of checksum errors for this session." + ::= { pgmReceiverPerformanceEntry 11 } + +pgmReceiverMalformedSpms OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of malformed SPMs for this session." + ::= { pgmReceiverPerformanceEntry 12 } + +pgmReceiverMalformedOdata OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of malformed ODATA packets for this + session." + ::= { pgmReceiverPerformanceEntry 13 } + +pgmReceiverMalformedRdata OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of malformed RDATA packets for this + session." + ::= { pgmReceiverPerformanceEntry 14 } + +pgmReceiverMalformedNcfs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of malformed NCF packets for this + session." + ::= { pgmReceiverPerformanceEntry 15 } + +pgmReceiverPacketsDiscarded OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of discarded packets for this + session." + ::= { pgmReceiverPerformanceEntry 16 } + +pgmReceiverLosses OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of detected missed packets for + this session. This counter is incremented + every time a Receiver detects a missing + packet." + ::= { pgmReceiverPerformanceEntry 17 } + +pgmReceiverBytesDeliveredToApp OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes, delivered to the + application." + ::= { pgmReceiverPerformanceEntry 18 } + +pgmReceiverMsgsDeliveredToApp OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of messages, delivered to the + application." + ::= { pgmReceiverPerformanceEntry 19 } + +pgmReceiverDupSpms OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of duplicate SPMs." + ::= { pgmReceiverPerformanceEntry 20 } + +pgmReceiverDupDatas OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of duplicate RDATA/ODATA." + ::= { pgmReceiverPerformanceEntry 21 } + +pgmReceiverDupParities OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of duplicate parities seen." + ::= { pgmReceiverPerformanceEntry 22 } + +pgmReceiverNakPacketsSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAK packets sent. + Includes parity and selective." + ::= { pgmReceiverPerformanceEntry 23 } + +pgmReceiverParityNakPacketsSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of parity NAK packets sent." + ::= { pgmReceiverPerformanceEntry 24 } + +pgmReceiverSelectiveNakPacketsSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of selective NAK packets sent." + ::= { pgmReceiverPerformanceEntry 25 } + +pgmReceiverParityNaksSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual parity NAK packets sent." + ::= { pgmReceiverPerformanceEntry 26 } + +pgmReceiverSelectiveNaksSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual selective NAK packets sent." + ::= { pgmReceiverPerformanceEntry 27 } + +pgmReceiverParityNaksRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual parity NAKs retransmitted." + ::= { pgmReceiverPerformanceEntry 28 } + +pgmReceiverSelectiveNaksRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual selective NAKs retransmitted." + ::= { pgmReceiverPerformanceEntry 29 } + +pgmReceiverNaksFailed OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual NAKs that failed." + ::= { pgmReceiverPerformanceEntry 30 } + +pgmReceiverParityNaksFailed OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual parity NAKs that failed." + ::= { pgmReceiverPerformanceEntry 31 } + +pgmReceiverSelectiveNaksFailed OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual selective NAKs that failed." + ::= { pgmReceiverPerformanceEntry 32 } + +pgmReceiverNaksFailedRxwAdvanced OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual NAKs that failed, due to the + window being advanced over them." + ::= { pgmReceiverPerformanceEntry 33 } + +pgmReceiverNaksFaledNcfRetriesExceeded OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual NAKs that failed, due to ncf + retry limit exceeded." + ::= { pgmReceiverPerformanceEntry 34 } + +pgmReceiverNaksFailedDataRetriesExceeded OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual NAKs that failed, due to data + retry limit exceeded." + ::= { pgmReceiverPerformanceEntry 35 } + +pgmReceiverNaksFailedGenExpired OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual NAKs that failed, due to NAK + generation interval expiring before it + could be repaired." + ::= { pgmReceiverPerformanceEntry 36 } + +pgmReceiverNakFailuresDelivered OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAK failures delivered to application." + ::= { pgmReceiverPerformanceEntry 37 } + +pgmReceiverParityNaksSuppressed OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual parity NAKs that were + suppressed from being sent due to reception + of an NCF or ODATA/RDATA for the loss." + ::= { pgmReceiverPerformanceEntry 38 } + +pgmReceiverSelectiveNaksSuppressed OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual selective NAKs that were + suppressed from being sent due to reception + of an NCF or ODATA/RDATA for the loss." + ::= { pgmReceiverPerformanceEntry 39 } + +pgmReceiverNakErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAK packets, that contained + errors in them." + ::= { pgmReceiverPerformanceEntry 40 } + +pgmReceiverOutstandingParityNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Current number of outstanding individual parity + NAKs that are waiting to be repaired." + ::= { pgmReceiverPerformanceEntry 41 } + +pgmReceiverOutstandingSelectiveNaks OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Current number of outstanding individual selective + NAKs that are waiting to be repaired." + ::= { pgmReceiverPerformanceEntry 42 } + +pgmReceiverLastActivity OBJECT-TYPE + SYNTAX Counter32 + UNITS "seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Last time activity was observed from + the Source. In seconds since the epoch, + January 1, 1970." + ::= { pgmReceiverPerformanceEntry 43 } + +pgmReceiverNakSvcTimeMin OBJECT-TYPE + SYNTAX Counter32 + UNITS "miliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The min time that it took for a loss + to be repaired." + ::= { pgmReceiverPerformanceEntry 44 } + +pgmReceiverNakSvcTimeMean OBJECT-TYPE + SYNTAX Counter32 + UNITS "miliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The mean time that it took for all losses + to be repaired." + ::= { pgmReceiverPerformanceEntry 45 } + +pgmReceiverNakSvcTimeMax OBJECT-TYPE + SYNTAX Counter32 + UNITS "miliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The max time that it took for a loss + to be repaired." + ::= { pgmReceiverPerformanceEntry 46 } + +pgmReceiverNakFailTimeMin OBJECT-TYPE + SYNTAX Counter32 + UNITS "miliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The min time it took for a loss + to be considered unrecoverable." + ::= { pgmReceiverPerformanceEntry 47 } + +pgmReceiverNakFailTimeMean OBJECT-TYPE + SYNTAX Counter32 + UNITS "miliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The mean time it took for all losses + to be considered unrecoverable." + ::= { pgmReceiverPerformanceEntry 48 } + +pgmReceiverNakFailTimeMax OBJECT-TYPE + SYNTAX Counter32 + UNITS "miliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The max time it took for a loss + to be considered unrecoverable." + ::= { pgmReceiverPerformanceEntry 49 } + +pgmReceiverNakTransmitMin OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The min number of times an individual NAK + needed to be retransmitted before it was repaired." + ::= { pgmReceiverPerformanceEntry 50 } + +pgmReceiverNakTransmitMean OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The mean number of times an individual NAK + needed to be retransmitted before it was repaired." + ::= { pgmReceiverPerformanceEntry 51 } + +pgmReceiverNakTransmitMax OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The max number of times an individual NAK + needed to be retransmitted before it was repaired." + ::= { pgmReceiverPerformanceEntry 52 } + +pgmReceiverAcksSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of ACKs sent from the congestion + control operation." + ::= { pgmReceiverPerformanceEntry 53 } + +pgmReceiverRxwTrail OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Sequence number of the trailing edge of + the transmission window as is being advertised + by the sender." + ::= { pgmReceiverPerformanceEntry 54 } + +pgmReceiverRxwLead OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Sequence number of the leading edge of + the transmission window as is being advertised + by the sender." + ::= { pgmReceiverPerformanceEntry 55 } + +pgmReceiverNakFailuresLastInterval OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The actual number of seconds since the + last pgmReceiverLastIntervalNakFailures + counter reset due to number of nak failures + threshold exceeded." + ::= { pgmReceiverPerformanceEntry 56 } + +pgmReceiverLastIntervalNakFailures OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of actual unrecoverable failures for + the requested threshold interval for this session." + ::= { pgmReceiverPerformanceEntry 57 } + +-- +-- Designated Local Repairer (DLR) +-- + +-- Designated Local Repairer (DLR) Default Configuration + +pgmDlrSaveDefaults OBJECT-TYPE + SYNTAX INTEGER { initial (1), + save (2), + pending (3), + success (4), + failure (5) } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Flag used to initiate the storing + of all default variable values to + non-volatile storage and to report the + result of the operation. + The following values can only be read, + never written : + initial(1) - returned prior to any requests + for saving the default configuration + pending(3) - saving in progress + success(4) - returned when a save(2) request + is successful + failure(5) - returned when a save(2) request + is unsuccessful + + The following values can only be written, + never read : + save(2) - to indicate that the default + configuration should be saved." + ::= { pgmDLR 1 } + +pgmDlrLastUpdateTime OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of TimeTicks since the last update + of the non-volatile storage." + ::= { pgmDLR 2 } + +pgmDlrGroupAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Multicast group address to listen for traffic + on." + ::= { pgmDLR 3 } + +pgmDlrCacheRtx OBJECT-TYPE + SYNTAX INTEGER + { + cacheOFF(1), + cacheON(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Specifies if the NE should also cache data for + retransmission or simply suppress duplicate + NAKs and forward the NAKs to it's parent NE or + sender." + ::= { pgmDLR 4 } + +pgmDlrActivityIvl OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Specifies the delay between activity checks + for specific PGM sessions." + ::= { pgmDLR 5 } + +pgmDlrMaxRate OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Specifies the maximum rate (in bps) for + retransmissions." + ::= { pgmDLR 6 } + +pgmDlrParentNeAddr OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Ip Address of the NE to send all NAKs to." + ::= { pgmDLR 7 } + +pgmDlrUpdateSinceLastSave OBJECT-TYPE + SYNTAX INTEGER + { + notUpdated(1), + updated(2) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Specifies if any of the Dlr Default + variables have been updated or not, + since the last successful pgmDlrSaveDefaults. + notUpdated - none of the default Dlr + variables were set after the last + successful save to a non-volatile + storage. + updated - at least one of the default Dlr + variables were set after the last + successful save to a non-volatile + storage." + ::= { pgmDLR 8 } + + +-- +-- PGM DLR Source/Re-transmitter Sessions +-- +pgmDlrSource OBJECT IDENTIFIER ::= { pgmDLR 100 } + +pgmDlrSourceNumberOfEntries OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of PGM Source sessions for + the PGM DLR." + ::= { pgmDlrSource 1 } + +-- PGM Dlr Source Fault Management Table + +pgmDlrSourceTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmDlrSourceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per TSI fault + management and general information + related to the PGM DLR Source sessions." + ::= {pgmDlrSource 2} + +pgmDlrSourceEntry OBJECT-TYPE + SYNTAX PgmDlrSourceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per PGM DLR Source sessions fault + management information." + INDEX { pgmDlrSourceGlobalId, + pgmDlrSourceSourcePort } + ::= { pgmDlrSourceTable 1 } + +PgmDlrSourceEntry ::= SEQUENCE { + pgmDlrSourceGlobalId + OCTET STRING, + pgmDlrSourceSourcePort + Unsigned32, + pgmDlrSourceGroupAddress + IpAddress, + pgmDlrSourceSourceGsi + OCTET STRING, + pgmDlrSourceSourcePortNumber + Unsigned32 + } + +pgmDlrSourceGlobalId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Globally unique source identifier (GSI)." + ::= { pgmDlrSourceEntry 1 } + +pgmDlrSourceSourcePort OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmDlrSourceEntry 2 } + +pgmDlrSourceGroupAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Multicast group interface address + to send multicast packets on." + ::= { pgmDlrSourceEntry 3 } + +pgmDlrSourceSourceGsi OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Globally unique source identifier (GSI)." + ::= { pgmDlrSourceEntry 4 } + +pgmDlrSourceSourcePortNumber OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmDlrSourceEntry 5 } + +-- PGM DLR Source Configuration Management Table + +pgmDlrSourceConfigTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmDlrSourceConfigEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per TSI configuration + management information related to the + PGM DLR Source sessions." + ::= {pgmDlrSource 3} + +pgmDlrSourceConfigEntry OBJECT-TYPE + SYNTAX PgmDlrSourceConfigEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per PGM DLR Source sessions configuration + management information." + INDEX { pgmDlrSourceConfigGlobalId, + pgmDlrSourceConfigSourcePort } + ::= { pgmDlrSourceConfigTable 1 } + +PgmDlrSourceConfigEntry ::= SEQUENCE { + pgmDlrSourceConfigGlobalId + OCTET STRING, + pgmDlrSourceConfigSourcePort + Unsigned32, + pgmDlrSourceGroupTtl + Unsigned32, + pgmDlrSourceRdataBackoffIvl + Unsigned32 + } + +pgmDlrSourceConfigGlobalId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Globally unique source identifier (GSI)." + ::= { pgmDlrSourceConfigEntry 1 } + +pgmDlrSourceConfigSourcePort OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmDlrSourceConfigEntry 2 } + +pgmDlrSourceGroupTtl OBJECT-TYPE + SYNTAX Unsigned32(1..255) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "This option sets the default TTL to use for + multicast packets. " + ::= { pgmDlrSourceConfigEntry 3 } + +pgmDlrSourceRdataBackoffIvl OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "This option sets the default RDATA backoff + interval. The value is expressed in milliseconds. + The value of 0 indicates no backoff." + ::= { pgmDlrSourceConfigEntry 4 } + + +-- PGM DLR Source Performance Management Table + +pgmDlrSourcePerformanceTable OBJECT-TYPE + SYNTAX SEQUENCE OF PgmDlrSourcePerformanceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The table holding per TSI performance + management information related to the + PGM DLR Source sessions." + ::= {pgmDlrSource 4} + +pgmDlrSourcePerformanceEntry OBJECT-TYPE + SYNTAX PgmDlrSourcePerformanceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per PGM DLR Source performance management + information." + INDEX { pgmDlrSourcePerformanceGlobalId, + pgmDlrSourcePerformanceSourcePort } + ::= { pgmDlrSourcePerformanceTable 1 } + +PgmDlrSourcePerformanceEntry ::= SEQUENCE { + pgmDlrSourcePerformanceGlobalId + OCTET STRING, + pgmDlrSourcePerformanceSourcePort + Unsigned32, + pgmDlrSourceRdataMsgsSent + Counter32, + pgmDlrSourceRdataBytesSent + Counter32, + pgmDlrSourceBytesSent + Counter32, + pgmDlrSourceNaksRcvd + Counter32, + pgmDlrSourceNaksIgnored + Counter32, + pgmDlrSourceNakErrors + Counter32, + pgmDlrSourceDiscards + Counter32, + pgmDlrSourceCksumErrors + Counter32, + pgmDlrSourceNNaksSent + Counter32, + pgmDlrSourceBytesBuffered + Counter32, + pgmDlrSourceMsgsBuffered + Counter32, + pgmDlrSourceParityBytesRetransmitted + Counter32, + pgmDlrSourceSelectiveBytesRetransmited + Counter32, + pgmDlrSourceParityMsgsRetransmitted + Counter32, + pgmDlrSourceSelectiveMsgsRetransmitted + Counter32, + pgmDlrSourceBytesAdmit + Counter32, + pgmDlrSourceMsgsAdmit + Counter32, + pgmDlrSourceNakPacketsReceived + Counter32, + pgmDlrSourceParityNakPacketsReceived + Counter32, + pgmDlrSourceSelectiveNakPacketsReceived + Counter32, + pgmDlrSourceParityNaksReceived + Counter32, + pgmDlrSourceSelectiveNaksReceived + Counter32, + pgmDlrSourceParityNaksIgnored + Counter32, + pgmDlrSourceSelectiveNaksIgnored + Counter32, + pgmDlrSourceAckErrors + Counter32, + pgmDlrSourceNNakErrors + Counter32, + pgmDlrSourceAckPacketsReceived + Counter32, + pgmDlrSourceNNakPacketsReceived + Counter32, + pgmDlrSourceParityNNakPacketsReceived + Counter32, + pgmDlrSourceSelectiveNNakPacketsReceived + Counter32, + pgmDlrSourceNNaksReceived + Counter32, + pgmDlrSourceParityNNaksReceived + Counter32, + pgmDlrSourceSelectiveNNaksReceived + Counter32 + } + +pgmDlrSourcePerformanceGlobalId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (12)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Globally unique source identifier (GSI)." + ::= { pgmDlrSourcePerformanceEntry 1 } + +pgmDlrSourcePerformanceSourcePort OBJECT-TYPE + SYNTAX Unsigned32(0..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Source port number." + ::= { pgmDlrSourcePerformanceEntry 2 } + +pgmDlrSourceRdataMsgsSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of Repair Data (RDATA) packets sent for + this PGM DLR." + ::= { pgmDlrSourcePerformanceEntry 3 } + +pgmDlrSourceRdataBytesSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of RDATA bytes sent." + ::= { pgmDlrSourcePerformanceEntry 4 } + +pgmDlrSourceBytesSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes sent. This includes IP and + PGM header and non-data msgs." + ::= { pgmDlrSourcePerformanceEntry 5 } + +pgmDlrSourceNaksRcvd OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAKs received on this TSI." + ::= { pgmDlrSourcePerformanceEntry 6 } + +pgmDlrSourceNaksIgnored OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAKs ignored on this TSI, due to + duplicates." + ::= { pgmDlrSourcePerformanceEntry 7 } + +pgmDlrSourceNakErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of malformed NAKs on this TSI." + ::= { pgmDlrSourcePerformanceEntry 8 } + +pgmDlrSourceDiscards OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of discarded packets on this TSI. + This counter is used to count all discarded + incoming packets per TSI in cases of + duplicates, header and packet errors, etc." + ::= { pgmDlrSourcePerformanceEntry 9 } + +pgmDlrSourceCksumErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of checksum errors on this TSI." + ::= { pgmDlrSourcePerformanceEntry 10 } + +pgmDlrSourceNNaksSent OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of Null NAKs (in number of packets) + sent by this PGM DLR session." + ::= { pgmDlrSourcePerformanceEntry 11 } + +pgmDlrSourceBytesBuffered OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes currently buffered for this + TSI." + ::= { pgmDlrSourcePerformanceEntry 12 } + +pgmDlrSourceMsgsBuffered OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of messages currently buffered for + this TSI." + ::= { pgmDlrSourcePerformanceEntry 13 } + +pgmDlrSourceParityBytesRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes sent in parity retransmissions." + ::= { pgmDlrSourcePerformanceEntry 14 } + +pgmDlrSourceSelectiveBytesRetransmited OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes sent in selective retransmissions." + ::= { pgmDlrSourcePerformanceEntry 15 } + +pgmDlrSourceParityMsgsRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of parity retransmissions sent." + ::= { pgmDlrSourcePerformanceEntry 16 } + +pgmDlrSourceSelectiveMsgsRetransmitted OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of selective retransmissions sent." + ::= { pgmDlrSourcePerformanceEntry 17 } + +pgmDlrSourceBytesAdmit OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of bytes currently in the rate controled + admit queue. Includes IP header, UDP header if + encapsulated, PGM header, and data." + ::= { pgmDlrSourcePerformanceEntry 18 } + +pgmDlrSourceMsgsAdmit OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of messages currently in the rate controled + admit queue. Includes data messages, retransmissions, + and SPMs." + ::= { pgmDlrSourcePerformanceEntry 19 } + +pgmDlrSourceNakPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of NAK packets received." + ::= { pgmDlrSourcePerformanceEntry 20 } + +pgmDlrSourceParityNakPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of parity NAK packets received." + ::= { pgmDlrSourcePerformanceEntry 21 } + +pgmDlrSourceSelectiveNakPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of selective NAK packets received." + ::= { pgmDlrSourcePerformanceEntry 22 } + +pgmDlrSourceParityNaksReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual parity NAKs received." + ::= { pgmDlrSourcePerformanceEntry 23 } + +pgmDlrSourceSelectiveNaksReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual selective NAKs received." + ::= { pgmDlrSourcePerformanceEntry 24 } + +pgmDlrSourceParityNaksIgnored OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual parity NAKs ignored." + ::= { pgmDlrSourcePerformanceEntry 25 } + +pgmDlrSourceSelectiveNaksIgnored OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual selective NAKs ignored." + ::= { pgmDlrSourcePerformanceEntry 26 } + +pgmDlrSourceAckErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of ACK packets received with error in + them, different than checksum error." + ::= { pgmDlrSourcePerformanceEntry 27 } + +pgmDlrSourceNNakErrors OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of Null NAK packets received that contain + error, rSifferent than checksum error." + ::= { pgmDlrSourcePerformanceEntry 28 } + +pgmDlrSourceAckPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of ACK packets received." + ::= { pgmDlrSourcePerformanceEntry 29 } + +pgmDlrSourceNNakPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of Null NAKs received." + ::= { pgmDlrSourcePerformanceEntry 30 } + +pgmDlrSourceParityNNakPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of parity Null NAK packets received." + ::= { pgmDlrSourcePerformanceEntry 31 } + +pgmDlrSourceSelectiveNNakPacketsReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of selective Null NAK packets received." + ::= { pgmDlrSourcePerformanceEntry 32 } + +pgmDlrSourceNNaksReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual NAKs, received in Null + NAK packets." + ::= { pgmDlrSourcePerformanceEntry 33 } + +pgmDlrSourceParityNNaksReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual parity NAKs, + received in Null NAK packets." + ::= { pgmDlrSourcePerformanceEntry 34 } + +pgmDlrSourceSelectiveNNaksReceived OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of individual NAKs, + received in Null NAK packets." + ::= { pgmDlrSourcePerformanceEntry 35 } + +-- Notifications + +pgmNotifications OBJECT IDENTIFIER ::= + { pgmNotificationPrefix 0 } + +pgmStart NOTIFICATION-TYPE + STATUS current + DESCRIPTION + "This trap is sent when the pgm snmp agent starts" + ::= { pgmNotifications 1 } + +pgmStop NOTIFICATION-TYPE + STATUS current + DESCRIPTION + "This trap is sent when the pgm snmp agent terminates" + ::= { pgmNotifications 2 } + +-- PGM Source Specific Traps + +pgmNewSourceTrap NOTIFICATION-TYPE + OBJECTS { + pgmSourceSourceGsi, + pgmSourceSourcePortNumber + } + STATUS current + DESCRIPTION + "New Source Session created." + ::= { pgmNotifications 3 } + +pgmClosedSourceTrap NOTIFICATION-TYPE + OBJECTS { + pgmSourceSourceGsi, + pgmSourceSourcePortNumber + } + STATUS current + DESCRIPTION + "Source Session closed." + ::= { pgmNotifications 4 } + +-- PGM Receiver Specific Traps + +pgmNewReceiverTrap NOTIFICATION-TYPE + OBJECTS { + pgmReceiverSourceGsi, + pgmReceiverSourcePortNumber, + pgmReceiverUniqueInstance + } + STATUS current + DESCRIPTION + "New Receiver Session created. + This trap is optional." + ::= { pgmNotifications 5 } + +pgmClosedReceiverTrap NOTIFICATION-TYPE + OBJECTS { + pgmReceiverSourceGsi, + pgmReceiverSourcePortNumber, + pgmReceiverUniqueInstance + } + STATUS current + DESCRIPTION + "Receiver Session closed. + This trap is optional." + ::= { pgmNotifications 6 } + +pgmNakFailuresTrap NOTIFICATION-TYPE + OBJECTS { + pgmReceiverSourceGsi, + pgmReceiverSourcePortNumber, + pgmReceiverUniqueInstance, + pgmReceiverNakFailureThresholdTimer, + pgmReceiverNakFailureThreshold, + pgmReceiverNakFailuresLastInterval, + pgmReceiverLastIntervalNakFailures + } + STATUS current + DESCRIPTION + "The number of unrecovered lost packets + exceeded the threshold limit for the + corresponding threshold interval." + ::= { pgmNotifications 7 } + +-- PGM Dlr Source Specific Traps + +pgmNewDlrSourceTrap NOTIFICATION-TYPE + OBJECTS { + pgmDlrSourceSourceGsi, + pgmDlrSourceSourcePortNumber + } + STATUS current + DESCRIPTION + "New Dlr Source Session created." + ::= { pgmNotifications 8 } + +pgmClosedDlrSourceTrap NOTIFICATION-TYPE + OBJECTS { + pgmDlrSourceSourceGsi, + pgmDlrSourceSourcePortNumber + } + STATUS current + DESCRIPTION + "Dlr Source Session closed." + ::= { pgmNotifications 9 } + +-- Conformance information + +pgmMIBConformance OBJECT IDENTIFIER ::= { pgmMIB 3 } +pgmMIBCompliances OBJECT IDENTIFIER ::= { pgmMIBConformance 1 } +pgmMIBGroups OBJECT IDENTIFIER ::= { pgmMIBConformance 2 } + +-- Compliance statements + +pgmNetworkElementMIBCompliance MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement for devices running as PGM + Network Elements." + MODULE -- this module + MANDATORY-GROUPS { pgmNetworkElementMIBGroup } + + ::= { pgmMIBCompliances 1 } + +pgmSourceMIBCompliance MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement for devices running as PGM + sources." + MODULE -- this module + MANDATORY-GROUPS { pgmSourceMIBGroup } + + ::= { pgmMIBCompliances 2 } + +pgmReceiverMIBCompliance MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement for devices running as PGM + receivers." + MODULE -- this module + MANDATORY-GROUPS { pgmReceiverMIBGroup } + + ::= { pgmMIBCompliances 3 } + +pgmDLRMIBCompliance MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement for devices running as PGM + designated local repairers (DLR)." + MODULE -- this module + MANDATORY-GROUPS { pgmDLRMIBGroup, + pgmReceiverMIBGroup } + + ::= { pgmMIBCompliances 4 } + +pgmTrapsMIBCompliance MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement for PGM traps." + MODULE -- this module + MANDATORY-GROUPS { pgmTrapsMIBGroup, + pgmTrapsSourceMIBGroup, + pgmTrapsReceiverMIBGroup, + pgmTrapsDlrSourceMIBGroup } + ::= { pgmMIBCompliances 5 } + +-- Units of conformance + +pgmNetworkElementMIBGroup OBJECT-GROUP + OBJECTS { pgmNeEnable, + pgmNeSessionLifeTime, + pgmNeMaxReXmitStates, + pgmNeMaxSessions, + pgmNeTotalInterfacesNumberOfEntries, + pgmNeIfPgmEnable, + pgmNeIfNakRptInterval, + pgmNeIfNakRptRate, + pgmNeIfNakRdataInterval, + pgmNeIfNakEliminateInterval, + pgmNeIfReXmitStates, + pgmNeIfReXmitTimedOut, + pgmNeIfInSpms, + pgmNeIfOutSpms, + pgmNeIfInParitySpms, + pgmNeIfOutParitySpms, + pgmNeIfInRdata, + pgmNeIfOutRdata, + pgmNeIfInParityRdata, + pgmNeIfOutParityRdata, + pgmNeIfInRdataNoSessionErrors, + pgmNeIfUniqueNaks, + pgmNeIfInNaks, + pgmNeIfOutNaks, + pgmNeIfUniqueParityNaks, + pgmNeIfInParityNaks, + pgmNeIfOutParityNaks, + pgmNeIfInNakNoSessionErrors, + pgmNeIfInNakSeqErrors, + pgmNeIfInParityNakTgErrors, + pgmNeIfInNnaks, + pgmNeIfOutNnaks, + pgmNeIfInParityNnaks, + pgmNeIfOutParityNnaks, + pgmNeIfInNnakNoSessionErrors, + pgmNeIfInNcfs, + pgmNeIfOutNcfs, + pgmNeIfInParityNcfs, + pgmNeIfOutParityNcfs, + pgmNeIfInNcfNoSessionErrors, + pgmNeIfInRedirectNcfs, + pgmNeIfMalformed, + pgmNeIfSpmFromSource, + pgmNeIfSpmBadSqn, + pgmNeIfSpmError, + pgmNeIfPollRandomIgnore, + pgmNeIfPollTsiStateError, + pgmNeIfPollParentError, + pgmNeIfPollTypeError, + pgmNeIfPollError, + pgmNeIfPollSuccess, + pgmNeIfPollOriginated, + pgmNeIfPolrNoState, + pgmNeIfPolrError, + pgmNeIfPolrParityError, + pgmNeIfPolrSuccess, + pgmNeIfPolrOriginated, + pgmNeIfNcfError, + pgmNeIfNcfParityError, + pgmNeIfNcfPartialParity, + pgmNeIfNcfReceived, + pgmNeIfNcfAnticipated, + pgmNeIfNcfRedirecting, + pgmNeIfNakEliminated, + pgmNeIfNakError, + pgmNeIfNakParityError, + pgmNeIfNNakEliminated, + pgmNeIfNNakError, + pgmNeIfNNakParityError, + pgmNeIfNNakCongestionReports, + pgmNeIfNakRetryExpired, + pgmNeIfNakRetryExpiredDLR, + pgmNeIfNakForwardedDLR, + pgmNeIfNakRetransmitted, + pgmNeIfRdataEliminatedOIF, + pgmNeIfRdataEliminatedSqn, + pgmNeIfInRdataFragments, + pgmNeIfRdataFragmentsNoSessionErrors, + pgmNeIfRdataFragmentsEliminatedOIF, + pgmNeIfRdataFragmentsEliminatedSqn, + pgmNeIfOutRdataFragments, + pgmNeTotalTsiNumberOfEntries, + pgmNeTsiStateBits, + pgmNeTsiDataDestinationPort, + pgmNeTsiSourceAddress, + pgmNeTsiGroupAddress, + pgmNeTsiUpstreamAddress, + pgmNeTsiUpstreamIfIndex, + pgmNeTsiDlrAddress, + pgmNeTsiSessionTrailEdgeSeq, + pgmNeTsiSessionIncrSeq, + pgmNeTsiLeadEdgeSeq, + pgmNeTsiInSpms, + pgmNeTsiOutSpms, + pgmNeTsiInParitySpms, + pgmNeTsiOutParitySpms, + pgmNeTsiTotalReXmitStates, + pgmNeTsiTotalReXmitTimedOut, + pgmNeTsiInRdata, + pgmNeTsiOutRdata, + pgmNeTsiInParityRdata, + pgmNeTsiOutParityRdata, + pgmNeTsiInRdataNoStateErrors, + pgmNeTsiUniqueNaks, + pgmNeTsiInNaks, + pgmNeTsiOutNaks, + pgmNeTsiUniqueParityNaks, + pgmNeTsiInParityNaks, + pgmNeTsiOutParityNaks, + pgmNeTsiInNakSeqErrors, + pgmNeTsiInNnaks, + pgmNeTsiOutNnaks, + pgmNeTsiInParityNnaks, + pgmNeTsiOutParityNnaks, + pgmNeTsiInNcfs, + pgmNeTsiOutNcfs, + pgmNeTsiInParityNcfs, + pgmNeTsiOutParityNcfs, + pgmNeTsiSpmSequenceNumber, + pgmNeTsiTransmissionGroupSize, + pgmNeTsiTimeout, + pgmNeTsiLastTtl, + pgmNeTsiLinkLossRate, + pgmNeTsiPathLossRate, + pgmNeTsiReceiverLossRate, + pgmNeTsiCongestionReportLead, + pgmNeTsiCongestionReportWorstReceiver, + pgmNeTsiRtxNumberOfEntries, + pgmNeTsiRtxReqParityTgCount, + pgmNeTsiRtxTimeout, + pgmNeTsiRtxStateBits, + pgmNeTsiRtxIfNumberOfEntries, + pgmNeTsiRtxIfPacketCount, + pgmNeTsiPolrNumberOfEntries, + pgmNeTsiPolrSequenceNumber, + pgmNeTsiPollNumberOfEntries, + pgmNeTsiPollSequence, + pgmNeTsiPollChildBackoff, + pgmNeTsiPollMask, + pgmNeTsiPollPeriod, + pgmNeTsiPollCount, + pgmNeTsiPollTimeout } + STATUS current + DESCRIPTION + "A collection of objects to support + management of PGM Network Elements." + ::= { pgmMIBGroups 1 } + +pgmSourceMIBGroup OBJECT-GROUP + OBJECTS { pgmSourceSaveDefaults, + pgmSourceLastUpdateTime, + pgmSourceDefaultTtl, + pgmSourceDefaultAdvMode, + pgmSourceDefaultLateJoin, + pgmSourceDefaultTxwMaxRte, + pgmSourceDefaultTxwSecs, + pgmSourceDefaultTxwAdvSecs, + pgmSourceDefaultAdvIvl, + pgmSourceDefaultSpmIvl, + pgmSourceDefaultSpmHeartBeatIvlMin, + pgmSourceDefaultSpmHeartBeatIvlMax, + pgmSourceDefaultRdataBackoffIvl, + pgmSourceDefaultFECProactiveParitySize, + pgmSourceDefaultGroupAddress, + pgmSourceUpdateSinceLastSave, + pgmSourceNumberOfEntries, + pgmSourceSourceAddress, + pgmSourceGroupAddress, + pgmSourceDestPort, + pgmSourceSourceGsi, + pgmSourceSourcePortNumber, + pgmSourceTtl, + pgmSourceAdvMode, + pgmSourceLateJoin, + pgmSourceTxwMaxRte, + pgmSourceTxwSecs, + pgmSourceTxwAdvSecs, + pgmSourceAdvIvl, + pgmSourceSpmIvl, + pgmSourceSpmHeartBeatIvlMin, + pgmSourceSpmHeartBeatIvlMax, + pgmSourceRdataBackoffIvl, + pgmSourceFEC, + pgmSourceFECTransmissionGrpSize, + pgmSourceFECProactiveParitySize, + pgmSourceSpmPathAddress, + pgmSourceDataBytesSent, + pgmSourceDataMsgsSent, + pgmSourceBytesBuffered, + pgmSourceMsgsBuffered, + pgmSourceBytesRetransmitted, + pgmSourceMsgsRetransmitted, + pgmSourceBytesSent, + pgmSourceRawNaksReceived, + pgmSourceNaksIgnored, + pgmSourceCksumErrors, + pgmSourceMalformedNaks, + pgmSourcePacketsDiscarded, + pgmSourceNaksRcvd, + pgmSourceParityBytesRetransmitted, + pgmSourceSelectiveBytesRetransmited, + pgmSourceParityMsgsRetransmitted, + pgmSourceSelectiveMsgsRetransmitted, + pgmSourceBytesAdmit, + pgmSourceMsgsAdmit, + pgmSourceParityNakPacketsReceived, + pgmSourceSelectiveNakPacketsReceived, + pgmSourceParityNaksReceived, + pgmSourceSelectiveNaksReceived, + pgmSourceParityNaksIgnored, + pgmSourceSelectiveNaksIgnored, + pgmSourceAckErrors, + pgmSourcePgmCCAcker, + pgmSourceTransmissionCurrentRate, + pgmSourceAckPacketsReceived, + pgmSourceNNakPacketsReceived, + pgmSourceParityNNakPacketsReceived, + pgmSourceSelectiveNNakPacketsReceived, + pgmSourceNNaksReceived, + pgmSourceParityNNaksReceived, + pgmSourceSelectiveNNaksReceived, + pgmSourceNNakErrors } + STATUS current + DESCRIPTION + "A collection of objects to support management of + PGM sources." + ::= { pgmMIBGroups 2 } + +pgmReceiverMIBGroup OBJECT-GROUP + OBJECTS { pgmReceiverSaveDefaults, + pgmReceiverLastUpdateTime, + pgmReceiverDefaultNakBackoffIvl, + pgmReceiverDefaultNakRepeatIvl, + pgmReceiverDefaultNakNcfRetries, + pgmReceiverDefaultNakRdataIvl, + pgmReceiverDefaultNakDataRetries, + pgmReceiverDefaultSendNaks, + pgmReceiverDefaultLateJoin, + pgmReceiverDefaultNakTtl, + pgmReceiverDefaultDeliveryOrder, + pgmReceiverDefaultNextPgmHop, + pgmReceiverDefaultGroupAddress, + pgmReceiverUpdateSinceLastSave, + pgmReceiverDefaultNakFailureThresholdTimer, + pgmReceiverDefaultNakFailureThreshold, + pgmReceiverNumberOfEntries, + pgmReceiverGroupAddress, + pgmReceiverDestPort, + pgmReceiverSourceAddress, + pgmReceiverLastHop, + pgmReceiverSourceGsi, + pgmReceiverSourcePortNumber, + pgmReceiverUniqueInstance, + pgmReceiverNakBackoffIvl, + pgmReceiverNakRepeatIvl, + pgmReceiverNakNcfRetries, + pgmReceiverNakRdataIvl, + pgmReceiverNakDataRetries, + pgmReceiverSendNaks, + pgmReceiverLateJoin, + pgmReceiverNakTtl, + pgmReceiverDeliveryOrder, + pgmReceiverMcastNaks, + pgmReceiverNakFailureThresholdTimer, + pgmReceiverNakFailureThreshold, + pgmReceiverDataBytesReceived, + pgmReceiverDataMsgsReceived, + pgmReceiverNaksSent, + pgmReceiverNaksRetransmitted, + pgmReceiverNakFailures, + pgmReceiverBytesReceived, + pgmReceiverNaksSuppressed, + pgmReceiverCksumErrors, + pgmReceiverMalformedSpms, + pgmReceiverMalformedOdata, + pgmReceiverMalformedRdata, + pgmReceiverMalformedNcfs, + pgmReceiverPacketsDiscarded, + pgmReceiverLosses, + pgmReceiverBytesDeliveredToApp, + pgmReceiverMsgsDeliveredToApp, + pgmReceiverDupSpms, + pgmReceiverDupDatas, + pgmReceiverDupParities, + pgmReceiverNakPacketsSent, + pgmReceiverParityNakPacketsSent, + pgmReceiverSelectiveNakPacketsSent, + pgmReceiverParityNaksSent, + pgmReceiverSelectiveNaksSent, + pgmReceiverParityNaksRetransmitted, + pgmReceiverSelectiveNaksRetransmitted, + pgmReceiverNaksFailed, + pgmReceiverParityNaksFailed, + pgmReceiverSelectiveNaksFailed, + pgmReceiverNaksFailedRxwAdvanced, + pgmReceiverNaksFaledNcfRetriesExceeded, + pgmReceiverNaksFailedDataRetriesExceeded, + pgmReceiverNaksFailedGenExpired, + pgmReceiverNakFailuresDelivered, + pgmReceiverParityNaksSuppressed, + pgmReceiverSelectiveNaksSuppressed, + pgmReceiverNakErrors, + pgmReceiverOutstandingParityNaks, + pgmReceiverOutstandingSelectiveNaks, + pgmReceiverLastActivity, + pgmReceiverNakSvcTimeMin, + pgmReceiverNakSvcTimeMean, + pgmReceiverNakSvcTimeMax, + pgmReceiverNakFailTimeMin, + pgmReceiverNakFailTimeMean, + pgmReceiverNakFailTimeMax, + pgmReceiverNakTransmitMin, + pgmReceiverNakTransmitMean, + pgmReceiverNakTransmitMax, + pgmReceiverAcksSent, + pgmReceiverRxwTrail, + pgmReceiverRxwLead, + pgmReceiverNakFailuresLastInterval, + pgmReceiverLastIntervalNakFailures } + STATUS current + DESCRIPTION + "A collection of objects to support management of + PGM receivers." + ::= { pgmMIBGroups 3 } + +pgmDLRMIBGroup OBJECT-GROUP + OBJECTS { pgmDlrSaveDefaults, + pgmDlrLastUpdateTime, + pgmDlrGroupAddress, + pgmDlrCacheRtx, + pgmDlrActivityIvl, + pgmDlrMaxRate, + pgmDlrParentNeAddr, + pgmDlrUpdateSinceLastSave, + pgmDlrSourceNumberOfEntries, + pgmDlrSourceGroupAddress, + pgmDlrSourceSourceGsi, + pgmDlrSourceSourcePortNumber, + pgmDlrSourceGroupTtl, + pgmDlrSourceRdataBackoffIvl, + pgmDlrSourceRdataMsgsSent, + pgmDlrSourceRdataBytesSent, + pgmDlrSourceBytesSent, + pgmDlrSourceNaksRcvd, + pgmDlrSourceNaksIgnored, + pgmDlrSourceNakErrors, + pgmDlrSourceDiscards, + pgmDlrSourceCksumErrors, + pgmDlrSourceNNaksSent, + pgmDlrSourceBytesBuffered, + pgmDlrSourceMsgsBuffered, + pgmDlrSourceParityBytesRetransmitted, + pgmDlrSourceSelectiveBytesRetransmited, + pgmDlrSourceParityMsgsRetransmitted, + pgmDlrSourceSelectiveMsgsRetransmitted, + pgmDlrSourceBytesAdmit, + pgmDlrSourceMsgsAdmit, + pgmDlrSourceNakPacketsReceived, + pgmDlrSourceParityNakPacketsReceived, + pgmDlrSourceSelectiveNakPacketsReceived, + pgmDlrSourceParityNaksReceived, + pgmDlrSourceSelectiveNaksReceived, + pgmDlrSourceParityNaksIgnored, + pgmDlrSourceSelectiveNaksIgnored, + pgmDlrSourceAckErrors, + pgmDlrSourceNNakErrors, + pgmDlrSourceAckPacketsReceived, + pgmDlrSourceNNakPacketsReceived, + pgmDlrSourceParityNNakPacketsReceived, + pgmDlrSourceSelectiveNNakPacketsReceived, + pgmDlrSourceNNaksReceived, + pgmDlrSourceParityNNaksReceived, + pgmDlrSourceSelectiveNNaksReceived } + STATUS current + DESCRIPTION + "A collection of objects to support management of + PGM designated local repairers (DLR)." + ::= { pgmMIBGroups 4 } + +pgmTrapsMIBGroup NOTIFICATION-GROUP + NOTIFICATIONS { pgmStart, + pgmStop } + STATUS current + DESCRIPTION + "A collection of objects to support pgm + specific traps." + ::= { pgmMIBGroups 5 } + +pgmTrapsSourceMIBGroup NOTIFICATION-GROUP + NOTIFICATIONS { pgmNewSourceTrap, + pgmClosedSourceTrap } + STATUS current + DESCRIPTION + "A collection of objects to support pgm + source specific traps." + ::= { pgmMIBGroups 6 } + +pgmTrapsReceiverMIBGroup NOTIFICATION-GROUP + NOTIFICATIONS { pgmNewReceiverTrap, + pgmClosedReceiverTrap, + pgmNakFailuresTrap } + STATUS current + DESCRIPTION + "A collection of objects to support pgm + receiver specific traps." + ::= { pgmMIBGroups 7 } + +pgmTrapsDlrSourceMIBGroup NOTIFICATION-GROUP + NOTIFICATIONS { pgmNewDlrSourceTrap, + pgmClosedDlrSourceTrap } + STATUS current + DESCRIPTION + "A collection of objects to support pgm + dlr source specific traps." + ::= { pgmMIBGroups 8 } + + +END + diff --git a/3rdparty/openpgm-svn-r1135/pgm/mld-semantics.txt b/3rdparty/openpgm-svn-r1135/pgm/mld-semantics.txt new file mode 100644 index 0000000..135400d --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/mld-semantics.txt @@ -0,0 +1,52 @@ + previous request following request return + ----------------- ----------------- ----------- + MCAST_JOIN_GROUP MCAST_JOIN_GROUP EADDRINUSE + MCAST_JOIN_GROUP MCAST_LEAVE_GROUP 0 + MCAST_JOIN_GROUP MCAST_JOIN_SOURCE_GROUP EINVAL + MCAST_JOIN_GROUP MCAST_LEAVE_SOURCE_GROUP EINVAL + MCAST_JOIN_GROUP MCAST_BLOCK_SOURCE 0 + MCAST_JOIN_SOURCE_GROUP MCAST_JOIN_GROUP EADDRINUSE + MCAST_JOIN_SOURCE_GROUP MCAST_LEAVE_GROUP 0 + MCAST_JOIN_SOURCE_GROUP MCAST_JOIN_SOURCE_GROUP (*1) + MCAST_JOIN_SOURCE_GROUP MCAST_LEAVE_SOURCE_GROUP (*2) + MCAST_JOIN_SOURCE_GROUP MCAST_BLOCK_SOURCE EINVAL + MCAST_JOIN_SOURCE_GROUP MCAST_UNBLOCK_SOURCE EINVAL + MCAST_BLOCK_SOURCE MCAST_JOIN_GROUP EADDRINUSE + MCAST_BLOCK_SOURCE MCAST_LEAVE_GROUP 0 + MCAST_BLOCK_SOURCE MCAST_JOIN_SOURCE_GROUP EINVAL + MCAST_BLOCK_SOURCE MCAST_LEAVE_SOURCE_GROUP EINVAL + MCAST_BLOCK_SOURCE MCAST_BLOCK_SOURCE (*1) + MCAST_BLOCK_SOURCE MCAST_UNBLOCK_SOURCE (*2) + +(*1) EADDRNOTAVAIL if source address is same of filtered one. Otherwise 0. +(*2) EADDRNOTAVAIL if source address is not same of filtered one. Otherwise 0. + + +http://planete.inria.fr/Hitoshi.Asaeda/mldv2/README.txt + + +The following steps apply for any-source applications: + + Use MCAST_JOIN_GROUP to join a group. + Use MCAST_BLOCK_SOURCE to turn off a given source, if required. + Use MCAST_UNBLOCK_SOURCE to re-allow a blocked source, if required. + Use MCAST_LEAVE_GROUP to leave the group. + +The following steps apply for controlled-source applications: + + Use MCAST_JOIN_SOURCE_GROUP to join each group/source pair. + Use MCAST_LEAVE_SOURCE_GROUP to leave each group/source, or use MCAST_LEAVE_GROUP to leave all sources, if the same group address is used by all sources. + +The following steps apply for any-source applications: + + Use IP_ADD_MEMBERSHIP to join a group (IPV6_ADD_MEMBERSHIP for IPv6). + Use IP_BLOCK_SOURCE to turn off a given source, if required. + Use IP_UNBLOCK_SOURCE to re-allow a blocked source, if required. + Use IP_DROP_MEMBERSHIP to leave the group (IPV6_DROP_MEMBERSHIP for IPv6). + +The following steps apply for controlled-source applications: + + Use IP_ADD_SOURCE_MEMBERSHIP to join each group/source pair. + Use IP_DROP_SOURCE_MEMBERSHIP to leave each group/source, or use IP_DROP_MEMBERSHIP to leave all sources, if the same group address is used by all sources. + +http://msdn.microsoft.com/en-us/library/ms738558(VS.85).aspx diff --git a/3rdparty/openpgm-svn-r1135/pgm/msfec.txt b/3rdparty/openpgm-svn-r1135/pgm/msfec.txt new file mode 100644 index 0000000..4b2c23a --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/msfec.txt @@ -0,0 +1,33 @@ +FEC parameters for Microsoft's PGM stack + + +FECBlockSize (n) [FECGroupSize+1, 255] +Maximum number of packets that can be sent for any group, including original data and parity packets. Maximum and default value is 255. + +FECProActivePackets +Number of packets to send proactively with each group. Use this option when the network is dispersed, and upstream NAK requests are expensive. + +FECGroupSize (k) [2, 128] +Number of packets to be treated as one group for the purpose of computing parity packets. Group size must be a power of two. In lossy networks, keep the group size relatively small. + +fFECOnDemandParityEnabled +Specifies whether the sender is enabled for sending parity repair packets. When TRUE, receivers should only request parity repair packets. + + +Reed Solomon codes: + + encode/decode time (us) +RS(255, 2) 4/6 +RS(255, 4) 7/10 +RS(255, 8) 14/18 +RS(255, 16) 29/34 +RS(255, 32) 57/64 +RS(255, 64) 119/134 +RS(255, 128) 236/fail(278) + +reference platform: Intel Xeon CPU 3.20Ghz + + +Implementation exact copy of Luigi Rizzo FEC code as demonstrated in RMDP: + +http://info.iet.unipi.it/~luigi/fec.html diff --git a/3rdparty/openpgm-svn-r1135/pgm/nametoindex.c b/3rdparty/openpgm-svn-r1135/pgm/nametoindex.c new file mode 100644 index 0000000..28444d1 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/nametoindex.c @@ -0,0 +1,249 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Windows interface name to interface index function. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef _WIN32 +# include +# include +#endif +#include +#include + + +//#define NAMETOINDEX_DEBUG + +#define MAX_TRIES 3 +#define DEFAULT_BUFFER_SIZE 4096 + + +#ifdef _WIN32 +static inline +void* +_pgm_heap_alloc ( + const size_t n_bytes + ) +{ +# ifdef CONFIG_USE_HEAPALLOC + return HeapAlloc (GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, n_bytes); +# else + return pgm_malloc (n_bytes); +# endif +} + +static inline +void +_pgm_heap_free ( + void* mem + ) +{ +# ifdef CONFIG_USE_HEAPALLOC + HeapFree (GetProcessHeap(), 0, mem); +# else + pgm_free (mem); +# endif +} + +/* Retrieve adapter index via name. + * Wine edition: First try GetAdapterIndex() then fallback to enumerating + * adapters via GetAdaptersInfo(). + * + * On error returns zero, no errors are defined. + */ + +static +unsigned /* type matching if_nametoindex() */ +_pgm_getadaptersinfo_nametoindex ( + const sa_family_t iffamily, + const char* ifname + ) +{ + pgm_return_val_if_fail (NULL != ifname, 0); + + pgm_assert (AF_INET6 != iffamily); + + DWORD dwRet, ifIndex; + ULONG ulOutBufLen = DEFAULT_BUFFER_SIZE; + PIP_ADAPTER_INFO pAdapterInfo = NULL; + PIP_ADAPTER_INFO pAdapter = NULL; + +/* loop to handle interfaces coming online causing a buffer overflow + * between first call to list buffer length and second call to enumerate. + */ + for (unsigned i = MAX_TRIES; i; i--) + { + pgm_debug ("IP_ADAPTER_INFO buffer length %lu bytes.", ulOutBufLen); + pAdapterInfo = (IP_ADAPTER_INFO*)_pgm_heap_alloc (ulOutBufLen); + dwRet = GetAdaptersInfo (pAdapterInfo, &ulOutBufLen); + if (ERROR_BUFFER_OVERFLOW == dwRet) { + _pgm_heap_free (pAdapterInfo); + pAdapterInfo = NULL; + } else { + break; + } + } + + switch (dwRet) { + case ERROR_SUCCESS: /* NO_ERROR */ + break; + case ERROR_BUFFER_OVERFLOW: + pgm_warn (_("GetAdaptersInfo repeatedly failed with ERROR_BUFFER_OVERFLOW.")); + if (pAdapterInfo) + _pgm_heap_free (pAdapterInfo); + return 0; + default: + pgm_warn (_("GetAdaptersInfo failed")); + if (pAdapterInfo) + _pgm_heap_free (pAdapterInfo); + return 0; + } + + for (pAdapter = pAdapterInfo; + pAdapter; + pAdapter = pAdapter->Next) + { + for (IP_ADDR_STRING *pIPAddr = &pAdapter->IpAddressList; + pIPAddr; + pIPAddr = pIPAddr->Next) + { +/* skip null adapters */ + if (strlen (pIPAddr->IpAddress.String) == 0) + continue; + + if (0 == strncmp (ifname, pAdapter->AdapterName, IF_NAMESIZE)) { + ifIndex = pAdapter->Index; + _pgm_heap_free (pAdapterInfo); + return ifIndex; + } + } + } + + if (pAdapterInfo) + _pgm_heap_free (pAdapterInfo); + return 0; +} + +/* Retrieve adapter index via name. + * Windows edition: First try GetAdapterIndex() then fallback to enumerating + * adapters via GetAdaptersAddresses(). + * + * On error returns zero, no errors are defined. + */ + +static +unsigned /* type matching if_nametoindex() */ +_pgm_getadaptersaddresses_nametoindex ( + const sa_family_t iffamily, + const char* ifname + ) +{ + pgm_return_val_if_fail (NULL != ifname, 0); + + ULONG ifIndex; + DWORD dwSize = DEFAULT_BUFFER_SIZE, dwRet; + IP_ADAPTER_ADDRESSES *pAdapterAddresses = NULL, *adapter; + +/* first see if GetAdapterIndex is working + */ + dwRet = GetAdapterIndex ((const LPWSTR)ifname, &ifIndex); + if (NO_ERROR == dwRet) + return ifIndex; + +/* fallback to finding index via iterating adapter list */ + +/* loop to handle interfaces coming online causing a buffer overflow + * between first call to list buffer length and second call to enumerate. + */ + for (unsigned i = MAX_TRIES; i; i--) + { + pAdapterAddresses = (IP_ADAPTER_ADDRESSES*)_pgm_heap_alloc (dwSize); + dwRet = GetAdaptersAddresses (AF_UNSPEC, + GAA_FLAG_SKIP_ANYCAST | + GAA_FLAG_SKIP_DNS_SERVER | + GAA_FLAG_SKIP_FRIENDLY_NAME | + GAA_FLAG_SKIP_MULTICAST, + NULL, + pAdapterAddresses, + &dwSize); + if (ERROR_BUFFER_OVERFLOW == dwRet) { + _pgm_heap_free (pAdapterAddresses); + pAdapterAddresses = NULL; + } else { + break; + } + } + + switch (dwRet) { + case ERROR_SUCCESS: + break; + case ERROR_BUFFER_OVERFLOW: + pgm_warn (_("GetAdaptersAddresses repeatedly failed with ERROR_BUFFER_OVERFLOW")); + if (pAdapterAddresses) + _pgm_heap_free (pAdapterAddresses); + return 0; + default: + pgm_warn (_("GetAdaptersAddresses failed")); + if (pAdapterAddresses) + _pgm_heap_free (pAdapterAddresses); + return 0; + } + + for (adapter = pAdapterAddresses; + adapter; + adapter = adapter->Next) + { + if (0 == strcmp (ifname, adapter->AdapterName)) { + ifIndex = AF_INET6 == iffamily ? adapter->Ipv6IfIndex : adapter->IfIndex; + _pgm_heap_free (pAdapterAddresses); + return ifIndex; + } + } + + if (pAdapterAddresses) + _pgm_heap_free (pAdapterAddresses); + return 0; +} +#endif /* _WIN32 */ + +/* Retrieve interface index for a specified adapter name. + * On error returns zero, no errors are defined. + */ + +unsigned /* type matching if_nametoindex() */ +pgm_if_nametoindex ( +#ifndef _WIN32 + PGM_GNUC_UNUSED const sa_family_t iffamily, +#else + const sa_family_t iffamily, +#endif + const char* ifname + ) +{ + pgm_return_val_if_fail (NULL != ifname, 0); + +#ifndef _WIN32 + return if_nametoindex (ifname); +#elif defined(CONFIG_TARGET_WINE) + return _pgm_getadaptersinfo_nametoindex (iffamily, ifname); +#else + return _pgm_getadaptersaddresses_nametoindex (iffamily, ifname); +#endif +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/nametoindex.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/nametoindex.c.c89.patch new file mode 100644 index 0000000..127eb4a --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/nametoindex.c.c89.patch @@ -0,0 +1,89 @@ +--- nametoindex.c 2010-05-21 11:34:30.000000000 +0800 ++++ nametoindex.c89 2010-08-04 12:57:20.000000000 +0800 +@@ -78,6 +78,7 @@ + + pgm_assert (AF_INET6 != iffamily); + ++ { + DWORD dwRet, ifIndex; + ULONG ulOutBufLen = DEFAULT_BUFFER_SIZE; + PIP_ADAPTER_INFO pAdapterInfo = NULL; +@@ -86,7 +87,9 @@ + /* loop to handle interfaces coming online causing a buffer overflow + * between first call to list buffer length and second call to enumerate. + */ +- for (unsigned i = MAX_TRIES; i; i--) ++ { ++ unsigned i; ++ for (i = MAX_TRIES; i; i--) + { + pgm_debug ("IP_ADAPTER_INFO buffer length %lu bytes.", ulOutBufLen); + pAdapterInfo = (IP_ADAPTER_INFO*)_pgm_heap_alloc (ulOutBufLen); +@@ -98,6 +101,7 @@ + break; + } + } ++ } + + switch (dwRet) { + case ERROR_SUCCESS: /* NO_ERROR */ +@@ -118,7 +122,9 @@ + pAdapter; + pAdapter = pAdapter->Next) + { +- for (IP_ADDR_STRING *pIPAddr = &pAdapter->IpAddressList; ++ { ++ IP_ADDR_STRING *pIPAddr; ++ for (pIPAddr = &pAdapter->IpAddressList; + pIPAddr; + pIPAddr = pIPAddr->Next) + { +@@ -132,11 +138,13 @@ + return ifIndex; + } + } ++ } + } + + if (pAdapterInfo) + _pgm_heap_free (pAdapterInfo); + return 0; ++ } + } + + /* Retrieve adapter index via name. +@@ -155,6 +163,7 @@ + { + pgm_return_val_if_fail (NULL != ifname, 0); + ++ { + ULONG ifIndex; + DWORD dwSize = DEFAULT_BUFFER_SIZE, dwRet; + IP_ADAPTER_ADDRESSES *pAdapterAddresses = NULL, *adapter; +@@ -170,7 +179,9 @@ + /* loop to handle interfaces coming online causing a buffer overflow + * between first call to list buffer length and second call to enumerate. + */ +- for (unsigned i = MAX_TRIES; i; i--) ++ { ++ unsigned i; ++ for (i = MAX_TRIES; i; i--) + { + pAdapterAddresses = (IP_ADAPTER_ADDRESSES*)_pgm_heap_alloc (dwSize); + dwRet = GetAdaptersAddresses (AF_UNSPEC, +@@ -188,6 +199,7 @@ + break; + } + } ++ } + + switch (dwRet) { + case ERROR_SUCCESS: +@@ -218,6 +230,7 @@ + if (pAdapterAddresses) + _pgm_heap_free (pAdapterAddresses); + return 0; ++ } + } + #endif /* _WIN32 */ + diff --git a/3rdparty/openpgm-svn-r1135/pgm/net-snmp.txt b/3rdparty/openpgm-svn-r1135/pgm/net-snmp.txt new file mode 100644 index 0000000..549bcc2 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/net-snmp.txt @@ -0,0 +1,34 @@ +net-snmp is hard coded on mib location, only the mib file names can +be specified outside of snmptranslate. + +$ mkdir -p ~/.snmp/mibs +$ cp mibs/PGM-MIB-petrova-01.txt ~/.snmp/mibs + +Basic test: + +$ snmptranslate -m ALL -IR pgmMIB +PGM-MIB::pgmMIB + +Display full pretty tree: + +$ snmptranslate -m ALL -Tp -IR pgmMIB ++--pgmMIB(112) + | + +--pgm(1) + | | + | +--pgmNetworkElement(1) + | | | +... + + +Now the framework tool can be used: + +$ env MIBS="+ALL" mib2c pgmMIB + +... + +To run with SNMP install snmpd, enable "master agentx" and walk: + +$ env MIBS="+ALL" snmpwalk -v 1 -c public localhost pgmMIB +End of MIB + diff --git a/3rdparty/openpgm-svn-r1135/pgm/net.c b/3rdparty/openpgm-svn-r1135/pgm/net.c new file mode 100644 index 0000000..eb5f403 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/net.c @@ -0,0 +1,181 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * network send wrapper. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#ifdef CONFIG_HAVE_POLL +# include +#endif +#ifndef _WIN32 +# include +# include +# include +#endif +#include +#include +#include +#include + + +#define NET_DEBUG + + +#if !defined(ENETUNREACH) && defined(WSAENETUNREACH) +# define ENETUNREACH WSAENETUNREACH +#endif +#if !defined(EHOSTUNREACH) && defined(WSAEHOSTUNREACH) +# define EHOSTUNREACH WSAEHOSTUNREACH +#endif +#if !defined(ENOBUFS) && defined(WSAENOBUFS) +# define ENOBUFS WSAENOBUFS +#endif + + +/* locked and rate regulated sendto + * + * on success, returns number of bytes sent. on error, -1 is returned, and + * errno set appropriately. + */ + +ssize_t +pgm_sendto_hops ( + pgm_sock_t* sock, + bool use_rate_limit, + bool use_router_alert, + int hops, /* -1 == system default */ + const void* restrict buf, + size_t len, + const struct sockaddr* restrict to, + socklen_t tolen + ) +{ + pgm_assert( NULL != sock ); + pgm_assert( NULL != buf ); + pgm_assert( len > 0 ); + pgm_assert( NULL != to ); + pgm_assert( tolen > 0 ); + +#ifdef NET_DEBUG + char saddr[INET_ADDRSTRLEN]; + pgm_sockaddr_ntop (to, saddr, sizeof(saddr)); + pgm_debug ("pgm_sendto (sock:%p use_rate_limit:%s use_router_alert:%s buf:%p len:%zu to:%s [toport:%d] tolen:%d)", + (const void*)sock, + use_rate_limit ? "TRUE" : "FALSE", + use_router_alert ? "TRUE" : "FALSE", + (const void*)buf, + len, + saddr, + ntohs (((const struct sockaddr_in*)to)->sin_port), + (int)tolen); +#endif + + const int send_sock = use_router_alert ? sock->send_with_router_alert_sock : sock->send_sock; + + if (use_rate_limit && + !pgm_rate_check (&sock->rate_control, len, sock->is_nonblocking)) + { + errno = ENOBUFS; + return (const ssize_t)-1; + } + + if (!use_router_alert && sock->can_send_data) + pgm_mutex_lock (&sock->send_mutex); + if (-1 != hops) + pgm_sockaddr_multicast_hops (send_sock, sock->send_gsr.gsr_group.ss_family, hops); + + ssize_t sent = sendto (send_sock, buf, len, 0, to, (socklen_t)tolen); + pgm_debug ("sendto returned %zd", sent); + if (sent < 0) { + int save_errno = pgm_sock_errno(); + if (PGM_UNLIKELY(errno != ENETUNREACH && /* Network is unreachable */ + errno != EHOSTUNREACH && /* No route to host */ + errno != EAGAIN)) /* would block on non-blocking send */ + { +#ifdef CONFIG_HAVE_POLL +/* poll for cleared socket */ + struct pollfd p = { + .fd = send_sock, + .events = POLLOUT, + .revents = 0 + }; + const int ready = poll (&p, 1, 500 /* ms */); +#else + fd_set writefds; + FD_ZERO(&writefds); + FD_SET(send_sock, &writefds); + struct timeval tv = { + .tv_sec = 0, + .tv_usec = 500 /* ms */ * 1000 + }; + const int ready = select (1, NULL, &writefds, NULL, &tv); +#endif /* CONFIG_HAVE_POLL */ + if (ready > 0) + { + sent = sendto (send_sock, buf, len, 0, to, (socklen_t)tolen); + if ( sent < 0 ) + { + save_errno = pgm_sock_errno(); + pgm_warn (_("sendto() %s failed: %s"), + inet_ntoa( ((const struct sockaddr_in*)to)->sin_addr ), + pgm_sock_strerror (save_errno)); + } + } + else if (ready == 0) + { + pgm_warn (_("sendto() %s failed: socket timeout."), + inet_ntoa( ((const struct sockaddr_in*)to)->sin_addr )); + } + else + { + save_errno = pgm_sock_errno(); + pgm_warn (_("blocked socket failed: %s"), + pgm_sock_strerror (save_errno)); + } + } + } + +/* revert to default value hop limit */ + if (-1 != hops) + pgm_sockaddr_multicast_hops (send_sock, sock->send_gsr.gsr_group.ss_family, sock->hops); + if (!use_router_alert && sock->can_send_data) + pgm_mutex_unlock (&sock->send_mutex); + return sent; +} + +/* socket helper, for setting pipe ends non-blocking + * + * on success, returns 0. on error, returns -1, and sets errno appropriately. + */ + +int +pgm_set_nonblocking ( + int fd[2] + ) +{ +/* pre-conditions */ + pgm_assert (fd[0]); + pgm_assert (fd[1]); + + pgm_sockaddr_nonblocking (fd[0], TRUE); + pgm_sockaddr_nonblocking (fd[1], TRUE); + return 0; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/net.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/net.c.c89.patch new file mode 100644 index 0000000..fd1d1f0 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/net.c.c89.patch @@ -0,0 +1,67 @@ +--- net.c 2010-05-21 11:35:44.000000000 +0800 ++++ net.c89 2010-08-04 15:21:42.000000000 +0800 +@@ -72,19 +72,22 @@ + pgm_assert( tolen > 0 ); + + #ifdef NET_DEBUG ++ { + char saddr[INET_ADDRSTRLEN]; + pgm_sockaddr_ntop (to, saddr, sizeof(saddr)); +- pgm_debug ("pgm_sendto (sock:%p use_rate_limit:%s use_router_alert:%s buf:%p len:%zu to:%s [toport:%d] tolen:%d)", ++ pgm_debug ("pgm_sendto (sock:%p use_rate_limit:%s use_router_alert:%s buf:%p len:%lu to:%s [toport:%d] tolen:%d)", + (const void*)sock, + use_rate_limit ? "TRUE" : "FALSE", + use_router_alert ? "TRUE" : "FALSE", + (const void*)buf, +- len, ++ (unsigned long)len, + saddr, + ntohs (((const struct sockaddr_in*)to)->sin_port), + (int)tolen); ++ } + #endif + ++ { + const int send_sock = use_router_alert ? sock->send_with_router_alert_sock : sock->send_sock; + + if (use_rate_limit && +@@ -97,8 +100,9 @@ + if (!use_router_alert && sock->can_send_data) + pgm_mutex_lock (&sock->send_mutex); + ++ { + ssize_t sent = sendto (send_sock, buf, len, 0, to, (socklen_t)tolen); +- pgm_debug ("sendto returned %zd", sent); ++ pgm_debug ("sendto returned %ld", (signed long)sent); + if (sent < 0) { + int save_errno = pgm_sock_errno(); + if (PGM_UNLIKELY(errno != ENETUNREACH && /* Network is unreachable */ +@@ -114,14 +118,14 @@ + }; + const int ready = poll (&p, 1, 500 /* ms */); + #else ++ int ready; + fd_set writefds; ++ struct timeval tv; + FD_ZERO(&writefds); + FD_SET(send_sock, &writefds); +- struct timeval tv = { +- .tv_sec = 0, +- .tv_usec = 500 /* ms */ * 1000 +- }; +- const int ready = select (1, NULL, &writefds, NULL, &tv); ++ tv.tv_sec = 0; ++ tv.tv_usec = 500 /* ms */ * 1000; ++ ready = select (1, NULL, &writefds, NULL, &tv); + #endif /* CONFIG_HAVE_POLL */ + if (ready > 0) + { +@@ -151,6 +155,8 @@ + if (!use_router_alert && sock->can_send_data) + pgm_mutex_unlock (&sock->send_mutex); + return sent; ++ } ++ } + } + + /* socket helper, for setting pipe ends non-blocking diff --git a/3rdparty/openpgm-svn-r1135/pgm/net_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/net_unittest.c new file mode 100644 index 0000000..fc684ca --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/net_unittest.c @@ -0,0 +1,375 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for network send wrapper. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include + + +/* mock state */ + +#define pgm_rate_check mock_pgm_rate_check +#define sendto mock_sendto +#define poll mock_poll +#define select mock_select +#define fcntl mock_fcntl + +#define NET_DEBUG +#include "net.c" + + +static +pgm_sock_t* +generate_sock (void) +{ + pgm_sock_t* sock = g_malloc0 (sizeof(pgm_sock_t)); + return sock; +} + +static +char* +flags_string ( + int flags + ) +{ + static char s[1024]; + + s[0] = '\0'; + if (flags & MSG_OOB) + strcat (s, "MSG_OOB"); +#define MSG(flag) \ + do { \ + if (flags & flag) { \ + strcat (s, s[0] ? ("|" #flag) : (#flag)); \ + } \ + } while (0) +#ifdef MSG_PEEK + MSG(MSG_PEEK); +#endif +#ifdef MSG_DONTROUTE + MSG(MSG_DONTROUTE); +#endif +#ifdef MSG_CTRUNC + MSG(MSG_CTRUNC); +#endif +#ifdef MSG_PROXY + MSG(MSG_PROXY); +#endif +#ifdef MSG_TRUNC + MSG(MSG_TRUNC); +#endif +#ifdef MSG_DONTWAIT + MSG(MSG_DONTWAIT); +#endif +#ifdef MSG_EOR + MSG(MSG_EOR); +#endif +#ifdef MSG_WAITALL + MSG(MSG_WAITALL); +#endif +#ifdef MSG_FIN + MSG(MSG_FIN); +#endif +#ifdef MSG_SYN + MSG(MSG_SYN); +#endif +#ifdef MSG_CONFIRM + MSG(MSG_CONFIRM); +#endif +#ifdef MSG_RST + MSG(MSG_RST); +#endif +#ifdef MSG_ERRQUEUE + MSG(MSG_ERRQUEUE); +#endif +#ifdef MSG_NOSIGNAL + MSG(MSG_NOSIGNAL); +#endif +#ifdef MSG_MORE + MSG(MSG_MORE); +#endif +#ifdef MSG_CMSG_CLOEXEC + MSG(MSG_CMSG_CLOEXEC); +#endif + if (!s[0]) { + if (flags) + sprintf (s, "0x%x", flags); + else + strcpy (s, "0"); + } + return s; +} + + +/* mock functions for external references */ + +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return 0; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_rate_check ( + pgm_rate_t* bucket, + const size_t data_size, + const bool is_nonblocking + ) +{ + g_debug ("mock_pgm_rate_check (bucket:%p data-size:%zu is-nonblocking:%s)", + (gpointer)bucket, data_size, is_nonblocking ? "TRUE" : "FALSE"); + return TRUE; +} + +ssize_t +mock_sendto ( + int s, + const void* buf, + size_t len, + int flags, + const struct sockaddr* to, + socklen_t tolen + ) +{ + char saddr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop (to, saddr, sizeof(saddr)); + g_debug ("mock_sendto (s:%i buf:%p len:%d flags:%s to:%s tolen:%d)", + s, buf, len, flags_string (flags), saddr, tolen); + return len; +} + +#ifdef CONFIG_HAVE_POLL +int +mock_poll ( + struct pollfd* fds, + nfds_t nfds, + int timeout + ) +{ + g_debug ("mock_poll (fds:%p nfds:%d timeout:%d)", + (gpointer)fds, (int)nfds, timeout); + return 0; +} +#else +int +mock_select ( + int nfds, + fd_set* readfds, + fd_set* writefds, + fd_set* exceptfds, + struct timeval* timeout + ) +{ + g_debug ("mock_select (nfds:%d readfds:%p writefds:%p exceptfds:%p timeout:%p)", + nfds, (gpointer)readfds, (gpointer)writefds, (gpointer)exceptfds, (gpointer)timeout); + return 0; +} +#endif + +int +mock_fcntl ( + int fd, + int cmd, + ... + ) +{ + long arg; + va_list args; + if (F_GETFL == cmd) { + g_debug ("mock_fcntl (fd:%d cmd:F_GETFL)", fd); + return 0; + } + if (F_SETFL == cmd) { + va_start (args, cmd); + arg = va_arg (args, long); + va_end (args); + g_debug ("mock_fcntl (fd:%d cmd:F_SETFL arg:%ld)", fd, arg); + return arg; + } + g_assert_not_reached(); +} + + +/* target: + * ssize_t + * pgm_sendto ( + * pgm_sock_t* sock, + * bool use_rate_limit, + * bool use_router_alert, + * const void* buf, + * size_t len, + * const struct sockaddr* to, + * socklen_t tolen + * ) + */ + +START_TEST (test_sendto_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + const char* buf = "i am not a string"; + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr ("172.12.90.1") + }; + gssize len = pgm_sendto (sock, FALSE, FALSE, buf, sizeof(buf), (struct sockaddr*)&addr, sizeof(addr)); + fail_unless (sizeof(buf) == len, "sendto underrun"); +} +END_TEST + +START_TEST (test_sendto_fail_001) +{ + const char* buf = "i am not a string"; + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr ("172.12.90.1") + }; + gssize len = pgm_sendto (NULL, FALSE, FALSE, buf, sizeof(buf), (struct sockaddr*)&addr, sizeof(addr)); + fail ("reached"); +} +END_TEST + +START_TEST (test_sendto_fail_002) +{ + pgm_sock_t* sock = generate_sock (); + const char* buf = "i am not a string"; + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr ("172.12.90.1") + }; + gssize len = pgm_sendto (sock, FALSE, FALSE, NULL, sizeof(buf), (struct sockaddr*)&addr, sizeof(addr)); + fail ("reached"); +} +END_TEST + +START_TEST (test_sendto_fail_003) +{ + pgm_sock_t* sock = generate_sock (); + const char* buf = "i am not a string"; + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr ("172.12.90.1") + }; + gssize len = pgm_sendto (sock, FALSE, FALSE, buf, 0, (struct sockaddr*)&addr, sizeof(addr)); + fail ("reached"); +} +END_TEST + +START_TEST (test_sendto_fail_004) +{ + pgm_sock_t* sock = generate_sock (); + const char* buf = "i am not a string"; + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr ("172.12.90.1") + }; + gssize len = pgm_sendto (sock, FALSE, FALSE, buf, sizeof(buf), NULL, sizeof(addr)); + fail ("reached"); +} +END_TEST + +START_TEST (test_sendto_fail_005) +{ + pgm_sock_t* sock = generate_sock (); + const char* buf = "i am not a string"; + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr ("172.12.90.1") + }; + gssize len = pgm_sendto (sock, FALSE, FALSE, buf, sizeof(buf), (struct sockaddr*)&addr, 0); + fail ("reached"); +} +END_TEST + +/* target: + * int + * pgm_set_nonblocking ( + * int filedes[2] + * ) + */ + +START_TEST (test_set_nonblocking_pass_001) +{ + int filedes[2] = { fileno (stdout), fileno (stderr) }; + int retval = pgm_set_nonblocking (filedes); +} +END_TEST + +START_TEST (test_set_nonblocking_fail_001) +{ + int filedes[2] = { 0, 0 }; + int retval = pgm_set_nonblocking (filedes); + fail ("reached"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_sendto = tcase_create ("sendto"); + suite_add_tcase (s, tc_sendto); + tcase_add_test (tc_sendto, test_sendto_pass_001); + tcase_add_test_raise_signal (tc_sendto, test_sendto_fail_001, SIGABRT); + tcase_add_test_raise_signal (tc_sendto, test_sendto_fail_002, SIGABRT); + tcase_add_test_raise_signal (tc_sendto, test_sendto_fail_003, SIGABRT); + tcase_add_test_raise_signal (tc_sendto, test_sendto_fail_004, SIGABRT); + tcase_add_test_raise_signal (tc_sendto, test_sendto_fail_005, SIGABRT); + + TCase* tc_set_nonblocking = tcase_create ("set-nonblocking"); + suite_add_tcase (s, tc_set_nonblocking); + tcase_add_test (tc_set_nonblocking, test_set_nonblocking_pass_001); + tcase_add_test_raise_signal (tc_set_nonblocking, test_set_nonblocking_fail_001, SIGABRT); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/options.txt b/3rdparty/openpgm-svn-r1135/pgm/options.txt new file mode 100644 index 0000000..cb01da0 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/options.txt @@ -0,0 +1,158 @@ + OPT_LENGTH 0x00 - Option's Length + +pgm_opt_header +pgm_opt_length + +first option, always present. + +-------------------------------------------------------------------------------- + + OPT_FRAGMENT 0x01 - Fragmentation + +pgm_opt_header +pgm_opt_fragment + +may be present for odata, rdata. 'MAY' exist for others, although a bit strange. + +-------------------------------------------------------------------------------- + + OPT_NAK_LIST 0x02 - List of NAK entries + +pgm_opt_header +pgm_opt_nak_list + +may be present for naks. + +-------------------------------------------------------------------------------- + + OPT_JOIN 0x03 - Late Joining + +pgm_opt_header +pgm_opt_join + +may be present for odata, rdata, spm. + +requires SPM to learn NLA already so not overly useful with odata/rdata, could be +used with video streaming to last i-frame data sequence number. + +-------------------------------------------------------------------------------- + + OPT_REDIRECT 0x07 - Redirect + +pgm_opt_header +pgm_opt_redirect +pgm_opt_redirect6 + +should be present for polrs from a dlr. + +-------------------------------------------------------------------------------- + + OPT_SYN 0x0D - Synchronization + +pgm_opt_header +pgm_opt_syn + +must only appear with odata or rdata. + +-------------------------------------------------------------------------------- + + OPT_FIN 0x0E - Session Fin receivers, conventional + feedbackish + +pgm_opt_header +opt_opt_fin + +may be present for odata, rdata, must appear in following spms. + +-------------------------------------------------------------------------------- + + OPT_RST 0x0F - Session Reset + +pgm_opt_header +pgm_opt_rst + +must only appear in spms. not many 'unrecoverable error conditions' exist though. + +-------------------------------------------------------------------------------- + + OPT_PARITY + +must appear in odata or rdata to indicate pro-active or on-demand parity data, +nak to request parity repair data, ncf to confirm parity nak. + + + OPT_VAR_PKTLEN + +may be present in odata or data to indicate variable size packets. + + + OPT_PARITY_PRM 0x08 - Forward Error Correction Parameters + +pgm_opt_header +pgm_opt_parity_prm + +appended to spms to inform of pro-active or on-demand parity. + +-------------------------------------------------------------------------------- + + OPT_PARITY_GRP 0x09 - Forward Error Correction Group Number + +pgm_opt_parity_grp + +appended to odata and rdata parity packets. + +-------------------------------------------------------------------------------- + + OPT_CURR_TGSIZE 0x0A - Forward Error Correction Group Size + +pgm_opt_curr_tgsize + +must appear in last odata or rdata packet of variable transmission group, may +appear in spms. + +-------------------------------------------------------------------------------- + + OPT_CR 0x10 - Congestion Report + +pgm_opt_header +pgm_opt_cr + +-------------------------------------------------------------------------------- + + OPT_CRQST 0x11 - Congestion Report Request + +pgm_opt_header +pgm_opt_crqst + +-------------------------------------------------------------------------------- + + OPT_NAK_BO_IVL 0x04 - NAK Back-Off Interval + +pgm_opt_header +pgm_opt_nak_bo_ivl + +-------------------------------------------------------------------------------- + + OPT_NAK_BO_RNG 0x05 - NAK Back-Off Range + +pgm_opt_header +pgm_opt_nak_bo_rng + +-------------------------------------------------------------------------------- + + OPT_NBR_UNREACH 0x0B - Neighbor Unreachable + +pgm_opt_header +pgm_opt_nbr_unreach + +-------------------------------------------------------------------------------- + + OPT_PATH_NLA 0x0C - Path NLA + +pgm_opt_header +pgm_opt_path_nla +pgm_opt6_path_nla + +-------------------------------------------------------------------------------- + + OPT_INVALID 0x7F - Option invalidated diff --git a/3rdparty/openpgm-svn-r1135/pgm/packet_parse.c b/3rdparty/openpgm-svn-r1135/pgm/packet_parse.c new file mode 100644 index 0000000..65c36a9 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/packet_parse.c @@ -0,0 +1,619 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM packet formats, RFC 3208. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __STDC_FORMAT_MACROS +#ifdef _MSC_VER +# include +#else +# include +#endif +#include +#include +#include + + +//#define PACKET_DEBUG + +#ifndef PACKET_DEBUG +# define PGM_DISABLE_ASSERT +#endif + + +/* locals */ + +static bool pgm_parse (struct pgm_sk_buff_t*const restrict, pgm_error_t**restrict); + + +/* Parse a raw-IP packet for IP and PGM header and any payload. + */ + +#define PGM_MIN_SIZE ( \ + sizeof(struct pgm_ip) + /* IPv4 header */ \ + sizeof(struct pgm_header) /* PGM header */ \ + ) + +bool +pgm_parse_raw ( + struct pgm_sk_buff_t* const restrict skb, /* data will be modified */ + struct sockaddr* const restrict dst, + pgm_error_t** restrict error + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + pgm_assert (NULL != dst); + + pgm_debug ("pgm_parse_raw (skb:%p dst:%p error:%p)", + (const void*)skb, (const void*)dst, (const void*)error); + +/* minimum size should be IPv4 header plus PGM header, check IP version later */ + if (PGM_UNLIKELY(skb->len < PGM_MIN_SIZE)) + { + pgm_set_error (error, + PGM_ERROR_DOMAIN_PACKET, + PGM_ERROR_BOUNDS, + _("IP packet too small at %" PRIu16 " bytes, expecting at least %" PRIu16 " bytes."), + skb->len, (uint16_t)PGM_MIN_SIZE); + return FALSE; + } + +/* IP packet header: IPv4 + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |Version| HL | ToS | Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Fragment ID |R|D|M| Fragment Offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | Protocol | Checksum | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Source IP Address | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Destination IP Address | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | IP Options when present ... + * +-+-+-+-+-+-+-+-+-+-+-+-+ ... + * | Data ... + * +-+-+- ... + * + * IPv6 + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |Version| Traffic Class | Flow Label | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Payload Length | Next Header | Hop Limit | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * | Source IP Address | + * | | + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * | Destination IP Address | + * | | + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | IP Options when present ... + * +-+-+-+-+-+-+-+-+-+-+-+-+ ... + * | Data ... + * +-+-+- ... + * + */ + +/* decode IP header */ + const struct pgm_ip* ip = (struct pgm_ip*)skb->data; + switch (ip->ip_v) { + case 4: { + struct sockaddr_in* sin = (struct sockaddr_in*)dst; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = ip->ip_dst.s_addr; + break; + } + + case 6: + pgm_set_error (error, + PGM_ERROR_DOMAIN_PACKET, + PGM_ERROR_AFNOSUPPORT, + _("IPv6 is not supported for raw IP header parsing.")); + return FALSE; + + default: + pgm_set_error (error, + PGM_ERROR_DOMAIN_PACKET, + PGM_ERROR_AFNOSUPPORT, + _("IP header reports an invalid version %d."), + ip->ip_v); + return FALSE; + } + + const size_t ip_header_length = ip->ip_hl * 4; /* IP header length in 32bit octets */ + if (PGM_UNLIKELY(ip_header_length < sizeof(struct pgm_ip))) + { + pgm_set_error (error, + PGM_ERROR_DOMAIN_PACKET, + PGM_ERROR_BOUNDS, + _("IP header reports an invalid header length %zu bytes."), + ip_header_length); + return FALSE; + } + +#ifndef CONFIG_HOST_ORDER_IP_LEN + size_t packet_length = ntohs (ip->ip_len); /* total packet length */ +#else + size_t packet_length = ip->ip_len; /* total packet length */ +#endif + + +/* ip_len can equal packet_length - ip_header_length in FreeBSD/NetBSD + * Stevens/Fenner/Rudolph, Unix Network Programming Vol.1, p.739 + * + * RFC3828 allows partial packets such that len < packet_length with UDP lite + */ + if (skb->len == packet_length + ip_header_length) { + packet_length += ip_header_length; + } + + if (PGM_UNLIKELY(skb->len < packet_length)) { /* redundant: often handled in kernel */ + pgm_set_error (error, + PGM_ERROR_DOMAIN_PACKET, + PGM_ERROR_BOUNDS, + _("IP packet received at %" PRIu16 " bytes whilst IP header reports %zu bytes."), + skb->len, packet_length); + return FALSE; + } + +/* packets that fail checksum will generally not be passed upstream except with rfc3828 + */ +#ifdef PGM_CHECK_IN_CKSUM + const uint16_t sum = in_cksum (data, packet_length, 0); + if (PGM_UNLIKELY(0 != sum)) { + const uint16_t ip_sum = ntohs (ip->ip_sum); + pgm_set_error (error, + PGM_ERROR_DOMAIN_PACKET, + PGM_ERROR_CKSUM, + _("IP packet checksum mismatch, reported 0x%x whilst calculated 0x%x."), + ip_sum, sum); + return FALSE; + } +#endif + +/* fragmentation offset, bit 0: 0, bit 1: do-not-fragment, bit 2: more-fragments */ +#ifndef CONFIG_HOST_ORDER_IP_OFF + const uint16_t offset = ntohs (ip->ip_off); +#else + const uint16_t offset = ip->ip_off; +#endif + if (PGM_UNLIKELY((offset & 0x1fff) != 0)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_PACKET, + PGM_ERROR_PROTO, + _("IP header reports packet fragmentation, offset %u."), + offset & 0x1fff); + return FALSE; + } + +/* PGM payload, header looks as follows: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Source Port | Destination Port | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Options | Checksum | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Global Source ID ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ... Global Source ID | TSDU Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type specific data ... + * +-+-+-+-+-+-+-+-+-+- ... + */ + + skb->pgm_header = (void*)( (char*)skb->data + ip_header_length ); + +/* advance DATA pointer to PGM packet */ + skb->data = skb->pgm_header; + skb->len -= ip_header_length; + return pgm_parse (skb, error); +} + +bool +pgm_parse_udp_encap ( + struct pgm_sk_buff_t*const restrict skb, /* will be modified */ + pgm_error_t** restrict error + ) +{ + pgm_assert (NULL != skb); + + if (PGM_UNLIKELY(skb->len < sizeof(struct pgm_header))) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_PACKET, + PGM_ERROR_BOUNDS, + _("UDP payload too small for PGM packet at %" PRIu16 " bytes, expecting at least %zu bytes."), + skb->len, sizeof(struct pgm_header)); + return FALSE; + } + +/* DATA payload is PGM packet, no headers */ + skb->pgm_header = skb->data; + return pgm_parse (skb, error); +} + +/* will modify packet contents to calculate and check PGM checksum + */ +static +bool +pgm_parse ( + struct pgm_sk_buff_t*const restrict skb, /* will be modified to calculate checksum */ + pgm_error_t** restrict error + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + +/* pgm_checksum == 0 means no transmitted checksum */ + if (skb->pgm_header->pgm_checksum) + { + const uint16_t sum = skb->pgm_header->pgm_checksum; + skb->pgm_header->pgm_checksum = 0; + const uint16_t pgm_sum = pgm_csum_fold (pgm_csum_partial ((const char*)skb->pgm_header, skb->len, 0)); + skb->pgm_header->pgm_checksum = sum; + if (PGM_UNLIKELY(pgm_sum != sum)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_PACKET, + PGM_ERROR_CKSUM, + _("PGM packet checksum mismatch, reported 0x%x whilst calculated 0x%x."), + pgm_sum, sum); + return FALSE; + } + } else { + if (PGM_ODATA == skb->pgm_header->pgm_type || + PGM_RDATA == skb->pgm_header->pgm_type) + { + pgm_set_error (error, + PGM_ERROR_DOMAIN_PACKET, + PGM_ERROR_PROTO, + _("PGM checksum missing whilst mandatory for %cDATA packets."), + PGM_ODATA == skb->pgm_header->pgm_type ? 'O' : 'R'); + return FALSE; + } + pgm_debug ("No PGM checksum :O"); + } + +/* copy packets source transport identifier */ + memcpy (&skb->tsi.gsi, skb->pgm_header->pgm_gsi, sizeof(pgm_gsi_t)); + skb->tsi.sport = skb->pgm_header->pgm_sport; + return TRUE; +} + +/* 8.1. Source Path Messages (SPM) + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SPM's Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Trailing Edge Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Leading Edge Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Path NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * NLA = Network Layer Address + * NLA AFI = NLA Address Family Indicator: rfc 1700 (ADDRESS FAMILY NUMBERS) + * => Path NLA = IP address of last network element + */ + +#define PGM_MIN_SPM_SIZE ( sizeof(struct pgm_spm) ) + +bool +pgm_verify_spm ( + const struct pgm_sk_buff_t*const skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + + const struct pgm_spm* spm = (const struct pgm_spm*)skb->data; + switch (ntohs (spm->spm_nla_afi)) { +/* truncated packet */ + case AFI_IP6: + if (PGM_UNLIKELY(skb->len < sizeof(struct pgm_spm6))) + return FALSE; + break; + case AFI_IP: + if (PGM_UNLIKELY(skb->len < sizeof(struct pgm_spm))) + return FALSE; + break; + + default: + return FALSE; + } + + return TRUE; +} + +/* 14.7.1. Poll Request + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLL's Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLL's Round | POLL's Sub-type | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Path NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | POLL's Back-off Interval | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Random String | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Matching Bit-Mask | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Sent to ODATA multicast group with IP Router Alert option. + */ + +#define PGM_MIN_POLL_SIZE ( sizeof(struct pgm_poll) ) + +bool +pgm_verify_poll ( + const struct pgm_sk_buff_t*const skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + + const struct pgm_poll* poll4 = (const struct pgm_poll*)skb->data; + switch (ntohs (poll4->poll_nla_afi)) { +/* truncated packet */ + case AFI_IP6: + if (PGM_UNLIKELY(skb->len < sizeof(struct pgm_poll6))) + return FALSE; + break; + case AFI_IP: + if (PGM_UNLIKELY(skb->len < sizeof(struct pgm_poll))) + return FALSE; + break; + + default: + return FALSE; + } + + return TRUE; +} + +/* 14.7.2. Poll Response + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLR's Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLR's Round | reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +bool +pgm_verify_polr ( + const struct pgm_sk_buff_t*const skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + +/* truncated packet */ + if (PGM_UNLIKELY(skb->len < sizeof(struct pgm_polr))) + return FALSE; + return TRUE; +} + +/* 8.2. Data Packet + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Data Packet Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Trailing Edge Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Data ... + * +-+-+- ... + */ + +/* no verification api */ + +/* 8.3. NAK + * + * Technically the AFI of the source and multicast group can be different + * but that would be very wibbly wobbly. One example is using a local DLR + * with a IPv4 address to reduce NAK cost for recovery on wide IPv6 + * distribution. + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Requested Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Source NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Multicast Group NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | Option Extensions when present ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... + */ + +#define PGM_MIN_NAK_SIZE ( sizeof(struct pgm_nak) ) + +bool +pgm_verify_nak ( + const struct pgm_sk_buff_t*const skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + + pgm_debug ("pgm_verify_nak (skb:%p)", (const void*)skb); + +/* truncated packet */ + if (PGM_UNLIKELY(skb->len < PGM_MIN_NAK_SIZE)) + return FALSE; + + const struct pgm_nak* nak = (struct pgm_nak*)skb->data; + const uint16_t nak_src_nla_afi = ntohs (nak->nak_src_nla_afi); + uint16_t nak_grp_nla_afi = 0; + +/* check source NLA: unicast address of the ODATA sender */ + switch (nak_src_nla_afi) { + case AFI_IP: + nak_grp_nla_afi = ntohs (nak->nak_grp_nla_afi); + break; + + case AFI_IP6: + nak_grp_nla_afi = ntohs (((const struct pgm_nak6*)nak)->nak6_grp_nla_afi); + break; + + default: + return FALSE; + } + +/* check multicast group NLA */ + switch (nak_grp_nla_afi) { + case AFI_IP6: + switch (nak_src_nla_afi) { +/* IPv4 + IPv6 NLA */ + case AFI_IP: + if (PGM_UNLIKELY(skb->len < ( sizeof(struct pgm_nak) + sizeof(struct in6_addr) - sizeof(struct in_addr) ))) + return FALSE; + break; + +/* IPv6 + IPv6 NLA */ + case AFI_IP6: + if (PGM_UNLIKELY(skb->len < sizeof(struct pgm_nak6))) + return FALSE; + break; + } + + case AFI_IP: + break; + + default: + return FALSE; + } + + return TRUE; +} + +/* 8.3. N-NAK + */ + +bool +pgm_verify_nnak ( + const struct pgm_sk_buff_t*const skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + + return pgm_verify_nak (skb); +} + +/* 8.3. NCF + */ + +bool +pgm_verify_ncf ( + const struct pgm_sk_buff_t*const skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + + return pgm_verify_nak (skb); +} + +/* 13.6. SPM Request + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... + */ + +bool +pgm_verify_spmr ( + PGM_GNUC_UNUSED const struct pgm_sk_buff_t*const skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + + return TRUE; +} + +/* PGMCC: ACK + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RX_MAX | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Received Packet Bitmap | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... + */ + +#define PGM_MIN_ACK_SIZE ( sizeof(struct pgm_ack) ) + +bool +pgm_verify_ack ( + PGM_GNUC_UNUSED const struct pgm_sk_buff_t*const skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + + return TRUE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/packet_parse.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/packet_parse.c.c89.patch new file mode 100644 index 0000000..fad2ec9 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/packet_parse.c.c89.patch @@ -0,0 +1,121 @@ +--- packet_parse.c 2010-08-04 15:32:09.000000000 +0800 ++++ packet_parse.c89 2010-08-04 15:32:40.000000000 +0800 +@@ -122,6 +122,7 @@ + */ + + /* decode IP header */ ++ { + const struct pgm_ip* ip = (struct pgm_ip*)skb->data; + switch (ip->ip_v) { + case 4: { +@@ -147,6 +148,7 @@ + return FALSE; + } + ++ { + const size_t ip_header_length = ip->ip_hl * 4; /* IP header length in 32bit octets */ + if (PGM_UNLIKELY(ip_header_length < sizeof(struct pgm_ip))) + { +@@ -158,6 +160,7 @@ + return FALSE; + } + ++ { + #ifndef CONFIG_HOST_ORDER_IP_LEN + size_t packet_length = ntohs (ip->ip_len); /* total packet length */ + #else +@@ -186,6 +189,7 @@ + /* packets that fail checksum will generally not be passed upstream except with rfc3828 + */ + #if PGM_CHECK_IN_CKSUM ++ { + const uint16_t sum = in_cksum (data, packet_length, 0); + if (PGM_UNLIKELY(0 != sum)) { + const uint16_t ip_sum = ntohs (ip->ip_sum); +@@ -196,9 +200,11 @@ + ip_sum, sum); + return FALSE; + } ++ } + #endif + + /* fragmentation offset, bit 0: 0, bit 1: do-not-fragment, bit 2: more-fragments */ ++ { + #ifndef CONFIG_HOST_ORDER_IP_OFF + const uint16_t offset = ntohs (ip->ip_off); + #else +@@ -236,6 +242,10 @@ + skb->data = skb->pgm_header; + skb->len -= ip_header_length; + return pgm_parse (skb, error); ++ } ++ } ++ } ++ } + } + + bool +@@ -277,6 +287,7 @@ + { + const uint16_t sum = skb->pgm_header->pgm_checksum; + skb->pgm_header->pgm_checksum = 0; ++ { + const uint16_t pgm_sum = pgm_csum_fold (pgm_csum_partial ((const char*)skb->pgm_header, skb->len, 0)); + skb->pgm_header->pgm_checksum = sum; + if (PGM_UNLIKELY(pgm_sum != sum)) { +@@ -287,6 +298,7 @@ + pgm_sum, sum); + return FALSE; + } ++ } + } else { + if (PGM_ODATA == skb->pgm_header->pgm_type || + PGM_RDATA == skb->pgm_header->pgm_type) +@@ -340,6 +352,7 @@ + /* pre-conditions */ + pgm_assert (NULL != skb); + ++ { + const struct pgm_spm* spm = (const struct pgm_spm*)skb->data; + switch (ntohs (spm->spm_nla_afi)) { + /* truncated packet */ +@@ -357,6 +370,7 @@ + } + + return TRUE; ++ } + } + + /* 14.7.1. Poll Request +@@ -394,6 +408,7 @@ + /* pre-conditions */ + pgm_assert (NULL != skb); + ++ { + const struct pgm_poll* poll4 = (const struct pgm_poll*)skb->data; + switch (ntohs (poll4->poll_nla_afi)) { + /* truncated packet */ +@@ -411,6 +426,7 @@ + } + + return TRUE; ++ } + } + + /* 14.7.2. Poll Response +@@ -497,6 +513,7 @@ + if (PGM_UNLIKELY(skb->len < PGM_MIN_NAK_SIZE)) + return FALSE; + ++ { + const struct pgm_nak* nak = (struct pgm_nak*)skb->data; + const uint16_t nak_src_nla_afi = ntohs (nak->nak_src_nla_afi); + uint16_t nak_grp_nla_afi = 0; +@@ -540,6 +557,7 @@ + } + + return TRUE; ++ } + } + + /* 8.3. N-NAK diff --git a/3rdparty/openpgm-svn-r1135/pgm/packet_parse_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/packet_parse_unittest.c new file mode 100644 index 0000000..94b06d2 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/packet_parse_unittest.c @@ -0,0 +1,382 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for PGM packet handling. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* mock state */ + +#define PACKET_DEBUG +#include "packet_parse.c" + + +static +struct pgm_sk_buff_t* +generate_raw_pgm (void) +{ + const char source[] = "i am not a string"; + const guint source_len = sizeof(source); + struct pgm_sk_buff_t* skb; + GError* err = NULL; + + skb = pgm_alloc_skb (1500); + skb->sock = (pgm_sock_t*)0x1; + skb->tstamp = 0x1; + skb->data = skb->head; + skb->len = sizeof(struct pgm_ip) + sizeof(struct pgm_header) + sizeof(struct pgm_data) + source_len; + skb->tail = (guint8*)skb->data + skb->len; + +/* add IP header */ + struct pgm_ip* iphdr = skb->data; + iphdr->ip_hl = sizeof(struct pgm_ip) / 4; + iphdr->ip_v = 4; + iphdr->ip_tos = 0; + iphdr->ip_len = g_htons (skb->len); + iphdr->ip_id = 0; + iphdr->ip_off = 0; + iphdr->ip_ttl = 16; + iphdr->ip_p = IPPROTO_PGM; + iphdr->ip_sum = 0; + iphdr->ip_src.s_addr = inet_addr ("127.0.0.1"); + iphdr->ip_dst.s_addr = inet_addr ("127.0.0.2"); + +/* add PGM header */ + struct pgm_header* pgmhdr = (gpointer)(iphdr + 1); + pgmhdr->pgm_sport = g_htons ((guint16)1000); + pgmhdr->pgm_dport = g_htons ((guint16)7500); + pgmhdr->pgm_type = PGM_ODATA; + pgmhdr->pgm_options = 0; + pgmhdr->pgm_gsi[0] = 1; + pgmhdr->pgm_gsi[1] = 2; + pgmhdr->pgm_gsi[2] = 3; + pgmhdr->pgm_gsi[3] = 4; + pgmhdr->pgm_gsi[4] = 5; + pgmhdr->pgm_gsi[5] = 6; + pgmhdr->pgm_tsdu_length = g_htons (source_len); + +/* add ODATA header */ + struct pgm_data* datahdr = (gpointer)(pgmhdr + 1); + datahdr->data_sqn = g_htonl ((guint32)0); + datahdr->data_trail = g_htonl ((guint32)-1); + +/* add payload */ + gpointer data = (gpointer)(datahdr + 1); + memcpy (data, source, source_len); + +/* finally PGM checksum */ + pgmhdr->pgm_checksum = 0; + pgmhdr->pgm_checksum = pgm_csum_fold (pgm_csum_partial (pgmhdr, sizeof(struct pgm_header) + sizeof(struct pgm_data) + source_len, 0)); + +/* and IP checksum */ + iphdr->ip_sum = pgm_inet_checksum (skb->head, skb->len, 0); + + return skb; +} + +static +struct pgm_sk_buff_t* +generate_udp_encap_pgm (void) +{ + const char source[] = "i am not a string"; + const guint source_len = sizeof(source); + struct pgm_sk_buff_t* skb; + GError* err = NULL; + + skb = pgm_alloc_skb (1500); + skb->sock = (pgm_sock_t*)0x1; + skb->tstamp = 0x1; + skb->data = skb->head; + skb->len = sizeof(struct pgm_header) + sizeof(struct pgm_data) + source_len; + skb->tail = (guint8*)skb->data + skb->len; + +/* add PGM header */ + struct pgm_header* pgmhdr = skb->head; + pgmhdr->pgm_sport = g_htons ((guint16)1000); + pgmhdr->pgm_dport = g_htons ((guint16)7500); + pgmhdr->pgm_type = PGM_ODATA; + pgmhdr->pgm_options = 0; + pgmhdr->pgm_gsi[0] = 1; + pgmhdr->pgm_gsi[1] = 2; + pgmhdr->pgm_gsi[2] = 3; + pgmhdr->pgm_gsi[3] = 4; + pgmhdr->pgm_gsi[4] = 5; + pgmhdr->pgm_gsi[5] = 6; + pgmhdr->pgm_tsdu_length = g_htons (source_len); + +/* add ODATA header */ + struct pgm_data* datahdr = (gpointer)(pgmhdr + 1); + datahdr->data_sqn = g_htonl ((guint32)0); + datahdr->data_trail = g_htonl ((guint32)-1); + +/* add payload */ + gpointer data = (gpointer)(datahdr + 1); + memcpy (data, source, source_len); + +/* finally PGM checksum */ + pgmhdr->pgm_checksum = 0; + pgmhdr->pgm_checksum = pgm_csum_fold (pgm_csum_partial (pgmhdr, sizeof(struct pgm_header) + sizeof(struct pgm_data) + source_len, 0)); + + return skb; +} + +/* mock functions for external references */ + +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return 0; +} + + +/* target: + * bool + * pgm_parse_raw ( + * struct pgm_sk_buff_t* const skb, + * struct sockaddr* const addr, + * pgm_error_t** error + * ) + */ + +START_TEST (test_parse_raw_pass_001) +{ + struct sockaddr_storage addr; + pgm_error_t* err = NULL; + struct pgm_sk_buff_t* skb = generate_raw_pgm (); + gboolean success = pgm_parse_raw (skb, (struct sockaddr*)&addr, &err); + if (!success && err) { + g_error ("Parsing raw packet: %s", err->message); + } + fail_unless (TRUE == success, "parse_raw failed"); + char saddr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&addr, saddr, sizeof(saddr)); + g_message ("Decoded destination NLA: %s", saddr); +} +END_TEST + +START_TEST (test_parse_raw_fail_001) +{ + struct sockaddr_storage addr; + pgm_error_t* err = NULL; + pgm_parse_raw (NULL, (struct sockaddr*)&addr, &err); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_parse_udp_encap ( + * struct pgm_sk_buff_t* const skb, + * pgm_error_t** error + * ) + */ + +START_TEST (test_parse_udp_encap_pass_001) +{ + pgm_error_t* err = NULL; + struct pgm_sk_buff_t* skb = generate_udp_encap_pgm (); + gboolean success = pgm_parse_udp_encap (skb, &err); + if (!success && err) { + g_error ("Parsing UDP encapsulated packet: %s", err->message); + } + fail_unless (TRUE == success, "parse_udp_encap failed"); +} +END_TEST + +START_TEST (test_parse_udp_encap_fail_001) +{ + pgm_error_t* err = NULL; + pgm_parse_udp_encap (NULL, &err); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_verify_spm ( + * struct pgm_sk_buff_t* const skb + * ) + */ + +START_TEST (test_verify_spm_pass_001) +{ +} +END_TEST + +START_TEST (test_verify_spm_fail_001) +{ + pgm_verify_spm (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_verify_spmr ( + * struct pgm_sk_buff_t* const skb + * ) + */ + +START_TEST (test_verify_spmr_pass_001) +{ +} +END_TEST + +START_TEST (test_verify_spmr_fail_001) +{ + pgm_verify_spmr (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_verify_nak ( + * struct pgm_sk_buff_t* const skb + * ) + */ + +START_TEST (test_verify_nak_pass_001) +{ +} +END_TEST + +START_TEST (test_verify_nak_fail_001) +{ + pgm_verify_nak (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_verify_nnak ( + * struct pgm_sk_buff_t* const skb + * ) + */ + +START_TEST (test_verify_nnak_pass_001) +{ +} +END_TEST + +START_TEST (test_verify_nnak_fail_001) +{ + pgm_verify_nnak (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_verify_ncf ( + * struct pgm_sk_buff_t* const skb + * ) + */ + +START_TEST (test_verify_ncf_pass_001) +{ +} +END_TEST + +START_TEST (test_verify_ncf_fail_001) +{ + pgm_verify_ncf (NULL); + fail ("reached"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_parse_raw = tcase_create ("parse-raw"); + suite_add_tcase (s, tc_parse_raw); + tcase_add_test (tc_parse_raw, test_parse_raw_pass_001); + tcase_add_test_raise_signal (tc_parse_raw, test_parse_raw_fail_001, SIGABRT); + + TCase* tc_parse_udp_encap = tcase_create ("parse-udp-encap"); + suite_add_tcase (s, tc_parse_udp_encap); + tcase_add_test (tc_parse_udp_encap, test_parse_udp_encap_pass_001); + tcase_add_test_raise_signal (tc_parse_udp_encap, test_parse_udp_encap_fail_001, SIGABRT); + + TCase* tc_verify_spm = tcase_create ("verify-spm"); + suite_add_tcase (s, tc_verify_spm); + tcase_add_test (tc_verify_spm, test_verify_spm_pass_001); + tcase_add_test_raise_signal (tc_verify_spm, test_verify_spm_fail_001, SIGABRT); + + TCase* tc_verify_spmr = tcase_create ("verify-spmr"); + suite_add_tcase (s, tc_verify_spmr); + tcase_add_test (tc_verify_spmr, test_verify_spmr_pass_001); + tcase_add_test_raise_signal (tc_verify_spmr, test_verify_spmr_fail_001, SIGABRT); + + TCase* tc_verify_nak = tcase_create ("verify-nak"); + suite_add_tcase (s, tc_verify_nak); + tcase_add_test (tc_verify_nak, test_verify_nak_pass_001); + tcase_add_test_raise_signal (tc_verify_nak, test_verify_nak_fail_001, SIGABRT); + + TCase* tc_verify_nnak = tcase_create ("verify-nnak"); + suite_add_tcase (s, tc_verify_nnak); + tcase_add_test (tc_verify_nnak, test_verify_nnak_pass_001); + tcase_add_test_raise_signal (tc_verify_nnak, test_verify_nnak_fail_001, SIGABRT); + + TCase* tc_verify_ncf = tcase_create ("verify-ncf"); + suite_add_tcase (s, tc_verify_ncf); + tcase_add_test (tc_verify_ncf, test_verify_ncf_pass_001); + tcase_add_test_raise_signal (tc_verify_ncf, test_verify_ncf_fail_001, SIGABRT); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/packet_test.c b/3rdparty/openpgm-svn-r1135/pgm/packet_test.c new file mode 100644 index 0000000..267700a --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/packet_test.c @@ -0,0 +1,1162 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM packet formats, RFC 3208. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __STDC_FORMAT_MACROS +#ifdef _MSC_VER +# include +#else +# include +#endif +#include +#include +#ifndef _WIN32 +# include +# include +# include +# include +#endif +#include +#include +#include + + +//#define PACKET_DEBUG + + +static bool pgm_print_spm (const struct pgm_header* const, const void*, const size_t); +static bool pgm_print_poll (const struct pgm_header* const, const void*, const size_t); +static bool pgm_print_polr (const struct pgm_header* const, const void*, const size_t); +static bool pgm_print_odata (const struct pgm_header* const, const void*, const size_t); +static bool pgm_print_rdata (const struct pgm_header* const, const void*, const size_t); +static bool pgm_print_nak (const struct pgm_header* const, const void*, const size_t); +static bool pgm_print_nnak (const struct pgm_header* const, const void*, const size_t); +static bool pgm_print_ncf (const struct pgm_header* const, const void*, const size_t); +static bool pgm_print_spmr (const struct pgm_header* const, const void*, const size_t); +static bool pgm_print_ack (const struct pgm_header* const, const void*, const size_t); +static ssize_t pgm_print_options (const void*, size_t); + +bool +pgm_print_packet ( + const void* data, + size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != data); + pgm_assert (len > 0); + +/* minimum size should be IP header plus PGM header */ + if (len < (sizeof(struct pgm_ip) + sizeof(struct pgm_header))) + { + printf ("Packet size too small: %zu bytes, expecting at least %zu bytes.\n", + len, sizeof(struct pgm_ip) + sizeof(struct pgm_header)); + return FALSE; + } + +/* decode IP header */ + const struct pgm_ip* ip = (const struct pgm_ip*)data; + if (ip->ip_v != 4) /* IP version, 4 or 6 */ + { + puts ("not IP4 packet :/"); /* v6 not currently handled */ + return FALSE; + } + printf ("IP "); + + const size_t ip_header_length = ip->ip_hl * 4; /* IP header length in 32bit octets */ + if (ip_header_length < sizeof(struct pgm_ip)) + { + puts ("bad IP header length :("); + return FALSE; + } + + size_t packet_length = ntohs(ip->ip_len); /* total packet length */ + +/* ip_len can equal packet_length - ip_header_length in FreeBSD/NetBSD + * Stevens/Fenner/Rudolph, Unix Network Programming Vol.1, p.739 + * + * RFC3828 allows partial packets such that len < packet_length with UDP lite + */ + if (len == packet_length + ip_header_length) { + packet_length += ip_header_length; + } + + if (len < packet_length) { /* redundant: often handled in kernel */ + puts ("truncated IP packet"); + return FALSE; + } + +/* TCP Segmentation Offload (TSO) might have zero length here */ + if (packet_length < ip_header_length) { + puts ("bad length :("); + return FALSE; + } + + const uint16_t offset = ntohs(ip->ip_off); + +/* 3 bits routing priority, 4 bits type of service: delay, throughput, reliability, cost */ + printf ("(tos 0x%x", (int)ip->ip_tos); + switch (ip->ip_tos & 0x3) + { + case 1: printf (",ECT(1)"); break; + case 2: printf (",ECT(0)"); break; + case 3: printf (",CE"); break; + default: break; + } + +/* time to live */ + if (ip->ip_ttl >= 1) printf (", ttl %u", ip->ip_ttl); + +/* fragmentation */ +#define IP_RDF 0x8000 +#define IP_DF 0x4000 +#define IP_MF 0x2000 +#define IP_OFFMASK 0x1fff + + printf (", id %u, offset %u, flags [%s%s]", + ntohs(ip->ip_id), + (offset & 0x1fff) * 8, + ((offset & IP_DF) ? "DF" : ""), + ((offset & IP_MF) ? "+" : "")); + printf (", length %zu", packet_length); + +/* IP options */ + if ((ip_header_length - sizeof(struct pgm_ip)) > 0) { + printf (", options ("); + pgm_ipopt_print((const void*)(ip + 1), ip_header_length - sizeof(struct pgm_ip)); + printf (" )"); + } + +/* packets that fail checksum will generally not be passed upstream except with rfc3828 + */ + const uint16_t ip_sum = pgm_inet_checksum(data, packet_length, 0); + if (ip_sum != 0) { + const uint16_t encoded_ip_sum = ntohs(ip->ip_sum); + printf (", bad cksum! %i", encoded_ip_sum); + } + + printf (") "); + +/* fragmentation offset, bit 0: 0, bit 1: do-not-fragment, bit 2: more-fragments */ + if ((offset & 0x1fff) != 0) { + puts ("fragmented packet :/"); + return FALSE; + } + +/* PGM payload, header looks as follows: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Source Port | Destination Port | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Options | Checksum | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Global Source ID ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ... Global Source ID | TSDU Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type specific data ... + * +-+-+-+-+-+-+-+-+-+- ... + */ + const struct pgm_header* pgm_header = (const struct pgm_header*)((const char*)data + ip_header_length); + const size_t pgm_length = packet_length - ip_header_length; + + if (pgm_length < sizeof(pgm_header)) { + puts ("bad packet size :("); + return FALSE; + } + + printf ("%s.%s > ", + pgm_gethostbyaddr((const struct in_addr*)&ip->ip_src), pgm_udpport_string(pgm_header->pgm_sport)); + printf ("%s.%s: PGM\n", + pgm_gethostbyaddr((const struct in_addr*)&ip->ip_dst), pgm_udpport_string(pgm_header->pgm_dport)); + + printf ("type: %s [%i] (version=%i, reserved=%i)\n" + "options: extensions=%s, network-significant=%s, parity packet=%s (variable size=%s)\n" + "global source id: %i.%i.%i.%i.%i.%i\n" + "tsdu length: %i\n", + + /* packet type */ /* packet version */ /* reserved = 0x0 */ + pgm_type_string(pgm_header->pgm_type & 0xf), + (pgm_header->pgm_type & 0xf), ((pgm_header->pgm_type & 0xc0) >> 6), ((pgm_header->pgm_type & 0x30) >> 4), + +/* bit 0 set => one or more option extensions are present */ + ((pgm_header->pgm_options & (0x1 << 7)) ? "true" : "false"), +/* bit 1 set => one or more options are network-significant */ + ((pgm_header->pgm_options & (0x1 << 6)) ? "true" : "false"), +/* bit 7 set => parity packet (OPT_PARITY) */ + ((pgm_header->pgm_options & (0x1 << 0)) ? "true" : "false"), +/* bit 6 set => parity packet for variable packet sizes (OPT_VAR_PKTLEN) */ + ((pgm_header->pgm_options & (0x1 << 1)) ? "true" : "false"), + + pgm_header->pgm_gsi[0], pgm_header->pgm_gsi[1], pgm_header->pgm_gsi[2], pgm_header->pgm_gsi[3], pgm_header->pgm_gsi[4], pgm_header->pgm_gsi[5], + ntohs(pgm_header->pgm_tsdu_length)); + + if (pgm_header->pgm_checksum) + { +#if 0 + const uint16_t encoded_pgm_sum = pgm_header->pgm_checksum; +/* requires modification of data buffer */ + pgm_header->pgm_checksum = 0; + const uint16_t pgm_sum = pgm_csum_fold (pgm_csum_partial((const char*)pgm_header, pgm_length, 0)); + if (pgm_sum != encoded_pgm_sum) { + printf ("PGM checksum incorrect, packet %x calculated %x :(\n", encoded_pgm_sum, pgm_sum); + return FALSE; + } +#endif + } else { + puts ("No PGM checksum :O"); + } + +/* now decode PGM packet types */ + const void* pgm_data = pgm_header + 1; + const size_t pgm_data_length = pgm_length - sizeof(pgm_header); /* can equal zero for SPMR's */ + + bool err = FALSE; + switch (pgm_header->pgm_type) { + case PGM_SPM: err = pgm_print_spm (pgm_header, pgm_data, pgm_data_length); break; + case PGM_POLL: err = pgm_print_poll (pgm_header, pgm_data, pgm_data_length); break; + case PGM_POLR: err = pgm_print_polr (pgm_header, pgm_data, pgm_data_length); break; + case PGM_ODATA: err = pgm_print_odata (pgm_header, pgm_data, pgm_data_length); break; + case PGM_RDATA: err = pgm_print_rdata (pgm_header, pgm_data, pgm_data_length); break; + case PGM_NAK: err = pgm_print_nak (pgm_header, pgm_data, pgm_data_length); break; + case PGM_NNAK: err = pgm_print_nnak (pgm_header, pgm_data, pgm_data_length); break; + case PGM_NCF: err = pgm_print_ncf (pgm_header, pgm_data, pgm_data_length); break; + case PGM_SPMR: err = pgm_print_spmr (pgm_header, pgm_data, pgm_data_length); break; + case PGM_ACK: err = pgm_print_ack (pgm_header, pgm_data, pgm_data_length); break; + default: puts ("unknown packet type :("); break; + } + + return err; +} + +/* 8.1. Source Path Messages (SPM) + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SPM's Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Trailing Edge Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Leading Edge Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Path NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * NLA = Network Layer Address + * NLA AFI = NLA Address Family Indicator: rfc 1700 (ADDRESS FAMILY NUMBERS) + * => Path NLA = IP address of last network element + */ + +#define PGM_MIN_SPM_SIZE ( sizeof(struct pgm_spm) ) + +static +bool +pgm_print_spm ( + const struct pgm_header* const header, + const void* data, + const size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != header); + pgm_assert (NULL != data); + pgm_assert (len > 0); + + printf ("SPM: "); + + if (len < PGM_MIN_SPM_SIZE) { + puts ("packet truncated :("); + return FALSE; + } + + const struct pgm_spm * spm = (const struct pgm_spm *)data; + const struct pgm_spm6* spm6 = (const struct pgm_spm6*)data; + const uint16_t spm_nla_afi = ntohs (spm->spm_nla_afi); + + printf ("sqn %" PRIu32 " trail %" PRIu32 "lu lead %" PRIu32 "lu nla-afi %u ", + ntohl(spm->spm_sqn), + ntohl(spm->spm_trail), + ntohl(spm->spm_lead), + spm_nla_afi); /* address family indicator */ + + char s[INET6_ADDRSTRLEN]; + const void* pgm_opt; + size_t pgm_opt_len; + switch (spm_nla_afi) { + case AFI_IP: + pgm_inet_ntop (AF_INET, &spm->spm_nla, s, sizeof(s)); + pgm_opt = (const uint8_t*)data + sizeof(struct pgm_spm); + pgm_opt_len = len - sizeof(struct pgm_spm); + break; + + case AFI_IP6: + if (len < sizeof (struct pgm_spm6)) { + puts ("packet truncated :("); + return FALSE; + } + + pgm_inet_ntop (AF_INET6, &spm6->spm6_nla, s, sizeof(s)); + pgm_opt = (const uint8_t*)data + sizeof(struct pgm_spm6); + pgm_opt_len = len - sizeof(struct pgm_spm6); + break; + + default: + printf ("unsupported afi"); + return FALSE; + } + + printf ("%s", s); + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT && + pgm_print_options (pgm_opt, pgm_opt_len) < 0 ) + { + return FALSE; + } + + printf ("\n"); + return TRUE; +} + +/* 14.7.1. Poll Request + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLL's Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLL's Round | POLL's Sub-type | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Path NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | POLL's Back-off Interval | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Random String | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Matching Bit-Mask | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Sent to ODATA multicast group with IP Router Alert option. + */ + +#define PGM_MIN_POLL_SIZE ( sizeof(struct pgm_poll) ) + +static +bool +pgm_print_poll ( + const struct pgm_header* const header, + const void* data, + const size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != header); + pgm_assert (NULL != data); + pgm_assert (len > 0); + + printf ("POLL: "); + + if (len < PGM_MIN_POLL_SIZE) { + puts ("packet truncated :("); + return FALSE; + } + + const struct pgm_poll * poll4 = (const struct pgm_poll *)data; + const struct pgm_poll6* poll6 = (const struct pgm_poll6*)data; + const uint16_t poll_nla_afi = ntohs (poll4->poll_nla_afi); + + printf ("sqn %" PRIu32 " round %u sub-type %u nla-afi %u ", + ntohl(poll4->poll_sqn), + ntohs(poll4->poll_round), + ntohs(poll4->poll_s_type), + poll_nla_afi); /* address family indicator */ + + char s[INET6_ADDRSTRLEN]; + const void* pgm_opt; + size_t pgm_opt_len; + switch (poll_nla_afi) { + case AFI_IP: + pgm_inet_ntop (AF_INET, &poll4->poll_nla, s, sizeof(s)); + pgm_opt = (const uint8_t*)data + sizeof(struct pgm_poll); + pgm_opt_len = len - sizeof(struct pgm_poll); + printf ("%s", s); + +/* back-off interval in microseconds */ + printf (" bo_ivl %u", poll4->poll_bo_ivl); + +/* random string */ + printf (" rand [%c%c%c%c]", + isprint (poll4->poll_rand[0]) ? poll4->poll_rand[0] : '.', + isprint (poll4->poll_rand[1]) ? poll4->poll_rand[1] : '.', + isprint (poll4->poll_rand[2]) ? poll4->poll_rand[2] : '.', + isprint (poll4->poll_rand[3]) ? poll4->poll_rand[3] : '.' ); + +/* matching bit-mask */ + printf (" mask 0x%x", poll4->poll_mask); + break; + + case AFI_IP6: + if (len < sizeof (struct pgm_poll6)) { + puts ("packet truncated :("); + return FALSE; + } + + pgm_inet_ntop (AF_INET6, &poll6->poll6_nla, s, sizeof (s)); + pgm_opt = (const uint8_t*)data + sizeof(struct pgm_poll6); + pgm_opt_len = len - sizeof(struct pgm_poll6); + printf ("%s", s); + +/* back-off interval in microseconds */ + printf (" bo_ivl %u", poll6->poll6_bo_ivl); + +/* random string */ + printf (" rand [%c%c%c%c]", + isprint (poll6->poll6_rand[0]) ? poll6->poll6_rand[0] : '.', + isprint (poll6->poll6_rand[1]) ? poll6->poll6_rand[1] : '.', + isprint (poll6->poll6_rand[2]) ? poll6->poll6_rand[2] : '.', + isprint (poll6->poll6_rand[3]) ? poll6->poll6_rand[3] : '.' ); + +/* matching bit-mask */ + printf (" mask 0x%x", poll6->poll6_mask); + break; + + default: + printf ("unsupported afi"); + return FALSE; + } + + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT && + pgm_print_options (pgm_opt, pgm_opt_len) < 0 ) + { + return FALSE; + } + + printf ("\n"); + return TRUE; +} + +/* 14.7.2. Poll Response + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLR's Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLR's Round | reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +static +bool +pgm_print_polr ( + const struct pgm_header* const header, + const void* data, + const size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != header); + pgm_assert (NULL != data); + pgm_assert (len > 0); + + printf ("POLR: "); + + if (len < sizeof(struct pgm_polr)) { + puts ("packet truncated :("); + return FALSE; + } + + const struct pgm_polr* polr = (const struct pgm_polr*)data; + + printf("sqn %" PRIu32 " round %u", + ntohl(polr->polr_sqn), + ntohs(polr->polr_round)); + + const void* pgm_opt = (const uint8_t*)data + sizeof(struct pgm_polr); + size_t pgm_opt_len = len - sizeof(struct pgm_polr); + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT && + pgm_print_options (pgm_opt, pgm_opt_len) < 0 ) + { + return FALSE; + } + + printf ("\n"); + return TRUE; +} + +/* 8.2. Data Packet + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Data Packet Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Trailing Edge Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Data ... + * +-+-+- ... + */ + +static +bool +pgm_print_odata ( + const struct pgm_header* const header, + const void* data, + const size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != header); + pgm_assert (NULL != data); + pgm_assert (len > 0); + + printf ("ODATA: "); + + if (len < sizeof(struct pgm_data)) { + puts ("packet truncated :("); + return FALSE; + } + + const struct pgm_data* odata = (const struct pgm_data*)data; + + printf ("sqn %" PRIu32 " trail %" PRIu32 " [", + ntohl(odata->data_sqn), + ntohl(odata->data_trail)); + +/* option extensions */ + const void* pgm_opt = (const uint8_t*)data + sizeof(struct pgm_data); + size_t pgm_opt_len = len - sizeof(struct pgm_data); + const char* payload = pgm_opt; + + if (header->pgm_options & PGM_OPT_PRESENT) { + const ssize_t opt_len = pgm_print_options (pgm_opt, pgm_opt_len); + if (opt_len < 0) + return FALSE; + payload += opt_len; + } + +/* data */ + const char* end = payload + ntohs (header->pgm_tsdu_length); + while (payload < end) { + if (isprint (*payload)) + putchar (*payload); + else + putchar ('.'); + payload++; + } + + printf ("]\n"); + return TRUE; +} + +/* 8.2. Repair Data + */ + +static +bool +pgm_print_rdata ( + const struct pgm_header* const header, + const void* data, + const size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != header); + pgm_assert (NULL != data); + pgm_assert (len > 0); + + printf ("RDATA: "); + + if (len < sizeof(struct pgm_data)) { + puts ("packet truncated :("); + return FALSE; + } + + const struct pgm_data* rdata = (const struct pgm_data*)data; + + printf ("sqn %" PRIu32 " trail %" PRIu32 " [", + ntohl (rdata->data_sqn), + ntohl (rdata->data_trail)); + +/* option extensions */ + const void* pgm_opt = (const uint8_t*)data + sizeof(struct pgm_data); + size_t pgm_opt_len = len - sizeof(struct pgm_data); + const char* payload = pgm_opt; + + if (header->pgm_options & PGM_OPT_PRESENT) { + const ssize_t opt_len = pgm_print_options (pgm_opt, pgm_opt_len); + if (opt_len < 0) + return FALSE; + payload += opt_len; + } + +/* data */ + const char* end = payload + ntohs (header->pgm_tsdu_length); + while (payload < end) { + if (isprint (*payload)) + putchar (*payload); + else + putchar ('.'); + payload++; + } + + printf ("]\n"); + return TRUE; +} + +/* 8.3. NAK + * + * Technically the AFI of the source and multicast group can be different + * but that would be very wibbly wobbly. One example is using a local DLR + * with a IPv4 address to reduce NAK cost for recovery on wide IPv6 + * distribution. + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Requested Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Source NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Multicast Group NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | Option Extensions when present ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... + */ + +#define PGM_MIN_NAK_SIZE ( sizeof(struct pgm_nak) ) + +static +bool +pgm_print_nak ( + const struct pgm_header* const header, + const void* data, + const size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != header); + pgm_assert (NULL != data); + pgm_assert (len > 0); + + printf ("NAK: "); + + if (len < PGM_MIN_NAK_SIZE) { + puts ("packet truncated :("); + return FALSE; + } + + const struct pgm_nak * nak = (const struct pgm_nak *)data; + const struct pgm_nak6* nak6 = (const struct pgm_nak6*)data; + const uint16_t nak_src_nla_afi = ntohs (nak->nak_src_nla_afi); + + printf ("sqn %" PRIu32 " src ", + ntohl(nak->nak_sqn)); + + char s[INET6_ADDRSTRLEN]; + const void* pgm_opt; + size_t pgm_opt_len; + +/* source nla */ + switch (nak_src_nla_afi) { + case AFI_IP: { + const uint16_t nak_grp_nla_afi = ntohs (nak->nak_grp_nla_afi); + if (nak_src_nla_afi != nak_grp_nla_afi) { + puts ("different source & group afi very wibbly wobbly :("); + return FALSE; + } + + pgm_inet_ntop (AF_INET, &nak->nak_src_nla, s, sizeof(s)); + pgm_opt = (const uint8_t*)data + sizeof(struct pgm_nak); + pgm_opt_len = len - sizeof(struct pgm_nak); + printf ("%s grp ", s); + + pgm_inet_ntop (AF_INET, &nak->nak_grp_nla, s, sizeof(s)); + printf ("%s", s); + break; + } + + case AFI_IP6: { + if (len < sizeof (struct pgm_nak6)) { + puts ("packet truncated :("); + return FALSE; + } + + const uint16_t nak_grp_nla_afi = ntohs (nak6->nak6_grp_nla_afi); + if (nak_src_nla_afi != nak_grp_nla_afi) { + puts ("different source & group afi very wibbly wobbly :("); + return FALSE; + } + + pgm_inet_ntop (AF_INET6, &nak6->nak6_src_nla, s, sizeof(s)); + pgm_opt = (const uint8_t*)data + sizeof(struct pgm_nak6); + pgm_opt_len = len - sizeof(struct pgm_nak6); + printf ("%s grp ", s); + + pgm_inet_ntop (AF_INET6, &nak6->nak6_grp_nla, s, sizeof(s)); + printf ("%s", s); + break; + } + + default: + puts ("unsupported afi"); + return FALSE; + } + + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT && + pgm_print_options (pgm_opt, pgm_opt_len) < 0 ) + { + return FALSE; + } + + printf ("\n"); + return TRUE; +} + +/* 8.3. N-NAK + */ + +static +bool +pgm_print_nnak ( + PGM_GNUC_UNUSED const struct pgm_header* const header, + PGM_GNUC_UNUSED const void* data, + const size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != header); + pgm_assert (NULL != data); + pgm_assert (len > 0); + + printf ("N-NAK: "); + + if (len < sizeof(struct pgm_nak)) { + puts ("packet truncated :("); + return FALSE; + } + +// struct pgm_nak* nnak = (struct pgm_nak*)data; + + return TRUE; +} + +/* 8.3. NCF + */ + +bool +pgm_print_ncf ( + PGM_GNUC_UNUSED const struct pgm_header* const header, + PGM_GNUC_UNUSED const void* data, + const size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != header); + pgm_assert (NULL != data); + pgm_assert (len > 0); + + printf ("NCF: "); + + if (len < sizeof(struct pgm_nak)) { + puts ("packet truncated :("); + return FALSE; + } + +// struct pgm_nak* ncf = (struct pgm_nak*)data; + + return TRUE; +} + +/* 13.6. SPM Request + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... + */ + +static +bool +pgm_print_spmr ( + const struct pgm_header* const header, + const void* data, + const size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != header); + pgm_assert (NULL != data); + pgm_assert (len > 0); + + printf ("SPMR: "); + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT && + pgm_print_options (data, len) < 0 ) + { + return FALSE; + } + + printf ("\n"); + return TRUE; +} + +/* PGMCC: ACK + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RX_MAX | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Received Packet Bitmap | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... + */ + +static +bool +pgm_print_ack ( + const struct pgm_header* const header, + const void* data, + const size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != header); + pgm_assert (NULL != data); + pgm_assert (len > 0); + + printf ("ACK: "); + + const struct pgm_ack* ack = (const struct pgm_ack*)data; + char bitmap[33]; + + for (unsigned i = 31; i; i--) + bitmap[i] = (ack->ack_bitmap & (1 << i)) ? '1' : '0'; + bitmap[32] = '\0'; + + printf ("rx_max %" PRIu32 " bitmap [%s] ", + ntohl(ack->ack_rx_max), bitmap); + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT && + pgm_print_options (data, len) < 0 ) + { + return FALSE; + } + + printf ("\n"); + return TRUE; +} + + +/* Parse PGM options fields, alters contents of packet. + * + * returns -1 on failure, or total length in octets of the option fields + */ + +static +ssize_t +pgm_print_options ( + const void* data, + size_t len + ) +{ +/* pre-conditions */ + pgm_assert (NULL != data); + pgm_assert (len > 0); + + printf (" OPTIONS:"); + if (len < sizeof(struct pgm_opt_length)) { + puts (" packet truncated :("); + return -1; + } + + const struct pgm_opt_length* opt_len = (const struct pgm_opt_length*)data; + if (opt_len->opt_length != sizeof(struct pgm_opt_length)) { + printf (" bad opt_length length %u\n", (unsigned)opt_len->opt_length); + return -1; + } + + uint16_t opt_total_length = ntohs (opt_len->opt_total_length); + printf (" total len %u ", opt_total_length); + if (opt_total_length < (sizeof(struct pgm_opt_length) + sizeof(struct pgm_opt_header)) || + opt_total_length > len) + { + puts ("bad total length"); + return -1; + } + +/* total length includes opt_length option */ + opt_total_length -= sizeof(struct pgm_opt_length); + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)(opt_len + 1); + +/* iterate through options (max 16) */ + unsigned count = 16; + while (opt_total_length && count) + { + if (opt_total_length < sizeof(struct pgm_opt_header) || + opt_header->opt_length > opt_total_length) + { + puts ("short on option data :o"); + return -1; + } + + if (opt_header->opt_type & PGM_OPT_END) { + printf ("OPT_END+"); + } + + switch (opt_header->opt_type & PGM_OPT_MASK) { + case PGM_OPT_FRAGMENT: + printf ("OPT_FRAGMENT "); + break; + + case PGM_OPT_NAK_LIST: + printf ("OPT_NAK_LIST "); + break; + + case PGM_OPT_JOIN: + printf ("OPT_JOIN "); + break; + + case PGM_OPT_REDIRECT: + printf ("OPT_REDIRECT "); + break; + + case PGM_OPT_SYN: + printf ("OPT_SYN "); + break; + + case PGM_OPT_FIN: + printf ("OPT_FIN "); + break; + + case PGM_OPT_RST: + printf ("OPT_RST "); + break; + + case PGM_OPT_PARITY_PRM: + printf ("OPT_PARITY_PRM "); + break; + + case PGM_OPT_CURR_TGSIZE: + printf ("OPT_CURR_TGSIZE "); + break; + + case PGM_OPT_CR: + printf ("OPT_CR "); + break; + + case PGM_OPT_CRQST: + printf ("OPT_CRQST "); + break; + + case PGM_OPT_PGMCC_DATA: + printf ("OPT_PGMCC_DATA "); + break; + + case PGM_OPT_PGMCC_FEEDBACK: + printf ("OPT_PGMCC_FEEDBACK "); + break; + + case PGM_OPT_NAK_BO_IVL: + printf ("OPT_NAK_BO_IVL "); + break; + + case PGM_OPT_NAK_BO_RNG: + printf ("OPT_NAK_BO_RNG "); + break; + + case PGM_OPT_NBR_UNREACH: + printf ("OPT_NBR_UNREACH "); + break; + + case PGM_OPT_PATH_NLA: + printf ("OPT_PATH_NLA "); + break; + + default: + printf ("OPT-%u{%u} ", opt_header->opt_type & PGM_OPT_MASK, opt_header->opt_length); + break; + } + + opt_total_length -= opt_header->opt_length; + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); + + count--; + } + + if (!count) { + puts ("too many options found"); + return -1; + } + + return ((const uint8_t*)opt_header - (const uint8_t*)data); +} + +const char* +pgm_type_string ( + uint8_t type + ) +{ + const char* c; + + switch (type) { + case PGM_SPM: c = "PGM_SPM"; break; + case PGM_POLL: c = "PGM_POLL"; break; + case PGM_POLR: c = "PGM_POLR"; break; + case PGM_ODATA: c = "PGM_ODATA"; break; + case PGM_RDATA: c = "PGM_RDATA"; break; + case PGM_NAK: c = "PGM_NAK"; break; + case PGM_NNAK: c = "PGM_NNAK"; break; + case PGM_NCF: c = "PGM_NCF"; break; + case PGM_SPMR: c = "PGM_SPMR"; break; + case PGM_ACK: c = "PGM_ACK"; break; + default: c = "(unknown)"; break; + } + + return c; +} + +const char* +pgm_udpport_string ( + uint16_t port + ) +{ + static pgm_hashtable_t *services = NULL; + + if (!services) { + services = pgm_hashtable_new (pgm_int_hash, pgm_int_equal); + } + + const int hash_key = port; + void* service_string = pgm_hashtable_lookup (services, &hash_key); + if (service_string != NULL) { + return service_string; + } + + struct servent* se = getservbyport (port, "udp"); + if (se == NULL) { + char buf[sizeof("00000")]; + snprintf(buf, sizeof(buf), "%u", ntohs(port)); + service_string = pgm_strdup(buf); + } else { + service_string = pgm_strdup(se->s_name); + } + pgm_hashtable_insert (services, &hash_key, service_string); + return service_string; +} + +const char* +pgm_gethostbyaddr ( + const struct in_addr* ap + ) +{ + static pgm_hashtable_t *hosts = NULL; + + if (!hosts) { + hosts = pgm_hashtable_new (pgm_str_hash, pgm_int_equal); + } + + const int hash_key = (int)ap->s_addr; + void* host_string = pgm_hashtable_lookup (hosts, &hash_key); + if (host_string != NULL) { + return host_string; + } + + struct hostent* he = gethostbyaddr((const char*)ap, sizeof(struct in_addr), AF_INET); + if (he == NULL) { + struct in_addr in; + memcpy (&in, ap, sizeof(in)); + host_string = pgm_strdup(inet_ntoa(in)); + } else { + host_string = pgm_strdup(he->h_name); + } + pgm_hashtable_insert (hosts, &hash_key, host_string); + return host_string; +} + +void +pgm_ipopt_print ( + const void* ipopt, + size_t length + ) +{ +/* pre-conditions */ + pgm_assert (NULL != ipopt); + + const char* op = ipopt; + + while (length) + { + char len = (*op == PGM_IPOPT_NOP || *op == PGM_IPOPT_EOL) ? 1 : op[1]; + switch (*op) { + case PGM_IPOPT_EOL: printf(" eol"); break; + case PGM_IPOPT_NOP: printf(" nop"); break; + case PGM_IPOPT_RR: printf(" rr"); break; /* 1 route */ + case PGM_IPOPT_TS: printf(" ts"); break; /* 1 TS */ +#if 0 + case PGM_IPOPT_SECURITY: printf(" sec-level"); break; + case PGM_IPOPT_LSRR: printf(" lsrr"); break; /* 1 route */ + case PGM_IPOPT_SATID: printf(" satid"); break; + case PGM_IPOPT_SSRR: printf(" ssrr"); break; /* 1 route */ +#endif + default: printf(" %x{%d}", (int)*op, (int)len); break; + } + + if (!len) { + puts ("invalid IP opt length"); + return; + } + + op += len; + length -= len; + } +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/packet_test.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/packet_test.c.c89.patch new file mode 100644 index 0000000..72dbcac --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/packet_test.c.c89.patch @@ -0,0 +1,401 @@ +--- packet_test.c 2010-08-04 15:24:51.000000000 +0800 ++++ packet_test.c89 2010-08-04 15:40:35.000000000 +0800 +@@ -66,12 +66,13 @@ + /* minimum size should be IP header plus PGM header */ + if (len < (sizeof(struct pgm_ip) + sizeof(struct pgm_header))) + { +- printf ("Packet size too small: %zu bytes, expecting at least %zu bytes.\n", ++ printf ("Packet size too small: %lu bytes, expecting at least %lu bytes.\n", + len, sizeof(struct pgm_ip) + sizeof(struct pgm_header)); + return FALSE; + } + + /* decode IP header */ ++ { + const struct pgm_ip* ip = (const struct pgm_ip*)data; + if (ip->ip_v != 4) /* IP version, 4 or 6 */ + { +@@ -80,6 +81,7 @@ + } + printf ("IP "); + ++ { + const size_t ip_header_length = ip->ip_hl * 4; /* IP header length in 32bit octets */ + if (ip_header_length < sizeof(struct pgm_ip)) + { +@@ -87,6 +89,7 @@ + return FALSE; + } + ++ { + size_t packet_length = ntohs(ip->ip_len); /* total packet length */ + + /* ip_len can equal packet_length - ip_header_length in FreeBSD/NetBSD +@@ -109,6 +112,7 @@ + return FALSE; + } + ++ { + const uint16_t offset = ntohs(ip->ip_off); + + /* 3 bits routing priority, 4 bits type of service: delay, throughput, reliability, cost */ +@@ -135,7 +139,7 @@ + (offset & 0x1fff) * 8, + ((offset & IP_DF) ? "DF" : ""), + ((offset & IP_MF) ? "+" : "")); +- printf (", length %zu", packet_length); ++ printf (", length %lu", packet_length); + + /* IP options */ + if ((ip_header_length - sizeof(struct pgm_ip)) > 0) { +@@ -146,6 +150,7 @@ + + /* packets that fail checksum will generally not be passed upstream except with rfc3828 + */ ++ { + const uint16_t ip_sum = pgm_inet_checksum(data, packet_length, 0); + if (ip_sum != 0) { + const uint16_t encoded_ip_sum = ntohs(ip->ip_sum); +@@ -176,6 +181,7 @@ + * | Type specific data ... + * +-+-+-+-+-+-+-+-+-+- ... + */ ++ { + const struct pgm_header* pgm_header = (const struct pgm_header*)((const char*)data + ip_header_length); + const size_t pgm_length = packet_length - ip_header_length; + +@@ -227,6 +233,7 @@ + } + + /* now decode PGM packet types */ ++ { + const void* pgm_data = pgm_header + 1; + const size_t pgm_data_length = pgm_length - sizeof(pgm_header); /* can equal zero for SPMR's */ + +@@ -246,6 +253,13 @@ + } + + return err; ++ } ++ } ++ } ++ } ++ } ++ } ++ } + } + + /* 8.1. Source Path Messages (SPM) +@@ -293,6 +307,7 @@ + return FALSE; + } + ++ { + const struct pgm_spm * spm = (const struct pgm_spm *)data; + const struct pgm_spm6* spm6 = (const struct pgm_spm6*)data; + const uint16_t spm_nla_afi = ntohs (spm->spm_nla_afi); +@@ -303,6 +318,7 @@ + ntohl(spm->spm_lead), + spm_nla_afi); /* address family indicator */ + ++ { + char s[INET6_ADDRSTRLEN]; + const void* pgm_opt; + size_t pgm_opt_len; +@@ -340,6 +356,8 @@ + + printf ("\n"); + return TRUE; ++ } ++ } + } + + /* 14.7.1. Poll Request +@@ -389,6 +407,7 @@ + return FALSE; + } + ++ { + const struct pgm_poll * poll4 = (const struct pgm_poll *)data; + const struct pgm_poll6* poll6 = (const struct pgm_poll6*)data; + const uint16_t poll_nla_afi = ntohs (poll4->poll_nla_afi); +@@ -399,6 +418,7 @@ + ntohs(poll4->poll_s_type), + poll_nla_afi); /* address family indicator */ + ++ { + char s[INET6_ADDRSTRLEN]; + const void* pgm_opt; + size_t pgm_opt_len; +@@ -463,6 +483,8 @@ + + printf ("\n"); + return TRUE; ++ } ++ } + } + + /* 14.7.2. Poll Response +@@ -498,12 +520,14 @@ + return FALSE; + } + ++ { + const struct pgm_polr* polr = (const struct pgm_polr*)data; + + printf("sqn %" PRIu32 " round %u", + ntohl(polr->polr_sqn), + ntohs(polr->polr_round)); + ++ { + const void* pgm_opt = (const uint8_t*)data + sizeof(struct pgm_polr); + size_t pgm_opt_len = len - sizeof(struct pgm_polr); + +@@ -516,6 +540,8 @@ + + printf ("\n"); + return TRUE; ++ } ++ } + } + + /* 8.2. Data Packet +@@ -553,6 +579,7 @@ + return FALSE; + } + ++ { + const struct pgm_data* odata = (const struct pgm_data*)data; + + printf ("sqn %" PRIu32 " trail %" PRIu32 " [", +@@ -560,6 +587,7 @@ + ntohl(odata->data_trail)); + + /* option extensions */ ++ { + const void* pgm_opt = (const uint8_t*)data + sizeof(struct pgm_data); + size_t pgm_opt_len = len - sizeof(struct pgm_data); + const char* payload = pgm_opt; +@@ -572,6 +600,7 @@ + } + + /* data */ ++ { + const char* end = payload + ntohs (header->pgm_tsdu_length); + while (payload < end) { + if (isprint (*payload)) +@@ -583,6 +612,9 @@ + + printf ("]\n"); + return TRUE; ++ } ++ } ++ } + } + + /* 8.2. Repair Data +@@ -608,6 +640,7 @@ + return FALSE; + } + ++ { + const struct pgm_data* rdata = (const struct pgm_data*)data; + + printf ("sqn %" PRIu32 " trail %" PRIu32 " [", +@@ -615,6 +648,7 @@ + ntohl (rdata->data_trail)); + + /* option extensions */ ++ { + const void* pgm_opt = (const uint8_t*)data + sizeof(struct pgm_data); + size_t pgm_opt_len = len - sizeof(struct pgm_data); + const char* payload = pgm_opt; +@@ -627,6 +661,7 @@ + } + + /* data */ ++ { + const char* end = payload + ntohs (header->pgm_tsdu_length); + while (payload < end) { + if (isprint (*payload)) +@@ -638,6 +673,9 @@ + + printf ("]\n"); + return TRUE; ++ } ++ } ++ } + } + + /* 8.3. NAK +@@ -686,6 +724,7 @@ + return FALSE; + } + ++ { + const struct pgm_nak * nak = (const struct pgm_nak *)data; + const struct pgm_nak6* nak6 = (const struct pgm_nak6*)data; + const uint16_t nak_src_nla_afi = ntohs (nak->nak_src_nla_afi); +@@ -693,6 +732,7 @@ + printf ("sqn %" PRIu32 " src ", + ntohl(nak->nak_sqn)); + ++ { + char s[INET6_ADDRSTRLEN]; + const void* pgm_opt; + size_t pgm_opt_len; +@@ -722,6 +762,7 @@ + return FALSE; + } + ++ { + const uint16_t nak_grp_nla_afi = ntohs (nak6->nak6_grp_nla_afi); + if (nak_src_nla_afi != nak_grp_nla_afi) { + puts ("different source & group afi very wibbly wobbly :("); +@@ -736,6 +777,7 @@ + pgm_inet_ntop (AF_INET6, &nak6->nak6_grp_nla, s, sizeof(s)); + printf ("%s", s); + break; ++ } + } + + default: +@@ -753,6 +795,8 @@ + + printf ("\n"); + return TRUE; ++ } ++ } + } + + /* 8.3. N-NAK +@@ -873,11 +917,15 @@ + + printf ("ACK: "); + ++ { + const struct pgm_ack* ack = (const struct pgm_ack*)data; + char bitmap[33]; + +- for (unsigned i = 31; i; i--) ++ { ++ unsigned i; ++ for (i = 31; i; i--) + bitmap[i] = (ack->ack_bitmap & (1 << i)) ? '1' : '0'; ++ } + bitmap[32] = '\0'; + + printf ("rx_max %" PRIu32 " bitmap [%s] ", +@@ -892,6 +940,7 @@ + + printf ("\n"); + return TRUE; ++ } + } + + +@@ -917,12 +966,14 @@ + return -1; + } + ++ { + const struct pgm_opt_length* opt_len = (const struct pgm_opt_length*)data; + if (opt_len->opt_length != sizeof(struct pgm_opt_length)) { + printf (" bad opt_length length %u\n", (unsigned)opt_len->opt_length); + return -1; + } + ++ { + uint16_t opt_total_length = ntohs (opt_len->opt_total_length); + printf (" total len %u ", opt_total_length); + if (opt_total_length < (sizeof(struct pgm_opt_length) + sizeof(struct pgm_opt_header)) || +@@ -934,6 +985,7 @@ + + /* total length includes opt_length option */ + opt_total_length -= sizeof(struct pgm_opt_length); ++ { + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)(opt_len + 1); + + /* iterate through options (max 16) */ +@@ -1037,6 +1089,9 @@ + } + + return ((const uint8_t*)opt_header - (const uint8_t*)data); ++ } ++ } ++ } + } + + const char* +@@ -1074,22 +1129,30 @@ + services = pgm_hashtable_new (pgm_int_hash, pgm_int_equal); + } + ++ { + const int hash_key = port; + void* service_string = pgm_hashtable_lookup (services, &hash_key); + if (service_string != NULL) { + return service_string; + } + ++ { + struct servent* se = getservbyport (port, "udp"); + if (se == NULL) { + char buf[sizeof("00000")]; +- snprintf(buf, sizeof(buf), "%u", ntohs(port)); ++#ifdef _MSC_VER ++ _snprintf_s (buf, sizeof(buf), _TRUNCATE, "%u", ntohs(port)); ++#else ++ snprintf (buf, sizeof(buf), "%u", ntohs(port)); ++#endif + service_string = pgm_strdup(buf); + } else { + service_string = pgm_strdup(se->s_name); + } + pgm_hashtable_insert (services, &hash_key, service_string); + return service_string; ++ } ++ } + } + + const char* +@@ -1103,12 +1166,14 @@ + hosts = pgm_hashtable_new (pgm_str_hash, pgm_int_equal); + } + ++ { + const int hash_key = (int)ap->s_addr; + void* host_string = pgm_hashtable_lookup (hosts, &hash_key); + if (host_string != NULL) { + return host_string; + } + ++ { + struct hostent* he = gethostbyaddr((const char*)ap, sizeof(struct in_addr), AF_INET); + if (he == NULL) { + struct in_addr in; +@@ -1119,6 +1184,8 @@ + } + pgm_hashtable_insert (hosts, &hash_key, host_string); + return host_string; ++ } ++ } + } + + void +@@ -1130,6 +1197,7 @@ + /* pre-conditions */ + pgm_assert (NULL != ipopt); + ++ { + const char* op = ipopt; + + while (length) +@@ -1157,6 +1225,7 @@ + op += len; + length -= len; + } ++ } + } + + /* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/packet_test_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/packet_test_unittest.c new file mode 100644 index 0000000..7edefbb --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/packet_test_unittest.c @@ -0,0 +1,169 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for PGM packet handling. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include + + +/* mock state */ + +#define PACKET_DEBUG +#include "packet_test.c" + + +static +struct pgm_sk_buff_t* +generate_raw_pgm (void) +{ + const char source[] = "i am not a string"; + const guint source_len = sizeof(source); + struct pgm_sk_buff_t* skb; + GError* err = NULL; + + skb = pgm_alloc_skb (1500); + skb->sock = (pgm_sock_t*)0x1; + skb->tstamp = 0x1; + skb->data = skb->head; + skb->len = sizeof(struct pgm_ip) + sizeof(struct pgm_header) + sizeof(struct pgm_data) + source_len; + skb->tail = (guint8*)skb->data + skb->len; + +/* add IP header */ + struct pgm_ip* iphdr = skb->data; + iphdr->ip_hl = sizeof(struct pgm_ip) / 4; + iphdr->ip_v = 4; + iphdr->ip_tos = 0; + iphdr->ip_len = g_htons (skb->len); + iphdr->ip_id = 0; + iphdr->ip_off = 0; + iphdr->ip_ttl = 16; + iphdr->ip_p = IPPROTO_PGM; + iphdr->ip_sum = 0; + iphdr->ip_src.s_addr = inet_addr ("127.0.0.1"); + iphdr->ip_dst.s_addr = inet_addr ("127.0.0.2"); + +/* add PGM header */ + struct pgm_header* pgmhdr = (gpointer)(iphdr + 1); + pgmhdr->pgm_sport = g_htons ((guint16)1000); + pgmhdr->pgm_dport = g_htons ((guint16)7500); + pgmhdr->pgm_type = PGM_ODATA; + pgmhdr->pgm_options = 0; + pgmhdr->pgm_gsi[0] = 1; + pgmhdr->pgm_gsi[1] = 2; + pgmhdr->pgm_gsi[2] = 3; + pgmhdr->pgm_gsi[3] = 4; + pgmhdr->pgm_gsi[4] = 5; + pgmhdr->pgm_gsi[5] = 6; + pgmhdr->pgm_tsdu_length = g_htons (source_len); + +/* add ODATA header */ + struct pgm_data* datahdr = (gpointer)(pgmhdr + 1); + datahdr->data_sqn = g_htonl ((guint32)0); + datahdr->data_trail = g_htonl ((guint32)-1); + +/* add payload */ + gpointer data = (gpointer)(datahdr + 1); + memcpy (data, source, source_len); + +/* finally PGM checksum */ + pgmhdr->pgm_checksum = 0; + pgmhdr->pgm_checksum = pgm_csum_fold (pgm_csum_partial (pgmhdr, sizeof(struct pgm_header) + sizeof(struct pgm_data) + source_len, 0)); + +/* and IP checksum */ + iphdr->ip_sum = pgm_inet_checksum (skb->head, skb->len, 0); + + return skb; +} + + +/* mock functions for external references */ + +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return 0; +} + + +/* target: + * gboolean + * pgm_print_packet ( + * gpointer data, + * gsize len + * ) + */ + +START_TEST (test_print_packet_pass_001) +{ + struct pgm_sk_buff_t* skb = generate_raw_pgm (); + pgm_print_packet (skb->head, skb->len); +} +END_TEST + +START_TEST (test_print_packet_fail_001) +{ + pgm_print_packet (NULL, 0); + fail ("reached"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_print_packet = tcase_create ("print-packet"); + suite_add_tcase (s, tc_print_packet); + tcase_add_test (tc_print_packet, test_print_packet_pass_001); + tcase_add_test_raise_signal (tc_print_packet, test_print_packet_fail_001, SIGABRT); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/pgmMIB.c b/3rdparty/openpgm-svn-r1135/pgm/pgmMIB.c new file mode 100644 index 0000000..1226c50 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/pgmMIB.c @@ -0,0 +1,3212 @@ +/* + * Note: this file originally auto-generated by mib2c using + * : mib2c.notify.conf,v 5.3 2004/04/15 12:29:19 dts12 Exp $ + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "pgm/snmp.h" +#include "impl/pgmMIB.h" +#include "impl/pgmMIB_columns.h" +#include "impl/pgmMIB_enums.h" + + +//#define PGMMIB_DEBUG + + +/* locals */ + +struct pgm_snmp_context_t { + pgm_slist_t* list; + pgm_list_t* node; + int index; /* table index */ + unsigned instance; /* unique number per node */ +}; + +typedef struct pgm_snmp_context_t pgm_snmp_context_t; + +static const oid snmptrap_oid[] = {1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0}; + + +/* functions */ + +static int initialize_table_pgmSourceTable(void); +static Netsnmp_Node_Handler pgmSourceTable_handler; +static Netsnmp_First_Data_Point pgmSourceTable_get_first_data_point; +static Netsnmp_Next_Data_Point pgmSourceTable_get_next_data_point; +static Netsnmp_Free_Loop_Context pgmSourceTable_free_loop_context; + +static int initialize_table_pgmSourceConfigTable(void); +static Netsnmp_Node_Handler pgmSourceConfigTable_handler; +static Netsnmp_First_Data_Point pgmSourceConfigTable_get_first_data_point; +static Netsnmp_Next_Data_Point pgmSourceConfigTable_get_next_data_point; +static Netsnmp_Free_Loop_Context pgmSourceConfigTable_free_loop_context; + +static int initialize_table_pgmSourcePerformanceTable(void); +static Netsnmp_Node_Handler pgmSourcePerformanceTable_handler; +static Netsnmp_First_Data_Point pgmSourcePerformanceTable_get_first_data_point; +static Netsnmp_Next_Data_Point pgmSourcePerformanceTable_get_next_data_point; +static Netsnmp_Free_Loop_Context pgmSourcePerformanceTable_free_loop_context; + +static int initialize_table_pgmReceiverTable(void); +static Netsnmp_Node_Handler pgmReceiverTable_handler; +static Netsnmp_First_Data_Point pgmReceiverTable_get_first_data_point; +static Netsnmp_Next_Data_Point pgmReceiverTable_get_next_data_point; +static Netsnmp_Free_Loop_Context pgmReceiverTable_free_loop_context; + +static int initialize_table_pgmReceiverConfigTable(void); +static Netsnmp_Node_Handler pgmReceiverConfigTable_handler; +static Netsnmp_First_Data_Point pgmReceiverConfigTable_get_first_data_point; +static Netsnmp_Next_Data_Point pgmReceiverConfigTable_get_next_data_point; +static Netsnmp_Free_Loop_Context pgmReceiverConfigTable_free_loop_context; + +static int initialize_table_pgmReceiverPerformanceTable(void); +static Netsnmp_Node_Handler pgmReceiverPerformanceTable_handler; +static Netsnmp_First_Data_Point pgmReceiverPerformanceTable_get_first_data_point; +static Netsnmp_Next_Data_Point pgmReceiverPerformanceTable_get_next_data_point; +static Netsnmp_Free_Loop_Context pgmReceiverPerformanceTable_free_loop_context; + + +bool +pgm_mib_init ( + pgm_error_t** error + ) +{ + if (MIB_REGISTERED_OK != initialize_table_pgmSourceTable()) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SNMP, + PGM_ERROR_FAILED, + _("pgmSourceTable registration: see SNMP log for further details.")); + return FALSE; + } + if (MIB_REGISTERED_OK != initialize_table_pgmSourceConfigTable()) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SNMP, + PGM_ERROR_FAILED, + _("pgmSourceConfigTable registration: see SNMP log for further details.")); + return FALSE; + } + if (MIB_REGISTERED_OK != initialize_table_pgmSourcePerformanceTable()) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SNMP, + PGM_ERROR_FAILED, + _("pgmSourcePerformanceTable registration: see SNMP log for further details.")); + return FALSE; + } + if (MIB_REGISTERED_OK != initialize_table_pgmReceiverTable()) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SNMP, + PGM_ERROR_FAILED, + _("pgmReceiverTable registration: see SNMP log for further details.")); + return FALSE; + } + if (MIB_REGISTERED_OK != initialize_table_pgmReceiverConfigTable()) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SNMP, + PGM_ERROR_FAILED, + _("pgmReceiverConfigTable registration: see SNMP log for further details.")); + return FALSE; + } + if (MIB_REGISTERED_OK != initialize_table_pgmReceiverPerformanceTable()) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SNMP, + PGM_ERROR_FAILED, + _("pgmReceiverPerformanceTable registration: see SNMP log for further details.")); + return FALSE; + } + + return TRUE; +} + +/* + * pgmSourceTable + * + * returns MIB_REGISTERED_OK on success, failures include: + * MIB_REGISTRATION_FAILED + * MIB_DUPLICATE_REGISTRATION + * SNMPERR_GENERR + */ + +static +int +initialize_table_pgmSourceTable (void) +{ + pgm_debug ("initialize_table_pgmSourceTable ()"); + + static const oid pgmSourceTable_oid[] = {1,3,6,1,3,112,1,2,100,2}; + netsnmp_table_registration_info* table_info = NULL; + netsnmp_iterator_info* iinfo = NULL; + netsnmp_handler_registration* reg = NULL; + + reg = netsnmp_create_handler_registration ("pgmSourceTable", pgmSourceTable_handler, + pgmSourceTable_oid, OID_LENGTH( pgmSourceTable_oid ), + HANDLER_CAN_RONLY); + if (!reg) + goto error; + + table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); + if (!table_info) + goto error; + + table_info->min_column = COLUMN_PGMSOURCESOURCEADDRESS; + table_info->max_column = COLUMN_PGMSOURCESOURCEPORTNUMBER; + + netsnmp_table_helper_add_indexes (table_info, + ASN_OCTET_STR, /* index: pgmSourceGlobalId */ + ASN_UNSIGNED, /* index: pgmSourceSourcePort */ + 0); + + iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info ); + if (!iinfo) + goto error; + + iinfo->get_first_data_point = pgmSourceTable_get_first_data_point; + iinfo->get_next_data_point = pgmSourceTable_get_next_data_point; + iinfo->free_loop_context_at_end = pgmSourceTable_free_loop_context; + iinfo->table_reginfo = table_info; + + return netsnmp_register_table_iterator (reg, iinfo); + +error: + if (table_info && table_info->indexes) /* table_data_free_func() is internal */ + snmp_free_var (table_info->indexes); + SNMP_FREE( table_info ); + SNMP_FREE( iinfo ); + netsnmp_handler_registration_free (reg); + + return -1; +} + +/* called for first row of data in SNMP table + * + * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context, + * optionally returns my_data_context. + * + * returns answer or NULL + */ + +static +netsnmp_variable_list* +pgmSourceTable_get_first_data_point( + void** my_loop_context, /* valid through one query of multiple "data points" */ + void** my_data_context, /* answer blob which is passed to handler() */ + netsnmp_variable_list* put_index_data, /* answer */ + netsnmp_iterator_info* mydata /* iinfo on init() */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmSourceTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_rwlock_reader_lock (&pgm_sock_list_lock); + + if (!pgm_sock_list) { + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + return NULL; + } + +/* create our own context for this SNMP loop */ + pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1); + context->list = pgm_sock_list; + *my_loop_context = context; + +/* pass on for generic row access */ + return pgmSourceTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata); +} + +static +netsnmp_variable_list* +pgmSourceTable_get_next_data_point( + void** my_loop_context, + void** my_data_context, + netsnmp_variable_list* put_index_data, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmSourceTable_get_next_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context; + netsnmp_variable_list *idx = put_index_data; + + if (!context->list) + return NULL; + + pgm_sock_t* sock = context->list->data; + +/* pgmSourceGlobalId */ + char gsi[ PGM_GSISTRLEN ]; + pgm_gsi_print_r (&sock->tsi.gsi, gsi, sizeof(gsi)); + snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi)); + idx = idx->next_variable; + +/* pgmSourceSourcePort */ + const unsigned sport = ntohs (sock->tsi.sport); + snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport)); + + *my_data_context = sock; + context->list = context->list->next; + + return put_index_data; +} + +static +void +pgmSourceTable_free_loop_context ( + void* my_loop_context, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmSourceTable_free_loop_context (my_loop_context:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context; + pgm_free (context); + my_loop_context = NULL; + + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); +} + +static +int +pgmSourceTable_handler ( + netsnmp_mib_handler* handler, + netsnmp_handler_registration* reginfo, + netsnmp_agent_request_info* reqinfo, + netsnmp_request_info* requests + ) +{ +/* pre-conditions */ + pgm_assert (NULL != handler); + pgm_assert (NULL != reginfo); + pgm_assert (NULL != reqinfo); + pgm_assert (NULL != requests); + + pgm_debug ("pgmSourceTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)", + (const void*)handler, + (const void*)reginfo, + (const void*)reqinfo, + (const void*)requests); + + switch (reqinfo->mode) { + +/* Read-support (also covers GetNext requests) */ + + case MODE_GET: + for (netsnmp_request_info* request = requests; + request; + request = request->next) + { + const pgm_sock_t* sock = (pgm_sock_t*)netsnmp_extract_iterator_context (request); + + if (!sock) { + netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE); + continue; + } + + netsnmp_variable_list *var = request->requestvb; + netsnmp_table_request_info* table_info = netsnmp_extract_table_info (request); + + if (!table_info) { + snmp_log (LOG_ERR, "pgmSourceTable_handler: empty table request info.\n"); + continue; + } + + switch (table_info->colnum) { + + case COLUMN_PGMSOURCESOURCEADDRESS: + { + struct sockaddr_in s4; + if (AF_INET == sock->send_gsr.gsr_source.ss_family) + memcpy (&s4, &sock->send_gsr.gsr_source, sizeof(s4)); + else + memset (&s4, 0, sizeof(s4)); + snmp_set_var_typed_value (var, ASN_IPADDRESS, + (const u_char*)&s4.sin_addr.s_addr, + sizeof(struct in_addr) ); + } + break; + + case COLUMN_PGMSOURCEGROUPADDRESS: + { + struct sockaddr_in s4; + if (AF_INET == sock->send_gsr.gsr_group.ss_family) + memcpy (&s4, &sock->send_gsr.gsr_group, sizeof(s4)); + else + memset (&s4, 0, sizeof(s4)); + snmp_set_var_typed_value (var, ASN_IPADDRESS, + (const u_char*)&s4.sin_addr.s_addr, + sizeof(struct in_addr) ); + } + break; + + case COLUMN_PGMSOURCEDESTPORT: + { + const unsigned dport = ntohs (sock->dport); + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&dport, sizeof(dport) ); + } + break; + +/* copy index[0] */ + case COLUMN_PGMSOURCESOURCEGSI: + snmp_set_var_typed_value (var, ASN_OCTET_STR, + (const u_char*)table_info->indexes->val.string, + table_info->indexes->val_len); + break; + +/* copy index[1] */ + case COLUMN_PGMSOURCESOURCEPORTNUMBER: + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)table_info->indexes->next_variable->val.integer, + table_info->indexes->next_variable->val_len); + + break; + + default: + snmp_log (LOG_ERR, "pgmSourceTable_handler: unknown column.\n"); + break; + } + } + break; + + case MODE_SET_RESERVE1: + default: + snmp_log (LOG_ERR, "pgmSourceTable_handler: unsupported mode.\n"); + break; + + } + + return SNMP_ERR_NOERROR; +} + +/* + * pgmSourceConfigTable + * + */ + +static +int +initialize_table_pgmSourceConfigTable(void) +{ + pgm_debug ("initialize_table_pgmSourceConfigTable ()"); + + static const oid pgmSourceConfigTable_oid[] = {1,3,6,1,3,112,1,2,100,3}; + netsnmp_table_registration_info* table_info = NULL; + netsnmp_iterator_info* iinfo = NULL; + netsnmp_handler_registration* reg = NULL; + + reg = netsnmp_create_handler_registration ("pgmSourceConfigTable", pgmSourceConfigTable_handler, + pgmSourceConfigTable_oid, OID_LENGTH( pgmSourceConfigTable_oid ), + HANDLER_CAN_RONLY); + if (!reg) + goto error; + + table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); + if (!table_info) + goto error; + + table_info->min_column = COLUMN_PGMSOURCETTL; + table_info->max_column = COLUMN_PGMSOURCESPMPATHADDRESS; + + netsnmp_table_helper_add_indexes (table_info, + ASN_OCTET_STR, /* index: pgmSourceConfigGlobalId */ + ASN_UNSIGNED, /* index: pgmSourceConfigSourcePort */ + 0); + + iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info ); + if (!iinfo) + goto error; + + iinfo->get_first_data_point = pgmSourceConfigTable_get_first_data_point; + iinfo->get_next_data_point = pgmSourceConfigTable_get_next_data_point; + iinfo->free_loop_context_at_end = pgmSourceConfigTable_free_loop_context; + iinfo->table_reginfo = table_info; + + return netsnmp_register_table_iterator (reg, iinfo); + +error: + if (table_info && table_info->indexes) /* table_data_free_func() is internal */ + snmp_free_var (table_info->indexes); + SNMP_FREE( table_info ); + SNMP_FREE( iinfo ); + netsnmp_handler_registration_free (reg); + + return -1; +} + +/* called for first row of data in SNMP table + * + * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context, + * optionally returns my_data_context. + * + * returns answer or NULL + */ + +static +netsnmp_variable_list* +pgmSourceConfigTable_get_first_data_point( + void** my_loop_context, /* valid through one query of multiple "data points" */ + void** my_data_context, /* answer blob which is passed to handler() */ + netsnmp_variable_list* put_index_data, /* answer */ + netsnmp_iterator_info* mydata /* iinfo on init() */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmSourceConfigTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_rwlock_reader_lock (&pgm_sock_list_lock); + + if (!pgm_sock_list) { + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + return NULL; + } + +/* create our own context for this SNMP loop */ + pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1); + context->list = pgm_sock_list; + *my_loop_context = context; + +/* pass on for generic row access */ + return pgmSourceConfigTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata); +} + +static +netsnmp_variable_list* +pgmSourceConfigTable_get_next_data_point( + void** my_loop_context, + void** my_data_context, + netsnmp_variable_list* put_index_data, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmSourceConfigTable_get_next_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context; + netsnmp_variable_list *idx = put_index_data; + + if (!context->list) + return NULL; + + pgm_sock_t* sock = context->list->data; + +/* pgmSourceGlobalId */ + char gsi[ PGM_GSISTRLEN ]; + pgm_gsi_print_r (&sock->tsi.gsi, gsi, sizeof(gsi)); + snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi)); + idx = idx->next_variable; + +/* pgmSourceSourcePort */ + const unsigned sport = ntohs (sock->tsi.sport); + snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport)); + + *my_data_context = sock; + context->list = context->list->next; + + return put_index_data; +} + +static +void +pgmSourceConfigTable_free_loop_context ( + void* my_loop_context, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmSourceConfigTable_free_loop_context (my_loop_context:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context; + pgm_free (context); + my_loop_context = NULL; + + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); +} + +static +int +pgmSourceConfigTable_handler ( + netsnmp_mib_handler* handler, + netsnmp_handler_registration* reginfo, + netsnmp_agent_request_info* reqinfo, + netsnmp_request_info* requests + ) +{ +/* pre-conditions */ + pgm_assert (NULL != handler); + pgm_assert (NULL != reginfo); + pgm_assert (NULL != reqinfo); + pgm_assert (NULL != requests); + + pgm_debug ("pgmSourceConfigTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)", + (const void*)handler, + (const void*)reginfo, + (const void*)reqinfo, + (const void*)requests); + + switch (reqinfo->mode) { + +/* Read-support (also covers GetNext requests) */ + + case MODE_GET: + for (netsnmp_request_info* request = requests; + request; + request = request->next) + { + const pgm_sock_t* sock = (pgm_sock_t*)netsnmp_extract_iterator_context (request); + + if (!sock) { + netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE); + continue; + } + + netsnmp_variable_list *var = request->requestvb; + netsnmp_table_request_info* table_info = netsnmp_extract_table_info (request); + + if (!table_info) { + snmp_log (LOG_ERR, "pgmSourceTable_handler: empty table request info.\n"); + continue; + } + + switch (table_info->colnum) { + + case COLUMN_PGMSOURCETTL: + { + const unsigned hops = sock->hops; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&hops, sizeof(hops) ); + } + break; + + case COLUMN_PGMSOURCEADVMODE: + { + const unsigned adv_mode = 0 == sock->adv_mode ? PGMSOURCEADVMODE_TIME : PGMSOURCEADVMODE_DATA; + snmp_set_var_typed_value (var, ASN_INTEGER, + (const u_char*)&adv_mode, sizeof(adv_mode) ); + } + break; + +/* FIXED: pgmSourceLateJoin = disable(2) */ + case COLUMN_PGMSOURCELATEJOIN: + { + const unsigned late_join = PGMSOURCELATEJOIN_DISABLE; + snmp_set_var_typed_value (var, ASN_INTEGER, + (const u_char*)&late_join, sizeof(late_join) ); + } + break; + + case COLUMN_PGMSOURCETXWMAXRTE: + { + const unsigned txw_max_rte = sock->txw_max_rte; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&txw_max_rte, sizeof(txw_max_rte) ); + } + break; + + case COLUMN_PGMSOURCETXWSECS: + { + const unsigned txw_secs = sock->txw_secs; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&txw_secs, sizeof(txw_secs) ); + } + break; + +/* FIXED: TXW_ADV_SECS = 0 */ + case COLUMN_PGMSOURCETXWADVSECS: + { + const unsigned txw_adv_secs = 0; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&txw_adv_secs, sizeof(txw_adv_secs) ); + } + break; + +/* FIXED: pgmSourceAdvIvl = TXW_ADV_SECS * 1000 = 0 */ + case COLUMN_PGMSOURCEADVIVL: + { + const unsigned adv_ivl = 0; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&adv_ivl, sizeof(adv_ivl) ); + } + break; + + case COLUMN_PGMSOURCESPMIVL: + { + const unsigned spm_ivl = pgm_to_msecs (sock->spm_ambient_interval); + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&spm_ivl, sizeof(spm_ivl) ); + } + break; + +/* TODO: IHB_MIN */ + case COLUMN_PGMSOURCESPMHEARTBEATIVLMIN: + { + const unsigned ihb_min = 0; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&ihb_min, sizeof(ihb_min) ); + } + break; + +/* TODO: IHB_MAX */ + case COLUMN_PGMSOURCESPMHEARTBEATIVLMAX: + { + const unsigned ihb_max = 0; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&ihb_max, sizeof(ihb_max) ); + } + break; + +/* NAK_BO_IVL */ + case COLUMN_PGMSOURCERDATABACKOFFIVL: + { + const unsigned nak_bo_ivl = pgm_to_msecs (sock->nak_bo_ivl); + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&nak_bo_ivl, sizeof(nak_bo_ivl) ); + } + break; + +/* FIXED: pgmSourceFEC = disabled(1) */ + case COLUMN_PGMSOURCEFEC: + { + const unsigned fec = (sock->use_ondemand_parity || sock->use_proactive_parity) ? 1 : 0; + snmp_set_var_typed_value (var, ASN_INTEGER, + (const u_char*)&fec, sizeof(fec) ); + } + break; + +/* FIXED: pgmSourceFECTransmissionGrpSize = 0 */ + case COLUMN_PGMSOURCEFECTRANSMISSIONGRPSIZE: + { + const unsigned fec_tgs = sock->rs_k; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&fec_tgs, sizeof(fec_tgs) ); + } + break; + +/* FIXED: pgmSourceFECProactiveParitySize = 0 */ + case COLUMN_PGMSOURCEFECPROACTIVEPARITYSIZE: + { + const unsigned fec_paps = sock->rs_proactive_h; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&fec_paps, sizeof(fec_paps) ); + } + break; + +/* IPv6 not supported */ + case COLUMN_PGMSOURCESPMPATHADDRESS: + { + struct sockaddr_in s4; + if (AF_INET == sock->recv_gsr[0].gsr_source.ss_family) + memcpy (&s4, &sock->recv_gsr[0].gsr_source, sizeof(s4)); + else + memset (&s4, 0, sizeof(s4)); + snmp_set_var_typed_value (var, ASN_IPADDRESS, + (const u_char*)&s4.sin_addr.s_addr, + sizeof(struct in_addr) ); + } + break; + + default: + snmp_log (LOG_ERR, "pgmSourceConfigTable_handler: unknown column.\n"); + break; + } + } + break; + + case MODE_SET_RESERVE1: + default: + snmp_log (LOG_ERR, "pgmSourceConfigTable_handler: unsupported mode.\n"); + break; + + } + + return SNMP_ERR_NOERROR; +} + +/* + * pgmSourcePerformanceTable + */ + +static +int +initialize_table_pgmSourcePerformanceTable (void) +{ + pgm_debug ("initialize_table_pgmSourcePerformanceTable ()"); + + static const oid pgmSourcePerformanceTable_oid[] = {1,3,6,1,3,112,1,2,100,4}; + netsnmp_table_registration_info* table_info = NULL; + netsnmp_iterator_info* iinfo = NULL; + netsnmp_handler_registration* reg = NULL; + + reg = netsnmp_create_handler_registration ("pgmSourcePerformanceTable", pgmSourcePerformanceTable_handler, + pgmSourcePerformanceTable_oid, OID_LENGTH( pgmSourcePerformanceTable_oid ), + HANDLER_CAN_RONLY); + if (!reg) + goto error; + + table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); + if (!table_info) + goto error; + + table_info->min_column = COLUMN_PGMSOURCEDATABYTESSENT; + table_info->max_column = COLUMN_PGMSOURCENNAKERRORS; + + netsnmp_table_helper_add_indexes (table_info, + ASN_OCTET_STR, /* index: pgmSourceGlobalId */ + ASN_UNSIGNED, /* index: pgmSourceSourcePort */ + 0); + + iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info ); + if (!iinfo) + goto error; + + iinfo->get_first_data_point = pgmSourcePerformanceTable_get_first_data_point; + iinfo->get_next_data_point = pgmSourcePerformanceTable_get_next_data_point; + iinfo->free_loop_context_at_end = pgmSourcePerformanceTable_free_loop_context; + iinfo->table_reginfo = table_info; + + return netsnmp_register_table_iterator (reg, iinfo); + +error: + if (table_info && table_info->indexes) /* table_data_free_func() is internal */ + snmp_free_var (table_info->indexes); + SNMP_FREE( table_info ); + SNMP_FREE( iinfo ); + netsnmp_handler_registration_free (reg); + + return -1; +} + +/* called for first row of data in SNMP table + * + * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context, + * optionally returns my_data_context. + * + * returns answer or NULL + */ + +static +netsnmp_variable_list* +pgmSourcePerformanceTable_get_first_data_point ( + void** my_loop_context, /* valid through one query of multiple "data points" */ + void** my_data_context, /* answer blob which is passed to handler() */ + netsnmp_variable_list* put_index_data, /* answer */ + netsnmp_iterator_info* mydata /* iinfo on init() */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmSourcePerformanceTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_rwlock_reader_lock (&pgm_sock_list_lock); + + if (!pgm_sock_list) { + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + return NULL; + } + +/* create our own context for this SNMP loop */ + pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1); + context->list = pgm_sock_list; + *my_loop_context = context; + +/* pass on for generic row access */ + return pgmSourcePerformanceTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata); +} + +static +netsnmp_variable_list* +pgmSourcePerformanceTable_get_next_data_point ( + void** my_loop_context, + void** my_data_context, + netsnmp_variable_list* put_index_data, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmSourcePerformanceTable_get_next_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context; + netsnmp_variable_list *idx = put_index_data; + + if (!context->list) + return NULL; + + pgm_sock_t* sock = context->list->data; + +/* pgmSourceGlobalId */ + char gsi[ PGM_GSISTRLEN ]; + pgm_gsi_print_r (&sock->tsi.gsi, gsi, sizeof(gsi)); + snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi)); + idx = idx->next_variable; + +/* pgmSourceSourcePort */ + const unsigned sport = ntohs (sock->tsi.sport); + snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport)); + + *my_data_context = sock; + context->list = context->list->next; + + return put_index_data; +} + +static +void +pgmSourcePerformanceTable_free_loop_context ( + void* my_loop_context, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmPerformanceSourceTable_free_loop_context (my_loop_context:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context; + pgm_free (context); + my_loop_context = NULL; + + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); +} + +static +int +pgmSourcePerformanceTable_handler ( + netsnmp_mib_handler* handler, + netsnmp_handler_registration* reginfo, + netsnmp_agent_request_info* reqinfo, + netsnmp_request_info* requests + ) +{ +/* pre-conditions */ + pgm_assert (NULL != handler); + pgm_assert (NULL != reginfo); + pgm_assert (NULL != reqinfo); + pgm_assert (NULL != requests); + + pgm_debug ("pgmSourcePerformanceTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)", + (const void*)handler, + (const void*)reginfo, + (const void*)reqinfo, + (const void*)requests); + + switch (reqinfo->mode) { + +/* Read-support (also covers GetNext requests) */ + + case MODE_GET: + for (netsnmp_request_info* request = requests; + request; + request = request->next) + { + const pgm_sock_t* sock = (pgm_sock_t*)netsnmp_extract_iterator_context (request); + + if (!sock) { + netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE); + continue; + } + + const pgm_txw_t* window = (const pgm_txw_t*)sock->window; + + netsnmp_variable_list *var = request->requestvb; + netsnmp_table_request_info* table_info = netsnmp_extract_table_info (request); + + if (!table_info) { + snmp_log (LOG_ERR, "pgmSourceTable_handler: empty table request info.\n"); + continue; + } + + switch (table_info->colnum) { + + case COLUMN_PGMSOURCEDATABYTESSENT: + { + const unsigned data_bytes = sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&data_bytes, sizeof(data_bytes) ); + } + break; + + case COLUMN_PGMSOURCEDATAMSGSSENT: + { + const unsigned data_msgs = sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&data_msgs, sizeof(data_msgs) ); + } + break; + + case COLUMN_PGMSOURCEBYTESBUFFERED: + { + const unsigned bytes_buffered = sock->can_send_data ? pgm_txw_size (window) : 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&bytes_buffered, sizeof(bytes_buffered) ); + } + break; + + case COLUMN_PGMSOURCEMSGSBUFFERED: + { + const unsigned msgs_buffered = sock->can_send_data ? pgm_txw_length (window) : 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&msgs_buffered, sizeof(msgs_buffered) ); + } + break; + +/* PGM_PC_SOURCE_SELECTIVE_BYTES_RETRANSMITTED + COLUMN_PGMSOURCEPARITYBYTESRETRANSMITTED */ + case COLUMN_PGMSOURCEBYTESRETRANSMITTED: + { + const unsigned bytes_resent = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_BYTES_RETRANSMITTED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&bytes_resent, sizeof(bytes_resent) ); + } + break; + +/* PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED + COLUMN_PGMSOURCEPARITYMSGSRETRANSMITTED */ + case COLUMN_PGMSOURCEMSGSRETRANSMITTED: + { + const unsigned msgs_resent = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&msgs_resent, sizeof(msgs_resent) ); + } + break; + + case COLUMN_PGMSOURCEBYTESSENT: + { + const unsigned bytes_sent = sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&bytes_sent, sizeof(bytes_sent) ); + } + break; + +/* COLUMN_PGMSOURCEPARITYNAKPACKETSRECEIVED + COLUMN_PGMSOURCESELECTIVENAKPACKETSRECEIVED */ + case COLUMN_PGMSOURCERAWNAKSRECEIVED: + { + const unsigned nak_packets = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&nak_packets, sizeof(nak_packets) ); + } + break; + +/* PGM_PC_SOURCE_SELECTIVE_NAKS_IGNORED + COLUMN_PGMSOURCEPARITYNAKSIGNORED */ + case COLUMN_PGMSOURCENAKSIGNORED: + { + const unsigned naks_ignored = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_IGNORED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&naks_ignored, sizeof(naks_ignored) ); + } + break; + + case COLUMN_PGMSOURCECKSUMERRORS: + { + const unsigned cksum_errors = sock->cumulative_stats[PGM_PC_SOURCE_CKSUM_ERRORS]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&cksum_errors, sizeof(cksum_errors) ); + } + break; + + case COLUMN_PGMSOURCEMALFORMEDNAKS: + { + const unsigned malformed_naks = sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&malformed_naks, sizeof(malformed_naks) ); + } + break; + + case COLUMN_PGMSOURCEPACKETSDISCARDED: + { + const unsigned packets_discarded = sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&packets_discarded, sizeof(packets_discarded) ); + } + break; + +/* PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED + COLUMN_PGMSOURCEPARITYNAKSRECEIVED */ + case COLUMN_PGMSOURCENAKSRCVD: + { + const unsigned naks_received = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&naks_received, sizeof(naks_received) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMSOURCEPARITYBYTESRETRANSMITTED: + { + const unsigned parity_bytes_resent = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&parity_bytes_resent, sizeof(parity_bytes_resent) ); + } + break; + + case COLUMN_PGMSOURCESELECTIVEBYTESRETRANSMITED: + { + const unsigned selective_bytes_resent = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_BYTES_RETRANSMITTED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&selective_bytes_resent, sizeof(selective_bytes_resent) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMSOURCEPARITYMSGSRETRANSMITTED: + { + const unsigned parity_msgs_resent = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&parity_msgs_resent, sizeof(parity_msgs_resent) ); + } + break; + + case COLUMN_PGMSOURCESELECTIVEMSGSRETRANSMITTED: + { + const unsigned selective_msgs_resent = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&selective_msgs_resent, sizeof(selective_msgs_resent) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMSOURCEBYTESADMIT: + { + const unsigned bytes_admit = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&bytes_admit, sizeof(bytes_admit) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMSOURCEMSGSADMIT: + { + const unsigned msgs_admit = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&msgs_admit, sizeof(msgs_admit) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMSOURCEPARITYNAKPACKETSRECEIVED: + { + const unsigned parity_nak_packets = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&parity_nak_packets, sizeof(parity_nak_packets) ); + } + break; + + case COLUMN_PGMSOURCESELECTIVENAKPACKETSRECEIVED: + { + const unsigned selective_nak_packets = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&selective_nak_packets, sizeof(selective_nak_packets) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMSOURCEPARITYNAKSRECEIVED: + { + const unsigned parity_naks = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&parity_naks, sizeof(parity_naks) ); + } + break; + + case COLUMN_PGMSOURCESELECTIVENAKSRECEIVED: + { + const unsigned selective_naks = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&selective_naks, sizeof(selective_naks) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMSOURCEPARITYNAKSIGNORED: + { + const unsigned parity_naks_ignored = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&parity_naks_ignored, sizeof(parity_naks_ignored) ); + } + break; + + case COLUMN_PGMSOURCESELECTIVENAKSIGNORED: + { + const unsigned selective_naks_ignored = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_IGNORED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&selective_naks_ignored, sizeof(selective_naks_ignored) ); + } + break; + + case COLUMN_PGMSOURCEACKERRORS: + { + const unsigned ack_errors = sock->cumulative_stats[PGM_PC_SOURCE_ACK_ERRORS];; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&ack_errors, sizeof(ack_errors) ); + } + break; + + case COLUMN_PGMSOURCEPGMCCACKER: + { + struct sockaddr_in s4; + if (AF_INET == sock->acker_nla.ss_family) + memcpy (&s4, &sock->acker_nla, sizeof(s4)); + else + memset (&s4, 0, sizeof(s4)); + snmp_set_var_typed_value (var, ASN_IPADDRESS, + (const u_char*)&s4.sin_addr.s_addr, + sizeof(struct in_addr) ); + } + break; + + case COLUMN_PGMSOURCETRANSMISSIONCURRENTRATE: + { + const unsigned tx_current_rate = sock->cumulative_stats[PGM_PC_SOURCE_TRANSMISSION_CURRENT_RATE]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&tx_current_rate, sizeof(tx_current_rate) ); + } + break; + + case COLUMN_PGMSOURCEACKPACKETSRECEIVED: + { + const unsigned ack_packets = sock->cumulative_stats[PGM_PC_SOURCE_ACK_PACKETS_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&ack_packets, sizeof(ack_packets) ); + } + break; + +/* COLUMN_PGMSOURCEPARITYNNAKPACKETSRECEIVED + COLUMN_PGMSOURCESELECTIVENNAKPACKETSRECEIVED */ + case COLUMN_PGMSOURCENNAKPACKETSRECEIVED: + { + const unsigned nnak_packets = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAK_PACKETS_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&nnak_packets, sizeof(nnak_packets) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMSOURCEPARITYNNAKPACKETSRECEIVED: + { + const unsigned parity_nnak_packets = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&parity_nnak_packets, sizeof(parity_nnak_packets) ); + } + break; + + case COLUMN_PGMSOURCESELECTIVENNAKPACKETSRECEIVED: + { + const unsigned selective_nnak_packets = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAK_PACKETS_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&selective_nnak_packets, sizeof(selective_nnak_packets) ); + } + break; + +/* COLUMN_PGMSOURCEPARITYNNAKSRECEIVED + COLUMN_PGMSOURCESELECTIVENNAKSRECEIVED */ + case COLUMN_PGMSOURCENNAKSRECEIVED: + { + const unsigned nnaks_received = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAKS_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&nnaks_received, sizeof(nnaks_received) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMSOURCEPARITYNNAKSRECEIVED: + { + const unsigned parity_nnaks = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&parity_nnaks, sizeof(parity_nnaks) ); + } + break; + + case COLUMN_PGMSOURCESELECTIVENNAKSRECEIVED: + { + const unsigned selective_nnaks = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAKS_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&selective_nnaks, sizeof(selective_nnaks) ); + } + break; + + case COLUMN_PGMSOURCENNAKERRORS: + { + const unsigned malformed_nnaks = sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&malformed_nnaks, sizeof(malformed_nnaks) ); + } + break; + + default: + snmp_log (LOG_ERR, "pgmSourcePerformanceTable_handler: unknown column.\n"); + break; + } + } + break; + + case MODE_SET_RESERVE1: + default: + snmp_log (LOG_ERR, "pgmSourcePerformanceTable_handler: unsupported mode.\n"); + break; + + } + + return SNMP_ERR_NOERROR; +} + +/* + * pgmReceiverTable + */ + +static +int +initialize_table_pgmReceiverTable(void) +{ + pgm_debug ("initialize_table_pgmReceiverTable ()"); + + static const oid pgmReceiverTable_oid[] = {1,3,6,1,3,112,1,3,100,2}; + netsnmp_table_registration_info* table_info = NULL; + netsnmp_iterator_info* iinfo = NULL; + netsnmp_handler_registration* reg = NULL; + + reg = netsnmp_create_handler_registration ("pgmReceiverTable", pgmReceiverTable_handler, + pgmReceiverTable_oid, OID_LENGTH( pgmReceiverTable_oid ), + HANDLER_CAN_RONLY); + if (!reg) + goto error; + + table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); + if (!table_info) + goto error; + + table_info->min_column = COLUMN_PGMRECEIVERGROUPADDRESS; + table_info->max_column = COLUMN_PGMRECEIVERUNIQUEINSTANCE; + + netsnmp_table_helper_add_indexes (table_info, + ASN_OCTET_STR, /* index: pgmReceiverGlobalId */ + ASN_UNSIGNED, /* index: pgmReceiverSourcePort */ + ASN_UNSIGNED, /* index: pgmReceiverInstance */ + 0); + + iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info ); + if (!iinfo) + goto error; + + iinfo->get_first_data_point = pgmReceiverTable_get_first_data_point; + iinfo->get_next_data_point = pgmReceiverTable_get_next_data_point; + iinfo->free_loop_context_at_end = pgmReceiverTable_free_loop_context; + iinfo->table_reginfo = table_info; + + return netsnmp_register_table_iterator (reg, iinfo); + +error: + if (table_info && table_info->indexes) /* table_data_free_func() is internal */ + snmp_free_var (table_info->indexes); + SNMP_FREE( table_info ); + SNMP_FREE( iinfo ); + netsnmp_handler_registration_free (reg); + + return -1; +} + +/* called for first row of data in SNMP table + * + * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context, + * optionally returns my_data_context. + * + * returns answer or NULL + */ + +static +netsnmp_variable_list* +pgmReceiverTable_get_first_data_point ( + void** my_loop_context, /* valid through one query of multiple "data points" */ + void** my_data_context, /* answer blob which is passed to handler() */ + netsnmp_variable_list* put_index_data, /* answer */ + netsnmp_iterator_info* mydata /* iinfo on init() */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmReceiverTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_rwlock_reader_lock (&pgm_sock_list_lock); + + if (!pgm_sock_list) { + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + return NULL; + } + +/* create our own context for this SNMP loop */ + pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1); + +/* hunt to find first node, through all socks */ + for (context->list = pgm_sock_list; + context->list; + context->list = context->list->next) + { +/* and through all peers for each sock */ + pgm_sock_t* sock = (pgm_sock_t*)context->list->data; + pgm_rwlock_reader_lock (&sock->peers_lock); + context->node = sock->peers_list; + if (context->node) { +/* maintain this sock's peers lock */ + break; + } + + pgm_rwlock_reader_unlock (&sock->peers_lock); + } + +/* no node found */ + if (!context->node) { + pgm_free (context); + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + return NULL; + } + + *my_loop_context = context; + +/* pass on for generic row access */ + return pgmReceiverTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata); +} + +static +netsnmp_variable_list* +pgmReceiverTable_get_next_data_point ( + void** my_loop_context, + void** my_data_context, + netsnmp_variable_list* put_index_data, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmReceiverTable_get_next_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context; + netsnmp_variable_list *idx = put_index_data; + + if (!context->list) + return NULL; + + pgm_sock_t* sock = context->list->data; + + if (!context->node) + return NULL; + + pgm_peer_t* peer = context->node->data; + +/* pgmReceiverGlobalId */ + char gsi[ PGM_GSISTRLEN ]; + pgm_gsi_print_r (&peer->tsi.gsi, gsi, sizeof(gsi)); + snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi)); + idx = idx->next_variable; + +/* pgmReceiverSourcePort */ + const unsigned sport = ntohs (peer->tsi.sport); + snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport)); + idx = idx->next_variable; + +/* pgmReceiverInstance */ + const unsigned instance = context->instance++; + snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&instance, sizeof(instance)); + +/* set data context to pass to handler callback */ + *my_data_context = peer; + +/* hunt for next valid node */ + if (context->node->next) { + context->node = context->node->next; + } else { + context->node = NULL; + while (context->list->next) + { + pgm_rwlock_reader_unlock (&sock->peers_lock); + context->list = context->list->next; + sock = context->list->data; + pgm_rwlock_reader_lock (&sock->peers_lock); + context->node = sock->peers_list; + if (context->node) { +/* keep lock */ + break; + } + } + } + + return put_index_data; +} + +static +void +pgmReceiverTable_free_loop_context ( + void* my_loop_context, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmReceiverTable_free_loop_context (my_loop_context:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context; + +/* check for intra-peer state */ + if (context->list) { + pgm_sock_t* sock = context->list->data; + pgm_rwlock_reader_unlock (&sock->peers_lock); + } + + pgm_free (context); + my_loop_context = NULL; + + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); +} + +static +int +pgmReceiverTable_handler ( + netsnmp_mib_handler* handler, + netsnmp_handler_registration* reginfo, + netsnmp_agent_request_info* reqinfo, + netsnmp_request_info* requests + ) +{ +/* pre-conditions */ + pgm_assert (NULL != handler); + pgm_assert (NULL != reginfo); + pgm_assert (NULL != reqinfo); + pgm_assert (NULL != requests); + + pgm_debug ("pgmReceiverTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)", + (const void*)handler, + (const void*)reginfo, + (const void*)reqinfo, + (const void*)requests); + + switch (reqinfo->mode) { + +/* Read-support (also covers GetNext requests) */ + + case MODE_GET: + for (netsnmp_request_info* request = requests; + request; + request = request->next) + { + const pgm_peer_t* peer = (pgm_peer_t*)netsnmp_extract_iterator_context (request); + + if (!peer) { + netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE); + continue; + } + + netsnmp_variable_list *var = request->requestvb; + netsnmp_table_request_info* table_info = netsnmp_extract_table_info(request); + + if (table_info == NULL) { + snmp_log (LOG_ERR, "pgmReceiverTable_handler: empty table request info.\n"); + continue; + } + + switch (table_info->colnum) { + + case COLUMN_PGMRECEIVERGROUPADDRESS: + { + struct sockaddr_in s4; + if (AF_INET == peer->group_nla.ss_family) + memcpy (&s4, &peer->group_nla, sizeof(s4)); + else + memset (&s4, 0, sizeof(s4)); + snmp_set_var_typed_value (var, ASN_IPADDRESS, + (const u_char*)&s4.sin_addr.s_addr, + sizeof(struct in_addr) ); + } + break; + +/* by definition same as sock */ + case COLUMN_PGMRECEIVERDESTPORT: + { + const unsigned dport = ntohs (peer->sock->dport); + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&dport, sizeof(dport) ); + } + break; + + case COLUMN_PGMRECEIVERSOURCEADDRESS: + { + struct sockaddr_in s4; + if (AF_INET == peer->nla.ss_family) + memcpy (&s4, &peer->nla, sizeof(s4)); + else + memset (&s4, 0, sizeof(s4)); + snmp_set_var_typed_value (var, ASN_IPADDRESS, + (const u_char*)&s4.sin_addr.s_addr, + sizeof(struct in_addr) ); + } + break; + + case COLUMN_PGMRECEIVERLASTHOP: + { + struct sockaddr_in s4; + if (AF_INET == peer->local_nla.ss_family) + memcpy (&s4, &peer->local_nla, sizeof(s4)); + else + memset (&s4, 0, sizeof(s4)); + snmp_set_var_typed_value (var, ASN_IPADDRESS, + (const u_char*)&s4.sin_addr.s_addr, + sizeof(struct in_addr) ); + } + break; + +/* copy index[0] */ + case COLUMN_PGMRECEIVERSOURCEGSI: + snmp_set_var_typed_value (var, ASN_OCTET_STR, + (const u_char*)table_info->indexes->val.string, + table_info->indexes->val_len); + break; + +/* copy index[1] */ + case COLUMN_PGMRECEIVERSOURCEPORTNUMBER: + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)table_info->indexes->next_variable->val.integer, + table_info->indexes->next_variable->val_len); + break; + +/* copy index[2] */ + case COLUMN_PGMRECEIVERUNIQUEINSTANCE: + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)table_info->indexes->next_variable->next_variable->val.integer, + table_info->indexes->next_variable->next_variable->val_len); + break; + + default: + snmp_log (LOG_ERR, "pgmReceiverTable_handler: unknown column.\n"); + break; + } + } + break; + + case MODE_SET_RESERVE1: + default: + snmp_log (LOG_ERR, "pgmReceiverTable_handler: unsupported mode.\n"); + break; + + } + + return SNMP_ERR_NOERROR; +} + +/* + * pgmReceiverConfigTable + * + */ + +static +int +initialize_table_pgmReceiverConfigTable(void) +{ + pgm_debug ("initialize_table_pgmReceiverConfigTable ()"); + + static const oid pgmReceiverConfigTable_oid[] = {1,3,6,1,3,112,1,3,100,3}; + netsnmp_table_registration_info* table_info = NULL; + netsnmp_iterator_info* iinfo = NULL; + netsnmp_handler_registration* reg = NULL; + + reg = netsnmp_create_handler_registration ("pgmReceiverConfigTable", pgmReceiverConfigTable_handler, + pgmReceiverConfigTable_oid, OID_LENGTH(pgmReceiverConfigTable_oid), + HANDLER_CAN_RONLY); + if (!reg) + goto error; + + table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); + if (!table_info) + goto error; + + table_info->min_column = COLUMN_PGMRECEIVERNAKBACKOFFIVL; + table_info->max_column = COLUMN_PGMRECEIVERNAKFAILURETHRESHOLD; + + netsnmp_table_helper_add_indexes (table_info, + ASN_OCTET_STR, /* index: pgmReceiverConfigGlobalId */ + ASN_UNSIGNED, /* index: pgmReceiverConfigSourcePort */ + ASN_UNSIGNED, /* index: pgmReceiverInstance */ + 0); + + iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info ); + if (!iinfo) + goto error; + + iinfo->get_first_data_point = pgmReceiverConfigTable_get_first_data_point; + iinfo->get_next_data_point = pgmReceiverConfigTable_get_next_data_point; + iinfo->free_loop_context_at_end = pgmReceiverConfigTable_free_loop_context; + iinfo->table_reginfo = table_info; + + return netsnmp_register_table_iterator (reg, iinfo); + +error: + if (table_info && table_info->indexes) /* table_data_free_func() is internal */ + snmp_free_var (table_info->indexes); + SNMP_FREE( table_info ); + SNMP_FREE( iinfo ); + netsnmp_handler_registration_free (reg); + + return -1; +} + +/* called for first row of data in SNMP table + * + * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context, + * optionally returns my_data_context. + * + * returns answer or NULL + */ + +static +netsnmp_variable_list* +pgmReceiverConfigTable_get_first_data_point( + void** my_loop_context, /* valid through one query of multiple "data points" */ + void** my_data_context, /* answer blob which is passed to handler() */ + netsnmp_variable_list* put_index_data, /* answer */ + netsnmp_iterator_info* mydata /* iinfo on init() */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmReceiverConfigTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_rwlock_reader_lock (&pgm_sock_list_lock); + + if (!pgm_sock_list) { + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + return NULL; + } + +/* create our own context for this SNMP loop */ + pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1); + +/* hunt to find first node, through all socks */ + for (context->list = pgm_sock_list; + context->list; + context->list = context->list->next) + { +/* and through all peers for each sock */ + pgm_sock_t* sock = (pgm_sock_t*)context->list->data; + pgm_rwlock_reader_lock (&sock->peers_lock); + context->node = sock->peers_list; + if (context->node) + break; + + pgm_rwlock_reader_unlock (&sock->peers_lock); + } + +/* no node found */ + if (!context->node) { + pgm_free (context); + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + return NULL; + } + + *my_loop_context = context; + +/* pass on for generic row access */ + return pgmReceiverConfigTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata); +} + +static +netsnmp_variable_list* +pgmReceiverConfigTable_get_next_data_point( + void** my_loop_context, + void** my_data_context, + netsnmp_variable_list* put_index_data, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmReceiverConfigTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context; + netsnmp_variable_list *idx = put_index_data; + + if (!context->list) + return NULL; + + pgm_sock_t* sock = context->list->data; + + if (!context->node) + return NULL; + + pgm_peer_t* peer = context->node->data; + +/* pgmReceiverGlobalId */ + char gsi[ PGM_GSISTRLEN ]; + pgm_gsi_print_r (&peer->tsi.gsi, gsi, sizeof(gsi)); + snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi)); + idx = idx->next_variable; + +/* pgmReceiverSourcePort */ + const unsigned sport = ntohs (peer->tsi.sport); + snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport)); + idx = idx->next_variable; + +/* pgmReceiverInstance */ + const unsigned instance = context->instance++; + snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&instance, sizeof(instance)); + +/* set data context to pass to handler callback */ + *my_data_context = peer; + +/* hunt for next valid node */ + if (context->node->next) { + context->node = context->node->next; + } else { + context->node = NULL; + while (context->list->next) + { + pgm_rwlock_reader_unlock (&sock->peers_lock); + context->list = context->list->next; + sock = context->list->data; + pgm_rwlock_reader_lock (&sock->peers_lock); + context->node = sock->peers_list; + if (context->node) { +/* keep lock */ + break; + } + } + } + + return put_index_data; +} + +static +void +pgmReceiverConfigTable_free_loop_context ( + void* my_loop_context, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmReceiverConfigTable_free_loop_context (my_loop_context:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context; + +/* check for intra-peer state */ + if (context->list) { + pgm_sock_t* sock = context->list->data; + pgm_rwlock_reader_unlock (&sock->peers_lock); + } + + pgm_free (context); + my_loop_context = NULL; + + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); +} + +static +int +pgmReceiverConfigTable_handler ( + netsnmp_mib_handler* handler, + netsnmp_handler_registration* reginfo, + netsnmp_agent_request_info* reqinfo, + netsnmp_request_info* requests + ) +{ +/* pre-conditions */ + pgm_assert (NULL != handler); + pgm_assert (NULL != reginfo); + pgm_assert (NULL != reqinfo); + pgm_assert (NULL != requests); + + pgm_debug ("pgmReceiverConfigTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)", + (const void*)handler, + (const void*)reginfo, + (const void*)reqinfo, + (const void*)requests); + + switch (reqinfo->mode) { + +/* Read-support (also covers GetNext requests) */ + + case MODE_GET: + for (netsnmp_request_info* request = requests; + request; + request = request->next) + { + const pgm_peer_t* peer = (pgm_peer_t*)netsnmp_extract_iterator_context(request); + + if (peer == NULL) { + netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE); + continue; + } + + netsnmp_variable_list *var = request->requestvb; + netsnmp_table_request_info* table_info = netsnmp_extract_table_info(request); + + if (table_info == NULL) { + snmp_log (LOG_ERR, "pgmReceiverTable_handler: empty table request info.\n"); + continue; + } + + switch (table_info->colnum) { + +/* nak_bo_ivl from sock */ + case COLUMN_PGMRECEIVERNAKBACKOFFIVL: + { + const unsigned nak_bo_ivl = peer->sock->nak_bo_ivl; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&nak_bo_ivl, sizeof(nak_bo_ivl) ); + } + break; + +/* nak_rpt_ivl from sock */ + case COLUMN_PGMRECEIVERNAKREPEATIVL: + { + const unsigned nak_rpt_ivl = peer->sock->nak_rpt_ivl; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&nak_rpt_ivl, sizeof(nak_rpt_ivl) ); + } + break; + +/* nak_ncf_retries from sock */ + case COLUMN_PGMRECEIVERNAKNCFRETRIES: + { + const unsigned nak_ncf_retries = peer->sock->nak_ncf_retries; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&nak_ncf_retries, sizeof(nak_ncf_retries) ); + } + break; + +/* nak_rdata_ivl from sock */ + case COLUMN_PGMRECEIVERNAKRDATAIVL: + { + const unsigned nak_rdata_ivl = peer->sock->nak_rdata_ivl; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&nak_rdata_ivl, sizeof(nak_rdata_ivl) ); + } + break; + +/* nak_data_retries from sock */ + case COLUMN_PGMRECEIVERNAKDATARETRIES: + { + const unsigned nak_data_retries = peer->sock->nak_data_retries; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&nak_data_retries, sizeof(nak_data_retries) ); + } + break; + +/* FIXED: pgmReceiverSendNaks = enabled(1) */ + case COLUMN_PGMRECEIVERSENDNAKS: + { + const unsigned send_naks = PGMRECEIVERSENDNAKS_ENABLED; + snmp_set_var_typed_value (var, ASN_INTEGER, + (const u_char*)&send_naks, sizeof(send_naks) ); + } + break; + +/* FIXED: pgmReceiverLateJoin = disabled(2) */ + case COLUMN_PGMRECEIVERLATEJOIN: + { + const unsigned late_join = PGMRECEIVERLATEJOIN_DISABLED; + snmp_set_var_typed_value (var, ASN_INTEGER, + (const u_char*)&late_join, sizeof(late_join) ); + } + break; + +/* FIXED: 1 for multicast */ + case COLUMN_PGMRECEIVERNAKTTL: + { + const unsigned nak_hops = 1; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&nak_hops, sizeof(nak_hops) ); + } + break; + +/* FIXED: pgmReceiverDeliveryOrder = ordered(2) */ + case COLUMN_PGMRECEIVERDELIVERYORDER: + { + const unsigned delivery_order = PGMRECEIVERDELIVERYORDER_ORDERED; + snmp_set_var_typed_value (var, ASN_INTEGER, + (const u_char*)&delivery_order, sizeof(delivery_order) ); + } + break; + +/* FIXED: pgmReceiverMcastNaks = disabled(2) */ + case COLUMN_PGMRECEIVERMCASTNAKS: + { + const unsigned mcast_naks = PGMRECEIVERMCASTNAKS_DISABLED; + snmp_set_var_typed_value (var, ASN_INTEGER, + (const u_char*)&mcast_naks, sizeof(mcast_naks) ); + } + break; + +/* TODO: traps */ + case COLUMN_PGMRECEIVERNAKFAILURETHRESHOLDTIMER: + case COLUMN_PGMRECEIVERNAKFAILURETHRESHOLD: + { + const unsigned threshold = 0; + snmp_set_var_typed_value (var, ASN_UNSIGNED, + (const u_char*)&threshold, sizeof(threshold) ); + } + break; + + default: + snmp_log (LOG_ERR, "pgmReceiverTable_handler: unknown column.\n"); + break; + } + } + break; + + case MODE_SET_RESERVE1: + default: + snmp_log (LOG_ERR, "pgmReceiverTable_handler: unsupported mode.\n"); + break; + + } + + return SNMP_ERR_NOERROR; +} + +/* + * pgmReceiverPerformanceTable + */ + +static +int +initialize_table_pgmReceiverPerformanceTable (void) +{ + pgm_debug ("initialize_table_pgmReceiverPerformanceTable ()"); + + static const oid pgmReceiverPerformanceTable_oid[] = {1,3,6,1,3,112,1,3,100,4}; + netsnmp_table_registration_info* table_info = NULL; + netsnmp_iterator_info* iinfo = NULL; + netsnmp_handler_registration* reg = NULL; + + reg = netsnmp_create_handler_registration ("pgmReceiverPerformanceTable", pgmReceiverPerformanceTable_handler, + pgmReceiverPerformanceTable_oid, OID_LENGTH( pgmReceiverPerformanceTable_oid ), + HANDLER_CAN_RONLY); + if (!reg) + goto error; + + table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); + if (!table_info) + goto error; + + table_info->min_column = COLUMN_PGMRECEIVERDATABYTESRECEIVED; + table_info->max_column = COLUMN_PGMRECEIVERLASTINTERVALNAKFAILURES; + + netsnmp_table_helper_add_indexes (table_info, + ASN_OCTET_STR, /* index: pgmReceiverGlobalId */ + ASN_UNSIGNED, /* index: pgmReceiverSourcePort */ + ASN_UNSIGNED, /* index: pgmReceiverInstance */ + 0); + + iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info ); + if (!iinfo) + goto error; + + iinfo->get_first_data_point = pgmReceiverPerformanceTable_get_first_data_point; + iinfo->get_next_data_point = pgmReceiverPerformanceTable_get_next_data_point; + iinfo->free_loop_context_at_end = pgmReceiverPerformanceTable_free_loop_context; + iinfo->table_reginfo = table_info; + + return netsnmp_register_table_iterator (reg, iinfo); + +error: + if (table_info && table_info->indexes) /* table_data_free_func() is internal */ + snmp_free_var (table_info->indexes); + SNMP_FREE( table_info ); + SNMP_FREE( iinfo ); + netsnmp_handler_registration_free (reg); + + return -1; +} + +/* called for first row of data in SNMP table + * + * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context, + * optionally returns my_data_context. + * + * returns answer or NULL + */ + +static +netsnmp_variable_list* +pgmReceiverPerformanceTable_get_first_data_point ( + void** my_loop_context, /* valid through one query of multiple "data points" */ + void** my_data_context, /* answer blob which is passed to handler() */ + netsnmp_variable_list* put_index_data, /* answer */ + netsnmp_iterator_info* mydata /* iinfo on init() */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmReceiverPerformanceTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_rwlock_reader_lock (&pgm_sock_list_lock); + + if (!pgm_sock_list) { + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + return NULL; + } + +/* create our own context for this SNMP loop */ + pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1); + +/* hunt to find first node, through all socks */ + for (context->list = pgm_sock_list; + context->list; + context->list = context->list->next) + { +/* and through all peers for each sock */ + pgm_sock_t* sock = (pgm_sock_t*)context->list->data; + pgm_rwlock_reader_lock (&sock->peers_lock); + context->node = sock->peers_list; + if (context->node) + break; + + pgm_rwlock_reader_unlock (&sock->peers_lock); + } + +/* no node found */ + if (!context->node) { + pgm_free (context); + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); + return NULL; + } + + *my_loop_context = context; + +/* pass on for generic row access */ + return pgmReceiverPerformanceTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata); +} + +static +netsnmp_variable_list* +pgmReceiverPerformanceTable_get_next_data_point ( + void** my_loop_context, + void** my_data_context, + netsnmp_variable_list* put_index_data, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != my_data_context); + pgm_assert (NULL != put_index_data); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmReceiverPerformanceTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)my_data_context, + (const void*)put_index_data, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context; + netsnmp_variable_list *idx = put_index_data; + + if (!context->list) + return NULL; + + pgm_sock_t* sock = context->list->data; + + if (!context->node) + return NULL; + + pgm_peer_t* peer = context->node->data; + +/* pgmReceiverGlobalId */ + char gsi[ PGM_GSISTRLEN ]; + pgm_gsi_print_r (&peer->tsi.gsi, gsi, sizeof(gsi)); + snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi)); + idx = idx->next_variable; + +/* pgmReceiverSourcePort */ + const unsigned sport = ntohs (peer->tsi.sport); + snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport)); + idx = idx->next_variable; + +/* pgmReceiverInstance */ + const unsigned instance = context->instance++; + snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&instance, sizeof(instance)); + +/* set data context to pass to handler callback */ + *my_data_context = peer; + +/* hunt for next valid node */ + if (context->node->next) { + context->node = context->node->next; + } else { + context->node = NULL; + while (context->list->next) + { + pgm_rwlock_reader_unlock (&sock->peers_lock); + context->list = context->list->next; + sock = context->list->data; + pgm_rwlock_reader_lock (&sock->peers_lock); + context->node = sock->peers_list; + + if (context->node) + break; + } + } + + return put_index_data; +} + +static +void +pgmReceiverPerformanceTable_free_loop_context ( + void* my_loop_context, + netsnmp_iterator_info* mydata + ) +{ +/* pre-conditions */ + pgm_assert (NULL != my_loop_context); + pgm_assert (NULL != mydata); + + pgm_debug ("pgmReceiverPerformanceTable_free_loop_context (my_loop_context:%p mydata:%p)", + (const void*)my_loop_context, + (const void*)mydata); + + pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context; + +/* check for intra-peer state */ + if (context->list) { + pgm_sock_t* sock = context->list->data; + pgm_rwlock_reader_unlock (&sock->peers_lock); + } + + pgm_free (context); + my_loop_context = NULL; + + pgm_rwlock_reader_unlock (&pgm_sock_list_lock); +} + +static +int +pgmReceiverPerformanceTable_handler ( + netsnmp_mib_handler* handler, + netsnmp_handler_registration* reginfo, + netsnmp_agent_request_info* reqinfo, + netsnmp_request_info* requests + ) +{ +/* pre-conditions */ + pgm_assert (NULL != handler); + pgm_assert (NULL != reginfo); + pgm_assert (NULL != reqinfo); + pgm_assert (NULL != requests); + + pgm_debug ("pgmReceiverPerformanceTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)", + (const void*)handler, + (const void*)reginfo, + (const void*)reqinfo, + (const void*)requests); + + switch (reqinfo->mode) { + +/* Read-support (also covers GetNext requests) */ + + case MODE_GET: + for (netsnmp_request_info* request = requests; + request; + request = request->next) + { + const pgm_peer_t* peer = (pgm_peer_t*)netsnmp_extract_iterator_context (request); + + if (!peer) { + netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE); + continue; + } + + const pgm_rxw_t* window = (const pgm_rxw_t*)peer->window; + + netsnmp_variable_list *var = request->requestvb; + netsnmp_table_request_info* table_info = netsnmp_extract_table_info (request); + + if (!table_info) { + snmp_log (LOG_ERR, "pgmReceiverTable_handler: empty table request info.\n"); + continue; + } + + switch (table_info->colnum) { + + case COLUMN_PGMRECEIVERDATABYTESRECEIVED: + { + const unsigned data_bytes = peer->cumulative_stats[PGM_PC_RECEIVER_DATA_BYTES_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&data_bytes, sizeof(data_bytes) ); + } + break; + + case COLUMN_PGMRECEIVERDATAMSGSRECEIVED: + { + const unsigned data_msgs = peer->cumulative_stats[PGM_PC_RECEIVER_DATA_MSGS_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&data_msgs, sizeof(data_msgs) ); + } + break; + +/* total */ + case COLUMN_PGMRECEIVERNAKSSENT: + { + const unsigned naks_sent = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SENT]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&naks_sent, sizeof(naks_sent) ); + } + break; + +/* total */ + case COLUMN_PGMRECEIVERNAKSRETRANSMITTED: + { + const unsigned naks_resent = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_RETRANSMITTED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&naks_resent, sizeof(naks_resent) ); + } + break; + +/* total */ + case COLUMN_PGMRECEIVERNAKFAILURES: + { + const unsigned nak_failures = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_FAILED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&nak_failures, sizeof(nak_failures) ); + } + break; + + case COLUMN_PGMRECEIVERBYTESRECEIVED: + { + const unsigned bytes_received = peer->cumulative_stats[PGM_PC_RECEIVER_BYTES_RECEIVED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&bytes_received, sizeof(bytes_received) ); + } + break; + +/* total */ + case COLUMN_PGMRECEIVERNAKSSUPPRESSED: + { + const unsigned naks_suppressed = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&naks_suppressed, sizeof(naks_suppressed) ); + } + break; + +/* bogus: same as source checksum errors */ + case COLUMN_PGMRECEIVERCKSUMERRORS: + { + const unsigned cksum_errors = peer->sock->cumulative_stats[PGM_PC_SOURCE_CKSUM_ERRORS]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&cksum_errors, sizeof(cksum_errors) ); + } + break; + + case COLUMN_PGMRECEIVERMALFORMEDSPMS: + { + const unsigned malformed_spms = peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_SPMS]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&malformed_spms, sizeof(malformed_spms) ); + } + break; + + case COLUMN_PGMRECEIVERMALFORMEDODATA: + { + const unsigned malformed_odata = peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_ODATA]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&malformed_odata, sizeof(malformed_odata) ); + } + break; + + case COLUMN_PGMRECEIVERMALFORMEDRDATA: + { + const unsigned malformed_rdata = peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_RDATA]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&malformed_rdata, sizeof(malformed_rdata) ); + } + break; + + case COLUMN_PGMRECEIVERMALFORMEDNCFS: + { + const unsigned malformed_ncfs = peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_NCFS]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&malformed_ncfs, sizeof(malformed_ncfs) ); + } + break; + + case COLUMN_PGMRECEIVERPACKETSDISCARDED: + { + const unsigned packets_discarded = peer->cumulative_stats[PGM_PC_RECEIVER_PACKETS_DISCARDED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&packets_discarded, sizeof(packets_discarded) ); + } + break; + + case COLUMN_PGMRECEIVERLOSSES: + { + const unsigned losses = window->cumulative_losses; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&losses, sizeof(losses) ); + } + break; + + case COLUMN_PGMRECEIVERBYTESDELIVEREDTOAPP: + { + const unsigned bytes_delivered =window->bytes_delivered; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&bytes_delivered, sizeof(bytes_delivered) ); + } + break; + + case COLUMN_PGMRECEIVERMSGSDELIVEREDTOAPP: + { + const unsigned msgs_delivered = window->msgs_delivered; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&msgs_delivered, sizeof(msgs_delivered) ); + } + break; + + case COLUMN_PGMRECEIVERDUPSPMS: + { + const unsigned dup_spms = peer->cumulative_stats[PGM_PC_RECEIVER_DUP_SPMS]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&dup_spms, sizeof(dup_spms) ); + } + break; + + case COLUMN_PGMRECEIVERDUPDATAS: + { + const unsigned dup_data = peer->cumulative_stats[PGM_PC_RECEIVER_DUP_DATAS]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&dup_data, sizeof(dup_data) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMRECEIVERDUPPARITIES: + { + const unsigned dup_parity = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&dup_parity, sizeof(dup_parity) ); + } + break; + +/* COLUMN_PGMRECEIVERPARITYNAKPACKETSSENT + COLUMN_PGMRECEIVERSELECTIVENAKPACKETSSENT */ + case COLUMN_PGMRECEIVERNAKPACKETSSENT: + { + const unsigned nak_packets = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAK_PACKETS_SENT]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&nak_packets, sizeof(nak_packets) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMRECEIVERPARITYNAKPACKETSSENT: + { + const unsigned parity_naks = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&parity_naks, sizeof(parity_naks) ); + } + break; + + case COLUMN_PGMRECEIVERSELECTIVENAKPACKETSSENT: + { + const unsigned nak_packets = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAK_PACKETS_SENT]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&nak_packets, sizeof(nak_packets) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMRECEIVERPARITYNAKSSENT: + { + const unsigned parity_naks = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&parity_naks, sizeof(parity_naks) ); + } + break; + + case COLUMN_PGMRECEIVERSELECTIVENAKSSENT: + { + const unsigned naks_sent = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SENT]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&naks_sent, sizeof(naks_sent) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMRECEIVERPARITYNAKSRETRANSMITTED: + { + const unsigned parity_resent = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&parity_resent, sizeof(parity_resent) ); + } + break; + + case COLUMN_PGMRECEIVERSELECTIVENAKSRETRANSMITTED: + { + const unsigned naks_resent = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_RETRANSMITTED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&naks_resent, sizeof(naks_resent) ); + } + break; + +/* COLUMN_PGMRECEIVERPARITYNAKSFAILED + COLUMN_PGMRECEIVERSELECTIVENAKSFAILED */ + case COLUMN_PGMRECEIVERNAKSFAILED: + { + const unsigned naks_failed = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_FAILED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&naks_failed, sizeof(naks_failed) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMRECEIVERPARITYNAKSFAILED: + { + const unsigned parity_failed = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&parity_failed, sizeof(parity_failed) ); + } + break; + + case COLUMN_PGMRECEIVERSELECTIVENAKSFAILED: + { + const unsigned naks_failed = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_FAILED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&naks_failed, sizeof(naks_failed) ); + } + break; + + case COLUMN_PGMRECEIVERNAKSFAILEDRXWADVANCED: + { + const unsigned rxw_failed = peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_RXW_ADVANCED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&rxw_failed, sizeof(rxw_failed) ); + } + break; + + case COLUMN_PGMRECEIVERNAKSFALEDNCFRETRIESEXCEEDED: + { + const unsigned ncf_retries = peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_NCF_RETRIES_EXCEEDED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&ncf_retries, sizeof(ncf_retries) ); + } + break; + + case COLUMN_PGMRECEIVERNAKSFAILEDDATARETRIESEXCEEDED: + { + const unsigned data_retries = peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_DATA_RETRIES_EXCEEDED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&data_retries, sizeof(data_retries) ); + } + break; + +/* FIXED: 0 - absolutely no idea what this means */ + case COLUMN_PGMRECEIVERNAKSFAILEDGENEXPIRED: + { + const unsigned happy_pandas = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&happy_pandas, sizeof(happy_pandas) ); + } + break; + + case COLUMN_PGMRECEIVERNAKFAILURESDELIVERED: + { + const unsigned delivered = peer->cumulative_stats[PGM_PC_RECEIVER_NAK_FAILURES_DELIVERED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&delivered, sizeof(delivered) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMRECEIVERPARITYNAKSSUPPRESSED: + { + const unsigned suppressed = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&suppressed, sizeof(suppressed) ); + } + break; + + case COLUMN_PGMRECEIVERSELECTIVENAKSSUPPRESSED: + { + const unsigned suppressed = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&suppressed, sizeof(suppressed) ); + } + break; + + case COLUMN_PGMRECEIVERNAKERRORS: + { + const unsigned malformed_naks = peer->cumulative_stats[PGM_PC_RECEIVER_NAK_ERRORS]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&malformed_naks, sizeof(malformed_naks) ); + } + break; + +/* FIXED: 0 */ + case COLUMN_PGMRECEIVEROUTSTANDINGPARITYNAKS: + { + const unsigned outstanding_parity = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&outstanding_parity, sizeof(outstanding_parity) ); + } + break; + + case COLUMN_PGMRECEIVEROUTSTANDINGSELECTIVENAKS: + { + const unsigned outstanding_selective = window->nak_backoff_queue.length + + window->wait_ncf_queue.length + + window->wait_data_queue.length; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&outstanding_selective, sizeof(outstanding_selective) ); + } + break; + + case COLUMN_PGMRECEIVERLASTACTIVITY: + { + union { + unsigned uint_value; + time_t time_t_value; + } last_activity; + pgm_time_since_epoch (&peer->last_packet, &last_activity.time_t_value); + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&last_activity.uint_value, sizeof(last_activity.uint_value) ); + } + break; + + case COLUMN_PGMRECEIVERNAKSVCTIMEMIN: + { + const unsigned min_repair_time = window->min_fill_time; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&min_repair_time, sizeof(min_repair_time) ); + } + break; + + case COLUMN_PGMRECEIVERNAKSVCTIMEMEAN: + { + const unsigned mean_repair_time = peer->cumulative_stats[PGM_PC_RECEIVER_NAK_SVC_TIME_MEAN]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&mean_repair_time, sizeof(mean_repair_time) ); + } + break; + + case COLUMN_PGMRECEIVERNAKSVCTIMEMAX: + { + const unsigned max_repair_time = window->max_fill_time; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&max_repair_time, sizeof(max_repair_time) ); + } + break; + + case COLUMN_PGMRECEIVERNAKFAILTIMEMIN: + { + const unsigned min_fail_time = peer->min_fail_time; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&min_fail_time, sizeof(min_fail_time) ); + } + break; + + case COLUMN_PGMRECEIVERNAKFAILTIMEMEAN: + { + const unsigned mean_fail_time = peer->cumulative_stats[PGM_PC_RECEIVER_NAK_FAIL_TIME_MEAN]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&mean_fail_time, sizeof(mean_fail_time) ); + } + break; + + case COLUMN_PGMRECEIVERNAKFAILTIMEMAX: + { + const unsigned max_fail_time = peer->max_fail_time; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&max_fail_time, sizeof(max_fail_time) ); + } + break; + + case COLUMN_PGMRECEIVERNAKTRANSMITMIN: + { + const unsigned min_transmit_count = window->min_nak_transmit_count; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&min_transmit_count, sizeof(min_transmit_count) ); + } + break; + + case COLUMN_PGMRECEIVERNAKTRANSMITMEAN: + { + const unsigned mean_transmit_count = peer->cumulative_stats[PGM_PC_RECEIVER_TRANSMIT_MEAN]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&mean_transmit_count, sizeof(mean_transmit_count) ); + } + break; + + case COLUMN_PGMRECEIVERNAKTRANSMITMAX: + { + const unsigned max_transmit_count = window->max_nak_transmit_count; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&max_transmit_count, sizeof(max_transmit_count) ); + } + break; + + case COLUMN_PGMRECEIVERACKSSENT: + { + const unsigned acks_sent = peer->cumulative_stats[PGM_PC_RECEIVER_ACKS_SENT]; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&acks_sent, sizeof(acks_sent) ); + } + break; + + case COLUMN_PGMRECEIVERRXWTRAIL: + { + const unsigned rxw_trail = window->rxw_trail; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&rxw_trail, sizeof(rxw_trail) ); + } + break; + + case COLUMN_PGMRECEIVERRXWLEAD: + { + const unsigned rxw_lead = window->lead; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&rxw_lead, sizeof(rxw_lead) ); + } + break; + +/* TODO: traps */ + case COLUMN_PGMRECEIVERNAKFAILURESLASTINTERVAL: + case COLUMN_PGMRECEIVERLASTINTERVALNAKFAILURES: + { + const unsigned failures = 0; + snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */ + (const u_char*)&failures, sizeof(failures) ); + } + break; + + default: + snmp_log (LOG_ERR, "pgmReceiverTable_handler: unknown column.\n"); + break; + } + } + break; + + case MODE_SET_RESERVE1: + default: + snmp_log (LOG_ERR, "pgmReceiverTable_handler: unsupported mode.\n"); + break; + + } + + return SNMP_ERR_NOERROR; +} + +/* + * SNMP TRAPS + */ + +int +send_pgmStart_trap (void) +{ + pgm_debug ("send_pgmStart_trap ()"); + + netsnmp_variable_list *var_list = NULL; + static const oid pgmStart_oid[] = { 1,3,6,1,3,112,2,0,1 }; + +/* + * Set the snmpTrapOid.0 value + */ + snmp_varlist_add_variable (&var_list, + snmptrap_oid, OID_LENGTH( snmptrap_oid ), + ASN_OBJECT_ID, + (const u_char*)pgmStart_oid, sizeof(pgmStart_oid)); +/* + * Add any extra (optional) objects here + */ + +/* + * Send the trap to the list of configured destinations + * and clean up + */ + send_v2trap (var_list); + snmp_free_varbind (var_list); + return SNMP_ERR_NOERROR; +} + +int +send_pgmStop_trap (void) +{ + pgm_debug ("send_pgmStop_trap ()"); + + netsnmp_variable_list *var_list = NULL; + static const oid pgmStop_oid[] = { 1,3,6,1,3,112,2,0,2 }; + +/* + * Set the snmpTrapOid.0 value + */ + snmp_varlist_add_variable (&var_list, + snmptrap_oid, OID_LENGTH(snmptrap_oid), + ASN_OBJECT_ID, + (const u_char*)pgmStop_oid, sizeof(pgmStop_oid)); + + +/* + * Add any extra (optional) objects here + */ + +/* + * Send the trap to the list of configured destinations + * and clean up + */ + send_v2trap (var_list); + snmp_free_varbind (var_list); + return SNMP_ERR_NOERROR; +} + +int +send_pgmNewSourceTrap_trap (void) +{ + pgm_debug ("send_pgmNewSourceTrap_trap ()"); + + netsnmp_variable_list *var_list = NULL; + static const oid pgmNewSourceTrap_oid[] = { 1,3,6,1,3,112,2,0,3 }; + static const oid pgmSourceSourceGsi_oid[] = { 1,3,6,1,3,112,1,2,100,2,1,6, /* insert index here */ }; + static const oid pgmSourceSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,2,100,2,1,7, /* insert index here */ }; + +/* + * Set the snmpTrapOid.0 value + */ + snmp_varlist_add_variable (&var_list, + snmptrap_oid, OID_LENGTH(snmptrap_oid), + ASN_OBJECT_ID, + (const u_char*)pgmNewSourceTrap_oid, sizeof(pgmNewSourceTrap_oid)); +/* + * Add any objects from the trap definition + */ + snmp_varlist_add_variable (&var_list, + pgmSourceSourceGsi_oid, OID_LENGTH(pgmSourceSourceGsi_oid), + ASN_OCTET_STR, +/* Set an appropriate value for pgmSourceSourceGsi */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmSourceSourcePortNumber_oid, OID_LENGTH(pgmSourceSourcePortNumber_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmSourceSourcePortNumber */ + NULL, 0); +/* + * Add any extra (optional) objects here + */ + +/* + * Send the trap to the list of configured destinations + * and clean up + */ + send_v2trap (var_list); + snmp_free_varbind (var_list); + return SNMP_ERR_NOERROR; +} + +int +send_pgmClosedSourceTrap_trap (void) +{ + pgm_debug ("send_pgmClosedSourceTrap_trap ()"); + + netsnmp_variable_list *var_list = NULL; + static const oid pgmClosedSourceTrap_oid[] = { 1,3,6,1,3,112,2,0,4 }; + static const oid pgmSourceSourceGsi_oid[] = { 1,3,6,1,3,112,1,2,100,2,1,6, /* insert index here */ }; + static const oid pgmSourceSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,2,100,2,1,7, /* insert index here */ }; + +/* + * Set the snmpTrapOid.0 value + */ + snmp_varlist_add_variable (&var_list, + snmptrap_oid, OID_LENGTH(snmptrap_oid), + ASN_OBJECT_ID, + (const u_char*)pgmClosedSourceTrap_oid, sizeof(pgmClosedSourceTrap_oid)); +/* + * Add any objects from the trap definition + */ + snmp_varlist_add_variable (&var_list, + pgmSourceSourceGsi_oid, OID_LENGTH(pgmSourceSourceGsi_oid), + ASN_OCTET_STR, +/* Set an appropriate value for pgmSourceSourceGsi */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmSourceSourcePortNumber_oid, OID_LENGTH(pgmSourceSourcePortNumber_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmSourceSourcePortNumber */ + NULL, 0); +/* + * Add any extra (optional) objects here + */ + +/* + * Send the trap to the list of configured destinations + * and clean up + */ + send_v2trap (var_list); + snmp_free_varbind (var_list); + return SNMP_ERR_NOERROR; +} + +int +send_pgmNewReceiverTrap_trap (void) +{ + pgm_debug ("send_pgmNewReceiverTrap_trap ()"); + + netsnmp_variable_list *var_list = NULL; + static const oid pgmNewReceiverTrap_oid[] = { 1,3,6,1,3,112,2,0,5 }; + static const oid pgmReceiverSourceGsi_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,8, /* insert index here */ }; + static const oid pgmReceiverSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,9, /* insert index here */ }; + static const oid pgmReceiverUniqueInstance_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,10, /* insert index here */ }; + +/* + * Set the snmpTrapOid.0 value + */ + snmp_varlist_add_variable (&var_list, + snmptrap_oid, OID_LENGTH(snmptrap_oid), + ASN_OBJECT_ID, + (const u_char*)pgmNewReceiverTrap_oid, sizeof(pgmNewReceiverTrap_oid)); +/* + * Add any objects from the trap definition + */ + snmp_varlist_add_variable (&var_list, + pgmReceiverSourceGsi_oid, OID_LENGTH(pgmReceiverSourceGsi_oid), + ASN_OCTET_STR, +/* Set an appropriate value for pgmReceiverSourceGsi */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmReceiverSourcePortNumber_oid, OID_LENGTH(pgmReceiverSourcePortNumber_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmReceiverSourcePortNumber */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmReceiverUniqueInstance_oid, OID_LENGTH(pgmReceiverUniqueInstance_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmReceiverUniqueInstance */ + NULL, 0); +/* + * Add any extra (optional) objects here + */ + +/* + * Send the trap to the list of configured destinations + * and clean up + */ + send_v2trap (var_list); + snmp_free_varbind (var_list); + return SNMP_ERR_NOERROR; +} + +int +send_pgmClosedReceiverTrap_trap (void) +{ + pgm_debug ("send_pgmClosedReceiverTrap_trap ()"); + + netsnmp_variable_list *var_list = NULL; + static const oid pgmClosedReceiverTrap_oid[] = { 1,3,6,1,3,112,2,0,6 }; + static const oid pgmReceiverSourceGsi_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,8, /* insert index here */ }; + static const oid pgmReceiverSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,9, /* insert index here */ }; + static const oid pgmReceiverUniqueInstance_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,10, /* insert index here */ }; + +/* + * Set the snmpTrapOid.0 value + */ + snmp_varlist_add_variable (&var_list, + snmptrap_oid, OID_LENGTH(snmptrap_oid), + ASN_OBJECT_ID, + (const u_char*)pgmClosedReceiverTrap_oid, sizeof(pgmClosedReceiverTrap_oid)); +/* + * Add any objects from the trap definition + */ + snmp_varlist_add_variable (&var_list, + pgmReceiverSourceGsi_oid, OID_LENGTH(pgmReceiverSourceGsi_oid), + ASN_OCTET_STR, +/* Set an appropriate value for pgmReceiverSourceGsi */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmReceiverSourcePortNumber_oid, OID_LENGTH(pgmReceiverSourcePortNumber_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmReceiverSourcePortNumber */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmReceiverUniqueInstance_oid, OID_LENGTH(pgmReceiverUniqueInstance_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmReceiverUniqueInstance */ + NULL, 0); +/* + * Add any extra (optional) objects here + */ + +/* + * Send the trap to the list of configured destinations + * and clean up + */ + send_v2trap (var_list); + snmp_free_varbind (var_list); + return SNMP_ERR_NOERROR; +} + +int +send_pgmNakFailuresTrap_trap (void) +{ + pgm_debug ("send_pgmNakFailuresTrap_trap ()"); + + netsnmp_variable_list *var_list = NULL; + static const oid pgmNakFailuresTrap_oid[] = { 1,3,6,1,3,112,2,0,7 }; + static const oid pgmReceiverSourceGsi_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,8, /* insert index here */ }; + static const oid pgmReceiverSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,9, /* insert index here */ }; + static const oid pgmReceiverUniqueInstance_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,10, /* insert index here */ }; + static const oid pgmReceiverNakFailureThresholdTimer_oid[] = { 1,3,6,1,3,112,1,3,100,3,1,14, /* insert index here */ }; + static const oid pgmReceiverNakFailureThreshold_oid[] = { 1,3,6,1,3,112,1,3,100,3,1,15, /* insert index here */ }; + static const oid pgmReceiverNakFailuresLastInterval_oid[] = { 1,3,6,1,3,112,1,3,100,4,1,56, /* insert index here */ }; + static const oid pgmReceiverLastIntervalNakFailures_oid[] = { 1,3,6,1,3,112,1,3,100,4,1,57, /* insert index here */ }; + +/* + * Set the snmpTrapOid.0 value + */ + snmp_varlist_add_variable (&var_list, + snmptrap_oid, OID_LENGTH(snmptrap_oid), + ASN_OBJECT_ID, + (const u_char*)pgmNakFailuresTrap_oid, sizeof(pgmNakFailuresTrap_oid)); +/* + * Add any objects from the trap definition + */ + snmp_varlist_add_variable (&var_list, + pgmReceiverSourceGsi_oid, OID_LENGTH(pgmReceiverSourceGsi_oid), + ASN_OCTET_STR, +/* Set an appropriate value for pgmReceiverSourceGsi */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmReceiverSourcePortNumber_oid, OID_LENGTH(pgmReceiverSourcePortNumber_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmReceiverSourcePortNumber */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmReceiverUniqueInstance_oid, OID_LENGTH(pgmReceiverUniqueInstance_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmReceiverUniqueInstance */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmReceiverNakFailureThresholdTimer_oid, OID_LENGTH(pgmReceiverNakFailureThresholdTimer_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmReceiverNakFailureThresholdTimer */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmReceiverNakFailureThreshold_oid, OID_LENGTH(pgmReceiverNakFailureThreshold_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmReceiverNakFailureThreshold */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmReceiverNakFailuresLastInterval_oid, OID_LENGTH(pgmReceiverNakFailuresLastInterval_oid), + ASN_COUNTER, +/* Set an appropriate value for pgmReceiverNakFailuresLastInterval */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmReceiverLastIntervalNakFailures_oid, OID_LENGTH(pgmReceiverLastIntervalNakFailures_oid), + ASN_COUNTER, +/* Set an appropriate value for pgmReceiverLastIntervalNakFailures */ + NULL, 0); +/* + * Add any extra (optional) objects here + */ + +/* + * Send the trap to the list of configured destinations + * and clean up + */ + send_v2trap (var_list); + snmp_free_varbind (var_list); + return SNMP_ERR_NOERROR; +} + +int +send_pgmNewDlrSourceTrap_trap (void) +{ + pgm_debug ("send_pgmNewDlrSourceTrap_trap ()"); + + netsnmp_variable_list *var_list = NULL; + static const oid pgmNewDlrSourceTrap_oid[] = { 1,3,6,1,3,112,2,0,8 }; + static const oid pgmDlrSourceSourceGsi_oid[] = { 1,3,6,1,3,112,1,4,100,2,1,4, /* insert index here */ }; + static const oid pgmDlrSourceSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,4,100,2,1,5, /* insert index here */ }; + +/* + * Set the snmpTrapOid.0 value + */ + snmp_varlist_add_variable (&var_list, + snmptrap_oid, OID_LENGTH(snmptrap_oid), + ASN_OBJECT_ID, + (const u_char*)pgmNewDlrSourceTrap_oid, sizeof(pgmNewDlrSourceTrap_oid)); +/* + * Add any objects from the trap definition + */ + snmp_varlist_add_variable (&var_list, + pgmDlrSourceSourceGsi_oid, OID_LENGTH(pgmDlrSourceSourceGsi_oid), + ASN_OCTET_STR, +/* Set an appropriate value for pgmDlrSourceSourceGsi */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmDlrSourceSourcePortNumber_oid, OID_LENGTH(pgmDlrSourceSourcePortNumber_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmDlrSourceSourcePortNumber */ + NULL, 0); +/* + * Add any extra (optional) objects here + */ + +/* + * Send the trap to the list of configured destinations + * and clean up + */ + send_v2trap (var_list); + snmp_free_varbind (var_list); + return SNMP_ERR_NOERROR; +} + +int +send_pgmClosedDlrSourceTrap_trap (void) +{ + pgm_debug ("send_pgmClosedDlrSourceTrap_trap ()"); + + netsnmp_variable_list *var_list = NULL; + static const oid pgmClosedDlrSourceTrap_oid[] = { 1,3,6,1,3,112,2,0,9 }; + static const oid pgmDlrSourceSourceGsi_oid[] = { 1,3,6,1,3,112,1,4,100,2,1,4, /* insert index here */ }; + static const oid pgmDlrSourceSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,4,100,2,1,5, /* insert index here */ }; + +/* + * Set the snmpTrapOid.0 value + */ + snmp_varlist_add_variable (&var_list, + snmptrap_oid, OID_LENGTH(snmptrap_oid), + ASN_OBJECT_ID, + (const u_char*)pgmClosedDlrSourceTrap_oid, sizeof(pgmClosedDlrSourceTrap_oid)); + +/* + * Add any objects from the trap definition + */ + snmp_varlist_add_variable (&var_list, + pgmDlrSourceSourceGsi_oid, OID_LENGTH(pgmDlrSourceSourceGsi_oid), + ASN_OCTET_STR, +/* Set an appropriate value for pgmDlrSourceSourceGsi */ + NULL, 0); + snmp_varlist_add_variable (&var_list, + pgmDlrSourceSourcePortNumber_oid, OID_LENGTH(pgmDlrSourceSourcePortNumber_oid), + ASN_UNSIGNED, +/* Set an appropriate value for pgmDlrSourceSourcePortNumber */ + NULL, 0); +/* + * Add any extra (optional) objects here + */ + +/* + * Send the trap to the list of configured destinations + * and clean up + */ + send_v2trap (var_list); + snmp_free_varbind (var_list); + return SNMP_ERR_NOERROR; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/pgmMIB_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/pgmMIB_unittest.c new file mode 100644 index 0000000..4cb4672 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/pgmMIB_unittest.c @@ -0,0 +1,257 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for PGM MIB routines. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include + + +/* mock state */ + +static GStaticRWLock mock_pgm_transport_list_lock = G_STATIC_RW_LOCK_INIT; +static GSList* mock_pgm_transport_list = NULL; + + +/* mock functions for external references */ + +static +netsnmp_handler_registration* +mock_netsnmp_create_handler_registration ( + const char* name, + Netsnmp_Node_Handler* handler_access_method, + oid* reg_oid, + size_t reg_oid_len, + int modes + ) +{ + netsnmp_handler_registration* handler = g_malloc0 (sizeof(netsnmp_handler_registration)); + return handler; +} + +static +void +mock_netsnmp_handler_registration_free ( + netsnmp_handler_registration* handler + ) +{ + g_assert (NULL != handler); + g_free (handler); +} + +static +void +mock_netsnmp_table_helper_add_indexes ( + netsnmp_table_registration_info* tinfo, + ... + ) +{ +} + +static +int +mock_netsnmp_register_table_iterator ( + netsnmp_handler_registration* reginfo, + netsnmp_iterator_info* iinfo + ) +{ + return MIB_REGISTERED_OK; +} + +static +int +mock_netsnmp_set_request_error ( + netsnmp_agent_request_info* reqinfo, + netsnmp_request_info* request, + int error_value + ) +{ + return 0; +} + +static +void* +mock_netsnmp_extract_iterator_context ( + netsnmp_request_info* reqinfo + ) +{ + return (void*)0x1; +} + +static +netsnmp_table_request_info* +mock_netsnmp_extract_table_info ( + netsnmp_request_info* reqinfo + ) +{ + return NULL; +} + +static +int +mock_snmp_set_var_typed_value ( + netsnmp_variable_list* newvar, + u_char type, + const u_char* val_str, + size_t val_len + ) +{ + return 0; +} + +static +netsnmp_variable_list* +mock_snmp_varlist_add_variable ( + netsnmp_variable_list** varlist, + const oid* oid, + size_t name_length, + u_char type, + const u_char* value, + size_t len + ) +{ + return NULL; +} + +static +void +mock_snmp_free_varbind ( + netsnmp_variable_list* var + ) +{ +} + +static +void +mock_snmp_free_var ( + netsnmp_variable_list* var + ) +{ +} + +static +int +mock_snmp_log ( + int priority, + const char* format, + ... + ) +{ + return 0; +} + +static +void +mock_send_v2trap ( + netsnmp_variable_list* var + ) +{ +} + +/** time module */ + +static +void +mock_pgm_time_since_epoch ( + pgm_time_t* pgm_time_t_time, + time_t* time_t_time + ) +{ + *time_t_time = pgm_to_secs (*pgm_time_t_time + 0); +} + + +#define netsnmp_create_handler_registration mock_netsnmp_create_handler_registration +#define netsnmp_handler_registration_free mock_netsnmp_handler_registration_free +#define netsnmp_table_helper_add_indexes mock_netsnmp_table_helper_add_indexes +#define netsnmp_register_table_iterator mock_netsnmp_register_table_iterator +#define netsnmp_set_request_error mock_netsnmp_set_request_error +#define netsnmp_extract_iterator_context mock_netsnmp_extract_iterator_context +#define netsnmp_extract_table_info mock_netsnmp_extract_table_info +#define snmp_set_var_typed_value mock_snmp_set_var_typed_value +#define snmp_varlist_add_variable mock_snmp_varlist_add_variable +#define snmp_free_varbind mock_snmp_free_varbind +#define snmp_free_var mock_snmp_free_var +#define snmp_log mock_snmp_log +#define send_v2trap mock_send_v2trap +#define pgm_transport_list mock_pgm_transport_list +#define pgm_transport_list_lock mock_pgm_transport_list_lock +#define pgm_time_since_epoch mock_pgm_time_since_epoch + +#define PGMMIB_DEBUG +#include "pgmMIB.c" + + +/* target: + * gboolean + * pgm_mib_init ( + * GError** error + * ) + */ + +START_TEST (test_init_pass_001) +{ + GError* err = NULL; + fail_unless (TRUE == pgm_mib_init (&err)); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_init = tcase_create ("init"); + suite_add_tcase (s, tc_init); + tcase_add_test (tc_init, test_init_pass_001); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/plan.txt b/3rdparty/openpgm-svn-r1135/pgm/plan.txt new file mode 100644 index 0000000..b1747ae --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/plan.txt @@ -0,0 +1,238 @@ +pgmdump +------- + +View all packets like tcpdump, but updated to full spec and allow dump of payload. + + +pgmtop +------ + +Dump realtime packet statistics in a ncurses display, mix of top/htop/netop. + + +basic_send +---------- + +Send an ODATA packet and terminate. + +Accept string payload and network parameters on command line. + +Send to multicast or send to unicast AFI. + +IPv4/6. + +Define optional session start, late join tags. + + +spm_idle +-------- + +Idle in an event loop sending out SPM packets. + + +stream_send +----------- + +Send a constant stream of ODATA and SPM packets. + + +basic_http +---------- + +Simple embedded web server + + +basic_recv +---------- + +Listen to packets indicating data loss, view details through web interface. + + +basic_container +--------------- + +Test performance of glib containers for fast allocating for a dynamic transmit window. + + +basic_txw +--------- + +Test performance of a basic transmit window implementation. + + +nak_txw +------- + +Test performance of random access to packets inside the window. + + +stream_send_with_nak +-------------------- + +Respond to NAK's with RDATA. + + +basic_recv_with_nak +------------------- + +Listen to packets and send NAK's to rebuild data. + + +dumpif +------ + +Display all IP based interfaces and basic details. + + +testif +------ + +Test various combinations of network specification. + + +sw_calc +------- + +Basic calculation tests of wrap-around sliding windows and a leading edge. + + +basic_recv_with_rxw +------------------- + +Listen to packets buffered with a receive window. + + +test_cpu_timers +--------------- + +Calculate drift between processors, cores, and hyper-threads. + + +pgmsend +-------- + +basic_send updated to use transmit window. + + +pgmrecv +-------- + +basic_recv_with_rxw without web interface, primary displays messages from pgmsend. + + +syncrecv +-------- + +pgmrecv implemented outside GLib with a synchronous coding paradigm. + + +pgmping +------- + +Dual mode: one to send fixed messages like pgmsend and listen for response, two to listen for +messages and reply. + + +block_send +---------- + +Send APDUs over ODATA. + + +(pgmrecv can receive APDUs) + +test_rs +------- + +Test 8-bit symbol Reed Solomon encoding and decoding with errors & erasures. + +test_fec +-------- + +Test fec creation and recovery. + + +send_with_fec +-------------------- + +Send APDUs over ODATA with FEC. + + + +Scenarios to reproduce +********************** + +- Packet loss in stream causing NAK generation. +- Link saturation in sending causing API feedback. +- Link peak stable speed. +- Maxium NAK generation to determine NCF/RDATA throughput. +- Corrupt packets with invalid IP checksum (? generate IP HDR in sender) +- Corrupt packets with invalid PGM checksum. +- Invalid packet values. +- NAK to NCF latency. +- NAK to RDATA latency. +- Publish bandwidth: total, per packet type, payload, per recipient (?) +- Subscribe bandwidth: total, per packet type, payload, per publisher (?) +- Restarting a session with similar or dissimilar sequence numbering. + +Outstanding questions +********************* + +- Is it faster to use chunks containing multiple packets instead of one MTU + per packet. Does aligning with system page size matter? +- Can g_timers be dropped easily for gettimeofday and avoid floating point math? Possible + to pass timer upstream with contiguous data for easy access. +- Can time evaluation be dropped to at most once per main loop event? +- Does code work 32 bit and is it optimal? +- Should trash stacks be monitored and controlled externally? For example, clearing + up after bursts or administrative control. +- Should trash stacks have a upper limit to then free to the slice allocator? +- Should lost packets be managed as ranges or individual sequence numbers, how + does each method affect performance? + +* The initial draft of PGM included OPT_RANGE option for NAKs to specify a range of lost + packets, this was replaced in the final draft with NAK lists. Some research hints that + ranges are suitable: + + http://www.isoc.org/inet2001/CD_proceedings/T54/T54.htm + http://tools.ietf.org/html/draft-speakman-pgm-spec-01 + +- Are place holders necessary? Can state timeouts be managed without a per sequence number + object? For example by the next data object, or an extra object for an ncf extended window: + note that nak packet generation should easily dwarfs time spent unless advantage is taken + of the additional 62 naks in a opt_nak_list. Caution has to be taken with the cost of + splitting a range when a packet is inserted in the middle, although idealy it should + be sequential from the trailing edge. +- Is it better to have send sockets per transport, or shared, bound to each interface? +- Cost of sharing state helper lists between receive windows? On culling a peer the lists + have to be purged. Saves iterating the hash list of receivers. +- Encapsulated UDP lists two ports, 3305 for broadcast, 3306 for unicast, how is this + supposed to map to regular PGM, and why the split? + +Basic TODO list +*************** + +- Ensure standardised error handling and status reporting. +- Implement mechanism for data-loss feedback. +- OPT_SYN & OPT_FIN on sending side. +- Shared trash stacks between multiple receive windows (contexts). +- Shared trash stacks between transmit and receive windows. +- FEC: compatibility with SmartPGM FEC, MS FEC? +- NAK backoff learning. +- Full conformance testing. (nak backoffs remaining) +- Unit testing. +- System testing with valgrind. +- Performance testing with oprofile. +- Basic DLR. +- Implement PGM sender (only) support thread? +- eventfd instead of pipes for recent Linux kernels. + +Optionals +********* + +- Some form of broadcast statistics for passive monitoring. +- (fix) BCH Reed-Solomon codec as possibly faster alternative, albeit incompatible with Microsoft. +- Recommendations as per the RMT working group of the IETF for AL-FEC codecs: Raptor codes, LDPC- + staircase and LDPC-triangle codes. +- XDR based messaging format as example of binary encoded messaging. + diff --git a/3rdparty/openpgm-svn-r1135/pgm/queue.c b/3rdparty/openpgm-svn-r1135/pgm/queue.c new file mode 100644 index 0000000..351c7ef --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/queue.c @@ -0,0 +1,110 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable double-ended queue. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + + +//#define QUEUE_DEBUG + +bool +pgm_queue_is_empty ( + const pgm_queue_t*const queue + ) +{ + pgm_return_val_if_fail (queue != NULL, TRUE); + + return queue->head == NULL; +} + +void +pgm_queue_push_head_link ( + pgm_queue_t* restrict queue, + pgm_list_t* restrict head_link + ) +{ + pgm_return_if_fail (queue != NULL); + pgm_return_if_fail (head_link != NULL); + pgm_return_if_fail (head_link->prev == NULL); + pgm_return_if_fail (head_link->next == NULL); + + head_link->next = queue->head; + if (queue->head) + queue->head->prev = head_link; + else + queue->tail = head_link; + queue->head = head_link; + queue->length++; +} + +pgm_list_t* +pgm_queue_pop_tail_link ( + pgm_queue_t* queue + ) +{ + pgm_return_val_if_fail (queue != NULL, NULL); + + if (queue->tail) + { + pgm_list_t *node = queue->tail; + + queue->tail = node->prev; + if (queue->tail) + { + queue->tail->next = NULL; + node->prev = NULL; + } + else + queue->head = NULL; + queue->length--; + + return node; + } + + return NULL; +} + +pgm_list_t* +pgm_queue_peek_tail_link ( + pgm_queue_t* queue + ) +{ + pgm_return_val_if_fail (queue != NULL, NULL); + + return queue->tail; +} + +void +pgm_queue_unlink ( + pgm_queue_t* restrict queue, + pgm_list_t* restrict target_link + ) +{ + pgm_return_if_fail (queue != NULL); + pgm_return_if_fail (target_link != NULL); + + if (target_link == queue->tail) + queue->tail = queue->tail->prev; + + queue->head = pgm_list_remove_link (queue->head, target_link); + queue->length--; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/rand.c b/3rdparty/openpgm-svn-r1135/pgm/rand.c new file mode 100644 index 0000000..91b71eb --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/rand.c @@ -0,0 +1,137 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable weak pseudo-random generator. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _WIN32 +# include +# include +#endif +#include + + +//#define RAND_DEBUG + + +/* locals */ + +static pgm_rand_t global_rand = { .seed = 0 }; +static volatile uint32_t rand_ref_count = 0; +static pgm_mutex_t rand_mutex; + + +void +pgm_rand_init (void) +{ + if (pgm_atomic_exchange_and_add32 (&rand_ref_count, 1) > 0) + return; + + pgm_mutex_init (&rand_mutex); +} + +void +pgm_rand_shutdown (void) +{ + pgm_return_if_fail (pgm_atomic_read32 (&rand_ref_count) > 0); + + if (pgm_atomic_exchange_and_add32 (&rand_ref_count, (uint32_t)-1) != 1) + return; + + pgm_mutex_free (&rand_mutex); +} + +void +pgm_rand_create ( + pgm_rand_t* new_rand + ) +{ +/* pre-conditions */ + pgm_assert (NULL != new_rand); + +#ifndef _WIN32 +/* attempt to read seed from kernel + */ + FILE* fp; + do { + fp = fopen ("/dev/urandom", "rb"); + } while (PGM_UNLIKELY(EINTR == errno)); + if (fp) { + size_t items_read; + do { + items_read = fread (&new_rand->seed, sizeof(new_rand->seed), 1, fp); + } while (PGM_UNLIKELY(EINTR == errno)); + fclose (fp); + if (1 == items_read) + return; + } +#endif /* !_WIN32 */ + const pgm_time_t now = pgm_time_update_now(); + new_rand->seed = (uint32_t)pgm_to_msecs (now); +} + +/* derived from POSIX.1-2001 example implementation of rand() + */ + +uint32_t +pgm_rand_int ( + pgm_rand_t* r + ) +{ +/* pre-conditions */ + pgm_assert (NULL != r); + + r->seed = r->seed * 1103515245 + 12345; + return r->seed; +} + +int32_t +pgm_rand_int_range ( + pgm_rand_t* r, + int32_t begin, + int32_t end + ) +{ +/* pre-conditions */ + pgm_assert (NULL != r); + + return begin + pgm_rand_int (r) % (end - begin); +} + +uint32_t +pgm_random_int (void) +{ + pgm_mutex_lock (&rand_mutex); + if (PGM_UNLIKELY(!global_rand.seed)) + pgm_rand_create (&global_rand); + const uint32_t rand_value = pgm_rand_int (&global_rand); + pgm_mutex_unlock (&rand_mutex); + return rand_value; +} + +int32_t +pgm_random_int_range ( + int32_t begin, + int32_t end + ) +{ + const uint32_t rand_value = pgm_random_int(); + return begin + rand_value % (end - begin); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/rand.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/rand.c.c89.patch new file mode 100644 index 0000000..074a3ef --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/rand.c.c89.patch @@ -0,0 +1,34 @@ +--- rand.c 2010-05-21 11:35:31.000000000 +0800 ++++ rand.c89 2010-08-04 15:44:47.000000000 +0800 +@@ -31,7 +31,7 @@ + + /* locals */ + +-static pgm_rand_t global_rand = { .seed = 0 }; ++static pgm_rand_t global_rand = { 0 }; + static volatile uint32_t rand_ref_count = 0; + static pgm_mutex_t rand_mutex; + +@@ -81,8 +81,10 @@ + return; + } + #endif /* !_WIN32 */ ++ { + const pgm_time_t now = pgm_time_update_now(); + new_rand->seed = (uint32_t)pgm_to_msecs (now); ++ } + } + + /* derived from POSIX.1-2001 example implementation of rand() +@@ -119,9 +121,11 @@ + pgm_mutex_lock (&rand_mutex); + if (PGM_UNLIKELY(!global_rand.seed)) + pgm_rand_create (&global_rand); ++ { + const uint32_t rand_value = pgm_rand_int (&global_rand); + pgm_mutex_unlock (&rand_mutex); + return rand_value; ++ } + } + + int32_t diff --git a/3rdparty/openpgm-svn-r1135/pgm/rate_control.c b/3rdparty/openpgm-svn-r1135/pgm/rate_control.c new file mode 100644 index 0000000..11f9e9b --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/rate_control.c @@ -0,0 +1,158 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Rate regulation. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + + +/* create machinery for rate regulation. + * the rate_per_sec is ammortized over millisecond time periods. + * + * NB: bucket MUST be memset 0 before calling. + */ + +void +pgm_rate_create ( + pgm_rate_t* bucket, + const ssize_t rate_per_sec, /* 0 = disable */ + const size_t iphdr_len, + const uint16_t max_tpdu + ) +{ +/* pre-conditions */ + pgm_assert (NULL != bucket); + pgm_assert (rate_per_sec >= max_tpdu); + + bucket->rate_per_sec = rate_per_sec; + bucket->iphdr_len = iphdr_len; + bucket->last_rate_check = pgm_time_update_now (); +/* pre-fill bucket */ + if ((rate_per_sec / 1000) >= max_tpdu) { + bucket->rate_per_msec = bucket->rate_per_sec / 1000; + bucket->rate_limit = bucket->rate_per_msec; + } else { + bucket->rate_limit = bucket->rate_per_sec; + } + pgm_spinlock_init (&bucket->spinlock); +} + +void +pgm_rate_destroy ( + pgm_rate_t* bucket + ) +{ +/* pre-conditions */ + pgm_assert (NULL != bucket); + + pgm_spinlock_free (&bucket->spinlock); +} + +/* check bit bucket whether an operation can proceed or should wait. + * + * returns TRUE when leaky bucket permits unless non-blocking flag is set. + * returns FALSE if operation should block and non-blocking flag is set. + */ + +bool +pgm_rate_check ( + pgm_rate_t* bucket, + const size_t data_size, + const bool is_nonblocking + ) +{ + int64_t new_rate_limit; + +/* pre-conditions */ + pgm_assert (NULL != bucket); + pgm_assert (data_size > 0); + + if (0 == bucket->rate_per_sec) + return TRUE; + + pgm_spinlock_lock (&bucket->spinlock); + pgm_time_t now = pgm_time_update_now(); + pgm_time_t time_since_last_rate_check = now - bucket->last_rate_check; + + if (bucket->rate_per_msec) + { + if (time_since_last_rate_check > pgm_msecs(1)) + new_rate_limit = bucket->rate_per_msec; + else { + new_rate_limit = bucket->rate_limit + ((bucket->rate_per_msec * time_since_last_rate_check) / 1000UL); + if (new_rate_limit > bucket->rate_per_msec) + new_rate_limit = bucket->rate_per_msec; + } + } + else + { + if (time_since_last_rate_check > pgm_secs(1)) + new_rate_limit = bucket->rate_per_sec; + else { + new_rate_limit = bucket->rate_limit + ((bucket->rate_per_sec * time_since_last_rate_check) / 1000000UL); + if (new_rate_limit > bucket->rate_per_sec) + new_rate_limit = bucket->rate_per_sec; + } + } + + new_rate_limit -= ( bucket->iphdr_len + data_size ); + if (is_nonblocking && new_rate_limit < 0) { + pgm_spinlock_unlock (&bucket->spinlock); + return FALSE; + } + + bucket->rate_limit = new_rate_limit; + bucket->last_rate_check = now; + if (bucket->rate_limit < 0) { + ssize_t sleep_amount; + do { + pgm_thread_yield(); + now = pgm_time_update_now(); + time_since_last_rate_check = now - bucket->last_rate_check; + sleep_amount = (ssize_t)pgm_to_secs (bucket->rate_per_sec * time_since_last_rate_check); + } while (sleep_amount + bucket->rate_limit < 0); + bucket->rate_limit += sleep_amount; + bucket->last_rate_check = now; + } + pgm_spinlock_unlock (&bucket->spinlock); + return TRUE; +} + +pgm_time_t +pgm_rate_remaining ( + pgm_rate_t* bucket, + const size_t n + ) +{ +/* pre-conditions */ + pgm_assert (NULL != bucket); + + if (PGM_UNLIKELY(0 == bucket->rate_per_sec)) + return 0; + + pgm_spinlock_lock (&bucket->spinlock); + const pgm_time_t now = pgm_time_update_now(); + const pgm_time_t time_since_last_rate_check = now - bucket->last_rate_check; + const int64_t bucket_bytes = bucket->rate_limit + pgm_to_secs (bucket->rate_per_sec * time_since_last_rate_check) - n; + pgm_spinlock_unlock (&bucket->spinlock); + + return bucket_bytes >= 0 ? 0 : (bucket->rate_per_sec / -bucket_bytes); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/rate_control.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/rate_control.c.c89.patch new file mode 100644 index 0000000..d779a78 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/rate_control.c.c89.patch @@ -0,0 +1,62 @@ +--- rate_control.c 2010-08-04 15:58:08.000000000 +0800 ++++ rate_control.c89 2010-08-04 15:57:59.000000000 +0800 +@@ -77,7 +77,7 @@ + const bool is_nonblocking + ) + { +- int64_t new_rate_limit; ++ ssize_t new_rate_limit; + + /* pre-conditions */ + pgm_assert (NULL != bucket); +@@ -87,6 +87,7 @@ + return TRUE; + + pgm_spinlock_lock (&bucket->spinlock); ++ { + pgm_time_t now = pgm_time_update_now(); + pgm_time_t time_since_last_rate_check = now - bucket->last_rate_check; + +@@ -95,7 +96,9 @@ + if (time_since_last_rate_check > pgm_msecs(1)) + new_rate_limit = bucket->rate_per_msec; + else { ++#pragma warning( disable : 4244 ) + new_rate_limit = bucket->rate_limit + ((bucket->rate_per_msec * time_since_last_rate_check) / 1000UL); ++#pragma warning( default : 4244 ) + if (new_rate_limit > bucket->rate_per_msec) + new_rate_limit = bucket->rate_per_msec; + } +@@ -105,7 +108,9 @@ + if (time_since_last_rate_check > pgm_secs(1)) + new_rate_limit = bucket->rate_per_sec; + else { ++#pragma warning( disable : 4244 ) + new_rate_limit = bucket->rate_limit + ((bucket->rate_per_sec * time_since_last_rate_check) / 1000000UL); ++#pragma warning( default : 4244 ) + if (new_rate_limit > bucket->rate_per_sec) + new_rate_limit = bucket->rate_per_sec; + } +@@ -132,6 +137,7 @@ + } + pgm_spinlock_unlock (&bucket->spinlock); + return TRUE; ++ } + } + + pgm_time_t +@@ -147,12 +153,14 @@ + return 0; + + pgm_spinlock_lock (&bucket->spinlock); ++ { + const pgm_time_t now = pgm_time_update_now(); + const pgm_time_t time_since_last_rate_check = now - bucket->last_rate_check; + const int64_t bucket_bytes = bucket->rate_limit + pgm_to_secs (bucket->rate_per_sec * time_since_last_rate_check) - n; + pgm_spinlock_unlock (&bucket->spinlock); + + return bucket_bytes >= 0 ? 0 : (bucket->rate_per_sec / -bucket_bytes); ++ } + } + + /* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/rate_control_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/rate_control_unittest.c new file mode 100644 index 0000000..7da5128 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/rate_control_unittest.c @@ -0,0 +1,241 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for rate regulation. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include +#include +#include + + +/* mock state */ + + +#define pgm_time_now mock_pgm_time_now +#define pgm_time_update_now mock_pgm_time_update_now + +#define RATE_CONTROL_DEBUG +#include "rate_control.c" + +static pgm_time_t mock_pgm_time_now = 0x1; +static pgm_time_t _mock_pgm_time_update_now (void); +pgm_time_update_func mock_pgm_time_update_now = _mock_pgm_time_update_now; + + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + +static +pgm_time_t +_mock_pgm_time_update_now (void) +{ + g_debug ("mock_pgm_time_now: %" PGM_TIME_FORMAT, mock_pgm_time_now); + return mock_pgm_time_now; +} + + +/* target: + * void + * pgm_rate_create ( + * pgm_rate_t* bucket_, + * const ssize_t rate_per_sec, + * const size_t iphdr_len, + * const uint16_t max_tpdu + * ) + */ + +START_TEST (test_create_pass_001) +{ + pgm_rate_t rate; + memset (&rate, 0, sizeof(rate)); + pgm_rate_create (&rate, 100*1000, 10, 1500); +} +END_TEST + +START_TEST (test_create_fail_001) +{ + pgm_rate_create (NULL, 0, 0, 1500); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_rate_destroy ( + * pgm_rate_t* bucket + * ) + */ + +START_TEST (test_destroy_pass_001) +{ + pgm_rate_t rate; + memset (&rate, 0, sizeof(rate)); + pgm_rate_create (&rate, 100*1000, 10, 1500); + pgm_rate_destroy (&rate); +} +END_TEST + +START_TEST (test_destroy_fail_001) +{ + pgm_rate_destroy (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_rate_check ( + * pgm_rate_t* bucket, + * const size_t data_size, + * const bool is_nonblocking + * ) + * + * 001: should use seconds resolution to allow 2 packets through then fault. + */ + +START_TEST (test_check_pass_001) +{ + pgm_rate_t rate; + memset (&rate, 0, sizeof(rate)); + pgm_rate_create (&rate, 2*1010, 10, 1500); + mock_pgm_time_now += pgm_secs(2); + fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + pgm_rate_destroy (&rate); +} +END_TEST + +START_TEST (test_check_fail_001) +{ + pgm_rate_check (NULL, 1000, FALSE); + fail ("reached"); +} +END_TEST + +/* 002: assert that only one packet should pass through small bucket + */ + +START_TEST (test_check_pass_002) +{ + pgm_rate_t rate; + memset (&rate, 0, sizeof(rate)); + pgm_rate_create (&rate, 2*900, 10, 1500); + mock_pgm_time_now += pgm_secs(2); + fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + pgm_rate_destroy (&rate); +} +END_TEST + +/* 003: millisecond resolution should initiate millisecond fills. + */ + +START_TEST (test_check_pass_003) +{ + pgm_rate_t rate; + memset (&rate, 0, sizeof(rate)); + pgm_rate_create (&rate, 2*1010*1000, 10, 1500); + mock_pgm_time_now += pgm_secs(2); + fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); +/* duplicate check at same time point */ + fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); +/* advance time causing a millisecond fill to occur */ + mock_pgm_time_now += pgm_msecs(1); + fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); +/* advance time to fill bucket enough for only one packet */ + mock_pgm_time_now += pgm_usecs(500); + fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); +/* advance time to fill the bucket a little but not enough for one packet */ + mock_pgm_time_now += pgm_usecs(100); + fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); +/* advance time a lot, should be limited to millisecond fill rate */ + mock_pgm_time_now += pgm_secs(10); + fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + fail_unless (TRUE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + fail_unless (FALSE == pgm_rate_check (&rate, 1000, TRUE), "rate_check failed"); + pgm_rate_destroy (&rate); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_create = tcase_create ("create"); + suite_add_tcase (s, tc_create); + tcase_add_test (tc_create, test_create_pass_001); + tcase_add_test_raise_signal (tc_create, test_create_fail_001, SIGABRT); + + TCase* tc_destroy = tcase_create ("destroy"); + suite_add_tcase (s, tc_destroy); + tcase_add_test (tc_destroy, test_destroy_pass_001); + tcase_add_test_raise_signal (tc_destroy, test_destroy_fail_001, SIGABRT); + + TCase* tc_check = tcase_create ("check"); + suite_add_tcase (s, tc_check); + tcase_add_test (tc_check, test_check_pass_001); + tcase_add_test (tc_check, test_check_pass_002); + tcase_add_test (tc_check, test_check_pass_003); + tcase_add_test_raise_signal (tc_check, test_check_fail_001, SIGABRT); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/receiver.c b/3rdparty/openpgm-svn-r1135/pgm/receiver.c new file mode 100644 index 0000000..ea2acea --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/receiver.c @@ -0,0 +1,2292 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM receiver socket. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __STDC_FORMAT_MACROS +#ifdef _MSC_VER +# include +#else +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + + +//#define RECEIVER_DEBUG +//#define SPM_DEBUG + +#ifndef RECEIVER_DEBUG +# define PGM_DISABLE_ASSERT +#endif + +#if !defined(ENOBUFS) && defined(WSAENOBUFS) +# define ENOBUFS WSAENOBUFS +#endif +#if !defined(ECONNRESET) && defined(WSAECONNRESET) +# define ECONNRESET WSAECONNRESET +#endif + +static bool send_spmr (pgm_sock_t*const restrict, pgm_peer_t*const restrict); +static bool send_nak (pgm_sock_t*const restrict, pgm_peer_t*const restrict, const uint32_t); +static bool send_parity_nak (pgm_sock_t*const restrict, pgm_peer_t*const restrict, const unsigned, const unsigned); +static bool send_nak_list (pgm_sock_t*const restrict, pgm_peer_t*const restrict, const struct pgm_sqn_list_t*const restrict); +static bool nak_rb_state (pgm_peer_t*, const pgm_time_t); +static void nak_rpt_state (pgm_peer_t*, const pgm_time_t); +static void nak_rdata_state (pgm_peer_t*, const pgm_time_t); +static inline pgm_peer_t* _pgm_peer_ref (pgm_peer_t*); +static bool on_general_poll (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict); +static bool on_dlr_poll (pgm_sock_t*const restrict, pgm_peer_t*const restrict, struct pgm_sk_buff_t*const restrict); + + +/* helpers for pgm_peer_t */ +static inline +pgm_time_t +next_ack_rb_expiry ( + const pgm_rxw_t* window + ) +{ + pgm_assert (NULL != window); + pgm_assert (NULL != window->ack_backoff_queue.tail); + + const struct pgm_peer_t* peer = (const struct pgm_peer_t*)window->ack_backoff_queue.tail->data; + pgm_assert (NULL != peer); + + pgm_assert (peer->sock->use_pgmcc); + return peer->ack_rb_expiry; +} + +static inline +pgm_time_t +next_nak_rb_expiry ( + const pgm_rxw_t* window + ) +{ + pgm_assert (NULL != window); + pgm_assert (NULL != window->nak_backoff_queue.tail); + + const struct pgm_sk_buff_t* skb = (const struct pgm_sk_buff_t*)window->nak_backoff_queue.tail; + const pgm_rxw_state_t* state = (const pgm_rxw_state_t*)&skb->cb; + return state->timer_expiry; +} + +static inline +pgm_time_t +next_nak_rpt_expiry ( + const pgm_rxw_t* window + ) +{ + pgm_assert (NULL != window); + pgm_assert (NULL != window->wait_ncf_queue.tail); + + const struct pgm_sk_buff_t* skb = (const struct pgm_sk_buff_t*)window->wait_ncf_queue.tail; + const pgm_rxw_state_t* state = (const pgm_rxw_state_t*)&skb->cb; + return state->timer_expiry; +} + +static inline +pgm_time_t +next_nak_rdata_expiry ( + const pgm_rxw_t* window + ) +{ + pgm_assert (NULL != window); + pgm_assert (NULL != window->wait_data_queue.tail); + + const struct pgm_sk_buff_t* skb = (const struct pgm_sk_buff_t*)window->wait_data_queue.tail; + const pgm_rxw_state_t* state = (const pgm_rxw_state_t*)&skb->cb; + return state->timer_expiry; +} + +/* calculate ACK_RB_IVL. + */ +static inline +uint32_t +ack_rb_ivl ( + pgm_sock_t* sock + ) /* not const as rand() updates the seed */ +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (sock->use_pgmcc); + pgm_assert_cmpuint (sock->ack_bo_ivl, >, 1); + + return pgm_rand_int_range (&sock->rand_, 1 /* us */, sock->ack_bo_ivl); +} + +/* calculate NAK_RB_IVL as random time interval 1 - NAK_BO_IVL. + */ +static inline +uint32_t +nak_rb_ivl ( + pgm_sock_t* sock + ) /* not const as rand() updates the seed */ +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert_cmpuint (sock->nak_bo_ivl, >, 1); + + return pgm_rand_int_range (&sock->rand_, 1 /* us */, sock->nak_bo_ivl); +} + +/* mark sequence as recovery failed. + */ + +static +void +cancel_skb ( + pgm_sock_t* restrict sock, + pgm_peer_t* restrict peer, + const struct pgm_sk_buff_t* restrict skb, + const pgm_time_t now + ) +{ + pgm_assert (NULL != sock); + pgm_assert (NULL != peer); + pgm_assert (NULL != skb); + pgm_assert_cmpuint (now, >=, skb->tstamp); + + pgm_trace (PGM_LOG_ROLE_RX_WINDOW, _("Lost data #%u due to cancellation."), skb->sequence); + + const uint32_t fail_time = now - skb->tstamp; + if (!peer->max_fail_time) + peer->max_fail_time = peer->min_fail_time = fail_time; + else if (fail_time > peer->max_fail_time) + peer->max_fail_time = fail_time; + else if (fail_time < peer->min_fail_time) + peer->min_fail_time = fail_time; + + pgm_rxw_lost (peer->window, skb->sequence); + PGM_HISTOGRAM_TIMES("Rx.FailTime", fail_time); + +/* mark receiver window for flushing on next recv() */ + pgm_peer_set_pending (sock, peer); +} + +/* check whether this receiver is the designated acker for the source + */ + +static inline +bool +_pgm_is_acker ( + const pgm_peer_t* restrict peer, + const struct pgm_sk_buff_t* restrict skb + ) +{ + struct sockaddr_storage acker_nla; + +/* pre-conditions */ + pgm_assert (NULL != peer); + pgm_assert (peer->sock->use_pgmcc); + pgm_assert (NULL != skb); + pgm_assert (NULL != skb->pgm_opt_pgmcc_data); + + pgm_nla_to_sockaddr (&skb->pgm_opt_pgmcc_data->opt_nla_afi, (struct sockaddr*)&acker_nla); + return (0 == pgm_sockaddr_cmp ((struct sockaddr*)&acker_nla, (struct sockaddr*)&peer->sock->send_addr)); +} + +/* is the source holding an acker election + */ + +static inline +bool +_pgm_is_acker_election ( + const struct pgm_sk_buff_t* restrict skb + ) +{ + pgm_assert (NULL != skb); + pgm_assert (NULL != skb->pgm_opt_pgmcc_data); + + const unsigned acker_afi = ntohs (skb->pgm_opt_pgmcc_data->opt_nla_afi); + switch (acker_afi) { + case AFI_IP: + if (INADDR_ANY == skb->pgm_opt_pgmcc_data->opt_nla.s_addr) + return TRUE; + break; + + case AFI_IP6: + if (0 == memcmp (&skb->pgm_opt_pgmcc_data->opt_nla, &in6addr_any, sizeof(in6addr_any))) + return TRUE; + break; + + default: break; + } + + return FALSE; +} + +/* add state for an ACK on a data packet. + */ + +static inline +void +_pgm_add_ack ( + pgm_peer_t* const restrict peer, + const pgm_time_t ack_rb_expiry + ) +{ + pgm_assert (NULL != peer); + pgm_assert (peer->sock->use_pgmcc); + + peer->ack_rb_expiry = ack_rb_expiry; + pgm_queue_push_head_link (&peer->window->ack_backoff_queue, &peer->ack_link); +} + +/* remove outstanding ACK + */ + +static inline +void +_pgm_remove_ack ( + pgm_peer_t* const restrict peer + ) +{ + pgm_assert (NULL != peer); + pgm_assert (peer->sock->use_pgmcc); + pgm_assert (!pgm_queue_is_empty (&peer->window->ack_backoff_queue)); + + pgm_queue_unlink (&peer->window->ack_backoff_queue, &peer->ack_link); + peer->ack_rb_expiry = 0; +} + +/* increase reference count for peer object + * + * on success, returns peer object. + */ + +static inline +pgm_peer_t* +_pgm_peer_ref ( + pgm_peer_t* peer + ) +{ +/* pre-conditions */ + pgm_assert (NULL != peer); + + pgm_atomic_inc32 (&peer->ref_count); + return peer; +} + +/* decrease reference count of peer object, destroying on last reference. + */ + +void +pgm_peer_unref ( + pgm_peer_t* peer + ) +{ +/* pre-conditions */ + pgm_assert (NULL != peer); + + if (pgm_atomic_exchange_and_add32 (&peer->ref_count, (uint32_t)-1) != 1) + return; + +/* receive window */ + pgm_rxw_destroy (peer->window); + peer->window = NULL; + +/* object */ + pgm_free (peer); + peer = NULL; +} + +/* find PGM options in received SKB. + * + * returns TRUE if opt_fragment is found, otherwise FALSE is returned. + */ + +static +bool +get_pgm_options ( + struct pgm_sk_buff_t* const skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + pgm_assert (NULL != skb->pgm_data); + + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(skb->pgm_data + 1); + bool found_opt = FALSE; + + pgm_assert (opt_header->opt_type == PGM_OPT_LENGTH); + pgm_assert (opt_header->opt_length == sizeof(struct pgm_opt_length)); + + pgm_debug ("get_pgm_options (skb:%p)", + (const void*)skb); + + skb->pgm_opt_fragment = NULL; + skb->pgm_opt_pgmcc_data = NULL; + +/* always at least two options, first is always opt_length */ + do { + opt_header = (struct pgm_opt_header*)((char*)opt_header + opt_header->opt_length); +/* option overflow */ + if (PGM_UNLIKELY((char*)opt_header > (char*)skb->data)) + break; + + switch (opt_header->opt_type & PGM_OPT_MASK) { + case PGM_OPT_FRAGMENT: + skb->pgm_opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); + found_opt = TRUE; + break; + + case PGM_OPT_PGMCC_DATA: + skb->pgm_opt_pgmcc_data = (struct pgm_opt_pgmcc_data*)(opt_header + 1); + found_opt = TRUE; + break; + + default: break; + } + + } while (!(opt_header->opt_type & PGM_OPT_END)); + return found_opt; +} + +/* a peer in the context of the sock is another party on the network sending PGM + * packets. for each peer we need a receive window and network layer address (nla) to + * which nak requests can be forwarded to. + * + * on success, returns new peer object. + */ + +pgm_peer_t* +pgm_new_peer ( + pgm_sock_t* const restrict sock, + const pgm_tsi_t* const restrict tsi, + const struct sockaddr* const restrict src_addr, + const socklen_t src_addrlen, + const struct sockaddr* const restrict dst_addr, + const socklen_t dst_addrlen, + const pgm_time_t now + ) +{ + pgm_peer_t* peer; + +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != src_addr); + pgm_assert (src_addrlen > 0); + pgm_assert (NULL != dst_addr); + pgm_assert (dst_addrlen > 0); + +#ifdef PGM_DEBUG + char saddr[INET6_ADDRSTRLEN], daddr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop (src_addr, saddr, sizeof(saddr)); + pgm_sockaddr_ntop (dst_addr, daddr, sizeof(daddr)); + pgm_debug ("pgm_new_peer (sock:%p tsi:%s src-addr:%s src-addrlen:%u dst-addr:%s dst-addrlen:%u)", + (void*)sock, pgm_tsi_print (tsi), saddr, (unsigned)src_addrlen, daddr, (unsigned)dst_addrlen); +#endif + + peer = pgm_new0 (pgm_peer_t, 1); + peer->expiry = now + sock->peer_expiry; + peer->sock = sock; + memcpy (&peer->tsi, tsi, sizeof(pgm_tsi_t)); + memcpy (&peer->group_nla, dst_addr, dst_addrlen); + memcpy (&peer->local_nla, src_addr, src_addrlen); +/* port at same location for sin/sin6 */ + ((struct sockaddr_in*)&peer->local_nla)->sin_port = htons (sock->udp_encap_ucast_port); + ((struct sockaddr_in*)&peer->nla)->sin_port = htons (sock->udp_encap_ucast_port); + +/* lock on rx window */ + peer->window = pgm_rxw_create (&peer->tsi, + sock->max_tpdu, + sock->rxw_sqns, + sock->rxw_secs, + sock->rxw_max_rte, + sock->ack_c_p); + peer->spmr_expiry = now + sock->spmr_expiry; + +/* Prepare ack_link */ + peer->ack_link.data = peer; + +/* add peer to hash table and linked list */ + pgm_rwlock_writer_lock (&sock->peers_lock); + pgm_peer_t* entry = _pgm_peer_ref (peer); + pgm_hashtable_insert (sock->peers_hashtable, &peer->tsi, entry); + peer->peers_link.data = peer; + sock->peers_list = pgm_list_prepend_link (sock->peers_list, &peer->peers_link); + pgm_rwlock_writer_unlock (&sock->peers_lock); + + pgm_timer_lock (sock); + if (pgm_time_after( sock->next_poll, peer->spmr_expiry )) + sock->next_poll = peer->spmr_expiry; + pgm_timer_unlock (sock); + return peer; +} + +/* copy any contiguous buffers in the peer list to the provided + * message vector. + * returns -ENOBUFS if the vector is full, returns -ECONNRESET if + * data loss is detected, returns 0 when all peers flushed. + */ + +int +pgm_flush_peers_pending ( + pgm_sock_t* const restrict sock, + struct pgm_msgv_t** restrict pmsg, + const struct pgm_msgv_t* const msg_end, /* at least pmsg + 1, same object */ + size_t* const restrict bytes_read, /* added to, not set */ + unsigned* const restrict data_read + ) +{ + int retval = 0; + +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != pmsg); + pgm_assert (NULL != *pmsg); + pgm_assert (NULL != msg_end); + pgm_assert (NULL != bytes_read); + pgm_assert (NULL != data_read); + + pgm_debug ("pgm_flush_peers_pending (sock:%p pmsg:%p msg-end:%p bytes-read:%p data-read:%p)", + (const void*)sock, (const void*)pmsg, (const void*)msg_end, (const void*)bytes_read, (const void*)data_read); + + while (sock->peers_pending) + { + pgm_peer_t* peer = sock->peers_pending->data; + if (peer->last_commit && peer->last_commit < sock->last_commit) + pgm_rxw_remove_commit (peer->window); + const ssize_t peer_bytes = pgm_rxw_readv (peer->window, pmsg, msg_end - *pmsg + 1); + + if (peer->last_cumulative_losses != ((pgm_rxw_t*)peer->window)->cumulative_losses) + { + sock->is_reset = TRUE; + peer->lost_count = ((pgm_rxw_t*)peer->window)->cumulative_losses - peer->last_cumulative_losses; + peer->last_cumulative_losses = ((pgm_rxw_t*)peer->window)->cumulative_losses; + } + + if (peer_bytes >= 0) + { + (*bytes_read) += peer_bytes; + (*data_read) ++; + peer->last_commit = sock->last_commit; + if (*pmsg > msg_end) { /* commit full */ + retval = -ENOBUFS; + break; + } + } else + peer->last_commit = 0; + if (PGM_UNLIKELY(sock->is_reset)) { + retval = -ECONNRESET; + break; + } +/* clear this reference and move to next */ + sock->peers_pending = pgm_slist_remove_first (sock->peers_pending); + } + + return retval; +} + +/* edge trigerred has receiver pending events + */ + +bool +pgm_peer_has_pending ( + pgm_peer_t* const peer + ) +{ +/* pre-conditions */ + pgm_assert (NULL != peer); + + if (NULL == peer->pending_link.data && ((pgm_rxw_t*)peer->window)->has_event) { + ((pgm_rxw_t*)peer->window)->has_event = 0; + return TRUE; + } + return FALSE; +} + +/* set receiver in pending event queue + */ + +void +pgm_peer_set_pending ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict peer + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != peer); + + if (peer->pending_link.data) return; + peer->pending_link.data = peer; + sock->peers_pending = pgm_slist_prepend_link (sock->peers_pending, &peer->pending_link); +} + +/* Create a new error SKB detailing data loss. + */ + +void +pgm_set_reset_error ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict source, + struct pgm_msgv_t* const restrict msgv + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != source); + pgm_assert (NULL != msgv); + + struct pgm_sk_buff_t* error_skb = pgm_alloc_skb (0); + error_skb->sock = sock; + error_skb->tstamp = pgm_time_update_now (); + memcpy (&error_skb->tsi, &source->tsi, sizeof(pgm_tsi_t)); + error_skb->sequence = source->lost_count; + msgv->msgv_skb[0] = error_skb; + msgv->msgv_len = 1; +} + +/* SPM indicate start of a session, continued presence of a session, or flushing final packets + * of a session. + * + * returns TRUE on valid packet, FALSE on invalid packet or duplicate SPM sequence number. + */ + +bool +pgm_on_spm ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict source, + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != source); + pgm_assert (NULL != skb); + + pgm_debug("pgm_on_spm (sock:%p source:%p skb:%p)", + (const void*)sock, (const void*)source, (const void*)skb); + + if (PGM_UNLIKELY(!pgm_verify_spm (skb))) { + pgm_trace(PGM_LOG_ROLE_NETWORK,_("Discarded invalid SPM.")); + source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_SPMS]++; + return FALSE; + } + + const struct pgm_spm* spm = (struct pgm_spm*) skb->data; + const struct pgm_spm6* spm6 = (struct pgm_spm6*)skb->data; + const uint32_t spm_sqn = ntohl (spm->spm_sqn); + +/* check for advancing sequence number, or first SPM */ + if (PGM_LIKELY(pgm_uint32_gte (spm_sqn, source->spm_sqn))) + { +/* copy NLA for replies */ + pgm_nla_to_sockaddr (&spm->spm_nla_afi, (struct sockaddr*)&source->nla); + +/* save sequence number */ + source->spm_sqn = spm_sqn; + +/* update receive window */ + const pgm_time_t nak_rb_expiry = skb->tstamp + nak_rb_ivl (sock); + const unsigned naks = pgm_rxw_update (source->window, + ntohl (spm->spm_lead), + ntohl (spm->spm_trail), + skb->tstamp, + nak_rb_expiry); + if (naks) { + pgm_timer_lock (sock); + if (pgm_time_after (sock->next_poll, nak_rb_expiry)) + sock->next_poll = nak_rb_expiry; + pgm_timer_unlock (sock); + } + +/* mark receiver window for flushing on next recv() */ + const pgm_rxw_t* window = source->window; + if (window->cumulative_losses != source->last_cumulative_losses && + !source->pending_link.data) + { + sock->is_reset = TRUE; + source->lost_count = window->cumulative_losses - source->last_cumulative_losses; + source->last_cumulative_losses = window->cumulative_losses; + pgm_peer_set_pending (sock, source); + } + } + else + { /* does not advance SPM sequence number */ + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded duplicate SPM.")); + source->cumulative_stats[PGM_PC_RECEIVER_DUP_SPMS]++; + return FALSE; + } + +/* check whether peer can generate parity packets */ + if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) + { + const struct pgm_opt_length* opt_len = (AF_INET6 == source->nla.ss_family) ? + (const struct pgm_opt_length*)(spm6 + 1) : + (const struct pgm_opt_length*)(spm + 1); + if (PGM_UNLIKELY(opt_len->opt_type != PGM_OPT_LENGTH)) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed SPM.")); + source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_SPMS]++; + return FALSE; + } + if (PGM_UNLIKELY(opt_len->opt_length != sizeof(struct pgm_opt_length))) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed SPM.")); + source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_SPMS]++; + return FALSE; + } +/* TODO: check for > 16 options & past packet end */ + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; + do { + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); + if ((opt_header->opt_type & PGM_OPT_MASK) == PGM_OPT_PARITY_PRM) + { + const struct pgm_opt_parity_prm* opt_parity_prm = (const struct pgm_opt_parity_prm*)(opt_header + 1); + if (PGM_UNLIKELY((opt_parity_prm->opt_reserved & PGM_PARITY_PRM_MASK) == 0)) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed SPM.")); + source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_SPMS]++; + return FALSE; + } + + const uint32_t parity_prm_tgs = ntohl (opt_parity_prm->parity_prm_tgs); + if (PGM_UNLIKELY(parity_prm_tgs < 2 || parity_prm_tgs > 128)) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed SPM.")); + source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_SPMS]++; + return FALSE; + } + + source->has_proactive_parity = opt_parity_prm->opt_reserved & PGM_PARITY_PRM_PRO; + source->has_ondemand_parity = opt_parity_prm->opt_reserved & PGM_PARITY_PRM_OND; + if (source->has_proactive_parity || source->has_ondemand_parity) { + source->is_fec_enabled = 1; + pgm_rxw_update_fec (source->window, parity_prm_tgs); + } + } + } while (!(opt_header->opt_type & PGM_OPT_END)); + } + +/* either way bump expiration timer */ + source->expiry = skb->tstamp + sock->peer_expiry; + source->spmr_expiry = 0; + if (source->spmr_tstamp > 0) { + PGM_HISTOGRAM_TIMES("Rx.SpmRequestResponseTime", skb->tstamp - source->spmr_tstamp); + source->spmr_tstamp = 0; + } + return TRUE; +} + +/* Multicast peer-to-peer NAK handling, pretty much the same as a NCF but different direction + * + * if NAK is valid, returns TRUE. on error, FALSE is returned. + */ + +bool +pgm_on_peer_nak ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict peer, + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != peer); + pgm_assert (NULL != skb); + + pgm_debug ("pgm_on_peer_nak (sock:%p peer:%p skb:%p)", + (const void*)sock, (const void*)peer, (const void*)skb); + + if (PGM_UNLIKELY(!pgm_verify_nak (skb))) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded invalid multicast NAK.")); + peer->cumulative_stats[PGM_PC_RECEIVER_NAK_ERRORS]++; + return FALSE; + } + + const struct pgm_nak* nak = (struct pgm_nak*) skb->data; + const struct pgm_nak6* nak6 = (struct pgm_nak6*)skb->data; + +/* NAK_SRC_NLA must not contain our sock unicast NLA */ + struct sockaddr_storage nak_src_nla; + pgm_nla_to_sockaddr (&nak->nak_src_nla_afi, (struct sockaddr*)&nak_src_nla); + if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&nak_src_nla, (struct sockaddr*)&sock->send_addr) == 0)) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded multicast NAK on NLA mismatch.")); + return FALSE; + } + +/* NAK_GRP_NLA contains one of our sock receive multicast groups: the sources send multicast group */ + struct sockaddr_storage nak_grp_nla; + pgm_nla_to_sockaddr ((AF_INET6 == nak_src_nla.ss_family) ? &nak6->nak6_grp_nla_afi : &nak->nak_grp_nla_afi, (struct sockaddr*)&nak_grp_nla); + bool found = FALSE; + for (unsigned i = 0; i < sock->recv_gsr_len; i++) + { + if (pgm_sockaddr_cmp ((struct sockaddr*)&nak_grp_nla, (struct sockaddr*)&sock->recv_gsr[i].gsr_group) == 0) + { + found = TRUE; + break; + } + } + + if (PGM_UNLIKELY(!found)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded multicast NAK on multicast group mismatch.")); + return FALSE; + } + +/* handle as NCF */ + int status = pgm_rxw_confirm (peer->window, + ntohl (nak->nak_sqn), + skb->tstamp, + skb->tstamp + sock->nak_rdata_ivl, + skb->tstamp + nak_rb_ivl(sock)); + if (PGM_RXW_UPDATED == status || PGM_RXW_APPENDED == status) + peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED]++; + +/* check NAK list */ + const uint32_t* nak_list = NULL; + unsigned nak_list_len = 0; + if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) + { + const struct pgm_opt_length* opt_len = (AF_INET6 == nak_src_nla.ss_family) ? + (const struct pgm_opt_length*)(nak6 + 1) : + (const struct pgm_opt_length*)(nak + 1); + if (PGM_UNLIKELY(opt_len->opt_type != PGM_OPT_LENGTH)) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed multicast NAK.")); + peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_NCFS]++; + return FALSE; + } + if (PGM_UNLIKELY(opt_len->opt_length != sizeof(struct pgm_opt_length))) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed multicast NAK.")); + peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_NCFS]++; + return FALSE; + } +/* TODO: check for > 16 options & past packet end */ + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; + do { + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); + if ((opt_header->opt_type & PGM_OPT_MASK) == PGM_OPT_NAK_LIST) + { + nak_list = ((const struct pgm_opt_nak_list*)(opt_header + 1))->opt_sqn; + nak_list_len = ( opt_header->opt_length - sizeof(struct pgm_opt_header) - sizeof(uint8_t) ) / sizeof(uint32_t); + break; + } + } while (!(opt_header->opt_type & PGM_OPT_END)); + } + + while (nak_list_len) { + status = pgm_rxw_confirm (peer->window, + ntohl (*nak_list), + skb->tstamp, + skb->tstamp + sock->nak_rdata_ivl, + skb->tstamp + nak_rb_ivl(sock)); + if (PGM_RXW_UPDATED == status || PGM_RXW_APPENDED == status) + peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED]++; + nak_list++; + nak_list_len--; + } + +/* mark receiver window for flushing on next recv() */ + const pgm_rxw_t* window = peer->window; + if (window->cumulative_losses != peer->last_cumulative_losses && + !peer->pending_link.data) + { + sock->is_reset = TRUE; + peer->lost_count = window->cumulative_losses - peer->last_cumulative_losses; + peer->last_cumulative_losses = window->cumulative_losses; + pgm_peer_set_pending (sock, peer); + } + return TRUE; +} + +/* NCF confirming receipt of a NAK from this sock or another on the LAN segment. + * + * Packet contents will match exactly the sent NAK, although not really that helpful. + * + * if NCF is valid, returns TRUE. on error, FALSE is returned. + */ + +bool +pgm_on_ncf ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict source, + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != source); + pgm_assert (NULL != skb); + + pgm_debug ("pgm_on_ncf (sock:%p source:%p skb:%p)", + (const void*)sock, (const void*)source, (const void*)skb); + + if (PGM_UNLIKELY(!pgm_verify_ncf (skb))) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded invalid NCF.")); + source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_NCFS]++; + return FALSE; + } + + const struct pgm_nak* ncf = (struct pgm_nak*) skb->data; + const struct pgm_nak6* ncf6 = (struct pgm_nak6*)skb->data; + +/* NCF_SRC_NLA may contain our sock unicast NLA, we don't really care */ + struct sockaddr_storage ncf_src_nla; + pgm_nla_to_sockaddr (&ncf->nak_src_nla_afi, (struct sockaddr*)&ncf_src_nla); + +#if 0 + if (PGM(pgm_sockaddr_cmp ((struct sockaddr*)&ncf_src_nla, (struct sockaddr*)&sock->send_addr) != 0)) { + g_trace ("INFO", "Discarded NCF on NLA mismatch."); + peer->cumulative_stats[PGM_PC_RECEIVER_PACKETS_DISCARDED]++; + return FALSE; + } +#endif + +/* NCF_GRP_NLA contains our sock multicast group */ + struct sockaddr_storage ncf_grp_nla; + pgm_nla_to_sockaddr ((AF_INET6 == ncf_src_nla.ss_family) ? &ncf6->nak6_grp_nla_afi : &ncf->nak_grp_nla_afi, (struct sockaddr*)&ncf_grp_nla); + if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&ncf_grp_nla, (struct sockaddr*)&sock->send_gsr.gsr_group) != 0)) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded NCF on multicast group mismatch.")); + return FALSE; + } + + const pgm_time_t ncf_rdata_ivl = skb->tstamp + sock->nak_rdata_ivl; + const pgm_time_t ncf_rb_ivl = skb->tstamp + nak_rb_ivl(sock); + int status = pgm_rxw_confirm (source->window, + ntohl (ncf->nak_sqn), + skb->tstamp, + ncf_rdata_ivl, + ncf_rb_ivl); + if (PGM_RXW_UPDATED == status || PGM_RXW_APPENDED == status) + { + const pgm_time_t ncf_ivl = (PGM_RXW_APPENDED == status) ? ncf_rb_ivl : ncf_rdata_ivl; + pgm_timer_lock (sock); + if (pgm_time_after (sock->next_poll, ncf_ivl)) { + sock->next_poll = ncf_ivl; + } + pgm_timer_unlock (sock); + source->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED]++; + } + +/* check NCF list */ + const uint32_t* ncf_list = NULL; + unsigned ncf_list_len = 0; + if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) + { + const struct pgm_opt_length* opt_len = (AF_INET6 == ncf_src_nla.ss_family) ? + (const struct pgm_opt_length*)(ncf6 + 1) : + (const struct pgm_opt_length*)(ncf + 1); + if (PGM_UNLIKELY(opt_len->opt_type != PGM_OPT_LENGTH)) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed NCF.")); + source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_NCFS]++; + return FALSE; + } + if (PGM_UNLIKELY(opt_len->opt_length != sizeof(struct pgm_opt_length))) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded malformed NCF.")); + source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_NCFS]++; + return FALSE; + } +/* TODO: check for > 16 options & past packet end */ + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; + do { + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); + if ((opt_header->opt_type & PGM_OPT_MASK) == PGM_OPT_NAK_LIST) + { + ncf_list = ((const struct pgm_opt_nak_list*)(opt_header + 1))->opt_sqn; + ncf_list_len = ( opt_header->opt_length - sizeof(struct pgm_opt_header) - sizeof(uint8_t) ) / sizeof(uint32_t); + break; + } + } while (!(opt_header->opt_type & PGM_OPT_END)); + } + + pgm_debug ("NCF contains 1+%d sequence numbers.", ncf_list_len); + while (ncf_list_len) + { + status = pgm_rxw_confirm (source->window, + ntohl (*ncf_list), + skb->tstamp, + ncf_rdata_ivl, + ncf_rb_ivl); + if (PGM_RXW_UPDATED == status || PGM_RXW_APPENDED == status) + source->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED]++; + ncf_list++; + ncf_list_len--; + } + +/* mark receiver window for flushing on next recv() */ + const pgm_rxw_t* window = source->window; + if (window->cumulative_losses != source->last_cumulative_losses && + !source->pending_link.data) + { + sock->is_reset = TRUE; + source->lost_count = window->cumulative_losses - source->last_cumulative_losses; + source->last_cumulative_losses = window->cumulative_losses; + pgm_peer_set_pending (sock, source); + } + return TRUE; +} + +/* send SPM-request to a new peer, this packet type has no contents + * + * on success, TRUE is returned, if operation would block FALSE is + * returned. + */ + +static +bool +send_spmr ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict source + ) +{ + ssize_t sent; + +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != source); + + pgm_debug ("send_spmr (sock:%p source:%p)", + (const void*)sock, (const void*)source); + + const size_t tpdu_length = sizeof(struct pgm_header); + char buf[ tpdu_length ]; + struct pgm_header* header = (struct pgm_header*)buf; + memcpy (header->pgm_gsi, &source->tsi.gsi, sizeof(pgm_gsi_t)); +/* dport & sport reversed communicating upstream */ + header->pgm_sport = sock->dport; + header->pgm_dport = source->tsi.sport; + header->pgm_type = PGM_SPMR; + header->pgm_options = 0; + header->pgm_tsdu_length = 0; + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + +/* send multicast SPMR TTL 1 */ + sent = pgm_sendto_hops (sock, + FALSE, /* not rate limited */ + FALSE, /* regular socket */ + 1, + header, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len ((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) + return FALSE; + +/* send unicast SPMR with regular TTL */ + sent = pgm_sendto (sock, + FALSE, + FALSE, + header, + tpdu_length, + (struct sockaddr*)&source->local_nla, + pgm_sockaddr_len ((struct sockaddr*)&source->local_nla)); + if (sent < 0 && EAGAIN == errno) + return FALSE; + + sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT] += tpdu_length * 2; + return TRUE; +} + +/* send selective NAK for one sequence number. + * + * on success, TRUE is returned, returns FALSE if would block on operation. + */ + +static +bool +send_nak ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict source, + const uint32_t sequence + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != source); + + pgm_debug ("send_nak (sock:%p peer:%p sequence:%" PRIu32 ")", + (void*)sock, (void*)source, sequence); + + size_t tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_nak); + if (AF_INET6 == source->nla.ss_family) + tpdu_length += sizeof(struct pgm_nak6) - sizeof(struct pgm_nak); + char buf[ tpdu_length ]; + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_nak* nak = (struct pgm_nak* )(header + 1); + struct pgm_nak6* nak6 = (struct pgm_nak6*)(header + 1); + memcpy (header->pgm_gsi, &source->tsi.gsi, sizeof(pgm_gsi_t)); + +/* dport & sport swap over for a nak */ + header->pgm_sport = sock->dport; + header->pgm_dport = source->tsi.sport; + header->pgm_type = PGM_NAK; + header->pgm_options = 0; + header->pgm_tsdu_length = 0; + +/* NAK */ + nak->nak_sqn = htonl (sequence); + +/* source nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&source->nla, (char*)&nak->nak_src_nla_afi); + +/* group nla: we match the NAK NLA to the same as advertised by the source, we might + * be listening to multiple multicast groups + */ + pgm_sockaddr_to_nla ((struct sockaddr*)&source->group_nla, + (AF_INET6 == source->nla.ss_family) ? (char*)&nak6->nak6_grp_nla_afi : (char*)&nak->nak_grp_nla_afi); + + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + + const ssize_t sent = pgm_sendto (sock, + FALSE, /* not rate limited */ + TRUE, /* with router alert */ + header, + tpdu_length, + (struct sockaddr*)&source->nla, + pgm_sockaddr_len((struct sockaddr*)&source->nla)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) + return FALSE; + + source->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAK_PACKETS_SENT]++; + source->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SENT]++; + return TRUE; +} + +/* Send a parity NAK requesting on-demand parity packet generation. + * + * on success, TRUE is returned, returns FALSE if operation would block. + */ + +static +bool +send_parity_nak ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict source, + const uint32_t nak_tg_sqn, /* transmission group (shifted) */ + const uint32_t nak_pkt_cnt /* count of parity packets to request */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != source); + pgm_assert (nak_pkt_cnt > 0); + + pgm_debug ("send_parity_nak (sock:%p source:%p nak-tg-sqn:%" PRIu32 " nak-pkt-cnt:%" PRIu32 ")", + (void*)sock, (void*)source, nak_tg_sqn, nak_pkt_cnt); + + size_t tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_nak); + if (AF_INET6 == source->nla.ss_family) + tpdu_length += sizeof(struct pgm_nak6) - sizeof(struct pgm_nak); + char buf[ tpdu_length ]; + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_nak* nak = (struct pgm_nak* )(header + 1); + struct pgm_nak6* nak6 = (struct pgm_nak6*)(header + 1); + memcpy (header->pgm_gsi, &source->tsi.gsi, sizeof(pgm_gsi_t)); + +/* dport & sport swap over for a nak */ + header->pgm_sport = sock->dport; + header->pgm_dport = source->tsi.sport; + header->pgm_type = PGM_NAK; + header->pgm_options = PGM_OPT_PARITY; /* this is a parity packet */ + header->pgm_tsdu_length = 0; + +/* NAK */ + nak->nak_sqn = htonl (nak_tg_sqn | (nak_pkt_cnt - 1) ); + +/* source nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&source->nla, (char*)&nak->nak_src_nla_afi); + +/* group nla: we match the NAK NLA to the same as advertised by the source, we might + * be listening to multiple multicast groups + */ + pgm_sockaddr_to_nla ((struct sockaddr*)&source->group_nla, + (AF_INET6 == source->nla.ss_family) ? (char*)&nak6->nak6_grp_nla_afi : (char*)&nak->nak_grp_nla_afi ); + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + + const ssize_t sent = pgm_sendto (sock, + FALSE, /* not rate limited */ + TRUE, /* with router alert */ + header, + tpdu_length, + (struct sockaddr*)&source->nla, + pgm_sockaddr_len((struct sockaddr*)&source->nla)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) + return FALSE; + + source->cumulative_stats[PGM_PC_RECEIVER_PARITY_NAK_PACKETS_SENT]++; + source->cumulative_stats[PGM_PC_RECEIVER_PARITY_NAKS_SENT]++; + return TRUE; +} + +/* A NAK packet with a OPT_NAK_LIST option extension + * + * on success, TRUE is returned. on error, FALSE is returned. + */ + +static +bool +send_nak_list ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict source, + const struct pgm_sqn_list_t* const restrict sqn_list + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != source); + pgm_assert (NULL != sqn_list); + pgm_assert_cmpuint (sqn_list->len, >, 1); + pgm_assert_cmpuint (sqn_list->len, <=, 63); + +#ifdef RECEIVER_DEBUG + char list[1024]; + sprintf (list, "%" PRIu32, sqn_list->sqn[0]); + for (unsigned i = 1; i < sqn_list->len; i++) { + char sequence[2 + strlen("4294967295")]; + sprintf (sequence, " %" PRIu32, sqn_list->sqn[i]); + strcat (list, sequence); + } + pgm_debug("send_nak_list (sock:%p source:%p sqn-list:[%s])", + (const void*)sock, (const void*)source, list); +#endif + + size_t tpdu_length = sizeof(struct pgm_header) + + sizeof(struct pgm_nak) + + sizeof(struct pgm_opt_length) + /* includes header */ + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(uint32_t) ); + if (AF_INET6 == source->nla.ss_family) + tpdu_length += sizeof(struct pgm_nak6) - sizeof(struct pgm_nak); + char buf[ tpdu_length ]; + if (PGM_UNLIKELY(pgm_mem_gc_friendly)) + memset (buf, 0, tpdu_length); + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_nak* nak = (struct pgm_nak* )(header + 1); + struct pgm_nak6* nak6 = (struct pgm_nak6*)(header + 1); + memcpy (header->pgm_gsi, &source->tsi.gsi, sizeof(pgm_gsi_t)); + +/* dport & sport swap over for a nak */ + header->pgm_sport = sock->dport; + header->pgm_dport = source->tsi.sport; + header->pgm_type = PGM_NAK; + header->pgm_options = PGM_OPT_PRESENT | PGM_OPT_NETWORK; + header->pgm_tsdu_length = 0; + +/* NAK */ + nak->nak_sqn = htonl (sqn_list->sqn[0]); + +/* source nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&source->nla, (char*)&nak->nak_src_nla_afi); + +/* group nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&source->group_nla, + (AF_INET6 == source->nla.ss_family) ? (char*)&nak6->nak6_grp_nla_afi : (char*)&nak->nak_grp_nla_afi); + +/* OPT_NAK_LIST */ + struct pgm_opt_length* opt_len = (AF_INET6 == source->nla.ss_family) ? (struct pgm_opt_length*)(nak6 + 1) : (struct pgm_opt_length*)(nak + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(uint32_t) ) ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_NAK_LIST | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(uint32_t) ); + struct pgm_opt_nak_list* opt_nak_list = (struct pgm_opt_nak_list*)(opt_header + 1); + opt_nak_list->opt_reserved = 0; + + for (unsigned i = 1; i < sqn_list->len; i++) + opt_nak_list->opt_sqn[i-1] = htonl (sqn_list->sqn[i]); + + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + + const ssize_t sent = pgm_sendto (sock, + FALSE, /* not rate limited */ + FALSE, /* regular socket */ + header, + tpdu_length, + (struct sockaddr*)&source->nla, + pgm_sockaddr_len((struct sockaddr*)&source->nla)); + if ( sent != (ssize_t)tpdu_length ) + return FALSE; + + source->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAK_PACKETS_SENT]++; + source->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SENT] += 1 + sqn_list->len; + return TRUE; +} + +/* send ACK upstream to source + * + * on success, TRUE is returned. on error, FALSE is returned. + */ + +static +bool +send_ack ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict source, + const pgm_time_t now + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (sock->use_pgmcc); + pgm_assert (NULL != source); + + pgm_debug ("send_ack (sock:%p source:%p now:%" PGM_TIME_FORMAT ")", + (const void*)sock, (const void*)source, now); + + size_t tpdu_length = sizeof(struct pgm_header) + + sizeof(struct pgm_ack) + + sizeof(struct pgm_opt_length) + /* includes header */ + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_pgmcc_feedback); + if (AF_INET6 == sock->send_addr.ss_family) + tpdu_length += sizeof(struct pgm_opt6_pgmcc_feedback) - sizeof(struct pgm_opt_pgmcc_feedback); + char buf[ tpdu_length ]; + if (PGM_UNLIKELY(pgm_mem_gc_friendly)) + memset (buf, 0, tpdu_length); + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_ack* ack = (struct pgm_ack*)(header + 1); + memcpy (header->pgm_gsi, &source->tsi.gsi, sizeof(pgm_gsi_t)); + +/* dport & sport swap over for an ack */ + header->pgm_sport = sock->dport; + header->pgm_dport = source->tsi.sport; + header->pgm_type = PGM_ACK; + header->pgm_options = PGM_OPT_PRESENT; + header->pgm_tsdu_length = 0; + +/* ACK */ + ack->ack_rx_max = htonl (pgm_rxw_lead (source->window)); + ack->ack_bitmap = htonl (source->window->bitmap); + +/* OPT_PGMCC_FEEDBACK */ + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(ack + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + (AF_INET6 == sock->send_addr.ss_family) ? + sizeof(struct pgm_opt6_pgmcc_feedback) : + sizeof(struct pgm_opt_pgmcc_feedback) ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_PGMCC_FEEDBACK | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + ( (AF_INET6 == sock->send_addr.ss_family) ? + sizeof(struct pgm_opt6_pgmcc_feedback) : + sizeof(struct pgm_opt_pgmcc_feedback) ); + struct pgm_opt_pgmcc_feedback* opt_pgmcc_feedback = (struct pgm_opt_pgmcc_feedback*)(opt_header + 1); + opt_pgmcc_feedback->opt_reserved = 0; + + const uint32_t t = source->ack_last_tstamp + pgm_to_msecs( now - source->last_data_tstamp ); + opt_pgmcc_feedback->opt_tstamp = htonl (t); + pgm_sockaddr_to_nla ((struct sockaddr*)&sock->send_addr, (char*)&opt_pgmcc_feedback->opt_nla_afi); + opt_pgmcc_feedback->opt_loss_rate = htons ((uint16_t)source->window->data_loss); + + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + + const ssize_t sent = pgm_sendto (sock, + FALSE, /* not rate limited */ + FALSE, /* regular socket */ + header, + tpdu_length, + (struct sockaddr*)&source->nla, + pgm_sockaddr_len((struct sockaddr*)&source->nla)); + if ( sent != (ssize_t)tpdu_length ) + return FALSE; + + source->cumulative_stats[PGM_PC_RECEIVER_ACKS_SENT]++; + return TRUE; +} + +/* check all receiver windows for ACKer elections, on expiration send an ACK. + * + * returns TRUE on success, returns FALSE if operation would block. + */ + +static +bool +ack_rb_state ( + pgm_peer_t* peer, + const pgm_time_t now + ) +{ +/* pre-conditions */ + pgm_assert (NULL != peer); + pgm_assert (peer->sock->use_pgmcc); + + pgm_debug ("ack_rb_state (peer:%p now:%" PGM_TIME_FORMAT ")", + (const void*)peer, now); + + pgm_rxw_t* window = peer->window; + pgm_sock_t* sock = peer->sock; + pgm_list_t* list; + + list = window->ack_backoff_queue.tail; + if (!list) { + pgm_assert (window->ack_backoff_queue.head == NULL); + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Backoff queue is empty in ack_rb_state.")); + return TRUE; + } else { + pgm_assert (window->ack_backoff_queue.head != NULL); + } + +/* have not learned this peers NLA */ + const bool is_valid_nla = (0 != peer->nla.ss_family); + + while (list) + { + pgm_list_t* next_list_el = list->prev; + +/* check for ACK backoff expiration */ + if (pgm_time_after_eq(now, peer->ack_rb_expiry)) + { +/* unreliable delivery */ + _pgm_remove_ack (peer); + + if (PGM_UNLIKELY(!is_valid_nla)) { + pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Unable to send ACK due to unknown NLA.")); + list = next_list_el; + continue; + } + + pgm_assert (!pgm_sockaddr_is_addr_unspecified ((struct sockaddr*)&peer->nla)); + + if (!send_ack (sock, peer, now)) + return FALSE; + } + else + { /* packet expires some time later */ + break; + } + + list = next_list_el; + } + + if (window->ack_backoff_queue.length == 0) + { + pgm_assert ((struct rxw_packet*)window->ack_backoff_queue.head == NULL); + pgm_assert ((struct rxw_packet*)window->ack_backoff_queue.tail == NULL); + } + else + { + pgm_assert ((struct rxw_packet*)window->ack_backoff_queue.head != NULL); + pgm_assert ((struct rxw_packet*)window->ack_backoff_queue.tail != NULL); + } + + if (window->ack_backoff_queue.tail) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Next expiry set in %f seconds."), + pgm_to_secsf(next_ack_rb_expiry(window) - now)); + } + else + { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("ACK backoff queue empty.")); + } + return TRUE; +} + +/* check all receiver windows for packets in BACK-OFF_STATE, on expiration send a NAK. + * update sock::next_nak_rb_timestamp for next expiration time. + * + * peer object is locked before entry. + * + * returns TRUE on success, returns FALSE if operation would block. + */ + +static +bool +nak_rb_state ( + pgm_peer_t* peer, + const pgm_time_t now + ) +{ +/* pre-conditions */ + pgm_assert (NULL != peer); + + pgm_debug ("nak_rb_state (peer:%p now:%" PGM_TIME_FORMAT ")", + (const void*)peer, now); + + pgm_rxw_t* window = peer->window; + pgm_sock_t* sock = peer->sock; + pgm_list_t* list; + struct pgm_sqn_list_t nak_list = { .len = 0 }; + +/* send all NAKs first, lack of data is blocking contiguous processing and its + * better to get the notification out a.s.a.p. even though it might be waiting + * in a kernel queue. + * + * alternative: after each packet check for incoming data and return to the + * event loop. bias for shorter loops as retry count increases. + */ + list = window->nak_backoff_queue.tail; + if (!list) { + pgm_assert (window->nak_backoff_queue.head == NULL); + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Backoff queue is empty in nak_rb_state.")); + return TRUE; + } else { + pgm_assert (window->nak_backoff_queue.head != NULL); + } + + unsigned dropped_invalid = 0; + +/* have not learned this peers NLA */ + const bool is_valid_nla = 0 != peer->nla.ss_family; + +/* TODO: process BOTH selective and parity NAKs? */ + +/* calculate current transmission group for parity enabled peers */ + if (peer->has_ondemand_parity) + { + const uint32_t tg_sqn_mask = 0xffffffff << window->tg_sqn_shift; + +/* NAKs only generated previous to current transmission group */ + const uint32_t current_tg_sqn = window->lead & tg_sqn_mask; + + uint32_t nak_tg_sqn = 0; + uint32_t nak_pkt_cnt = 0; + +/* parity NAK generation */ + + while (list) + { + pgm_list_t* next_list_el = list->prev; + struct pgm_sk_buff_t* skb = (struct pgm_sk_buff_t*)list; + pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; + +/* check this packet for state expiration */ + if (pgm_time_after_eq (now, state->timer_expiry)) + { + if (PGM_UNLIKELY(!is_valid_nla)) { + dropped_invalid++; + pgm_rxw_lost (window, skb->sequence); +/* mark receiver window for flushing on next recv() */ + pgm_peer_set_pending (sock, peer); + list = next_list_el; + continue; + } + +/* TODO: parity nak lists */ + const uint32_t tg_sqn = skb->sequence & tg_sqn_mask; + if ( ( nak_pkt_cnt && tg_sqn == nak_tg_sqn ) || + ( !nak_pkt_cnt && tg_sqn != current_tg_sqn ) ) + { + pgm_rxw_state (window, skb, PGM_PKT_STATE_WAIT_NCF); + + if (!nak_pkt_cnt++) + nak_tg_sqn = tg_sqn; + state->nak_transmit_count++; + +#ifdef PGM_ABSOLUTE_EXPIRY + state->timer_expiry += sock->nak_rpt_ivl; + while (pgm_time_after_eq (now, state->timer_expiry)) { + state->timer_expiry += sock->nak_rpt_ivl; + state->ncf_retry_count++; + } +#else + state->timer_expiry = now + sock->nak_rpt_ivl; +#endif + pgm_timer_lock (sock); + if (pgm_time_after (sock->next_poll, state->timer_expiry)) + sock->next_poll = state->timer_expiry; + pgm_timer_unlock (sock); + } + else + { /* different transmission group */ + break; + } + } + else + { /* packet expires some time later */ + break; + } + + list = next_list_el; + } + + if (nak_pkt_cnt && !send_parity_nak (sock, peer, nak_tg_sqn, nak_pkt_cnt)) + return FALSE; + } + else + { + +/* select NAK generation */ + + while (list) + { + pgm_list_t* next_list_el = list->prev; + struct pgm_sk_buff_t* skb = (struct pgm_sk_buff_t*)list; + pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; + +/* check this packet for state expiration */ + if (pgm_time_after_eq(now, state->timer_expiry)) + { + if (PGM_UNLIKELY(!is_valid_nla)) { + dropped_invalid++; + pgm_rxw_lost (window, skb->sequence); +/* mark receiver window for flushing on next recv() */ + pgm_peer_set_pending (sock, peer); + list = next_list_el; + continue; + } + + pgm_rxw_state (window, skb, PGM_PKT_STATE_WAIT_NCF); + nak_list.sqn[nak_list.len++] = skb->sequence; + state->nak_transmit_count++; + +/* we have two options here, calculate the expiry time in the new state relative to the current + * state execution time, skipping missed expirations due to delay in state processing, or base + * from the actual current time. + */ +#ifdef PGM_ABSOLUTE_EXPIRY + state->timer_expiry += sock->nak_rpt_ivl; + while (pgm_time_after_eq(now, state->timer_expiry)){ + state->timer_expiry += sock->nak_rpt_ivl; + state->ncf_retry_count++; + } +#else + state->timer_expiry = now + sock->nak_rpt_ivl; +pgm_trace(PGM_LOG_ROLE_NETWORK,_("nak_rpt_expiry in %f seconds."), + pgm_to_secsf( state->timer_expiry - now ) ); +#endif + pgm_timer_lock (sock); + if (pgm_time_after (sock->next_poll, state->timer_expiry)) + sock->next_poll = state->timer_expiry; + pgm_timer_unlock (sock); + + if (nak_list.len == PGM_N_ELEMENTS(nak_list.sqn)) { + if (sock->can_send_nak && !send_nak_list (sock, peer, &nak_list)) + return FALSE; + nak_list.len = 0; + } + } + else + { /* packet expires some time later */ + break; + } + + list = next_list_el; + } + + if (sock->can_send_nak && nak_list.len) + { + if (nak_list.len > 1 && !send_nak_list (sock, peer, &nak_list)) + return FALSE; + else if (!send_nak (sock, peer, nak_list.sqn[0])) + return FALSE; + } + + } + + if (PGM_UNLIKELY(dropped_invalid)) + { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Dropped %u messages due to invalid NLA."), dropped_invalid); + +/* mark receiver window for flushing on next recv() */ + if (window->cumulative_losses != peer->last_cumulative_losses && + !peer->pending_link.data) + { + sock->is_reset = TRUE; + peer->lost_count = window->cumulative_losses - peer->last_cumulative_losses; + peer->last_cumulative_losses = window->cumulative_losses; + pgm_peer_set_pending (sock, peer); + } + } + + if (window->nak_backoff_queue.length == 0) + { + pgm_assert ((struct rxw_packet*)window->nak_backoff_queue.head == NULL); + pgm_assert ((struct rxw_packet*)window->nak_backoff_queue.tail == NULL); + } + else + { + pgm_assert ((struct rxw_packet*)window->nak_backoff_queue.head != NULL); + pgm_assert ((struct rxw_packet*)window->nak_backoff_queue.tail != NULL); + } + + if (window->nak_backoff_queue.tail) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Next expiry set in %f seconds."), + pgm_to_secsf(next_nak_rb_expiry(window) - now)); + } + else + { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("NAK backoff queue empty.")); + } + return TRUE; +} + +/* check this peer for NAK state timers, uses the tail of each queue for the nearest + * timer execution. + * + * returns TRUE on complete sweep, returns FALSE if operation would block. + */ + +bool +pgm_check_peer_state ( + pgm_sock_t*const sock, + const pgm_time_t now + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + + pgm_debug ("pgm_check_peer_state (sock:%p now:%" PGM_TIME_FORMAT ")", + (const void*)sock, now); + + if (!sock->peers_list) + return TRUE; + + pgm_list_t* list = sock->peers_list; + do { + pgm_list_t* next = list->next; + pgm_peer_t* peer = list->data; + pgm_rxw_t* window = peer->window; + + if (peer->spmr_expiry) + { + if (pgm_time_after_eq (now, peer->spmr_expiry)) + { + if (sock->can_send_nak) { + if (!send_spmr (sock, peer)) { + return FALSE; + } + peer->spmr_tstamp = now; + } + peer->spmr_expiry = 0; + } + } + + if (window->ack_backoff_queue.tail) + { + pgm_assert (sock->use_pgmcc); + + if (pgm_time_after_eq (now, next_ack_rb_expiry (window))) + if (!ack_rb_state (peer, now)) { + return FALSE; + } + } + + if (window->nak_backoff_queue.tail) + { + if (pgm_time_after_eq (now, next_nak_rb_expiry (window))) + if (!nak_rb_state (peer, now)) { + return FALSE; + } + } + + if (window->wait_ncf_queue.tail) + { + if (pgm_time_after_eq (now, next_nak_rpt_expiry (window))) + nak_rpt_state (peer, now); + } + + if (window->wait_data_queue.tail) + { + if (pgm_time_after_eq (now, next_nak_rdata_expiry (window))) + nak_rdata_state (peer, now); + } + +/* expired, remove from hash table and linked list */ + if (pgm_time_after_eq (now, peer->expiry)) + { + if (peer->pending_link.data) + { + pgm_trace (PGM_LOG_ROLE_SESSION,_("Peer expiration postponed due to committing data, tsi %s"), pgm_tsi_print (&peer->tsi)); + peer->expiry += sock->peer_expiry; + } + else if (window->committed_count) + { + pgm_trace (PGM_LOG_ROLE_SESSION,_("Peer expiration postponed due to committed data, tsi %s"), pgm_tsi_print (&peer->tsi)); + peer->expiry += sock->peer_expiry; + } + else + { + pgm_trace (PGM_LOG_ROLE_SESSION,_("Peer expired, tsi %s"), pgm_tsi_print (&peer->tsi)); + pgm_hashtable_remove (sock->peers_hashtable, &peer->tsi); + sock->peers_list = pgm_list_remove_link (sock->peers_list, &peer->peers_link); + if (sock->last_hash_value == peer) + sock->last_hash_value = NULL; + pgm_peer_unref (peer); + } + } + + list = next; + } while (list); + +/* check for waiting contiguous packets */ + if (sock->peers_pending && !sock->is_pending_read) + { + pgm_debug ("prod rx thread"); + pgm_notify_send (&sock->pending_notify); + sock->is_pending_read = TRUE; + } + return TRUE; +} + +/* find the next state expiration time among the socks peers. + * + * on success, returns the earliest of the expiration parameter or next + * peer expiration time. + */ + +pgm_time_t +pgm_min_receiver_expiry ( + pgm_time_t expiration, /* absolute time */ + pgm_sock_t* sock + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + + pgm_debug ("pgm_min_receiver_expiry (expiration:%" PGM_TIME_FORMAT " sock:%p)", + expiration, (const void*)sock); + + if (!sock->peers_list) + return expiration; + + pgm_list_t* list = sock->peers_list; + do { + pgm_list_t* next = list->next; + pgm_peer_t* peer = (pgm_peer_t*)list->data; + pgm_rxw_t* window = peer->window; + + if (peer->spmr_expiry) + { + if (pgm_time_after_eq (expiration, peer->spmr_expiry)) + expiration = peer->spmr_expiry; + } + + if (window->ack_backoff_queue.tail) + { + pgm_assert (sock->use_pgmcc); + if (pgm_time_after_eq (expiration, next_ack_rb_expiry (window))) + expiration = next_ack_rb_expiry (window); + } + + if (window->nak_backoff_queue.tail) + { + if (pgm_time_after_eq (expiration, next_nak_rb_expiry (window))) + expiration = next_nak_rb_expiry (window); + } + + if (window->wait_ncf_queue.tail) + { + if (pgm_time_after_eq (expiration, next_nak_rpt_expiry (window))) + expiration = next_nak_rpt_expiry (window); + } + + if (window->wait_data_queue.tail) + { + if (pgm_time_after_eq (expiration, next_nak_rdata_expiry (window))) + expiration = next_nak_rdata_expiry (window); + } + + list = next; + } while (list); + + return expiration; +} + +/* check WAIT_NCF_STATE, on expiration move back to BACK-OFF_STATE, on exceeding NAK_NCF_RETRIES + * cancel the sequence number. + */ +static +void +nak_rpt_state ( + pgm_peer_t* peer, + const pgm_time_t now + ) +{ +/* pre-conditions */ + pgm_assert (NULL != peer); + + pgm_debug ("nak_rpt_state (peer:%p now:%" PGM_TIME_FORMAT ")", + (void*)peer, now); + + pgm_rxw_t* window = peer->window; + pgm_sock_t* sock = peer->sock; + pgm_list_t* list = window->wait_ncf_queue.tail; + + unsigned dropped_invalid = 0; + unsigned dropped = 0; + +/* have not learned this peers NLA */ + const bool is_valid_nla = 0 != peer->nla.ss_family; + + while (list) + { + pgm_list_t* next_list_el = list->prev; + struct pgm_sk_buff_t* skb = (struct pgm_sk_buff_t*)list; + pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; + +/* check this packet for state expiration */ + if (pgm_time_after_eq (now, state->timer_expiry)) + { + if (PGM_UNLIKELY(!is_valid_nla)) { + dropped_invalid++; + pgm_rxw_lost (window, skb->sequence); +/* mark receiver window for flushing on next recv() */ + pgm_peer_set_pending (sock, peer); + list = next_list_el; + continue; + } + + if (++state->ncf_retry_count >= sock->nak_ncf_retries) + { + dropped++; + cancel_skb (sock, peer, skb, now); + peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_NCF_RETRIES_EXCEEDED]++; + } + else + { +/* retry */ +// state->timer_expiry += nak_rb_ivl(sock); + state->timer_expiry = now + nak_rb_ivl (sock); + pgm_rxw_state (window, skb, PGM_PKT_STATE_BACK_OFF); + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("NCF retry #%u attempt %u/%u."), skb->sequence, state->ncf_retry_count, sock->nak_ncf_retries); + } + } + else + { +/* packet expires some time later */ + pgm_trace(PGM_LOG_ROLE_RX_WINDOW,_("NCF retry #%u is delayed %f seconds."), + skb->sequence, pgm_to_secsf (state->timer_expiry - now)); + break; + } + + list = next_list_el; + } + + if (window->wait_ncf_queue.length == 0) + { + pgm_assert ((pgm_rxw_state_t*)window->wait_ncf_queue.head == NULL); + pgm_assert ((pgm_rxw_state_t*)window->wait_ncf_queue.tail == NULL); + } + else + { + pgm_assert ((pgm_rxw_state_t*)window->wait_ncf_queue.head != NULL); + pgm_assert ((pgm_rxw_state_t*)window->wait_ncf_queue.tail != NULL); + } + + if (PGM_UNLIKELY(dropped_invalid)) { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Dropped %u messages due to invalid NLA."), dropped_invalid); + } + + if (PGM_UNLIKELY(dropped)) { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Dropped %u messages due to ncf cancellation, " + "rxw_sqns %" PRIu32 + " bo %" PRIu32 + " ncf %" PRIu32 + " wd %" PRIu32 + " lost %" PRIu32 + " frag %" PRIu32), + dropped, + pgm_rxw_length (window), + window->nak_backoff_queue.length, + window->wait_ncf_queue.length, + window->wait_data_queue.length, + window->lost_count, + window->fragment_count); + } + +/* mark receiver window for flushing on next recv() */ + if (PGM_UNLIKELY(window->cumulative_losses != peer->last_cumulative_losses && + !peer->pending_link.data)) + { + sock->is_reset = TRUE; + peer->lost_count = window->cumulative_losses - peer->last_cumulative_losses; + peer->last_cumulative_losses = window->cumulative_losses; + pgm_peer_set_pending (sock, peer); + } + + if (window->wait_ncf_queue.tail) + { + if (next_nak_rpt_expiry (window) > now) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Next expiry set in %f seconds."), pgm_to_secsf (next_nak_rpt_expiry (window) - now)); + } else { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Next expiry set in -%f seconds."), pgm_to_secsf (now - next_nak_rpt_expiry (window))); + } + } + else + { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Wait ncf queue empty.")); + } +} + +/* check WAIT_DATA_STATE, on expiration move back to BACK-OFF_STATE, on exceeding NAK_DATA_RETRIES + * canel the sequence number. + */ +static +void +nak_rdata_state ( + pgm_peer_t* peer, + const pgm_time_t now + ) +{ +/* pre-conditions */ + pgm_assert (NULL != peer); + + pgm_debug ("nak_rdata_state (peer:%p now:%" PGM_TIME_FORMAT ")", + (const void*)peer, now); + + pgm_rxw_t* window = peer->window; + pgm_sock_t* sock = peer->sock; + pgm_list_t* list = window->wait_data_queue.tail; + + unsigned dropped_invalid = 0; + unsigned dropped = 0; + +/* have not learned this peers NLA */ + const bool is_valid_nla = 0 != peer->nla.ss_family; + + while (list) + { + pgm_list_t* next_list_el = list->prev; + struct pgm_sk_buff_t* rdata_skb = (struct pgm_sk_buff_t*)list; + pgm_assert (NULL != rdata_skb); + pgm_rxw_state_t* rdata_state = (pgm_rxw_state_t*)&rdata_skb->cb; + +/* check this packet for state expiration */ + if (pgm_time_after_eq (now, rdata_state->timer_expiry)) + { + if (PGM_UNLIKELY(!is_valid_nla)) { + dropped_invalid++; + pgm_rxw_lost (window, rdata_skb->sequence); +/* mark receiver window for flushing on next recv() */ + pgm_peer_set_pending (sock, peer); + list = next_list_el; + continue; + } + + if (++rdata_state->data_retry_count >= sock->nak_data_retries) + { + dropped++; + cancel_skb (sock, peer, rdata_skb, now); + peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_DATA_RETRIES_EXCEEDED]++; + list = next_list_el; + continue; + } + +// rdata_state->timer_expiry += nak_rb_ivl(sock); + rdata_state->timer_expiry = now + nak_rb_ivl (sock); + pgm_rxw_state (window, rdata_skb, PGM_PKT_STATE_BACK_OFF); + +/* retry back to back-off state */ + pgm_trace(PGM_LOG_ROLE_RX_WINDOW,_("Data retry #%u attempt %u/%u."), rdata_skb->sequence, rdata_state->data_retry_count, sock->nak_data_retries); + } + else + { /* packet expires some time later */ + break; + } + + + list = next_list_el; + } + + if (window->wait_data_queue.length == 0) + { + pgm_assert (NULL == (pgm_rxw_state_t*)window->wait_data_queue.head); + pgm_assert (NULL == (pgm_rxw_state_t*)window->wait_data_queue.tail); + } + else + { + pgm_assert (NULL != (pgm_rxw_state_t*)window->wait_data_queue.head); + pgm_assert (NULL != (pgm_rxw_state_t*)window->wait_data_queue.tail); + } + + if (PGM_UNLIKELY(dropped_invalid)) { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Dropped %u messages due to invalid NLA."), dropped_invalid); + } + + if (PGM_UNLIKELY(dropped)) { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Dropped %u messages due to data cancellation."), dropped); + } + +/* mark receiver window for flushing on next recv() */ + if (PGM_UNLIKELY(window->cumulative_losses != peer->last_cumulative_losses && + !peer->pending_link.data)) + { + sock->is_reset = TRUE; + peer->lost_count = window->cumulative_losses - peer->last_cumulative_losses; + peer->last_cumulative_losses = window->cumulative_losses; + pgm_peer_set_pending (sock, peer); + } + + if (window->wait_data_queue.tail) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Next expiry set in %f seconds."), pgm_to_secsf (next_nak_rdata_expiry (window) - now)); + } else { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Wait data queue empty.")); + } +} + +/* ODATA or RDATA packet with any of the following options: + * + * OPT_FRAGMENT - this TPDU part of a larger APDU. + * + * Ownership of skb is taken and must be passed to the receive window or destroyed. + * + * returns TRUE is skb has been replaced, FALSE is remains unchanged and can be recycled. + */ + +bool +pgm_on_data ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict source, + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != source); + pgm_assert (NULL != skb); + + pgm_debug ("pgm_on_data (sock:%p source:%p skb:%p)", + (void*)sock, (void*)source, (void*)skb); + + unsigned msg_count = 0; + const pgm_time_t nak_rb_expiry = skb->tstamp + nak_rb_ivl (sock); + pgm_time_t ack_rb_expiry = 0; + const unsigned tsdu_length = ntohs (skb->pgm_header->pgm_tsdu_length); + + skb->pgm_data = skb->data; + + const unsigned opt_total_length = (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) ? ntohs(*(uint16_t*)( (char*)( skb->pgm_data + 1 ) + sizeof(uint16_t))) : 0; + +/* advance data pointer to payload */ + pgm_skb_pull (skb, sizeof(struct pgm_data) + opt_total_length); + + if (opt_total_length > 0 && /* there are options */ + get_pgm_options (skb) && /* valid options */ + sock->use_pgmcc && /* PGMCC is enabled */ + NULL != skb->pgm_opt_pgmcc_data && /* PGMCC options */ + 0 == source->ack_rb_expiry) /* not partaking in a current election */ + { + ack_rb_expiry = skb->tstamp + ack_rb_ivl (sock); + } + + const int add_status = pgm_rxw_add (source->window, skb, skb->tstamp, nak_rb_expiry); + +/* skb reference is now invalid */ + bool flush_naks = FALSE; + + switch (add_status) { + case PGM_RXW_MISSING: + flush_naks = TRUE; +/* fall through */ + case PGM_RXW_INSERTED: + case PGM_RXW_APPENDED: + msg_count++; + break; + + case PGM_RXW_DUPLICATE: + source->cumulative_stats[PGM_PC_RECEIVER_DUP_DATAS]++; + goto discarded; + + case PGM_RXW_MALFORMED: + source->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_ODATA]++; +/* fall through */ + case PGM_RXW_BOUNDS: +discarded: + return FALSE; + + default: pgm_assert_not_reached(); break; + } + +/* valid data */ + PGM_HISTOGRAM_COUNTS("Rx.DataBytesReceived", tsdu_length); + source->cumulative_stats[PGM_PC_RECEIVER_DATA_BYTES_RECEIVED] += tsdu_length; + source->cumulative_stats[PGM_PC_RECEIVER_DATA_MSGS_RECEIVED] += msg_count; + +/* congestion control */ + if (0 != ack_rb_expiry) + { +/* save source timestamp and local timestamp for RTT calculation */ + source->ack_last_tstamp = ntohl (skb->pgm_opt_pgmcc_data->opt_tstamp); + source->last_data_tstamp = skb->tstamp; + if (_pgm_is_acker (source, skb)) + { + if (PGM_UNLIKELY(pgm_sockaddr_is_addr_unspecified ((struct sockaddr*)&source->nla))) + { + pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Unable to send ACK due to unknown NLA.")); + } + else if (PGM_UNLIKELY(!send_ack (sock, source, skb->tstamp))) + { + pgm_debug ("send_ack failed"); + } + ack_rb_expiry = 0; + } + else if (_pgm_is_acker_election (skb)) + { + pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("ACKer election.")); + _pgm_add_ack (source, ack_rb_expiry); + } + else if (0 != source->window->ack_backoff_queue.length) + { +/* purge ACK backoff queue as host is not elected ACKer */ + _pgm_remove_ack (source); + ack_rb_expiry = 0; + } + else + { +/* no election, not the elected ACKer, no outstanding ACKs */ + ack_rb_expiry = 0; + } + } + + if (flush_naks || 0 != ack_rb_expiry) { +/* flush out 1st time nak packets */ + pgm_timer_lock (sock); + if (flush_naks && pgm_time_after (sock->next_poll, nak_rb_expiry)) + sock->next_poll = nak_rb_expiry; + if (0 != ack_rb_expiry && pgm_time_after (sock->next_poll, ack_rb_expiry)) + sock->next_poll = ack_rb_expiry; + pgm_timer_unlock (sock); + } + return TRUE; +} + +/* POLLs are generated by PGM Parents (Sources or Network Elements). + * + * returns TRUE on valid packet, FALSE on invalid packet. + */ + +bool +pgm_on_poll ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict source, + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != source); + pgm_assert (NULL != skb); + + pgm_debug ("pgm_on_poll (sock:%p source:%p skb:%p)", + (void*)sock, (void*)source, (void*)skb); + + if (PGM_UNLIKELY(!pgm_verify_poll (skb))) { + pgm_trace(PGM_LOG_ROLE_NETWORK,_("Discarded invalid POLL.")); + return FALSE; + } + + struct pgm_poll* poll4 = (struct pgm_poll*) skb->data; + struct pgm_poll6* poll6 = (struct pgm_poll6*)skb->data; + uint32_t poll_rand; + memcpy (&poll_rand, (AFI_IP6 == ntohs (poll4->poll_nla_afi)) ? poll6->poll6_rand : poll4->poll_rand, sizeof(poll_rand)); + const uint32_t poll_mask = (AFI_IP6 == ntohs (poll4->poll_nla_afi)) ? ntohl (poll6->poll6_mask) : ntohl (poll4->poll_mask); + +/* Check for probability match */ + if (poll_mask && + (sock->rand_node_id & poll_mask) != poll_rand) + { +/* discard early */ + return FALSE; + } + +/* scoped per path nla + * TODO: manage list of pollers per peer + */ + const uint32_t poll_sqn = ntohl (poll4->poll_sqn); + const uint16_t poll_round = ntohs (poll4->poll_round); + +/* Check for new poll round */ + if (poll_round && + poll_sqn != source->last_poll_sqn) + { + return FALSE; + } + +/* save sequence and round of valid poll */ + source->last_poll_sqn = poll_sqn; + source->last_poll_round = poll_round; + + const uint16_t poll_s_type = ntohs (poll4->poll_s_type); + +/* Check poll type */ + switch (poll_s_type) { + case PGM_POLL_GENERAL: + return on_general_poll (sock, source, skb); + + case PGM_POLL_DLR: + return on_dlr_poll (sock, source, skb); + + default: +/* unknown sub-type, discard */ + break; + } + + return FALSE; +} + +/* Used to count PGM children */ + +static +bool +on_general_poll ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict source, + struct pgm_sk_buff_t* const restrict skb + ) +{ + struct pgm_poll* poll4 = (struct pgm_poll*) skb->data; + struct pgm_poll6* poll6 = (struct pgm_poll6*)skb->data; + +/* TODO: cancel any pending poll-response */ + +/* defer response based on provided back-off interval */ + const uint32_t poll_bo_ivl = (AFI_IP6 == ntohs (poll4->poll_nla_afi)) ? ntohl (poll6->poll6_bo_ivl) : ntohl (poll4->poll_bo_ivl); + source->polr_expiry = skb->tstamp + pgm_rand_int_range (&sock->rand_, 0, poll_bo_ivl); + pgm_nla_to_sockaddr (&poll4->poll_nla_afi, (struct sockaddr*)&source->poll_nla); +/* TODO: schedule poll-response */ + + return TRUE; +} + +/* Used to count off-tree DLRs */ + +static +bool +on_dlr_poll ( + PGM_GNUC_UNUSED pgm_sock_t* const restrict sock, + PGM_GNUC_UNUSED pgm_peer_t* const restrict source, + PGM_GNUC_UNUSED struct pgm_sk_buff_t* const restrict skb + ) +{ +/* we are not a DLR */ + return FALSE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/receiver.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/receiver.c.c89.patch new file mode 100644 index 0000000..a8c3e6d --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/receiver.c.c89.patch @@ -0,0 +1,887 @@ +--- receiver.c 2010-08-05 11:18:06.000000000 +0800 ++++ receiver.c89 2010-08-05 11:39:03.000000000 +0800 +@@ -71,8 +71,10 @@ + pgm_assert (NULL != window); + pgm_assert (NULL != window->ack_backoff_queue.tail); + ++ { + const struct pgm_peer_t* peer = (const struct pgm_peer_t*)window->ack_backoff_queue.tail; + return peer->ack_rb_expiry; ++ } + } + + static inline +@@ -84,9 +86,11 @@ + pgm_assert (NULL != window); + pgm_assert (NULL != window->nak_backoff_queue.tail); + ++ { + const struct pgm_sk_buff_t* skb = (const struct pgm_sk_buff_t*)window->nak_backoff_queue.tail; + const pgm_rxw_state_t* state = (const pgm_rxw_state_t*)&skb->cb; + return state->timer_expiry; ++ } + } + + static inline +@@ -98,9 +102,11 @@ + pgm_assert (NULL != window); + pgm_assert (NULL != window->wait_ncf_queue.tail); + ++ { + const struct pgm_sk_buff_t* skb = (const struct pgm_sk_buff_t*)window->wait_ncf_queue.tail; + const pgm_rxw_state_t* state = (const pgm_rxw_state_t*)&skb->cb; + return state->timer_expiry; ++ } + } + + static inline +@@ -112,9 +118,11 @@ + pgm_assert (NULL != window); + pgm_assert (NULL != window->wait_data_queue.tail); + ++ { + const struct pgm_sk_buff_t* skb = (const struct pgm_sk_buff_t*)window->wait_data_queue.tail; + const pgm_rxw_state_t* state = (const pgm_rxw_state_t*)&skb->cb; + return state->timer_expiry; ++ } + } + + /* calculate ACK_RB_IVL. +@@ -127,9 +135,9 @@ + { + /* pre-conditions */ + pgm_assert (NULL != sock); +- pgm_assert_cmpuint (sock->ack_bo_ivl, >, 1); ++ pgm_assert_cmpuint ((unsigned int)sock->ack_bo_ivl, >, 1); + +- return pgm_rand_int_range (&sock->rand_, 1 /* us */, sock->ack_bo_ivl); ++ return pgm_rand_int_range (&sock->rand_, 1 /* us */, (unsigned int)sock->ack_bo_ivl); + } + + /* calculate NAK_RB_IVL as random time interval 1 - NAK_BO_IVL. +@@ -142,9 +150,9 @@ + { + /* pre-conditions */ + pgm_assert (NULL != sock); +- pgm_assert_cmpuint (sock->nak_bo_ivl, >, 1); ++ pgm_assert_cmpuint ((unsigned int)sock->nak_bo_ivl, >, 1); + +- return pgm_rand_int_range (&sock->rand_, 1 /* us */, sock->nak_bo_ivl); ++ return pgm_rand_int_range (&sock->rand_, 1 /* us */, (unsigned int)sock->nak_bo_ivl); + } + + /* mark sequence as recovery failed. +@@ -162,11 +170,12 @@ + pgm_assert (NULL != sock); + pgm_assert (NULL != peer); + pgm_assert (NULL != skb); +- pgm_assert_cmpuint (now, >=, skb->tstamp); ++ pgm_assert_cmpuint ((unsigned int)now, >=, (unsigned int)skb->tstamp); + + pgm_trace (PGM_LOG_ROLE_RX_WINDOW, _("Lost data #%u due to cancellation."), skb->sequence); + +- const uint32_t fail_time = now - skb->tstamp; ++ { ++ const uint32_t fail_time = (uint32_t)(now - skb->tstamp); + if (!peer->max_fail_time) + peer->max_fail_time = peer->min_fail_time = fail_time; + else if (fail_time > peer->max_fail_time) +@@ -176,6 +185,7 @@ + + pgm_rxw_lost (peer->window, skb->sequence); + PGM_HISTOGRAM_TIMES("Rx.FailTime", fail_time); ++ } + + /* mark receiver window for flushing on next recv() */ + pgm_peer_set_pending (sock, peer); +@@ -214,6 +224,7 @@ + pgm_assert (NULL != skb); + pgm_assert (NULL != skb->pgm_opt_pgmcc_data); + ++ { + const unsigned acker_afi = ntohs (skb->pgm_opt_pgmcc_data->opt_nla_afi); + switch (acker_afi) { + case AFI_IP: +@@ -230,6 +241,7 @@ + } + + return FALSE; ++ } + } + + /* add state for an ACK on a data packet. +@@ -316,6 +328,7 @@ + pgm_assert (NULL != skb); + pgm_assert (NULL != skb->pgm_data); + ++ { + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(skb->pgm_data + 1); + bool found_opt = FALSE; + +@@ -351,6 +364,7 @@ + + } while (!(opt_header->opt_type & PGM_OPT_END)); + return found_opt; ++ } + } + + /* a peer in the context of the sock is another party on the network sending PGM +@@ -381,11 +395,13 @@ + pgm_assert (dst_addrlen > 0); + + #ifdef PGM_DEBUG ++ { + char saddr[INET6_ADDRSTRLEN], daddr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop (src_addr, saddr, sizeof(saddr)); + pgm_sockaddr_ntop (dst_addr, daddr, sizeof(daddr)); + pgm_debug ("pgm_new_peer (sock:%p tsi:%s src-addr:%s src-addrlen:%u dst-addr:%s dst-addrlen:%u)", + (void*)sock, pgm_tsi_print (tsi), saddr, (unsigned)src_addrlen, daddr, (unsigned)dst_addrlen); ++ } + #endif + + peer = pgm_new0 (pgm_peer_t, 1); +@@ -409,10 +425,12 @@ + + /* add peer to hash table and linked list */ + pgm_rwlock_writer_lock (&sock->peers_lock); ++ { + pgm_peer_t* entry = _pgm_peer_ref (peer); + pgm_hashtable_insert (sock->peers_hashtable, &peer->tsi, entry); + peer->peers_link.data = peer; + sock->peers_list = pgm_list_prepend_link (sock->peers_list, &peer->peers_link); ++ } + pgm_rwlock_writer_unlock (&sock->peers_lock); + + pgm_timer_lock (sock); +@@ -455,6 +473,7 @@ + pgm_peer_t* peer = sock->peers_pending->data; + if (peer->last_commit && peer->last_commit < sock->last_commit) + pgm_rxw_remove_commit (peer->window); ++ { + const ssize_t peer_bytes = pgm_rxw_readv (peer->window, pmsg, msg_end - *pmsg + 1); + + if (peer->last_cumulative_losses != ((pgm_rxw_t*)peer->window)->cumulative_losses) +@@ -481,6 +500,7 @@ + } + /* clear this reference and move to next */ + sock->peers_pending = pgm_slist_remove_first (sock->peers_pending); ++ } + } + + return retval; +@@ -537,6 +557,7 @@ + pgm_assert (NULL != source); + pgm_assert (NULL != msgv); + ++ { + struct pgm_sk_buff_t* error_skb = pgm_alloc_skb (0); + error_skb->sock = sock; + error_skb->tstamp = pgm_time_update_now (); +@@ -544,6 +565,7 @@ + error_skb->sequence = source->lost_count; + msgv->msgv_skb[0] = error_skb; + msgv->msgv_len = 1; ++ } + } + + /* SPM indicate start of a session, continued presence of a session, or flushing final packets +@@ -573,6 +595,7 @@ + return FALSE; + } + ++ { + const struct pgm_spm* spm = (struct pgm_spm*) skb->data; + const struct pgm_spm6* spm6 = (struct pgm_spm6*)skb->data; + const uint32_t spm_sqn = ntohl (spm->spm_sqn); +@@ -587,6 +610,7 @@ + source->spm_sqn = spm_sqn; + + /* update receive window */ ++ { + const pgm_time_t nak_rb_expiry = skb->tstamp + nak_rb_ivl (sock); + const unsigned naks = pgm_rxw_update (source->window, + ntohl (spm->spm_lead), +@@ -601,6 +625,7 @@ + } + + /* mark receiver window for flushing on next recv() */ ++ { + const pgm_rxw_t* window = source->window; + if (window->cumulative_losses != source->last_cumulative_losses && + !source->pending_link.data) +@@ -610,6 +635,8 @@ + source->last_cumulative_losses = window->cumulative_losses; + pgm_peer_set_pending (sock, source); + } ++ } ++ } + } + else + { /* does not advance SPM sequence number */ +@@ -637,6 +664,7 @@ + return FALSE; + } + /* TODO: check for > 16 options & past packet end */ ++ { + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; + do { + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); +@@ -650,6 +678,7 @@ + return FALSE; + } + ++ { + const uint32_t parity_prm_tgs = ntohl (opt_parity_prm->parity_prm_tgs); + if (PGM_UNLIKELY(parity_prm_tgs < 2 || parity_prm_tgs > 128)) + { +@@ -664,8 +693,10 @@ + source->is_fec_enabled = 1; + pgm_rxw_update_fec (source->window, parity_prm_tgs); + } ++ } + } + } while (!(opt_header->opt_type & PGM_OPT_END)); ++ } + } + + /* either way bump expiration timer */ +@@ -676,6 +707,7 @@ + source->spmr_tstamp = 0; + } + return TRUE; ++ } + } + + /* Multicast peer-to-peer NAK handling, pretty much the same as a NCF but different direction +@@ -705,6 +737,7 @@ + return FALSE; + } + ++ { + const struct pgm_nak* nak = (struct pgm_nak*) skb->data; + const struct pgm_nak6* nak6 = (struct pgm_nak6*)skb->data; + +@@ -718,10 +751,14 @@ + } + + /* NAK_GRP_NLA contains one of our sock receive multicast groups: the sources send multicast group */ ++ { + struct sockaddr_storage nak_grp_nla; + pgm_nla_to_sockaddr ((AF_INET6 == nak_src_nla.ss_family) ? &nak6->nak6_grp_nla_afi : &nak->nak_grp_nla_afi, (struct sockaddr*)&nak_grp_nla); ++ { + bool found = FALSE; +- for (unsigned i = 0; i < sock->recv_gsr_len; i++) ++ { ++ unsigned i; ++ for (i = 0; i < sock->recv_gsr_len; i++) + { + if (pgm_sockaddr_cmp ((struct sockaddr*)&nak_grp_nla, (struct sockaddr*)&sock->recv_gsr[i].gsr_group) == 0) + { +@@ -729,6 +766,7 @@ + break; + } + } ++ } + + if (PGM_UNLIKELY(!found)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded multicast NAK on multicast group mismatch.")); +@@ -736,6 +774,7 @@ + } + + /* handle as NCF */ ++ { + int status = pgm_rxw_confirm (peer->window, + ntohl (nak->nak_sqn), + skb->tstamp, +@@ -745,6 +784,7 @@ + peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED]++; + + /* check NAK list */ ++ { + const uint32_t* nak_list = NULL; + unsigned nak_list_len = 0; + if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) +@@ -765,6 +805,7 @@ + return FALSE; + } + /* TODO: check for > 16 options & past packet end */ ++ { + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; + do { + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); +@@ -775,6 +816,7 @@ + break; + } + } while (!(opt_header->opt_type & PGM_OPT_END)); ++ } + } + + while (nak_list_len) { +@@ -790,6 +832,7 @@ + } + + /* mark receiver window for flushing on next recv() */ ++ { + const pgm_rxw_t* window = peer->window; + if (window->cumulative_losses != peer->last_cumulative_losses && + !peer->pending_link.data) +@@ -800,6 +843,12 @@ + pgm_peer_set_pending (sock, peer); + } + return TRUE; ++ } ++ } ++ } ++ } ++ } ++ } + } + + /* NCF confirming receipt of a NAK from this sock or another on the LAN segment. +@@ -831,6 +880,7 @@ + return FALSE; + } + ++ { + const struct pgm_nak* ncf = (struct pgm_nak*) skb->data; + const struct pgm_nak6* ncf6 = (struct pgm_nak6*)skb->data; + +@@ -847,6 +897,7 @@ + #endif + + /* NCF_GRP_NLA contains our sock multicast group */ ++ { + struct sockaddr_storage ncf_grp_nla; + pgm_nla_to_sockaddr ((AF_INET6 == ncf_src_nla.ss_family) ? &ncf6->nak6_grp_nla_afi : &ncf->nak_grp_nla_afi, (struct sockaddr*)&ncf_grp_nla); + if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&ncf_grp_nla, (struct sockaddr*)&sock->send_gsr.gsr_group) != 0)) +@@ -855,6 +906,7 @@ + return FALSE; + } + ++ { + const pgm_time_t ncf_rdata_ivl = skb->tstamp + sock->nak_rdata_ivl; + const pgm_time_t ncf_rb_ivl = skb->tstamp + nak_rb_ivl(sock); + int status = pgm_rxw_confirm (source->window, +@@ -874,6 +926,7 @@ + } + + /* check NCF list */ ++ { + const uint32_t* ncf_list = NULL; + unsigned ncf_list_len = 0; + if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) +@@ -894,6 +947,7 @@ + return FALSE; + } + /* TODO: check for > 16 options & past packet end */ ++ { + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; + do { + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); +@@ -904,6 +958,7 @@ + break; + } + } while (!(opt_header->opt_type & PGM_OPT_END)); ++ } + } + + pgm_debug ("NCF contains 1+%d sequence numbers.", ncf_list_len); +@@ -921,6 +976,7 @@ + } + + /* mark receiver window for flushing on next recv() */ ++ { + const pgm_rxw_t* window = source->window; + if (window->cumulative_losses != source->last_cumulative_losses && + !source->pending_link.data) +@@ -931,6 +987,11 @@ + pgm_peer_set_pending (sock, source); + } + return TRUE; ++ } ++ } ++ } ++ } ++ } + } + + /* send SPM-request to a new peer, this packet type has no contents +@@ -953,8 +1014,9 @@ + pgm_debug ("send_spmr (sock:%p source:%p)", + (const void*)sock, (const void*)source); + ++ { + const size_t tpdu_length = sizeof(struct pgm_header); +- char buf[ tpdu_length ]; ++ char* buf = pgm_newa (char, tpdu_length); + struct pgm_header* header = (struct pgm_header*)buf; + memcpy (header->pgm_gsi, &source->tsi.gsi, sizeof(pgm_gsi_t)); + /* dport & sport reversed communicating upstream */ +@@ -968,6 +1030,7 @@ + + /* send multicast SPMR TTL 1 */ + pgm_sockaddr_multicast_hops (sock->send_sock, sock->send_gsr.gsr_group.ss_family, 1); ++ { + ssize_t sent = pgm_sendto (sock, + FALSE, /* not rate limited */ + FALSE, /* regular socket */ +@@ -992,6 +1055,8 @@ + + sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT] += tpdu_length * 2; + return TRUE; ++ } ++ } + } + + /* send selective NAK for one sequence number. +@@ -1014,10 +1079,12 @@ + pgm_debug ("send_nak (sock:%p peer:%p sequence:%" PRIu32 ")", + (void*)sock, (void*)source, sequence); + ++ { + size_t tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_nak); + if (AF_INET6 == source->nla.ss_family) + tpdu_length += sizeof(struct pgm_nak6) - sizeof(struct pgm_nak); +- char buf[ tpdu_length ]; ++ { ++ char* buf = pgm_newa (char, tpdu_length); + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_nak* nak = (struct pgm_nak* )(header + 1); + struct pgm_nak6* nak6 = (struct pgm_nak6*)(header + 1); +@@ -1045,6 +1112,7 @@ + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + ++ { + const ssize_t sent = pgm_sendto (sock, + FALSE, /* not rate limited */ + TRUE, /* with router alert */ +@@ -1058,6 +1126,9 @@ + source->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAK_PACKETS_SENT]++; + source->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SENT]++; + return TRUE; ++ } ++ } ++ } + } + + /* Send a parity NAK requesting on-demand parity packet generation. +@@ -1082,10 +1153,12 @@ + pgm_debug ("send_parity_nak (sock:%p source:%p nak-tg-sqn:%" PRIu32 " nak-pkt-cnt:%" PRIu32 ")", + (void*)sock, (void*)source, nak_tg_sqn, nak_pkt_cnt); + ++ { + size_t tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_nak); + if (AF_INET6 == source->nla.ss_family) + tpdu_length += sizeof(struct pgm_nak6) - sizeof(struct pgm_nak); +- char buf[ tpdu_length ]; ++ { ++ char* buf = pgm_newa (char, tpdu_length); + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_nak* nak = (struct pgm_nak* )(header + 1); + struct pgm_nak6* nak6 = (struct pgm_nak6*)(header + 1); +@@ -1112,6 +1185,7 @@ + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + ++ { + const ssize_t sent = pgm_sendto (sock, + FALSE, /* not rate limited */ + TRUE, /* with router alert */ +@@ -1125,6 +1199,9 @@ + source->cumulative_stats[PGM_PC_RECEIVER_PARITY_NAK_PACKETS_SENT]++; + source->cumulative_stats[PGM_PC_RECEIVER_PARITY_NAKS_SENT]++; + return TRUE; ++ } ++ } ++ } + } + + /* A NAK packet with a OPT_NAK_LIST option extension +@@ -1148,17 +1225,23 @@ + pgm_assert_cmpuint (sqn_list->len, <=, 63); + + #ifdef RECEIVER_DEBUG ++ { + char list[1024]; + sprintf (list, "%" PRIu32, sqn_list->sqn[0]); +- for (unsigned i = 1; i < sqn_list->len; i++) { ++ { ++ unsigned i; ++ for (i = 1; i < sqn_list->len; i++) { + char sequence[2 + strlen("4294967295")]; + sprintf (sequence, " %" PRIu32, sqn_list->sqn[i]); + strcat (list, sequence); + } ++ } + pgm_debug("send_nak_list (sock:%p source:%p sqn-list:[%s])", + (const void*)sock, (const void*)source, list); ++ } + #endif + ++ { + size_t tpdu_length = sizeof(struct pgm_header) + + sizeof(struct pgm_nak) + + sizeof(struct pgm_opt_length) + /* includes header */ +@@ -1167,9 +1250,11 @@ + ( (sqn_list->len-1) * sizeof(uint32_t) ); + if (AF_INET6 == source->nla.ss_family) + tpdu_length += sizeof(struct pgm_nak6) - sizeof(struct pgm_nak); +- char buf[ tpdu_length ]; ++ { ++ char* buf = pgm_newa (char, tpdu_length); + if (PGM_UNLIKELY(pgm_mem_gc_friendly)) + memset (buf, 0, tpdu_length); ++ { + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_nak* nak = (struct pgm_nak* )(header + 1); + struct pgm_nak6* nak6 = (struct pgm_nak6*)(header + 1); +@@ -1193,6 +1278,7 @@ + (AF_INET6 == source->nla.ss_family) ? (char*)&nak6->nak6_grp_nla_afi : (char*)&nak->nak_grp_nla_afi); + + /* OPT_NAK_LIST */ ++ { + struct pgm_opt_length* opt_len = (AF_INET6 == source->nla.ss_family) ? (struct pgm_opt_length*)(nak6 + 1) : (struct pgm_opt_length*)(nak + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); +@@ -1200,19 +1286,25 @@ + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(uint32_t) ) ); ++ { + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_NAK_LIST | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(uint32_t) ); ++ { + struct pgm_opt_nak_list* opt_nak_list = (struct pgm_opt_nak_list*)(opt_header + 1); + opt_nak_list->opt_reserved = 0; + +- for (unsigned i = 1; i < sqn_list->len; i++) ++ { ++ unsigned i; ++ for (i = 1; i < sqn_list->len; i++) + opt_nak_list->opt_sqn[i-1] = htonl (sqn_list->sqn[i]); ++ } + + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + ++ { + const ssize_t sent = pgm_sendto (sock, + FALSE, /* not rate limited */ + FALSE, /* regular socket */ +@@ -1226,6 +1318,13 @@ + source->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAK_PACKETS_SENT]++; + source->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SENT] += 1 + sqn_list->len; + return TRUE; ++ } ++ } ++ } ++ } ++ } ++ } ++ } + } + + /* send ACK upstream to source +@@ -1248,6 +1347,7 @@ + pgm_debug ("send_ack (sock:%p source:%p now:%" PGM_TIME_FORMAT ")", + (const void*)sock, (const void*)source, now); + ++ { + size_t tpdu_length = sizeof(struct pgm_header) + + sizeof(struct pgm_ack) + + sizeof(struct pgm_opt_length) + /* includes header */ +@@ -1255,9 +1355,11 @@ + sizeof(struct pgm_opt_pgmcc_feedback); + if (AF_INET6 == sock->send_addr.ss_family) + tpdu_length += sizeof(struct pgm_opt6_pgmcc_feedback) - sizeof(struct pgm_opt_pgmcc_feedback); +- char buf[ tpdu_length ]; ++ { ++ char* buf = pgm_newa (char, tpdu_length); + if (PGM_UNLIKELY(pgm_mem_gc_friendly)) + memset (buf, 0, tpdu_length); ++ { + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_ack* ack = (struct pgm_ack*)(header + 1); + memcpy (header->pgm_gsi, &source->tsi.gsi, sizeof(pgm_gsi_t)); +@@ -1274,6 +1376,7 @@ + ack->ack_bitmap = htonl (source->window->bitmap); + + /* OPT_PGMCC_FEEDBACK */ ++ { + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(ack + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); +@@ -1282,16 +1385,19 @@ + (AF_INET6 == sock->send_addr.ss_family) ? + sizeof(struct pgm_opt6_pgmcc_feedback) : + sizeof(struct pgm_opt_pgmcc_feedback) ); ++ { + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_PGMCC_FEEDBACK | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + ( (AF_INET6 == sock->send_addr.ss_family) ? + sizeof(struct pgm_opt6_pgmcc_feedback) : + sizeof(struct pgm_opt_pgmcc_feedback) ); ++ { + struct pgm_opt_pgmcc_feedback* opt_pgmcc_feedback = (struct pgm_opt_pgmcc_feedback*)(opt_header + 1); + opt_pgmcc_feedback->opt_reserved = 0; + +- const uint32_t t = source->ack_last_tstamp + pgm_to_msecs( now - source->last_data_tstamp ); ++ { ++ const uint32_t t = (uint32_t)(source->ack_last_tstamp + pgm_to_msecs( now - source->last_data_tstamp )); + opt_pgmcc_feedback->opt_tstamp = htonl (t); + pgm_sockaddr_to_nla ((struct sockaddr*)&sock->send_addr, (char*)&opt_pgmcc_feedback->opt_nla_afi); + opt_pgmcc_feedback->opt_loss_rate = htons ((uint16_t)source->window->data_loss); +@@ -1299,6 +1405,7 @@ + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + ++ { + const ssize_t sent = pgm_sendto (sock, + FALSE, /* not rate limited */ + FALSE, /* regular socket */ +@@ -1311,6 +1418,14 @@ + + source->cumulative_stats[PGM_PC_RECEIVER_ACKS_SENT]++; + return TRUE; ++ } ++ } ++ } ++ } ++ } ++ } ++ } ++ } + } + + /* check all receiver windows for ACKer elections, on expiration send an ACK. +@@ -1331,6 +1446,7 @@ + pgm_debug ("ack_rb_state (peer:%p now:%" PGM_TIME_FORMAT ")", + (const void*)peer, now); + ++ { + pgm_rxw_t* window = peer->window; + pgm_sock_t* sock = peer->sock; + pgm_list_t* list; +@@ -1345,6 +1461,7 @@ + } + + /* have not learned this peers NLA */ ++ { + const bool is_valid_nla = (0 != peer->nla.ss_family); + + while (list) +@@ -1397,6 +1514,8 @@ + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("ACK backoff queue empty.")); + } + return TRUE; ++ } ++ } + } + + /* check all receiver windows for packets in BACK-OFF_STATE, on expiration send a NAK. +@@ -1420,10 +1539,11 @@ + pgm_debug ("nak_rb_state (peer:%p now:%" PGM_TIME_FORMAT ")", + (const void*)peer, now); + ++ { + pgm_rxw_t* window = peer->window; + pgm_sock_t* sock = peer->sock; + pgm_list_t* list; +- struct pgm_sqn_list_t nak_list = { .len = 0 }; ++ struct pgm_sqn_list_t nak_list = { 0 }; + + /* send all NAKs first, lack of data is blocking contiguous processing and its + * better to get the notification out a.s.a.p. even though it might be waiting +@@ -1441,6 +1561,7 @@ + pgm_assert (window->nak_backoff_queue.head != NULL); + } + ++ { + unsigned dropped_invalid = 0; + + /* have not learned this peers NLA */ +@@ -1480,6 +1601,7 @@ + } + + /* TODO: parity nak lists */ ++ { + const uint32_t tg_sqn = skb->sequence & tg_sqn_mask; + if ( ( nak_pkt_cnt && tg_sqn == nak_tg_sqn ) || + ( !nak_pkt_cnt && tg_sqn != current_tg_sqn ) ) +@@ -1508,6 +1630,7 @@ + { /* different transmission group */ + break; + } ++ } + } + else + { /* packet expires some time later */ +@@ -1627,6 +1750,8 @@ + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("NAK backoff queue empty.")); + } + return TRUE; ++ } ++ } + } + + /* check this peer for NAK state timers, uses the tail of each queue for the nearest +@@ -1650,6 +1775,7 @@ + if (!sock->peers_list) + return TRUE; + ++ { + pgm_list_t* list = sock->peers_list; + do { + pgm_list_t* next = list->next; +@@ -1733,6 +1859,7 @@ + sock->is_pending_read = TRUE; + } + return TRUE; ++ } + } + + /* find the next state expiration time among the socks peers. +@@ -1756,6 +1883,7 @@ + if (!sock->peers_list) + return expiration; + ++ { + pgm_list_t* list = sock->peers_list; + do { + pgm_list_t* next = list->next; +@@ -1796,6 +1924,7 @@ + } while (list); + + return expiration; ++ } + } + + /* check WAIT_NCF_STATE, on expiration move back to BACK-OFF_STATE, on exceeding NAK_NCF_RETRIES +@@ -1814,6 +1943,7 @@ + pgm_debug ("nak_rpt_state (peer:%p now:%" PGM_TIME_FORMAT ")", + (void*)peer, now); + ++ { + pgm_rxw_t* window = peer->window; + pgm_sock_t* sock = peer->sock; + pgm_list_t* list = window->wait_ncf_queue.tail; +@@ -1923,6 +2053,7 @@ + { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Wait ncf queue empty.")); + } ++ } + } + + /* check WAIT_DATA_STATE, on expiration move back to BACK-OFF_STATE, on exceeding NAK_DATA_RETRIES +@@ -1941,6 +2072,7 @@ + pgm_debug ("nak_rdata_state (peer:%p now:%" PGM_TIME_FORMAT ")", + (const void*)peer, now); + ++ { + pgm_rxw_t* window = peer->window; + pgm_sock_t* sock = peer->sock; + pgm_list_t* list = window->wait_data_queue.tail; +@@ -1956,6 +2088,7 @@ + pgm_list_t* next_list_el = list->prev; + struct pgm_sk_buff_t* rdata_skb = (struct pgm_sk_buff_t*)list; + pgm_assert (NULL != rdata_skb); ++ { + pgm_rxw_state_t* rdata_state = (pgm_rxw_state_t*)&rdata_skb->cb; + + /* check this packet for state expiration */ +@@ -1991,8 +2124,8 @@ + break; + } + +- + list = next_list_el; ++ } + } + + if (window->wait_data_queue.length == 0) +@@ -2029,6 +2162,7 @@ + } else { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Wait data queue empty.")); + } ++ } + } + + /* ODATA or RDATA packet with any of the following options: +@@ -2055,6 +2189,7 @@ + pgm_debug ("pgm_on_data (sock:%p source:%p skb:%p)", + (void*)sock, (void*)source, (void*)skb); + ++ { + unsigned msg_count = 0; + const pgm_time_t nak_rb_expiry = skb->tstamp + nak_rb_ivl (sock); + pgm_time_t ack_rb_expiry = 0; +@@ -2062,6 +2197,7 @@ + + skb->pgm_data = skb->data; + ++ { + const unsigned opt_total_length = (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) ? ntohs(*(uint16_t*)( (char*)( skb->pgm_data + 1 ) + sizeof(uint16_t))) : 0; + + /* advance data pointer to payload */ +@@ -2076,6 +2212,7 @@ + ack_rb_expiry = skb->tstamp + ack_rb_ivl (sock); + } + ++ { + const int add_status = pgm_rxw_add (source->window, skb, skb->tstamp, nak_rb_expiry); + + /* skb reference is now invalid */ +@@ -2155,6 +2292,9 @@ + pgm_timer_unlock (sock); + } + return TRUE; ++ } ++ } ++ } + } + + /* POLLs are generated by PGM Parents (Sources or Network Elements). +@@ -2182,10 +2322,12 @@ + return FALSE; + } + ++ { + struct pgm_poll* poll4 = (struct pgm_poll*) skb->data; + struct pgm_poll6* poll6 = (struct pgm_poll6*)skb->data; + uint32_t poll_rand; + memcpy (&poll_rand, (AFI_IP6 == ntohs (poll4->poll_nla_afi)) ? poll6->poll6_rand : poll4->poll_rand, sizeof(poll_rand)); ++ { + const uint32_t poll_mask = (AFI_IP6 == ntohs (poll4->poll_nla_afi)) ? ntohl (poll6->poll6_mask) : ntohl (poll4->poll_mask); + + /* Check for probability match */ +@@ -2199,6 +2341,7 @@ + /* scoped per path nla + * TODO: manage list of pollers per peer + */ ++ { + const uint32_t poll_sqn = ntohl (poll4->poll_sqn); + const uint16_t poll_round = ntohs (poll4->poll_round); + +@@ -2213,6 +2356,7 @@ + source->last_poll_sqn = poll_sqn; + source->last_poll_round = poll_round; + ++ { + const uint16_t poll_s_type = ntohs (poll4->poll_s_type); + + /* Check poll type */ +@@ -2229,6 +2373,10 @@ + } + + return FALSE; ++ } ++ } ++ } ++ } + } + + /* Used to count PGM children */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/receiver.c.rej b/3rdparty/openpgm-svn-r1135/pgm/receiver.c.rej new file mode 100644 index 0000000..bb4bac3 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/receiver.c.rej @@ -0,0 +1,37 @@ +*************** +*** 71,77 **** + pgm_assert (NULL != window); + pgm_assert (NULL != window->ack_backoff_queue.tail); + +- const struct pgm_peer_t* peer = (const struct pgm_peer_t*)window->ack_backoff_queue.tail; + pgm_assert (peer->sock->use_pgmcc); + return peer->ack_rb_expiry; + } +--- 71,79 ---- + pgm_assert (NULL != window); + pgm_assert (NULL != window->ack_backoff_queue.tail); + ++ const struct pgm_peer_t* peer = (const struct pgm_peer_t*)window->ack_backoff_queue.tail->data; ++ pgm_assert (NULL != peer); ++ + pgm_assert (peer->sock->use_pgmcc); + return peer->ack_rb_expiry; + } +*************** +*** 416,421 **** + sock->ack_c_p); + peer->spmr_expiry = now + sock->spmr_expiry; + + /* add peer to hash table and linked list */ + pgm_rwlock_writer_lock (&sock->peers_lock); + pgm_peer_t* entry = _pgm_peer_ref (peer); +--- 418,426 ---- + sock->ack_c_p); + peer->spmr_expiry = now + sock->spmr_expiry; + ++ /* Prepare ack_link */ ++ peer->ack_link.data = peer; ++ + /* add peer to hash table and linked list */ + pgm_rwlock_writer_lock (&sock->peers_lock); + pgm_peer_t* entry = _pgm_peer_ref (peer); diff --git a/3rdparty/openpgm-svn-r1135/pgm/receiver_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/receiver_unittest.c new file mode 100644 index 0000000..959afe3 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/receiver_unittest.c @@ -0,0 +1,902 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for PGM receiver transport. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include + + +/* mock state */ + +#define TEST_NETWORK "" +#define TEST_PORT 7500 +#define TEST_MAX_TPDU 1500 +#define TEST_TXW_SQNS 32 +#define TEST_RXW_SQNS 32 +#define TEST_HOPS 16 +#define TEST_SPM_AMBIENT ( pgm_secs(30) ) +#define TEST_SPM_HEARTBEAT_INIT { pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(1300), pgm_secs(7), pgm_secs(16), pgm_secs(25), pgm_secs(30) } +#define TEST_PEER_EXPIRY ( pgm_secs(300) ) +#define TEST_SPMR_EXPIRY ( pgm_msecs(250) ) +#define TEST_NAK_BO_IVL ( pgm_msecs(50) ) +#define TEST_NAK_RPT_IVL ( pgm_secs(2) ) +#define TEST_NAK_RDATA_IVL ( pgm_secs(2) ) +#define TEST_NAK_DATA_RETRIES 5 +#define TEST_NAK_NCF_RETRIES 2 + + +#define pgm_histogram_add mock_pgm_histogram_add +#define pgm_verify_spm mock_pgm_verify_spm +#define pgm_verify_nak mock_pgm_verify_nak +#define pgm_verify_ncf mock_pgm_verify_ncf +#define pgm_verify_poll mock_pgm_verify_poll +#define pgm_sendto_hops mock_pgm_sendto_hops +#define pgm_time_now mock_pgm_time_now +#define pgm_time_update_now mock_pgm_time_update_now +#define pgm_rxw_destroy mock_pgm_rxw_destroy +#define pgm_rxw_create mock_pgm_rxw_create +#define pgm_rxw_update mock_pgm_rxw_update +#define pgm_rxw_update_fec mock_pgm_rxw_update_fec +#define pgm_rxw_confirm mock_pgm_rxw_confirm +#define pgm_rxw_lost mock_pgm_rxw_lost +#define pgm_rxw_state mock_pgm_rxw_state +#define pgm_rxw_add mock_pgm_rxw_add +#define pgm_rxw_remove_commit mock_pgm_rxw_remove_commit +#define pgm_rxw_readv mock_pgm_rxw_readv +#define pgm_csum_fold mock_pgm_csum_fold +#define pgm_compat_csum_partial mock_pgm_compat_csum_partial +#define pgm_histogram_init mock_pgm_histogram_init +#define pgm_setsockopt mock_pgm_setsockopt + + +#define RECEIVER_DEBUG +#include "receiver.c" + + +static +void +mock_setup (void) +{ + if (!g_thread_supported ()) g_thread_init (NULL); +} + +static +struct pgm_sock_t* +generate_sock (void) +{ + struct pgm_sock_t* sock = g_malloc0 (sizeof(struct pgm_sock_t)); + return sock; +} + +static +pgm_peer_t* +generate_peer (void) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_peer_t* peer = g_malloc0 (sizeof(pgm_peer_t)); + peer->window = g_malloc0 (sizeof(pgm_rxw_t)); + pgm_atomic_inc32 (&peer->ref_count); + return peer; +} + +/** socket module */ +static +int +mock_pgm_poll_info ( + pgm_sock_t* const sock, + struct pollfd* fds, + int* n_fds, + int events + ) +{ +} + +static +gboolean +mock_pgm_on_nak ( + pgm_sock_t* const sock, + struct pgm_sk_buff_t* const skb + ) +{ + return TRUE; +} + +static +gboolean +mock_pgm_on_nnak ( + pgm_sock_t* const sock, + struct pgm_sk_buff_t* const skb + ) +{ + return TRUE; +} + +static +gboolean +mock_pgm_on_spmr ( + pgm_sock_t* const sock, + pgm_peer_t* const peer, + struct pgm_sk_buff_t* const skb + ) +{ + return TRUE; +} + +/** net module */ +PGM_GNUC_INTERNAL +ssize_t +mock_pgm_sendto_hops ( + pgm_sock_t* sock, + bool use_rate_limit, + bool use_router_alert, + int hops, + const void* buf, + size_t len, + const struct sockaddr* to, + socklen_t tolen + ) +{ + return len; +} + +/** time module */ +static pgm_time_t mock_pgm_time_now = 0x1; +static pgm_time_t _mock_pgm_time_update_now (void); +pgm_time_update_func mock_pgm_time_update_now = _mock_pgm_time_update_now; + +pgm_time_t +_mock_pgm_time_update_now (void) +{ + return mock_pgm_time_now; +} + +/* packet module */ +bool +mock_pgm_verify_spm ( + const struct pgm_sk_buff_t* const skb + ) +{ + return TRUE; +} + +bool +mock_pgm_verify_nak ( + const struct pgm_sk_buff_t* const skb + ) +{ + return TRUE; +} + +bool +mock_pgm_verify_ncf ( + const struct pgm_sk_buff_t* const skb + ) +{ + return TRUE; +} + +bool +mock_pgm_verify_poll ( + const struct pgm_sk_buff_t* const skb + ) +{ + return TRUE; +} + +/* receive window module */ +pgm_rxw_t* +mock_pgm_rxw_create ( + const pgm_tsi_t* tsi, + const uint16_t tpdu_size, + const unsigned sqns, + const unsigned secs, + const ssize_t max_rte, + const uint32_t ack_c_p + ) +{ + return g_malloc0 (sizeof(pgm_rxw_t)); +} + +void +mock_pgm_rxw_destroy ( + pgm_rxw_t* const window + ) +{ + g_assert (NULL != window); + g_free (window); +} + +int +mock_pgm_rxw_confirm ( + pgm_rxw_t* const window, + const uint32_t sequence, + const pgm_time_t now, + const pgm_time_t nak_rdata_expiry, + const pgm_time_t nak_rb_expiry + ) +{ + return PGM_RXW_DUPLICATE; +} + +void +mock_pgm_rxw_lost ( + pgm_rxw_t* const window, + const uint32_t sequence + ) +{ +} + +void +mock_pgm_rxw_state ( + pgm_rxw_t* const window, + struct pgm_sk_buff_t* const skb, + const int new_state + ) +{ +} + +unsigned +mock_pgm_rxw_update ( + pgm_rxw_t* const window, + const uint32_t txw_lead, + const uint32_t txw_trail, + const pgm_time_t now, + const pgm_time_t nak_rb_expiry + ) +{ + return 0; +} + +void +mock_pgm_rxw_update_fec ( + pgm_rxw_t* const window, + const uint8_t rs_k + ) +{ +} + +int +mock_pgm_rxw_add ( + pgm_rxw_t* const window, + struct pgm_sk_buff_t* const skb, + const pgm_time_t now, + const pgm_time_t nak_rb_expiry + ) +{ + return PGM_RXW_APPENDED; +} + +void +mock_pgm_rxw_remove_commit ( + pgm_rxw_t* const window + ) +{ +} + +ssize_t +mock_pgm_rxw_readv ( + pgm_rxw_t* const window, + struct pgm_msgv_t** pmsg, + const unsigned pmsglen + ) +{ + return 0; +} + +/* checksum module */ +uint16_t +mock_pgm_csum_fold ( + uint32_t csum + ) +{ + return 0x0; +} + +uint32_t +mock_pgm_compat_csum_partial ( + const void* addr, + uint16_t len, + uint32_t csum + ) +{ + return 0x0; +} + +void +mock_pgm_histogram_init ( + pgm_histogram_t* histogram + ) +{ +} + +void +mock_pgm_histogram_add ( + pgm_histogram_t* histogram, + int value + ) +{ +} + +/* mock functions for external references */ + +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return 0; +} + +bool +mock_pgm_setsockopt ( + pgm_sock_t* const sock, + const int level, + const int optname, + const void* optval, + const socklen_t optlen + ) +{ + if (NULL == sock) + return FALSE; + return TRUE; +} + +/* target: + * void + * pgm_peer_unref ( + * pgm_peer_t* peer + * ) + */ + +/* last ref */ +START_TEST (test_peer_unref_pass_001) +{ + pgm_peer_t* peer = generate_peer(); + pgm_peer_unref (peer); +} +END_TEST + +/* non-last ref */ +START_TEST (test_peer_unref_pass_002) +{ + pgm_peer_t* peer = _pgm_peer_ref (generate_peer()); + pgm_peer_unref (peer); +} +END_TEST + + +START_TEST (test_peer_unref_fail_001) +{ + pgm_peer_unref (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_check_peer_state ( + * pgm_sock_t* sock, + * const pgm_time_t now + * ) + */ + +START_TEST (test_check_peer_state_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + sock->is_bound = TRUE; + pgm_check_peer_state (sock, mock_pgm_time_now); +} +END_TEST + +START_TEST (test_check_peer_state_fail_001) +{ + pgm_check_peer_state (NULL, mock_pgm_time_now); + fail ("reached"); +} +END_TEST + +/* target: + * pgm_time_t + * pgm_min_receiver_expiry ( + * pgm_time_t expiration, + * pgm_sock_t* sock + * ) + */ + +START_TEST (test_min_receiver_expiry_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + sock->is_bound = TRUE; + const pgm_time_t expiration = pgm_secs(1); + pgm_time_t next_expiration = pgm_min_receiver_expiry (expiration, sock); +} +END_TEST + +START_TEST (test_min_receiver_expiry_fail_001) +{ + const pgm_time_t expiration = pgm_secs(1); + pgm_min_receiver_expiry (expiration, NULL); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_RXW_SQNS, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_rxw_sqns_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + const int level = IPPROTO_PGM; + const int optname = PGM_RXW_SQNS; + const int rxw_sqns = 100; + const void* optval = &rxw_sqns; + const socklen_t optlen = sizeof(int); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_rxw_sqns failed"); +} +END_TEST + +START_TEST (test_set_rxw_sqns_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_RXW_SQNS; + const int rxw_sqns = 100; + const void* optval = &rxw_sqns; + const socklen_t optlen = sizeof(int); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_rxw_sqns failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_RXW_SECS, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_rxw_secs_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + const int level = IPPROTO_PGM; + const int optname = PGM_RXW_SECS; + const int rxw_secs = 10; + const void* optval = &rxw_secs; + const socklen_t optlen = sizeof(int); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_rxw_secs failed"); +} +END_TEST + +START_TEST (test_set_rxw_secs_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_RXW_SECS; + const int rxw_secs = 10; + const void* optval = &rxw_secs; + const socklen_t optlen = sizeof(int); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_rxw_secs failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_RXW_MAX_RTE, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_rxw_max_rte_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + const int level = IPPROTO_PGM; + const int optname = PGM_RXW_MAX_RTE; + const int rxw_max_rte = 100*1000; + const void* optval = &rxw_max_rte; + const socklen_t optlen = sizeof(int); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_rxw_max_rte failed"); +} +END_TEST + +START_TEST (test_set_rxw_max_rte_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_RXW_MAX_RTE; + const int rxw_max_rte = 100*1000; + const void* optval = &rxw_max_rte; + const socklen_t optlen = sizeof(int); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_rxw_max_rte failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_PEER_EXPIRY, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_peer_expiry_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + const int level = IPPROTO_PGM; + const int optname = PGM_PEER_EXPIRY; + const int peer_expiry = pgm_secs(100); + const void* optval = &peer_expiry; + const socklen_t optlen = sizeof(int); +/* pre-checking should verify value to spm ambient interval + sock->spm_ambient_interval = pgm_secs(30); + */ + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_peer_expiry failed"); +} +END_TEST + +START_TEST (test_set_peer_expiry_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_PEER_EXPIRY; + const int peer_expiry = pgm_secs(100); + const void* optval = &peer_expiry; + const socklen_t optlen = sizeof(int); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_peer_expiry failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_SPMR_EXPIRY, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_spmr_expiry_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + const int level = IPPROTO_PGM; + const int optname = PGM_SPMR_EXPIRY; + const int spmr_expiry = pgm_secs(10); + const void* optval = &spmr_expiry; + const socklen_t optlen = sizeof(int); +/* pre-checking should verify value to spm ambient interval + sock->spm_ambient_interval = pgm_secs(30); + */ + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_spmr_expiry failed"); +} +END_TEST + +START_TEST (test_set_spmr_expiry_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_SPMR_EXPIRY; + const int spmr_expiry = pgm_secs(10); + const void* optval = &spmr_expiry; + const socklen_t optlen = sizeof(int); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_spmr_expiry failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_NAK_BO_IVL, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_nak_bo_ivl_pass_001) +{ + const int level = IPPROTO_PGM; + pgm_sock_t* sock = generate_sock (); + const int optname = PGM_NAK_BO_IVL; + const int nak_bo_ivl = pgm_msecs(1000); + const void* optval = &nak_bo_ivl; + const socklen_t optlen = sizeof(int); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_nak_bo_ivl failed"); +} +END_TEST + +START_TEST (test_set_nak_bo_ivl_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_NAK_BO_IVL; + const int nak_bo_ivl = pgm_msecs(1000); + const void* optval = &nak_bo_ivl; + const socklen_t optlen = sizeof(int); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_nak_bo_ivl failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_NAK_RPT_IVL, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_nak_rpt_ivl_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + const int level = IPPROTO_PGM; + const int optname = PGM_NAK_RPT_IVL; + const int nak_rpt_ivl = pgm_msecs(1000); + const void* optval = &nak_rpt_ivl; + const socklen_t optlen = sizeof(int); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_nak_rpt_ivl failed"); +} +END_TEST + +START_TEST (test_set_nak_rpt_ivl_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_NAK_RPT_IVL; + const int nak_rpt_ivl = pgm_msecs(1000); + const void* optval = &nak_rpt_ivl; + const socklen_t optlen = sizeof(int); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_nak_rpt_ivl failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_NAK_RDATA_IVL, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_nak_rdata_ivl_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + const int level = IPPROTO_PGM; + const int optname = PGM_NAK_RDATA_IVL; + const int nak_rdata_ivl = pgm_msecs(1000); + const void* optval = &nak_rdata_ivl; + const socklen_t optlen = sizeof(int); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_nak_rdata_ivl failed"); +} +END_TEST + +START_TEST (test_set_nak_rdata_ivl_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_NAK_RDATA_IVL; + const int nak_rdata_ivl = pgm_msecs(1000); + const void* optval = &nak_rdata_ivl; + const socklen_t optlen = sizeof(int); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_nak_rdata_ivl failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_NAK_DATA_RETRIES, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_nak_data_retries_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + const int level = IPPROTO_PGM; + const int optname = PGM_NAK_DATA_RETRIES; + const int retries = 1000; + const void* optval = &retries; + const socklen_t optlen = sizeof(int); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_nak_data_retries failed"); +} +END_TEST + +START_TEST (test_set_nak_data_retries_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_NAK_DATA_RETRIES; + const int retries = 1000; + const void* optval = &retries; + const socklen_t optlen = sizeof(int); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_nak_data_retries failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_NAK_NCF_RETRIES, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_nak_ncf_retries_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + const int level = IPPROTO_PGM; + const int optname = PGM_NAK_NCF_RETRIES; + const int retries = 1000; + const void* optval = &retries; + const socklen_t optlen = sizeof(int); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_ncf_data_retries failed"); +} +END_TEST + +START_TEST (test_set_nak_ncf_retries_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_NAK_NCF_RETRIES; + const int retries = 1000; + const void* optval = &retries; + const socklen_t optlen = sizeof(int); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_ncf_data_retries failed"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_peer_unref = tcase_create ("peer_unref"); + suite_add_tcase (s, tc_peer_unref); + tcase_add_checked_fixture (tc_peer_unref, mock_setup, NULL); + tcase_add_test (tc_peer_unref, test_peer_unref_pass_001); + tcase_add_test_raise_signal (tc_peer_unref, test_peer_unref_fail_001, SIGABRT); + +/* formally check-peer-nak-state */ + TCase* tc_check_peer_state = tcase_create ("check-peer-state"); + suite_add_tcase (s, tc_check_peer_state); + tcase_add_checked_fixture (tc_check_peer_state, mock_setup, NULL); + tcase_add_test (tc_check_peer_state, test_check_peer_state_pass_001); + tcase_add_test_raise_signal (tc_check_peer_state, test_check_peer_state_fail_001, SIGABRT); + +/* formally min-nak-expiry */ + TCase* tc_min_receiver_expiry = tcase_create ("min-receiver-expiry"); + suite_add_tcase (s, tc_min_receiver_expiry); + tcase_add_checked_fixture (tc_min_receiver_expiry, mock_setup, NULL); + tcase_add_test (tc_min_receiver_expiry, test_min_receiver_expiry_pass_001); + tcase_add_test_raise_signal (tc_min_receiver_expiry, test_min_receiver_expiry_fail_001, SIGABRT); + + TCase* tc_set_rxw_sqns = tcase_create ("set-rxw_sqns"); + suite_add_tcase (s, tc_set_rxw_sqns); + tcase_add_checked_fixture (tc_set_rxw_sqns, mock_setup, NULL); + tcase_add_test (tc_set_rxw_sqns, test_set_rxw_sqns_pass_001); + tcase_add_test (tc_set_rxw_sqns, test_set_rxw_sqns_fail_001); + + TCase* tc_set_rxw_secs = tcase_create ("set-rxw-secs"); + suite_add_tcase (s, tc_set_rxw_secs); + tcase_add_checked_fixture (tc_set_rxw_secs, mock_setup, NULL); + tcase_add_test (tc_set_rxw_secs, test_set_rxw_secs_pass_001); + tcase_add_test (tc_set_rxw_secs, test_set_rxw_secs_fail_001); + + TCase* tc_set_rxw_max_rte = tcase_create ("set-rxw-max-rte"); + suite_add_tcase (s, tc_set_rxw_max_rte); + tcase_add_checked_fixture (tc_set_rxw_max_rte, mock_setup, NULL); + tcase_add_test (tc_set_rxw_max_rte, test_set_rxw_max_rte_pass_001); + tcase_add_test (tc_set_rxw_max_rte, test_set_rxw_max_rte_fail_001); + + TCase* tc_set_peer_expiry = tcase_create ("set-peer-expiry"); + suite_add_tcase (s, tc_set_peer_expiry); + tcase_add_checked_fixture (tc_set_peer_expiry, mock_setup, NULL); + tcase_add_test (tc_set_peer_expiry, test_set_peer_expiry_pass_001); + tcase_add_test (tc_set_peer_expiry, test_set_peer_expiry_fail_001); + + TCase* tc_set_spmr_expiry = tcase_create ("set-spmr-expiry"); + suite_add_tcase (s, tc_set_spmr_expiry); + tcase_add_checked_fixture (tc_set_spmr_expiry, mock_setup, NULL); + tcase_add_test (tc_set_spmr_expiry, test_set_spmr_expiry_pass_001); + tcase_add_test (tc_set_spmr_expiry, test_set_spmr_expiry_fail_001); + + TCase* tc_set_nak_bo_ivl = tcase_create ("set-nak-bo-ivl"); + suite_add_tcase (s, tc_set_nak_bo_ivl); + tcase_add_checked_fixture (tc_set_nak_bo_ivl, mock_setup, NULL); + tcase_add_test (tc_set_nak_bo_ivl, test_set_nak_bo_ivl_pass_001); + tcase_add_test (tc_set_nak_bo_ivl, test_set_nak_bo_ivl_fail_001); + + TCase* tc_set_nak_rpt_ivl = tcase_create ("set-nak-rpt-ivl"); + suite_add_tcase (s, tc_set_nak_rpt_ivl); + tcase_add_checked_fixture (tc_set_nak_rpt_ivl, mock_setup, NULL); + tcase_add_test (tc_set_nak_rpt_ivl, test_set_nak_rpt_ivl_pass_001); + tcase_add_test (tc_set_nak_rpt_ivl, test_set_nak_rpt_ivl_fail_001); + + TCase* tc_set_nak_rdata_ivl = tcase_create ("set-nak-rdata-ivl"); + suite_add_tcase (s, tc_set_nak_rdata_ivl); + tcase_add_checked_fixture (tc_set_nak_rdata_ivl, mock_setup, NULL); + tcase_add_test (tc_set_nak_rdata_ivl, test_set_nak_rdata_ivl_pass_001); + tcase_add_test (tc_set_nak_rdata_ivl, test_set_nak_rdata_ivl_fail_001); + + TCase* tc_set_nak_data_retries = tcase_create ("set-nak-data-retries"); + suite_add_tcase (s, tc_set_nak_data_retries); + tcase_add_checked_fixture (tc_set_nak_data_retries, mock_setup, NULL); + tcase_add_test (tc_set_nak_data_retries, test_set_nak_data_retries_pass_001); + tcase_add_test (tc_set_nak_data_retries, test_set_nak_data_retries_fail_001); + + TCase* tc_set_nak_ncf_retries = tcase_create ("set-nak-ncf-retries"); + suite_add_tcase (s, tc_set_nak_ncf_retries); + tcase_add_checked_fixture (tc_set_nak_ncf_retries, mock_setup, NULL); + tcase_add_test (tc_set_nak_ncf_retries, test_set_nak_ncf_retries_pass_001); + tcase_add_test (tc_set_nak_ncf_retries, test_set_nak_ncf_retries_fail_001); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/recv.c b/3rdparty/openpgm-svn-r1135/pgm/recv.c new file mode 100644 index 0000000..595beec --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/recv.c @@ -0,0 +1,1062 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Transport recv API. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include +#ifndef _WIN32 +# include +# include +# include /* _GNU_SOURCE for in6_pktinfo */ +#else +# include +# include +#endif +#include +#include +#include +#include +#include +#include + + +//#define RECV_DEBUG + +#ifndef RECV_DEBUG +# define PGM_DISABLE_ASSERT +#endif + +#ifdef _WIN32 +# define cmsghdr wsacmsghdr +# define CMSG_FIRSTHDR(msg) WSA_CMSG_FIRSTHDR(msg) +# define CMSG_NXTHDR(msg, cmsg) WSA_CMSG_NXTHDR(msg, cmsg) +# define CMSG_DATA(cmsg) WSA_CMSG_DATA(cmsg) +# define CMSG_SPACE(len) WSA_CMSG_SPACE(len) +# define CMSG_LEN(len) WSA_CMSG_LEN(len) +#endif + + +/* read a packet into a PGM skbuff + * on success returns packet length, on closed socket returns 0, + * on error returns -1. + */ + +static +ssize_t +recvskb ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t* const restrict skb, + const int flags, + struct sockaddr* const restrict src_addr, + const socklen_t src_addrlen, + struct sockaddr* const restrict dst_addr, + const socklen_t dst_addrlen + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + pgm_assert (NULL != src_addr); + pgm_assert (src_addrlen > 0); + pgm_assert (NULL != dst_addr); + pgm_assert (dst_addrlen > 0); + + pgm_debug ("recvskb (sock:%p skb:%p flags:%d src-addr:%p src-addrlen:%d dst-addr:%p dst-addrlen:%d)", + (void*)sock, (void*)skb, flags, (void*)src_addr, (int)src_addrlen, (void*)dst_addr, (int)dst_addrlen); + + if (PGM_UNLIKELY(sock->is_destroyed)) + return 0; + +#ifdef CONFIG_TARGET_WINE + socklen_t fromlen = src_addrlen; + const ssize_t len = recvfrom (sock->recv_sock, skb->head, sock->max_tpdu, 0, src_addr, &fromlen); + if (len <= 0) + return len; +#else + struct pgm_iovec iov = { + .iov_base = skb->head, + .iov_len = sock->max_tpdu + }; + char aux[ 1024 ]; +# ifndef _WIN32 + struct msghdr msg = { + .msg_name = src_addr, + .msg_namelen = src_addrlen, + .msg_iov = (void*)&iov, + .msg_iovlen = 1, + .msg_control = aux, + .msg_controllen = sizeof(aux), + .msg_flags = 0 + }; + + ssize_t len = recvmsg (sock->recv_sock, &msg, flags); + if (len <= 0) + return len; +# else /* !_WIN32 */ + WSAMSG msg = { + .name = (LPSOCKADDR)src_addr, + .namelen = src_addrlen, + .lpBuffers = (LPWSABUF)&iov, + .dwBufferCount = 1, + .dwFlags = 0 + }; + msg.Control.buf = aux; + msg.Control.len = sizeof(aux); + DWORD len; + if (SOCKET_ERROR == pgm_WSARecvMsg (sock->recv_sock, &msg, &len, NULL, NULL)) { + return -1; + } +# endif /* !_WIN32 */ +#endif /* !CONFIG_TARGET_WINE */ + +#ifdef PGM_DEBUG + if (PGM_UNLIKELY(pgm_loss_rate > 0)) { + const unsigned percent = pgm_rand_int_range (&sock->rand_, 0, 100); + if (percent <= pgm_loss_rate) { + pgm_debug ("Simulated packet loss"); +# ifndef _WIN32 + errno = EAGAIN; +# else + WSASetLastError (WSAEWOULDBLOCK); +# endif + return -1; + } + } +#endif + + skb->sock = sock; + skb->tstamp = pgm_time_update_now(); + skb->data = skb->head; + skb->len = len; + skb->zero_padded = 0; + skb->tail = (char*)skb->data + len; + +#ifdef CONFIG_TARGET_WINE + pgm_assert (pgm_sockaddr_len (&sock->recv_gsr[0].gsr_group) <= dst_addrlen); + memcpy (dst_addr, &sock->recv_gsr[0].gsr_group, pgm_sockaddr_len (&sock->recv_gsr[0].gsr_group)); +#else + if (sock->udp_encap_ucast_port || + AF_INET6 == pgm_sockaddr_family (src_addr)) + { +#ifdef CONFIG_HAVE_WSACMSGHDR + WSACMSGHDR* cmsg; +#else + struct cmsghdr* cmsg; +#endif + for (cmsg = CMSG_FIRSTHDR(&msg); + cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) + { +/* both IP_PKTINFO and IP_RECVDSTADDR exist on OpenSolaris, so capture + * each type if defined. + */ +#ifdef IP_PKTINFO + if (IPPROTO_IP == cmsg->cmsg_level && + IP_PKTINFO == cmsg->cmsg_type) + { + const void* pktinfo = CMSG_DATA(cmsg); +/* discard on invalid address */ + if (PGM_UNLIKELY(NULL == pktinfo)) { + pgm_debug ("in_pktinfo is NULL"); + return -1; + } + const struct in_pktinfo* in = pktinfo; + struct sockaddr_in s4; + memset (&s4, 0, sizeof(s4)); + s4.sin_family = AF_INET; + s4.sin_addr.s_addr = in->ipi_addr.s_addr; + memcpy (dst_addr, &s4, sizeof(s4)); + break; + } +#endif +#ifdef IP_RECVDSTADDR + if (IPPROTO_IP == cmsg->cmsg_level && + IP_RECVDSTADDR == cmsg->cmsg_type) + { + const void* recvdstaddr = CMSG_DATA(cmsg); +/* discard on invalid address */ + if (PGM_UNLIKELY(NULL == recvdstaddr)) { + pgm_debug ("in_recvdstaddr is NULL"); + return -1; + } + const struct in_addr* in = recvdstaddr; + struct sockaddr_in s4; + memset (&s4, 0, sizeof(s4)); + s4.sin_family = AF_INET; + s4.sin_addr.s_addr = in->s_addr; + memcpy (dst_addr, &s4, sizeof(s4)); + break; + } +#endif +#if !defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR) +# error "No defined CMSG type for IPv4 destination address." +#endif + + if (IPPROTO_IPV6 == cmsg->cmsg_level && + IPV6_PKTINFO == cmsg->cmsg_type) + { + const void* pktinfo = CMSG_DATA(cmsg); +/* discard on invalid address */ + if (PGM_UNLIKELY(NULL == pktinfo)) { + pgm_debug ("in6_pktinfo is NULL"); + return -1; + } + const struct in6_pktinfo* in6 = pktinfo; + struct sockaddr_in6 s6; + memset (&s6, 0, sizeof(s6)); + s6.sin6_family = AF_INET6; + s6.sin6_addr = in6->ipi6_addr; + s6.sin6_scope_id = in6->ipi6_ifindex; + memcpy (dst_addr, &s6, sizeof(s6)); +/* does not set flow id */ + break; + } + } + } +#endif + return len; +} + +/* upstream = receiver to source, peer-to-peer = receive to receiver + * + * NB: SPMRs can be upstream or peer-to-peer, if the packet is multicast then its + * a peer-to-peer message, if its unicast its an upstream message. + * + * returns TRUE on valid processed packet, returns FALSE on discarded packet. + */ + +static +bool +on_upstream ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + pgm_assert_cmpuint (skb->pgm_header->pgm_dport, ==, sock->tsi.sport); + + pgm_debug ("on_upstream (sock:%p skb:%p)", + (const void*)sock, (const void*)skb); + + if (PGM_UNLIKELY(!sock->can_send_data)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded packet for muted source.")); + goto out_discarded; + } + +/* unicast upstream message, note that dport & sport are reversed */ + if (PGM_UNLIKELY(skb->pgm_header->pgm_sport != sock->dport)) { +/* its upstream/peer-to-peer for another session */ + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded packet on data-destination port mismatch.")); + goto out_discarded; + } + + if (PGM_UNLIKELY(!pgm_gsi_equal (&skb->tsi.gsi, &sock->tsi.gsi))) { +/* its upstream/peer-to-peer for another session */ + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded packet on data-destination port mismatch.")); + goto out_discarded; + } + +/* advance SKB pointer to PGM type header */ + skb->data = (char*)skb->data + sizeof(struct pgm_header); + skb->len -= sizeof(struct pgm_header); + + switch (skb->pgm_header->pgm_type) { + case PGM_NAK: + if (PGM_UNLIKELY(!pgm_on_nak (sock, skb))) + goto out_discarded; + break; + + case PGM_NNAK: + if (PGM_UNLIKELY(!pgm_on_nnak (sock, skb))) + goto out_discarded; + break; + + case PGM_SPMR: + if (PGM_UNLIKELY(!pgm_on_spmr (sock, NULL, skb))) + goto out_discarded; + break; + + case PGM_ACK: + if (PGM_UNLIKELY(!pgm_on_ack (sock, skb))) + goto out_discarded; + break; + + case PGM_POLR: + default: + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded unsupported PGM type packet.")); + goto out_discarded; + } + + return TRUE; +out_discarded: + sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED]++; + return FALSE; +} + +/* peer to peer message, either multicast NAK or multicast SPMR. + * + * returns TRUE on valid processed packet, returns FALSE on discarded packet. + */ + +static +bool +on_peer ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t* const restrict skb, + pgm_peer_t** restrict source + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + pgm_assert_cmpuint (skb->pgm_header->pgm_dport, !=, sock->tsi.sport); + pgm_assert (NULL != source); + + pgm_debug ("on_peer (sock:%p skb:%p source:%p)", + (const void*)sock, (const void*)skb, (const void*)source); + +/* we are not the source */ + if (PGM_UNLIKELY(!sock->can_recv_data)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded packet for muted receiver.")); + goto out_discarded; + } + +/* unicast upstream message, note that dport & sport are reversed */ + if (PGM_UNLIKELY(skb->pgm_header->pgm_sport != sock->dport)) { +/* its upstream/peer-to-peer for another session */ + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded packet on data-destination port mismatch.")); + goto out_discarded; + } + +/* check to see the source this peer-to-peer message is about is in our peer list */ + pgm_tsi_t upstream_tsi; + memcpy (&upstream_tsi.gsi, &skb->tsi.gsi, sizeof(pgm_gsi_t)); + upstream_tsi.sport = skb->pgm_header->pgm_dport; + + pgm_rwlock_reader_lock (&sock->peers_lock); + *source = pgm_hashtable_lookup (sock->peers_hashtable, &upstream_tsi); + pgm_rwlock_reader_unlock (&sock->peers_lock); + if (PGM_UNLIKELY(NULL == *source)) { +/* this source is unknown, we don't care about messages about it */ + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded peer packet about new source.")); + goto out_discarded; + } + +/* advance SKB pointer to PGM type header */ + skb->data = (char*)skb->data + sizeof(struct pgm_header); + skb->len -= sizeof(struct pgm_header); + + switch (skb->pgm_header->pgm_type) { + case PGM_NAK: + if (PGM_UNLIKELY(!pgm_on_peer_nak (sock, *source, skb))) + goto out_discarded; + break; + + case PGM_SPMR: + if (PGM_UNLIKELY(!pgm_on_spmr (sock, *source, skb))) + goto out_discarded; + break; + + case PGM_NNAK: + case PGM_POLR: + default: + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded unsupported PGM type packet.")); + goto out_discarded; + } + + return TRUE; +out_discarded: + if (*source) + (*source)->cumulative_stats[PGM_PC_RECEIVER_PACKETS_DISCARDED]++; + else if (sock->can_send_data) + sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED]++; + return FALSE; +} + +/* source to receiver message + * + * returns TRUE on valid processed packet, returns FALSE on discarded packet. + */ + +static +bool +on_downstream ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t* const restrict skb, + struct sockaddr* const restrict src_addr, + struct sockaddr* const restrict dst_addr, + pgm_peer_t** restrict source + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + pgm_assert (NULL != src_addr); + pgm_assert (NULL != dst_addr); + pgm_assert (NULL != source); + +#ifdef RECV_DEBUG + char saddr[INET6_ADDRSTRLEN], daddr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop (src_addr, saddr, sizeof(saddr)); + pgm_sockaddr_ntop (dst_addr, daddr, sizeof(daddr)); + pgm_debug ("on_downstream (sock:%p skb:%p src-addr:%s dst-addr:%s source:%p)", + (const void*)sock, (const void*)skb, saddr, daddr, (const void*)source); +#endif + + if (PGM_UNLIKELY(!sock->can_recv_data)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded packet for muted receiver.")); + goto out_discarded; + } + +/* pgm packet DPORT contains our sock DPORT */ + if (PGM_UNLIKELY(skb->pgm_header->pgm_dport != sock->dport)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded packet on data-destination port mismatch.")); + goto out_discarded; + } + +/* search for TSI peer context or create a new one */ + if (PGM_LIKELY(pgm_tsi_hash (&skb->tsi) == sock->last_hash_key && + NULL != sock->last_hash_value)) + { + *source = sock->last_hash_value; + } + else + { + pgm_rwlock_reader_lock (&sock->peers_lock); + *source = pgm_hashtable_lookup_extended (sock->peers_hashtable, &skb->tsi, &sock->last_hash_key); + pgm_rwlock_reader_unlock (&sock->peers_lock); + if (PGM_UNLIKELY(NULL == *source)) { + *source = pgm_new_peer (sock, + &skb->tsi, + (struct sockaddr*)src_addr, pgm_sockaddr_len(src_addr), + (struct sockaddr*)dst_addr, pgm_sockaddr_len(dst_addr), + skb->tstamp); + } + sock->last_hash_value = *source; + } + + (*source)->cumulative_stats[PGM_PC_RECEIVER_BYTES_RECEIVED] += skb->len; + (*source)->last_packet = skb->tstamp; + + skb->data = (void*)( skb->pgm_header + 1 ); + skb->len -= sizeof(struct pgm_header); + +/* handle PGM packet type */ + switch (skb->pgm_header->pgm_type) { + case PGM_ODATA: + case PGM_RDATA: + if (PGM_UNLIKELY(!pgm_on_data (sock, *source, skb))) + goto out_discarded; + sock->rx_buffer = pgm_alloc_skb (sock->max_tpdu); + break; + + case PGM_NCF: + if (PGM_UNLIKELY(!pgm_on_ncf (sock, *source, skb))) + goto out_discarded; + break; + + case PGM_SPM: + if (PGM_UNLIKELY(!pgm_on_spm (sock, *source, skb))) + goto out_discarded; + +/* update group NLA if appropriate */ + if (PGM_LIKELY(pgm_sockaddr_is_addr_multicast ((struct sockaddr*)dst_addr))) + memcpy (&(*source)->group_nla, dst_addr, pgm_sockaddr_len(dst_addr)); + break; + +#ifdef CONFIG_PGM_POLLING + case PGM_POLL: + if (PGM_UNLIKELY(!pgm_on_poll (sock, *source, skb))) + goto out_discarded; + break; +#endif + + default: + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded unsupported PGM type packet.")); + goto out_discarded; + } + + return TRUE; +out_discarded: + if (*source) + (*source)->cumulative_stats[PGM_PC_RECEIVER_PACKETS_DISCARDED]++; + else if (sock->can_send_data) + sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED]++; + return FALSE; +} + +/* process a pgm packet + * + * returns TRUE on valid processed packet, returns FALSE on discarded packet. + */ +static +bool +on_pgm ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t* const restrict skb, + struct sockaddr* const restrict src_addr, + struct sockaddr* const restrict dst_addr, + pgm_peer_t** restrict source + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + pgm_assert (NULL != src_addr); + pgm_assert (NULL != dst_addr); + pgm_assert (NULL != source); + +#ifdef RECV_DEBUG + char saddr[INET6_ADDRSTRLEN], daddr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop (src_addr, saddr, sizeof(saddr)); + pgm_sockaddr_ntop (dst_addr, daddr, sizeof(daddr)); + pgm_debug ("on_pgm (sock:%p skb:%p src-addr:%s dst-addr:%s source:%p)", + (const void*)sock, (const void*)skb, saddr, daddr, (const void*)source); +#endif + + if (PGM_IS_DOWNSTREAM (skb->pgm_header->pgm_type)) + return on_downstream (sock, skb, src_addr, dst_addr, source); + if (skb->pgm_header->pgm_dport == sock->tsi.sport) + { + if (PGM_IS_UPSTREAM (skb->pgm_header->pgm_type) || + PGM_IS_PEER (skb->pgm_header->pgm_type)) + { + return on_upstream (sock, skb); + } + } + else if (PGM_IS_PEER (skb->pgm_header->pgm_type)) + return on_peer (sock, skb, source); + + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Discarded PGM packet.")); + if (sock->can_send_data) + sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED]++; + return FALSE; +} + +/* block on receiving socket whilst holding sock::waiting-mutex + * returns EAGAIN for waiting data, returns EINTR for waiting timer event, + * returns ENOENT on closed sock, and returns EFAULT for libc error. + */ + +static +int +wait_for_event ( + pgm_sock_t* const sock + ) +{ + int n_fds = 3; + +/* pre-conditions */ + pgm_assert (NULL != sock); + + pgm_debug ("wait_for_event (sock:%p)", (const void*)sock); + + do { + if (PGM_UNLIKELY(sock->is_destroyed)) + return ENOENT; + + if (sock->can_send_data && !pgm_txw_retransmit_is_empty (sock->window)) +/* tight loop on blocked send */ + pgm_on_deferred_nak (sock); + +#ifdef CONFIG_HAVE_POLL + struct pollfd fds[ n_fds ]; + memset (fds, 0, sizeof(fds)); + const int status = pgm_poll_info (sock, fds, &n_fds, POLLIN); + pgm_assert (-1 != status); +#else + fd_set readfds; + FD_ZERO(&readfds); + const int status = pgm_select_info (sock, &readfds, NULL, &n_fds); + pgm_assert (-1 != status); +#endif /* CONFIG_HAVE_POLL */ + +/* flush any waiting notifications */ + if (sock->is_pending_read) { + pgm_notify_clear (&sock->pending_notify); + sock->is_pending_read = FALSE; + } + + int timeout; + if (sock->can_send_data && !pgm_txw_retransmit_is_empty (sock->window)) + timeout = 0; + else + timeout = pgm_timer_expiration (sock); + +#ifdef CONFIG_HAVE_POLL + const int ready = poll (fds, n_fds, timeout /* μs */ / 1000 /* to ms */); +#else + struct timeval tv_timeout = { + .tv_sec = timeout > 1000000L ? timeout / 1000000UL : 0, + .tv_usec = timeout > 1000000L ? timeout % 1000000UL : timeout + }; + const int ready = select (n_fds, &readfds, NULL, NULL, &tv_timeout); +#endif + if (PGM_UNLIKELY(-1 == ready)) { + pgm_debug ("block returned errno=%i",errno); + return EFAULT; + } else if (ready > 0) { + pgm_debug ("recv again on empty"); + return EAGAIN; + } + } while (pgm_timer_check (sock)); + pgm_debug ("state generated event"); + return EINTR; +} + +/* data incoming on receive sockets, can be from a sender or receiver, or simply bogus. + * for IPv4 we receive the IP header to handle fragmentation, for IPv6 we cannot, but the + * underlying stack handles this for us. + * + * recvmsgv reads a vector of apdus each contained in a IO scatter/gather array. + * + * can be called due to event from incoming socket(s) or timer induced data loss. + * + * On success, returns PGM_IO_STATUS_NORMAL and saves the count of bytes read + * into _bytes_read. With non-blocking sockets a block returns + * PGM_IO_STATUS_WOULD_BLOCK. When rate limited sending repair data, returns + * PGM_IO_STATUS_RATE_LIMITED and caller should wait. During recovery state, + * returns PGM_IO_STATUS_TIMER_PENDING and caller should also wait. On + * unrecoverable dataloss, returns PGM_IO_STATUS_CONN_RESET. If connection is + * closed, returns PGM_IO_STATUS_EOF. On error, returns PGM_IO_STATUS_ERROR. + */ + +int +pgm_recvmsgv ( + pgm_sock_t* const restrict sock, + struct pgm_msgv_t* const restrict msg_start, + const size_t msg_len, + const int flags, /* MSG_DONTWAIT for non-blocking */ + size_t* restrict _bytes_read, /* may be NULL */ + pgm_error_t** restrict error + ) +{ + int status = PGM_IO_STATUS_WOULD_BLOCK; + + pgm_debug ("pgm_recvmsgv (sock:%p msg-start:%p msg-len:%zu flags:%d bytes-read:%p error:%p)", + (void*)sock, (void*)msg_start, msg_len, flags, (void*)_bytes_read, (void*)error); + +/* parameters */ + pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); + if (PGM_LIKELY(msg_len)) pgm_return_val_if_fail (NULL != msg_start, PGM_IO_STATUS_ERROR); + +/* shutdown */ + if (PGM_UNLIKELY(!pgm_rwlock_reader_trylock (&sock->lock))) + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + +/* state */ + if (PGM_UNLIKELY(!sock->is_bound || sock->is_destroyed)) + { + pgm_rwlock_reader_unlock (&sock->lock); + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + } + +/* pre-conditions */ + pgm_assert (NULL != sock->rx_buffer); + pgm_assert (sock->max_tpdu > 0); + if (sock->can_recv_data) { + pgm_assert (NULL != sock->peers_hashtable); + pgm_assert_cmpuint (sock->nak_bo_ivl, >, 1); + pgm_assert (pgm_notify_is_valid (&sock->pending_notify)); + } + +/* receiver */ + pgm_mutex_lock (&sock->receiver_mutex); + + if (PGM_UNLIKELY(sock->is_reset)) { + pgm_assert (NULL != sock->peers_pending); + pgm_assert (NULL != sock->peers_pending->data); + pgm_peer_t* peer = sock->peers_pending->data; + if (flags & MSG_ERRQUEUE) + pgm_set_reset_error (sock, peer, msg_start); + else if (error) { + char tsi[PGM_TSISTRLEN]; + pgm_tsi_print_r (&peer->tsi, tsi, sizeof(tsi)); + pgm_set_error (error, + PGM_ERROR_DOMAIN_RECV, + PGM_ERROR_CONNRESET, + _("Transport has been reset on unrecoverable loss from %s."), + tsi); + } + if (!sock->is_abort_on_reset) + sock->is_reset = !sock->is_reset; + pgm_mutex_unlock (&sock->receiver_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_RESET; + } + +/* timer status */ + if (pgm_timer_check (sock) && + !pgm_timer_dispatch (sock)) + { +/* block on send-in-recv */ + status = PGM_IO_STATUS_RATE_LIMITED; + } +/* NAK status */ + else if (sock->can_send_data) + { + if (!pgm_txw_retransmit_is_empty (sock->window)) + { + if (!pgm_on_deferred_nak (sock)) + status = PGM_IO_STATUS_RATE_LIMITED; + } + else + pgm_notify_clear (&sock->rdata_notify); + } + + size_t bytes_read = 0; + unsigned data_read = 0; + struct pgm_msgv_t* pmsg = msg_start; + const struct pgm_msgv_t* msg_end = msg_start + msg_len - 1; + + if (PGM_UNLIKELY(0 == ++(sock->last_commit))) + ++(sock->last_commit); + + /* second, flush any remaining contiguous messages from previous call(s) */ + if (sock->peers_pending) { + if (0 != pgm_flush_peers_pending (sock, &pmsg, msg_end, &bytes_read, &data_read)) + goto out; +/* returns on: reset or full buffer */ + } + +/* read the data: + * + * We cannot actually block here as packets pushed by the timers need to be addressed too. + */ + struct sockaddr_storage src, dst; + ssize_t len; + size_t bytes_received = 0; + +recv_again: + + len = recvskb (sock, + sock->rx_buffer, /* PGM skbuff */ + 0, + (struct sockaddr*)&src, + sizeof(src), + (struct sockaddr*)&dst, + sizeof(dst)); + if (len < 0) + { +#ifndef _WIN32 + const int save_errno = errno; + if (PGM_LIKELY(EAGAIN == save_errno)) { + goto check_for_repeat; + } + status = PGM_IO_STATUS_ERROR; + pgm_set_error (error, + PGM_ERROR_DOMAIN_RECV, + pgm_error_from_errno (save_errno), + _("Transport socket error: %s"), + strerror (save_errno)); +#else + const int save_wsa_errno = WSAGetLastError (); + if (PGM_LIKELY(WSAEWOULDBLOCK == save_wsa_errno)) { + goto check_for_repeat; + } + status = PGM_IO_STATUS_ERROR; + pgm_set_error (error, + PGM_ERROR_DOMAIN_RECV, + pgm_error_from_wsa_errno (save_wsa_errno), + _("Transport socket error: %s"), + pgm_wsastrerror (save_wsa_errno)); +#endif /* !_WIN32 */ + goto out; + } + else if (0 == len) + { +/* cannot return NORMAL/0 as that is valid payload with SKB */ + status = PGM_IO_STATUS_EOF; + goto out; + } + else + { + bytes_received += len; + } + + pgm_error_t* err = NULL; + const bool is_valid = (sock->udp_encap_ucast_port || AF_INET6 == src.ss_family) ? + pgm_parse_udp_encap (sock->rx_buffer, &err) : + pgm_parse_raw (sock->rx_buffer, (struct sockaddr*)&dst, &err); + if (PGM_UNLIKELY(!is_valid)) + { +/* inherently cannot determine PGM_PC_RECEIVER_CKSUM_ERRORS unless only one receiver */ + pgm_trace (PGM_LOG_ROLE_NETWORK, + _("Discarded invalid packet: %s"), + (err && err->message) ? err->message : "(null)"); + pgm_error_free (err); + if (sock->can_send_data) { + if (err && PGM_ERROR_CKSUM == err->code) + sock->cumulative_stats[PGM_PC_SOURCE_CKSUM_ERRORS]++; + sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED]++; + } + goto recv_again; + } + + pgm_peer_t* source = NULL; + if (PGM_UNLIKELY(!on_pgm (sock, sock->rx_buffer, (struct sockaddr*)&src, (struct sockaddr*)&dst, &source))) + goto recv_again; + +/* check whether this source has waiting data */ + if (source && pgm_peer_has_pending (source)) { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("New pending data.")); + pgm_peer_set_pending (sock, source); + } + +flush_pending: +/* flush any congtiguous packets generated by the receipt of this packet */ + if (sock->peers_pending) + { + if (0 != pgm_flush_peers_pending (sock, &pmsg, msg_end, &bytes_read, &data_read)) + { +/* recv vector is now full */ + goto out; + } + } + +check_for_repeat: +/* repeat if non-blocking and not full */ + if (sock->is_nonblocking || + flags & MSG_DONTWAIT) + { + if (len > 0 && pmsg <= msg_end) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Recv again on not-full")); + goto recv_again; /* \:D/ */ + } + } + else + { +/* repeat if blocking and empty, i.e. received non data packet. + */ + if (0 == data_read) { + const int wait_status = wait_for_event (sock); + switch (wait_status) { + case EAGAIN: + goto recv_again; + case EINTR: + if (!pgm_timer_dispatch (sock)) + goto check_for_repeat; + goto flush_pending; + case ENOENT: + pgm_mutex_unlock (&sock->receiver_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_EOF; + case EFAULT: + pgm_set_error (error, + PGM_ERROR_DOMAIN_RECV, + pgm_error_from_errno (errno), + _("Waiting for event: %s"), +#ifndef _WIN32 + strerror (errno) +#else + pgm_wsastrerror (WSAGetLastError()) /* from select() */ +#endif + ); + pgm_mutex_unlock (&sock->receiver_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_ERROR; + default: + pgm_assert_not_reached(); + } + } + } + +out: + if (0 == data_read) + { +/* clear event notification */ + if (sock->is_pending_read) { + pgm_notify_clear (&sock->pending_notify); + sock->is_pending_read = FALSE; + } +/* report data loss */ + if (PGM_UNLIKELY(sock->is_reset)) { + pgm_assert (NULL != sock->peers_pending); + pgm_assert (NULL != sock->peers_pending->data); + pgm_peer_t* peer = sock->peers_pending->data; + if (flags & MSG_ERRQUEUE) + pgm_set_reset_error (sock, peer, msg_start); + else if (error) { + char tsi[PGM_TSISTRLEN]; + pgm_tsi_print_r (&peer->tsi, tsi, sizeof(tsi)); + pgm_set_error (error, + PGM_ERROR_DOMAIN_RECV, + PGM_ERROR_CONNRESET, + _("Transport has been reset on unrecoverable loss from %s."), + tsi); + } + if (!sock->is_abort_on_reset) + sock->is_reset = !sock->is_reset; + pgm_mutex_unlock (&sock->receiver_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_RESET; + } + pgm_mutex_unlock (&sock->receiver_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + if (PGM_IO_STATUS_WOULD_BLOCK == status && + ( sock->can_send_data || + ( sock->can_recv_data && NULL != sock->peers_list ))) + { + status = PGM_IO_STATUS_TIMER_PENDING; + } + return status; + } + + if (sock->peers_pending) + { +/* set event notification for additional available data */ + if (sock->is_pending_read && sock->is_edge_triggered_recv) + { +/* empty pending-pipe */ + pgm_notify_clear (&sock->pending_notify); + sock->is_pending_read = FALSE; + } + else if (!sock->is_pending_read && !sock->is_edge_triggered_recv) + { +/* fill pending-pipe */ + pgm_notify_send (&sock->pending_notify); + sock->is_pending_read = TRUE; + } + } + + if (NULL != _bytes_read) + *_bytes_read = bytes_read; + pgm_mutex_unlock (&sock->receiver_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_NORMAL; +} + +/* read one contiguous apdu and return as a IO scatter/gather array. msgv is owned by + * the caller, tpdu contents are owned by the receive window. + * + * on success, returns PGM_IO_STATUS_NORMAL. + */ + +int +pgm_recvmsg ( + pgm_sock_t* const restrict sock, + struct pgm_msgv_t* const restrict msgv, + const int flags, /* MSG_DONTWAIT for non-blocking */ + size_t* restrict bytes_read, /* may be NULL */ + pgm_error_t** restrict error + ) +{ + pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); + pgm_return_val_if_fail (NULL != msgv, PGM_IO_STATUS_ERROR); + + pgm_debug ("pgm_recvmsg (sock:%p msgv:%p flags:%d bytes_read:%p error:%p)", + (const void*)sock, (const void*)msgv, flags, (const void*)bytes_read, (const void*)error); + + return pgm_recvmsgv (sock, msgv, 1, flags, bytes_read, error); +} + +/* vanilla read function. copies from the receive window to the provided buffer + * location. the caller must provide an adequately sized buffer to store the largest + * expected apdu or else it will be truncated. + * + * on success, returns PGM_IO_STATUS_NORMAL. + */ + +int +pgm_recvfrom ( + pgm_sock_t* const restrict sock, + void* restrict buf, + const size_t buflen, + const int flags, /* MSG_DONTWAIT for non-blocking */ + size_t* restrict _bytes_read, /* may be NULL */ + struct pgm_sockaddr_t* restrict from, /* may be NULL */ + socklen_t* restrict fromlen, + pgm_error_t** restrict error + ) +{ + struct pgm_msgv_t msgv; + size_t bytes_read = 0; + + pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); + if (PGM_LIKELY(buflen)) pgm_return_val_if_fail (NULL != buf, PGM_IO_STATUS_ERROR); + if (fromlen) { + pgm_return_val_if_fail (NULL != from, PGM_IO_STATUS_ERROR); + pgm_return_val_if_fail (sizeof (struct pgm_sockaddr_t) == *fromlen, PGM_IO_STATUS_ERROR); + } + + pgm_debug ("pgm_recvfrom (sock:%p buf:%p buflen:%zu flags:%d bytes-read:%p from:%p from:%p error:%p)", + (const void*)sock, buf, buflen, flags, (const void*)_bytes_read, (const void*)from, (const void*)fromlen, (const void*)error); + + const int status = pgm_recvmsg (sock, &msgv, flags & ~(MSG_ERRQUEUE), &bytes_read, error); + if (PGM_IO_STATUS_NORMAL != status) + return status; + + size_t bytes_copied = 0; + struct pgm_sk_buff_t** skb = msgv.msgv_skb; + struct pgm_sk_buff_t* pskb = *skb; + + if (from) { + from->sa_port = ntohs (sock->dport); + from->sa_addr.sport = ntohs (pskb->tsi.sport); + memcpy (&from->sa_addr.gsi, &pskb->tsi.gsi, sizeof(pgm_gsi_t)); + } + + while (bytes_copied < bytes_read) { + size_t copy_len = pskb->len; + if (bytes_copied + copy_len > buflen) { + pgm_warn (_("APDU truncated, original length %zu bytes."), + bytes_read); + copy_len = buflen - bytes_copied; + bytes_read = buflen; + } + memcpy ((char*)buf + bytes_copied, pskb->data, copy_len); + bytes_copied += copy_len; + pskb = *(++skb); + } + if (_bytes_read) + *_bytes_read = bytes_copied; + return PGM_IO_STATUS_NORMAL; +} + +/* Basic recv operation, copying data from window to application. + * + * on success, returns PGM_IO_STATUS_NORMAL. + */ + +int +pgm_recv ( + pgm_sock_t* const restrict sock, + void* restrict buf, + const size_t buflen, + const int flags, /* MSG_DONTWAIT for non-blocking */ + size_t* const restrict bytes_read, /* may be NULL */ + pgm_error_t** restrict error + ) +{ + pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); + if (PGM_LIKELY(buflen)) pgm_return_val_if_fail (NULL != buf, PGM_IO_STATUS_ERROR); + + pgm_debug ("pgm_recv (sock:%p buf:%p buflen:%zu flags:%d bytes-read:%p error:%p)", + (const void*)sock, buf, buflen, flags, (const void*)bytes_read, (const void*)error); + + return pgm_recvfrom (sock, buf, buflen, flags, bytes_read, NULL, NULL, error); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/recv.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/recv.c.c89.patch new file mode 100644 index 0000000..171c74c --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/recv.c.c89.patch @@ -0,0 +1,404 @@ +--- recv.c 2010-08-04 16:33:15.000000000 +0800 ++++ recv.c89 2010-08-05 11:37:10.000000000 +0800 +@@ -45,12 +45,11 @@ + + #ifdef _WIN32 + # define cmsghdr wsacmsghdr +-# define CMSG_FIRSTHDR(msg) WSA_CMSG_FIRSTHDR(msg) +-# define CMSG_NXTHDR(msg, cmsg) WSA_CMSG_NXTHDR(msg, cmsg) +-# define CMSG_DATA(cmsg) WSA_CMSG_DATA(cmsg) +-# define CMSG_SPACE(len) WSA_CMSG_SPACE(len) +-# define CMSG_LEN(len) WSA_CMSG_LEN(len) +-# endif ++# define PGM_CMSG_FIRSTHDR(msg) WSA_CMSG_FIRSTHDR(msg) ++# define PGM_CMSG_NXTHDR(msg, cmsg) WSA_CMSG_NXTHDR(msg, cmsg) ++# define PGM_CMSG_DATA(cmsg) WSA_CMSG_DATA(cmsg) ++# define PGM_CMSG_SPACE(len) WSA_CMSG_SPACE(len) ++# define PGM_CMSG_LEN(len) WSA_CMSG_LEN(len) + #endif + + +@@ -91,11 +90,11 @@ + if (len <= 0) + return len; + #else +- struct pgm_iovec iov = { +- .iov_base = skb->head, +- .iov_len = sock->max_tpdu +- }; ++ { ++ struct pgm_iovec iov; + char aux[ 1024 ]; ++ iov.iov_base = skb->head; ++ iov.iov_len = sock->max_tpdu; + # ifndef _WIN32 + struct msghdr msg = { + .msg_name = src_addr, +@@ -111,15 +110,16 @@ + if (len <= 0) + return len; + # else /* !_WIN32 */ +- WSAMSG msg = { +- .name = (LPSOCKADDR)src_addr, +- .namelen = src_addrlen, +- .lpBuffers = (LPWSABUF)&iov, +- .dwBufferCount = 1, +- .dwFlags = 0 +- }; ++ { ++ WSAMSG msg; ++ msg.name = (LPSOCKADDR)src_addr; ++ msg.namelen = src_addrlen; ++ msg.lpBuffers = (LPWSABUF)&iov; ++ msg.dwBufferCount = 1; ++ msg.dwFlags = 0; + msg.Control.buf = aux; + msg.Control.len = sizeof(aux); ++ { + DWORD len; + if (SOCKET_ERROR == pgm_WSARecvMsg (sock->recv_sock, &msg, &len, NULL, NULL)) { + return -1; +@@ -145,7 +145,7 @@ + skb->sock = sock; + skb->tstamp = pgm_time_update_now(); + skb->data = skb->head; +- skb->len = len; ++ skb->len = (uint16_t)len; + skb->zero_padded = 0; + skb->tail = (char*)skb->data + len; + +@@ -161,9 +161,9 @@ + #else + struct cmsghdr* cmsg; + #endif +- for (cmsg = CMSG_FIRSTHDR(&msg); ++ for (cmsg = PGM_CMSG_FIRSTHDR(&msg); + cmsg != NULL; +- cmsg = CMSG_NXTHDR(&msg, cmsg)) ++ cmsg = PGM_CMSG_NXTHDR(&msg, cmsg)) + { + /* both IP_PKTINFO and IP_RECVDSTADDR exist on OpenSolaris, so capture + * each type if defined. +@@ -172,12 +172,13 @@ + if (IPPROTO_IP == cmsg->cmsg_level && + IP_PKTINFO == cmsg->cmsg_type) + { +- const void* pktinfo = CMSG_DATA(cmsg); ++ const void* pktinfo = PGM_CMSG_DATA(cmsg); + /* discard on invalid address */ + if (PGM_UNLIKELY(NULL == pktinfo)) { + pgm_debug ("in_pktinfo is NULL"); + return -1; + } ++ { + const struct in_pktinfo* in = pktinfo; + struct sockaddr_in s4; + memset (&s4, 0, sizeof(s4)); +@@ -185,18 +186,20 @@ + s4.sin_addr.s_addr = in->ipi_addr.s_addr; + memcpy (dst_addr, &s4, sizeof(s4)); + break; ++ } + } + #endif + #ifdef IP_RECVDSTADDR + if (IPPROTO_IP == cmsg->cmsg_level && + IP_RECVDSTADDR == cmsg->cmsg_type) + { +- const void* recvdstaddr = CMSG_DATA(cmsg); ++ const void* recvdstaddr = PGM_CMSG_DATA(cmsg); + /* discard on invalid address */ + if (PGM_UNLIKELY(NULL == recvdstaddr)) { + pgm_debug ("in_recvdstaddr is NULL"); + return -1; + } ++ { + const struct in_addr* in = recvdstaddr; + struct sockaddr_in s4; + memset (&s4, 0, sizeof(s4)); +@@ -204,6 +207,7 @@ + s4.sin_addr.s_addr = in->s_addr; + memcpy (dst_addr, &s4, sizeof(s4)); + break; ++ } + } + #endif + #if !defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR) +@@ -213,12 +217,13 @@ + if (IPPROTO_IPV6 == cmsg->cmsg_level && + IPV6_PKTINFO == cmsg->cmsg_type) + { +- const void* pktinfo = CMSG_DATA(cmsg); ++ const void* pktinfo = PGM_CMSG_DATA(cmsg); + /* discard on invalid address */ + if (PGM_UNLIKELY(NULL == pktinfo)) { + pgm_debug ("in6_pktinfo is NULL"); + return -1; + } ++ { + const struct in6_pktinfo* in6 = pktinfo; + struct sockaddr_in6 s6; + memset (&s6, 0, sizeof(s6)); +@@ -228,11 +233,15 @@ + memcpy (dst_addr, &s6, sizeof(s6)); + /* does not set flow id */ + break; ++ } + } + } + } + #endif + return len; ++ } ++ } ++ } + } + + /* upstream = receiver to source, peer-to-peer = receive to receiver +@@ -349,6 +358,7 @@ + } + + /* check to see the source this peer-to-peer message is about is in our peer list */ ++ { + pgm_tsi_t upstream_tsi; + memcpy (&upstream_tsi.gsi, &skb->tsi.gsi, sizeof(pgm_gsi_t)); + upstream_tsi.sport = skb->pgm_header->pgm_dport; +@@ -391,6 +401,7 @@ + else if (sock->can_send_data) + sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED]++; + return FALSE; ++ } + } + + /* source to receiver message +@@ -416,11 +427,13 @@ + pgm_assert (NULL != source); + + #ifdef RECV_DEBUG ++ { + char saddr[INET6_ADDRSTRLEN], daddr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop (src_addr, saddr, sizeof(saddr)); + pgm_sockaddr_ntop (dst_addr, daddr, sizeof(daddr)); + pgm_debug ("on_downstream (sock:%p skb:%p src-addr:%s dst-addr:%s source:%p)", + (const void*)sock, (const void*)skb, saddr, daddr, (const void*)source); ++ } + #endif + + if (PGM_UNLIKELY(!sock->can_recv_data)) { +@@ -527,11 +540,13 @@ + pgm_assert (NULL != source); + + #ifdef RECV_DEBUG ++ { + char saddr[INET6_ADDRSTRLEN], daddr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop (src_addr, saddr, sizeof(saddr)); + pgm_sockaddr_ntop (dst_addr, daddr, sizeof(daddr)); + pgm_debug ("on_pgm (sock:%p skb:%p src-addr:%s dst-addr:%s source:%p)", + (const void*)sock, (const void*)skb, saddr, daddr, (const void*)source); ++ } + #endif + + if (PGM_IS_DOWNSTREAM (skb->pgm_header->pgm_type)) +@@ -585,8 +600,10 @@ + const int status = pgm_poll_info (sock, fds, &n_fds, POLLIN); + pgm_assert (-1 != status); + #else ++ { + fd_set readfds; + FD_ZERO(&readfds); ++ { + const int status = pgm_select_info (sock, &readfds, NULL, &n_fds); + pgm_assert (-1 != status); + #endif /* CONFIG_HAVE_POLL */ +@@ -597,19 +614,21 @@ + sock->is_pending_read = FALSE; + } + ++ { + int timeout; + if (sock->can_send_data && !pgm_txw_retransmit_is_empty (sock->window)) + timeout = 0; + else +- timeout = pgm_timer_expiration (sock); ++ timeout = (int)pgm_timer_expiration (sock); + + #ifdef CONFIG_HAVE_POLL + const int ready = poll (fds, n_fds, timeout /* μs */ / 1000 /* to ms */); + #else +- struct timeval tv_timeout = { +- .tv_sec = timeout > 1000000L ? timeout / 1000000UL : 0, +- .tv_usec = timeout > 1000000L ? timeout % 1000000UL : timeout +- }; ++ { ++ struct timeval tv_timeout; ++ tv_timeout.tv_sec = timeout > 1000000L ? timeout / 1000000UL : 0; ++ tv_timeout.tv_usec = timeout > 1000000L ? timeout % 1000000UL : timeout; ++ { + const int ready = select (n_fds, &readfds, NULL, NULL, &tv_timeout); + #endif + if (PGM_UNLIKELY(-1 == ready)) { +@@ -619,6 +638,11 @@ + pgm_debug ("recv again on empty"); + return EAGAIN; + } ++ } ++ } ++ } ++ } ++ } + } while (pgm_timer_check (sock)); + pgm_debug ("state generated event"); + return EINTR; +@@ -653,7 +677,7 @@ + { + int status = PGM_IO_STATUS_WOULD_BLOCK; + +- pgm_debug ("pgm_recvmsgv (sock:%p msg-start:%p msg-len:%zu flags:%d bytes-read:%p error:%p)", ++ pgm_debug ("pgm_recvmsgv (sock:%p msg-start:%p msg-len:%lu flags:%d bytes-read:%p error:%p)", + (void*)sock, (void*)msg_start, msg_len, flags, (void*)_bytes_read, (void*)error); + + /* parameters */ +@@ -676,7 +700,7 @@ + pgm_assert (sock->max_tpdu > 0); + if (sock->can_recv_data) { + pgm_assert (NULL != sock->peers_hashtable); +- pgm_assert_cmpuint (sock->nak_bo_ivl, >, 1); ++ pgm_assert_cmpuint ((unsigned int)sock->nak_bo_ivl, >, 1); + pgm_assert (pgm_notify_is_valid (&sock->pending_notify)); + } + +@@ -686,6 +710,7 @@ + if (PGM_UNLIKELY(sock->is_reset)) { + pgm_assert (NULL != sock->peers_pending); + pgm_assert (NULL != sock->peers_pending->data); ++ { + pgm_peer_t* peer = sock->peers_pending->data; + if (flags & MSG_ERRQUEUE) + pgm_set_reset_error (sock, peer, msg_start); +@@ -703,6 +728,7 @@ + pgm_mutex_unlock (&sock->receiver_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_RESET; ++ } + } + + /* timer status */ +@@ -724,6 +750,7 @@ + pgm_notify_clear (&sock->rdata_notify); + } + ++ { + size_t bytes_read = 0; + unsigned data_read = 0; + struct pgm_msgv_t* pmsg = msg_start; +@@ -743,6 +770,7 @@ + * + * We cannot actually block here as packets pushed by the timers need to be addressed too. + */ ++ { + struct sockaddr_storage src, dst; + ssize_t len; + size_t bytes_received = 0; +@@ -770,6 +798,7 @@ + _("Transport socket error: %s"), + strerror (save_errno)); + #else ++ { + const int save_wsa_errno = WSAGetLastError (); + if (PGM_LIKELY(WSAEWOULDBLOCK == save_wsa_errno)) { + goto check_for_repeat; +@@ -780,6 +809,7 @@ + pgm_error_from_wsa_errno (save_wsa_errno), + _("Transport socket error: %s"), + pgm_wsastrerror (save_wsa_errno)); ++ } + #endif /* !_WIN32 */ + goto out; + } +@@ -794,6 +824,7 @@ + bytes_received += len; + } + ++ { + pgm_error_t* err = NULL; + const bool is_valid = (sock->udp_encap_ucast_port || AF_INET6 == src.ss_family) ? + pgm_parse_udp_encap (sock->rx_buffer, &err) : +@@ -813,6 +844,7 @@ + goto recv_again; + } + ++ { + pgm_peer_t* source = NULL; + if (PGM_UNLIKELY(!on_pgm (sock, sock->rx_buffer, (struct sockaddr*)&src, (struct sockaddr*)&dst, &source))) + goto recv_again; +@@ -893,6 +925,7 @@ + if (PGM_UNLIKELY(sock->is_reset)) { + pgm_assert (NULL != sock->peers_pending); + pgm_assert (NULL != sock->peers_pending->data); ++ { + pgm_peer_t* peer = sock->peers_pending->data; + if (flags & MSG_ERRQUEUE) + pgm_set_reset_error (sock, peer, msg_start); +@@ -910,6 +943,7 @@ + pgm_mutex_unlock (&sock->receiver_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_RESET; ++ } + } + pgm_mutex_unlock (&sock->receiver_mutex); + pgm_rwlock_reader_unlock (&sock->lock); +@@ -944,6 +978,10 @@ + pgm_mutex_unlock (&sock->receiver_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_NORMAL; ++ } ++ } ++ } ++ } + } + + /* read one contiguous apdu and return as a IO scatter/gather array. msgv is owned by +@@ -999,13 +1037,15 @@ + pgm_return_val_if_fail (sizeof (struct pgm_sockaddr_t) == *fromlen, PGM_IO_STATUS_ERROR); + } + +- pgm_debug ("pgm_recvfrom (sock:%p buf:%p buflen:%zu flags:%d bytes-read:%p from:%p from:%p error:%p)", ++ pgm_debug ("pgm_recvfrom (sock:%p buf:%p buflen:%lu flags:%d bytes-read:%p from:%p from:%p error:%p)", + (const void*)sock, buf, buflen, flags, (const void*)_bytes_read, (const void*)from, (const void*)fromlen, (const void*)error); + ++ { + const int status = pgm_recvmsg (sock, &msgv, flags & ~(MSG_ERRQUEUE), &bytes_read, error); + if (PGM_IO_STATUS_NORMAL != status) + return status; + ++ { + size_t bytes_copied = 0; + struct pgm_sk_buff_t** skb = msgv.msgv_skb; + struct pgm_sk_buff_t* pskb = *skb; +@@ -1019,7 +1059,7 @@ + while (bytes_copied < bytes_read) { + size_t copy_len = pskb->len; + if (bytes_copied + copy_len > buflen) { +- pgm_warn (_("APDU truncated, original length %zu bytes."), ++ pgm_warn (_("APDU truncated, original length %lu bytes."), + bytes_read); + copy_len = buflen - bytes_copied; + bytes_read = buflen; +@@ -1031,6 +1071,8 @@ + if (_bytes_read) + *_bytes_read = bytes_copied; + return PGM_IO_STATUS_NORMAL; ++ } ++ } + } + + /* Basic recv operation, copying data from window to application. +@@ -1051,7 +1093,7 @@ + pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); + if (PGM_LIKELY(buflen)) pgm_return_val_if_fail (NULL != buf, PGM_IO_STATUS_ERROR); + +- pgm_debug ("pgm_recv (sock:%p buf:%p buflen:%zu flags:%d bytes-read:%p error:%p)", ++ pgm_debug ("pgm_recv (sock:%p buf:%p buflen:%lu flags:%d bytes-read:%p error:%p)", + (const void*)sock, buf, buflen, flags, (const void*)bytes_read, (const void*)error); + + return pgm_recvfrom (sock, buf, buflen, flags, bytes_read, NULL, NULL, error); diff --git a/3rdparty/openpgm-svn-r1135/pgm/recv_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/recv_unittest.c new file mode 100644 index 0000000..2719897 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/recv_unittest.c @@ -0,0 +1,1600 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for transport recv api + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include /* _GNU_SOURCE for in6_pktinfo */ +#include +#include +#include + + +/* mock state */ + +#define TEST_NETWORK "" +#define TEST_DPORT 7500 +#define TEST_SPORT 1000 +#define TEST_SRC_ADDR "127.0.0.1" +#define TEST_END_ADDR "127.0.0.2" +#define TEST_GROUP_ADDR "239.192.0.1" +#define TEST_PEER_ADDR "127.0.0.6" +#define TEST_DLR_ADDR "127.0.0.9" +#define TEST_MAX_TPDU 1500 +#define TEST_TXW_SQNS 32 +#define TEST_RXW_SQNS 32 +#define TEST_HOPS 16 +#define TEST_SPM_AMBIENT ( pgm_secs(30) ) +#define TEST_SPM_HEARTBEAT_INIT { pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(1300), pgm_secs(7), pgm_secs(16), pgm_secs(25), pgm_secs(30) } +#define TEST_PEER_EXPIRY ( pgm_secs(300) ) +#define TEST_SPMR_EXPIRY ( pgm_msecs(250) ) +#define TEST_NAK_BO_IVL ( pgm_msecs(50) ) +#define TEST_NAK_RPT_IVL ( pgm_secs(2) ) +#define TEST_NAK_RDATA_IVL ( pgm_secs(2) ) +#define TEST_NAK_DATA_RETRIES 5 +#define TEST_NAK_NCF_RETRIES 2 + +struct mock_recvmsg_t { + struct msghdr* mr_msg; + ssize_t mr_retval; + int mr_errno; +}; + +struct pgm_peer_t; + +GList* mock_recvmsg_list = NULL; +static int mock_pgm_type = -1; +static gboolean mock_reset_on_spmr = FALSE; +static gboolean mock_data_on_spmr = FALSE; +static struct pgm_peer_t* mock_peer = NULL; +GList* mock_data_list = NULL; +unsigned mock_pgm_loss_rate = 0; + + +static ssize_t mock_recvmsg (int, struct msghdr*, int); + +#define pgm_parse_raw mock_pgm_parse_raw +#define pgm_parse_udp_encap mock_pgm_parse_udp_encap +#define pgm_verify_spm mock_pgm_verify_spm +#define pgm_verify_nak mock_pgm_verify_nak +#define pgm_verify_ncf mock_pgm_verify_ncf +#define pgm_poll_info mock_pgm_poll_info +#define pgm_set_reset_error mock_pgm_set_reset_error +#define pgm_flush_peers_pending mock_pgm_flush_peers_pending +#define pgm_peer_has_pending mock_pgm_peer_has_pending +#define pgm_peer_set_pending mock_pgm_peer_set_pending +#define pgm_txw_retransmit_is_empty mock_pgm_txw_retransmit_is_empty +#define pgm_rxw_create mock_pgm_rxw_create +#define pgm_rxw_readv mock_pgm_rxw_readv +#define pgm_new_peer mock_pgm_new_peer +#define pgm_on_data mock_pgm_on_data +#define pgm_on_spm mock_pgm_on_spm +#define pgm_on_ack mock_pgm_on_ack +#define pgm_on_nak mock_pgm_on_nak +#define pgm_on_deferred_nak mock_pgm_on_deferred_nak +#define pgm_on_peer_nak mock_pgm_on_peer_nak +#define pgm_on_nnak mock_pgm_on_nnak +#define pgm_on_ncf mock_pgm_on_ncf +#define pgm_on_spmr mock_pgm_on_spmr +#define pgm_sendto mock_pgm_sendto +#define pgm_timer_prepare mock_pgm_timer_prepare +#define pgm_timer_check mock_pgm_timer_check +#define pgm_timer_expiration mock_pgm_timer_expiration +#define pgm_timer_dispatch mock_pgm_timer_dispatch +#define pgm_time_now mock_pgm_time_now +#define pgm_time_update_now mock_pgm_time_update_now +#define recvmsg mock_recvmsg +#define pgm_loss_rate mock_pgm_loss_rate + +#define RECV_DEBUG +#include "recv.c" + + +pgm_rxw_t* mock_pgm_rxw_create (const pgm_tsi_t*, const uint16_t, const unsigned, const unsigned, const ssize_t, const uint32_t); +static pgm_time_t _mock_pgm_time_update_now (void); +pgm_time_update_func mock_pgm_time_update_now = _mock_pgm_time_update_now; + + +static +void +mock_setup (void) +{ + if (!g_thread_supported ()) g_thread_init (NULL); +} + +static +struct pgm_sock_t* +generate_sock (void) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, g_htons(TEST_SPORT) }; + struct pgm_sock_t* sock = g_new0 (struct pgm_sock_t, 1); + sock->window = g_new0 (pgm_txw_t, 1); + memcpy (&sock->tsi, &tsi, sizeof(pgm_tsi_t)); + sock->is_bound = TRUE; + sock->rx_buffer = pgm_alloc_skb (TEST_MAX_TPDU); + sock->max_tpdu = TEST_MAX_TPDU; + sock->rxw_sqns = TEST_RXW_SQNS; + sock->dport = g_htons(TEST_DPORT); + sock->can_send_data = TRUE; + sock->can_send_nak = TRUE; + sock->can_recv_data = TRUE; + sock->peers_hashtable = pgm_hashtable_new (pgm_tsi_hash, pgm_tsi_equal); + pgm_rand_create (&sock->rand_); + sock->nak_bo_ivl = 100*1000; + pgm_notify_init (&sock->pending_notify); + pgm_notify_init (&sock->rdata_notify); + return sock; +} + +static +struct pgm_sk_buff_t* +generate_packet (void) +{ + struct pgm_sk_buff_t* skb; + + skb = pgm_alloc_skb (TEST_MAX_TPDU); + skb->data = skb->head; + skb->len = sizeof(struct pgm_ip) + sizeof(struct pgm_header); + skb->tail = (guint8*)skb->data + skb->len; + +/* add IP header */ + struct pgm_ip* iphdr = skb->data; + iphdr->ip_hl = sizeof(struct pgm_ip) / 4; + iphdr->ip_v = 4; + iphdr->ip_tos = 0; + iphdr->ip_id = 0; + iphdr->ip_off = 0; + iphdr->ip_ttl = 16; + iphdr->ip_p = IPPROTO_PGM; + iphdr->ip_sum = 0; + iphdr->ip_src.s_addr = inet_addr (TEST_SRC_ADDR); + iphdr->ip_dst.s_addr = inet_addr (TEST_GROUP_ADDR); + +/* add PGM header */ + struct pgm_header* pgmhdr = (gpointer)(iphdr + 1); + pgmhdr->pgm_sport = g_htons ((guint16)TEST_SPORT); + pgmhdr->pgm_dport = g_htons ((guint16)TEST_DPORT); + pgmhdr->pgm_options = 0; + pgmhdr->pgm_gsi[0] = 1; + pgmhdr->pgm_gsi[1] = 2; + pgmhdr->pgm_gsi[2] = 3; + pgmhdr->pgm_gsi[3] = 4; + pgmhdr->pgm_gsi[4] = 5; + pgmhdr->pgm_gsi[5] = 6; + pgmhdr->pgm_tsdu_length = 0; + pgmhdr->pgm_checksum = 0; + + skb->pgm_header = pgmhdr; + return skb; +} + +static +void +generate_odata ( + const char* source, + const guint source_len, + const guint32 data_sqn, + const guint32 data_trail, + gpointer* packet, + gsize* len + ) +{ + struct pgm_sk_buff_t* skb = generate_packet (); + pgm_skb_put (skb, sizeof(struct pgm_data) + source_len); + +/* add ODATA header */ + struct pgm_data* datahdr = (gpointer)(skb->pgm_header + 1); + datahdr->data_sqn = g_htonl (data_sqn); + datahdr->data_trail = g_htonl (data_trail); + +/* add payload */ + gpointer data = (gpointer)(datahdr + 1); + memcpy (data, source, source_len); + +/* finalize PGM header */ + struct pgm_header* pgmhdr = skb->pgm_header; + pgmhdr->pgm_type = PGM_ODATA; + pgmhdr->pgm_tsdu_length = g_htons (source_len); + +/* finalize IP header */ + struct pgm_ip* iphdr = skb->data; + iphdr->ip_len = g_htons (skb->len); + + *packet = skb->head; + *len = skb->len; +} + +static +void +generate_spm ( + const guint32 spm_sqn, + const guint32 spm_trail, + const guint32 spm_lead, + gpointer* packet, + gsize* len + ) +{ + struct pgm_sk_buff_t* skb = generate_packet (); + pgm_skb_put (skb, sizeof(struct pgm_spm)); + +/* add SPM header */ + struct pgm_spm* spm = (gpointer)(skb->pgm_header + 1); + spm->spm_sqn = g_htonl (spm_sqn); + spm->spm_trail = g_htonl (spm_trail); + spm->spm_lead = g_htonl (spm_lead); + +/* finalize PGM header */ + struct pgm_header* pgmhdr = skb->pgm_header; + pgmhdr->pgm_type = PGM_SPM; + +/* finalize IP header */ + struct pgm_ip* iphdr = skb->data; + iphdr->ip_len = g_htons (skb->len); + + *packet = skb->head; + *len = skb->len; +} + +static +void +generate_nak ( + const guint32 nak_sqn, + gpointer* packet, + gsize* len + ) +{ + struct pgm_sk_buff_t* skb = generate_packet (); + pgm_skb_put (skb, sizeof(struct pgm_spm)); + +/* update IP header */ + struct pgm_ip* iphdr = skb->data; + iphdr->ip_src.s_addr = inet_addr (TEST_END_ADDR); + iphdr->ip_dst.s_addr = inet_addr (TEST_SRC_ADDR); + +/* update PGM header */ + struct pgm_header* pgmhdr = skb->pgm_header; + pgmhdr->pgm_sport = g_htons ((guint16)TEST_DPORT); + pgmhdr->pgm_dport = g_htons ((guint16)TEST_SPORT); + +/* add NAK header */ + struct pgm_nak* nak = (gpointer)(skb->pgm_header + 1); + nak->nak_sqn = g_htonl (nak_sqn); + +/* finalize PGM header */ + pgmhdr->pgm_type = PGM_NAK; + +/* finalize IP header */ + iphdr->ip_len = g_htons (skb->len); + + *packet = skb->head; + *len = skb->len; +} + +static +void +generate_peer_nak ( + const guint32 nak_sqn, + gpointer* packet, + gsize* len + ) +{ + struct pgm_sk_buff_t* skb = generate_packet (); + pgm_skb_put (skb, sizeof(struct pgm_spm)); + +/* update IP header */ + struct pgm_ip* iphdr = skb->data; + iphdr->ip_src.s_addr = inet_addr (TEST_PEER_ADDR); + iphdr->ip_dst.s_addr = inet_addr (TEST_GROUP_ADDR); + +/* update PGM header */ + struct pgm_header* pgmhdr = skb->pgm_header; + pgmhdr->pgm_sport = g_htons ((guint16)TEST_DPORT); + pgmhdr->pgm_dport = g_htons ((guint16)TEST_SPORT); + +/* add NAK header */ + struct pgm_nak* nak = (gpointer)(skb->pgm_header + 1); + nak->nak_sqn = g_htonl (nak_sqn); + +/* finalize PGM header */ + pgmhdr->pgm_type = PGM_NAK; + +/* finalize IP header */ + iphdr->ip_len = g_htons (skb->len); + + *packet = skb->head; + *len = skb->len; +} + +static +void +generate_nnak ( + const guint32 nak_sqn, + gpointer* packet, + gsize* len + ) +{ + struct pgm_sk_buff_t* skb = generate_packet (); + pgm_skb_put (skb, sizeof(struct pgm_nak)); + +/* update IP header */ + struct pgm_ip* iphdr = skb->data; + iphdr->ip_src.s_addr = inet_addr (TEST_DLR_ADDR); + iphdr->ip_dst.s_addr = inet_addr (TEST_SRC_ADDR); + +/* update PGM header */ + struct pgm_header* pgmhdr = skb->pgm_header; + pgmhdr->pgm_sport = g_htons ((guint16)TEST_DPORT); + pgmhdr->pgm_dport = g_htons ((guint16)TEST_SPORT); + +/* add NNAK header */ + struct pgm_nak* nak = (gpointer)(skb->pgm_header + 1); + nak->nak_sqn = g_htonl (nak_sqn); + +/* finalize PGM header */ + pgmhdr->pgm_type = PGM_NNAK; + +/* finalize IP header */ + iphdr->ip_len = g_htons (skb->len); + + *packet = skb->head; + *len = skb->len; +} + +static +void +generate_ncf ( + const guint32 nak_sqn, + gpointer* packet, + gsize* len + ) +{ + struct pgm_sk_buff_t* skb = generate_packet (); + pgm_skb_put (skb, sizeof(struct pgm_nak)); + +/* add NAK header */ + struct pgm_nak* nak = (gpointer)(skb->pgm_header + 1); + nak->nak_sqn = g_htonl (nak_sqn); + +/* finalize PGM header */ + struct pgm_header* pgmhdr = skb->pgm_header; + pgmhdr->pgm_type = PGM_NCF; + +/* finalize IP header */ + struct pgm_ip* iphdr = skb->data; + iphdr->ip_len = g_htons (skb->len); + + *packet = skb->head; + *len = skb->len; +} + +static +void +generate_spmr ( + gpointer* packet, + gsize* len + ) +{ + struct pgm_sk_buff_t* skb = generate_packet (); + +/* update IP header */ + struct pgm_ip* iphdr = skb->data; + iphdr->ip_src.s_addr = inet_addr (TEST_END_ADDR); + iphdr->ip_dst.s_addr = inet_addr (TEST_SRC_ADDR); + +/* update PGM header */ + struct pgm_header* pgmhdr = skb->pgm_header; + pgmhdr->pgm_sport = g_htons ((guint16)TEST_DPORT); + pgmhdr->pgm_dport = g_htons ((guint16)TEST_SPORT); + +/* finalize PGM header */ + pgmhdr->pgm_type = PGM_SPMR; + +/* finalize IP header */ + iphdr->ip_len = g_htons (skb->len); + + *packet = skb->head; + *len = skb->len; +} + +static +void +generate_peer_spmr ( + gpointer* packet, + gsize* len + ) +{ + struct pgm_sk_buff_t* skb = generate_packet (); + +/* update IP header */ + struct pgm_ip* iphdr = skb->data; + iphdr->ip_src.s_addr = inet_addr (TEST_PEER_ADDR); + iphdr->ip_dst.s_addr = inet_addr (TEST_GROUP_ADDR); + +/* update PGM header */ + struct pgm_header* pgmhdr = skb->pgm_header; + pgmhdr->pgm_sport = g_htons ((guint16)TEST_DPORT); + pgmhdr->pgm_dport = g_htons ((guint16)TEST_SPORT); + +/* finalize PGM header */ + pgmhdr->pgm_type = PGM_SPMR; + +/* finalize IP header */ + iphdr->ip_len = g_htons (skb->len); + + *packet = skb->head; + *len = skb->len; +} + +static +void +generate_msghdr ( + const gpointer packet, + const gsize packet_len + ) +{ + struct pgm_ip* iphdr = packet; + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = iphdr->ip_src.s_addr + }; + struct iovec iov = { + .iov_base = packet, + .iov_len = packet_len + }; + struct cmsghdr* packet_cmsg = g_malloc0 (sizeof(struct cmsghdr) + sizeof(struct in_pktinfo)); + packet_cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(struct in_pktinfo); + packet_cmsg->cmsg_level = IPPROTO_IP; + packet_cmsg->cmsg_type = IP_PKTINFO; + struct in_pktinfo packet_info = { + .ipi_ifindex = 2, + .ipi_spec_dst = iphdr->ip_src.s_addr, /* local address */ + .ipi_addr = iphdr->ip_dst.s_addr /* destination address */ + }; + memcpy ((char*)(packet_cmsg + 1), &packet_info, sizeof(struct in_pktinfo)); + struct msghdr packet_msg = { + .msg_name = g_memdup (&addr, sizeof(addr)), /* source address */ + .msg_namelen = sizeof(addr), + .msg_iov = g_memdup (&iov, sizeof(iov)), + .msg_iovlen = 1, + .msg_control = &packet_cmsg, + .msg_controllen = sizeof(struct cmsghdr) + sizeof(struct in_pktinfo), + .msg_flags = 0 + }; + + struct mock_recvmsg_t* mr = g_malloc (sizeof(struct mock_recvmsg_t)); + mr->mr_msg = g_memdup (&packet_msg, sizeof(packet_msg)); + mr->mr_errno = 0; + mr->mr_retval = packet_len; + mock_recvmsg_list = g_list_append (mock_recvmsg_list, mr); +} + +static +void +push_block_event (void) +{ +/* block */ + struct mock_recvmsg_t* mr = g_malloc (sizeof(struct mock_recvmsg_t)); + mr->mr_msg = NULL; + mr->mr_errno = EAGAIN; + mr->mr_retval = -1; + mock_recvmsg_list = g_list_append (mock_recvmsg_list, mr); +} + +/** packet module */ +bool +mock_pgm_parse_raw ( + struct pgm_sk_buff_t* const skb, + struct sockaddr* const dst, + pgm_error_t** error + ) +{ + const struct pgm_ip* ip = (struct pgm_ip*)skb->data; + struct sockaddr_in* sin = (struct sockaddr_in*)dst; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = ip->ip_dst.s_addr; + const gsize ip_header_length = ip->ip_hl * 4; + skb->pgm_header = (gpointer)( (guint8*)skb->data + ip_header_length ); + skb->data = skb->pgm_header; + skb->len -= ip_header_length; + memcpy (&skb->tsi.gsi, skb->pgm_header->pgm_gsi, sizeof(pgm_gsi_t)); + skb->tsi.sport = skb->pgm_header->pgm_sport; + return TRUE; +} + +bool +mock_pgm_parse_udp_encap ( + struct pgm_sk_buff_t* const skb, + pgm_error_t** error + ) +{ + skb->pgm_header = skb->data; + memcpy (&skb->tsi.gsi, skb->pgm_header->pgm_gsi, sizeof(pgm_gsi_t)); + skb->tsi.sport = skb->pgm_header->pgm_sport; + return TRUE; +} + +bool +mock_pgm_verify_spm ( + const struct pgm_sk_buff_t* const skb + ) +{ + return TRUE; +} + +bool +mock_pgm_verify_nak ( + const struct pgm_sk_buff_t* const skb + ) +{ + return TRUE; +} + +bool +mock_pgm_verify_ncf ( + const struct pgm_sk_buff_t* const skb + ) +{ + return TRUE; +} + +/** socket module */ +int +mock_pgm_poll_info ( + pgm_sock_t* const sock, + struct pollfd* fds, + int* n_fds, + int events + ) +{ +} + +pgm_peer_t* +mock__pgm_peer_ref ( + pgm_peer_t* peer + ) +{ + pgm_atomic_inc32 (&peer->ref_count); + return peer; +} + +PGM_GNUC_INTERNAL +pgm_peer_t* +mock_pgm_new_peer ( + pgm_sock_t* const sock, + const pgm_tsi_t* const tsi, + const struct sockaddr* const src_addr, + const socklen_t src_addr_len, + const struct sockaddr* const dst_addr, + const socklen_t dst_addr_len, + const pgm_time_t now + ) +{ + pgm_peer_t* peer = g_malloc0 (sizeof(pgm_peer_t)); + peer->expiry = now + sock->peer_expiry; + peer->sock = sock; + memcpy (&peer->tsi, tsi, sizeof(pgm_tsi_t)); + memcpy (&peer->group_nla, dst_addr, dst_addr_len); + memcpy (&peer->local_nla, src_addr, src_addr_len); + ((struct sockaddr_in*)&peer->local_nla)->sin_port = g_htons (sock->udp_encap_ucast_port); + ((struct sockaddr_in*)&peer->nla)->sin_port = g_htons (sock->udp_encap_ucast_port); + peer->window = mock_pgm_rxw_create (&peer->tsi, + sock->max_tpdu, + sock->rxw_sqns, + sock->rxw_secs, + sock->rxw_max_rte, + sock->ack_c_p); + peer->spmr_expiry = now + sock->spmr_expiry; + gpointer entry = mock__pgm_peer_ref(peer); + pgm_hashtable_insert (sock->peers_hashtable, &peer->tsi, entry); + peer->peers_link.next = sock->peers_list; + peer->peers_link.data = peer; + if (sock->peers_list) + sock->peers_list->prev = &peer->peers_link; + sock->peers_list = &peer->peers_link; + return peer; +} + +PGM_GNUC_INTERNAL +void +mock_pgm_set_reset_error ( + pgm_sock_t* const sock, + pgm_peer_t* const source, + struct pgm_msgv_t* const msgv + ) +{ +} + +PGM_GNUC_INTERNAL +int +mock_pgm_flush_peers_pending ( + pgm_sock_t* const sock, + struct pgm_msgv_t** pmsg, + const struct pgm_msgv_t* const msg_end, + size_t* const bytes_read, + unsigned* const data_read + ) +{ + if (mock_data_list) { + size_t len = 0; + unsigned count = 0; + while (mock_data_list && *pmsg <= msg_end) { + struct pgm_msgv_t* mock_msgv = mock_data_list->data; + (*pmsg)->msgv_len = mock_msgv->msgv_len; + for (unsigned i = 0; i < mock_msgv->msgv_len; i++) { + (*pmsg)->msgv_skb[i] = mock_msgv->msgv_skb[i]; + len += mock_msgv->msgv_skb[i]->len; + } + count++; + (*pmsg)++; + mock_data_list = g_list_delete_link (mock_data_list, mock_data_list); + } + *bytes_read = len; + *data_read = count; + if (*pmsg > msg_end) + return -ENOBUFS; + } + return 0; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_peer_has_pending ( + pgm_peer_t* const peer + ) +{ + return FALSE; +} + +PGM_GNUC_INTERNAL +void +mock_pgm_peer_set_pending ( + pgm_sock_t* const sock, + pgm_peer_t* const peer + ) +{ + g_assert (NULL != sock); + g_assert (NULL != peer); + if (peer->pending_link.data) return; + peer->pending_link.data = peer; + peer->pending_link.next = sock->peers_pending; + sock->peers_pending = &peer->pending_link; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_on_data ( + pgm_sock_t* const sock, + pgm_peer_t* const sender, + struct pgm_sk_buff_t* const skb + ) +{ + g_debug ("mock_pgm_on_data (sock:%p sender:%p skb:%p)", + (gpointer)sock, (gpointer)sender, (gpointer)skb); + mock_pgm_type = PGM_ODATA; + ((pgm_rxw_t*)sender->window)->has_event = 1; + return TRUE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_on_ack ( + pgm_sock_t* const sock, + struct pgm_sk_buff_t* const skb + ) +{ + g_debug ("mock_pgm_on_ack (sock:%p skb:%p)", + (gpointer)sock, (gpointer)skb); + mock_pgm_type = PGM_ACK; + return TRUE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_on_deferred_nak ( + pgm_sock_t* const sock + ) +{ + return TRUE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_on_nak ( + pgm_sock_t* const sock, + struct pgm_sk_buff_t* const skb + ) +{ + g_debug ("mock_pgm_on_nak (sock:%p skb:%p)", + (gpointer)sock, (gpointer)skb); + mock_pgm_type = PGM_NAK; + return TRUE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_on_peer_nak ( + pgm_sock_t* const sock, + pgm_peer_t* const sender, + struct pgm_sk_buff_t* const skb + ) +{ + g_debug ("mock_pgm_on_peer_nak (sock:%p sender:%p skb:%p)", + (gpointer)sock, (gpointer)sender, (gpointer)skb); + mock_pgm_type = PGM_NAK; + return TRUE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_on_ncf ( + pgm_sock_t* const sock, + pgm_peer_t* const sender, + struct pgm_sk_buff_t* const skb + ) +{ + g_debug ("mock_pgm_on_ncf (sock:%p sender:%p skb:%p)", + (gpointer)sock, (gpointer)sender, (gpointer)skb); + mock_pgm_type = PGM_NCF; + return TRUE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_on_nnak ( + pgm_sock_t* const sock, + struct pgm_sk_buff_t* const skb + ) +{ + g_debug ("mock_pgm_on_nnak (sock:%p skb:%p)", + (gpointer)sock, (gpointer)skb); + mock_pgm_type = PGM_NNAK; + return TRUE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_on_spm ( + pgm_sock_t* const sock, + pgm_peer_t* const sender, + struct pgm_sk_buff_t* const skb + ) +{ + g_debug ("mock_pgm_on_spm (sock:%p sender:%p skb:%p)", + (gpointer)sock, (gpointer)sender, (gpointer)skb); + mock_pgm_type = PGM_SPM; + return TRUE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_on_spmr ( + pgm_sock_t* const sock, + pgm_peer_t* const peer, + struct pgm_sk_buff_t* const skb + ) +{ + g_debug ("mock_pgm_on_spmr (sock:%p peer:%p skb:%p)", + (gpointer)sock, (gpointer)peer, (gpointer)skb); + mock_pgm_type = PGM_SPMR; + if (mock_reset_on_spmr) { + sock->is_reset = TRUE; + mock_pgm_peer_set_pending (sock, mock_peer); + } + if (mock_data_on_spmr) { + mock_pgm_peer_set_pending (sock, mock_peer); + } + return TRUE; +} + +/** transmit window */ +PGM_GNUC_INTERNAL +bool +mock_pgm_txw_retransmit_is_empty ( + const pgm_txw_t*const window + ) +{ + return TRUE; +} + +/** receive window */ +pgm_rxw_t* +mock_pgm_rxw_create ( + const pgm_tsi_t* tsi, + const uint16_t tpdu_size, + const unsigned sqns, + const unsigned secs, + const ssize_t max_rte, + const uint32_t ack_c_p + ) +{ + return g_new0 (pgm_rxw_t, 1); +} + +ssize_t +mock_pgm_rxw_readv ( + pgm_rxw_t* const window, + struct pgm_msgv_t** pmsg, + const unsigned pmsglen + ) +{ + return -1; +} + +/** net module */ +PGM_GNUC_INTERNAL +ssize_t +mock_pgm_sendto ( + pgm_sock_t* sock, + bool use_rate_limit, + bool use_router_alert, + const void* buf, + size_t len, + const struct sockaddr* to, + socklen_t tolen + ) +{ + return len; +} + +/** timer module */ +PGM_GNUC_INTERNAL +bool +mock_pgm_timer_prepare ( + pgm_sock_t* const sock + ) +{ + return FALSE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_timer_check ( + pgm_sock_t* const sock + ) +{ + return FALSE; +} + +PGM_GNUC_INTERNAL +pgm_time_t +mock_pgm_timer_expiration ( + pgm_sock_t* const sock + ) +{ + return 100L; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_timer_dispatch ( + pgm_sock_t* const sock + ) +{ + return TRUE; +} + +/** time module */ +static pgm_time_t mock_pgm_time_now = 0x1; + +static +pgm_time_t +_mock_pgm_time_update_now (void) +{ + return mock_pgm_time_now; +} + +/** libc */ +static +ssize_t +mock_recvmsg ( + int s, + struct msghdr* msg, + int flags + ) +{ + g_assert (NULL != msg); + g_assert (NULL != mock_recvmsg_list); + + g_debug ("mock_recvmsg (s:%d msg:%p flags:%d)", + s, (gpointer)msg, flags); + + struct mock_recvmsg_t* mr = mock_recvmsg_list->data; + struct msghdr* mock_msg = mr->mr_msg; + ssize_t mock_retval = mr->mr_retval; + int mock_errno = mr->mr_errno; + mock_recvmsg_list = g_list_delete_link (mock_recvmsg_list, mock_recvmsg_list); + if (mock_msg) { + g_assert_cmpuint (mock_msg->msg_namelen, <=, msg->msg_namelen); + g_assert_cmpuint (mock_msg->msg_iovlen, <=, msg->msg_iovlen); + g_assert_cmpuint (mock_msg->msg_controllen, <=, msg->msg_controllen); + if (mock_msg->msg_namelen) + memcpy (msg->msg_name, mock_msg->msg_name, mock_msg->msg_namelen); + if (mock_msg->msg_iovlen) { + for (unsigned i = 0; i < mock_msg->msg_iovlen; i++) { + g_assert (mock_msg->msg_iov[i].iov_len <= msg->msg_iov[i].iov_len); + memcpy (msg->msg_iov[i].iov_base, mock_msg->msg_iov[i].iov_base, mock_msg->msg_iov[i].iov_len); + } + } + if (mock_msg->msg_controllen) + memcpy (msg->msg_control, mock_msg->msg_control, mock_msg->msg_controllen); + msg->msg_flags = mock_msg->msg_flags; + } + errno = mock_errno; + return mock_retval; +} + + +/* mock functions for external references */ + +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return 0; +} + + +/* target: + * int + * pgm_recv ( + * pgm_sock_t* sock, + * void* data, + * size_t len, + * int flags, + * size_t* bytes_read, + * pgm_error_t** error + * ) + * + * Most tests default to PGM_IO_STATUS_TIMER_PENDING, PGM_IO_STATUS_WOULD_BLOCK is not expected due + * to peer state engine and SPM broadcasts. + */ + +START_TEST (test_block_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + push_block_event (); + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); +} +END_TEST + +/* recv -> on_data */ +START_TEST (test_data_pass_001) +{ + const char source[] = "i am not a string"; + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gpointer packet; gsize packet_len; + generate_odata (source, sizeof(source), 0 /* sqn */, -1 /* trail */, &packet, &packet_len); + generate_msghdr (packet, packet_len); + push_block_event (); + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (PGM_ODATA == mock_pgm_type, "unexpected PGM packet"); +} +END_TEST + +/* recv -> on_spm */ +START_TEST (test_spm_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gpointer packet; gsize packet_len; + generate_spm (200 /* spm-sqn */, -1 /* trail */, 0 /* lead */, &packet, &packet_len); + generate_msghdr (packet, packet_len); + push_block_event (); + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (PGM_SPM == mock_pgm_type, "unexpected PGM packet"); +} +END_TEST + +/* recv -> on_nak */ +START_TEST (test_nak_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gpointer packet; gsize packet_len; + generate_nak (0 /* sqn */, &packet, &packet_len); + generate_msghdr (packet, packet_len); + push_block_event (); + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (PGM_NAK == mock_pgm_type, "unexpected PGM packet"); +} +END_TEST + +/* recv -> on_peer_nak */ +START_TEST (test_peer_nak_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gpointer packet; gsize packet_len; + generate_peer_nak (0 /* sqn */, &packet, &packet_len); + generate_msghdr (packet, packet_len); + push_block_event (); + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (PGM_NAK == mock_pgm_type, "unexpected PGM packet"); +} +END_TEST + +/* recv -> on_nnak */ +START_TEST (test_nnak_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gpointer packet; gsize packet_len; + generate_nnak (0 /* sqn */, &packet, &packet_len); + generate_msghdr (packet, packet_len); + push_block_event (); + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (PGM_NNAK == mock_pgm_type, "unexpected PGM packet"); +} +END_TEST + +/* recv -> on_ncf */ +START_TEST (test_ncf_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gpointer packet; gsize packet_len; + generate_ncf (0 /* sqn */, &packet, &packet_len); + generate_msghdr (packet, packet_len); + push_block_event (); + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (PGM_NCF == mock_pgm_type, "unexpected PGM packet"); +} +END_TEST + +/* recv -> on_spmr */ +START_TEST (test_spmr_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gpointer packet; gsize packet_len; + generate_spmr (&packet, &packet_len); + generate_msghdr (packet, packet_len); + push_block_event (); + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (PGM_SPMR == mock_pgm_type, "unexpected PGM packet"); +} +END_TEST + +/* recv -> on (peer) spmr */ +START_TEST (test_peer_spmr_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gpointer packet; gsize packet_len; + generate_peer_spmr (&packet, &packet_len); + generate_msghdr (packet, packet_len); + push_block_event (); + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (PGM_SPMR == mock_pgm_type, "unexpected PGM packet"); +} +END_TEST + +/* recv -> lost data */ +START_TEST (test_lost_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + sock->is_reset = TRUE; + const pgm_tsi_t peer_tsi = { { 9, 8, 7, 6, 5, 4 }, g_htons(9000) }; + struct sockaddr_in grp_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_GROUP_ADDR) + }, peer_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_END_ADDR) + }; + pgm_peer_t* peer = mock_pgm_new_peer (sock, &peer_tsi, (struct sockaddr*)&grp_addr, sizeof(grp_addr), (struct sockaddr*)&peer_addr, sizeof(peer_addr), mock_pgm_time_now); + fail_if (NULL == peer, "new_peer failed"); + mock_pgm_peer_set_pending (sock, peer); + push_block_event (); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_RESET == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + if (err) { + g_message ("%s", err->message); + pgm_error_free (err); + err = NULL; + } + push_block_event (); + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); +} +END_TEST + +/* recv -> lost data and abort transport */ +START_TEST (test_abort_on_lost_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + sock->is_reset = TRUE; + sock->is_abort_on_reset = TRUE; + const pgm_tsi_t peer_tsi = { { 9, 8, 7, 6, 5, 4 }, g_htons(9000) }; + struct sockaddr_in grp_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_GROUP_ADDR) + }, peer_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_END_ADDR) + }; + pgm_peer_t* peer = mock_pgm_new_peer (sock, &peer_tsi, (struct sockaddr*)&grp_addr, sizeof(grp_addr), (struct sockaddr*)&peer_addr, sizeof(peer_addr), mock_pgm_time_now); + fail_if (NULL == peer, "new_peer failed"); + mock_pgm_peer_set_pending (sock, peer); + push_block_event (); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_RESET == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + if (err) { + g_message ("%s", err->message); + pgm_error_free (err); + err = NULL; + } + push_block_event (); + fail_unless (PGM_IO_STATUS_RESET == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); +} +END_TEST + +/* recv -> (spmr) & loss */ +START_TEST (test_then_lost_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + mock_reset_on_spmr = TRUE; + gpointer packet; gsize packet_len; + generate_spmr (&packet, &packet_len); + generate_msghdr (packet, packet_len); + const pgm_tsi_t peer_tsi = { { 9, 8, 7, 6, 5, 4 }, g_htons(9000) }; + struct sockaddr_in grp_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_GROUP_ADDR) + }, peer_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_END_ADDR) + }; + mock_peer = mock_pgm_new_peer (sock, &peer_tsi, (struct sockaddr*)&grp_addr, sizeof(grp_addr), (struct sockaddr*)&peer_addr, sizeof(peer_addr), mock_pgm_time_now); + fail_if (NULL == mock_peer, "new_peer failed"); + push_block_event (); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_RESET == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + if (err) { + g_message ("%s", err->message); + pgm_error_free (err); + err = NULL; + } + push_block_event (); + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); +} +END_TEST + +/* recv -> data & loss & abort transport */ +START_TEST (test_then_abort_on_lost_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + mock_reset_on_spmr = TRUE; + sock->is_abort_on_reset = TRUE; + gpointer packet; gsize packet_len; + generate_spmr (&packet, &packet_len); + generate_msghdr (packet, packet_len); + const pgm_tsi_t peer_tsi = { { 9, 8, 7, 6, 5, 4 }, g_htons(9000) }; + struct sockaddr_in grp_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_GROUP_ADDR) + }, peer_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_END_ADDR) + }; + mock_peer = mock_pgm_new_peer (sock, &peer_tsi, (struct sockaddr*)&grp_addr, sizeof(grp_addr), (struct sockaddr*)&peer_addr, sizeof(peer_addr), mock_pgm_time_now); + fail_if (NULL == mock_peer, "new_peer failed"); + push_block_event (); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_RESET == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + if (err) { + g_message ("%s", err->message); + pgm_error_free (err); + err = NULL; + } + push_block_event (); + fail_unless (PGM_IO_STATUS_RESET == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); +} +END_TEST + +/* new waiting peer -> return data */ +START_TEST (test_on_data_pass_001) +{ + const char source[] = "i am not a string"; + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + mock_data_on_spmr = TRUE; + gpointer packet; gsize packet_len; + generate_spmr (&packet, &packet_len); + generate_msghdr (packet, packet_len); + const pgm_tsi_t peer_tsi = { { 9, 8, 7, 6, 5, 4 }, g_htons(9000) }; + struct sockaddr_in grp_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_GROUP_ADDR) + }, peer_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_END_ADDR) + }; + mock_peer = mock_pgm_new_peer (sock, &peer_tsi, (struct sockaddr*)&grp_addr, sizeof(grp_addr), (struct sockaddr*)&peer_addr, sizeof(peer_addr), mock_pgm_time_now); + fail_if (NULL == mock_peer, "new_peer failed"); + struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); + pgm_skb_put (skb, sizeof(source)); + memcpy (skb->data, source, sizeof(source)); + struct pgm_msgv_t* msgv = g_new0 (struct pgm_msgv_t, 1); + msgv->msgv_len = 1; + msgv->msgv_skb[0] = skb; + mock_data_list = g_list_append (mock_data_list, msgv); + push_block_event (); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (NULL == err, "error raised"); + fail_unless ((gsize)sizeof(source) == bytes_read, "unexpected data length"); + push_block_event (); + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); +} +END_TEST + +/* zero length data waiting */ +START_TEST (test_on_zero_pass_001) +{ + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + mock_data_on_spmr = TRUE; + gpointer packet; gsize packet_len; + generate_spmr (&packet, &packet_len); + generate_msghdr (packet, packet_len); + const pgm_tsi_t peer_tsi = { { 9, 8, 7, 6, 5, 4 }, g_htons(9000) }; + struct sockaddr_in grp_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_GROUP_ADDR) + }, peer_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_END_ADDR) + }; + mock_peer = mock_pgm_new_peer (sock, &peer_tsi, (struct sockaddr*)&grp_addr, sizeof(grp_addr), (struct sockaddr*)&peer_addr, sizeof(peer_addr), mock_pgm_time_now); + fail_if (NULL == mock_peer, "new_peer failed"); + struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); + struct pgm_msgv_t* msgv = g_new0 (struct pgm_msgv_t, 1); + msgv->msgv_len = 1; + msgv->msgv_skb[0] = skb; + mock_data_list = g_list_append (mock_data_list, msgv); + push_block_event (); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (NULL == err, "error raised"); + fail_unless ((gsize)0 == bytes_read, "unexpected data length"); + push_block_event (); + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); +} +END_TEST + +/* more than vector length data waiting */ +START_TEST (test_on_many_data_pass_001) +{ + const char* source[] = { + "i am not a string", + "i am not an iguana", + "i am not a peach" + }; + pgm_sock_t* sock = generate_sock(); + fail_if (NULL == sock, "generate_sock failed"); + mock_data_on_spmr = TRUE; + gpointer packet; gsize packet_len; + generate_spmr (&packet, &packet_len); + generate_msghdr (packet, packet_len); + const pgm_tsi_t peer_tsi = { { 9, 8, 7, 6, 5, 4 }, g_htons(9000) }; + struct sockaddr_in grp_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_GROUP_ADDR) + }, peer_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr(TEST_END_ADDR) + }; + mock_peer = mock_pgm_new_peer (sock, &peer_tsi, (struct sockaddr*)&grp_addr, sizeof(grp_addr), (struct sockaddr*)&peer_addr, sizeof(peer_addr), mock_pgm_time_now); + fail_if (NULL == mock_peer, "new_peer failed"); + struct pgm_sk_buff_t* skb; + struct pgm_msgv_t* msgv; +/* #1 */ + msgv = g_new0 (struct pgm_msgv_t, 1); + skb = pgm_alloc_skb (TEST_MAX_TPDU); + pgm_skb_put (skb, strlen(source[0]) + 1); + memcpy (skb->data, source[0], strlen(source[0])); + msgv->msgv_len = 1; + msgv->msgv_skb[0] = skb; + mock_data_list = g_list_append (mock_data_list, msgv); +/* #2 */ + msgv = g_new0 (struct pgm_msgv_t, 1); + skb = pgm_alloc_skb (TEST_MAX_TPDU); + pgm_skb_put (skb, strlen(source[1]) + 1); + memcpy (skb->data, source[1], strlen(source[1])); + msgv->msgv_len = 1; + msgv->msgv_skb[0] = skb; + mock_data_list = g_list_append (mock_data_list, msgv); +/* #3 */ + msgv = g_new0 (struct pgm_msgv_t, 1); + skb = pgm_alloc_skb (TEST_MAX_TPDU); + pgm_skb_put (skb, strlen(source[2]) + 1); + memcpy (skb->data, source[2], strlen(source[2])); + msgv->msgv_len = 1; + msgv->msgv_skb[0] = skb; + mock_data_list = g_list_append (mock_data_list, msgv); + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + gsize bytes_read; + pgm_error_t* err = NULL; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (NULL == err, "error raised"); + fail_unless ((gsize)(strlen(source[0]) + 1) == bytes_read, "unexpected data length"); + g_message ("#1 = \"%s\"", buffer); + fail_unless (PGM_IO_STATUS_NORMAL == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (NULL == err, "error raised"); + fail_unless ((gsize)(strlen(source[1]) + 1) == bytes_read, "unexpected data length"); + g_message ("#2 = \"%s\"", buffer); + fail_unless (PGM_IO_STATUS_NORMAL == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); + fail_unless (NULL == err, "error raised"); + fail_unless ((gsize)(strlen(source[2]) + 1) == bytes_read, "unexpected data length"); + g_message ("#3 = \"%s\"", buffer); + push_block_event (); + fail_unless (PGM_IO_STATUS_TIMER_PENDING == pgm_recv (sock, buffer, sizeof(buffer), MSG_DONTWAIT, &bytes_read, &err), "recv faied"); +} +END_TEST + +START_TEST (test_recv_fail_001) +{ + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + fail_unless (PGM_IO_STATUS_ERROR == pgm_recv (NULL, buffer, sizeof(buffer), 0, NULL, NULL), "recv faied"); +} +END_TEST + +/* target: + * int + * pgm_recvfrom ( + * pgm_sock_t* sock, + * void* data, + * size_t len, + * int flags, + * size_t* bytes_read, + * struct pgm_sockaddr_t* from, + * socklen_t* fromlen, + * pgm_error_t** error + * ) + */ + +START_TEST (test_recvfrom_fail_001) +{ + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + struct pgm_sockaddr_t from; + socklen_t fromlen = sizeof(from); + fail_unless (PGM_IO_STATUS_ERROR == pgm_recvfrom (NULL, buffer, sizeof(buffer), 0, NULL, &from, &fromlen, NULL), "recvfrom failed"); +} +END_TEST + +/* target: + * int + * pgm_recvmsg ( + * pgm_sock_t* sock, + * pgm_msgv_t* msgv, + * int flags, + * size_t* bytes_read, + * pgm_error_t** error + * ) + */ + +START_TEST (test_recvmsg_fail_001) +{ + struct pgm_msgv_t msgv; + fail_unless (PGM_IO_STATUS_ERROR == pgm_recvmsg (NULL, &msgv, 0, NULL, NULL), "recvmsg failed"); +} +END_TEST + +/* target: + * int + * pgm_recvmsgv ( + * pgm_sock_t* sock, + * pgm_msgv_t* msgv, + * unsigned msgv_length, + * int flags, + * size_t* bytes_read, + * pgm_error_t** error + * ) + */ + +START_TEST (test_recvmsgv_fail_001) +{ + struct pgm_msgv_t msgv[1]; + fail_unless (PGM_IO_STATUS_ERROR == pgm_recvmsgv (NULL, msgv, G_N_ELEMENTS(msgv), 0, NULL, NULL), "recvmsgv failed"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_block = tcase_create ("block"); + suite_add_tcase (s, tc_block); + tcase_add_checked_fixture (tc_block, mock_setup, NULL); + tcase_add_test (tc_block, test_block_pass_001); + + TCase* tc_data = tcase_create ("data"); + suite_add_tcase (s, tc_data); + tcase_add_checked_fixture (tc_data, mock_setup, NULL); + tcase_add_test (tc_data, test_data_pass_001); + + TCase* tc_spm = tcase_create ("spm"); + suite_add_tcase (s, tc_spm); + tcase_add_checked_fixture (tc_spm, mock_setup, NULL); + tcase_add_test (tc_spm, test_spm_pass_001); + + TCase* tc_nak = tcase_create ("nak"); + suite_add_tcase (s, tc_nak); + tcase_add_checked_fixture (tc_nak, mock_setup, NULL); + tcase_add_test (tc_nak, test_nak_pass_001); + + TCase* tc_peer_nak = tcase_create ("peer-nak"); + suite_add_tcase (s, tc_peer_nak); + tcase_add_checked_fixture (tc_peer_nak, mock_setup, NULL); + tcase_add_test (tc_peer_nak, test_peer_nak_pass_001); + + TCase* tc_nnak = tcase_create ("nnak"); + suite_add_tcase (s, tc_nnak); + tcase_add_checked_fixture (tc_nnak, mock_setup, NULL); + tcase_add_test (tc_nnak, test_nnak_pass_001); + + TCase* tc_ncf = tcase_create ("ncf"); + suite_add_tcase (s, tc_ncf); + tcase_add_checked_fixture (tc_ncf, mock_setup, NULL); + tcase_add_test (tc_ncf, test_ncf_pass_001); + + TCase* tc_spmr = tcase_create ("spmr"); + suite_add_tcase (s, tc_spmr); + tcase_add_checked_fixture (tc_spmr, mock_setup, NULL); + tcase_add_test (tc_spmr, test_spmr_pass_001); + + TCase* tc_peer_spmr = tcase_create ("peer-spmr"); + suite_add_tcase (s, tc_peer_spmr); + tcase_add_checked_fixture (tc_peer_spmr, mock_setup, NULL); + tcase_add_test (tc_peer_spmr, test_peer_spmr_pass_001); + + TCase* tc_lost = tcase_create ("lost"); + suite_add_tcase (s, tc_lost); + tcase_add_checked_fixture (tc_lost, mock_setup, NULL); + tcase_add_test (tc_lost, test_lost_pass_001); + + TCase* tc_abort_on_lost = tcase_create ("abort-on-lost"); + suite_add_tcase (s, tc_abort_on_lost); + tcase_add_checked_fixture (tc_abort_on_lost, mock_setup, NULL); + tcase_add_test (tc_abort_on_lost, test_abort_on_lost_pass_001); + + TCase* tc_then_lost = tcase_create ("then-lost"); + suite_add_tcase (s, tc_then_lost); + tcase_add_checked_fixture (tc_then_lost, mock_setup, NULL); + tcase_add_test (tc_then_lost, test_then_lost_pass_001); + + TCase* tc_then_abort_on_lost = tcase_create ("then-abort-on-lost"); + suite_add_tcase (s, tc_then_abort_on_lost); + tcase_add_checked_fixture (tc_then_abort_on_lost, mock_setup, NULL); + tcase_add_test (tc_then_abort_on_lost, test_then_abort_on_lost_pass_001); + + TCase* tc_on_data = tcase_create ("on-data"); + suite_add_tcase (s, tc_on_data); + tcase_add_checked_fixture (tc_on_data, mock_setup, NULL); + tcase_add_test (tc_on_data, test_on_data_pass_001); + + TCase* tc_on_zero = tcase_create ("on-zero"); + suite_add_tcase (s, tc_on_zero); + tcase_add_checked_fixture (tc_on_zero, mock_setup, NULL); + tcase_add_test (tc_on_zero, test_on_zero_pass_001); + + TCase* tc_on_many_data = tcase_create ("on-many-data"); + suite_add_tcase (s, tc_on_many_data); + tcase_add_checked_fixture (tc_on_many_data, mock_setup, NULL); + tcase_add_test (tc_on_many_data, test_on_many_data_pass_001); + + TCase* tc_recv = tcase_create ("recv"); + suite_add_tcase (s, tc_recv); + tcase_add_checked_fixture (tc_recv, mock_setup, NULL); + tcase_add_test (tc_recv, test_recv_fail_001); + + TCase* tc_recvfrom = tcase_create ("recvfrom"); + suite_add_tcase (s, tc_recvfrom); + tcase_add_checked_fixture (tc_recvfrom, mock_setup, NULL); + tcase_add_test (tc_recvfrom, test_recvfrom_fail_001); + + TCase* tc_recvmsg = tcase_create ("recvmsg"); + suite_add_tcase (s, tc_recvmsg); + tcase_add_checked_fixture (tc_recvmsg, mock_setup, NULL); + tcase_add_test (tc_recvmsg, test_recvmsg_fail_001); + + TCase* tc_recvmsgv = tcase_create ("recvmsgv"); + suite_add_tcase (s, tc_recvmsgv); + tcase_add_checked_fixture (tc_recvmsgv, mock_setup, NULL); + tcase_add_test (tc_recvmsgv, test_recvmsgv_fail_001); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/reed_solomon.c b/3rdparty/openpgm-svn-r1135/pgm/reed_solomon.c new file mode 100644 index 0000000..a9a292c --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/reed_solomon.c @@ -0,0 +1,576 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Reed-Solomon forward error correction based on Vandermonde matrices. + * + * Output is incompatible with BCH style Reed-Solomon encoding. + * + * draft-ietf-rmt-bb-fec-rs-05.txt + * + rfc5052 + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + + +/* Vector GF(2⁸) plus-equals multiplication. + * + * d[] += b • s[] + */ + +static +void +_pgm_gf_vec_addmul ( + pgm_gf8_t* restrict d, + const pgm_gf8_t b, + const pgm_gf8_t* restrict s, + uint16_t len /* length of vectors */ + ) +{ + uint_fast16_t i; + uint_fast16_t count8; + + if (PGM_UNLIKELY(b == 0)) + return; + +#ifdef CONFIG_GALOIS_MUL_LUT + const pgm_gf8_t* gfmul_b = &pgm_gftable[ (uint16_t)b << 8 ]; +#endif + + i = 0; + count8 = len >> 3; /* 8-way unrolls */ + if (count8) + { + while (count8--) { +#ifdef CONFIG_GALOIS_MUL_LUT + d[i ] ^= gfmul_b[ s[i ] ]; + d[i+1] ^= gfmul_b[ s[i+1] ]; + d[i+2] ^= gfmul_b[ s[i+2] ]; + d[i+3] ^= gfmul_b[ s[i+3] ]; + d[i+4] ^= gfmul_b[ s[i+4] ]; + d[i+5] ^= gfmul_b[ s[i+5] ]; + d[i+6] ^= gfmul_b[ s[i+6] ]; + d[i+7] ^= gfmul_b[ s[i+7] ]; +#else + d[i ] ^= gfmul( b, s[i ] ); + d[i+1] ^= gfmul( b, s[i+1] ); + d[i+2] ^= gfmul( b, s[i+2] ); + d[i+3] ^= gfmul( b, s[i+3] ); + d[i+4] ^= gfmul( b, s[i+4] ); + d[i+5] ^= gfmul( b, s[i+5] ); + d[i+6] ^= gfmul( b, s[i+6] ); + d[i+7] ^= gfmul( b, s[i+7] ); +#endif + i += 8; + } + +/* remaining */ + len %= 8; + } + + while (len--) { +#ifdef CONFIG_GALOIS_MUL_LUT + d[i] ^= gfmul_b[ s[i] ]; +#else + d[i] ^= gfmul( b, s[i] ); +#endif + i++; + } +} + +/* Basic matrix multiplication. + * + * C = AB + * n + * c_i,j = ∑ a_i,j × b_r,j = a_i,1 × b_1,j + a_i,2 × b_2,j + ⋯ + a_i,n × b_n,j + * r=1 + */ + +static +void +_pgm_matmul ( + const pgm_gf8_t* restrict a, /* m-by-n */ + const pgm_gf8_t* restrict b, /* n-by-p */ + pgm_gf8_t* restrict c, /* ∴ m-by-p */ + const uint16_t m, + const uint16_t n, + const uint16_t p + ) +{ + for (uint_fast16_t j = 0; j < m; j++) + { + for (uint_fast16_t i = 0; i < p; i++) + { + pgm_gf8_t sum = 0; + + for (uint_fast16_t k = 0; k < n; k++) + { + sum ^= pgm_gfmul ( a[ (j * n) + k ], b[ (k * p) + i ] ); + } + + c[ (j * p) + i ] = sum; + } + } +} + +/* Generic square matrix inversion + */ + +#ifdef CONFIG_XOR_SWAP +/* whilst cute the xor swap is quite slow */ +#define SWAP(a, b) (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b))) +#else +#define SWAP(a, b) do { const pgm_gf8_t _t = (b); (b) = (a); (a) = _t; } while (0) +#endif + +static +void +_pgm_matinv ( + pgm_gf8_t* M, /* is n-by-n */ + const uint8_t n + ) +{ + uint8_t pivot_rows[ n ]; + uint8_t pivot_cols[ n ]; + bool pivots[ n ]; + memset (pivots, 0, sizeof(pivots)); + + pgm_gf8_t identity[ n ]; + memset (identity, 0, sizeof(identity)); + + for (uint_fast8_t i = 0; i < n; i++) + { + uint_fast8_t row = 0, col = 0; + +/* check diagonal for new pivot */ + if (!pivots[ i ] && M[ (i * n) + i ]) + { + row = col = i; + } + else + { + for (uint_fast8_t j = 0; j < n; j++) + { + if (pivots[ j ]) continue; + + for (uint_fast8_t x = 0; x < n; x++) + { + if (!pivots[ x ] && M[ (j * n) + x ]) + { + row = j; + col = x; + goto found; + } + } + } + } + +found: + pivots[ col ] = TRUE; + +/* pivot */ + if (row != col) + { + for (uint_fast8_t x = 0; x < n; x++) + { + pgm_gf8_t *pivot_row = &M[ (row * n) + x ], + *pivot_col = &M[ (col * n) + x ]; + SWAP( *pivot_row, *pivot_col ); + } + } + +/* save location */ + pivot_rows[ i ] = row; + pivot_cols[ i ] = col; + +/* divide row by pivot element */ + if (M[ (col * n) + col ] != 1) + { + const pgm_gf8_t c = M[ (col * n) + col ]; + M[ (col * n) + col ] = 1; + + for (uint_fast8_t x = 0; x < n; x++) + { + M[ (col * n) + x ] = pgm_gfdiv( M[ (col * n) + x ], c ); + } + } + +/* reduce if not an identity row */ + identity[ col ] = 1; + if (memcmp (&M[ (col * n) ], identity, n * sizeof(pgm_gf8_t))) + { + for ( uint_fast8_t x = 0; + x < n; + x++ ) + { + if (x == col) continue; + + const pgm_gf8_t c = M[ (x * n) + col ]; + M[ (x * n) + col ] = 0; + + _pgm_gf_vec_addmul (&M[ x * n ], c, &M[ col * n ], n); + } + } + identity[ col ] = 0; + } + +/* revert all pivots */ + for (int_fast16_t i = n - 1; i >= 0; i--) + { + if (pivot_rows[ i ] != pivot_cols[ i ]) + { + for (uint_fast8_t j = 0; j < n; j++) + { + pgm_gf8_t *pivot_row = &M[ (j * n) + pivot_rows[ i ] ], + *pivot_col = &M[ (j * n) + pivot_cols[ i ] ]; + SWAP( *pivot_row, *pivot_col ); + } + } + } +} + +/* Gauss–Jordan elimination optimised for Vandermonde matrices + * + * matrix = matrix⁻¹ + * + * A Vandermonde matrix exhibits geometric progression in each row: + * + * ⎡ 1 α₁ α₁² ⋯ α₁^^(n-1) ⎤ + * V = ⎢ 1 α₂ α₂² ⋯ α₂^^(n-1) ⎥ + * ⎣ 1 α₃ α₃² ⋯ α₃^^(n-1) ⎦ + * + * First column is actually α_m⁰, second column is α_m¹. + * + * As only the second column is actually unique so optimise from that. + */ + +static +void +_pgm_matinv_vandermonde ( + pgm_gf8_t* V, /* is n-by-n */ + const uint8_t n + ) +{ +/* trivial cases */ + if (n == 1) return; + +/* P_j(α) is polynomial of degree n - 1 defined by + * + * n + * P_j(α) = ∏ (α - α_m) + * m=1 + * + * 1: Work out coefficients. + */ + + pgm_gf8_t P[ n ]; + memset (P, 0, sizeof(P)); + +/* copy across second row, i.e. j = 2 */ + for (uint_fast8_t i = 0; i < n; i++) + { + P[ i ] = V[ (i * n) + 1 ]; + } + + pgm_gf8_t alpha[ n ]; + memset (alpha, 0, sizeof(alpha)); + + alpha[ n - 1 ] = P[ 0 ]; + for (uint_fast8_t i = 1; i < n; i++) + { + for (uint_fast8_t j = (n - i); j < (n - 1); j++) + { + alpha[ j ] ^= pgm_gfmul( P[ i ], alpha[ j + 1 ] ); + } + alpha[ n - 1 ] ^= P[ i ]; + } + +/* 2: Obtain numberators and denominators by synthetic division. + */ + + pgm_gf8_t b[ n ]; + b[ n - 1 ] = 1; + for (uint_fast8_t j = 0; j < n; j++) + { + const pgm_gf8_t xx = P[ j ]; + pgm_gf8_t t = 1; + +/* skip first iteration */ + for (int_fast16_t i = n - 2; i >= 0; i--) + { + b[ i ] = alpha[ i + 1 ] ^ pgm_gfmul( xx, b[ i + 1 ] ); + t = pgm_gfmul( xx, t ) ^ b[ i ]; + } + + for (uint_fast8_t i = 0; i < n; i++) + { + V[ (i * n) + j ] = pgm_gfdiv ( b[ i ], t ); + } + } +} + +/* create the generator matrix of a reed-solomon code. + * + * s GM e + * ⎧ ⎡ s₀ ⎤ ⎡ 1 0 0 ⎤ ⎡ e₀ ⎤ ⎫ + * ⎪ ⎢ ⋮ ⎥ ⎢ 0 1 ⎥ = ⎢ ⋮ ⎥ ⎬ n + * k ⎨ ⎢ ⋮ ⎥ × ⎢ ⋱ ⎥ ⎣e_{n-1}⎦ ⎭ + * ⎪ ⎢ ⋮ ⎥ ⎢ ⋱ ⎥ + * ⎩ ⎣s_{k-1}⎦ ⎣ 0 0 1 ⎦ + * + * e = s × GM + */ + +void +pgm_rs_create ( + pgm_rs_t* rs, + const uint8_t n, + const uint8_t k + ) +{ + pgm_assert (NULL != rs); + pgm_assert (n > 0); + pgm_assert (k > 0); + + rs->n = n; + rs->k = k; + rs->GM = pgm_new0 (pgm_gf8_t, n * k); + rs->RM = pgm_new0 (pgm_gf8_t, k * k); + +/* alpha = root of primitive polynomial of degree m + * ( 1 + x² + x³ + x⁴ + x⁸ ) + * + * V = Vandermonde matrix of k rows and n columns. + * + * Be careful, Harry! + */ +#ifdef CONFIG_PREFER_MALLOC + pgm_gf8_t* V = pgm_new0 (pgm_gf8_t, n * k); +#else + pgm_gf8_t* V = pgm_newa (pgm_gf8_t, n * k); + memset (V, 0, n * k); +#endif + pgm_gf8_t* p = V + k; + V[0] = 1; + for (uint_fast8_t j = 0; j < (n - 1); j++) + { + for (uint_fast8_t i = 0; i < k; i++) + { +/* the {i, j} entry of V_{k,n} is v_{i,j} = α^^(i×j), + * where 0 <= i <= k - 1 and 0 <= j <= n - 1. + */ + *p++ = pgm_gfantilog[ ( i * j ) % PGM_GF_MAX ]; + } + } + +/* This generator matrix would create a Maximum Distance Separable (MDS) + * matrix, a systematic result is required, i.e. original data is left + * unchanged. + * + * GM = V_{k,k}⁻¹ × V_{k,n} + * + * 1: matrix V_{k,k} formed by the first k columns of V_{k,n} + */ + pgm_gf8_t* V_kk = V; + pgm_gf8_t* V_kn = V + (k * k); + +/* 2: invert it + */ + _pgm_matinv_vandermonde (V_kk, k); + +/* 3: multiply by V_{k,n} + */ + _pgm_matmul (V_kn, V_kk, rs->GM + (k * k), n - k, k, k); + +#ifdef CONFIG_PREFER_MALLOC + pgm_free (V); +#endif + +/* 4: set identity matrix for original data + */ + for (uint_fast8_t i = 0; i < k; i++) + { + rs->GM[ (i * k) + i ] = 1; + } +} + +void +pgm_rs_destroy ( + pgm_rs_t* rs + ) +{ + pgm_assert (NULL != rs); + + if (rs->RM) { + pgm_free (rs->RM); + rs->RM = NULL; + } + + if (rs->GM) { + pgm_free (rs->GM); + rs->GM = NULL; + } +} + +/* create a parity packet from a vector of original data packets and + * FEC block packet offset. + */ + +void +pgm_rs_encode ( + pgm_rs_t* restrict rs, + const pgm_gf8_t** restrict src, /* length rs_t::k */ + const uint8_t offset, + pgm_gf8_t* restrict dst, + const uint16_t len + ) +{ + pgm_assert (NULL != rs); + pgm_assert (NULL != src); + pgm_assert (offset >= rs->k && offset < rs->n); /* parity packet */ + pgm_assert (NULL != dst); + pgm_assert (len > 0); + + memset (dst, 0, len); + for (uint_fast8_t i = 0; i < rs->k; i++) + { + const pgm_gf8_t c = rs->GM[ (offset * rs->k) + i ]; + _pgm_gf_vec_addmul (dst, c, src[i], len); + } +} + +/* original data block of packets with missing packet entries replaced + * with on-demand parity packets. + */ + +void +pgm_rs_decode_parity_inline ( + pgm_rs_t* restrict rs, + pgm_gf8_t** restrict block, /* length rs_t::k */ + const uint8_t* restrict offsets, /* offsets within FEC block, 0 < offset < n */ + const uint16_t len /* packet length */ + ) +{ + pgm_assert (NULL != rs); + pgm_assert (NULL != block); + pgm_assert (NULL != offsets); + pgm_assert (len > 0); + +/* create new recovery matrix from generator + */ + for (uint_fast8_t i = 0; i < rs->k; i++) + { + if (offsets[i] < rs->k) { + memset (&rs->RM[ i * rs->k ], 0, rs->k * sizeof(pgm_gf8_t)); + rs->RM[ (i * rs->k) + i ] = 1; + continue; + } + memcpy (&rs->RM[ i * rs->k ], &rs->GM[ offsets[ i ] * rs->k ], rs->k * sizeof(pgm_gf8_t)); + } + +/* invert */ + _pgm_matinv (rs->RM, rs->k); + + pgm_gf8_t* repairs[ rs->k ]; + +/* multiply out, through the length of erasures[] */ + for (uint_fast8_t j = 0; j < rs->k; j++) + { + if (offsets[ j ] < rs->k) + continue; + +#ifdef CONFIG_PREFER_MALLOC + pgm_gf8_t* erasure = repairs[ j ] = pgm_malloc0 (len); +#else + pgm_gf8_t* erasure = repairs[ j ] = pgm_alloca (len); + memset (erasure, 0, len); +#endif + for (uint_fast8_t i = 0; i < rs->k; i++) + { + pgm_gf8_t* src = block[ i ]; + pgm_gf8_t c = rs->RM[ (j * rs->k) + i ]; + _pgm_gf_vec_addmul (erasure, c, src, len); + } + } + +/* move repaired over parity packets */ + for (uint_fast8_t j = 0; j < rs->k; j++) + { + if (offsets[ j ] < rs->k) + continue; + + memcpy (block[ j ], repairs[ j ], len * sizeof(pgm_gf8_t)); +#ifdef CONFIG_PREFER_MALLOC + pgm_free (repairs[ j ]); +#endif + } +} + +/* entire FEC block of original data and parity packets. + * + * erased packet buffers must be zeroed. + */ +void +pgm_rs_decode_parity_appended ( + pgm_rs_t* restrict rs, + pgm_gf8_t** restrict block, /* length rs_t::n, the FEC block */ + const uint8_t* restrict offsets, /* ordered index of packets */ + const uint16_t len /* packet length */ + ) +{ + pgm_assert (NULL != rs); + pgm_assert (NULL != block); + pgm_assert (NULL != offsets); + pgm_assert (len > 0); + +/* create new recovery matrix from generator + */ + for (uint_fast8_t i = 0; i < rs->k; i++) + { + if (offsets[i] < rs->k) { + memset (&rs->RM[ i * rs->k ], 0, rs->k * sizeof(pgm_gf8_t)); + rs->RM[ (i * rs->k) + i ] = 1; + continue; + } + memcpy (&rs->RM[ i * rs->k ], &rs->GM[ offsets[ i ] * rs->k ], rs->k * sizeof(pgm_gf8_t)); + } + +/* invert */ + _pgm_matinv (rs->RM, rs->k); + +/* multiply out, through the length of erasures[] */ + for (uint_fast8_t j = 0; j < rs->k; j++) + { + if (offsets[ j ] < rs->k) + continue; + + uint_fast8_t p = rs->k; + pgm_gf8_t* erasure = block[ j ]; + for (uint_fast8_t i = 0; i < rs->k; i++) + { + pgm_gf8_t* src; + if (offsets[ i ] < rs->k) + src = block[ i ]; + else + src = block[ p++ ]; + const pgm_gf8_t c = rs->RM[ (j * rs->k) + i ]; + _pgm_gf_vec_addmul (erasure, c, src, len); + } + } +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/reed_solomon.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/reed_solomon.c.c89.patch new file mode 100644 index 0000000..93b2ee2 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/reed_solomon.c.c89.patch @@ -0,0 +1,467 @@ +--- reed_solomon.c 2010-05-21 11:35:32.000000000 +0800 ++++ reed_solomon.c89 2010-08-04 16:46:52.000000000 +0800 +@@ -48,6 +48,7 @@ + return; + + #ifdef CONFIG_GALOIS_MUL_LUT ++ { + const pgm_gf8_t* gfmul_b = &pgm_gftable[ (uint16_t)b << 8 ]; + #endif + +@@ -90,6 +91,10 @@ + #endif + i++; + } ++ ++#ifdef CONFIG_GALOIS_MUL_LUT ++ } ++#endif + } + + /* Basic matrix multiplication. +@@ -111,19 +116,28 @@ + const uint16_t p + ) + { +- for (uint_fast16_t j = 0; j < m; j++) + { +- for (uint_fast16_t i = 0; i < p; i++) ++ uint_fast16_t j; ++ for (j = 0; j < m; j++) ++ { ++ { ++ uint_fast16_t i; ++ for (i = 0; i < p; i++) + { + pgm_gf8_t sum = 0; + +- for (uint_fast16_t k = 0; k < n; k++) ++ { ++ uint_fast16_t k; ++ for (k = 0; k < n; k++) + { + sum ^= pgm_gfmul ( a[ (j * n) + k ], b[ (k * p) + i ] ); + } ++ } + + c[ (j * p) + i ] = sum; + } ++ } ++ } + } + } + +@@ -144,15 +158,16 @@ + const uint8_t n + ) + { +- uint8_t pivot_rows[ n ]; +- uint8_t pivot_cols[ n ]; +- bool pivots[ n ]; ++ uint8_t* pivot_rows = pgm_newa (uint8_t, n); ++ uint8_t* pivot_cols = pgm_newa (uint8_t, n); ++ bool* pivots = pgm_newa (bool, n); ++ pgm_gf8_t* identity = pgm_newa (pgm_gf8_t, n); + memset (pivots, 0, sizeof(pivots)); +- +- pgm_gf8_t identity[ n ]; + memset (identity, 0, sizeof(identity)); + +- for (uint_fast8_t i = 0; i < n; i++) ++ { ++ uint_fast8_t i; ++ for (i = 0; i < n; i++) + { + uint_fast8_t row = 0, col = 0; + +@@ -163,11 +178,15 @@ + } + else + { +- for (uint_fast8_t j = 0; j < n; j++) ++ { ++ uint_fast8_t j; ++ for (j = 0; j < n; j++) + { + if (pivots[ j ]) continue; + +- for (uint_fast8_t x = 0; x < n; x++) ++ { ++ uint_fast8_t x; ++ for (x = 0; x < n; x++) + { + if (!pivots[ x ] && M[ (j * n) + x ]) + { +@@ -176,6 +195,8 @@ + goto found; + } + } ++ } ++ } + } + } + +@@ -185,12 +206,15 @@ + /* pivot */ + if (row != col) + { +- for (uint_fast8_t x = 0; x < n; x++) ++ { ++ uint_fast8_t x; ++ for (x = 0; x < n; x++) + { + pgm_gf8_t *pivot_row = &M[ (row * n) + x ], + *pivot_col = &M[ (col * n) + x ]; + SWAP( *pivot_row, *pivot_col ); + } ++ } + } + + /* save location */ +@@ -203,44 +227,59 @@ + const pgm_gf8_t c = M[ (col * n) + col ]; + M[ (col * n) + col ] = 1; + +- for (uint_fast8_t x = 0; x < n; x++) ++ { ++ uint_fast8_t x; ++ for (x = 0; x < n; x++) + { + M[ (col * n) + x ] = pgm_gfdiv( M[ (col * n) + x ], c ); + } ++ } + } + + /* reduce if not an identity row */ + identity[ col ] = 1; + if (memcmp (&M[ (col * n) ], identity, n * sizeof(pgm_gf8_t))) + { +- for ( uint_fast8_t x = 0; ++ { ++ uint_fast8_t x; ++ for ( x = 0; + x < n; + x++ ) + { + if (x == col) continue; + ++ { + const pgm_gf8_t c = M[ (x * n) + col ]; + M[ (x * n) + col ] = 0; + + _pgm_gf_vec_addmul (&M[ x * n ], c, &M[ col * n ], n); ++ } ++ } + } + } + identity[ col ] = 0; + } ++ } + + /* revert all pivots */ +- for (int_fast16_t i = n - 1; i >= 0; i--) ++ { ++ int_fast16_t i; ++ for (i = n - 1; i >= 0; i--) + { + if (pivot_rows[ i ] != pivot_cols[ i ]) + { +- for (uint_fast8_t j = 0; j < n; j++) ++ { ++ uint_fast8_t j; ++ for (j = 0; j < n; j++) + { + pgm_gf8_t *pivot_row = &M[ (j * n) + pivot_rows[ i ] ], + *pivot_col = &M[ (j * n) + pivot_cols[ i ] ]; + SWAP( *pivot_row, *pivot_col ); + } ++ } + } + } ++ } + } + + /* Gauss–Jordan elimination optimised for Vandermonde matrices +@@ -277,49 +316,73 @@ + * 1: Work out coefficients. + */ + +- pgm_gf8_t P[ n ]; ++ { ++ pgm_gf8_t* P = pgm_newa (pgm_gf8_t, n); + memset (P, 0, sizeof(P)); + + /* copy across second row, i.e. j = 2 */ +- for (uint_fast8_t i = 0; i < n; i++) ++ { ++ uint_fast8_t i; ++ for (i = 0; i < n; i++) + { + P[ i ] = V[ (i * n) + 1 ]; + } ++ } + +- pgm_gf8_t alpha[ n ]; ++ { ++ pgm_gf8_t* alpha = pgm_newa (pgm_gf8_t, n); + memset (alpha, 0, sizeof(alpha)); + + alpha[ n - 1 ] = P[ 0 ]; +- for (uint_fast8_t i = 1; i < n; i++) + { +- for (uint_fast8_t j = (n - i); j < (n - 1); j++) ++ uint_fast8_t i; ++ for (i = 1; i < n; i++) ++ { ++ { ++ uint_fast8_t j; ++ for (j = (n - i); j < (n - 1); j++) + { + alpha[ j ] ^= pgm_gfmul( P[ i ], alpha[ j + 1 ] ); + } ++ } + alpha[ n - 1 ] ^= P[ i ]; + } ++ } + + /* 2: Obtain numberators and denominators by synthetic division. + */ + +- pgm_gf8_t b[ n ]; ++ { ++ pgm_gf8_t* b = pgm_newa (pgm_gf8_t, n); + b[ n - 1 ] = 1; +- for (uint_fast8_t j = 0; j < n; j++) ++ { ++ uint_fast8_t j; ++ for (j = 0; j < n; j++) + { + const pgm_gf8_t xx = P[ j ]; + pgm_gf8_t t = 1; + + /* skip first iteration */ +- for (int_fast16_t i = n - 2; i >= 0; i--) ++ { ++ int_fast16_t i; ++ for (i = n - 2; i >= 0; i--) + { + b[ i ] = alpha[ i + 1 ] ^ pgm_gfmul( xx, b[ i + 1 ] ); + t = pgm_gfmul( xx, t ) ^ b[ i ]; + } ++ } + +- for (uint_fast8_t i = 0; i < n; i++) ++ { ++ uint_fast8_t i; ++ for (i = 0; i < n; i++) + { + V[ (i * n) + j ] = pgm_gfdiv ( b[ i ], t ); + } ++ } ++ } ++ } ++ } ++ } + } + } + +@@ -358,23 +421,31 @@ + * + * Be careful, Harry! + */ ++ { + #ifdef CONFIG_PREFER_MALLOC + pgm_gf8_t* V = pgm_new0 (pgm_gf8_t, n * k); + #else + pgm_gf8_t* V = pgm_newa (pgm_gf8_t, n * k); + memset (V, 0, n * k); + #endif ++ { + pgm_gf8_t* p = V + k; + V[0] = 1; +- for (uint_fast8_t j = 0; j < (n - 1); j++) + { +- for (uint_fast8_t i = 0; i < k; i++) ++ uint_fast8_t j; ++ for (j = 0; j < (n - 1); j++) ++ { ++ { ++ uint_fast8_t i; ++ for (i = 0; i < k; i++) + { + /* the {i, j} entry of V_{k,n} is v_{i,j} = α^^(i×j), + * where 0 <= i <= k - 1 and 0 <= j <= n - 1. + */ + *p++ = pgm_gfantilog[ ( i * j ) % PGM_GF_MAX ]; + } ++ } ++ } + } + + /* This generator matrix would create a Maximum Distance Separable (MDS) +@@ -385,6 +456,7 @@ + * + * 1: matrix V_{k,k} formed by the first k columns of V_{k,n} + */ ++ { + pgm_gf8_t* V_kk = V; + pgm_gf8_t* V_kn = V + (k * k); + +@@ -402,10 +474,16 @@ + + /* 4: set identity matrix for original data + */ +- for (uint_fast8_t i = 0; i < k; i++) ++ { ++ uint_fast8_t i; ++ for (i = 0; i < k; i++) + { + rs->GM[ (i * k) + i ] = 1; + } ++ } ++ } ++ } ++ } + } + + void +@@ -446,11 +524,14 @@ + pgm_assert (len > 0); + + memset (dst, 0, len); +- for (uint_fast8_t i = 0; i < rs->k; i++) ++ { ++ uint_fast8_t i; ++ for (i = 0; i < rs->k; i++) + { + const pgm_gf8_t c = rs->GM[ (offset * rs->k) + i ]; + _pgm_gf_vec_addmul (dst, c, src[i], len); + } ++ } + } + + /* original data block of packets with missing packet entries replaced +@@ -472,7 +553,9 @@ + + /* create new recovery matrix from generator + */ +- for (uint_fast8_t i = 0; i < rs->k; i++) ++ { ++ uint_fast8_t i; ++ for (i = 0; i < rs->k; i++) + { + if (offsets[i] < rs->k) { + memset (&rs->RM[ i * rs->k ], 0, rs->k * sizeof(pgm_gf8_t)); +@@ -481,34 +564,46 @@ + } + memcpy (&rs->RM[ i * rs->k ], &rs->GM[ offsets[ i ] * rs->k ], rs->k * sizeof(pgm_gf8_t)); + } ++ } + + /* invert */ + _pgm_matinv (rs->RM, rs->k); + +- pgm_gf8_t* repairs[ rs->k ]; ++ { ++ pgm_gf8_t** repairs = pgm_newa (pgm_gf8_t*, rs->k); + + /* multiply out, through the length of erasures[] */ +- for (uint_fast8_t j = 0; j < rs->k; j++) ++ { ++ uint_fast8_t j; ++ for (j = 0; j < rs->k; j++) + { + if (offsets[ j ] < rs->k) + continue; + ++ { + #ifdef CONFIG_PREFER_MALLOC + pgm_gf8_t* erasure = repairs[ j ] = pgm_malloc0 (len); + #else + pgm_gf8_t* erasure = repairs[ j ] = pgm_alloca (len); + memset (erasure, 0, len); + #endif +- for (uint_fast8_t i = 0; i < rs->k; i++) ++ { ++ uint_fast8_t i; ++ for (i = 0; i < rs->k; i++) + { + pgm_gf8_t* src = block[ i ]; + pgm_gf8_t c = rs->RM[ (j * rs->k) + i ]; + _pgm_gf_vec_addmul (erasure, c, src, len); + } ++ } ++ } ++ } + } + + /* move repaired over parity packets */ +- for (uint_fast8_t j = 0; j < rs->k; j++) ++ { ++ uint_fast8_t j; ++ for (j = 0; j < rs->k; j++) + { + if (offsets[ j ] < rs->k) + continue; +@@ -518,6 +613,8 @@ + pgm_free (repairs[ j ]); + #endif + } ++ } ++ } + } + + /* entire FEC block of original data and parity packets. +@@ -539,7 +636,9 @@ + + /* create new recovery matrix from generator + */ +- for (uint_fast8_t i = 0; i < rs->k; i++) ++ { ++ uint_fast8_t i; ++ for (i = 0; i < rs->k; i++) + { + if (offsets[i] < rs->k) { + memset (&rs->RM[ i * rs->k ], 0, rs->k * sizeof(pgm_gf8_t)); +@@ -548,28 +647,39 @@ + } + memcpy (&rs->RM[ i * rs->k ], &rs->GM[ offsets[ i ] * rs->k ], rs->k * sizeof(pgm_gf8_t)); + } ++ } + + /* invert */ + _pgm_matinv (rs->RM, rs->k); + + /* multiply out, through the length of erasures[] */ +- for (uint_fast8_t j = 0; j < rs->k; j++) ++ { ++ uint_fast8_t j; ++ for (j = 0; j < rs->k; j++) + { + if (offsets[ j ] < rs->k) + continue; + ++ { + uint_fast8_t p = rs->k; + pgm_gf8_t* erasure = block[ j ]; +- for (uint_fast8_t i = 0; i < rs->k; i++) ++ { ++ uint_fast8_t i; ++ for (i = 0; i < rs->k; i++) + { + pgm_gf8_t* src; + if (offsets[ i ] < rs->k) + src = block[ i ]; + else + src = block[ p++ ]; ++ { + const pgm_gf8_t c = rs->RM[ (j * rs->k) + i ]; + _pgm_gf_vec_addmul (erasure, c, src, len); ++ } ++ } + } ++ } ++ } + } + } + diff --git a/3rdparty/openpgm-svn-r1135/pgm/reed_solomon_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/reed_solomon_unittest.c new file mode 100644 index 0000000..5e0e75e --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/reed_solomon_unittest.c @@ -0,0 +1,305 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for Reed-Solomon forward error correction based on Vandermonde matrices. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include + + +/* mock state */ + + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + + +#define REED_SOLOMON_DEBUG +#include "reed_solomon.c" + + +/* target: + * void + * pgm_rs_create ( + * pgm_rs_t* rs, + * const uint8_t n, + * const uint8_t k + * ) + */ + +START_TEST (test_create_pass_001) +{ + pgm_rs_t rs; + pgm_rs_create (&rs, 255, 16); +} +END_TEST + +START_TEST (test_create_fail_001) +{ + pgm_rs_create (NULL, 255, 16); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_rs_destroy ( + * pgm_rs_t* rs, + * ) + */ + +START_TEST (test_destroy_pass_001) +{ + pgm_rs_t rs; + pgm_rs_create (&rs, 255, 16); + pgm_rs_destroy (&rs); +} +END_TEST + +START_TEST (test_destroy_fail_001) +{ + pgm_rs_destroy (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_rs_encode ( + * pgm_rs_t* rs, + * const pgm_gf8_t** src, + * const uint8_t offset, + * pgm_gf8_t* dst, + * const uint16_t len + * ) + */ + +START_TEST (test_encode_pass_001) +{ + const gchar source[] = "i am not a string"; + const guint16 source_len = strlen (source); + pgm_rs_t rs; + const guint8 k = source_len; + const guint8 parity_index = k; + const guint16 packet_len = 100; + pgm_gf8_t* source_packets[k]; + pgm_gf8_t* parity_packet = g_malloc0 (packet_len); + pgm_rs_create (&rs, 255, k); + for (unsigned i = 0; i < k; i++) { + source_packets[i] = g_malloc0 (packet_len); + source_packets[i][0] = source[i]; + g_message ("packet#%2.2d: 0x%02.2x '%c'", i, source[i], source[i]); + } + pgm_rs_encode (&rs, (const pgm_gf8_t**)source_packets, parity_index, parity_packet, packet_len); + g_message ("parity-packet: %02.2x", parity_packet[0]); + pgm_rs_destroy (&rs); +} +END_TEST + +START_TEST (test_encode_fail_001) +{ + pgm_rs_encode (NULL, NULL, 0, NULL, 0); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_rs_decode_parity_inline ( + * pgm_rs_t* rs, + * pgm_gf8_t** block, + * const uint8_t* offsets, + * const uint16_t len + * ) + */ + +START_TEST (test_decode_parity_inline_pass_001) +{ + const gchar source[] = "i am not a string"; + const guint16 source_len = strlen (source); + pgm_rs_t rs; + const guint8 k = source_len; + const guint8 parity_index = k; + const guint16 packet_len = 100; + pgm_gf8_t* source_packets[k]; + pgm_gf8_t* parity_packet = g_malloc0 (packet_len); + pgm_rs_create (&rs, 255, k); + for (unsigned i = 0; i < k; i++) { + source_packets[i] = g_malloc0 (packet_len); + source_packets[i][0] = source[i]; + } + pgm_rs_encode (&rs, (const pgm_gf8_t**)source_packets, parity_index, parity_packet, packet_len); +/* simulate error occuring at index #3 */ + const guint erased_index = 3; + source_packets[erased_index][0] = 'X'; + for (unsigned i = 0; i < k; i++) { + g_message ("damaged-packet#%2.2d: 0x%02.2x '%c'", + i, source_packets[i][0], source_packets[i][0]); + } + guint8 offsets[k]; + for (unsigned i = 0; i < k; i++) + offsets[i] = i; +/* erased offset now points to parity packet at k */ + offsets[erased_index] = parity_index; +/* move parity inline */ + g_free (source_packets[erased_index]); + source_packets[erased_index] = parity_packet; + pgm_rs_decode_parity_inline (&rs, source_packets, offsets, packet_len); + pgm_rs_destroy (&rs); + for (unsigned i = 0; i < k; i++) { + g_message ("repaired-packet#%2.2d: 0x%02.2x '%c'", + i, source_packets[i][0], source_packets[i][0]); + } +} +END_TEST + +START_TEST (test_decode_parity_inline_fail_001) +{ + pgm_rs_decode_parity_inline (NULL, NULL, NULL, 0); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_rs_decode_parity_appended ( + * pgm_rs_t* rs, + * pgm_gf8_t* block, + * const uint8_t* offsets, + * const uint16_t len + * ) + */ + +START_TEST (test_decode_parity_appended_pass_001) +{ + const gchar source[] = "i am not a string"; + const guint16 source_len = strlen (source); + pgm_rs_t rs; + const guint8 k = source_len; + const guint8 parity_index = k; + const guint16 packet_len = 100; + pgm_gf8_t* source_packets[k+1]; /* include 1 appended parity packet */ + pgm_gf8_t* parity_packet = g_malloc0 (packet_len); + pgm_rs_create (&rs, 255, k); + for (unsigned i = 0; i < k; i++) { + source_packets[i] = g_malloc0 (packet_len); + source_packets[i][0] = source[i]; + } + pgm_rs_encode (&rs, (const pgm_gf8_t**)source_packets, parity_index, parity_packet, packet_len); +/* simulate error occuring at index #3 */ + const guint erased_index = 3; + source_packets[erased_index][0] = 'X'; + for (unsigned i = 0; i < k; i++) { + g_message ("damaged-packet#%2.2d: 0x%02.2x '%c'", + i, source_packets[i][0], source_packets[i][0]); + } + guint8 offsets[k]; + for (unsigned i = 0; i < k; i++) + offsets[i] = i; +/* erased offset now points to parity packet at k */ + offsets[erased_index] = parity_index; +/* erase damaged packet */ + memset (source_packets[erased_index], 0, packet_len); +/* append parity to source packet block */ + source_packets[parity_index] = parity_packet; + pgm_rs_decode_parity_appended (&rs, source_packets, offsets, packet_len); + pgm_rs_destroy (&rs); + for (unsigned i = 0; i < k; i++) { + g_message ("repaired-packet#%2.2d: 0x%02.2x '%c'", + i, source_packets[i][0], source_packets[i][0]); + } +} +END_TEST + +START_TEST (test_decode_parity_appended_fail_001) +{ + pgm_rs_decode_parity_appended (NULL, NULL, NULL, 0); + fail ("reached"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_create = tcase_create ("create"); + suite_add_tcase (s, tc_create); + tcase_add_test (tc_create, test_create_pass_001); + tcase_add_test_raise_signal (tc_create, test_create_fail_001, SIGABRT); + + TCase* tc_destroy = tcase_create ("destroy"); + suite_add_tcase (s, tc_destroy); + tcase_add_test (tc_destroy, test_destroy_pass_001); + tcase_add_test_raise_signal (tc_destroy, test_destroy_fail_001, SIGABRT); + + TCase* tc_encode = tcase_create ("encode"); + suite_add_tcase (s, tc_encode); + tcase_add_test (tc_encode, test_encode_pass_001); + tcase_add_test_raise_signal (tc_encode, test_encode_fail_001, SIGABRT); + + TCase* tc_decode_parity_inline = tcase_create ("decode-parity-inline"); + suite_add_tcase (s, tc_decode_parity_inline); + tcase_add_test (tc_decode_parity_inline, test_decode_parity_inline_pass_001); + tcase_add_test_raise_signal (tc_decode_parity_inline, test_decode_parity_inline_fail_001, SIGABRT); + + TCase* tc_decode_parity_appended = tcase_create ("decode-parity-appended"); + suite_add_tcase (s, tc_decode_parity_appended); + tcase_add_test (tc_decode_parity_appended, test_decode_parity_appended_pass_001); + tcase_add_test_raise_signal (tc_decode_parity_appended, test_decode_parity_appended_fail_001, SIGABRT); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/rxw.c b/3rdparty/openpgm-svn-r1135/pgm/rxw.c new file mode 100644 index 0000000..a6891d9 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/rxw.c @@ -0,0 +1,2233 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * A basic receive window: pointer array implementation. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __STDC_FORMAT_MACROS +#ifdef _MSC_VER +# include +#else +# include +#endif +#include +#include +#include + + +//#define RXW_DEBUG + +#ifndef RXW_DEBUG +# define PGM_DISABLE_ASSERT +#endif + + +/* testing function: is TSI null + * + * returns TRUE if null, returns FALSE if not null. + */ + +static inline +bool +_pgm_tsi_is_null ( + const void*const tsi + ) +{ + const union { + pgm_tsi_t tsi; + uint32_t l[2]; + } *u = tsi; + +/* pre-conditions */ + pgm_assert (NULL != tsi); + + return (0 == u->l[0] && 0 == u->l[1]); +} + +/* sequence state must be smaller than PGM skbuff control buffer */ +PGM_STATIC_ASSERT(sizeof(struct pgm_rxw_state_t) <= sizeof(((struct pgm_sk_buff_t*)0)->cb)); + +static void _pgm_rxw_define (pgm_rxw_t*const, const uint32_t); +static void _pgm_rxw_update_trail (pgm_rxw_t*const, const uint32_t); +static inline uint32_t _pgm_rxw_update_lead (pgm_rxw_t*const, const uint32_t, const pgm_time_t, const pgm_time_t); +static inline uint32_t _pgm_rxw_tg_sqn (pgm_rxw_t*const, const uint32_t); +static inline uint32_t _pgm_rxw_pkt_sqn (pgm_rxw_t*const, const uint32_t); +static inline bool _pgm_rxw_is_first_of_tg_sqn (pgm_rxw_t*const, const uint32_t); +static inline bool _pgm_rxw_is_last_of_tg_sqn (pgm_rxw_t*const, const uint32_t); +static int _pgm_rxw_insert (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*const restrict); +static int _pgm_rxw_append (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*const restrict, const pgm_time_t); +static int _pgm_rxw_add_placeholder_range (pgm_rxw_t*const, const uint32_t, const pgm_time_t, const pgm_time_t); +static void _pgm_rxw_unlink (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*const restrict); +static uint32_t _pgm_rxw_remove_trail (pgm_rxw_t*const); +static void _pgm_rxw_state (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*const restrict, const int); +static inline void _pgm_rxw_shuffle_parity (pgm_rxw_t*const restrict, struct pgm_sk_buff_t*const restrict); +static inline ssize_t _pgm_rxw_incoming_read (pgm_rxw_t*const restrict, struct pgm_msgv_t**restrict, uint32_t); +static bool _pgm_rxw_is_apdu_complete (pgm_rxw_t*const restrict, const uint32_t); +static inline ssize_t _pgm_rxw_incoming_read_apdu (pgm_rxw_t*const restrict, struct pgm_msgv_t**restrict); +static inline int _pgm_rxw_recovery_update (pgm_rxw_t*const, const uint32_t, const pgm_time_t); +static inline int _pgm_rxw_recovery_append (pgm_rxw_t*const, const pgm_time_t, const pgm_time_t); + + +/* returns the pointer at the given index of the window. + */ + +static +struct pgm_sk_buff_t* +_pgm_rxw_peek ( + const pgm_rxw_t* const window, + const uint32_t sequence + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + + if (pgm_rxw_is_empty (window)) + return NULL; + + if (pgm_uint32_gte (sequence, window->trail) && pgm_uint32_lte (sequence, window->lead)) + { + const uint_fast32_t index_ = sequence % pgm_rxw_max_length (window); + struct pgm_sk_buff_t* skb = window->pdata[index_]; +/* availability only guaranteed inside commit window */ + if (pgm_uint32_lt (sequence, window->commit_lead)) { + pgm_assert (NULL != skb); + pgm_assert (pgm_skb_is_valid (skb)); + pgm_assert (!_pgm_tsi_is_null (&skb->tsi)); + } + return skb; + } + + return NULL; +} + +/* sections of the receive window: + * + * | Commit | Incoming | + * |<---------------->|<------------>| + * | | | + * trail commit-lead lead + * + * commit buffers are currently held by the application, the window trail + * cannot be advanced if packets remain in the commit buffer. + * + * incoming buffers are waiting to be passed to the application. + */ + +static inline +uint32_t +_pgm_rxw_commit_length ( + const pgm_rxw_t* const window + ) +{ + pgm_assert (NULL != window); + return window->commit_lead - window->trail; +} + +static inline +bool +_pgm_rxw_commit_is_empty ( + const pgm_rxw_t* const window + ) +{ + pgm_assert (NULL != window); + return (_pgm_rxw_commit_length (window) == 0); +} + +static inline +uint32_t +_pgm_rxw_incoming_length ( + const pgm_rxw_t* const window + ) +{ + pgm_assert (NULL != window); + return ( 1 + window->lead ) - window->commit_lead; +} + +static inline +bool +_pgm_rxw_incoming_is_empty ( + const pgm_rxw_t* const window + ) +{ + pgm_assert (NULL != window); + return (_pgm_rxw_incoming_length (window) == 0); +} + +/* constructor for receive window. zero-length windows are not permitted. + * + * returns pointer to window. + */ + +pgm_rxw_t* +pgm_rxw_create ( + const pgm_tsi_t*const tsi, + const uint16_t tpdu_size, + const unsigned sqns, /* transmit window size in sequence numbers */ + const unsigned secs, /* size in seconds */ + const ssize_t max_rte, /* max bandwidth */ + const uint32_t ack_c_p + ) +{ + pgm_rxw_t* window; + +/* pre-conditions */ + pgm_assert (NULL != tsi); + pgm_assert_cmpuint (tpdu_size, >, 0); + if (sqns) { + pgm_assert_cmpuint (sqns, >, 0); + pgm_assert_cmpuint (sqns & PGM_UINT32_SIGN_BIT, ==, 0); + pgm_assert_cmpuint (secs, ==, 0); + pgm_assert_cmpuint (max_rte, ==, 0); + } else { + pgm_assert_cmpuint (secs, >, 0); + pgm_assert_cmpuint (max_rte, >, 0); + } + + pgm_debug ("create (tsi:%s max-tpdu:%" PRIu16 " sqns:%" PRIu32 " secs %u max-rte %zd ack-c_p %" PRIu32 ")", + pgm_tsi_print (tsi), tpdu_size, sqns, secs, max_rte, ack_c_p); + +/* calculate receive window parameters */ + pgm_assert (sqns || (secs && max_rte)); + const unsigned alloc_sqns = sqns ? sqns : ( (secs * max_rte) / tpdu_size ); + window = pgm_malloc0 (sizeof(pgm_rxw_t) + ( alloc_sqns * sizeof(struct pgm_sk_buff_t*) )); + + window->tsi = tsi; + window->max_tpdu = tpdu_size; + +/* empty state: + * + * trail = 0, lead = -1 + * commit_trail = commit_lead = rxw_trail = rxw_trail_init = 0 + */ + window->lead = -1; + window->trail = window->lead + 1; + +/* limit retransmit requests on late session joining */ + window->is_constrained = TRUE; + +/* minimum value of RS::k = 1 */ + window->tg_size = 1; + +/* PGMCC filter weight */ + window->ack_c_p = pgm_fp16 (ack_c_p); + window->bitmap = 0xffffffff; + +/* pointer array */ + window->alloc = alloc_sqns; + +/* post-conditions */ + pgm_assert_cmpuint (pgm_rxw_max_length (window), ==, alloc_sqns); + pgm_assert_cmpuint (pgm_rxw_length (window), ==, 0); + pgm_assert_cmpuint (pgm_rxw_size (window), ==, 0); + pgm_assert (pgm_rxw_is_empty (window)); + pgm_assert (!pgm_rxw_is_full (window)); + + return window; +} + +/* destructor for receive window. must not be called more than once for same window. + */ + +void +pgm_rxw_destroy ( + pgm_rxw_t* const window + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert_cmpuint (window->alloc, >, 0); + + pgm_debug ("destroy (window:%p)", (const void*)window); + +/* contents of window */ + while (!pgm_rxw_is_empty (window)) { + _pgm_rxw_remove_trail (window); + } + +/* window must now be empty */ + pgm_assert_cmpuint (pgm_rxw_length (window), ==, 0); + pgm_assert_cmpuint (pgm_rxw_size (window), ==, 0); + pgm_assert (pgm_rxw_is_empty (window)); + pgm_assert (!pgm_rxw_is_full (window)); + +/* window */ + pgm_free (window); +} + +/* add skb to receive window. window has fixed size and will not grow. + * PGM skbuff data/tail pointers must point to the PGM payload, and hence skb->len + * is allowed to be zero. + * + * if the skb sequence number indicates lost packets placeholders will be defined + * for each missing entry in the window. + * + * side effects: + * + * 1) sequence number is set in skb from PGM header value. + * 2) window may be updated with new skb. + * 3) placeholders may be created for detected lost packets. + * 4) parity skbs may be shuffled to accomodate original data. + * + * returns: + * PGM_RXW_INSERTED - packet filled a waiting placeholder, skb consumed. + * PGM_RXW_APPENDED - packet advanced window lead, skb consumed. + * PGM_RXW_MISSING - missing packets detected whilst window lead was adanced, skb consumed. + * PGM_RXW_DUPLICATE - re-transmission of previously seen packet. + * PGM_RXW_MALFORMED - corrupted or invalid packet. + * PGM_RXW_BOUNDS - packet out of window. + * + * it is an error to try to free the skb after adding to the window. + */ + +int +pgm_rxw_add ( + pgm_rxw_t* const restrict window, + struct pgm_sk_buff_t* const restrict skb, + const pgm_time_t now, + const pgm_time_t nak_rb_expiry /* calculated expiry time for this skb */ + ) +{ + pgm_rxw_state_t* const state = (pgm_rxw_state_t*)&skb->cb; + int status; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != skb); + pgm_assert_cmpuint (nak_rb_expiry, >, 0); + pgm_assert_cmpuint (pgm_rxw_max_length (window), >, 0); + pgm_assert (pgm_skb_is_valid (skb)); + pgm_assert (((const pgm_list_t*)skb)->next == NULL); + pgm_assert (((const pgm_list_t*)skb)->prev == NULL); + pgm_assert (!_pgm_tsi_is_null (&skb->tsi)); + pgm_assert ((char*)skb->data > (char*)skb->head); + pgm_assert (sizeof(struct pgm_header) + sizeof(struct pgm_data) <= (size_t)((char*)skb->data - (char*)skb->head)); + pgm_assert (skb->len == ((char*)skb->tail - (char*)skb->data)); + + pgm_debug ("add (window:%p skb:%p nak_rb_expiry:%" PGM_TIME_FORMAT ")", + (const void*)window, (const void*)skb, nak_rb_expiry); + + skb->sequence = ntohl (skb->pgm_data->data_sqn); + +/* protocol sanity check: tsdu size */ + if (PGM_UNLIKELY(skb->len != ntohs (skb->pgm_header->pgm_tsdu_length))) + return PGM_RXW_MALFORMED; + +/* protocol sanity check: valid trail pointer wrt. sequence */ + if (PGM_UNLIKELY(skb->sequence - ntohl (skb->pgm_data->data_trail) >= ((UINT32_MAX/2)-1))) + return PGM_RXW_MALFORMED; + +/* verify fragment header for original data, parity packets include a + * parity fragment header + */ + if (!(skb->pgm_header->pgm_options & PGM_OPT_PARITY) && + skb->pgm_opt_fragment) + { +/* protocol sanity check: single fragment APDU */ + if (PGM_UNLIKELY(ntohl (skb->of_apdu_len) == skb->len)) + skb->pgm_opt_fragment = NULL; + +/* protocol sanity check: minimum APDU length */ + if (PGM_UNLIKELY(ntohl (skb->of_apdu_len) < skb->len)) + return PGM_RXW_MALFORMED; + +/* protocol sanity check: sequential ordering */ + if (PGM_UNLIKELY(pgm_uint32_gt (ntohl (skb->of_apdu_first_sqn), skb->sequence))) + return PGM_RXW_MALFORMED; + +/* protocol sanity check: maximum APDU length */ + if (PGM_UNLIKELY(ntohl (skb->of_apdu_len) > PGM_MAX_APDU)) + return PGM_RXW_MALFORMED; + } + +/* first packet of a session defines the window */ + if (PGM_UNLIKELY(!window->is_defined)) + _pgm_rxw_define (window, skb->sequence - 1); /* previous_lead needed for append to occur */ + else + _pgm_rxw_update_trail (window, ntohl (skb->pgm_data->data_trail)); + +/* bounds checking for parity data occurs at the transmission group sequence number */ + if (skb->pgm_header->pgm_options & PGM_OPT_PARITY) + { + if (pgm_uint32_lt (_pgm_rxw_tg_sqn (window, skb->sequence), _pgm_rxw_tg_sqn (window, window->commit_lead))) + return PGM_RXW_DUPLICATE; + + if (pgm_uint32_lt (_pgm_rxw_tg_sqn (window, skb->sequence), _pgm_rxw_tg_sqn (window, window->lead))) { + window->has_event = 1; + return _pgm_rxw_insert (window, skb); + } + + const struct pgm_sk_buff_t* const first_skb = _pgm_rxw_peek (window, _pgm_rxw_tg_sqn (window, skb->sequence)); + const pgm_rxw_state_t* const first_state = (pgm_rxw_state_t*)&first_skb->cb; + + if (_pgm_rxw_tg_sqn (window, skb->sequence) == _pgm_rxw_tg_sqn (window, window->lead)) { + window->has_event = 1; + if (NULL == first_state || first_state->is_contiguous) { + state->is_contiguous = 1; + return _pgm_rxw_append (window, skb, now); + } else + return _pgm_rxw_insert (window, skb); + } + + pgm_assert (NULL != first_state); + status = _pgm_rxw_add_placeholder_range (window, _pgm_rxw_tg_sqn (window, skb->sequence), now, nak_rb_expiry); + } + else + { + if (pgm_uint32_lt (skb->sequence, window->commit_lead)) { + if (pgm_uint32_gte (skb->sequence, window->trail)) + return PGM_RXW_DUPLICATE; + else + return PGM_RXW_BOUNDS; + } + + if (pgm_uint32_lte (skb->sequence, window->lead)) { + window->has_event = 1; + return _pgm_rxw_insert (window, skb); + } + + if (skb->sequence == pgm_rxw_next_lead (window)) { + window->has_event = 1; + if (_pgm_rxw_is_first_of_tg_sqn (window, skb->sequence)) + state->is_contiguous = 1; + return _pgm_rxw_append (window, skb, now); + } + + status = _pgm_rxw_add_placeholder_range (window, skb->sequence, now, nak_rb_expiry); + } + + if (PGM_RXW_APPENDED == status) { + status = _pgm_rxw_append (window, skb, now); + if (PGM_RXW_APPENDED == status) + status = PGM_RXW_MISSING; + } + return status; +} + +/* trail is the next packet to commit upstream, lead is the leading edge + * of the receive window with possible gaps inside, rxw_trail is the transmit + * window trail for retransmit requests. + */ + +/* define window by parameters of first data packet. + */ + +static +void +_pgm_rxw_define ( + pgm_rxw_t* const window, + const uint32_t lead + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (pgm_rxw_is_empty (window)); + pgm_assert (_pgm_rxw_commit_is_empty (window)); + pgm_assert (_pgm_rxw_incoming_is_empty (window)); + pgm_assert (!window->is_defined); + + window->lead = lead; + window->commit_lead = window->rxw_trail = window->rxw_trail_init = window->trail = window->lead + 1; + window->is_constrained = window->is_defined = TRUE; + +/* post-conditions */ + pgm_assert (pgm_rxw_is_empty (window)); + pgm_assert (_pgm_rxw_commit_is_empty (window)); + pgm_assert (_pgm_rxw_incoming_is_empty (window)); + pgm_assert (window->is_defined); + pgm_assert (window->is_constrained); +} + +/* update window with latest transmitted parameters. + * + * returns count of placeholders added into window, used to start sending naks. + */ + +unsigned +pgm_rxw_update ( + pgm_rxw_t* const window, + const uint32_t txw_lead, + const uint32_t txw_trail, + const pgm_time_t now, + const pgm_time_t nak_rb_expiry /* packet expiration time */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert_cmpuint (nak_rb_expiry, >, 0); + + pgm_debug ("pgm_rxw_update (window:%p txw-lead:%" PRIu32 " txw-trail:%" PRIu32 " nak-rb-expiry:%" PGM_TIME_FORMAT ")", + (void*)window, txw_lead, txw_trail, nak_rb_expiry); + + if (PGM_UNLIKELY(!window->is_defined)) { + _pgm_rxw_define (window, txw_lead); + return 0; + } + + _pgm_rxw_update_trail (window, txw_trail); + return _pgm_rxw_update_lead (window, txw_lead, now, nak_rb_expiry); +} + +/* update trailing edge of receive window + */ + +static +void +_pgm_rxw_update_trail ( + pgm_rxw_t* const window, + const uint32_t txw_trail + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + +/* advertised trail is less than the current value */ + if (PGM_UNLIKELY(pgm_uint32_lte (txw_trail, window->rxw_trail))) + return; + +/* protocol sanity check: advertised trail jumps too far ahead */ + if (PGM_UNLIKELY(txw_trail - window->rxw_trail > ((UINT32_MAX/2)-1))) + return; + +/* retransmissions requests are constrained on startup until the advertised trail advances + * beyond the first data sequence number. + */ + if (PGM_UNLIKELY(window->is_constrained)) + { + if (pgm_uint32_gt (txw_trail, window->rxw_trail_init)) + window->is_constrained = FALSE; + else + return; + } + + window->rxw_trail = txw_trail; + +/* new value doesn't affect window */ + if (PGM_UNLIKELY(pgm_uint32_lte (window->rxw_trail, window->trail))) + return; + +/* jump remaining sequence numbers if window is empty */ + if (pgm_rxw_is_empty (window)) + { + const uint32_t distance = (int32_t)(window->rxw_trail) - (int32_t)(window->trail); + window->commit_lead = window->trail += distance; + window->lead += distance; + +/* add loss to bitmap */ + if (distance > 32) window->bitmap = 0; + else window->bitmap <<= distance; + +/* update the Exponential Moving Average (EMA) data loss with long jump: + * s_t = α × (p₁ + (1 - α) × p₂ + (1 - α)² × p₃ + ⋯) + * omitting the weight by stopping after k terms, + * = α × ((1 - α)^^k + (1 - α)^^{k+1} +(1 - α)^^{k+1} + ⋯) + * = α × (1 - α)^^k × (1 + (1 - α) + (1 - α)² + ⋯) + * = (1 - α)^^k + */ + window->data_loss = pgm_fp16mul (window->data_loss, pgm_fp16pow (pgm_fp16 (1) - window->ack_c_p, distance)); + + window->cumulative_losses += distance; + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Data loss due to trailing edge update, fragment count %" PRIu32 "."),window->fragment_count); + pgm_assert (pgm_rxw_is_empty (window)); + pgm_assert (_pgm_rxw_commit_is_empty (window)); + pgm_assert (_pgm_rxw_incoming_is_empty (window)); + return; + } + +/* remove all buffers between commit lead and advertised rxw_trail */ + for (uint32_t sequence = window->commit_lead; + pgm_uint32_gt (window->rxw_trail, sequence) && pgm_uint32_gte (window->lead, sequence); + sequence++) + { + struct pgm_sk_buff_t* skb; + pgm_rxw_state_t* state; + + skb = _pgm_rxw_peek (window, sequence); + pgm_assert (NULL != skb); + state = (pgm_rxw_state_t*)&skb->cb; + + switch (state->pkt_state) { + case PGM_PKT_STATE_HAVE_DATA: + case PGM_PKT_STATE_HAVE_PARITY: + case PGM_PKT_STATE_LOST_DATA: + break; + + case PGM_PKT_STATE_ERROR: + pgm_assert_not_reached(); + + default: + pgm_rxw_lost (window, sequence); + break; + } + } + +/* post-conditions: only after flush */ +// pgm_assert (!pgm_rxw_is_full (window)); +} + +/* update FEC parameters + */ + +void +pgm_rxw_update_fec ( + pgm_rxw_t* const window, + const uint8_t rs_k + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert_cmpuint (rs_k, >, 1); + + pgm_debug ("pgm_rxw_update_fec (window:%p rs(k):%u)", + (void*)window, rs_k); + + if (window->is_fec_available) { + if (rs_k == window->rs.k) return; + pgm_rs_destroy (&window->rs); + } else + window->is_fec_available = 1; + pgm_rs_create (&window->rs, PGM_RS_DEFAULT_N, rs_k); + window->tg_sqn_shift = pgm_power2_log2 (rs_k); + window->tg_size = window->rs.k; +} + +/* add one placeholder to leading edge due to detected lost packet. + */ + +static +void +_pgm_rxw_add_placeholder ( + pgm_rxw_t* const window, + const pgm_time_t now, + const pgm_time_t nak_rb_expiry + ) +{ + struct pgm_sk_buff_t* skb; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (!pgm_rxw_is_full (window)); + +/* advance lead */ + window->lead++; + +/* add loss to bitmap */ + window->bitmap <<= 1; + +/* update the Exponential Moving Average (EMA) data loss with loss: + * s_t = α × x_{t-1} + (1 - α) × s_{t-1} + * x_{t-1} = 1 + * ∴ s_t = α + (1 - α) × s_{t-1} + */ + window->data_loss = window->ack_c_p + pgm_fp16mul ((pgm_fp16 (1) - window->ack_c_p), window->data_loss); + + skb = pgm_alloc_skb (window->max_tpdu); + pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; + skb->tstamp = now; + skb->sequence = window->lead; + state->timer_expiry = nak_rb_expiry; + + if (!_pgm_rxw_is_first_of_tg_sqn (window, skb->sequence)) + { + struct pgm_sk_buff_t* first_skb = _pgm_rxw_peek (window, _pgm_rxw_tg_sqn (window, skb->sequence)); + if (first_skb) { + pgm_rxw_state_t* first_state = (pgm_rxw_state_t*)&first_skb->cb; + first_state->is_contiguous = 0; + } + } + +/* add skb to window */ + const uint_fast32_t index_ = skb->sequence % pgm_rxw_max_length (window); + window->pdata[index_] = skb; + + pgm_rxw_state (window, skb, PGM_PKT_STATE_BACK_OFF); + +/* post-conditions */ + pgm_assert_cmpuint (pgm_rxw_length (window), >, 0); + pgm_assert_cmpuint (pgm_rxw_length (window), <=, pgm_rxw_max_length (window)); + pgm_assert_cmpuint (_pgm_rxw_incoming_length (window), >, 0); +} + +/* add a range of placeholders to the window. + */ + +static +int +_pgm_rxw_add_placeholder_range ( + pgm_rxw_t* const window, + const uint32_t sequence, + const pgm_time_t now, + const pgm_time_t nak_rb_expiry + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (pgm_uint32_gt (sequence, pgm_rxw_lead (window))); + +/* check bounds of commit window */ + const uint32_t new_commit_sqns = ( 1 + sequence ) - window->trail; + if ( !_pgm_rxw_commit_is_empty (window) && + (new_commit_sqns >= pgm_rxw_max_length (window)) ) + { + _pgm_rxw_update_lead (window, sequence, now, nak_rb_expiry); + return PGM_RXW_BOUNDS; /* effectively a slow consumer */ + } + + if (pgm_rxw_is_full (window)) { + pgm_assert (_pgm_rxw_commit_is_empty (window)); + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Receive window full on placeholder sequence.")); + _pgm_rxw_remove_trail (window); + } + +/* if packet is non-contiguous to current leading edge add place holders + * TODO: can be rather inefficient on packet loss looping through dropped sequence numbers + */ + while (pgm_rxw_next_lead (window) != sequence) + { + _pgm_rxw_add_placeholder (window, now, nak_rb_expiry); + if (pgm_rxw_is_full (window)) { + pgm_assert (_pgm_rxw_commit_is_empty (window)); + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Receive window full on placeholder sequence.")); + _pgm_rxw_remove_trail (window); + } + } + +/* post-conditions */ + pgm_assert (!pgm_rxw_is_full (window)); + + return PGM_RXW_APPENDED; +} + +/* update leading edge of receive window. + * + * returns number of place holders added. + */ + +static +unsigned +_pgm_rxw_update_lead ( + pgm_rxw_t* const window, + const uint32_t txw_lead, + const pgm_time_t now, + const pgm_time_t nak_rb_expiry + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + +/* advertised lead is less than the current value */ + if (PGM_UNLIKELY(pgm_uint32_lte (txw_lead, window->lead))) + return 0; + + uint32_t lead; + +/* committed packets limit constrain the lead until they are released */ + if (!_pgm_rxw_commit_is_empty (window) && + (txw_lead - window->trail) >= pgm_rxw_max_length (window)) + { + lead = window->trail + pgm_rxw_max_length (window) - 1; + if (lead == window->lead) + return 0; + } + else + lead = txw_lead; + + unsigned lost = 0; + + while (window->lead != lead) + { +/* slow consumer or fast producer */ + if (pgm_rxw_is_full (window)) { + pgm_assert (_pgm_rxw_commit_is_empty (window)); + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Receive window full on window lead advancement.")); + _pgm_rxw_remove_trail (window); + } + _pgm_rxw_add_placeholder (window, now, nak_rb_expiry); + lost++; + } + + return lost; +} + +/* checks whether an APDU is unrecoverable due to lost TPDUs. + */ +static inline +bool +_pgm_rxw_is_apdu_lost ( + pgm_rxw_t* const restrict window, + struct pgm_sk_buff_t* const restrict skb + ) +{ + const pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != skb); + +/* lost is lost */ + if (PGM_PKT_STATE_LOST_DATA == state->pkt_state) + return TRUE; + +/* by definition, a single-TPDU APDU is complete */ + if (!skb->pgm_opt_fragment) + return FALSE; + + const uint32_t apdu_first_sqn = ntohl (skb->of_apdu_first_sqn); + +/* by definition, first fragment indicates APDU is available */ + if (apdu_first_sqn == skb->sequence) + return FALSE; + + const struct pgm_sk_buff_t* const first_skb = _pgm_rxw_peek (window, apdu_first_sqn); +/* first fragment out-of-bounds */ + if (NULL == first_skb) + return TRUE; + + const pgm_rxw_state_t* first_state = (pgm_rxw_state_t*)&first_skb->cb; + if (PGM_PKT_STATE_LOST_DATA == first_state->pkt_state) + return TRUE; + + return FALSE; +} + +/* return the first missing packet sequence in the specified transmission + * group or NULL if not required. + */ + +static inline +struct pgm_sk_buff_t* +_pgm_rxw_find_missing ( + pgm_rxw_t* const window, + const uint32_t tg_sqn /* tg_sqn | pkt_sqn */ + ) +{ + struct pgm_sk_buff_t* skb; + pgm_rxw_state_t* state; + +/* pre-conditions */ + pgm_assert (NULL != window); + + for (uint32_t i = tg_sqn, j = 0; j < window->tg_size; i++, j++) + { + skb = _pgm_rxw_peek (window, i); + pgm_assert (NULL != skb); + state = (pgm_rxw_state_t*)&skb->cb; + switch (state->pkt_state) { + case PGM_PKT_STATE_BACK_OFF: + case PGM_PKT_STATE_WAIT_NCF: + case PGM_PKT_STATE_WAIT_DATA: + case PGM_PKT_STATE_LOST_DATA: + return skb; + + case PGM_PKT_STATE_HAVE_DATA: + case PGM_PKT_STATE_HAVE_PARITY: + break; + + default: pgm_assert_not_reached(); break; + } + } + + return NULL; +} + +/* returns TRUE if skb is a parity packet with packet length not + * matching the transmission group length without the variable-packet-length + * flag set. + */ + +static inline +bool +_pgm_rxw_is_invalid_var_pktlen ( + pgm_rxw_t* const restrict window, + const struct pgm_sk_buff_t* const restrict skb + ) +{ + const struct pgm_sk_buff_t* first_skb; + +/* pre-conditions */ + pgm_assert (NULL != window); + + if (!window->is_fec_available) + return FALSE; + + if (skb->pgm_header->pgm_options & PGM_OPT_VAR_PKTLEN) + return FALSE; + + const uint32_t tg_sqn = _pgm_rxw_tg_sqn (window, skb->sequence); + if (tg_sqn == skb->sequence) + return FALSE; + + first_skb = _pgm_rxw_peek (window, tg_sqn); + if (NULL == first_skb) + return TRUE; /* transmission group unrecoverable */ + + if (first_skb->len == skb->len) + return FALSE; + + return TRUE; +} + +static inline +bool +_pgm_rxw_has_payload_op ( + const struct pgm_sk_buff_t* const skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != skb); + pgm_assert (NULL != skb->pgm_header); + + return skb->pgm_opt_fragment || skb->pgm_header->pgm_options & PGM_OP_ENCODED; +} + +/* returns TRUE is skb options are invalid when compared to the transmission group + */ + +static inline +bool +_pgm_rxw_is_invalid_payload_op ( + pgm_rxw_t* const restrict window, + const struct pgm_sk_buff_t* const restrict skb + ) +{ + const struct pgm_sk_buff_t* first_skb; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != skb); + + if (!window->is_fec_available) + return FALSE; + + const uint32_t tg_sqn = _pgm_rxw_tg_sqn (window, skb->sequence); + if (tg_sqn == skb->sequence) + return FALSE; + + first_skb = _pgm_rxw_peek (window, tg_sqn); + if (NULL == first_skb) + return TRUE; /* transmission group unrecoverable */ + + if (_pgm_rxw_has_payload_op (first_skb) == _pgm_rxw_has_payload_op (skb)) + return FALSE; + + return TRUE; +} + +/* insert skb into window range, discard if duplicate. window will have placeholder, + * parity, or data packet already matching sequence. + * + * returns: + * PGM_RXW_INSERTED - packet filled a waiting placeholder, skb consumed. + * PGM_RXW_DUPLICATE - re-transmission of previously seen packet. + * PGM_RXW_MALFORMED - corrupted or invalid packet. + * PGM_RXW_BOUNDS - packet out of window. + */ + +static +int +_pgm_rxw_insert ( + pgm_rxw_t* const restrict window, + struct pgm_sk_buff_t* const restrict new_skb + ) +{ + struct pgm_sk_buff_t* skb; + pgm_rxw_state_t* state; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != new_skb); + pgm_assert (!_pgm_rxw_incoming_is_empty (window)); + + if (PGM_UNLIKELY(_pgm_rxw_is_invalid_var_pktlen (window, new_skb) || + _pgm_rxw_is_invalid_payload_op (window, new_skb))) + return PGM_RXW_MALFORMED; + + if (new_skb->pgm_header->pgm_options & PGM_OPT_PARITY) + { + skb = _pgm_rxw_find_missing (window, new_skb->sequence); + if (NULL == skb) + return PGM_RXW_DUPLICATE; + state = (pgm_rxw_state_t*)&skb->cb; + } + else + { + skb = _pgm_rxw_peek (window, new_skb->sequence); + pgm_assert (NULL != skb); + state = (pgm_rxw_state_t*)&skb->cb; + + if (state->pkt_state == PGM_PKT_STATE_HAVE_DATA) + return PGM_RXW_DUPLICATE; + } + +/* APDU fragments are already declared lost */ + if (new_skb->pgm_opt_fragment && + _pgm_rxw_is_apdu_lost (window, new_skb)) + { + pgm_rxw_lost (window, skb->sequence); + return PGM_RXW_BOUNDS; + } + +/* verify placeholder state */ + switch (state->pkt_state) { + case PGM_PKT_STATE_BACK_OFF: + case PGM_PKT_STATE_WAIT_NCF: + case PGM_PKT_STATE_WAIT_DATA: + case PGM_PKT_STATE_LOST_DATA: + break; + + case PGM_PKT_STATE_HAVE_PARITY: + _pgm_rxw_shuffle_parity (window, skb); + break; + + default: pgm_assert_not_reached(); break; + } + +/* statistics */ + const pgm_time_t fill_time = new_skb->tstamp - skb->tstamp; + PGM_HISTOGRAM_TIMES("Rx.RepairTime", fill_time); + PGM_HISTOGRAM_COUNTS("Rx.NakTransmits", state->nak_transmit_count); + PGM_HISTOGRAM_COUNTS("Rx.NcfRetries", state->ncf_retry_count); + PGM_HISTOGRAM_COUNTS("Rx.DataRetries", state->data_retry_count); + if (!window->max_fill_time) { + window->max_fill_time = window->min_fill_time = fill_time; + } + else + { + if (fill_time > window->max_fill_time) + window->max_fill_time = fill_time; + else if (fill_time < window->min_fill_time) + window->min_fill_time = fill_time; + + if (!window->max_nak_transmit_count) { + window->max_nak_transmit_count = window->min_nak_transmit_count = state->nak_transmit_count; + } else { + if (state->nak_transmit_count > window->max_nak_transmit_count) + window->max_nak_transmit_count = state->nak_transmit_count; + else if (state->nak_transmit_count < window->min_nak_transmit_count) + window->min_nak_transmit_count = state->nak_transmit_count; + } + } + +/* add packet to bitmap */ + const uint_fast32_t pos = window->lead - new_skb->sequence; + if (pos < 32) { + window->bitmap |= 1 << pos; + } + +/* update the Exponential Moving Average (EMA) data loss with repair data. + * s_t = α × x_{t-1} + (1 - α) × s_{t-1} + * x_{t-1} = 0 + * ∴ s_t = (1 - α) × s_{t-1} + */ + const uint_fast32_t s = pgm_fp16pow (pgm_fp16 (1) - window->ack_c_p, pos); + if (s > window->data_loss) window->data_loss = 0; + else window->data_loss -= s; + +/* replace place holder skb with incoming skb */ + memcpy (new_skb->cb, skb->cb, sizeof(skb->cb)); + pgm_rxw_state_t* rxw_state = (void*)new_skb->cb; + rxw_state->pkt_state = PGM_PKT_STATE_ERROR; + _pgm_rxw_unlink (window, skb); + pgm_free_skb (skb); + const uint_fast32_t index_ = new_skb->sequence % pgm_rxw_max_length (window); + window->pdata[index_] = new_skb; + if (new_skb->pgm_header->pgm_options & PGM_OPT_PARITY) + _pgm_rxw_state (window, new_skb, PGM_PKT_STATE_HAVE_PARITY); + else + _pgm_rxw_state (window, new_skb, PGM_PKT_STATE_HAVE_DATA); + window->size += new_skb->len; + + return PGM_RXW_INSERTED; +} + +/* shuffle parity packet at skb->sequence to any other needed spot. + */ + +static inline +void +_pgm_rxw_shuffle_parity ( + pgm_rxw_t* const restrict window, + struct pgm_sk_buff_t* const restrict skb + ) +{ + uint_fast32_t index_; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != skb); + + struct pgm_sk_buff_t* restrict missing = _pgm_rxw_find_missing (window, skb->sequence); + if (NULL == missing) + return; + +/* replace place holder skb with parity skb */ + char cb[48]; + _pgm_rxw_unlink (window, missing); + memcpy (cb, skb->cb, sizeof(skb->cb)); + memcpy (skb->cb, missing->cb, sizeof(skb->cb)); + memcpy (missing->cb, cb, sizeof(skb->cb)); + index_ = skb->sequence % pgm_rxw_max_length (window); + window->pdata[index_] = skb; + index_ = missing->sequence % pgm_rxw_max_length (window); + window->pdata[index_] = missing; +} + +/* skb advances the window lead. + * + * returns: + * PGM_RXW_APPENDED - packet advanced window lead, skb consumed. + * PGM_RXW_MALFORMED - corrupted or invalid packet. + * PGM_RXW_BOUNDS - packet out of window. + */ + +static +int +_pgm_rxw_append ( + pgm_rxw_t* const restrict window, + struct pgm_sk_buff_t* const restrict skb, + const pgm_time_t now + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != skb); + if (skb->pgm_header->pgm_options & PGM_OPT_PARITY) { + pgm_assert (_pgm_rxw_tg_sqn (window, skb->sequence) == _pgm_rxw_tg_sqn (window, pgm_rxw_lead (window))); + } else { + pgm_assert (skb->sequence == pgm_rxw_next_lead (window)); + } + + if (PGM_UNLIKELY(_pgm_rxw_is_invalid_var_pktlen (window, skb) || + _pgm_rxw_is_invalid_payload_op (window, skb))) + return PGM_RXW_MALFORMED; + + if (pgm_rxw_is_full (window)) { + if (_pgm_rxw_commit_is_empty (window)) { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Receive window full on new data.")); + _pgm_rxw_remove_trail (window); + } else { + return PGM_RXW_BOUNDS; /* constrained by commit window */ + } + } + +/* advance leading edge */ + window->lead++; + +/* add packet to bitmap */ + window->bitmap = (window->bitmap << 1) | 1; + +/* update the Exponential Moving Average (EMA) data loss with data: + * s_t = α × x_{t-1} + (1 - α) × s_{t-1} + * x_{t-1} = 0 + * ∴ s_t = (1 - α) × s_{t-1} + */ + window->data_loss = pgm_fp16mul (window->data_loss, pgm_fp16 (1) - window->ack_c_p); + +/* APDU fragments are already declared lost */ + if (PGM_UNLIKELY(skb->pgm_opt_fragment && + _pgm_rxw_is_apdu_lost (window, skb))) + { + struct pgm_sk_buff_t* lost_skb = pgm_alloc_skb (window->max_tpdu); + lost_skb->tstamp = now; + lost_skb->sequence = skb->sequence; + +/* add lost-placeholder skb to window */ + const uint_fast32_t index_ = lost_skb->sequence % pgm_rxw_max_length (window); + window->pdata[index_] = lost_skb; + + _pgm_rxw_state (window, lost_skb, PGM_PKT_STATE_LOST_DATA); + return PGM_RXW_BOUNDS; + } + +/* add skb to window */ + if (skb->pgm_header->pgm_options & PGM_OPT_PARITY) + { + const uint_fast32_t index_ = skb->sequence % pgm_rxw_max_length (window); + window->pdata[index_] = skb; + _pgm_rxw_state (window, skb, PGM_PKT_STATE_HAVE_PARITY); + } + else + { + const uint_fast32_t index_ = skb->sequence % pgm_rxw_max_length (window); + window->pdata[index_] = skb; + _pgm_rxw_state (window, skb, PGM_PKT_STATE_HAVE_DATA); + } + +/* statistics */ + window->size += skb->len; + + return PGM_RXW_APPENDED; +} + +/* remove references to all commit packets not in the same transmission group + * as the commit-lead + */ + +void +pgm_rxw_remove_commit ( + pgm_rxw_t* const window + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + + const uint32_t tg_sqn_of_commit_lead = _pgm_rxw_tg_sqn (window, window->commit_lead); + + while (!_pgm_rxw_commit_is_empty (window) && + tg_sqn_of_commit_lead != _pgm_rxw_tg_sqn (window, window->trail)) + { + _pgm_rxw_remove_trail (window); + } +} + +/* flush packets but instead of calling on_data append the contiguous data packets + * to the provided scatter/gather vector. + * + * when transmission groups are enabled, packets remain in the windows tagged committed + * until the transmission group has been completely committed. this allows the packet + * data to be used in parity calculations to recover the missing packets. + * + * returns -1 on nothing read, returns length of bytes read, 0 is a valid read length. + * + * PGM skbuffs will have an increased reference count and must be unreferenced by the + * calling application. + */ + +ssize_t +pgm_rxw_readv ( + pgm_rxw_t* const restrict window, + struct pgm_msgv_t** restrict pmsg, /* message array, updated as messages appended */ + const unsigned pmsglen /* number of items in pmsg */ + ) +{ + const struct pgm_msgv_t* msg_end; + struct pgm_sk_buff_t* skb; + pgm_rxw_state_t* state; + ssize_t bytes_read; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != pmsg); + pgm_assert_cmpuint (pmsglen, >, 0); + + pgm_debug ("readv (window:%p pmsg:%p pmsglen:%u)", + (void*)window, (void*)pmsg, pmsglen); + + msg_end = *pmsg + pmsglen - 1; + + if (_pgm_rxw_incoming_is_empty (window)) + return -1; + + skb = _pgm_rxw_peek (window, window->commit_lead); + pgm_assert (NULL != skb); + + state = (pgm_rxw_state_t*)&skb->cb; + switch (state->pkt_state) { + case PGM_PKT_STATE_HAVE_DATA: + bytes_read = _pgm_rxw_incoming_read (window, pmsg, msg_end - *pmsg + 1); + break; + + case PGM_PKT_STATE_LOST_DATA: +/* do not purge in situ sequence */ + if (_pgm_rxw_commit_is_empty (window)) { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Removing lost trail from window")); + _pgm_rxw_remove_trail (window); + } else { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Locking trail at commit window")); + } +/* fall through */ + case PGM_PKT_STATE_BACK_OFF: + case PGM_PKT_STATE_WAIT_NCF: + case PGM_PKT_STATE_WAIT_DATA: + case PGM_PKT_STATE_HAVE_PARITY: + bytes_read = -1; + break; + + case PGM_PKT_STATE_COMMIT_DATA: + case PGM_PKT_STATE_ERROR: + default: + pgm_assert_not_reached(); + break; + } + + return bytes_read; +} + +/* remove lost sequences from the trailing edge of the window. lost sequence + * at lead of commit window invalidates all parity-data packets as any + * transmission group is now unrecoverable. + * + * returns number of sequences purged. + */ + +static +unsigned +_pgm_rxw_remove_trail ( + pgm_rxw_t* const window + ) +{ + struct pgm_sk_buff_t* skb; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (!pgm_rxw_is_empty (window)); + + skb = _pgm_rxw_peek (window, window->trail); + pgm_assert (NULL != skb); + _pgm_rxw_unlink (window, skb); + window->size -= skb->len; +/* remove reference to skb */ + if (PGM_UNLIKELY(pgm_mem_gc_friendly)) { + const uint_fast32_t index_ = skb->sequence % pgm_rxw_max_length (window); + window->pdata[index_] = NULL; + } + pgm_free_skb (skb); + if (window->trail++ == window->commit_lead) { +/* data-loss */ + window->commit_lead++; + window->cumulative_losses++; + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Data loss due to pulled trailing edge, fragment count %" PRIu32 "."),window->fragment_count); + return 1; + } + return 0; +} + +unsigned +pgm_rxw_remove_trail ( + pgm_rxw_t* const window + ) +{ + pgm_debug ("remove_trail (window:%p)", (const void*)window); + return _pgm_rxw_remove_trail (window); +} + +/* read contiguous APDU-grouped sequences from the incoming window. + * + * side effects: + * + * 1) increments statics for window messages and bytes read. + * + * returns count of bytes read. + */ + +static inline +ssize_t +_pgm_rxw_incoming_read ( + pgm_rxw_t* const restrict window, + struct pgm_msgv_t** restrict pmsg, /* message array, updated as messages appended */ + unsigned pmsglen /* number of items in pmsg */ + ) +{ + const struct pgm_msgv_t* msg_end; + struct pgm_sk_buff_t* skb; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != pmsg); + pgm_assert_cmpuint (pmsglen, >, 0); + pgm_assert (!_pgm_rxw_incoming_is_empty (window)); + + pgm_debug ("_pgm_rxw_incoming_read (window:%p pmsg:%p pmsglen:%u)", + (void*)window, (void*)pmsg, pmsglen); + + msg_end = *pmsg + pmsglen - 1; + ssize_t bytes_read = 0; + size_t data_read = 0; + + do { + skb = _pgm_rxw_peek (window, window->commit_lead); + pgm_assert (NULL != skb); + if (_pgm_rxw_is_apdu_complete (window, + skb->pgm_opt_fragment ? ntohl (skb->of_apdu_first_sqn) : skb->sequence)) + { + bytes_read += _pgm_rxw_incoming_read_apdu (window, pmsg); + data_read ++; + } + else break; + } while (*pmsg <= msg_end && !_pgm_rxw_incoming_is_empty (window)); + + window->bytes_delivered += bytes_read; + window->msgs_delivered += data_read; + return data_read > 0 ? bytes_read : -1; +} + +/* returns TRUE if transmission group is lost. + * + * checking is lightly limited to bounds. + */ + +static inline +bool +_pgm_rxw_is_tg_sqn_lost ( + pgm_rxw_t* const window, + const uint32_t tg_sqn /* transmission group sequence */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert_cmpuint (_pgm_rxw_pkt_sqn (window, tg_sqn), ==, 0); + + if (pgm_rxw_is_empty (window)) + return TRUE; + + if (pgm_uint32_lt (tg_sqn, window->trail)) + return TRUE; + + return FALSE; +} + +/* reconstruct missing sequences in a transmission group using embedded parity data. + */ + +static +void +_pgm_rxw_reconstruct ( + pgm_rxw_t* const window, + const uint32_t tg_sqn /* transmission group sequence */ + ) +{ + struct pgm_sk_buff_t* skb; + pgm_rxw_state_t* state; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (1 == window->is_fec_available); + pgm_assert_cmpuint (_pgm_rxw_pkt_sqn (window, tg_sqn), ==, 0); + + skb = _pgm_rxw_peek (window, tg_sqn); + pgm_assert (NULL != skb); + + const bool is_var_pktlen = skb->pgm_header->pgm_options & PGM_OPT_VAR_PKTLEN; + const bool is_op_encoded = skb->pgm_header->pgm_options & PGM_OPT_PRESENT; + const uint16_t parity_length = ntohs (skb->pgm_header->pgm_tsdu_length); + struct pgm_sk_buff_t* tg_skbs[ window->rs.n ]; + pgm_gf8_t* tg_data[ window->rs.n ]; + pgm_gf8_t* tg_opts[ window->rs.n ]; + uint8_t offsets[ window->rs.k ]; + uint8_t rs_h = 0; + + for (uint32_t i = tg_sqn, j = 0; i != (tg_sqn + window->rs.k); i++, j++) + { + skb = _pgm_rxw_peek (window, i); + pgm_assert (NULL != skb); + state = (pgm_rxw_state_t*)&skb->cb; + switch (state->pkt_state) { + case PGM_PKT_STATE_HAVE_DATA: + tg_skbs[ j ] = skb; + tg_data[ j ] = skb->data; + tg_opts[ j ] = (pgm_gf8_t*)skb->pgm_opt_fragment; + offsets[ j ] = j; + break; + + case PGM_PKT_STATE_HAVE_PARITY: + tg_skbs[ window->rs.k + rs_h ] = skb; + tg_data[ window->rs.k + rs_h ] = skb->data; + tg_opts[ window->rs.k + rs_h ] = (pgm_gf8_t*)skb->pgm_opt_fragment; + offsets[ j ] = window->rs.k + rs_h; + ++rs_h; +/* fall through and alloc new skb for reconstructed data */ + case PGM_PKT_STATE_BACK_OFF: + case PGM_PKT_STATE_WAIT_NCF: + case PGM_PKT_STATE_WAIT_DATA: + case PGM_PKT_STATE_LOST_DATA: + skb = pgm_alloc_skb (window->max_tpdu); + pgm_skb_reserve (skb, sizeof(struct pgm_header) + sizeof(struct pgm_data)); + skb->pgm_header = skb->head; + skb->pgm_data = (void*)( skb->pgm_header + 1 ); + if (is_op_encoded) { + const uint16_t opt_total_length = sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment); + pgm_skb_reserve (skb, opt_total_length); + skb->pgm_opt_fragment = (void*)( skb->pgm_data + 1 ); + pgm_skb_put (skb, parity_length); + memset (skb->pgm_opt_fragment, 0, opt_total_length + parity_length); + } else { + pgm_skb_put (skb, parity_length); + memset (skb->data, 0, parity_length); + } + tg_skbs[ j ] = skb; + tg_data[ j ] = skb->data; + tg_opts[ j ] = (void*)skb->pgm_opt_fragment; + break; + + default: pgm_assert_not_reached(); break; + } + + if (!skb->zero_padded) { + memset (skb->tail, 0, parity_length - skb->len); + skb->zero_padded = 1; + } + + } + +/* reconstruct payload */ + pgm_rs_decode_parity_appended (&window->rs, + tg_data, + offsets, + parity_length); + +/* reconstruct opt_fragment option */ + if (is_op_encoded) + pgm_rs_decode_parity_appended (&window->rs, + tg_opts, + offsets, + sizeof(struct pgm_opt_fragment)); + +/* swap parity skbs with reconstructed skbs */ + for (uint_fast8_t i = 0; i < window->rs.k; i++) + { + if (offsets[i] < window->rs.k) + continue; + + struct pgm_sk_buff_t* repair_skb = tg_skbs[i]; + + if (is_var_pktlen) + { + const uint16_t pktlen = *(uint16_t*)( (char*)repair_skb->tail - sizeof(uint16_t)); + if (pktlen > parity_length) { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Invalid encoded variable packet length in reconstructed packet, dropping entire transmission group.")); + pgm_free_skb (repair_skb); + for (uint_fast8_t j = i; j < window->rs.k; j++) + { + if (offsets[j] < window->rs.k) + continue; + pgm_rxw_lost (window, tg_skbs[offsets[j]]->sequence); + } + break; + } + const uint16_t padding = parity_length - pktlen; + repair_skb->len -= padding; + repair_skb->tail = (char*)repair_skb->tail - padding; + } + +#ifdef PGM_DISABLE_ASSERT + _pgm_rxw_insert (window, repair_skb); +#else + pgm_assert_cmpint (_pgm_rxw_insert (window, repair_skb), ==, PGM_RXW_INSERTED); +#endif + } +} + +/* check every TPDU in an APDU and verify that the data has arrived + * and is available to commit to the application. + * + * if APDU sits in a transmission group that can be reconstructed use parity + * data then the entire group will be decoded and any missing data packets + * replaced by the recovery calculation. + * + * packets with single fragment fragment headers must be normalised as regular + * packets before calling. + * + * APDUs exceeding PGM_MAX_FRAGMENTS or PGM_MAX_APDU length will be discarded. + * + * returns FALSE if APDU is incomplete or longer than max_len sequences. + */ + +static +bool +_pgm_rxw_is_apdu_complete ( + pgm_rxw_t* const window, + const uint32_t first_sequence + ) +{ + struct pgm_sk_buff_t* skb; + pgm_rxw_state_t* state; + +/* pre-conditions */ + pgm_assert (NULL != window); + + pgm_debug ("_pgm_rxw_is_apdu_complete (window:%p first-sequence:%" PRIu32 ")", + (const void*)window, first_sequence); + + skb = _pgm_rxw_peek (window, first_sequence); + if (PGM_UNLIKELY(NULL == skb)) { + return FALSE; + } + + const size_t apdu_size = skb->pgm_opt_fragment ? ntohl (skb->of_apdu_len) : skb->len; + const uint32_t tg_sqn = _pgm_rxw_tg_sqn (window, first_sequence); + uint32_t sequence = first_sequence; + unsigned contiguous_tpdus = 0; + size_t contiguous_size = 0; + bool check_parity = FALSE; + + pgm_assert_cmpuint (apdu_size, >=, skb->len); + +/* protocol sanity check: maximum length */ + if (PGM_UNLIKELY(apdu_size > PGM_MAX_APDU)) { + pgm_rxw_lost (window, first_sequence); + return FALSE; + } + + do { + state = (pgm_rxw_state_t*)&skb->cb; + + if (!check_parity && + PGM_PKT_STATE_HAVE_DATA != state->pkt_state) + { + if (window->is_fec_available && + !_pgm_rxw_is_tg_sqn_lost (window, tg_sqn) ) + { + check_parity = TRUE; +/* pre-seed committed sequence count */ + if (pgm_uint32_lte (tg_sqn, window->commit_lead)) + contiguous_tpdus += window->commit_lead - tg_sqn; + } + else + return FALSE; + } + + if (check_parity) + { + if (PGM_PKT_STATE_HAVE_DATA == state->pkt_state || + PGM_PKT_STATE_HAVE_PARITY == state->pkt_state) + ++contiguous_tpdus; + +/* have sufficient been received for reconstruction */ + if (contiguous_tpdus >= window->tg_size) { + _pgm_rxw_reconstruct (window, tg_sqn); + return _pgm_rxw_is_apdu_complete (window, first_sequence); + } + } + else + { +/* single packet APDU, already complete */ + if (PGM_PKT_STATE_HAVE_DATA == state->pkt_state && + !skb->pgm_opt_fragment) + return TRUE; + +/* protocol sanity check: matching first sequence reference */ + if (PGM_UNLIKELY(ntohl (skb->of_apdu_first_sqn) != first_sequence)) { + pgm_rxw_lost (window, first_sequence); + return FALSE; + } + +/* protocol sanity check: matching apdu length */ + if (PGM_UNLIKELY(ntohl (skb->of_apdu_len) != apdu_size)) { + pgm_rxw_lost (window, first_sequence); + return FALSE; + } + +/* protocol sanity check: maximum number of fragments per apdu */ + if (PGM_UNLIKELY(++contiguous_tpdus > PGM_MAX_FRAGMENTS)) { + pgm_rxw_lost (window, first_sequence); + return FALSE; + } + + contiguous_size += skb->len; + if (apdu_size == contiguous_size) + return TRUE; + else if (PGM_UNLIKELY(apdu_size < contiguous_size)) { + pgm_rxw_lost (window, first_sequence); + return FALSE; + } + } + + skb = _pgm_rxw_peek (window, ++sequence); + } while (skb); + +/* pending */ + return FALSE; +} + +/* read one APDU consisting of one or more TPDUs. target array is guaranteed + * to be big enough to store complete APDU. + */ + +static inline +ssize_t +_pgm_rxw_incoming_read_apdu ( + pgm_rxw_t* const restrict window, + struct pgm_msgv_t** restrict pmsg /* message array, updated as messages appended */ + ) +{ + struct pgm_sk_buff_t *skb; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != pmsg); + + pgm_debug ("_pgm_rxw_incoming_read_apdu (window:%p pmsg:%p)", + (const void*)window, (const void*)pmsg); + + skb = _pgm_rxw_peek (window, window->commit_lead); + size_t contiguous_len = 0; + const size_t apdu_len = skb->pgm_opt_fragment ? ntohl (skb->of_apdu_len) : skb->len; + unsigned i = 0; + pgm_assert_cmpuint (apdu_len, >=, skb->len); + (*pmsg)->msgv_len = 0; + do { + _pgm_rxw_state (window, skb, PGM_PKT_STATE_COMMIT_DATA); + (*pmsg)->msgv_skb[i++] = skb; + (*pmsg)->msgv_len++; + contiguous_len += skb->len; + window->commit_lead++; + if (apdu_len == contiguous_len) + break; + skb = _pgm_rxw_peek (window, window->commit_lead); + } while (apdu_len > contiguous_len); + + (*pmsg)++; + +/* post-conditions */ + pgm_assert (!_pgm_rxw_commit_is_empty (window)); + +return contiguous_len; +} + +/* returns transmission group sequence (TG_SQN) from sequence (SQN). + */ + +static inline +uint32_t +_pgm_rxw_tg_sqn ( + pgm_rxw_t* const window, + const uint32_t sequence + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + + const uint32_t tg_sqn_mask = 0xffffffff << window->tg_sqn_shift; + return sequence & tg_sqn_mask; +} + +/* returns packet number (PKT_SQN) from sequence (SQN). + */ + +static inline +uint32_t +_pgm_rxw_pkt_sqn ( + pgm_rxw_t* const window, + const uint32_t sequence + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + + const uint32_t tg_sqn_mask = 0xffffffff << window->tg_sqn_shift; + return sequence & ~tg_sqn_mask; +} + +/* returns TRUE when the sequence is the first of a transmission group. + */ + +static inline +bool +_pgm_rxw_is_first_of_tg_sqn ( + pgm_rxw_t* const window, + const uint32_t sequence + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + + return _pgm_rxw_pkt_sqn (window, sequence) == 0; +} + +/* returns TRUE when the sequence is the last of a transmission group + */ + +static inline +bool +_pgm_rxw_is_last_of_tg_sqn ( + pgm_rxw_t* const window, + const uint32_t sequence + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + + return _pgm_rxw_pkt_sqn (window, sequence) == window->tg_size - 1; +} + +/* set PGM skbuff to new FSM state. + */ + +static +void +_pgm_rxw_state ( + pgm_rxw_t* const restrict window, + struct pgm_sk_buff_t* const restrict skb, + const int new_pkt_state + ) +{ + pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != skb); + +/* remove current state */ + if (PGM_PKT_STATE_ERROR != state->pkt_state) + _pgm_rxw_unlink (window, skb); + + switch (new_pkt_state) { + case PGM_PKT_STATE_BACK_OFF: + pgm_queue_push_head_link (&window->nak_backoff_queue, (pgm_list_t*)skb); + break; + + case PGM_PKT_STATE_WAIT_NCF: + pgm_queue_push_head_link (&window->wait_ncf_queue, (pgm_list_t*)skb); + break; + + case PGM_PKT_STATE_WAIT_DATA: + pgm_queue_push_head_link (&window->wait_data_queue, (pgm_list_t*)skb); + break; + + case PGM_PKT_STATE_HAVE_DATA: + window->fragment_count++; + pgm_assert_cmpuint (window->fragment_count, <=, pgm_rxw_length (window)); + break; + + case PGM_PKT_STATE_HAVE_PARITY: + window->parity_count++; + pgm_assert_cmpuint (window->parity_count, <=, pgm_rxw_length (window)); + break; + + case PGM_PKT_STATE_COMMIT_DATA: + window->committed_count++; + pgm_assert_cmpuint (window->committed_count, <=, pgm_rxw_length (window)); + break; + + case PGM_PKT_STATE_LOST_DATA: + window->lost_count++; + window->cumulative_losses++; + window->has_event = 1; + pgm_assert_cmpuint (window->lost_count, <=, pgm_rxw_length (window)); + break; + + case PGM_PKT_STATE_ERROR: + break; + + default: pgm_assert_not_reached(); break; + } + + state->pkt_state = new_pkt_state; +} + +void +pgm_rxw_state ( + pgm_rxw_t* const restrict window, + struct pgm_sk_buff_t* const restrict skb, + const int new_pkt_state + ) +{ + pgm_debug ("state (window:%p skb:%p new_pkt_state:%s)", + (const void*)window, (const void*)skb, pgm_pkt_state_string (new_pkt_state)); + _pgm_rxw_state (window, skb, new_pkt_state); +} + +/* remove current state from sequence. + */ + +static +void +_pgm_rxw_unlink ( + pgm_rxw_t* const restrict window, + struct pgm_sk_buff_t* const restrict skb + ) +{ + pgm_queue_t* queue; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != skb); + + pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; + + switch (state->pkt_state) { + case PGM_PKT_STATE_BACK_OFF: + pgm_assert (!pgm_queue_is_empty (&window->nak_backoff_queue)); + queue = &window->nak_backoff_queue; + goto unlink_queue; + + case PGM_PKT_STATE_WAIT_NCF: + pgm_assert (!pgm_queue_is_empty (&window->wait_ncf_queue)); + queue = &window->wait_ncf_queue; + goto unlink_queue; + + case PGM_PKT_STATE_WAIT_DATA: + pgm_assert (!pgm_queue_is_empty (&window->wait_data_queue)); + queue = &window->wait_data_queue; +unlink_queue: + pgm_queue_unlink (queue, (pgm_list_t*)skb); + break; + + case PGM_PKT_STATE_HAVE_DATA: + pgm_assert_cmpuint (window->fragment_count, >, 0); + window->fragment_count--; + break; + + case PGM_PKT_STATE_HAVE_PARITY: + pgm_assert_cmpuint (window->parity_count, >, 0); + window->parity_count--; + break; + + case PGM_PKT_STATE_COMMIT_DATA: + pgm_assert_cmpuint (window->committed_count, >, 0); + window->committed_count--; + break; + + case PGM_PKT_STATE_LOST_DATA: + pgm_assert_cmpuint (window->lost_count, >, 0); + window->lost_count--; + break; + + case PGM_PKT_STATE_ERROR: + break; + + default: pgm_assert_not_reached(); break; + } + + state->pkt_state = PGM_PKT_STATE_ERROR; + pgm_assert (((pgm_list_t*)skb)->next == NULL); + pgm_assert (((pgm_list_t*)skb)->prev == NULL); +} + +/* returns the pointer at the given index of the window. + */ + +struct pgm_sk_buff_t* +pgm_rxw_peek ( + pgm_rxw_t* const window, + const uint32_t sequence + ) +{ + pgm_debug ("peek (window:%p sequence:%" PRIu32 ")", (void*)window, sequence); + return _pgm_rxw_peek (window, sequence); +} + +/* mark an existing sequence lost due to failed recovery. + */ + +void +pgm_rxw_lost ( + pgm_rxw_t* const window, + const uint32_t sequence + ) +{ + struct pgm_sk_buff_t* skb; + pgm_rxw_state_t* state; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (!pgm_rxw_is_empty (window)); + + pgm_debug ("lost (window:%p sequence:%" PRIu32 ")", + (const void*)window, sequence); + + skb = _pgm_rxw_peek (window, sequence); + pgm_assert (NULL != skb); + + state = (pgm_rxw_state_t*)&skb->cb; + + if (PGM_UNLIKELY(!(state->pkt_state == PGM_PKT_STATE_BACK_OFF || + state->pkt_state == PGM_PKT_STATE_WAIT_NCF || + state->pkt_state == PGM_PKT_STATE_WAIT_DATA || + state->pkt_state == PGM_PKT_STATE_HAVE_DATA || /* fragments */ + state->pkt_state == PGM_PKT_STATE_HAVE_PARITY))) + { + pgm_fatal (_("Unexpected state %s(%u)"), pgm_pkt_state_string (state->pkt_state), state->pkt_state); + pgm_assert_not_reached(); + } + + _pgm_rxw_state (window, skb, PGM_PKT_STATE_LOST_DATA); +} + +/* received a uni/multicast ncf, search for a matching nak & tag or extend window if + * beyond lead + * + * returns: + * PGM_RXW_BOUNDS - sequence is outside of window, or window is undefined. + * PGM_RXW_UPDATED - receiver state updated, waiting for data. + * PGM_RXW_DUPLICATE - data already exists at sequence. + * PGM_RXW_APPENDED - lead is extended with state set waiting for data. + */ + +int +pgm_rxw_confirm ( + pgm_rxw_t* const window, + const uint32_t sequence, + const pgm_time_t now, + const pgm_time_t nak_rdata_expiry, /* pre-calculated expiry times */ + const pgm_time_t nak_rb_expiry + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + + pgm_debug ("confirm (window:%p sequence:%" PRIu32 " nak_rdata_expiry:%" PGM_TIME_FORMAT " nak_rb_expiry:%" PGM_TIME_FORMAT ")", + (void*)window, sequence, nak_rdata_expiry, nak_rb_expiry); + +/* NCFs do not define the transmit window */ + if (PGM_UNLIKELY(!window->is_defined)) + return PGM_RXW_BOUNDS; + +/* sequence already committed */ + if (pgm_uint32_lt (sequence, window->commit_lead)) { + if (pgm_uint32_gte (sequence, window->trail)) + return PGM_RXW_DUPLICATE; + else + return PGM_RXW_BOUNDS; + } + + if (pgm_uint32_lte (sequence, window->lead)) + return _pgm_rxw_recovery_update (window, sequence, nak_rdata_expiry); + + if (sequence == window->lead) + return _pgm_rxw_recovery_append (window, now, nak_rdata_expiry); + else { + _pgm_rxw_add_placeholder_range (window, sequence, now, nak_rb_expiry); + return _pgm_rxw_recovery_append (window, now, nak_rdata_expiry); + } +} + +/* update an incoming sequence with state transition to WAIT-DATA. + * + * returns: + * PGM_RXW_UPDATED - receiver state updated, waiting for data. + * PGM_RXW_DUPLICATE - data already exists at sequence. + */ + +static inline +int +_pgm_rxw_recovery_update ( + pgm_rxw_t* const window, + const uint32_t sequence, + const pgm_time_t nak_rdata_expiry /* pre-calculated expiry times */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + +/* fetch skb from window and bump expiration times */ + struct pgm_sk_buff_t* skb = _pgm_rxw_peek (window, sequence); + pgm_assert (NULL != skb); + pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; + switch (state->pkt_state) { + case PGM_PKT_STATE_BACK_OFF: + case PGM_PKT_STATE_WAIT_NCF: + pgm_rxw_state (window, skb, PGM_PKT_STATE_WAIT_DATA); + +/* fall through */ + case PGM_PKT_STATE_WAIT_DATA: + state->timer_expiry = nak_rdata_expiry; + return PGM_RXW_UPDATED; + + case PGM_PKT_STATE_HAVE_DATA: + case PGM_PKT_STATE_HAVE_PARITY: + case PGM_PKT_STATE_COMMIT_DATA: + case PGM_PKT_STATE_LOST_DATA: + break; + + default: pgm_assert_not_reached(); break; + } + + return PGM_RXW_DUPLICATE; +} + +/* append an skb to the incoming window with WAIT-DATA state. + * + * returns: + * PGM_RXW_APPENDED - lead is extended with state set waiting for data. + * PGM_RXW_BOUNDS - constrained by commit window + */ + +static inline +int +_pgm_rxw_recovery_append ( + pgm_rxw_t* const window, + const pgm_time_t now, + const pgm_time_t nak_rdata_expiry /* pre-calculated expiry times */ + ) +{ + struct pgm_sk_buff_t* skb; + +/* pre-conditions */ + pgm_assert (NULL != window); + + if (pgm_rxw_is_full (window)) { + if (_pgm_rxw_commit_is_empty (window)) { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Receive window full on confirmed sequence.")); + _pgm_rxw_remove_trail (window); + } else { + return PGM_RXW_BOUNDS; /* constrained by commit window */ + } + } + +/* advance leading edge */ + window->lead++; + +/* add loss to bitmap */ + window->bitmap <<= 1; + +/* update the Exponential Moving Average (EMA) data loss with loss: + * s_t = α × x_{t-1} + (1 - α) × s_{t-1} + * x_{t-1} = 1 + * ∴ s_t = α + (1 - α) × s_{t-1} + */ + window->data_loss = window->ack_c_p + pgm_fp16mul (pgm_fp16 (1) - window->ack_c_p, window->data_loss); + + skb = pgm_alloc_skb (window->max_tpdu); + pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; + skb->tstamp = now; + skb->sequence = window->lead; + state->timer_expiry = nak_rdata_expiry; + + const uint_fast32_t index_ = pgm_rxw_lead (window) % pgm_rxw_max_length (window); + window->pdata[index_] = skb; + _pgm_rxw_state (window, skb, PGM_PKT_STATE_WAIT_DATA); + + return PGM_RXW_APPENDED; +} + +/* dumps window state to stdout + */ + +void +pgm_rxw_dump ( + const pgm_rxw_t* const window + ) +{ + pgm_info ("window = {" + "tsi = {gsi = {identifier = %i.%i.%i.%i.%i.%i}, sport = %" PRIu16 "}, " + "nak_backoff_queue = {head = %p, tail = %p, length = %u}, " + "wait_ncf_queue = {head = %p, tail = %p, length = %u}, " + "wait_data_queue = {head = %p, tail = %p, length = %u}, " + "lost_count = %" PRIu32 ", " + "fragment_count = %" PRIu32 ", " + "parity_count = %" PRIu32 ", " + "committed_count = %" PRIu32 ", " + "max_tpdu = %" PRIu16 ", " + "tg_size = %" PRIu32 ", " + "tg_sqn_shift = %u, " + "lead = %" PRIu32 ", " + "trail = %" PRIu32 ", " + "rxw_trail = %" PRIu32 ", " + "rxw_trail_init = %" PRIu32 ", " + "commit_lead = %" PRIu32 ", " + "is_constrained = %u, " + "is_defined = %u, " + "has_event = %u, " + "is_fec_available = %u, " + "min_fill_time = %" PRIu32 ", " + "max_fill_time = %" PRIu32 ", " + "min_nak_transmit_count = %" PRIu32 ", " + "max_nak_transmit_count = %" PRIu32 ", " + "cumulative_losses = %" PRIu32 ", " + "bytes_delivered = %" PRIu32 ", " + "msgs_delivered = %" PRIu32 ", " + "size = %zu, " + "alloc = %" PRIu32 ", " + "pdata = []" + "}", + window->tsi->gsi.identifier[0], + window->tsi->gsi.identifier[1], + window->tsi->gsi.identifier[2], + window->tsi->gsi.identifier[3], + window->tsi->gsi.identifier[4], + window->tsi->gsi.identifier[5], + ntohs (window->tsi->sport), + (void*)window->nak_backoff_queue.head, + (void*)window->nak_backoff_queue.tail, + window->nak_backoff_queue.length, + (void*)window->wait_ncf_queue.head, + (void*)window->wait_ncf_queue.tail, + window->wait_ncf_queue.length, + (void*)window->wait_data_queue.head, + (void*)window->wait_data_queue.tail, + window->wait_data_queue.length, + window->lost_count, + window->fragment_count, + window->parity_count, + window->committed_count, + window->max_tpdu, + window->tg_size, + window->tg_sqn_shift, + window->lead, + window->trail, + window->rxw_trail, + window->rxw_trail_init, + window->commit_lead, + window->is_constrained, + window->is_defined, + window->has_event, + window->is_fec_available, + window->min_fill_time, + window->max_fill_time, + window->min_nak_transmit_count, + window->max_nak_transmit_count, + window->cumulative_losses, + window->bytes_delivered, + window->msgs_delivered, + window->size, + window->alloc + ); +} + +/* state string helper + */ + +const char* +pgm_pkt_state_string ( + const int pkt_state + ) +{ + const char* c; + + switch (pkt_state) { + case PGM_PKT_STATE_BACK_OFF: c = "PGM_PKT_STATE_BACK_OFF"; break; + case PGM_PKT_STATE_WAIT_NCF: c = "PGM_PKT_STATE_WAIT_NCF"; break; + case PGM_PKT_STATE_WAIT_DATA: c = "PGM_PKT_STATE_WAIT_DATA"; break; + case PGM_PKT_STATE_HAVE_DATA: c = "PGM_PKT_STATE_HAVE_DATA"; break; + case PGM_PKT_STATE_HAVE_PARITY: c = "PGM_PKT_STATE_HAVE_PARITY"; break; + case PGM_PKT_STATE_COMMIT_DATA: c = "PGM_PKT_STATE_COMMIT_DATA"; break; + case PGM_PKT_STATE_LOST_DATA: c = "PGM_PKT_STATE_LOST_DATA"; break; + case PGM_PKT_STATE_ERROR: c = "PGM_PKT_STATE_ERROR"; break; + default: c = "(unknown)"; break; + } + + return c; +} + +const char* +pgm_rxw_returns_string ( + const int rxw_returns + ) +{ + const char* c; + + switch (rxw_returns) { + case PGM_RXW_OK: c = "PGM_RXW_OK"; break; + case PGM_RXW_INSERTED: c = "PGM_RXW_INSERTED"; break; + case PGM_RXW_APPENDED: c = "PGM_RXW_APPENDED"; break; + case PGM_RXW_UPDATED: c = "PGM_RXW_UPDATED"; break; + case PGM_RXW_MISSING: c = "PGM_RXW_MISSING"; break; + case PGM_RXW_DUPLICATE: c = "PGM_RXW_DUPLICATE"; break; + case PGM_RXW_MALFORMED: c = "PGM_RXW_MALFORMED"; break; + case PGM_RXW_BOUNDS: c = "PGM_RXW_BOUNDS"; break; + case PGM_RXW_SLOW_CONSUMER: c = "PGM_RXW_SLOW_CONSUMER"; break; + case PGM_RXW_UNKNOWN: c = "PGM_RXW_UNKNOWN"; break; + default: c = "(unknown)"; break; + } + + return c; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/rxw.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/rxw.c.c89.patch new file mode 100644 index 0000000..1eef6b2 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/rxw.c.c89.patch @@ -0,0 +1,565 @@ +--- rxw.c 2010-08-04 17:06:11.000000000 +0800 ++++ rxw.c89 2010-08-05 11:36:04.000000000 +0800 +@@ -198,11 +198,12 @@ + pgm_assert_cmpuint (max_rte, >, 0); + } + +- pgm_debug ("create (tsi:%s max-tpdu:%" PRIu16 " sqns:%" PRIu32 " secs %u max-rte %zd ack-c_p %" PRIu32 ")", ++ pgm_debug ("create (tsi:%s max-tpdu:%" PRIu16 " sqns:%" PRIu32 " secs %u max-rte %ld ack-c_p %" PRIu32 ")", + pgm_tsi_print (tsi), tpdu_size, sqns, secs, max_rte, ack_c_p); + + /* calculate receive window parameters */ + pgm_assert (sqns || (secs && max_rte)); ++ { + const unsigned alloc_sqns = sqns ? sqns : ( (secs * max_rte) / tpdu_size ); + window = pgm_malloc0 (sizeof(pgm_rxw_t) + ( alloc_sqns * sizeof(struct pgm_sk_buff_t*) )); + +@@ -238,6 +239,7 @@ + pgm_assert (!pgm_rxw_is_full (window)); + + return window; ++ } + } + + /* destructor for receive window. must not be called more than once for same window. +@@ -308,7 +310,7 @@ + /* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != skb); +- pgm_assert_cmpuint (nak_rb_expiry, >, 0); ++ pgm_assert_cmpuint ((unsigned int)nak_rb_expiry, >, 0); + pgm_assert_cmpuint (pgm_rxw_max_length (window), >, 0); + pgm_assert (pgm_skb_is_valid (skb)); + pgm_assert (((const pgm_list_t*)skb)->next == NULL); +@@ -371,6 +373,7 @@ + return _pgm_rxw_insert (window, skb); + } + ++ { + const struct pgm_sk_buff_t* const first_skb = _pgm_rxw_peek (window, _pgm_rxw_tg_sqn (window, skb->sequence)); + const pgm_rxw_state_t* const first_state = (pgm_rxw_state_t*)&first_skb->cb; + +@@ -385,6 +388,7 @@ + + pgm_assert (NULL != first_state); + status = _pgm_rxw_add_placeholder_range (window, _pgm_rxw_tg_sqn (window, skb->sequence), now, nak_rb_expiry); ++ } + } + else + { +@@ -468,7 +472,7 @@ + { + /* pre-conditions */ + pgm_assert (NULL != window); +- pgm_assert_cmpuint (nak_rb_expiry, >, 0); ++ pgm_assert_cmpuint ((unsigned int)nak_rb_expiry, >, 0); + + pgm_debug ("pgm_rxw_update (window:%p txw-lead:%" PRIu32 " txw-trail:%" PRIu32 " nak-rb-expiry:%" PGM_TIME_FORMAT ")", + (void*)window, txw_lead, txw_trail, nak_rb_expiry); +@@ -549,7 +553,9 @@ + } + + /* remove all buffers between commit lead and advertised rxw_trail */ +- for (uint32_t sequence = window->commit_lead; ++ { ++ uint32_t sequence; ++ for (sequence = window->commit_lead; + pgm_uint32_gt (window->rxw_trail, sequence) && pgm_uint32_gte (window->lead, sequence); + sequence++) + { +@@ -574,6 +580,7 @@ + break; + } + } ++ } + + /* post-conditions: only after flush */ + // pgm_assert (!pgm_rxw_is_full (window)); +@@ -636,6 +643,7 @@ + window->data_loss = window->ack_c_p + pgm_fp16mul ((pgm_fp16 (1) - window->ack_c_p), window->data_loss); + + skb = pgm_alloc_skb (window->max_tpdu); ++ { + pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; + skb->tstamp = now; + skb->sequence = window->lead; +@@ -651,6 +659,7 @@ + } + + /* add skb to window */ ++ { + const uint_fast32_t index_ = skb->sequence % pgm_rxw_max_length (window); + window->pdata[index_] = skb; + +@@ -660,6 +669,8 @@ + pgm_assert_cmpuint (pgm_rxw_length (window), >, 0); + pgm_assert_cmpuint (pgm_rxw_length (window), <=, pgm_rxw_max_length (window)); + pgm_assert_cmpuint (_pgm_rxw_incoming_length (window), >, 0); ++ } ++ } + } + + /* add a range of placeholders to the window. +@@ -679,6 +690,7 @@ + pgm_assert (pgm_uint32_gt (sequence, pgm_rxw_lead (window))); + + /* check bounds of commit window */ ++ { + const uint32_t new_commit_sqns = ( 1 + sequence ) - window->trail; + if ( !_pgm_rxw_commit_is_empty (window) && + (new_commit_sqns >= pgm_rxw_max_length (window)) ) +@@ -710,6 +722,7 @@ + pgm_assert (!pgm_rxw_is_full (window)); + + return PGM_RXW_APPENDED; ++ } + } + + /* update leading edge of receive window. +@@ -733,6 +746,7 @@ + if (PGM_UNLIKELY(pgm_uint32_lte (txw_lead, window->lead))) + return 0; + ++ { + uint32_t lead; + + /* committed packets limit constrain the lead until they are released */ +@@ -746,6 +760,7 @@ + else + lead = txw_lead; + ++ { + unsigned lost = 0; + + while (window->lead != lead) +@@ -761,6 +776,8 @@ + } + + return lost; ++ } ++ } + } + + /* checks whether an APDU is unrecoverable due to lost TPDUs. +@@ -786,22 +803,28 @@ + if (!skb->pgm_opt_fragment) + return FALSE; + ++ { + const uint32_t apdu_first_sqn = ntohl (skb->of_apdu_first_sqn); + + /* by definition, first fragment indicates APDU is available */ + if (apdu_first_sqn == skb->sequence) + return FALSE; + ++ { + const struct pgm_sk_buff_t* const first_skb = _pgm_rxw_peek (window, apdu_first_sqn); + /* first fragment out-of-bounds */ + if (NULL == first_skb) + return TRUE; + ++ { + const pgm_rxw_state_t* first_state = (pgm_rxw_state_t*)&first_skb->cb; + if (PGM_PKT_STATE_LOST_DATA == first_state->pkt_state) + return TRUE; + + return FALSE; ++ } ++ } ++ } + } + + /* return the first missing packet sequence in the specified transmission +@@ -821,7 +844,9 @@ + /* pre-conditions */ + pgm_assert (NULL != window); + +- for (uint32_t i = tg_sqn, j = 0; j < window->tg_size; i++, j++) ++ { ++ uint32_t i, j; ++ for (i = tg_sqn, j = 0; j < window->tg_size; i++, j++) + { + skb = _pgm_rxw_peek (window, i); + pgm_assert (NULL != skb); +@@ -840,6 +865,7 @@ + default: pgm_assert_not_reached(); break; + } + } ++ } + + return NULL; + } +@@ -867,6 +893,7 @@ + if (skb->pgm_header->pgm_options & PGM_OPT_VAR_PKTLEN) + return FALSE; + ++ { + const uint32_t tg_sqn = _pgm_rxw_tg_sqn (window, skb->sequence); + if (tg_sqn == skb->sequence) + return FALSE; +@@ -879,6 +906,7 @@ + return FALSE; + + return TRUE; ++ } + } + + static inline +@@ -913,6 +941,7 @@ + if (!window->is_fec_available) + return FALSE; + ++ { + const uint32_t tg_sqn = _pgm_rxw_tg_sqn (window, skb->sequence); + if (tg_sqn == skb->sequence) + return FALSE; +@@ -925,6 +954,7 @@ + return FALSE; + + return TRUE; ++ } + } + + /* insert skb into window range, discard if duplicate. window will have placeholder, +@@ -997,20 +1027,21 @@ + } + + /* statistics */ ++ { + const pgm_time_t fill_time = new_skb->tstamp - skb->tstamp; + PGM_HISTOGRAM_TIMES("Rx.RepairTime", fill_time); + PGM_HISTOGRAM_COUNTS("Rx.NakTransmits", state->nak_transmit_count); + PGM_HISTOGRAM_COUNTS("Rx.NcfRetries", state->ncf_retry_count); + PGM_HISTOGRAM_COUNTS("Rx.DataRetries", state->data_retry_count); + if (!window->max_fill_time) { +- window->max_fill_time = window->min_fill_time = fill_time; ++ window->max_fill_time = window->min_fill_time = (uint32_t)fill_time; + } + else + { + if (fill_time > window->max_fill_time) +- window->max_fill_time = fill_time; ++ window->max_fill_time = (uint32_t)fill_time; + else if (fill_time < window->min_fill_time) +- window->min_fill_time = fill_time; ++ window->min_fill_time = (uint32_t)fill_time; + + if (!window->max_nak_transmit_count) { + window->max_nak_transmit_count = window->min_nak_transmit_count = state->nak_transmit_count; +@@ -1023,6 +1054,7 @@ + } + + /* add packet to bitmap */ ++ { + const uint_fast32_t pos = window->lead - new_skb->sequence; + if (pos < 32) { + window->bitmap |= 1 << pos; +@@ -1033,16 +1065,19 @@ + * x_{t-1} = 0 + * ∴ s_t = (1 - α) × s_{t-1} + */ ++ { + const uint_fast32_t s = pgm_fp16pow (pgm_fp16 (1) - window->ack_c_p, pos); + if (s > window->data_loss) window->data_loss = 0; + else window->data_loss -= s; + + /* replace place holder skb with incoming skb */ + memcpy (new_skb->cb, skb->cb, sizeof(skb->cb)); ++ { + pgm_rxw_state_t* rxw_state = (void*)new_skb->cb; + rxw_state->pkt_state = PGM_PKT_STATE_ERROR; + _pgm_rxw_unlink (window, skb); + pgm_free_skb (skb); ++ { + const uint_fast32_t index_ = new_skb->sequence % pgm_rxw_max_length (window); + window->pdata[index_] = new_skb; + if (new_skb->pgm_header->pgm_options & PGM_OPT_PARITY) +@@ -1052,6 +1087,11 @@ + window->size += new_skb->len; + + return PGM_RXW_INSERTED; ++ } ++ } ++ } ++ } ++ } + } + + /* shuffle parity packet at skb->sequence to any other needed spot. +@@ -1070,11 +1110,13 @@ + pgm_assert (NULL != window); + pgm_assert (NULL != skb); + ++ { + struct pgm_sk_buff_t* restrict missing = _pgm_rxw_find_missing (window, skb->sequence); + if (NULL == missing) + return; + + /* replace place holder skb with parity skb */ ++ { + char cb[48]; + _pgm_rxw_unlink (window, missing); + memcpy (cb, skb->cb, sizeof(skb->cb)); +@@ -1084,6 +1126,8 @@ + window->pdata[index_] = skb; + index_ = missing->sequence % pgm_rxw_max_length (window); + window->pdata[index_] = missing; ++ } ++ } + } + + /* skb advances the window lead. +@@ -1146,11 +1190,13 @@ + lost_skb->sequence = skb->sequence; + + /* add lost-placeholder skb to window */ ++ { + const uint_fast32_t index_ = lost_skb->sequence % pgm_rxw_max_length (window); + window->pdata[index_] = lost_skb; + + _pgm_rxw_state (window, lost_skb, PGM_PKT_STATE_LOST_DATA); + return PGM_RXW_BOUNDS; ++ } + } + + /* add skb to window */ +@@ -1185,6 +1231,7 @@ + /* pre-conditions */ + pgm_assert (NULL != window); + ++ { + const uint32_t tg_sqn_of_commit_lead = _pgm_rxw_tg_sqn (window, window->commit_lead); + + while (!_pgm_rxw_commit_is_empty (window) && +@@ -1192,6 +1239,7 @@ + { + _pgm_rxw_remove_trail (window); + } ++ } + } + + /* flush packets but instead of calling on_data append the contiguous data packets +@@ -1344,7 +1392,9 @@ + pgm_debug ("_pgm_rxw_incoming_read (window:%p pmsg:%p pmsglen:%u)", + (void*)window, (void*)pmsg, pmsglen); + ++ { + msg_end = *pmsg + pmsglen - 1; ++ { + ssize_t bytes_read = 0; + size_t data_read = 0; + +@@ -1363,6 +1413,8 @@ + window->bytes_delivered += bytes_read; + window->msgs_delivered += data_read; + return data_read > 0 ? bytes_read : -1; ++ } ++ } + } + + /* returns TRUE if transmission group is lost. +@@ -1411,16 +1463,19 @@ + skb = _pgm_rxw_peek (window, tg_sqn); + pgm_assert (NULL != skb); + ++ { + const bool is_var_pktlen = skb->pgm_header->pgm_options & PGM_OPT_VAR_PKTLEN; + const bool is_op_encoded = skb->pgm_header->pgm_options & PGM_OPT_PRESENT; + const uint16_t parity_length = ntohs (skb->pgm_header->pgm_tsdu_length); +- struct pgm_sk_buff_t* tg_skbs[ window->rs.n ]; +- pgm_gf8_t* tg_data[ window->rs.n ]; +- pgm_gf8_t* tg_opts[ window->rs.n ]; +- uint8_t offsets[ window->rs.k ]; ++ struct pgm_sk_buff_t** tg_skbs = pgm_newa (struct pgm_sk_buff_t*, window->rs.n); ++ pgm_gf8_t** tg_data = pgm_newa (pgm_gf8_t*, window->rs.n); ++ pgm_gf8_t** tg_opts = pgm_newa (pgm_gf8_t*, window->rs.n); ++ uint8_t* offsets = pgm_newa (uint8_t, window->rs.k); + uint8_t rs_h = 0; + +- for (uint32_t i = tg_sqn, j = 0; i != (tg_sqn + window->rs.k); i++, j++) ++ { ++ uint32_t i, j; ++ for (i = tg_sqn, j = 0; i != (tg_sqn + window->rs.k); i++, j++) + { + skb = _pgm_rxw_peek (window, i); + pgm_assert (NULL != skb); +@@ -1474,6 +1529,7 @@ + } + + } ++ } + + /* reconstruct payload */ + pgm_rs_decode_parity_appended (&window->rs, +@@ -1489,11 +1545,14 @@ + sizeof(struct pgm_opt_fragment)); + + /* swap parity skbs with reconstructed skbs */ +- for (uint_fast8_t i = 0; i < window->rs.k; i++) ++ { ++ uint_fast8_t i; ++ for (i = 0; i < window->rs.k; i++) + { + if (offsets[i] < window->rs.k) + continue; + ++ { + struct pgm_sk_buff_t* repair_skb = tg_skbs[i]; + + if (is_var_pktlen) +@@ -1502,17 +1561,22 @@ + if (pktlen > parity_length) { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Invalid encoded variable packet length in reconstructed packet, dropping entire transmission group.")); + pgm_free_skb (repair_skb); +- for (uint_fast8_t j = i; j < window->rs.k; j++) ++ { ++ uint_fast8_t j; ++ for (j = i; j < window->rs.k; j++) + { + if (offsets[j] < window->rs.k) + continue; + pgm_rxw_lost (window, tg_skbs[offsets[j]]->sequence); + } ++ } + break; + } ++ { + const uint16_t padding = parity_length - pktlen; + repair_skb->len -= padding; + repair_skb->tail = (char*)repair_skb->tail - padding; ++ } + } + + #ifdef PGM_DISABLE_ASSERT +@@ -1520,6 +1584,9 @@ + #else + pgm_assert_cmpint (_pgm_rxw_insert (window, repair_skb), ==, PGM_RXW_INSERTED); + #endif ++ } ++ } ++ } + } + } + +@@ -1559,6 +1626,7 @@ + return FALSE; + } + ++ { + const size_t apdu_size = skb->pgm_opt_fragment ? ntohl (skb->of_apdu_len) : skb->len; + const uint32_t tg_sqn = _pgm_rxw_tg_sqn (window, first_sequence); + uint32_t sequence = first_sequence; +@@ -1643,6 +1711,7 @@ + + /* pending */ + return FALSE; ++ } + } + + /* read one APDU consisting of one or more TPDUs. target array is guaranteed +@@ -1666,6 +1735,7 @@ + (const void*)window, (const void*)pmsg); + + skb = _pgm_rxw_peek (window, window->commit_lead); ++ { + size_t contiguous_len = 0; + const size_t apdu_len = skb->pgm_opt_fragment ? ntohl (skb->of_apdu_len) : skb->len; + unsigned i = 0; +@@ -1687,7 +1757,8 @@ + /* post-conditions */ + pgm_assert (!_pgm_rxw_commit_is_empty (window)); + +-return contiguous_len; ++ return contiguous_len; ++ } + } + + /* returns transmission group sequence (TG_SQN) from sequence (SQN). +@@ -1703,8 +1774,10 @@ + /* pre-conditions */ + pgm_assert (NULL != window); + ++ { + const uint32_t tg_sqn_mask = 0xffffffff << window->tg_sqn_shift; + return sequence & tg_sqn_mask; ++ } + } + + /* returns packet number (PKT_SQN) from sequence (SQN). +@@ -1720,8 +1793,10 @@ + /* pre-conditions */ + pgm_assert (NULL != window); + ++ { + const uint32_t tg_sqn_mask = 0xffffffff << window->tg_sqn_shift; + return sequence & ~tg_sqn_mask; ++ } + } + + /* returns TRUE when the sequence is the first of a transmission group. +@@ -1849,6 +1924,7 @@ + pgm_assert (NULL != window); + pgm_assert (NULL != skb); + ++ { + pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; + + switch (state->pkt_state) { +@@ -1898,6 +1974,7 @@ + state->pkt_state = PGM_PKT_STATE_ERROR; + pgm_assert (((pgm_list_t*)skb)->next == NULL); + pgm_assert (((pgm_list_t*)skb)->prev == NULL); ++ } + } + + /* returns the pointer at the given index of the window. +@@ -2017,8 +2094,10 @@ + pgm_assert (NULL != window); + + /* fetch skb from window and bump expiration times */ ++ { + struct pgm_sk_buff_t* skb = _pgm_rxw_peek (window, sequence); + pgm_assert (NULL != skb); ++ { + pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; + switch (state->pkt_state) { + case PGM_PKT_STATE_BACK_OFF: +@@ -2040,6 +2119,8 @@ + } + + return PGM_RXW_DUPLICATE; ++ } ++ } + } + + /* append an skb to the incoming window with WAIT-DATA state. +@@ -2085,16 +2166,20 @@ + window->data_loss = window->ack_c_p + pgm_fp16mul (pgm_fp16 (1) - window->ack_c_p, window->data_loss); + + skb = pgm_alloc_skb (window->max_tpdu); ++ { + pgm_rxw_state_t* state = (pgm_rxw_state_t*)&skb->cb; + skb->tstamp = now; + skb->sequence = window->lead; + state->timer_expiry = nak_rdata_expiry; + ++ { + const uint_fast32_t index_ = pgm_rxw_lead (window) % pgm_rxw_max_length (window); + window->pdata[index_] = skb; + _pgm_rxw_state (window, skb, PGM_PKT_STATE_WAIT_DATA); + + return PGM_RXW_APPENDED; ++ } ++ } + } + + /* dumps window state to stdout +@@ -2133,7 +2218,7 @@ + "cumulative_losses = %" PRIu32 ", " + "bytes_delivered = %" PRIu32 ", " + "msgs_delivered = %" PRIu32 ", " +- "size = %zu, " ++ "size = %lu, " + "alloc = %" PRIu32 ", " + "pdata = []" + "}", diff --git a/3rdparty/openpgm-svn-r1135/pgm/rxw_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/rxw_unittest.c new file mode 100644 index 0000000..635c854 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/rxw_unittest.c @@ -0,0 +1,1844 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for receive window. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + + +/* mock global */ + + +#define pgm_histogram_add mock_pgm_histogram_add +#define pgm_time_now mock_pgm_time_now +#define pgm_rs_create mock_pgm_rs_create +#define pgm_rs_destroy mock_pgm_rs_destroy +#define pgm_rs_decode_parity_appended mock_pgm_rs_decode_parity_appended +#define pgm_histogram_init mock_pgm_histogram_init + +#define RXW_DEBUG +#include "rxw.c" + +#ifdef PGM_DISABLE_ASSERT +# error "PGM_DISABLE_ASSERT set" +#endif + +static pgm_time_t mock_pgm_time_now = 0x1; + + +/* mock functions for external references */ + +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return 0; +} + +/** reed-solomon module */ +void +mock_pgm_rs_create ( + pgm_rs_t* rs, + uint8_t n, + uint8_t k + ) +{ +} + +void +mock_pgm_rs_destroy ( + pgm_rs_t* rs + ) +{ +} + +void +mock_pgm_rs_decode_parity_appended ( + pgm_rs_t* rs, + pgm_gf8_t** block, + const uint8_t* offsets, + uint16_t len + ) +{ +// null +} + +void +mock_pgm_histogram_init ( + pgm_histogram_t* histogram + ) +{ +} + +void +mock_pgm_histogram_add ( + pgm_histogram_t* histogram, + int value + ) +{ +} + + +/* generate valid skb, data pointer pointing to PGM payload + */ +static +struct pgm_sk_buff_t* +generate_valid_skb (void) +{ + const pgm_tsi_t tsi = { { 200, 202, 203, 204, 205, 206 }, 2000 }; + const guint16 tsdu_length = 1000; + const guint16 header_length = sizeof(struct pgm_header) + sizeof(struct pgm_data); + struct pgm_sk_buff_t* skb = pgm_alloc_skb (1500); + memcpy (&skb->tsi, &tsi, sizeof(tsi)); +/* fake but valid socket and timestamp */ + skb->sock = (pgm_sock_t*)0x1; + skb->tstamp = pgm_time_now; +/* header */ + pgm_skb_reserve (skb, header_length); + memset (skb->head, 0, header_length); + skb->pgm_header = (struct pgm_header*)skb->head; + skb->pgm_data = (struct pgm_data*)(skb->pgm_header + 1); + skb->pgm_header->pgm_type = PGM_ODATA; + skb->pgm_header->pgm_tsdu_length = g_htons (tsdu_length); +/* DATA */ + pgm_skb_put (skb, tsdu_length); + return skb; +} + +/* target: + * pgm_rxw_t* + * pgm_rxw_create ( + * const pgm_tsi_t* tsi, + * const uint16_t tpdu_size, + * const unsigned sqns, + * const unsigned secs, + * const ssize_t max_rte, + * const uint32_t ack_c_p + * ) + */ + +/* vanilla sequence count window */ +START_TEST (test_create_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + fail_if (NULL == pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p), "create failed"); +} +END_TEST + +/* vanilla time based window */ +START_TEST (test_create_pass_002) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + fail_if (NULL == pgm_rxw_create (&tsi, 1500, 0, 60, 800000, ack_c_p), "create failed"); +} +END_TEST + +/* jumbo frame */ +START_TEST (test_create_pass_003) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + fail_if (NULL == pgm_rxw_create (&tsi, 9000, 0, 60, 800000, ack_c_p), "create failed"); +} +END_TEST + +/* max frame */ +START_TEST (test_create_pass_004) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + fail_if (NULL == pgm_rxw_create (&tsi, UINT16_MAX, 0, 60, 800000, ack_c_p), "create failed"); +} +END_TEST + +/* invalid tsi pointer */ +START_TEST (test_create_fail_001) +{ + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (NULL, 1500, 100, 0, 0, ack_c_p); + fail ("reached"); +} +END_TEST + +/* invalid tpdu size */ +START_TEST (test_create_fail_002) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + fail_if (NULL == pgm_rxw_create (&tsi, 0, 100, 0, 0, ack_c_p), "create failed"); +} +END_TEST + +START_TEST (test_create_fail_003) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 0, 0, 60, 800000, ack_c_p); + fail ("reached"); +} +END_TEST + +/* no specified sequence count or time value */ +START_TEST (test_create_fail_004) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 0, 0, 0, 800000, ack_c_p); + fail ("reached"); +} +END_TEST + +/* no specified rate */ +START_TEST (test_create_fail_005) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 0, 0, 60, 0, ack_c_p); + fail ("reached"); +} +END_TEST + +/* all invalid */ +START_TEST (test_create_fail_006) +{ + pgm_rxw_t* window = pgm_rxw_create (NULL, 0, 0, 0, 0, 0); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_rxw_destroy ( + * pgm_rxw_t* const window + * ) + */ + +START_TEST (test_destroy_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_destroy_fail_001) +{ + pgm_rxw_destroy (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * int + * pgm_rxw_add ( + * pgm_rxw_t* const window, + * struct pgm_sk_buff_t* const skb, + * const pgm_time_t now, + * const pgm_time_t nak_rb_expiry + * ) + * failures raise assert errors and stop process execution. + */ + +START_TEST (test_add_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + pgm_rxw_destroy (window); +} +END_TEST + +/* missing + inserted */ +START_TEST (test_add_pass_002) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); +/* #1 */ + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); +/* #2 with jump */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (2); + fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not missing"); +/* #3 to fill in gap */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (1); + fail_unless (PGM_RXW_INSERTED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not inserted"); + pgm_rxw_destroy (window); +} +END_TEST + +/* duplicate + append */ +START_TEST (test_add_pass_003) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); +/* #1 */ + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); +/* #2 repeat sequence */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + fail_unless (PGM_RXW_DUPLICATE == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not duplicate"); +/* #3 append */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (1); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + pgm_rxw_destroy (window); +} +END_TEST + +/* malformed: tpdu too long */ +START_TEST (test_add_pass_004) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_header->pgm_tsdu_length = g_htons (65535); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_MALFORMED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not malformed"); +} +END_TEST + +/* bounds + append */ +START_TEST (test_add_pass_005) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); +/* #1 */ + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + skb->pgm_data->data_trail = g_htonl (-10); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); +/* #2 jump backwards */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (-1); + skb->pgm_data->data_trail = g_htonl (-10); + fail_unless (PGM_RXW_BOUNDS == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not bounds"); +/* #3 append */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (1); + skb->pgm_data->data_trail = g_htonl (-10); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); +/* #4 jump forward */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (100 + (UINT32_MAX / 2)); + skb->pgm_data->data_trail = g_htonl (UINT32_MAX / 2); + fail_unless (PGM_RXW_BOUNDS == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not bounds"); +/* #5 append */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (2); + skb->pgm_data->data_trail = g_htonl (-10); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + pgm_rxw_destroy (window); +} +END_TEST + +/* null skb */ +START_TEST (test_add_fail_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + int retval = pgm_rxw_add (window, NULL, now, nak_rb_expiry); + fail ("reached"); +} +END_TEST + +/* null window */ +START_TEST (test_add_fail_002) +{ + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + int retval = pgm_rxw_add (NULL, skb, now, nak_rb_expiry); + fail ("reached"); +} +END_TEST + +/* null skb content */ +START_TEST (test_add_fail_003) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + char buffer[1500]; + memset (buffer, 0, sizeof(buffer)); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + int retval = pgm_rxw_add (window, (struct pgm_sk_buff_t*)buffer, now, nak_rb_expiry); + fail ("reached"); +} +END_TEST + +/* 0 nak_rb_expiry */ +START_TEST (test_add_fail_004) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + int retval = pgm_rxw_add (window, skb, now, 0); + fail ("reached"); +} +END_TEST + +/* target: + * struct pgm_sk_buff_t* + * pgm_rxw_peek ( + * pgm_rxw_t* const window, + * const uint32_t sequence + * ) + */ + +START_TEST (test_peek_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + fail_unless (NULL == pgm_rxw_peek (window, 0)); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (skb == pgm_rxw_peek (window, 0), "peek failed"); + fail_unless (NULL == pgm_rxw_peek (window, 1), "peek failed"); + fail_unless (NULL == pgm_rxw_peek (window, -1), "peek failed"); + pgm_rxw_destroy (window); +} +END_TEST + +/* null window */ +START_TEST (test_peek_fail_001) +{ + struct pgm_sk_buff_t* skb = pgm_rxw_peek (NULL, 0); + fail ("reached"); +} +END_TEST + +/** inline function tests **/ +/* pgm_rxw_max_length () + */ +START_TEST (test_max_length_pass_001) +{ + const guint window_length = 100; + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, window_length, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + fail_unless (window_length == pgm_rxw_max_length (window), "max_length failed"); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_max_length_fail_001) +{ + const unsigned len = pgm_rxw_max_length (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_rxw_length () + */ +START_TEST (test_length_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + fail_unless (0 == pgm_rxw_length (window), "length failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (1 == pgm_rxw_length (window), "length failed"); +/* #2 */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (1); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (2 == pgm_rxw_length (window), "length failed"); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_length_fail_001) +{ + const uint32_t answer = pgm_rxw_length (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_rxw_size () + */ +START_TEST (test_size_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + fail_unless (0 == pgm_rxw_size (window), "size failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (1000 == pgm_rxw_size (window), "size failed"); +/* #2 */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (1); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (2000 == pgm_rxw_size (window), "size failed"); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_size_fail_001) +{ + const size_t answer = pgm_rxw_size (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_rxw_is_empty + */ +START_TEST (test_is_empty_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + fail_unless (pgm_rxw_is_empty (window), "is_empty failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_if (pgm_rxw_is_empty (window), "is_empty failed"); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_is_empty_fail_001) +{ + const bool answer = pgm_rxw_is_empty (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_rxw_is_full + */ +START_TEST (test_is_full_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 1, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + fail_if (pgm_rxw_is_full (window), "is_full failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (pgm_rxw_is_full (window), "is_full failed"); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_is_full_fail_001) +{ + const bool answer = pgm_rxw_is_full (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_rxw_lead + */ +START_TEST (test_lead_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + guint32 lead = pgm_rxw_lead (window); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (lead + 1 == pgm_rxw_lead (window), "lead failed"); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_lead_fail_001) +{ + const uint32_t answer = pgm_rxw_lead (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_rxw_next_lead + */ +START_TEST (test_next_lead_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + guint32 next_lead = pgm_rxw_next_lead (window); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (next_lead == pgm_rxw_lead (window), "lead failed"); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_next_lead_fail_001) +{ + const uint32_t answer = pgm_rxw_next_lead (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * ssize_t + * pgm_rxw_readv ( + * pgm_rxw_t* const window, + * struct pgm_msgv_t** pmsg, + * const unsigned msg_len + * ) + */ + +START_TEST (test_readv_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_msgv_t msgv[2], *pmsg; +/* #1 empty */ + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); +/* #2 single TPDU-APDU */ + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + pmsg = msgv; + fail_unless (1000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); +/* #3,4 two APDUs */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (1); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (2); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + pmsg = msgv; + fail_unless (2000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); +/* #5,6 skip and repair APDU */ + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (4); + fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not missing"); + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (3); + fail_unless (PGM_RXW_INSERTED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not inserted"); + pmsg = msgv; + fail_unless (2000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + pgm_rxw_destroy (window); +} +END_TEST + +/* zero-length */ +START_TEST (test_readv_pass_002) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_msgv_t msgv[2], *pmsg; + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + pgm_rxw_destroy (window); +} +END_TEST + +/* full window */ +START_TEST (test_readv_pass_003) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_msgv_t msgv[1], *pmsg; + struct pgm_sk_buff_t* skb; + for (unsigned i = 0; i < 100; i++) + { + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless ((1 + i) == pgm_rxw_length (window), "length failed"); + } + fail_unless (pgm_rxw_is_full (window), "is_full failed"); + fail_unless (_pgm_rxw_commit_is_empty (window), "commit_is_empty failed"); + for (unsigned i = 0; i < 100; i++) + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + fail_unless ((1 + i) == _pgm_rxw_commit_length (window), "commit_length failed"); + } + fail_unless (pgm_rxw_length (window) == _pgm_rxw_commit_length (window), "commit_length failed"); + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + pgm_rxw_destroy (window); +} +END_TEST + +/* full + 1 window */ +START_TEST (test_readv_pass_004) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_msgv_t msgv[1], *pmsg; + struct pgm_sk_buff_t* skb; + for (unsigned i = 0; i < 101; i++) + { + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (MIN(100, 1 + i) == pgm_rxw_length (window), "length failed"); + } + fail_unless (pgm_rxw_is_full (window), "is_full failed"); + fail_unless (_pgm_rxw_commit_is_empty (window), "commit_is_empty failed"); + for (unsigned i = 0; i < 100; i++) + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + fail_unless ((1 + i) == _pgm_rxw_commit_length (window), "commit_length failed"); + } + fail_unless (pgm_rxw_length (window) == _pgm_rxw_commit_length (window), "commit_length failed"); + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + pgm_rxw_destroy (window); +} +END_TEST + +/* full - 2 lost last in window */ +START_TEST (test_readv_pass_005) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_msgv_t msgv[1], *pmsg; + struct pgm_sk_buff_t* skb; + for (unsigned i = 0; i < 98; i++) + { + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless ((1 + i) == pgm_rxw_length (window), "length failed"); + } + fail_if (pgm_rxw_is_full (window), "is_full failed"); + fail_unless (_pgm_rxw_commit_is_empty (window), "commit_is_empty failed"); + { + unsigned i = 99; + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not missing"); + fail_unless ((1 + i) == pgm_rxw_length (window), "length failed"); + } + fail_unless (pgm_rxw_is_full (window)); + fail_unless (_pgm_rxw_commit_is_empty (window)); + for (unsigned i = 0; i < 98; i++) + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + fail_unless ((1 + i) == _pgm_rxw_commit_length (window), "commit_length failed"); + } + fail_unless (pgm_rxw_length (window) == (2 + _pgm_rxw_commit_length (window)), "commit_length failed"); +/* read end-of-window */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + } + pgm_rxw_destroy (window); +} +END_TEST + +/* add full window, readv 1 skb, add 1 more */ +START_TEST (test_readv_pass_006) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_msgv_t msgv[1], *pmsg; + struct pgm_sk_buff_t* skb; + for (unsigned i = 0; i < 100; i++) + { + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (MIN(100, 1 + i) == pgm_rxw_length (window), "length failed"); + } + fail_unless (pgm_rxw_is_full (window)); + fail_unless (_pgm_rxw_commit_is_empty (window)); +/* read one skb */ + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + fail_unless (1 == _pgm_rxw_commit_length (window), "commit_length failed"); + } +/* add one more new skb */ + { + unsigned i = 100; + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_BOUNDS == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not bounds"); + fail_unless (MIN(100, 1 + i) == pgm_rxw_length (window), "length failed"); + } +/* read off 99 more skbs */ + for (unsigned i = 0; i < 99; i++) + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + fail_unless ((2 + i) == _pgm_rxw_commit_length (window), "commit_length failed"); + } +/* read end-of-window */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + } + pgm_rxw_destroy (window); +} +END_TEST + +/* NULL window */ +START_TEST (test_readv_fail_001) +{ + struct pgm_msgv_t msgv[1], *pmsg = msgv; + gssize len = pgm_rxw_readv (NULL, &pmsg, G_N_ELEMENTS(msgv)); + fail ("reached"); +} +END_TEST + +/* NULL pmsg */ +START_TEST (test_readv_fail_002) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + struct pgm_msgv_t msgv[1], *pmsg = msgv; + gssize len = pgm_rxw_readv (window, NULL, G_N_ELEMENTS(msgv)); + fail ("reached"); +} +END_TEST + +/* 0 msg-len */ +START_TEST (test_readv_fail_003) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + struct pgm_msgv_t msgv[1], *pmsg = msgv; + gssize len = pgm_rxw_readv (window, &pmsg, 0); + fail ("reached"); +} +END_TEST + +/* target: + * + * void + * pgm_rxw_remove_commit ( + * pgm_rxw_t* const window + * ) + */ + +/* full - 2 lost last in window */ +START_TEST (test_remove_commit_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_msgv_t msgv[1], *pmsg; + struct pgm_sk_buff_t* skb; + for (unsigned i = 0; i < 98; i++) + { + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless ((1 + i) == pgm_rxw_length (window), "length failed"); + } + fail_if (pgm_rxw_is_full (window)); + fail_unless (_pgm_rxw_commit_is_empty (window)); +/* #98 is missing */ + { + unsigned i = 99; + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless ((1 + i) == pgm_rxw_length (window), "length failed"); + } + fail_unless (pgm_rxw_is_full (window), "is_full failed"); + fail_unless (_pgm_rxw_commit_is_empty (window), "commit_is_empty"); +/* now mark #98 lost */ + pgm_rxw_lost (window, 98); + for (unsigned i = 0; i < 98; i++) + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + fail_unless ((1 + i) == _pgm_rxw_commit_length (window), "commit_length failed"); + } + fail_unless (100 == pgm_rxw_length (window), "length failed"); + fail_unless ( 98 == _pgm_rxw_commit_length (window), "commit_length failed"); +/* read end-of-window */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + } + fail_unless (100 == pgm_rxw_length (window), "length failed"); + fail_unless ( 98 == _pgm_rxw_commit_length (window), "commit_length failed"); + pgm_rxw_remove_commit (window); +/* read lost skb #98 */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + } + pgm_rxw_remove_commit (window); +/* read valid skb #99 */ + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + } +/* read end-of-window */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + } + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_remove_commit_fail_001) +{ + pgm_rxw_remove_commit (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * unsigned + * pgm_rxw_remove_trail ( + * pgm_rxw_t* const window + * ) + */ + +START_TEST (test_remove_trail_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_msgv_t msgv[2], *pmsg; + fail_unless (0 == pgm_rxw_remove_trail (window), "remove_trail failed"); +/* #1,2 two APDUs */ + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (1); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry)); + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (2); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry)); + fail_unless (1 == pgm_rxw_remove_trail (window), "remove_trail failed"); + fail_unless (1 == pgm_rxw_length (window), "length failed"); + fail_unless (1000 == pgm_rxw_size (window), "size failed"); + pmsg = msgv; + fail_unless (1000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + fail_unless (0 == pgm_rxw_remove_trail (window), "remove_trail failed"); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_remove_trail_fail_001) +{ + guint count = pgm_rxw_remove_trail (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * unsigned + * pgm_rxw_update ( + * pgm_rxw_t* const window, + * const uint32_t txw_trail, + * const uint32_t txw_lead, + * const pgm_time_t now, + * const pgm_time_t nak_rb_expiry + * ) + */ + +START_TEST (test_update_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (0 == pgm_rxw_update (window, 100, 99, now, nak_rb_expiry), "update failed"); +/* dupe */ + fail_unless (0 == pgm_rxw_update (window, 100, 99, now, nak_rb_expiry), "update failed"); +/* #1 at 100 */ + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (100); + fail_unless (PGM_RXW_BOUNDS == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not bounds"); +/* #2 at 101 */ + skb->pgm_data->data_sqn = g_htonl (101); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + struct pgm_msgv_t msgv[1], *pmsg = msgv; + fail_unless (1000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); +/* #3 at 102 */ + fail_unless (1 == pgm_rxw_update (window, 102, 99, now, nak_rb_expiry), "update failed"); + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (102); + fail_unless (PGM_RXW_INSERTED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not inserted"); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_update_fail_001) +{ + guint count = pgm_rxw_update (NULL, 0, 0, 0, 0); + fail ("reached"); +} +END_TEST + +/* target: + * int + * pgm_rxw_confirm ( + * pgm_rxw_t* const window, + * const uint32_t sequence, + * const pgm_time_t now, + * const pgm_time_t nak_rdata_expiry, + * const pgm_time_t nak_rb_expiry + * ) + */ + +START_TEST (test_confirm_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + const pgm_time_t now = 1; + const pgm_time_t nak_rdata_expiry = 2; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_BOUNDS == pgm_rxw_confirm (window, 0, now, nak_rdata_expiry, nak_rb_expiry), "confirm not bounds"); +/* #1 at 100 */ + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (100); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (1 == pgm_rxw_length (window), "length failed"); + fail_unless (PGM_RXW_BOUNDS == pgm_rxw_confirm (window, 99, now, nak_rdata_expiry, nak_rb_expiry), "confirm not bounds"); + fail_unless (PGM_RXW_DUPLICATE == pgm_rxw_confirm (window, 100, now, nak_rdata_expiry, nak_rb_expiry), "confirm not duplicate"); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_confirm (window, 101, now, nak_rdata_expiry, nak_rb_expiry), "confirm not appended"); + fail_unless (2 == pgm_rxw_length (window)); + fail_unless (PGM_RXW_UPDATED == pgm_rxw_confirm (window, 101, now, nak_rdata_expiry, nak_rb_expiry), "confirm not updated"); +/* #2 at 101 */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (101); + fail_unless (PGM_RXW_INSERTED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not inserted"); + struct pgm_msgv_t msgv[2], *pmsg = msgv; + fail_unless (2000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + pgm_rxw_destroy (window); +} +END_TEST + +/* constrained confirm */ +START_TEST (test_confirm_pass_002) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_msgv_t msgv[1], *pmsg; + struct pgm_sk_buff_t* skb; + for (unsigned i = 0; i < 100; i++) + { + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (MIN(100, 1 + i) == pgm_rxw_length (window), "length failed"); + } + fail_unless (pgm_rxw_is_full (window), "is_full failed"); + fail_unless (_pgm_rxw_commit_is_empty (window), "is_empty failed"); +/* read one skb */ + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + fail_unless (1 == _pgm_rxw_commit_length (window), "commit_length failed"); + } +/* confirm next sequence */ + const pgm_time_t now = 1; + const pgm_time_t nak_rdata_expiry = 2; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_BOUNDS == pgm_rxw_confirm (window, 100, now, nak_rdata_expiry, nak_rb_expiry), "confirm not bounds"); +/* read off 99 more skbs */ + for (unsigned i = 0; i < 99; i++) + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + fail_unless ((2 + i) == _pgm_rxw_commit_length (window), "commit_length failed"); + } +/* read end-of-window */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + } + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_confirm_fail_001) +{ + int retval = pgm_rxw_confirm (NULL, 0, 0, 0, 0); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_rxw_lost ( + * pgm_rxw_t* const window, + * const uint32_t sequence + * ) + */ + +START_TEST (test_lost_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + const pgm_time_t now = 1; + const pgm_time_t nak_rdata_expiry = 2; + const pgm_time_t nak_rb_expiry = 2; +/* #1 at 100 */ + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (100); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); + fail_unless (1 == pgm_rxw_length (window), "length failed"); + fail_unless (1000 == pgm_rxw_size (window), "size failed"); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_confirm (window, 101, now, nak_rdata_expiry, nak_rb_expiry), "confirm not appended"); + fail_unless (2 == pgm_rxw_length (window), "length failed"); + fail_unless (1000 == pgm_rxw_size (window), "size failed"); + pgm_rxw_lost (window, 101); + fail_unless (2 == pgm_rxw_length (window), "length failed"); + fail_unless (1000 == pgm_rxw_size (window), "size failed"); +/* #2 at 101 */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (101); + fail_unless (PGM_RXW_INSERTED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not inserted"); + fail_unless (2 == pgm_rxw_length (window), "length failed"); + fail_unless (2000 == pgm_rxw_size (window), "size failed"); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_lost_fail_001) +{ + pgm_rxw_lost (NULL, 0); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_rxw_state ( + * pgm_rxw_t* const window, + * struct pgm_sk_buff_t* skb, + * int new_state + * ) + */ + +START_TEST (test_state_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + const pgm_time_t now = 1; + const pgm_time_t nak_rdata_expiry = 2; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (0 == pgm_rxw_update (window, 100, 99, now, nak_rb_expiry), "update failed"); + fail_unless (PGM_RXW_APPENDED == pgm_rxw_confirm (window, 101, now, nak_rdata_expiry, nak_rb_expiry), "confirm not appended"); + struct pgm_sk_buff_t* skb = pgm_rxw_peek (window, 101); + pgm_rxw_state (window, skb, PGM_PKT_STATE_WAIT_NCF); + pgm_rxw_state (window, skb, PGM_PKT_STATE_WAIT_DATA); + pgm_rxw_destroy (window); +} +END_TEST + +START_TEST (test_state_fail_001) +{ + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + pgm_rxw_state (NULL, skb, PGM_PKT_STATE_BACK_OFF); + fail ("reached"); +} +END_TEST + +START_TEST (test_state_fail_002) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + pgm_rxw_state (window, NULL, PGM_PKT_STATE_BACK_OFF); + fail ("reached"); +} +END_TEST + +START_TEST (test_state_fail_003) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + pgm_rxw_state (window, skb, -1); + fail ("reached"); +} +END_TEST + +/* pgm_peer_has_pending + */ + +START_TEST (test_has_pending_pass_001) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window, "create failed"); +/* empty */ + fail_unless (0 == window->has_event, "unexpected event"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (0); + const pgm_time_t now = 1; + const pgm_time_t nak_rdata_expiry = 2; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not appended"); +/* 1 sequence */ + fail_unless (1 == window->has_event, "no event"); + window->has_event = 0; +/* jump */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (2); + fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not missing"); + fail_unless (0 == window->has_event, "unexpected event"); +/* loss */ + pgm_rxw_lost (window, 1); + fail_unless (1 == window->has_event, "no event"); + window->has_event = 0; +/* insert */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + skb->pgm_data->data_sqn = g_htonl (1); + fail_unless (PGM_RXW_INSERTED == pgm_rxw_add (window, skb, now, nak_rb_expiry), "add not inserted"); + fail_unless (1 == window->has_event, "no event"); + window->has_event = 0; +/* confirm */ + fail_unless (PGM_RXW_APPENDED == pgm_rxw_confirm (window, 3, now, nak_rdata_expiry, nak_rb_expiry), "confirm not appended"); + fail_unless (0 == window->has_event, "unexpected event"); +/* partial read */ + struct pgm_msgv_t msgv[2], *pmsg = msgv; + fail_unless (2000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + fail_unless (0 == window->has_event, "unexpected event"); +/* finish read */ + pmsg = msgv; + fail_unless (1000 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)), "readv failed"); + fail_unless (0 == window->has_event, "unexpected event"); + pgm_rxw_destroy (window); +} +END_TEST + +static +Suite* +make_basic_test_suite (void) +{ + Suite* s; + + s = suite_create ("basic receive window API"); + + TCase* tc_create = tcase_create ("create"); + suite_add_tcase (s, tc_create); + tcase_add_test (tc_create, test_create_pass_001); + tcase_add_test (tc_create, test_create_pass_002); + tcase_add_test (tc_create, test_create_pass_003); + tcase_add_test (tc_create, test_create_pass_004); + tcase_add_test_raise_signal (tc_create, test_create_fail_001, SIGABRT); + tcase_add_test_raise_signal (tc_create, test_create_fail_002, SIGABRT); + tcase_add_test_raise_signal (tc_create, test_create_fail_003, SIGABRT); + tcase_add_test_raise_signal (tc_create, test_create_fail_004, SIGABRT); + + TCase* tc_destroy = tcase_create ("destroy"); + suite_add_tcase (s, tc_destroy); + tcase_add_test (tc_destroy, test_destroy_pass_001); + tcase_add_test_raise_signal (tc_destroy, test_destroy_fail_001, SIGABRT); + + TCase* tc_add = tcase_create ("add"); + suite_add_tcase (s, tc_add); + tcase_add_test (tc_add, test_add_pass_001); + tcase_add_test (tc_add, test_add_pass_002); + tcase_add_test (tc_add, test_add_pass_003); + tcase_add_test (tc_add, test_add_pass_004); + tcase_add_test (tc_add, test_add_pass_005); + tcase_add_test_raise_signal (tc_add, test_add_fail_001, SIGABRT); + tcase_add_test_raise_signal (tc_add, test_add_fail_002, SIGABRT); + tcase_add_test_raise_signal (tc_add, test_add_fail_003, SIGABRT); + + TCase* tc_peek = tcase_create ("peek"); + suite_add_tcase (s, tc_peek); + tcase_add_test (tc_peek, test_peek_pass_001); + tcase_add_test_raise_signal (tc_peek, test_peek_fail_001, SIGABRT); + + TCase* tc_max_length = tcase_create ("max-length"); + suite_add_tcase (s, tc_max_length); + tcase_add_test (tc_max_length, test_max_length_pass_001); + tcase_add_test_raise_signal (tc_max_length, test_max_length_fail_001, SIGABRT); + + TCase* tc_length = tcase_create ("length"); + suite_add_tcase (s, tc_length); + tcase_add_test (tc_length, test_length_pass_001); + tcase_add_test_raise_signal (tc_length, test_length_fail_001, SIGABRT); + + TCase* tc_size = tcase_create ("size"); + suite_add_tcase (s, tc_size); + tcase_add_test (tc_size, test_size_pass_001); + tcase_add_test_raise_signal (tc_size, test_size_fail_001, SIGABRT); + + TCase* tc_is_empty = tcase_create ("is-empty"); + suite_add_tcase (s, tc_is_empty); + tcase_add_test (tc_is_empty, test_is_empty_pass_001); + tcase_add_test_raise_signal (tc_is_empty, test_is_empty_fail_001, SIGABRT); + + TCase* tc_is_full = tcase_create ("is-full"); + suite_add_tcase (s, tc_is_full); + tcase_add_test (tc_is_full, test_is_full_pass_001); + tcase_add_test_raise_signal (tc_is_full, test_is_full_fail_001, SIGABRT); + + TCase* tc_lead = tcase_create ("lead"); + suite_add_tcase (s, tc_lead); + tcase_add_test (tc_lead, test_lead_pass_001); + tcase_add_test_raise_signal (tc_lead, test_lead_fail_001, SIGABRT); + + TCase* tc_next_lead = tcase_create ("next-lead"); + suite_add_tcase (s, tc_next_lead); + tcase_add_test (tc_next_lead, test_next_lead_pass_001); + tcase_add_test_raise_signal (tc_next_lead, test_next_lead_fail_001, SIGABRT); + + TCase* tc_readv = tcase_create ("readv"); + suite_add_tcase (s, tc_readv); + tcase_add_test (tc_readv, test_readv_pass_001); + tcase_add_test (tc_readv, test_readv_pass_002); + tcase_add_test (tc_readv, test_readv_pass_003); + tcase_add_test (tc_readv, test_readv_pass_004); + tcase_add_test (tc_readv, test_readv_pass_005); + tcase_add_test (tc_readv, test_readv_pass_006); + tcase_add_test_raise_signal (tc_readv, test_readv_fail_001, SIGABRT); + tcase_add_test_raise_signal (tc_readv, test_readv_fail_002, SIGABRT); + tcase_add_test_raise_signal (tc_readv, test_readv_fail_003, SIGABRT); + + TCase* tc_remove_commit = tcase_create ("remove-commit"); + suite_add_tcase (s, tc_remove_commit); + tcase_add_test (tc_remove_commit, test_remove_commit_pass_001); + tcase_add_test_raise_signal (tc_remove_commit, test_remove_commit_fail_001, SIGABRT); + + TCase* tc_remove_trail = tcase_create ("remove-trail"); + TCase* tc_update = tcase_create ("update"); + suite_add_tcase (s, tc_update); + tcase_add_test (tc_update, test_update_pass_001); + tcase_add_test_raise_signal (tc_update, test_update_fail_001, SIGABRT); + + TCase* tc_confirm = tcase_create ("confirm"); + suite_add_tcase (s, tc_confirm); + tcase_add_test (tc_confirm, test_confirm_pass_001); + tcase_add_test (tc_confirm, test_confirm_pass_002); + tcase_add_test_raise_signal (tc_confirm, test_confirm_fail_001, SIGABRT); + + TCase* tc_lost = tcase_create ("lost"); + suite_add_tcase (s, tc_lost); + tcase_add_test (tc_lost, test_lost_pass_001); + tcase_add_test_raise_signal (tc_lost, test_lost_fail_001, SIGABRT); + + TCase* tc_state = tcase_create ("state"); + suite_add_tcase (s, tc_state); + tcase_add_test (tc_state, test_state_pass_001); + tcase_add_test_raise_signal (tc_state, test_state_fail_001, SIGABRT); + + return s; +} + +/* read through lost packet */ +START_TEST (test_readv_pass_007) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window); + struct pgm_msgv_t msgv[1], *pmsg; + struct pgm_sk_buff_t* skb; +/* add #0 */ + { + unsigned i = 0; + skb = generate_valid_skb (); + fail_if (NULL == skb); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry)); + fail_unless ((1 + i) == pgm_rxw_length (window)); + } +/* add # 2 */ + { + unsigned i = 2; + skb = generate_valid_skb (); + fail_if (NULL == skb); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry)); + fail_unless ((1 + i) == pgm_rxw_length (window)); + } +/* lose #1 */ + { + pgm_rxw_lost (window, 1); + } + fail_unless (_pgm_rxw_commit_is_empty (window)); +/* read #0 */ + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + } +/* end-of-window */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + } + pgm_rxw_remove_commit (window); +/* read lost skb #1 */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + } + pgm_rxw_remove_commit (window); +/* read #2 */ + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + } +/* end-of-window */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + } + pgm_rxw_destroy (window); +} +END_TEST + +/* read through loss extended window */ +START_TEST (test_readv_pass_008) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window); + struct pgm_msgv_t msgv[1], *pmsg; + struct pgm_sk_buff_t* skb; +/* add #0 */ + { + unsigned i = 0; + skb = generate_valid_skb (); + fail_if (NULL == skb); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry)); + fail_unless ((1 + i) == pgm_rxw_length (window)); + } + fail_unless (_pgm_rxw_commit_is_empty (window)); +/* read #0 */ + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + } + pgm_rxw_remove_commit (window); +/* end-of-window */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + } +/* add #100 */ + { + unsigned i = 100; + skb = generate_valid_skb (); + fail_if (NULL == skb); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry)); + } +/* lose #1-99 */ + { + for (unsigned i = 1; i < 100; i++) + pgm_rxw_lost (window, i); + } +/* read #100 */ + { + int i = 0; + int bytes_read; + pmsg = msgv; + do { + bytes_read = pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)); + pgm_rxw_remove_commit (window); + i++; + if (i > 100) break; + } while (-1 == bytes_read); + fail_unless (100 == i); + } +/* end-of-window */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + } + pgm_rxw_destroy (window); +} +END_TEST + +/* read through long data-loss */ +START_TEST (test_readv_pass_009) +{ + pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const uint32_t ack_c_p = 500; + pgm_rxw_t* window = pgm_rxw_create (&tsi, 1500, 100, 0, 0, ack_c_p); + fail_if (NULL == window); + struct pgm_msgv_t msgv[1], *pmsg; + struct pgm_sk_buff_t* skb; +/* add #0 */ + { + unsigned i = 0; + skb = generate_valid_skb (); + fail_if (NULL == skb); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_APPENDED == pgm_rxw_add (window, skb, now, nak_rb_expiry)); + fail_unless ((1 + i) == pgm_rxw_length (window)); + } + fail_unless (_pgm_rxw_commit_is_empty (window)); +/* read #0 */ + { + pmsg = msgv; + fail_unless (0 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + } + pgm_rxw_remove_commit (window); +/* end-of-window */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + } +/* add #2000 */ + { + unsigned i = 2000; + skb = generate_valid_skb (); + fail_if (NULL == skb); + skb->pgm_header->pgm_tsdu_length = g_htons (0); + skb->tail = (guint8*)skb->tail - skb->len; + skb->len = 0; + skb->pgm_data->data_sqn = g_htonl (i); + const pgm_time_t now = 1; + const pgm_time_t nak_rb_expiry = 2; + fail_unless (PGM_RXW_MISSING == pgm_rxw_add (window, skb, now, nak_rb_expiry)); + } +/* lose #1-1999 */ + { + for (unsigned i = 1901; i < 2000; i++) + pgm_rxw_lost (window, i); + } +/* read #2000 */ + { + int i = 0; + int bytes_read; + pmsg = msgv; + do { + bytes_read = pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv)); + pgm_rxw_remove_commit (window); + i++; + if (i > 100) break; + } while (-1 == bytes_read); + fail_unless (100 == i); + } +/* end-of-window */ + { + pmsg = msgv; + fail_unless (-1 == pgm_rxw_readv (window, &pmsg, G_N_ELEMENTS(msgv))); + } + pgm_rxw_destroy (window); +} +END_TEST + +/* a.k.a. unreliable delivery + */ + +static +Suite* +make_best_effort_test_suite (void) +{ + Suite* s; + + s = suite_create ("Best effort delivery"); + + TCase* tc_readv = tcase_create ("readv"); + suite_add_tcase (s, tc_readv); + tcase_add_test (tc_readv, test_readv_pass_007); + tcase_add_test (tc_readv, test_readv_pass_008); + tcase_add_test (tc_readv, test_readv_pass_009); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_basic_test_suite ()); + srunner_add_suite (sr, make_best_effort_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/signal.c b/3rdparty/openpgm-svn-r1135/pgm/signal.c new file mode 100644 index 0000000..ab1fb86 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/signal.c @@ -0,0 +1,179 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Re-entrant safe signal handling. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include +#include /* _GNU_SOURCE for strsignal() */ +#include +#ifndef G_OS_WIN32 +# include +#else +# include +#endif +#include +#include "pgm/signal.h" + + +//#define SIGNAL_DEBUG + + +/* globals */ + +static pgm_sighandler_t signal_list[NSIG]; +static int signal_pipe[2]; +static GIOChannel* signal_io = NULL; + +static void on_signal (int); +static gboolean on_io_signal (GIOChannel*, GIOCondition, gpointer); +static const char* cond_string (GIOCondition); + + +static +void +set_nonblock ( + const int s, + const gboolean v + ) +{ +#ifndef G_OS_WIN32 + int flags = fcntl (s, F_GETFL); + if (!v) flags &= ~O_NONBLOCK; + else flags |= O_NONBLOCK; + fcntl (s, F_SETFL, flags); +#else + u_long mode = v; + ioctlsocket (s, FIONBIO, &mode); +#endif +} + +/* install signal handler and return unix fd to add to event loop + */ + +gboolean +pgm_signal_install ( + int signum, + pgm_sighandler_t handler, + gpointer user_data + ) +{ + g_debug ("pgm_signal_install (signum:%d handler:%p user_data:%p)", + signum, (const void*)handler, user_data); + + if (NULL == signal_io) + { +#ifdef G_OS_UNIX + if (pipe (signal_pipe)) +#else + if (_pipe (signal_pipe, 4096, _O_BINARY | _O_NOINHERIT)) +#endif + return FALSE; + + set_nonblock (signal_pipe[0], TRUE); + set_nonblock (signal_pipe[1], TRUE); +/* add to evm */ + signal_io = g_io_channel_unix_new (signal_pipe[0]); + g_io_add_watch (signal_io, G_IO_IN, on_io_signal, user_data); + } + + signal_list[signum] = handler; + return (SIG_ERR != signal (signum, on_signal)); +} + +/* process signal from operating system + */ + +static +void +on_signal ( + int signum + ) +{ + g_debug ("on_signal (signum:%d)", signum); + if (write (signal_pipe[1], &signum, sizeof(signum)) != sizeof(signum)) + { +#ifndef G_OS_WIN32 + g_warning ("Unix signal %s (%d) lost", strsignal (signum), signum); +#else + g_warning ("Unix signal (%d) lost", signum); +#endif + } +} + +/* process signal from pipe + */ + +static +gboolean +on_io_signal ( + GIOChannel* source, + GIOCondition cond, + gpointer user_data + ) +{ +/* pre-conditions */ + g_assert (NULL != source); + g_assert (G_IO_IN == cond); + + g_debug ("on_io_signal (source:%p cond:%s user_data:%p)", + (gpointer)source, cond_string (cond), user_data); + + int signum; + const gsize bytes_read = read (g_io_channel_unix_get_fd (source), &signum, sizeof(signum)); + + if (sizeof(signum) == bytes_read) + { + signal_list[signum] (signum, user_data); + } + else + { + g_warning ("Lost data in signal pipe, read %" G_GSIZE_FORMAT " byte%s expected %" G_GSIZE_FORMAT ".", + bytes_read, bytes_read > 1 ? "s" : "", sizeof(signum)); + } + + return TRUE; +} + +static +const char* +cond_string ( + GIOCondition cond + ) +{ + const char* c; + + switch (cond) { + case G_IO_IN: c = "G_IO_IN"; break; + case G_IO_OUT: c = "G_IO_OUT"; break; + case G_IO_PRI: c = "G_IO_PRI"; break; + case G_IO_ERR: c = "G_IO_ERR"; break; + case G_IO_HUP: c = "G_IO_HUP"; break; + case G_IO_NVAL: c = "G_IO_NVAL"; break; + default: c = "(unknown)"; break; + } + + return c; +} + + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/signal_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/signal_unittest.c new file mode 100644 index 0000000..4784053 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/signal_unittest.c @@ -0,0 +1,115 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for re-entrant safe signal handling. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include + + +/* mock state */ + +static +void +on_sigusr1 ( + int signum, + gpointer user_data + ) +{ + g_assert (SIGUSR1 == signum); + g_assert (NULL != user_data); + GMainLoop* loop = (GMainLoop*)user_data; + g_debug ("on_sigusr1 (signum:%d)", signum); + g_main_loop_quit (loop); +} + +/* mock functions for external references */ + +#define SIGNAL_DEBUG +#include "signal.c" + + +/* target: + * pgm_sighandler_t + * pgm_signal_install ( + * int signum, + pgm_sighandler_t handler + * ) + */ + +static +gboolean +on_startup ( + gpointer data + ) +{ + g_assert (NULL != data); + const int signum = *(const int*)data; + fail_unless (0 == raise (signum)); + return FALSE; +} + +START_TEST (test_install_pass_001) +{ + const int signum = SIGUSR1; + GMainLoop* loop = g_main_loop_new (NULL, FALSE); + fail_unless (TRUE == pgm_signal_install (signum, on_sigusr1, loop)); + g_timeout_add (0, (GSourceFunc)on_startup, &signum); + g_main_loop_run (loop); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_install = tcase_create ("install"); + suite_add_tcase (s, tc_install); + tcase_add_test (tc_install, test_install_pass_001); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/skbuff.c b/3rdparty/openpgm-svn-r1135/pgm/skbuff.c new file mode 100644 index 0000000..5db6ffc --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/skbuff.c @@ -0,0 +1,115 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM socket buffers + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "pgm/skbuff.h" + + +void +pgm_skb_over_panic ( + const struct pgm_sk_buff_t*const skb, + const uint16_t len + ) +{ + pgm_fatal ("skput:over: %u put:%u", + skb->len, len); + pgm_assert_not_reached(); +} + +void +pgm_skb_under_panic ( + const struct pgm_sk_buff_t*const skb, + const uint16_t len + ) +{ + pgm_fatal ("skput:under: %u put:%u", + skb->len, len); + pgm_assert_not_reached(); +} + +#ifndef SKB_DEBUG +bool +pgm_skb_is_valid ( + PGM_GNUC_UNUSED const struct pgm_sk_buff_t*const skb + ) +{ + return TRUE; +} +#else +bool +pgm_skb_is_valid ( + const struct pgm_sk_buff_t*const skb + ) +{ + pgm_return_val_if_fail (skb, FALSE); +/* link_ */ +/* socket */ + pgm_return_val_if_fail (skb->sock, FALSE); +/* tstamp */ + pgm_return_val_if_fail (skb->tstamp > 0, FALSE); +/* tsi */ +/* sequence can be any value */ +/* cb can be any value */ +/* len can be any value */ +/* zero_padded can be any value */ +/* gpointers */ + pgm_return_val_if_fail (skb->head, FALSE); + pgm_return_val_if_fail ((const char*)skb->head > (const char*)&skb->users, FALSE); + pgm_return_val_if_fail (skb->data, FALSE); + pgm_return_val_if_fail ((const char*)skb->data >= (const char*)skb->head, FALSE); + pgm_return_val_if_fail (skb->tail, FALSE); + pgm_return_val_if_fail ((const char*)skb->tail >= (const char*)skb->data, FALSE); + pgm_return_val_if_fail (skb->len == (char*)skb->tail - (const char*)skb->data, FALSE); + pgm_return_val_if_fail (skb->end, FALSE); + pgm_return_val_if_fail ((const char*)skb->end >= (const char*)skb->tail, FALSE); +/* pgm_header */ + if (skb->pgm_header) { + pgm_return_val_if_fail ((const char*)skb->pgm_header >= (const char*)skb->head, FALSE); + pgm_return_val_if_fail ((const char*)skb->pgm_header + sizeof(struct pgm_header) <= (const char*)skb->tail, FALSE); + pgm_return_val_if_fail (skb->pgm_data, FALSE); + pgm_return_val_if_fail ((const char*)skb->pgm_data >= (const char*)skb->pgm_header + sizeof(struct pgm_header), FALSE); + pgm_return_val_if_fail ((const char*)skb->pgm_data <= (const char*)skb->tail, FALSE); + if (skb->pgm_opt_fragment) { + pgm_return_val_if_fail ((const char*)skb->pgm_opt_fragment > (const char*)skb->pgm_data, FALSE); + pgm_return_val_if_fail ((const char*)skb->pgm_opt_fragment + sizeof(struct pgm_opt_fragment) < (const char*)skb->tail, FALSE); +/* of_apdu_first_sqn can be any value */ +/* of_frag_offset */ + pgm_return_val_if_fail (ntohl (skb->of_frag_offset) < ntohl (skb->of_apdu_len), FALSE); +/* of_apdu_len can be any value */ + } + pgm_return_val_if_fail (PGM_ODATA == skb->pgm_header->pgm_type || PGM_RDATA == skb->pgm_header->pgm_type, FALSE); +/* FEC broken */ + pgm_return_val_if_fail (0 == (skb->pgm_header->pgm_options & PGM_OPT_PARITY), FALSE); + pgm_return_val_if_fail (0 == (skb->pgm_header->pgm_options & PGM_OPT_VAR_PKTLEN), FALSE); + } else { + pgm_return_val_if_fail (NULL == skb->pgm_data, FALSE); + pgm_return_val_if_fail (NULL == skb->pgm_opt_fragment, FALSE); + } +/* truesize */ + pgm_return_val_if_fail (skb->truesize >= sizeof(struct pgm_sk_buff_t*) + skb->len, FALSE); + pgm_return_val_if_fail (skb->truesize == ((const char*)skb->end - (const char*)skb), FALSE); +/* users */ + pgm_return_val_if_fail (pgm_atomic_read32 (&skb->users) > 0, FALSE); + return TRUE; +} +#endif /* SKB_DEBUG */ + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/slist.c b/3rdparty/openpgm-svn-r1135/pgm/slist.c new file mode 100644 index 0000000..9ba68ea --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/slist.c @@ -0,0 +1,166 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable singly-linked list. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + + +//#define SLIST_DEBUG + +pgm_slist_t* +pgm_slist_append ( + pgm_slist_t* restrict list, + void* restrict data + ) +{ + pgm_slist_t* new_list; + pgm_slist_t* last; + + new_list = pgm_new (pgm_slist_t, 1); + new_list->data = data; + new_list->next = NULL; + + if (list) + { + last = pgm_slist_last (list); + last->next = new_list; + return list; + } + else + return new_list; +} + +pgm_slist_t* +pgm_slist_prepend ( + pgm_slist_t* restrict list, + void* restrict data + ) +{ + pgm_slist_t *new_list; + + new_list = pgm_new (pgm_slist_t, 1); + new_list->data = data; + new_list->next = list; + + return new_list; +} + +pgm_slist_t* +pgm_slist_prepend_link ( + pgm_slist_t* restrict list, + pgm_slist_t* restrict link_ + ) +{ + pgm_slist_t *new_list; + + new_list = link_; + new_list->next = list; + + return new_list; +} + +pgm_slist_t* +pgm_slist_remove ( + pgm_slist_t* restrict list, + const void* restrict data + ) +{ + pgm_slist_t *tmp = list, *prev = NULL; + + while (tmp) + { + if (tmp->data == data) + { + if (prev) + prev->next = tmp->next; + else + list = tmp->next; + pgm_free (tmp); + break; + } + prev = tmp; + tmp = prev->next; + } + + return list; +} + +pgm_slist_t* +pgm_slist_remove_first ( + pgm_slist_t* list + ) +{ + pgm_slist_t *tmp; + + if (PGM_LIKELY (NULL != list)) + { + tmp = list->next; + list->data = NULL; + list->next = NULL; + return tmp; + } + else + return NULL; +} + +void +pgm_slist_free ( + pgm_slist_t* list + ) +{ + while (list) + { + pgm_slist_t* current = list; + list = list->next; + pgm_free (current); + } +} + +pgm_slist_t* +pgm_slist_last ( + pgm_slist_t* list + ) +{ + if (PGM_LIKELY (NULL != list)) + { + while (list->next) + list = list->next; + } + + return list; +} + +unsigned +pgm_slist_length ( + pgm_slist_t* list + ) +{ + unsigned length = 0; + + while (list) + { + length++; + list = list->next; + } + + return length; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/snmp.c b/3rdparty/openpgm-svn-r1135/pgm/snmp.c new file mode 100644 index 0000000..5673878 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/snmp.c @@ -0,0 +1,222 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * SNMP agent, single session. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include +#include +#include + +#include "pgm/snmp.h" +#include "impl/pgmMIB.h" + + +/* globals */ + +bool pgm_agentx_subagent = TRUE; +char* pgm_agentx_socket = NULL; +char* pgm_snmp_appname = "PGM"; + +/* locals */ + +#ifndef _WIN32 +static pthread_t snmp_thread; +static void* snmp_routine (void*); +#else +static HANDLE snmp_thread; +static unsigned __stdcall snmp_routine (void*); +#endif +static pgm_notify_t snmp_notify = PGM_NOTIFY_INIT; +static volatile uint32_t snmp_ref_count = 0; + + +/* Calling application needs to redirect SNMP logging before prior to this + * function. + */ + +bool +pgm_snmp_init ( + pgm_error_t** error + ) +{ + if (pgm_atomic_exchange_and_add32 (&snmp_ref_count, 1) > 0) + return TRUE; + + if (pgm_agentx_subagent) + { + pgm_minor (_("Configuring as SNMP AgentX sub-agent.")); + if (pgm_agentx_socket) + { + pgm_minor (_("Using AgentX socket %s."), pgm_agentx_socket); + netsnmp_ds_set_string (NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_X_SOCKET, + pgm_agentx_socket); + } + netsnmp_ds_set_boolean (NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_ROLE, + TRUE); + } + + pgm_minor (_("Initialising SNMP agent.")); + if (0 != init_agent (pgm_snmp_appname)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SNMP, + PGM_ERROR_FAILED, + _("Initialise SNMP agent: see SNMP log for further details.")); + goto err_cleanup; + } + + if (!pgm_mib_init (error)) { + goto err_cleanup; + } + +/* read config and parse mib */ + pgm_minor (_("Initialising SNMP.")); + init_snmp (pgm_snmp_appname); + + if (!pgm_agentx_subagent) + { + pgm_minor (_("Connecting to SNMP master agent.")); + if (0 != init_master_agent ()) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SNMP, + PGM_ERROR_FAILED, + _("Initialise SNMP master agent: see SNMP log for further details.")); + snmp_shutdown (pgm_snmp_appname); + goto err_cleanup; + } + } + +/* create notification channel */ + if (0 != pgm_notify_init (&snmp_notify)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SNMP, + pgm_error_from_errno (errno), + _("Creating SNMP notification channel: %s"), + strerror (errno)); + snmp_shutdown (pgm_snmp_appname); + goto err_cleanup; + } + +/* spawn thread to handle SNMP requests */ +#ifndef _WIN32 + const int status = pthread_create (&snmp_thread, NULL, &snmp_routine, NULL); + if (0 != status) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SNMP, + pgm_error_from_errno (errno), + _("Creating SNMP thread: %s"), + strerror (errno)); + snmp_shutdown (pgm_snmp_appname); + goto err_cleanup; + } +#else + snmp_thread = (HANDLE)_beginthreadex (NULL, 0, &snmp_routine, NULL, 0, NULL); + const int save_errno = errno; + if (0 == snmp_thread) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SNMP, + pgm_error_from_errno (save_errno), + _("Creating SNMP thread: %s"), + strerror (save_errno)); + snmp_shutdown (pgm_snmp_appname); + goto err_cleanup; + } +#endif /* _WIN32 */ + return TRUE; +err_cleanup: + if (pgm_notify_is_valid (&snmp_notify)) { + pgm_notify_destroy (&snmp_notify); + } + pgm_atomic_dec32 (&snmp_ref_count); + return FALSE; +} + +/* Terminate SNMP thread and free resources. + */ + +bool +pgm_snmp_shutdown (void) +{ + pgm_return_val_if_fail (pgm_atomic_read32 (&snmp_ref_count) > 0, FALSE); + + if (pgm_atomic_exchange_and_add32 (&snmp_ref_count, (uint32_t)-1) != 1) + return TRUE; + + pgm_notify_send (&snmp_notify); +#ifndef _WIN32 + pthread_join (snmp_thread, NULL); +#else + CloseHandle (snmp_thread); +#endif + pgm_notify_destroy (&snmp_notify); + snmp_shutdown (pgm_snmp_appname); + return TRUE; +} + +/* Thread routine for processing SNMP requests + */ + +static +#ifndef _WIN32 +void* +#else +unsigned +__stdcall +#endif +snmp_routine ( + PGM_GNUC_UNUSED void* arg + ) +{ + const int notify_fd = pgm_notify_get_fd (&snmp_notify); + + for (;;) + { + int fds = 0, block = 1; + fd_set fdset; + struct timeval timeout; + + FD_ZERO(&fdset); + snmp_select_info (&fds, &fdset, &timeout, &block); + FD_SET(notify_fd, &fdset); + if (notify_fd+1 > fds) + fds = notify_fd+1; + fds = select (fds, &fdset, NULL, NULL, block ? NULL : &timeout); + if (FD_ISSET(notify_fd, &fdset)) + break; + if (fds) + snmp_read (&fdset); + else + snmp_timeout(); + } + +/* cleanup */ +#ifndef _WIN32 + return NULL; +#else + _endthread(); + return 0; +#endif /* WIN32 */ +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/snmp_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/snmp_unittest.c new file mode 100644 index 0000000..9005a82 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/snmp_unittest.c @@ -0,0 +1,184 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for SNMP. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include + +#include "pgm/transport.h" + + +/* mock state */ +static const guint mock_pgm_major_version = 0; +static const guint mock_pgm_minor_version = 0; +static const guint mock_pgm_micro_version = 0; +static GStaticRWLock mock_pgm_transport_list_lock = G_STATIC_RW_LOCK_INIT; +static GSList* mock_pgm_transport_list = NULL; + +static +gboolean +mock_pgm_tsi_equal ( + gconstpointer v1, + gconstpointer v2 + ) +{ + return memcmp (v1, v2, sizeof(struct pgm_tsi_t)) == 0; +} + +static +void +mock_pgm_time_since_epoch ( + pgm_time_t* pgm_time_t_time, + time_t* time_t_time + ) +{ + *time_t_time = pgm_to_secs (*pgm_time_t_time + 0); +} + +static +gboolean +mock_pgm_mib_init ( + GError** error + ) +{ + return TRUE; +} + +/* mock functions for external references */ + +#define pgm_major_version mock_pgm_major_version +#define pgm_minor_version mock_pgm_minor_version +#define pgm_micro_version mock_pgm_micro_version +#define pgm_transport_list_lock mock_pgm_transport_list_lock +#define pgm_transport_list mock_pgm_transport_list +#define pgm_tsi_equal mock_pgm_tsi_equal +#define pgm_time_since_epoch mock_pgm_time_since_epoch +#define pgm_mib_init mock_pgm_mib_init + + +#define SNMP_DEBUG +#include "snmp.c" + + +/* target: + * gboolean + * pgm_snmp_init ( + * GError** error + * ) + */ + +START_TEST (test_init_pass_001) +{ + GError* err = NULL; + fail_unless (TRUE == pgm_snmp_init (&err)); + fail_unless (NULL == err); +} +END_TEST + +/* duplicate servers */ +START_TEST (test_init_fail_001) +{ + GError* err = NULL; + fail_unless (TRUE == pgm_snmp_init (&err)); + fail_unless (FALSE == pgm_snmp_init (&err)); +} +END_TEST + +/* target: + * gboolean + * pgm_snmp_shutdown (void) + */ + +START_TEST (test_shutdown_pass_001) +{ + GError* err = NULL; + fail_unless (TRUE == pgm_snmp_init (&err)); + fail_unless (NULL == err); + fail_unless (TRUE == pgm_snmp_shutdown ()); +} +END_TEST + +/* repeatability + */ +START_TEST (test_shutdown_pass_002) +{ + GError* err = NULL; + fail_unless (TRUE == pgm_snmp_init (&err)); + fail_unless (NULL == err); + fail_unless (TRUE == pgm_snmp_shutdown ()); + fail_unless (TRUE == pgm_snmp_init (&err)); + fail_unless (NULL == err); + fail_unless (TRUE == pgm_snmp_shutdown ()); +} +END_TEST + +/* no running server */ +START_TEST (test_shutdown_fail_001) +{ + fail_unless (FALSE == pgm_snmp_shutdown ()); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_init = tcase_create ("init"); + suite_add_tcase (s, tc_init); + tcase_add_test (tc_init, test_init_pass_001); + tcase_add_test (tc_init, test_init_fail_001); + + TCase* tc_shutdown = tcase_create ("shutdown"); + suite_add_tcase (s, tc_shutdown); + tcase_add_test (tc_shutdown, test_shutdown_pass_001); + tcase_add_test (tc_shutdown, test_shutdown_pass_002); + tcase_add_test (tc_shutdown, test_shutdown_fail_001); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/sockaddr.c b/3rdparty/openpgm-svn-r1135/pgm/sockaddr.c new file mode 100644 index 0000000..df43d26 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/sockaddr.c @@ -0,0 +1,1193 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * struct sockaddr functions independent of in or in6. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#ifndef _WIN32 +# include +# include +#endif +#include + + +/* FreeBSD */ +#ifndef IPV6_ADD_MEMBERSHIP +# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +# define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP +#endif +/* OpenSolaris differences */ +#if !defined(_WIN32) && !defined(MCAST_MSFILTER) +# include +#endif +#ifndef SOL_IP +# define SOL_IP IPPROTO_IP +#endif +#ifndef SOL_IPV6 +# define SOL_IPV6 IPPROTO_IPV6 +#endif +#ifndef IP_MAX_MEMBERSHIPS +# define IP_MAX_MEMBERSHIPS 20 +#endif + + +sa_family_t +pgm_sockaddr_family ( + const struct sockaddr* sa + ) +{ + return sa->sa_family; +} + +uint16_t +pgm_sockaddr_port ( + const struct sockaddr* sa + ) +{ + uint16_t sa_port; + switch (sa->sa_family) { + case AF_INET: { + struct sockaddr_in s4; + memcpy (&s4, sa, sizeof(s4)); + sa_port = s4.sin_port; + break; + } + + case AF_INET6: { + struct sockaddr_in6 s6; + memcpy (&s6, sa, sizeof(s6)); + sa_port = s6.sin6_port; + break; + } + + default: + sa_port = 0; + break; + } + return sa_port; +} + +socklen_t +pgm_sockaddr_len ( + const struct sockaddr* sa + ) +{ + socklen_t sa_len; + switch (sa->sa_family) { + case AF_INET: sa_len = sizeof(struct sockaddr_in); break; + case AF_INET6: sa_len = sizeof(struct sockaddr_in6); break; + default: sa_len = 0; break; + } + return sa_len; +} + +socklen_t +pgm_sockaddr_storage_len ( + const struct sockaddr_storage* ss + ) +{ + socklen_t ss_len; + switch (ss->ss_family) { + case AF_INET: ss_len = sizeof(struct sockaddr_in); break; + case AF_INET6: ss_len = sizeof(struct sockaddr_in6); break; + default: ss_len = 0; break; + } + return ss_len; +} + +uint32_t +pgm_sockaddr_scope_id ( + const struct sockaddr* sa + ) +{ + uint32_t scope_id; + if (AF_INET6 == sa->sa_family) { + struct sockaddr_in6 s6; + memcpy (&s6, sa, sizeof(s6)); + scope_id = s6.sin6_scope_id; + } else + scope_id = 0; + return scope_id; +} + +int +pgm_sockaddr_ntop ( + const struct sockaddr* restrict sa, + char* restrict host, + size_t hostlen + ) +{ + return getnameinfo (sa, pgm_sockaddr_len (sa), + host, hostlen, + NULL, 0, + NI_NUMERICHOST); +} + +int +pgm_sockaddr_pton ( + const char* restrict src, + struct sockaddr* restrict dst /* will error on wrong size */ + ) +{ + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, /* not really */ + .ai_protocol = IPPROTO_TCP, /* not really */ + .ai_flags = AI_NUMERICHOST + }, *result = NULL; + const int status = getaddrinfo (src, NULL, &hints, &result); + if (PGM_LIKELY(0 == status)) { + memcpy (dst, result->ai_addr, result->ai_addrlen); + freeaddrinfo (result); + return 1; + } + return 0; +} + +/* returns tri-state value: 1 if sa is multicast, 0 if sa is not multicast, -1 on error + */ + +int +pgm_sockaddr_is_addr_multicast ( + const struct sockaddr* sa + ) +{ + int retval; + + switch (sa->sa_family) { + case AF_INET: { + struct sockaddr_in s4; + memcpy (&s4, sa, sizeof(s4)); + retval = IN_MULTICAST(ntohl( s4.sin_addr.s_addr )); + break; + } + + case AF_INET6: { + struct sockaddr_in6 s6; + memcpy (&s6, sa, sizeof(s6)); + retval = IN6_IS_ADDR_MULTICAST( &s6.sin6_addr ); + break; + } + + default: + retval = -1; + break; + } + return retval; +} + +/* returns 1 if sa is unspecified, 0 if specified. + */ + +int +pgm_sockaddr_is_addr_unspecified ( + const struct sockaddr* sa + ) +{ + int retval; + + switch (sa->sa_family) { + case AF_INET: { + struct sockaddr_in s4; + memcpy (&s4, sa, sizeof(s4)); + retval = (INADDR_ANY == s4.sin_addr.s_addr); + break; + } + + case AF_INET6: { + struct sockaddr_in6 s6; + memcpy (&s6, sa, sizeof(s6)); + retval = IN6_IS_ADDR_UNSPECIFIED( &s6.sin6_addr ); + break; + } + + default: + retval = -1; + break; + } + return retval; +} + +int +pgm_sockaddr_cmp ( + const struct sockaddr* restrict sa1, + const struct sockaddr* restrict sa2 + ) +{ + int retval = 0; + + if (sa1->sa_family != sa2->sa_family) + retval = sa1->sa_family < sa2->sa_family ? -1 : 1; + else { + switch (sa1->sa_family) { + case AF_INET: { + struct sockaddr_in sa1_in, sa2_in; + memcpy (&sa1_in, sa1, sizeof(sa1_in)); + memcpy (&sa2_in, sa2, sizeof(sa2_in)); + if (sa1_in.sin_addr.s_addr != sa2_in.sin_addr.s_addr) + retval = sa1_in.sin_addr.s_addr < sa2_in.sin_addr.s_addr ? -1 : 1; + break; + } + +/* IN6_ARE_ADDR_EQUAL(a,b) only returns true or false */ + case AF_INET6: { + struct sockaddr_in6 sa1_in6, sa2_in6; + memcpy (&sa1_in6, sa1, sizeof(sa1_in6)); + memcpy (&sa2_in6, sa2, sizeof(sa2_in6)); + retval = memcmp (&sa1_in6.sin6_addr, &sa2_in6.sin6_addr, sizeof(struct in6_addr)); + if (0 == retval && sa1_in6.sin6_scope_id != sa2_in6.sin6_scope_id) + retval = sa1_in6.sin6_scope_id < sa2_in6.sin6_scope_id ? -1 : 1; + break; + } + + default: + break; + } + } + return retval; +} + +/* IP header included with data. + * + * If no error occurs, pgm_sockaddr_hdrincl returns zero. Otherwise, a value + * of PGM_SOCKET_ERROR is returned, and a specific error code can be retrieved + * by calling pgm_sock_errno(). + */ + +int +pgm_sockaddr_hdrincl ( + const int s, + const sa_family_t sa_family, + const bool v + ) +{ + int retval = PGM_SOCKET_ERROR; + + switch (sa_family) { + case AF_INET: { +#ifndef _WIN32 +/* Solaris:ip(7P) Mentioned but not detailed. + * + * Linux:ip(7) "A boolean integer flag is zero when it is false, otherwise + * true. If enabled, the user supplies an IP header in front of the user + * data." Mentions only send-side, nothing about receive-side. + * Linux:raw(7) "For receiving the IP header is always included in the packet." + * + * FreeBSD,OS X:IP(4) provided by example "int hincl = 1;" + * + * Stevens: "IP_HDRINCL has datatype int." + */ + const int optval = v ? 1 : 0; +#else + const DWORD optval = v ? 1 : 0; +#endif + retval = setsockopt (s, IPPROTO_IP, IP_HDRINCL, (const char*)&optval, sizeof(optval)); + break; + } + + case AF_INET6: /* method only exists on Win32, just ignore */ + retval = 0; + break; + + default: break; + } + return retval; +} + +/* Return destination IP address. + * + * If no error occurs, pgm_sockaddr_pktinfo returns zero. Otherwise, a value + * of PGM_SOCKET_ERROR is returned, and a specific error code can be retrieved + * by calling pgm_sock_errno(). + */ + +int +pgm_sockaddr_pktinfo ( + const int s, + const sa_family_t sa_family, + const bool v + ) +{ + int retval = PGM_SOCKET_ERROR; +#ifndef _WIN32 +/* Solaris:ip(7P) "The following options take in_pktinfo_t as the parameter" + * Completely different, although ip6(7P) is a little better, "The following + * options are boolean switches controlling the reception of ancillary data" + * + * Linux:ip(7) "A boolean integer flag is zero when it is false, otherwise + * true. The argument is a flag that tells the socket whether the IP_PKTINFO + * message should be passed or not." + * Linux:ipv6(7) Not listed, however IPV6_PKTINFO is with "Argument is a pointer + * to a boolean value in an integer." + * + * Absent from FreeBSD & OS X, suggested replacement IP_RECVDSTADDR. + * OS X:IP6(4) "IPV6_PKTINFO int *" + * + * Stevens: "IP_RECVDSTADDR has datatype int." + */ + const int optval = v ? 1 : 0; +#else + const DWORD optval = v ? 1 : 0; +#endif + + switch (sa_family) { + case AF_INET: +#ifdef IP_RECVDSTADDR + retval = setsockopt (s, IPPROTO_IP, IP_RECVDSTADDR, (const char*)&optval, sizeof(optval)); +#else + retval = setsockopt (s, IPPROTO_IP, IP_PKTINFO, (const char*)&optval, sizeof(optval)); +#endif + break; + + case AF_INET6: +#ifdef IPV6_RECVPKTINFO + retval = setsockopt (s, IPPROTO_IPV6, IPV6_RECVPKTINFO, (const char*)&optval, sizeof(optval)); +#else + retval = setsockopt (s, IPPROTO_IPV6, IPV6_PKTINFO, (const char*)&optval, sizeof(optval)); +#endif + break; + + default: break; + } + return retval; +} + +/* Set IP Router Alert option for all outgoing packets. + * + * If no error occurs, pgm_sockaddr_router_alert returns zero. Otherwise, a + * value of PGM_SOCKET_ERROR is returned, and a specific error code can be + * retrieved by calling pgm_sock_errno(). + */ + +int +pgm_sockaddr_router_alert ( + const int s, + const sa_family_t sa_family, + const bool v + ) +{ + int retval = PGM_SOCKET_ERROR; +#ifdef CONFIG_IP_ROUTER_ALERT +/* Linux:ip(7) "A boolean integer flag is zero when it is false, otherwise + * true. Expects an integer flag." + * Linux:ipv6(7) "Argument is a pointer to an integer." + * + * Sent on special queue to rsvpd on Linux and so best avoided. + */ + const int optval = v ? 1 : 0; + + switch (sa_family) { + case AF_INET: + retval = setsockopt (s, IPPROTO_IP, IP_ROUTER_ALERT, (const char*)&optval, sizeof(optval)); + break; + + case AF_INET6: + retval = setsockopt (s, IPPROTO_IPV6, IPV6_ROUTER_ALERT, (const char*)&optval, sizeof(optval)); + break; + + default: break; + } +#else +# if defined(CONFIG_HAVE_IPOPTION) +/* NB: struct ipoption is not very portable and requires a lot of additional headers */ + const struct ipoption router_alert = { + .ipopt_dst = 0, + .ipopt_list = { PGM_IPOPT_RA, 0x04, 0x00, 0x00 } + }; + const int optlen = v ? sizeof(router_alert) : 0; +# else +/* manually set the IP option */ + const int ipopt_ra = (PGM_IPOPT_RA << 24) | (0x04 << 16); + const int router_alert = htonl (ipopt_ra); + const int optlen = v ? sizeof(router_alert) : 0; +# endif + + switch (sa_family) { + case AF_INET: +/* Linux:ip(7) "The maximum option size for IPv4 is 40 bytes." + */ + retval = setsockopt (s, IPPROTO_IP, IP_OPTIONS, (const char*)&router_alert, optlen); +retval = 0; + break; + + default: break; + } +#endif + return retval; +} + +/* Type-of-service and precedence. + * + * If no error occurs, pgm_sockaddr_tos returns zero. Otherwise, a value of + * PGM_SOCKET_ERROR is returned, and a specific error code can be retrieved by + * calling pgm_sock_errno(). + */ + +int +pgm_sockaddr_tos ( + const int s, + const sa_family_t sa_family, + const int tos + ) +{ + int retval = PGM_SOCKET_ERROR; + + switch (sa_family) { + case AF_INET: { +#ifndef _WIN32 +/* Solaris:ip(7P) "This option takes an integer argument as its input value." + * + * Linux:ip(7) "TOS is a byte." + * + * FreeBSD,OS X:IP(4) provided by example "int tos = IPTOS_LOWDELAY;" + * + * Stevens: "IP_TOS has datatype int." + */ + const int optval = tos; +#else +/* IP_TOS only works on Win32 with system override: + * http://support.microsoft.com/kb/248611 + * TODO: Implement GQoS (IPv4 only), qWAVE QOS is Vista+ only + */ + const DWORD optval = tos; +#endif + retval = setsockopt (s, IPPROTO_IP, IP_TOS, (const char*)&optval, sizeof(optval)); + break; + } + + case AF_INET6: /* TRAFFIC_CLASS not implemented */ + break; + + default: break; + } + return retval; +} + +/* Join multicast group. + * NB: IPV6_JOIN_GROUP == IPV6_ADD_MEMBERSHIP + * + * If no error occurs, pgm_sockaddr_join_group returns zero. Otherwise, a + * value of PGM_SOCKET_ERROR is returned, and a specific error code can be + * retrieved by calling pgm_sock_errno(). + */ + +int +pgm_sockaddr_join_group ( + const int s, + const sa_family_t sa_family, + const struct group_req* gr + ) +{ + int retval = PGM_SOCKET_ERROR; +#ifdef CONFIG_HAVE_MCAST_JOIN +/* Solaris:ip(7P) "The following options take a struct ip_mreq_source as the + * parameter." Presumably with source field zeroed out. + * Solaris:ip6(7P) "Takes a struct group_req as the parameter." + * Different type for each family, however group_req is protocol-independent. + * + * Stevens: "MCAST_JOIN_GROUP has datatype group_req{}." + * + * RFC3678: Argument type struct group_req + */ + const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6; + retval = setsockopt (s, recv_level, MCAST_JOIN_GROUP, gr, sizeof(struct group_req)); +#else + switch (sa_family) { + case AF_INET: { +/* Solaris:ip(7P) Just mentions "Join a multicast group." + * No further details provided. + * + * Linux:ip(7) "Argument is an ip_mreqn structure. For compatibility, the old + * ip_mreq structure (present since Linux 1.2) is still supported." + * + * FreeBSD,OS X:IP(4) provided by example "struct ip_mreq mreq;" + * + * Windows can optionally abuse imt_interface to be 0.0.0. + * + * Stevens: "IP_ADD_MEMBERSHIP has datatype ip_mreq{}." + * + * RFC3678: Argument type struct ip_mreq + */ +#ifdef CONFIG_HAVE_IP_MREQN + struct ip_mreqn mreqn; + struct sockaddr_in ifaddr; + memset (&mreqn, 0, sizeof(mreqn)); + mreqn.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gr->gr_group)->sin_addr.s_addr; + if (!pgm_if_indextoaddr (gr->gr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL)) + return -1; + mreqn.imr_address.s_addr = ifaddr.sin_addr.s_addr; + mreqn.imr_ifindex = gr->gr_interface; + retval = setsockopt (s, SOL_IP, IP_ADD_MEMBERSHIP, (const char*)&mreqn, sizeof(mreqn)); +#else + struct ip_mreq mreq; + struct sockaddr_in ifaddr; + memset (&mreq, 0, sizeof(mreq)); + mreq.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gr->gr_group)->sin_addr.s_addr; + if (!pgm_if_indextoaddr (gr->gr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL)) + return -1; + mreq.imr_interface.s_addr = ifaddr.sin_addr.s_addr; + retval = setsockopt (s, SOL_IP, IP_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)); +#endif /* !CONFIG_HAVE_IP_MREQN */ + break; + } + + case AF_INET6: { +/* Solaris:ip6(7P) "Takes a struct ipv6_mreq as the parameter;" + * + * Linux:ipv6(7) "Argument is a pointer to a struct ipv6_mreq structure." + * + * OS X:IP6(4) "IPV6_JOIN_GROUP struct ipv6_mreq *" + * + * Stevens: "IPV6_JOIN_GROUP has datatype ipv6_mreq{}." + */ + struct ipv6_mreq mreq6; + memset (&mreq6, 0, sizeof(mreq6)); + mreq6.ipv6mr_multiaddr = ((const struct sockaddr_in6*)&gr->gr_group)->sin6_addr; + mreq6.ipv6mr_interface = gr->gr_interface; + retval = setsockopt (s, SOL_IPV6, IPV6_ADD_MEMBERSHIP, (const char*)&mreq6, sizeof(mreq6)); + break; + } + + default: break; + } +#endif /* CONFIG_HAVE_MCAST_JOIN */ + return retval; +} + +/* leave a joined group + */ + +int +pgm_sockaddr_leave_group ( + const int s, + const sa_family_t sa_family, + const struct group_req* gr + ) +{ + int retval = PGM_SOCKET_ERROR; +#ifdef CONFIG_HAVE_MCAST_JOIN + const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6; + retval = setsockopt (s, recv_level, MCAST_LEAVE_GROUP, gr, sizeof(struct group_req)); +#else + switch (sa_family) { + case AF_INET: { +#ifdef CONFIG_HAVE_IP_MREQN + struct ip_mreqn mreqn; + struct sockaddr_in ifaddr; + memset (&mreqn, 0, sizeof(mreqn)); + mreqn.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gr->gr_group)->sin_addr.s_addr; + if (!pgm_if_indextoaddr (gr->gr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL)) + return -1; + mreqn.imr_address.s_addr = ifaddr.sin_addr.s_addr; + mreqn.imr_ifindex = gr->gr_interface; + retval = setsockopt (s, SOL_IP, IP_DROP_MEMBERSHIP, (const char*)&mreqn, sizeof(mreqn)); +#else + struct ip_mreq mreq; + struct sockaddr_in ifaddr; + memset (&mreq, 0, sizeof(mreq)); + mreq.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gr->gr_group)->sin_addr.s_addr; + if (!pgm_if_indextoaddr (gr->gr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL)) + return -1; + mreq.imr_interface.s_addr = ifaddr.sin_addr.s_addr; + retval = setsockopt (s, SOL_IP, IP_DROP_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)); +#endif /* !CONFIG_HAVE_IP_MREQN */ + break; + } + + case AF_INET6: { + struct ipv6_mreq mreq6; + memset (&mreq6, 0, sizeof(mreq6)); + mreq6.ipv6mr_multiaddr = ((const struct sockaddr_in6*)&gr->gr_group)->sin6_addr; + mreq6.ipv6mr_interface = gr->gr_interface; + retval = setsockopt (s, SOL_IPV6, IPV6_DROP_MEMBERSHIP, (const char*)&mreq6, sizeof(mreq6)); + break; + } + + default: break; + } +#endif /* CONFIG_HAVE_MCAST_JOIN */ + return retval; +} + +/* block either at the NIC or kernel, packets from a particular source + */ + +int +pgm_sockaddr_block_source ( + const int s, + const sa_family_t sa_family, + const struct group_source_req* gsr + ) +{ + int retval = PGM_SOCKET_ERROR; +#ifdef CONFIG_HAVE_MCAST_JOIN + const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6; + retval = setsockopt (s, recv_level, MCAST_BLOCK_SOURCE, gsr, sizeof(struct group_source_req)); +#elif defined(IP_BLOCK_SOURCE) + switch (sa_family) { + case AF_INET: { + struct ip_mreq_source mreqs; + struct sockaddr_in ifaddr; + memset (&mreqs, 0, sizeof(mreqs)); + mreqs.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_group)->sin_addr.s_addr; + mreqs.imr_sourceaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_source)->sin_addr.s_addr; + pgm_if_indextoaddr (gsr->gsr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL); + mreqs.imr_interface.s_addr = ifaddr.sin_addr.s_addr; + retval = setsockopt (s, SOL_IP, IP_BLOCK_SOURCE, (const char*)&mreqs, sizeof(mreqs)); + break; + } + + case AF_INET6: +/* No IPv6 API implemented, MCAST_BLOCK_SOURCE should be available instead. + */ + break; + + default: break; + } +#endif /* CONFIG_HAVE_MCAST_JOIN */ + return retval; +} + +/* unblock a blocked multicast source. + */ + +int +pgm_sockaddr_unblock_source ( + const int s, + const sa_family_t sa_family, + const struct group_source_req* gsr + ) +{ + int retval = PGM_SOCKET_ERROR; +#ifdef CONFIG_HAVE_MCAST_JOIN + const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6; + retval = setsockopt (s, recv_level, MCAST_UNBLOCK_SOURCE, gsr, sizeof(struct group_source_req)); +#elif defined(IP_UNBLOCK_SOURCE) + switch (sa_family) { + case AF_INET: { + struct ip_mreq_source mreqs; + struct sockaddr_in ifaddr; + memset (&mreqs, 0, sizeof(mreqs)); + mreqs.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_group)->sin_addr.s_addr; + mreqs.imr_sourceaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_source)->sin_addr.s_addr; + pgm_if_indextoaddr (gsr->gsr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL); + mreqs.imr_interface.s_addr = ifaddr.sin_addr.s_addr; + retval = setsockopt (s, SOL_IP, IP_UNBLOCK_SOURCE, (const char*)&mreqs, sizeof(mreqs)); + break; + } + + case AF_INET6: +/* No IPv6 API implemented, MCAST_UNBLOCK_SOURCE should be available instead. + */ + break; + + default: break; + } +#endif /* CONFIG_HAVE_MCAST_JOIN */ + return retval; +} + +/* Join source-specific multicast. + * NB: Silently reverts to ASM if SSM not supported. + * + * If no error occurs, pgm_sockaddr_join_source_group returns zero. + * Otherwise, a value of PGM_SOCKET_ERROR is returned, and a specific error + * code can be retrieved by calling pgm_sock_errno(). + */ + +int +pgm_sockaddr_join_source_group ( + const int s, + const sa_family_t sa_family, + const struct group_source_req* gsr + ) +{ + int retval = PGM_SOCKET_ERROR; +#ifdef CONFIG_HAVE_MCAST_JOIN +/* Solaris:ip(7P) "The following options take a struct ip_mreq_source as the + * parameter." + * Solaris:ip6(7P) "Takes a struct group_source_req as the parameter." + * Different type for each family, however group_source_req is protocol- + * independent. + * + * Stevens: "MCAST_JOIN_SOURCE_GROUP has datatype group_source_req{}." + * + * RFC3678: Argument type struct group_source_req + */ + const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6; + retval = setsockopt (s, recv_level, MCAST_JOIN_SOURCE_GROUP, gsr, sizeof(struct group_source_req)); +#elif defined(IP_ADD_SOURCE_MEMBERSHIP) + switch (sa_family) { + case AF_INET: { +/* Solaris:ip(7P) "The following options take a struct ip_mreq as the + * parameter." Incorrect literature wrt RFC. + * + * Linux:ip(7) absent. + * + * OS X:IP(4) absent. + * + * Stevens: "IP_ADD_SOURCE_MEMBERSHIP has datatype ip_mreq_source{}." + * + * RFC3678: Argument type struct ip_mreq_source + */ + struct ip_mreq_source mreqs; + struct sockaddr_in ifaddr; + memset (&mreqs, 0, sizeof(mreqs)); + mreqs.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_group)->sin_addr.s_addr; + mreqs.imr_sourceaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_source)->sin_addr.s_addr; + pgm_if_indextoaddr (gsr->gsr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL); + mreqs.imr_interface.s_addr = ifaddr.sin_addr.s_addr; + retval = setsockopt (s, SOL_IP, IP_ADD_SOURCE_MEMBERSHIP, (const char*)&mreqs, sizeof(mreqs)); + break; + } + + case AF_INET6: +/* No IPv6 API implemented, MCAST_JOIN_SOURCE_GROUP should be available instead. + */ + retval = pgm_sockaddr_join_group (s, sa_family, (const struct group_req*)gsr); + break; + + default: break; + } +#else + retval = pgm_sockaddr_join_group (s, sa_family, (const struct group_req*)gsr); +#endif /* CONFIG_HAVE_MCAST_JOIN */ + return retval; +} + +/* drop a SSM source + */ + +int +pgm_sockaddr_leave_source_group ( + const int s, + const sa_family_t sa_family, + const struct group_source_req* gsr + ) +{ + int retval = PGM_SOCKET_ERROR; +#ifdef CONFIG_HAVE_MCAST_JOIN + const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6; + retval = setsockopt (s, recv_level, MCAST_LEAVE_SOURCE_GROUP, gsr, sizeof(struct group_source_req)); +#elif defined(IP_ADD_SOURCE_MEMBERSHIP) + switch (sa_family) { + case AF_INET: { + struct ip_mreq_source mreqs; + struct sockaddr_in ifaddr; + memset (&mreqs, 0, sizeof(mreqs)); + mreqs.imr_multiaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_group)->sin_addr.s_addr; + mreqs.imr_sourceaddr.s_addr = ((const struct sockaddr_in*)&gsr->gsr_source)->sin_addr.s_addr; + pgm_if_indextoaddr (gsr->gsr_interface, AF_INET, 0, (struct sockaddr*)&ifaddr, NULL); + mreqs.imr_interface.s_addr = ifaddr.sin_addr.s_addr; + retval = setsockopt (s, SOL_IP, IP_DROP_SOURCE_MEMBERSHIP, (const char*)&mreqs, sizeof(mreqs)); + break; + } + + case AF_INET6: +/* No IPv6 API implemented, MCAST_LEAVE_SOURCE_GROUP should be available instead. + */ + retval = pgm_sockaddr_leave_group (s, sa_family, (const struct group_req*)gsr); + break; + + default: break; + } +#else + retval = pgm_sockaddr_leave_group (s, sa_family, (const struct group_req*)gsr); +#endif /* CONFIG_HAVE_MCAST_JOIN */ + return retval; +} + +#if defined(MCAST_MSFILTER) || defined(SIOCSMSFILTER) +/* Batch block and unblock sources. + */ + +int +pgm_sockaddr_msfilter ( + const int s, + const sa_family_t sa_family, + const struct group_filter* gf_list + ) +{ + int retval = PGM_SOCKET_ERROR; +#ifdef MCAST_MSFILTER + const int recv_level = (AF_INET == sa_family) ? SOL_IP : SOL_IPV6; + const socklen_t len = GROUP_FILTER_SIZE(gf_list->gf_numsrc); + retval = setsockopt (s, recv_level, MCAST_MSFILTER, (const char*)gf_list, len); +#elif defined(SIOCSMSFILTER) + retval = ioctl (s, SIOCSMSFILTER, (const char*)gf_list); +#endif + return retval; +} +#endif /* MCAST_MSFILTER || SIOCSMSFILTER */ + +/* Specify outgoing interface. + * + * If no error occurs, pgm_sockaddr_multicast_if returns zero. Otherwise, a + * value of PGM_SOCKET_ERROR is returned, and a specific error code can be + * retrieved by calling pgm_sock_errno(). + */ + +int +pgm_sockaddr_multicast_if ( + int s, + const struct sockaddr* address, + unsigned ifindex + ) +{ + int retval = PGM_SOCKET_ERROR; + + switch (address->sa_family) { + case AF_INET: { +/* Solaris:ip(7P) "This option takes a struct in_addr as an argument, and it + * selects that interface for outgoing IP multicast packets." + * + * Linux:ip(7) "Argument is an ip_mreqn or ip_mreq structure similar to + * IP_ADD_MEMBERSHIP." + * + * OS X:IP(4) provided by example "struct in_addr addr;" + * + * Stevens: "IP_MULTICAST_IF has datatype struct in_addr{}." + */ + struct sockaddr_in s4; + memcpy (&s4, address, sizeof(struct sockaddr_in)); + retval = setsockopt (s, IPPROTO_IP, IP_MULTICAST_IF, (const char*)&s4.sin_addr, sizeof(s4.sin_addr)); + break; + } + + case AF_INET6: { +#ifndef _WIN32 +/* Solaris:ip6(7P) "This option takes an integer as an argument; the integer + * is the interface index of the selected interface." + * + * Linux:ipv6(7) "The argument is a pointer to an interface index (see + * netdevice(7)) in an integer." + * + * OS X:IP6(4) "IPV6_MULTICAST_IF u_int *" + * + * Stevens: "IPV6_MULTICAST_IF has datatype u_int." + */ + const unsigned int optval = ifindex; +#else + const DWORD optval = ifindex; +#endif + retval = setsockopt (s, IPPROTO_IPV6, IPV6_MULTICAST_IF, (const char*)&optval, sizeof(optval)); + break; + } + + default: break; + } + return retval; +} + +/* Specify multicast loop, other applications on the same host may receive + * outgoing packets. This does not affect unicast packets such as NAKs. + * + * If no error occurs, pgm_sockaddr_multicast_loop returns zero. Otherwise, a + * value of PGM_SOCKET_ERROR is returned, and a specific error code can be + * retrieved by calling pgm_sock_errno(). + */ + +int +pgm_sockaddr_multicast_loop ( + const int s, + const sa_family_t sa_family, + const bool v + ) +{ + int retval = PGM_SOCKET_ERROR; + + switch (sa_family) { + case AF_INET: { +#ifndef _WIN32 +/* Solaris:ip(7P) "Setting the unsigned character argument to 0 causes the + * opposite behavior, meaning that when multiple zones are present, the + * datagrams are delivered to all zones except the sending zone." + * + * Linux:ip(7) "Sets or reads a boolean integer argument" + * + * OS X:IP(4) provided by example "u_char loop;" + * + * Stevens: "IP_MULTICAST_LOOP has datatype u_char." + */ + const unsigned char optval = v ? 1 : 0; +#else + const DWORD optval = v ? 1 : 0; +#endif + retval = setsockopt (s, IPPROTO_IP, IP_MULTICAST_LOOP, (const char*)&optval, sizeof(optval)); + break; + } + + case AF_INET6: { +#ifndef _WIN32 +/* Solaris:ip(7P) "Setting the unsigned character argument to 0 will cause the opposite behavior." + * + * Linux:ipv6(7) "Argument is a pointer to boolean." + * + * OS X:IP6(7) "IPV6_MULTICAST_LOOP u_int *" + * + * Stevens: "IPV6_MULTICAST_LOOP has datatype u_int." + */ + const unsigned int optval = v ? 1 : 0; +#else + const DWORD optval = v ? 1 : 0; +#endif + retval = setsockopt (s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const char*)&optval, sizeof(optval)); + break; + } + + default: break; + } + return retval; +} + +/* Specify TTL or outgoing hop limit. + * NB: Only affects multicast hops, unicast hop-limit is not changed. + * + * If no error occurs, pgm_sockaddr_multicast_hops returns zero. Otherwise, a + * value of PGM_SOCKET_ERROR is returned, and a specific error code can be + * retrieved by calling pgm_sock_errno(). + */ + +int +pgm_sockaddr_multicast_hops ( + const int s, + const sa_family_t sa_family, + const unsigned hops + ) +{ + int retval = PGM_SOCKET_ERROR; + + switch (sa_family) { + case AF_INET: { +#ifndef _WIN32 +/* Solaris:ip(7P) "This option takes an unsigned character as an argument." + * + * Linux:ip(7) "Argument is an integer." + * + * OS X:IP(4) provided by example for SOCK_DGRAM with IP_TTL: "int ttl = 60;", + * or for SOCK_RAW & SOCK_DGRAM with IP_MULTICAST_TTL: "u_char ttl;" + * + * Stevens: "IP_MULTICAST_TTL has datatype u_char." + */ + const unsigned char optval = hops; +#else + const DWORD optval = hops; +#endif + retval = setsockopt (s, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&optval, sizeof(optval)); + break; + } + + case AF_INET6: { +#ifndef _WIN32 +/* Solaris:ip6(7P) "This option takes an integer as an argument." + * + * Linux:ipv6(7) "Argument is a pointer to an integer." + * + * OS X:IP6(7) "IPV6_MULTICAST_HOPS int *" + * + * Stevens: "IPV6_MULTICAST_HOPS has datatype int." + */ + const int optval = hops; +#else + const DWORD optval = hops; +#endif + retval = setsockopt (s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char*)&optval, sizeof(optval)); + break; + } + + default: break; + } + return retval; +} + +void +pgm_sockaddr_nonblocking ( + const int s, + const bool v + ) +{ +#ifndef _WIN32 + int flags = fcntl (s, F_GETFL); + if (!v) flags &= ~O_NONBLOCK; + else flags |= O_NONBLOCK; + fcntl (s, F_SETFL, flags); +#else + u_long mode = v; + ioctlsocket (s, FIONBIO, &mode); +#endif +} + +/* Note that are sockaddr structure is not passed these functions inherently + * cannot support IPv6 Zone Indices and hence are rather limited for the + * link-local scope. + */ +const char* +pgm_inet_ntop ( + int af, + const void* restrict src, + char* restrict dst, + socklen_t size + ) +{ + pgm_assert (AF_INET == af || AF_INET6 == af); + pgm_assert (NULL != src); + pgm_assert (NULL != dst); + pgm_assert (size > 0); + + switch (af) { + case AF_INET: + { + struct sockaddr_in sin; + memset (&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr = *(const struct in_addr*)src; + getnameinfo ((struct sockaddr*)&sin, sizeof(sin), + dst, size, + NULL, 0, + NI_NUMERICHOST); + return dst; + } + case AF_INET6: + { + struct sockaddr_in6 sin6; + memset (&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = *(const struct in6_addr*)src; + getnameinfo ((struct sockaddr*)&sin6, sizeof(sin6), + dst, size, + NULL, 0, + NI_NUMERICHOST); + return dst; + } + } + + errno = EAFNOSUPPORT; + return NULL; +} + +int +pgm_inet_pton ( + int af, + const char* restrict src, + void* restrict dst + ) +{ + pgm_assert (AF_INET == af || AF_INET6 == af); + pgm_assert (NULL != src); + pgm_assert (NULL != dst); + + struct addrinfo hints = { + .ai_family = af, + .ai_socktype = SOCK_STREAM, /* not really */ + .ai_protocol = IPPROTO_TCP, /* not really */ + .ai_flags = AI_NUMERICHOST + }, *result = NULL; + + const int e = getaddrinfo (src, NULL, &hints, &result); + if (0 != e) { + return 0; /* error */ + } + + pgm_assert (NULL != result->ai_addr); + pgm_assert (0 != result->ai_addrlen); + + switch (result->ai_addr->sa_family) { + case AF_INET: { + struct sockaddr_in s4; + memcpy (&s4, result->ai_addr, sizeof(s4)); + memcpy (dst, &s4.sin_addr.s_addr, sizeof(struct in_addr)); + break; + } + + case AF_INET6: { + struct sockaddr_in6 s6; + memcpy (&s6, result->ai_addr, sizeof(s6)); + memcpy (dst, &s6.sin6_addr, sizeof(struct in6_addr)); + break; + } + + default: + pgm_assert_not_reached(); + break; + } + + freeaddrinfo (result); + return 1; /* success */ +} + +int +pgm_nla_to_sockaddr ( + const void* restrict nla, + struct sockaddr* restrict sa + ) +{ + uint16_t nla_family; + int retval = 0; + + memcpy (&nla_family, nla, sizeof(nla_family)); + sa->sa_family = ntohs (nla_family); + switch (sa->sa_family) { + case AFI_IP: + sa->sa_family = AF_INET; + ((struct sockaddr_in*)sa)->sin_addr.s_addr = ((const struct in_addr*)((const char*)nla + sizeof(uint32_t)))->s_addr; + break; + + case AFI_IP6: + sa->sa_family = AF_INET6; + memcpy (&((struct sockaddr_in6*)sa)->sin6_addr, (const struct in6_addr*)((const char*)nla + sizeof(uint32_t)), sizeof(struct in6_addr)); + break; + + default: + retval = -EINVAL; + break; + } + + return retval; +} + +int +pgm_sockaddr_to_nla ( + const struct sockaddr* restrict sa, + void* restrict nla + ) +{ + int retval = 0; + + *(uint16_t*)nla = sa->sa_family; + *(uint16_t*)((char*)nla + sizeof(uint16_t)) = 0; /* reserved 16bit space */ + switch (sa->sa_family) { + case AF_INET: + *(uint16_t*)nla = htons (AFI_IP); + ((struct in_addr*)((char*)nla + sizeof(uint32_t)))->s_addr = ((const struct sockaddr_in*)sa)->sin_addr.s_addr; + break; + + case AF_INET6: + *(uint16_t*)nla = htons (AFI_IP6); + memcpy ((struct in6_addr*)((char*)nla + sizeof(uint32_t)), &((const struct sockaddr_in6*)sa)->sin6_addr, sizeof(struct in6_addr)); + break; + + default: + retval = -EINVAL; + break; + } + + return retval; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/sockaddr.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/sockaddr.c.c89.patch new file mode 100644 index 0000000..96fa367 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/sockaddr.c.c89.patch @@ -0,0 +1,75 @@ +--- sockaddr.c 2010-08-04 17:08:05.000000000 +0800 ++++ sockaddr.c89 2010-08-05 13:19:20.000000000 +0800 +@@ -145,12 +145,13 @@ + struct sockaddr* restrict dst /* will error on wrong size */ + ) + { +- struct addrinfo hints = { +- .ai_family = AF_UNSPEC, +- .ai_socktype = SOCK_STREAM, /* not really */ +- .ai_protocol = IPPROTO_TCP, /* not really */ +- .ai_flags = AI_NUMERICHOST +- }, *result = NULL; ++ struct addrinfo hints, *result; ++ memset (&hints, 0, sizeof(hints)); ++ hints.ai_family = AF_UNSPEC; ++ hints.ai_socktype = SOCK_STREAM; /* not really */ ++ hints.ai_protocol = IPPROTO_TCP; /* not really */ ++ hints.ai_flags = AI_NUMERICHOST; ++ { + const int status = getaddrinfo (src, NULL, &hints, &result); + if (PGM_LIKELY(0 == status)) { + memcpy (dst, result->ai_addr, result->ai_addrlen); +@@ -158,6 +159,7 @@ + return 1; + } + return 0; ++ } + } + + /* returns tri-state value: 1 if sa is multicast, 0 if sa is not multicast, -1 on error +@@ -830,7 +832,13 @@ + const socklen_t len = GROUP_FILTER_SIZE(gf_list->gf_numsrc); + retval = setsockopt (s, recv_level, MCAST_MSFILTER, (const char*)gf_list, len); + #elif defined(SIOCSMSFILTER) ++# ifdef _WIN32 ++# pragma warning( disable : 4090 ) ++ retval = ioctlsocket (s, SIOCSMSFILTER, (const char*)gf_list); ++# pragma warning( default : 4090 ) ++# else + retval = ioctl (s, SIOCSMSFILTER, (const char*)gf_list); ++# endif + #endif + return retval; + } +@@ -1092,13 +1100,15 @@ + pgm_assert (NULL != src); + pgm_assert (NULL != dst); + +- struct addrinfo hints = { +- .ai_family = af, +- .ai_socktype = SOCK_STREAM, /* not really */ +- .ai_protocol = IPPROTO_TCP, /* not really */ +- .ai_flags = AI_NUMERICHOST +- }, *result = NULL; ++ { ++ struct addrinfo hints, *result; ++ memset (&hints, 0, sizeof(hints)); ++ hints.ai_family = af; ++ hints.ai_socktype = SOCK_STREAM; /* not really */ ++ hints.ai_protocol = IPPROTO_TCP; /* not really */ ++ hints.ai_flags = AI_NUMERICHOST; + ++ { + const int e = getaddrinfo (src, NULL, &hints, &result); + if (0 != e) { + return 0; /* error */ +@@ -1129,6 +1139,8 @@ + + freeaddrinfo (result); + return 1; /* success */ ++ } ++ } + } + + int diff --git a/3rdparty/openpgm-svn-r1135/pgm/socket.c b/3rdparty/openpgm-svn-r1135/pgm/socket.c new file mode 100644 index 0000000..f838f18 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/socket.c @@ -0,0 +1,2150 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM socket: manage incoming & outgoing sockets with ambient SPMs, + * transmit & receive windows. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#ifdef CONFIG_HAVE_POLL +# include +#endif +#ifdef CONFIG_HAVE_EPOLL +# include +#endif +#include +#include +#include +#include +#include +#include +#include + + +//#define SOCK_DEBUG +//#define SOCK_SPM_DEBUG + + +/* global locals */ +pgm_rwlock_t pgm_sock_list_lock; /* list of all sockets for admin interfaces */ +pgm_slist_t* pgm_sock_list = NULL; + + +static const char* pgm_family_string (const int) PGM_GNUC_CONST; +static const char* pgm_sock_type_string (const int) PGM_GNUC_CONST; +static const char* pgm_protocol_string (const int) PGM_GNUC_CONST; + + +size_t +pgm_pkt_offset ( + bool can_fragment, + sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + const size_t data_size = sizeof(struct pgm_header) + sizeof(struct pgm_data); + size_t pkt_size = data_size; + if (can_fragment || (0 != pgmcc_family)) + pkt_size += sizeof(struct pgm_opt_length) + sizeof(struct pgm_opt_header); + if (can_fragment) + pkt_size += sizeof(struct pgm_opt_fragment); + if (AF_INET == pgmcc_family) + pkt_size += sizeof(struct pgm_opt_pgmcc_data); + else if (AF_INET6 == pgmcc_family) + pkt_size += sizeof(struct pgm_opt6_pgmcc_data); + return pkt_size; +} + +/* destroy a pgm_sock object and contents, if last sock also destroy + * associated event loop + * + * outstanding locks: + * 1) pgm_sock_t::lock + * 2) pgm_sock_t::receiver_mutex + * 3) pgm_sock_t::source_mutex + * 4) pgm_sock_t::txw_spinlock + * 5) pgm_sock_t::timer_mutex + * + * If application calls a function on the sock after destroy() it is a + * programmer error: segv likely to occur on unlock. + * + * on success, returns TRUE, on failure returns FALSE. + */ + +bool +pgm_close ( + pgm_sock_t* sock, + bool flush + ) +{ + pgm_return_val_if_fail (sock != NULL, FALSE); + if (!pgm_rwlock_reader_trylock (&sock->lock)) + pgm_return_val_if_reached (FALSE); + pgm_return_val_if_fail (!sock->is_destroyed, FALSE); + pgm_debug ("pgm_sock_destroy (sock:%p flush:%s)", + (const void*)sock, + flush ? "TRUE":"FALSE"); +/* flag existing calls */ + sock->is_destroyed = TRUE; +/* cancel running blocking operations */ + if (PGM_INVALID_SOCKET != sock->recv_sock) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Closing receive socket.")); + pgm_closesocket (sock->recv_sock); + sock->recv_sock = PGM_INVALID_SOCKET; + } + if (PGM_INVALID_SOCKET != sock->send_sock) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Closing send socket.")); + pgm_closesocket (sock->send_sock); + sock->send_sock = PGM_INVALID_SOCKET; + } + pgm_rwlock_reader_unlock (&sock->lock); + pgm_debug ("blocking on destroy lock ..."); + pgm_rwlock_writer_lock (&sock->lock); + + pgm_debug ("removing sock from inventory."); + pgm_rwlock_writer_lock (&pgm_sock_list_lock); + pgm_sock_list = pgm_slist_remove (pgm_sock_list, sock); + pgm_rwlock_writer_unlock (&pgm_sock_list_lock); + +/* flush source side by sending heartbeat SPMs */ + if (sock->can_send_data && + sock->is_connected && + flush) + { + pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Flushing PGM source with session finish option broadcast SPMs.")); + if (!pgm_send_spm (sock, PGM_OPT_FIN) || + !pgm_send_spm (sock, PGM_OPT_FIN) || + !pgm_send_spm (sock, PGM_OPT_FIN)) + { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Failed to send flushing SPMs.")); + } + } + + if (sock->peers_hashtable) { + pgm_debug ("destroying peer lookup table."); + pgm_hashtable_destroy (sock->peers_hashtable); + sock->peers_hashtable = NULL; + } + if (sock->peers_list) { + pgm_debug ("destroying peer list."); + do { + pgm_list_t* next = sock->peers_list->next; + pgm_peer_unref ((pgm_peer_t*)sock->peers_list->data); + + sock->peers_list = next; + } while (sock->peers_list); + } + + if (sock->window) { + pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Destroying transmit window.")); + pgm_txw_shutdown (sock->window); + sock->window = NULL; + } + pgm_trace (PGM_LOG_ROLE_RATE_CONTROL,_("Destroying rate control.")); + pgm_rate_destroy (&sock->rate_control); + if (PGM_INVALID_SOCKET != sock->send_with_router_alert_sock) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Closing send with router alert socket.")); + pgm_closesocket (sock->send_with_router_alert_sock); + sock->send_with_router_alert_sock = PGM_INVALID_SOCKET; + } + if (sock->spm_heartbeat_interval) { + pgm_debug ("freeing SPM heartbeat interval data."); + pgm_free (sock->spm_heartbeat_interval); + sock->spm_heartbeat_interval = NULL; + } + if (sock->rx_buffer) { + pgm_debug ("freeing receive buffer."); + pgm_free_skb (sock->rx_buffer); + sock->rx_buffer = NULL; + } + pgm_debug ("destroying notification channels."); + if (sock->can_send_data) { + if (sock->use_pgmcc) { + pgm_notify_destroy (&sock->ack_notify); + } + pgm_notify_destroy (&sock->rdata_notify); + } + pgm_notify_destroy (&sock->pending_notify); + pgm_debug ("freeing sock locks."); + pgm_rwlock_free (&sock->peers_lock); + pgm_spinlock_free (&sock->txw_spinlock); + pgm_mutex_free (&sock->send_mutex); + pgm_mutex_free (&sock->timer_mutex); + pgm_mutex_free (&sock->source_mutex); + pgm_mutex_free (&sock->receiver_mutex); + pgm_rwlock_writer_unlock (&sock->lock); + pgm_rwlock_free (&sock->lock); + pgm_debug ("freeing sock data."); + pgm_free (sock); + pgm_debug ("finished."); + return TRUE; +} + +/* Create a pgm_sock object. Create sockets that require superuser + * priviledges. If interface ports are specified then UDP encapsulation will + * be used instead of raw protocol. + * + * If send == recv only two sockets need to be created iff ip headers are not + * required (IPv6). + * + * All receiver addresses must be the same family. + * interface and multiaddr must be the same family. + * family cannot be AF_UNSPEC! + * + * returns TRUE on success, or FALSE on error and sets error appropriately. + */ + +#if ( AF_INET != PF_INET ) || ( AF_INET6 != PF_INET6 ) +#error AF_INET and PF_INET are different values, the bananas are jumping in their pyjamas! +#endif + +bool +pgm_socket ( + pgm_sock_t** restrict sock, + const sa_family_t family, /* communications domain */ + const int pgm_sock_type, + const int protocol, + pgm_error_t** restrict error + ) +{ + pgm_sock_t* new_sock; + + pgm_return_val_if_fail (NULL != sock, FALSE); + pgm_return_val_if_fail (AF_INET == family || AF_INET6 == family, FALSE); + pgm_return_val_if_fail (SOCK_SEQPACKET == pgm_sock_type, FALSE); + pgm_return_val_if_fail (IPPROTO_UDP == protocol || IPPROTO_PGM == protocol, FALSE); + + pgm_debug ("socket (sock:%p family:%s sock-type:%s protocol:%s error:%p)", + (const void*)sock, pgm_family_string(family), pgm_sock_type_string(pgm_sock_type), pgm_protocol_string(protocol), (const void*)error); + + new_sock = pgm_new0 (pgm_sock_t, 1); + new_sock->family = family; + new_sock->socket_type = pgm_sock_type; + new_sock->protocol = protocol; + new_sock->can_send_data = TRUE; + new_sock->can_send_nak = TRUE; + new_sock->can_recv_data = TRUE; + new_sock->dport = DEFAULT_DATA_DESTINATION_PORT; + new_sock->tsi.sport = DEFAULT_DATA_SOURCE_PORT; + new_sock->adv_mode = 0; /* advance with time */ + +/* PGMCC */ + new_sock->acker_nla.ss_family = family; + +/* source-side */ + pgm_mutex_init (&new_sock->source_mutex); +/* transmit window */ + pgm_spinlock_init (&new_sock->txw_spinlock); +/* send socket */ + pgm_mutex_init (&new_sock->send_mutex); +/* next timer & spm expiration */ + pgm_mutex_init (&new_sock->timer_mutex); +/* receiver-side */ + pgm_mutex_init (&new_sock->receiver_mutex); +/* peer hash map & list lock */ + pgm_rwlock_init (&new_sock->peers_lock); +/* destroy lock */ + pgm_rwlock_init (&new_sock->lock); + +/* open sockets to implement PGM */ + int socket_type; + if (IPPROTO_UDP == new_sock->protocol) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Opening UDP encapsulated sockets.")); + socket_type = SOCK_DGRAM; + new_sock->udp_encap_ucast_port = DEFAULT_UDP_ENCAP_UCAST_PORT; + new_sock->udp_encap_mcast_port = DEFAULT_UDP_ENCAP_MCAST_PORT; + } else { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Opening raw sockets.")); + socket_type = SOCK_RAW; + } + + if ((new_sock->recv_sock = socket (new_sock->family, + socket_type, + new_sock->protocol)) == PGM_INVALID_SOCKET) + { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + _("Creating receive socket: %s"), + pgm_sock_strerror (save_errno)); +#ifndef _WIN32 + if (EPERM == save_errno) { + pgm_error (_("PGM protocol requires CAP_NET_RAW capability, e.g. sudo execcap 'cap_net_raw=ep'")); + } +#endif + goto err_destroy; + } + + if ((new_sock->send_sock = socket (new_sock->family, + socket_type, + new_sock->protocol)) == PGM_INVALID_SOCKET) + { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + _("Creating send socket: %s"), + pgm_sock_strerror (save_errno)); + goto err_destroy; + } + + if ((new_sock->send_with_router_alert_sock = socket (new_sock->family, + socket_type, + new_sock->protocol)) == PGM_INVALID_SOCKET) + { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + _("Creating IP Router Alert (RFC 2113) send socket: %s"), + pgm_sock_strerror (save_errno)); + goto err_destroy; + } + + if (IPPROTO_UDP == new_sock->protocol) + { +/* Stevens: "SO_REUSEADDR has datatype int." + */ + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Set socket sharing.")); + const int v = 1; + if (PGM_SOCKET_ERROR == setsockopt (new_sock->recv_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&v, sizeof(v)) || + PGM_SOCKET_ERROR == setsockopt (new_sock->send_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&v, sizeof(v)) || + PGM_SOCKET_ERROR == setsockopt (new_sock->send_with_router_alert_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&v, sizeof(v))) + { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + _("Enabling reuse of socket local address: %s"), + pgm_sock_strerror (save_errno)); + goto err_destroy; + } + +/* request extra packet information to determine destination address on each packet */ +#ifndef CONFIG_TARGET_WINE + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Request socket packet-info.")); + const sa_family_t recv_family = new_sock->family; + if (PGM_SOCKET_ERROR == pgm_sockaddr_pktinfo (new_sock->recv_sock, recv_family, TRUE)) + { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + _("Enabling receipt of ancillary information per incoming packet: %s"), + pgm_sock_strerror (save_errno)); + goto err_destroy; + } +#endif + } + else + { + const sa_family_t recv_family = new_sock->family; + if (AF_INET == recv_family) + { +/* include IP header only for incoming data, only works for IPv4 */ + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Request IP headers.")); + if (PGM_SOCKET_ERROR == pgm_sockaddr_hdrincl (new_sock->recv_sock, recv_family, TRUE)) + { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + _("Enabling IP header in front of user data: %s"), + pgm_sock_strerror (save_errno)); + goto err_destroy; + } + } + else + { + pgm_assert (AF_INET6 == recv_family); + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Request socket packet-info.")); + if (PGM_SOCKET_ERROR == pgm_sockaddr_pktinfo (new_sock->recv_sock, recv_family, TRUE)) + { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + _("Enabling receipt of control message per incoming datagram: %s"), + pgm_sock_strerror (save_errno)); + goto err_destroy; + } + } + } + + *sock = new_sock; + + pgm_rwlock_writer_lock (&pgm_sock_list_lock); + pgm_sock_list = pgm_slist_append (pgm_sock_list, *sock); + pgm_rwlock_writer_unlock (&pgm_sock_list_lock); + pgm_debug ("PGM socket successfully created."); + return TRUE; + +err_destroy: + if (PGM_INVALID_SOCKET != new_sock->recv_sock) { + if (PGM_SOCKET_ERROR == pgm_closesocket (new_sock->recv_sock)) { + const int save_errno = pgm_sock_errno(); + pgm_warn (_("Close on receive socket failed: %s"), + pgm_sock_strerror (save_errno)); + } + new_sock->recv_sock = PGM_INVALID_SOCKET; + } + if (PGM_INVALID_SOCKET != new_sock->send_sock) { + if (PGM_SOCKET_ERROR == pgm_closesocket (new_sock->send_sock)) { + const int save_errno = pgm_sock_errno(); + pgm_warn (_("Close on send socket failed: %s"), + pgm_sock_strerror (save_errno)); + } + new_sock->send_sock = PGM_INVALID_SOCKET; + } + if (PGM_INVALID_SOCKET != new_sock->send_with_router_alert_sock) { + if (PGM_SOCKET_ERROR == pgm_closesocket (new_sock->send_with_router_alert_sock)) { + const int save_errno = pgm_sock_errno(); + pgm_warn (_("Close on IP Router Alert (RFC 2113) send socket failed: %s"), + pgm_sock_strerror (save_errno)); + } + new_sock->send_with_router_alert_sock = PGM_INVALID_SOCKET; + } + pgm_free (new_sock); + return FALSE; +} + +bool +pgm_getsockopt ( + pgm_sock_t* const restrict sock, + const int level, /* always IPPROTO_PGM */ + const int optname, + void* restrict optval, + socklen_t* restrict optlen /* required */ + ) +{ + bool status = FALSE; + pgm_return_val_if_fail (sock != NULL, status); + pgm_return_val_if_fail (level == IPPROTO_PGM, status); + pgm_return_val_if_fail (optval != NULL, status); + pgm_return_val_if_fail (optlen != NULL, status); + if (PGM_UNLIKELY(!pgm_rwlock_reader_trylock (&sock->lock))) + pgm_return_val_if_reached (status); + if (PGM_UNLIKELY(sock->is_destroyed)) { + pgm_rwlock_reader_unlock (&sock->lock); + return status; + } + switch (optname) { +/* maximum transmission packet size */ + case PGM_MTU: + if (PGM_UNLIKELY(*optlen != sizeof (int))) + break; + *(int*restrict)optval = sock->max_tpdu; + status = TRUE; + break; + +/* maximum segment size for unfragmented APDU */ + case PGM_MSSS: + if (PGM_UNLIKELY(*optlen != sizeof (int))) + break; + *(int*restrict)optval = sock->max_tsdu; + status = TRUE; + break; + +/* maximum segment size for fragmented APDU */ + case PGM_MSS: + if (PGM_UNLIKELY(*optlen != sizeof (int))) + break; + *(int*restrict)optval = sock->max_tsdu_fragment; + status = TRUE; + break; + +/* maximum payload size for an APDU */ + case PGM_PDU: + if (PGM_UNLIKELY(*optlen != sizeof (int))) + break; + *(int*restrict)optval = sock->max_apdu; + status = TRUE; + break; + + case PGM_ABORT_ON_RESET: + if (PGM_UNLIKELY(*optlen != sizeof (int))) + break; + *(int*restrict)optval = sock->is_abort_on_reset ? 1 : 0; + status = TRUE; + break; + +/* send socket */ + case PGM_SEND_SOCK: + if (PGM_UNLIKELY(!sock->is_connected)) + break; + if (PGM_UNLIKELY(*optlen != sizeof (int))) + break; + *(int*)optval = sock->send_sock; + status = TRUE; + break; + +/* receive socket */ + case PGM_RECV_SOCK: + if (PGM_UNLIKELY(!sock->is_connected)) + break; + if (PGM_UNLIKELY(*optlen != sizeof (int))) + break; + *(int*)optval = sock->recv_sock; + status = TRUE; + break; + +/* repair socket */ + case PGM_REPAIR_SOCK: + if (PGM_UNLIKELY(!sock->is_connected)) + break; + if (PGM_UNLIKELY(*optlen != sizeof (int))) + break; + *(int*)optval = pgm_notify_get_fd (&sock->rdata_notify); + status = TRUE; + break; + +/* pending socket */ + case PGM_PENDING_SOCK: + if (PGM_UNLIKELY(!sock->is_connected)) + break; + if (PGM_UNLIKELY(*optlen != sizeof (int))) + break; + *(int*)optval = pgm_notify_get_fd (&sock->pending_notify); + status = TRUE; + break; + +/* ACK or congestion socket */ + case PGM_ACK_SOCK: + if (PGM_UNLIKELY(!sock->is_connected)) + break; + if (PGM_UNLIKELY(*optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(!sock->use_pgmcc)) + break; + *(int*)optval = pgm_notify_get_fd (&sock->ack_notify); + status = TRUE; + break; + + +/* timeout for pending timer */ + case PGM_TIME_REMAIN: + if (PGM_UNLIKELY(!sock->is_connected)) + break; + if (PGM_UNLIKELY(*optlen != sizeof (struct timeval))) + break; + { + struct timeval* tv = optval; + const pgm_time_t usecs = pgm_timer_expiration (sock); + tv->tv_sec = usecs / 1000000UL; + tv->tv_usec = usecs % 1000000UL; + } + status = TRUE; + break; + +/* timeout for blocking sends */ + case PGM_RATE_REMAIN: + if (PGM_UNLIKELY(!sock->is_connected)) + break; + if (PGM_UNLIKELY(*optlen != sizeof (struct timeval))) + break; + { + struct timeval* tv = optval; + const pgm_time_t usecs = pgm_rate_remaining (&sock->rate_control, sock->blocklen); + tv->tv_sec = usecs / 1000000UL; + tv->tv_usec = usecs % 1000000UL; + } + status = TRUE; + break; + + + } + pgm_rwlock_reader_unlock (&sock->lock); + return status; +} + +bool +pgm_setsockopt ( + pgm_sock_t* const sock, + const int level, /* IPPROTO_PGM or SOL_SOCKET */ + const int optname, + const void* optval, + const socklen_t optlen + ) +{ + bool status = FALSE; + pgm_return_val_if_fail (sock != NULL, status); + pgm_return_val_if_fail (IPPROTO_PGM == level || SOL_SOCKET == level, status); + if (PGM_UNLIKELY(!pgm_rwlock_reader_trylock (&sock->lock))) + pgm_return_val_if_reached (status); + if (PGM_UNLIKELY(sock->is_connected || sock->is_destroyed)) { + pgm_rwlock_reader_unlock (&sock->lock); + return status; + } + + switch (level) { + case SOL_SOCKET: + switch (optname) { + +/* 0 < wmem < wmem_max (user) + * + * operating system and sysctl dependent maximum, minimum on Linux 256 (doubled). + */ + case SO_SNDBUF: + if (PGM_SOCKET_ERROR == setsockopt (sock->send_sock, SOL_SOCKET, SO_SNDBUF, (const char*)optval, optlen) || + PGM_SOCKET_ERROR == setsockopt (sock->send_with_router_alert_sock, SOL_SOCKET, SO_SNDBUF, (const char*)optval, optlen)) + break; + status = TRUE; + break; + +/* 0 < rmem < rmem_max (user) + * + * minimum on Linux is 2048 (doubled). + */ + case SO_RCVBUF: + if (PGM_SOCKET_ERROR == setsockopt (sock->recv_sock, SOL_SOCKET, SO_RCVBUF, (const char*)optval, optlen)) + break; + status = TRUE; + break; + + default: + break; + } + break; + + case IPPROTO_PGM: + switch (optname) { + +/* RFC2113 IP Router Alert + */ + case PGM_IP_ROUTER_ALERT: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + { + const bool v = (0 != *(const int*)optval); + if (PGM_SOCKET_ERROR == pgm_sockaddr_router_alert (sock->send_with_router_alert_sock, sock->family, v)) + break; + } + status = TRUE; + break; + +/* IPv4: 68 <= tpdu < 65536 (RFC 2765) + * IPv6: 1280 <= tpdu < 65536 (RFC 2460) + */ + case PGM_MTU: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval < (int)(sizeof(struct pgm_ip) + sizeof(struct pgm_header)))) + break; + if (PGM_UNLIKELY(*(const int*)optval > UINT16_MAX)) + break; + sock->max_tpdu = *(const int*)optval; + status = TRUE; + break; + +/* 1 = enable multicast loopback. + * 0 = default, to disable. + */ + case PGM_MULTICAST_LOOP: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + { + const bool v = (0 != *(const int*)optval); +#ifndef _WIN32 /* loop on send */ + if (PGM_SOCKET_ERROR == pgm_sockaddr_multicast_loop (sock->send_sock, sock->family, v) || + PGM_SOCKET_ERROR == pgm_sockaddr_multicast_loop (sock->send_with_router_alert_sock, sock->family, v)) + break; +#else /* loop on receive */ + if (PGM_SOCKET_ERROR == pgm_sockaddr_multicast_loop (sock->recv_sock, sock->family, v)) + break; +#endif + } + status = TRUE; + break; + +/* 0 < hops < 256, hops == -1 use kernel default (ignored). + */ + case PGM_MULTICAST_HOPS: +#ifndef CONFIG_TARGET_WINE + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + if (PGM_UNLIKELY(*(const int*)optval > UINT8_MAX)) + break; + { + sock->hops = *(const int*)optval; + if (PGM_SOCKET_ERROR == pgm_sockaddr_multicast_hops (sock->send_sock, sock->family, sock->hops) || + PGM_SOCKET_ERROR == pgm_sockaddr_multicast_hops (sock->send_with_router_alert_sock, sock->family, sock->hops)) + break; + } +#endif + status = TRUE; + break; + +/* IP Type of Service (ToS) or RFC 3246, differentiated services (DSCP) + */ + case PGM_TOS: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_SOCKET_ERROR == pgm_sockaddr_tos (sock->send_sock, sock->family, *(const int*)optval) || + PGM_SOCKET_ERROR == pgm_sockaddr_tos (sock->send_with_router_alert_sock, sock->family, *(const int*)optval)) + { + pgm_warn (_("ToS/DSCP setting requires CAP_NET_ADMIN or ADMIN capability.")); + break; + } + status = TRUE; + break; + +/* periodic ambient broadcast SPM interval in milliseconds. + */ + case PGM_AMBIENT_SPM: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + sock->spm_ambient_interval = *(const int*)optval; + status = TRUE; + break; + +/* sequence of heartbeat broadcast SPMS to flush out original + */ + case PGM_HEARTBEAT_SPM: + if (PGM_UNLIKELY(0 != optlen % sizeof (int))) + break; + { + sock->spm_heartbeat_len = optlen / sizeof (int); + sock->spm_heartbeat_interval = pgm_new (unsigned, sock->spm_heartbeat_len + 1); + sock->spm_heartbeat_interval[0] = 0; + for (unsigned i = 0; i < sock->spm_heartbeat_len; i++) + sock->spm_heartbeat_interval[i + 1] = ((const int*)optval)[i]; + } + status = TRUE; + break; + +/* size of transmit window in sequence numbers. + * 0 < txw_sqns < one less than half sequence space + */ + case PGM_TXW_SQNS: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + if (PGM_UNLIKELY(*(const int*)optval >= (int)((UINT32_MAX/2)-1))) + break; + sock->txw_sqns = *(const int*)optval; + status = TRUE; + break; + +/* size of transmit window in seconds. + * 0 < secs < ( txw_sqns / txw_max_rte ) + */ + case PGM_TXW_SECS: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + sock->txw_secs = *(const int*)optval; + status = TRUE; + break; + +/* maximum transmit rate. + * 0 < txw_max_rte < interface capacity + * 10mb : 1250000 + * 100mb : 12500000 + * 1gb : 125000000 + */ + case PGM_TXW_MAX_RTE: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + sock->txw_max_rte = *(const int*)optval; + status = TRUE; + break; + +/* timeout for peers. + * 0 < 2 * spm_ambient_interval <= peer_expiry + */ + case PGM_PEER_EXPIRY: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + sock->peer_expiry = *(const int*)optval; + status = TRUE; + break; + +/* maximum back off range for listening for multicast SPMR. + * 0 < spmr_expiry < spm_ambient_interval + */ + case PGM_SPMR_EXPIRY: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + sock->spmr_expiry = *(const int*)optval; + status = TRUE; + break; + +/* size of receive window in sequence numbers. + * 0 < rxw_sqns < one less than half sequence space + */ + case PGM_RXW_SQNS: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + if (PGM_UNLIKELY(*(const int*)optval >= (int)((UINT32_MAX/2)-1))) + break; + sock->rxw_sqns = *(const int*)optval; + status = TRUE; + break; + +/* size of receive window in seconds. + * 0 < secs < ( rxw_sqns / rxw_max_rte ) + */ + case PGM_RXW_SECS: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + sock->rxw_secs = *(const int*)optval; + status = TRUE; + break; + +/* maximum receive rate, for determining window size with txw_secs. + * 0 < rxw_max_rte < interface capacity + */ + case PGM_RXW_MAX_RTE: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + sock->rxw_max_rte = *(const int*)optval; + status = TRUE; + break; + +/* maximum NAK back-off value nak_rb_ivl in milliseconds. + * 0 < nak_rb_ivl <= nak_bo_ivl + */ + case PGM_NAK_BO_IVL: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + sock->nak_bo_ivl = *(const int*)optval; + status = TRUE; + break; + +/* repeat interval prior to re-sending a NAK, in milliseconds. + */ + case PGM_NAK_RPT_IVL: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + sock->nak_rpt_ivl = *(const int*)optval; + status = TRUE; + break; + +/* interval waiting for repair data, in milliseconds. + */ + case PGM_NAK_RDATA_IVL: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + sock->nak_rdata_ivl = *(const int*)optval; + status = TRUE; + break; + +/* limit for data. + * 0 < nak_data_retries < 256 + */ + case PGM_NAK_DATA_RETRIES: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + if (PGM_UNLIKELY(*(const int*)optval > UINT8_MAX)) + break; + sock->nak_data_retries = *(const int*)optval; + status = TRUE; + break; + +/* limit for NAK confirms. + * 0 < nak_ncf_retries < 256 + */ + case PGM_NAK_NCF_RETRIES: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + if (PGM_UNLIKELY(*(const int*)optval > UINT8_MAX)) + break; + sock->nak_ncf_retries = *(const int*)optval; + status = TRUE; + break; + +/* Enable FEC for this sock, specifically Reed Solmon encoding RS(n,k), common + * setting is RS(255, 223). + * + * inputs: + * + * n = FEC Block size = [k+1, 255] + * k = original data packets == transmission group size = [2, 4, 8, 16, 32, 64, 128] + * m = symbol size = 8 bits + * + * outputs: + * + * h = 2t = n - k = parity packets + * + * when h > k parity packets can be lost. + */ + case PGM_USE_FEC: + if (PGM_UNLIKELY(optlen != sizeof (struct pgm_fecinfo_t))) + break; + { + const struct pgm_fecinfo_t* fecinfo = optval; + if (PGM_UNLIKELY(0 != (fecinfo->group_size & (fecinfo->group_size - 1)))) + break; + if (PGM_UNLIKELY(fecinfo->group_size < 2 || fecinfo->group_size > 128)) + break; + if (PGM_UNLIKELY(fecinfo->group_size > fecinfo->block_size)) + break; + const uint8_t parity_packets = fecinfo->block_size - fecinfo->group_size; +/* technically could re-send previous packets */ + if (PGM_UNLIKELY(fecinfo->proactive_packets > parity_packets)) + break; +/* check validity of parameters */ + if (PGM_UNLIKELY(fecinfo->group_size > 223 && ((parity_packets * 223.0) / fecinfo->group_size) < 1.0)) + { + pgm_error (_("k/h ratio too low to generate parity data.")); + break; + } + sock->use_proactive_parity = (fecinfo->proactive_packets > 0); + sock->use_ondemand_parity = fecinfo->ondemand_parity_enabled; + sock->use_var_pktlen = fecinfo->var_pktlen_enabled; + sock->rs_n = fecinfo->block_size; + sock->rs_k = fecinfo->group_size; + sock->rs_proactive_h = fecinfo->proactive_packets; + } + status = TRUE; + break; + +/* congestion reporting */ + case PGM_USE_CR: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + if (PGM_UNLIKELY(*(const int*)optval <= 0)) + break; + sock->crqst_ivl = *(const int*)optval; + sock->use_cr = (sock->crqst_ivl > 0); + status = TRUE; + break; + +/* congestion control */ + case PGM_USE_PGMCC: + if (PGM_UNLIKELY(optlen != sizeof (struct pgm_pgmccinfo_t))) + break; + { + const struct pgm_pgmccinfo_t* pgmccinfo = optval; + sock->ack_bo_ivl = pgmccinfo->ack_bo_ivl; + sock->ack_c = pgmccinfo->ack_c; + sock->ack_c_p = pgmccinfo->ack_c_p; + sock->use_pgmcc = (sock->ack_c > 0); + } + status = TRUE; + break; + +/* declare socket only for sending, discard any incoming SPM, ODATA, + * RDATA, etc, packets. + */ + case PGM_SEND_ONLY: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + sock->can_recv_data = (0 == *(const int*)optval); + status = TRUE; + break; + +/* declare socket only for receiving, no transmit window will be created + * and no SPM broadcasts sent. + */ + case PGM_RECV_ONLY: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + sock->can_send_data = (0 == *(const int*)optval); + status = TRUE; + break; + +/* passive receiving socket, i.e. no back channel to source + */ + case PGM_PASSIVE: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + sock->can_send_nak = (0 == *(const int*)optval); + status = TRUE; + break; + +/* on unrecoverable data loss stop socket from further transmission and + * receiving. + */ + case PGM_ABORT_ON_RESET: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + sock->is_abort_on_reset = (0 != *(const int*)optval); + status = TRUE; + break; + +/* default non-blocking operation on send and receive sockets. + */ + case PGM_NOBLOCK: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + sock->is_nonblocking = (0 != *(const int*)optval); + pgm_sockaddr_nonblocking (sock->recv_sock, sock->is_nonblocking); + pgm_sockaddr_nonblocking (sock->send_sock, sock->is_nonblocking); + pgm_sockaddr_nonblocking (sock->send_with_router_alert_sock, sock->is_nonblocking); + status = TRUE; + break; + +/* sending group, singular. note that the address is only stored and used + * later in sendto() calls, this routine only considers the interface. + */ + case PGM_SEND_GROUP: + if (PGM_UNLIKELY(optlen != sizeof(struct group_req))) + break; + memcpy (&sock->send_gsr, optval, sizeof(struct group_req)); + if (PGM_UNLIKELY(sock->family != sock->send_gsr.gsr_group.ss_family)) + break; +/* multicast group for later usage with sendto() */ + if (sock->udp_encap_mcast_port) + ((struct sockaddr_in*)&sock->send_gsr.gsr_group)->sin_port = htons (sock->udp_encap_mcast_port); +/* interface */ + if ((PGM_SOCKET_ERROR == pgm_sockaddr_multicast_if (sock->send_sock, + (const struct sockaddr*)&sock->send_addr, + sock->send_gsr.gsr_interface)) || + (PGM_SOCKET_ERROR == pgm_sockaddr_multicast_if (sock->send_with_router_alert_sock, + (const struct sockaddr*)&sock->send_addr, + sock->send_gsr.gsr_interface))) + { + break; + } + else if (PGM_UNLIKELY(pgm_log_mask & PGM_LOG_ROLE_NETWORK)) + { + char addr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((const struct sockaddr*)&sock->send_addr, addr, sizeof(addr)); + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Multicast send interface set to %s index %u"), + addr, + (unsigned)sock->send_gsr.gsr_interface); + } + status = TRUE; + break; + +/* for any-source applications (ASM), join a new group + */ + case PGM_JOIN_GROUP: + if (PGM_UNLIKELY(optlen != sizeof(struct group_req))) + break; + if (PGM_UNLIKELY(sock->recv_gsr_len >= IP_MAX_MEMBERSHIPS)) + break; + { + const struct group_req* gr = optval; +/* verify not duplicate group/interface pairing */ + for (unsigned i = 0; i < sock->recv_gsr_len; i++) + { + if (pgm_sockaddr_cmp ((const struct sockaddr*)&gr->gr_group, (struct sockaddr*)&sock->recv_gsr[i].gsr_group) == 0 && + pgm_sockaddr_cmp ((const struct sockaddr*)&gr->gr_group, (struct sockaddr*)&sock->recv_gsr[i].gsr_source) == 0 && + (gr->gr_interface == sock->recv_gsr[i].gsr_interface || + 0 == sock->recv_gsr[i].gsr_interface )) + { +#ifdef SOCKET_DEBUG + char s[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((const struct sockaddr*)&gr->gr_group, s, sizeof(s)); + if (sock->recv_gsr[i].gsr_interface) { + pgm_warn(_("Socket has already joined group %s on interface %u"), s, gr->gr_interface); + } else { + pgm_warn(_("Socket has already joined group %s on all interfaces."), s); + } +#endif + break; + } + } + if (PGM_UNLIKELY(sock->family != gr->gr_group.ss_family)) + break; + sock->recv_gsr[sock->recv_gsr_len].gsr_interface = gr->gr_interface; + memcpy (&sock->recv_gsr[sock->recv_gsr_len].gsr_group, &gr->gr_group, pgm_sockaddr_len ((const struct sockaddr*)&gr->gr_group)); + if (sock->udp_encap_mcast_port) + ((struct sockaddr_in*)&sock->recv_gsr[sock->recv_gsr_len].gsr_group)->sin_port = htons (sock->udp_encap_mcast_port); + memcpy (&sock->recv_gsr[sock->recv_gsr_len].gsr_source, &gr->gr_group, pgm_sockaddr_len ((const struct sockaddr*)&gr->gr_group)); + if (PGM_SOCKET_ERROR == pgm_sockaddr_join_group (sock->recv_sock, sock->family, gr)) + break; + else if (PGM_UNLIKELY(pgm_log_mask & PGM_LOG_ROLE_NETWORK)) + { + char addr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((const struct sockaddr*)&gr->gr_group, addr, sizeof(addr)); + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Join multicast group %s on interface index %u"), + addr, + (unsigned)gr->gr_interface); + } + sock->recv_gsr_len++; + } + status = TRUE; + break; + +/* for any-source applications (ASM), leave a joined group. + */ + case PGM_LEAVE_GROUP: + if (PGM_UNLIKELY(optlen != sizeof(struct group_req))) + break; + if (PGM_UNLIKELY(0 == sock->recv_gsr_len)) + break; + { + const struct group_req* gr = optval; + for (unsigned i = 0; i < sock->recv_gsr_len;) + { + if ((pgm_sockaddr_cmp ((const struct sockaddr*)&gr->gr_group, (struct sockaddr*)&sock->recv_gsr[i].gsr_group) == 0) && +/* drop all matching receiver entries */ + (gr->gr_interface == 0 || +/* drop all sources with matching interface */ + gr->gr_interface == sock->recv_gsr[i].gsr_interface) ) + { + sock->recv_gsr_len--; + if (i < (IP_MAX_MEMBERSHIPS - 1)) + { + memmove (&sock->recv_gsr[i], &sock->recv_gsr[i+1], (sock->recv_gsr_len - i) * sizeof(struct group_source_req)); + continue; + } + } + i++; + } + if (PGM_UNLIKELY(sock->family != gr->gr_group.ss_family)) + break; + if (PGM_SOCKET_ERROR == pgm_sockaddr_leave_group (sock->recv_sock, sock->family, gr)) + break; + else if (PGM_UNLIKELY(pgm_log_mask & PGM_LOG_ROLE_NETWORK)) + { + char addr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((const struct sockaddr*)&gr->gr_group, addr, sizeof(addr)); + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Leave multicast group %s on interface index %u"), + addr, + (unsigned)gr->gr_interface); + } + } + status = TRUE; + break; + +/* for any-source applications (ASM), turn off a given source + */ + case PGM_BLOCK_SOURCE: + if (PGM_UNLIKELY(optlen != sizeof(struct group_source_req))) + break; + { + const struct group_source_req* gsr = optval; + if (PGM_UNLIKELY(sock->family != gsr->gsr_group.ss_family)) + break; + if (PGM_SOCKET_ERROR == pgm_sockaddr_block_source (sock->recv_sock, sock->family, gsr)) + break; + } + status = TRUE; + break; + +/* for any-source applications (ASM), re-allow a blocked source + */ + case PGM_UNBLOCK_SOURCE: + if (PGM_UNLIKELY(optlen != sizeof(struct group_source_req))) + break; + { + const struct group_source_req* gsr = optval; + if (PGM_UNLIKELY(sock->family != gsr->gsr_group.ss_family)) + break; + if (PGM_SOCKET_ERROR == pgm_sockaddr_unblock_source (sock->recv_sock, sock->family, gsr)) + break; + } + status = TRUE; + break; + +/* for controlled-source applications (SSM), join each group/source pair. + * + * SSM joins are allowed on top of ASM in order to merge a remote source onto the local segment. + */ + case PGM_JOIN_SOURCE_GROUP: + if (PGM_UNLIKELY(optlen != sizeof(struct group_source_req))) + break; + if (PGM_UNLIKELY(sock->recv_gsr_len >= IP_MAX_MEMBERSHIPS)) + break; + { + const struct group_source_req* gsr = optval; +/* verify if existing group/interface pairing */ + for (unsigned i = 0; i < sock->recv_gsr_len; i++) + { + if (pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_group, (struct sockaddr*)&sock->recv_gsr[i].gsr_group) == 0 && + (gsr->gsr_interface == sock->recv_gsr[i].gsr_interface || + 0 == sock->recv_gsr[i].gsr_interface )) + { + if (pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_source, (struct sockaddr*)&sock->recv_gsr[i].gsr_source) == 0) + { +#ifdef SOCKET_DEBUG + char s1[INET6_ADDRSTRLEN], s2[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((const struct sockaddr*)&gsr->gsr_group, s1, sizeof(s1)); + pgm_sockaddr_ntop ((const struct sockaddr*)&gsr->gsr_source, s2, sizeof(s2)); + if (sock->recv_gsr[i].gsr_interface) { + pgm_warn(_("Socket has already joined group %s from source %s on interface %d"), + s1, s2, (unsigned)gsr->gsr_interface); + } else { + pgm_warn(_("Socket has already joined group %s from source %s on all interfaces"), + s1, s2); + } +#endif + break; + } + break; + } + } + if (PGM_UNLIKELY(sock->family != gsr->gsr_group.ss_family)) + break; + if (PGM_UNLIKELY(sock->family != gsr->gsr_source.ss_family)) + break; + if (PGM_SOCKET_ERROR == pgm_sockaddr_join_source_group (sock->recv_sock, sock->family, gsr)) + break; + memcpy (&sock->recv_gsr[sock->recv_gsr_len], gsr, sizeof(struct group_source_req)); + sock->recv_gsr_len++; + } + status = TRUE; + break; + +/* for controlled-source applications (SSM), leave each group/source pair + */ + case PGM_LEAVE_SOURCE_GROUP: + if (PGM_UNLIKELY(optlen != sizeof(struct group_source_req))) + break; + if (PGM_UNLIKELY(0 == sock->recv_gsr_len)) + break; + { + const struct group_source_req* gsr = optval; +/* verify if existing group/interface pairing */ + for (unsigned i = 0; i < sock->recv_gsr_len; i++) + { + if (pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_group, (struct sockaddr*)&sock->recv_gsr[i].gsr_group) == 0 && + pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_source, (struct sockaddr*)&sock->recv_gsr[i].gsr_source) == 0 && + gsr->gsr_interface == sock->recv_gsr[i].gsr_interface) + { + sock->recv_gsr_len--; + if (i < (IP_MAX_MEMBERSHIPS - 1)) + { + memmove (&sock->recv_gsr[i], &sock->recv_gsr[i+1], (sock->recv_gsr_len - i) * sizeof(struct group_source_req)); + break; + } + } + } + if (PGM_UNLIKELY(sock->family != gsr->gsr_group.ss_family)) + break; + if (PGM_UNLIKELY(sock->family != gsr->gsr_source.ss_family)) + break; + if (PGM_SOCKET_ERROR == pgm_sockaddr_leave_source_group (sock->recv_sock, sock->family, gsr)) + break; + } + status = TRUE; + break; + +/* batch block and unblock sources */ + case PGM_MSFILTER: +#if defined(MCAST_MSFILTER) || defined(SIOCSMSFILTER) + if (PGM_UNLIKELY(optlen < (socklen_t)sizeof(struct group_filter))) + break; + { + const struct group_filter* gf_list = optval; + if ((socklen_t)GROUP_FILTER_SIZE( gf_list->gf_numsrc ) != optlen) + break; + if (PGM_UNLIKELY(sock->family != gf_list->gf_group.ss_family)) + break; +/* check only first */ + if (PGM_UNLIKELY(sock->family != gf_list->gf_slist[0].ss_family)) + break; + if (PGM_SOCKET_ERROR == pgm_sockaddr_msfilter (sock->recv_sock, sock->family, gf_list)) + break; + } + status = TRUE; +#endif + break; + +/* UDP encapsulation ports */ + case PGM_UDP_ENCAP_UCAST_PORT: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + sock->udp_encap_ucast_port = *(const int*)optval; + status = TRUE; + break; + + case PGM_UDP_ENCAP_MCAST_PORT: + if (PGM_UNLIKELY(optlen != sizeof (int))) + break; + sock->udp_encap_mcast_port = *(const int*)optval; + status = TRUE; + break; + + default: + break; + } + break; + + default: + break; + } + + pgm_rwlock_reader_unlock (&sock->lock); + return status; +} + +bool +pgm_bind ( + pgm_sock_t* restrict sock, + const struct pgm_sockaddr_t*const restrict sockaddr, + const socklen_t sockaddrlen, + pgm_error_t** restrict error + ) +{ + struct pgm_interface_req_t null_req; + memset (&null_req, 0, sizeof(null_req)); + return pgm_bind3 (sock, sockaddr, sockaddrlen, &null_req, sizeof(null_req), &null_req, sizeof(null_req), error); +} + +/* bind the sockets to the link layer to start receiving data. + * + * returns TRUE on success, or FALSE on error and sets error appropriately, + */ + +bool +pgm_bind3 ( + pgm_sock_t* restrict sock, + const struct pgm_sockaddr_t*const restrict sockaddr, + const socklen_t sockaddrlen, + const struct pgm_interface_req_t*const send_req, /* only use gr_interface and gr_group::sin6_scope */ + const socklen_t send_req_len, + const struct pgm_interface_req_t*const recv_req, + const socklen_t recv_req_len, + pgm_error_t** restrict error /* maybe NULL */ + ) +{ + pgm_return_val_if_fail (NULL != sock, FALSE); + pgm_return_val_if_fail (NULL != sockaddr, FALSE); + pgm_return_val_if_fail (0 != sockaddrlen, FALSE); + if (sockaddr->sa_addr.sport) pgm_return_val_if_fail (sockaddr->sa_addr.sport != sockaddr->sa_port, FALSE); + pgm_return_val_if_fail (NULL != send_req, FALSE); + pgm_return_val_if_fail (sizeof(struct pgm_interface_req_t) == send_req_len, FALSE); + pgm_return_val_if_fail (NULL != recv_req, FALSE); + pgm_return_val_if_fail (sizeof(struct pgm_interface_req_t) == recv_req_len, FALSE); + + if (!pgm_rwlock_writer_trylock (&sock->lock)) + pgm_return_val_if_reached (FALSE); + if (sock->is_bound || + sock->is_destroyed) + { + pgm_rwlock_writer_unlock (&sock->lock); + pgm_return_val_if_reached (FALSE); + } + +/* sanity checks on state */ + if (sock->max_tpdu < (sizeof(struct pgm_ip) + sizeof(struct pgm_header))) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("Invalid maximum TPDU size.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (sock->can_send_data) { + if (PGM_UNLIKELY(0 == sock->spm_ambient_interval)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("SPM ambient interval not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (PGM_UNLIKELY(0 == sock->spm_heartbeat_len)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("SPM heartbeat interval not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (PGM_UNLIKELY(0 == sock->txw_sqns && 0 == sock->txw_secs)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("TXW_SQNS not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (PGM_UNLIKELY(0 == sock->txw_sqns && 0 == sock->txw_max_rte)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("TXW_MAX_RTE not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + } + if (sock->can_recv_data) { + if (PGM_UNLIKELY(0 == sock->rxw_sqns && 0 == sock->rxw_secs)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("RXW_SQNS not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (PGM_UNLIKELY(0 == sock->rxw_sqns && 0 == sock->rxw_max_rte)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("RXW_MAX_RTE not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (PGM_UNLIKELY(0 == sock->peer_expiry)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("Peer timeout not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (PGM_UNLIKELY(0 == sock->spmr_expiry)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("SPM-Request timeout not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (PGM_UNLIKELY(0 == sock->nak_bo_ivl)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("NAK_BO_IVL not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (PGM_UNLIKELY(0 == sock->nak_rpt_ivl)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("NAK_RPT_IVL not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (PGM_UNLIKELY(0 == sock->nak_rdata_ivl)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("NAK_RDATA_IVL not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (PGM_UNLIKELY(0 == sock->nak_data_retries)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("NAK_DATA_RETRIES not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (PGM_UNLIKELY(0 == sock->nak_ncf_retries)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + PGM_ERROR_FAILED, + _("NAK_NCF_RETRIES not configured.")); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + } + + pgm_debug ("bind3 (sock:%p sockaddr:%p sockaddrlen:%u send-req:%p send-req-len:%u recv-req:%p recv-req-len:%u error:%p)", + (const void*)sock, (const void*)sockaddr, (unsigned)sockaddrlen, (const void*)send_req, (unsigned)send_req_len, (const void*)recv_req, (unsigned)recv_req_len, (const void*)error); + + memcpy (&sock->tsi, &sockaddr->sa_addr, sizeof(pgm_tsi_t)); + sock->dport = htons (sockaddr->sa_port); + if (sock->tsi.sport) { + sock->tsi.sport = htons (sock->tsi.sport); + } else { + do { + sock->tsi.sport = htons (pgm_random_int_range (0, UINT16_MAX)); + } while (sock->tsi.sport == sock->dport); + } + +/* pseudo-random number generator for back-off intervals */ + pgm_rand_create (&sock->rand_); + +/* PGM Children support of POLLs requires 32-bit random node identifier RAND_NODE_ID */ + if (sock->can_recv_data) { + sock->rand_node_id = pgm_rand_int (&sock->rand_); + } + + if (sock->can_send_data) + { +/* Windows notify call will raise an assertion on error, only Unix versions will return + * a valid error. + */ + if (sock->use_pgmcc && + 0 != pgm_notify_init (&sock->ack_notify)) + { + const int save_errno = errno; + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_errno (save_errno), + _("Creating ACK notification channel: %s"), + strerror (save_errno)); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (0 != pgm_notify_init (&sock->rdata_notify)) + { + const int save_errno = errno; + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_errno (save_errno), + _("Creating RDATA notification channel: %s"), + strerror (save_errno)); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + } + if (0 != pgm_notify_init (&sock->pending_notify)) + { + const int save_errno = errno; + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_errno (save_errno), + _("Creating waiting peer notification channel: %s"), + strerror (save_errno)); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + +/* determine IP header size for rate regulation engine & stats */ + sock->iphdr_len = (AF_INET == sock->family) ? sizeof(struct pgm_ip) : sizeof(struct pgm_ip6_hdr); + pgm_trace (PGM_LOG_ROLE_NETWORK,"Assuming IP header size of %zu bytes", sock->iphdr_len); + + if (sock->udp_encap_ucast_port) { + const size_t udphdr_len = sizeof(struct pgm_udphdr); + pgm_trace (PGM_LOG_ROLE_NETWORK,"Assuming UDP header size of %zu bytes", udphdr_len); + sock->iphdr_len += udphdr_len; + } + + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + sock->max_tsdu = sock->max_tpdu - sock->iphdr_len - pgm_pkt_offset (FALSE, pgmcc_family); + sock->max_tsdu_fragment = sock->max_tpdu - sock->iphdr_len - pgm_pkt_offset (TRUE, pgmcc_family); + const unsigned max_fragments = sock->txw_sqns ? MIN( PGM_MAX_FRAGMENTS, sock->txw_sqns ) : PGM_MAX_FRAGMENTS; + sock->max_apdu = MIN( PGM_MAX_APDU, max_fragments * sock->max_tsdu_fragment ); + + if (sock->can_send_data) + { + pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Create transmit window.")); + sock->window = sock->txw_sqns ? + pgm_txw_create (&sock->tsi, + 0, /* MAX_TPDU */ + sock->txw_sqns, /* TXW_SQNS */ + 0, /* TXW_SECS */ + 0, /* TXW_MAX_RTE */ + sock->use_ondemand_parity || sock->use_proactive_parity, + sock->rs_n, + sock->rs_k) : + pgm_txw_create (&sock->tsi, + sock->max_tpdu, /* MAX_TPDU */ + 0, /* TXW_SQNS */ + sock->txw_secs, /* TXW_SECS */ + sock->txw_max_rte, /* TXW_MAX_RTE */ + sock->use_ondemand_parity || sock->use_proactive_parity, + sock->rs_n, + sock->rs_k); + pgm_assert (NULL != sock->window); + } + +/* create peer list */ + if (sock->can_recv_data) { + sock->peers_hashtable = pgm_hashtable_new (pgm_tsi_hash, pgm_tsi_equal); + pgm_assert (NULL != sock->peers_hashtable); + } + +/* Bind UDP sockets to interfaces, note multicast on a bound interface is + * fruity on some platforms. Roughly, binding to INADDR_ANY provides all + * data, binding to the multicast group provides only multicast traffic, + * and binding to the interface address provides only unicast traffic. + * + * Multicast routing, IGMP & MLD require a link local address, for IPv4 + * this is provided through MULTICAST_IF and IPv6 through bind, and these + * may be overridden by per packet scopes. + * + * After binding, default interfaces (0.0.0.0) are resolved. + */ +/* TODO: different ports requires a new bound socket */ + + union { + struct sockaddr sa; + struct sockaddr_in s4; + struct sockaddr_in6 s6; + struct sockaddr_storage ss; + } recv_addr, recv_addr2, send_addr, send_with_router_alert_addr; + +#ifdef CONFIG_BIND_INADDR_ANY +/* force default interface for bind-only, source address is still valid for multicast membership. + * effectively same as running getaddrinfo(hints = {ai_flags = AI_PASSIVE}) + */ + if (AF_INET == sock->family) { + memset (&recv_addr.s4, 0, sizeof(struct sockaddr_in)); + recv_addr.s4.sin_family = AF_INET; + recv_addr.s4.sin_addr.s_addr = INADDR_ANY; + } else { + memset (&recv_addr.s6, 0, sizeof(struct sockaddr_in6)); + recv_addr.s6.sin6_family = AF_INET6; + recv_addr.s6.sin6_addr = in6addr_any; + } + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Binding receive socket to INADDR_ANY")); +#else + if (!pgm_if_indextoaddr (recv_req->ir_interface, + sock->family, + recv_req->ir_scope_id, + &recv_addr.sa, + error)) + { + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + else if (PGM_UNLIKELY(pgm_log_mask & PGM_LOG_ROLE_NETWORK)) + { + if (AF_INET6 == sock_family) + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Binding receive socket to interface index %u scope %u"), + recv_req->ir_interface, + recv_req->ir_scope_id); + else + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Binding receive socket to interface index %u"), + recv_req->ir_interface); + } + +#endif /* CONFIG_BIND_INADDR_ANY */ + + memcpy (&recv_addr2.sa, &recv_addr.sa, pgm_sockaddr_len (&recv_addr.sa)); + +/* UDP port */ + ((struct sockaddr_in*)&recv_addr)->sin_port = htons (sock->udp_encap_mcast_port); + + if (PGM_SOCKET_ERROR == bind (sock->recv_sock, + &recv_addr.sa, + pgm_sockaddr_len (&recv_addr.sa))) + { + char addr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&recv_addr, addr, sizeof(addr)); + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + _("Binding receive socket to address %s: %s"), + addr, + pgm_sock_strerror (save_errno)); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + + if (PGM_UNLIKELY(pgm_log_mask & PGM_LOG_ROLE_NETWORK)) + { + char s[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&recv_addr, s, sizeof(s)); + pgm_debug ("bind succeeded on recv_gsr[0] interface %s", s); + } + +/* keep a copy of the original address source to re-use for router alert bind */ + memset (&send_addr, 0, sizeof(send_addr)); + + if (!pgm_if_indextoaddr (send_req->ir_interface, + sock->family, + send_req->ir_scope_id, + (struct sockaddr*)&send_addr, + error)) + { + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + else if (PGM_UNLIKELY(pgm_log_mask & PGM_LOG_ROLE_NETWORK)) + { + if (AF_INET6 == sock->family) + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Binding send socket to interface index %u scope %u"), + send_req->ir_interface, + send_req->ir_scope_id); + else + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Binding send socket to interface index %u"), + send_req->ir_interface); + } + + memcpy (&send_with_router_alert_addr, &send_addr, pgm_sockaddr_len ((struct sockaddr*)&send_addr)); + if (PGM_SOCKET_ERROR == bind (sock->send_sock, + (struct sockaddr*)&send_addr, + pgm_sockaddr_len ((struct sockaddr*)&send_addr))) + { + char addr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&send_addr, addr, sizeof(addr)); + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + _("Binding send socket to address %s: %s"), + addr, + pgm_sock_strerror (save_errno)); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + +/* resolve bound address if 0.0.0.0 */ + if (AF_INET == send_addr.ss.ss_family) + { + if ((INADDR_ANY == ((struct sockaddr_in*)&send_addr)->sin_addr.s_addr) && + !pgm_if_getnodeaddr (AF_INET, (struct sockaddr*)&send_addr, sizeof(send_addr), error)) + { + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + } + else if ((memcmp (&in6addr_any, &((struct sockaddr_in6*)&send_addr)->sin6_addr, sizeof(in6addr_any)) == 0) && + !pgm_if_getnodeaddr (AF_INET6, (struct sockaddr*)&send_addr, sizeof(send_addr), error)) + { + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + + if (PGM_UNLIKELY(pgm_log_mask & PGM_LOG_ROLE_NETWORK)) + { + char s[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&send_addr, s, sizeof(s)); + pgm_debug ("bind succeeded on send_gsr interface %s", s); + } + + if (PGM_SOCKET_ERROR == bind (sock->send_with_router_alert_sock, + (struct sockaddr*)&send_with_router_alert_addr, + pgm_sockaddr_len((struct sockaddr*)&send_with_router_alert_addr))) + { + char addr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&send_with_router_alert_addr, addr, sizeof(addr)); + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + _("Binding IP Router Alert (RFC 2113) send socket to address %s: %s"), + addr, + pgm_sock_strerror (save_errno)); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + + if (PGM_UNLIKELY(pgm_log_mask & PGM_LOG_ROLE_NETWORK)) + { + char s[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&send_with_router_alert_addr, s, sizeof(s)); + pgm_debug ("bind (router alert) succeeded on send_gsr interface %s", s); + } + +/* save send side address for broadcasting as source nla */ + memcpy (&sock->send_addr, &send_addr, pgm_sockaddr_len ((struct sockaddr*)&send_addr)); + +/* rx to nak processor notify channel */ + if (sock->can_send_data) + { +/* setup rate control */ + if (sock->txw_max_rte) + { + pgm_trace (PGM_LOG_ROLE_RATE_CONTROL,_("Setting rate regulation to %zd bytes per second."), + sock->txw_max_rte); + + pgm_rate_create (&sock->rate_control, sock->txw_max_rte, sock->iphdr_len, sock->max_tpdu); + sock->is_controlled_spm = TRUE; /* must always be set */ + sock->is_controlled_odata = TRUE; + sock->is_controlled_rdata = TRUE; + } + else + { + sock->is_controlled_spm = FALSE; + sock->is_controlled_odata = FALSE; + sock->is_controlled_rdata = FALSE; + } + } + +/* allocate first incoming packet buffer */ + sock->rx_buffer = pgm_alloc_skb (sock->max_tpdu); + +/* bind complete */ + sock->is_bound = TRUE; + +/* cleanup */ + pgm_rwlock_writer_unlock (&sock->lock); + pgm_debug ("PGM socket successfully bound."); + return TRUE; +} + +bool +pgm_connect ( + pgm_sock_t* restrict sock, + pgm_error_t** restrict error /* maybe NULL */ + ) +{ + pgm_return_val_if_fail (sock != NULL, FALSE); + pgm_return_val_if_fail (sock->recv_gsr_len > 0, FALSE); +#ifdef CONFIG_TARGET_WINE + pgm_return_val_if_fail (sock->recv_gsr_len == 1, FALSE); +#endif + for (unsigned i = 0; i < sock->recv_gsr_len; i++) + { + pgm_return_val_if_fail (sock->recv_gsr[i].gsr_group.ss_family == sock->recv_gsr[0].gsr_group.ss_family, FALSE); + pgm_return_val_if_fail (sock->recv_gsr[i].gsr_group.ss_family == sock->recv_gsr[i].gsr_source.ss_family, FALSE); + } + pgm_return_val_if_fail (sock->send_gsr.gsr_group.ss_family == sock->recv_gsr[0].gsr_group.ss_family, FALSE); +/* shutdown */ + if (PGM_UNLIKELY(!pgm_rwlock_writer_trylock (&sock->lock))) + pgm_return_val_if_reached (FALSE); +/* state */ + if (PGM_UNLIKELY(sock->is_connected || !sock->is_bound || sock->is_destroyed)) { + pgm_rwlock_writer_unlock (&sock->lock); + pgm_return_val_if_reached (FALSE); + } + + pgm_debug ("connect (sock:%p error:%p)", + (const void*)sock, (const void*)error); + +/* rx to nak processor notify channel */ + if (sock->can_send_data) + { +/* announce new sock by sending out SPMs */ + if (!pgm_send_spm (sock, PGM_OPT_SYN) || + !pgm_send_spm (sock, PGM_OPT_SYN) || + !pgm_send_spm (sock, PGM_OPT_SYN)) + { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + _("Sending SPM broadcast: %s"), + pgm_sock_strerror (save_errno)); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + + sock->next_poll = sock->next_ambient_spm = pgm_time_update_now() + sock->spm_ambient_interval; + +/* start PGMCC with one token */ + sock->tokens = sock->cwnd_size = pgm_fp8 (1); + +/* slow start threshold */ + sock->ssthresh = pgm_fp8 (4); + +/* ACK timeout, should be greater than first SPM heartbeat interval in order to be scheduled correctly */ + sock->ack_expiry_ivl = pgm_secs (3); + +/* start full history */ + sock->ack_bitmap = 0xffffffff; + } + else + { + pgm_assert (sock->can_recv_data); + sock->next_poll = pgm_time_update_now() + pgm_secs( 30 ); + } + + sock->is_connected = TRUE; + +/* cleanup */ + pgm_rwlock_writer_unlock (&sock->lock); + pgm_debug ("PGM socket successfully connected."); + return TRUE; +} + +/* return local endpoint address + */ + +bool +pgm_getsockname ( + pgm_sock_t* const restrict sock, + struct pgm_sockaddr_t* restrict addr, + socklen_t* restrict addrlen + ) +{ + pgm_assert (NULL != sock); + pgm_assert (NULL != addr); + pgm_assert (NULL != addrlen); + pgm_assert (sizeof(struct pgm_sockaddr_t) == *addrlen); + + if (!sock->is_bound) { + errno = EBADF; + return FALSE; + } + + addr->sa_port = sock->dport; + memcpy (&addr->sa_addr, &sock->tsi, sizeof(pgm_tsi_t)); + return TRUE; +} + +/* add select parameters for the receive socket(s) + * + * returns highest file descriptor used plus one. + */ + +int +pgm_select_info ( + pgm_sock_t* const restrict sock, + fd_set* const restrict readfds, /* blocking recv fds */ + fd_set* const restrict writefds, /* blocking send fds */ + int* const restrict n_fds /* in: max fds, out: max (in:fds, sock:fds) */ + ) +{ + int fds = 0; + + pgm_assert (NULL != sock); + pgm_assert (NULL != n_fds); + + if (!sock->is_bound || sock->is_destroyed) + { + errno = EBADF; + return -1; + } + + const bool is_congested = (sock->use_pgmcc && sock->tokens < pgm_fp8 (1)) ? TRUE : FALSE; + + if (readfds) + { + FD_SET(sock->recv_sock, readfds); + fds = sock->recv_sock + 1; + if (sock->can_send_data) { + const int rdata_fd = pgm_notify_get_fd (&sock->rdata_notify); + FD_SET(rdata_fd, readfds); + fds = MAX(fds, rdata_fd + 1); + if (is_congested) { + const int ack_fd = pgm_notify_get_fd (&sock->ack_notify); + FD_SET(ack_fd, readfds); + fds = MAX(fds, ack_fd + 1); + } + } + const int pending_fd = pgm_notify_get_fd (&sock->pending_notify); + FD_SET(pending_fd, readfds); + fds = MAX(fds, pending_fd + 1); + } + + if (sock->can_send_data && writefds && !is_congested) + { + FD_SET(sock->send_sock, writefds); + fds = MAX(sock->send_sock + 1, fds); + } + + return *n_fds = MAX(fds, *n_fds); +} + +#ifdef CONFIG_HAVE_POLL +/* add poll parameters for the receive socket(s) + * + * returns number of pollfd structures filled. + */ + +int +pgm_poll_info ( + pgm_sock_t* const restrict sock, + struct pollfd* const restrict fds, + int* const restrict n_fds, /* in: #fds, out: used #fds */ + const int events /* POLLIN, POLLOUT */ + ) +{ + pgm_assert (NULL != sock); + pgm_assert (NULL != fds); + pgm_assert (NULL != n_fds); + + if (!sock->is_bound || sock->is_destroyed) + { + errno = EBADF; + return -1; + } + + int moo = 0; + +/* we currently only support one incoming socket */ + if (events & POLLIN) + { + pgm_assert ( (1 + moo) <= *n_fds ); + fds[moo].fd = sock->recv_sock; + fds[moo].events = POLLIN; + moo++; + if (sock->can_send_data) { + pgm_assert ( (1 + moo) <= *n_fds ); + fds[moo].fd = pgm_notify_get_fd (&sock->rdata_notify); + fds[moo].events = POLLIN; + moo++; + } + pgm_assert ( (1 + moo) <= *n_fds ); + fds[moo].fd = pgm_notify_get_fd (&sock->pending_notify); + fds[moo].events = POLLIN; + moo++; + } + +/* ODATA only published on regular socket, no need to poll router-alert sock */ + if (sock->can_send_data && events & POLLOUT) + { + pgm_assert ( (1 + moo) <= *n_fds ); + if (sock->use_pgmcc && sock->tokens < pgm_fp8 (1)) { +/* rx thread poll for ACK */ + fds[moo].fd = pgm_notify_get_fd (&sock->ack_notify); + fds[moo].events = POLLIN; + } else { +/* kernel resource poll */ + fds[moo].fd = sock->send_sock; + fds[moo].events = POLLOUT; + } + moo++; + } + + return *n_fds = moo; +} +#endif /* CONFIG_HAVE_POLL */ + +/* add epoll parameters for the recieve socket(s), events should + * be set to EPOLLIN to wait for incoming events (data), and EPOLLOUT to wait + * for non-blocking write. + * + * returns 0 on success, -1 on failure and sets errno appropriately. + */ +#ifdef CONFIG_HAVE_EPOLL +int +pgm_epoll_ctl ( + pgm_sock_t* const sock, + const int epfd, + const int op, /* EPOLL_CTL_ADD, ... */ + const int events /* EPOLLIN, EPOLLOUT */ + ) +{ + if (!(op == EPOLL_CTL_ADD || op == EPOLL_CTL_MOD)) + { + errno = EINVAL; + return -1; + } + else if (!sock->is_bound || sock->is_destroyed) + { + errno = EBADF; + return -1; + } + + struct epoll_event event; + int retval = 0; + + if (events & EPOLLIN) + { + event.events = events & (EPOLLIN | EPOLLET | EPOLLONESHOT); + event.data.ptr = sock; + retval = epoll_ctl (epfd, op, sock->recv_sock, &event); + if (retval) + goto out; + if (sock->can_send_data) { + retval = epoll_ctl (epfd, op, pgm_notify_get_fd (&sock->rdata_notify), &event); + if (retval) + goto out; + } + retval = epoll_ctl (epfd, op, pgm_notify_get_fd (&sock->pending_notify), &event); + if (retval) + goto out; + + if (events & EPOLLET) + sock->is_edge_triggered_recv = TRUE; + } + + if (sock->can_send_data && events & EPOLLOUT) + { + bool enable_ack_socket = FALSE; + bool enable_send_socket = FALSE; + +/* both sockets need to be added when PGMCC is enabled */ + if (sock->use_pgmcc && EPOLL_CTL_ADD == op) { + enable_ack_socket = enable_send_socket = TRUE; + } else { +/* automagically switch socket when congestion stall occurs */ + if (sock->use_pgmcc && sock->tokens < pgm_fp8 (1)) + enable_ack_socket = TRUE; + else + enable_send_socket = TRUE; + } + + if (enable_ack_socket) + { +/* rx thread poll for ACK */ + event.events = EPOLLIN | (events & (EPOLLONESHOT)); + event.data.ptr = sock; + retval = epoll_ctl (epfd, op, pgm_notify_get_fd (&sock->ack_notify), &event); + } + + if (enable_send_socket) + { +/* kernel resource poll */ + event.events = events & (EPOLLOUT | EPOLLET | EPOLLONESHOT); + event.data.ptr = sock; + retval = epoll_ctl (epfd, op, sock->send_sock, &event); + } + } +out: + return retval; +} +#endif + +static +const char* +pgm_family_string ( + const int family + ) +{ + const char* c; + + switch (family) { + case AF_UNSPEC: c = "AF_UNSPEC"; break; + case AF_INET: c = "AF_INET"; break; + case AF_INET6: c = "AF_INET6"; break; + default: c = "(unknown)"; break; + } + + return c; +} + +static +const char* +pgm_sock_type_string ( + const int sock_type + ) +{ + const char* c; + + switch (sock_type) { + case SOCK_SEQPACKET: c = "SOCK_SEQPACKET"; break; + default: c = "(unknown)"; break; + } + + return c; +} + +static +const char* +pgm_protocol_string ( + const int protocol + ) +{ + const char* c; + + switch (protocol) { + case IPPROTO_UDP: c = "IPPROTO_UDP"; break; + case IPPROTO_PGM: c = "IPPROTO_PGM"; break; + default: c = "(unknown)"; break; + } + + return c; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/socket.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/socket.c.c89.patch new file mode 100644 index 0000000..ee48d8f --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/socket.c.c89.patch @@ -0,0 +1,403 @@ +--- socket.c 2010-08-04 17:24:23.000000000 +0800 ++++ socket.c89 2010-08-05 11:24:10.000000000 +0800 +@@ -243,7 +243,9 @@ + new_sock->adv_mode = 0; /* advance with time */ + + /* PGMCC */ ++#pragma warning( disable : 4244 ) + new_sock->acker_nla.ss_family = family; ++#pragma warning( default : 4244 ) + + /* source-side */ + pgm_mutex_init (&new_sock->source_mutex); +@@ -261,6 +263,7 @@ + pgm_rwlock_init (&new_sock->lock); + + /* open sockets to implement PGM */ ++ { + int socket_type; + if (IPPROTO_UDP == new_sock->protocol) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Opening UDP encapsulated sockets.")); +@@ -351,6 +354,7 @@ + } + pgm_free (new_sock); + return FALSE; ++ } + } + + bool +@@ -432,8 +436,8 @@ + { + struct timeval* tv = optval; + const pgm_time_t usecs = pgm_timer_expiration (sock); +- tv->tv_sec = usecs / 1000000UL; +- tv->tv_usec = usecs % 1000000UL; ++ tv->tv_sec = (long)(usecs / 1000000UL); ++ tv->tv_usec = (long)(usecs % 1000000UL); + } + status = TRUE; + break; +@@ -447,8 +451,8 @@ + { + struct timeval* tv = optval; + const pgm_time_t usecs = pgm_rate_remaining (&sock->rate_control, sock->blocklen); +- tv->tv_sec = usecs / 1000000UL; +- tv->tv_usec = usecs % 1000000UL; ++ tv->tv_sec = (long)(usecs / 1000000UL); ++ tv->tv_usec = (long)(usecs % 1000000UL); + } + status = TRUE; + break; +@@ -599,8 +603,11 @@ + sock->spm_heartbeat_len = optlen / sizeof (int); + sock->spm_heartbeat_interval = pgm_new (unsigned, sock->spm_heartbeat_len + 1); + sock->spm_heartbeat_interval[0] = 0; +- for (unsigned i = 0; i < sock->spm_heartbeat_len; i++) ++ { ++ unsigned i; ++ for (i = 0; i < sock->spm_heartbeat_len; i++) + sock->spm_heartbeat_interval[i + 1] = ((const int*)optval)[i]; ++ } + } + status = TRUE; + break; +@@ -795,6 +802,7 @@ + break; + if (PGM_UNLIKELY(fecinfo->group_size > fecinfo->block_size)) + break; ++ { + const uint8_t parity_packets = fecinfo->block_size - fecinfo->group_size; + /* technically could re-send previous packets */ + if (PGM_UNLIKELY(fecinfo->proactive_packets > parity_packets)) +@@ -811,6 +819,7 @@ + sock->rs_n = fecinfo->block_size; + sock->rs_k = fecinfo->group_size; + sock->rs_proactive_h = fecinfo->proactive_packets; ++ } + } + status = TRUE; + break; +@@ -916,7 +925,9 @@ + { + const struct group_req* gr = optval; + /* verify not duplicate group/interface pairing */ +- for (unsigned i = 0; i < sock->recv_gsr_len; i++) ++ { ++ unsigned i; ++ for (i = 0; i < sock->recv_gsr_len; i++) + { + if (pgm_sockaddr_cmp ((const struct sockaddr*)&gr->gr_group, (struct sockaddr*)&sock->recv_gsr[i].gsr_group) == 0 && + pgm_sockaddr_cmp ((const struct sockaddr*)&gr->gr_group, (struct sockaddr*)&sock->recv_gsr[i].gsr_source) == 0 && +@@ -935,6 +946,7 @@ + break; + } + } ++ } + if (PGM_UNLIKELY(sock->family != gr->gr_group.ss_family)) + break; + if (PGM_SOCKET_ERROR == pgm_sockaddr_join_group (sock->recv_sock, sock->family, gr)) +@@ -956,7 +968,9 @@ + break; + { + const struct group_req* gr = optval; +- for (unsigned i = 0; i < sock->recv_gsr_len;) ++ { ++ unsigned i; ++ for (i = 0; i < sock->recv_gsr_len;) + { + if ((pgm_sockaddr_cmp ((const struct sockaddr*)&gr->gr_group, (struct sockaddr*)&sock->recv_gsr[i].gsr_group) == 0) && + /* drop all matching receiver entries */ +@@ -973,6 +987,7 @@ + } + i++; + } ++ } + if (PGM_UNLIKELY(sock->family != gr->gr_group.ss_family)) + break; + if (PGM_SOCKET_ERROR == pgm_sockaddr_leave_group (sock->recv_sock, sock->family, gr)) +@@ -1023,7 +1038,9 @@ + { + const struct group_source_req* gsr = optval; + /* verify if existing group/interface pairing */ +- for (unsigned i = 0; i < sock->recv_gsr_len; i++) ++ { ++ unsigned i; ++ for (i = 0; i < sock->recv_gsr_len; i++) + { + if (pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_group, (struct sockaddr*)&sock->recv_gsr[i].gsr_group) == 0 && + (gsr->gsr_interface == sock->recv_gsr[i].gsr_interface || +@@ -1048,6 +1065,7 @@ + break; + } + } ++ } + if (PGM_UNLIKELY(sock->family != gsr->gsr_group.ss_family)) + break; + if (PGM_UNLIKELY(sock->family != gsr->gsr_source.ss_family)) +@@ -1070,7 +1088,9 @@ + { + const struct group_source_req* gsr = optval; + /* verify if existing group/interface pairing */ +- for (unsigned i = 0; i < sock->recv_gsr_len; i++) ++ { ++ unsigned i; ++ for (i = 0; i < sock->recv_gsr_len; i++) + { + if (pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_group, (struct sockaddr*)&sock->recv_gsr[i].gsr_group) == 0 && + pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_source, (struct sockaddr*)&sock->recv_gsr[i].gsr_source) == 0 && +@@ -1084,6 +1104,7 @@ + } + } + } ++ } + if (PGM_UNLIKELY(sock->family != gsr->gsr_group.ss_family)) + break; + if (PGM_UNLIKELY(sock->family != gsr->gsr_source.ss_family)) +@@ -1336,52 +1357,78 @@ + if (sock->use_pgmcc && + 0 != pgm_notify_init (&sock->ack_notify)) + { ++#ifdef _MSC_VER ++ char buffer[1024]; ++#endif + const int save_errno = errno; + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_errno (save_errno), + _("Creating ACK notification channel: %s"), +- strerror (save_errno)); ++#ifdef _MSC_VER ++ strerror_s (buffer, sizeof(buffer), save_errno) ++#else ++ strerror (save_errno) ++#endif ++ ); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + if (0 != pgm_notify_init (&sock->rdata_notify)) + { ++#ifdef _MSC_VER ++ char buffer[1024]; ++#endif + const int save_errno = errno; + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_errno (save_errno), + _("Creating RDATA notification channel: %s"), +- strerror (save_errno)); ++#ifdef _MSC_VER ++ strerror_s (buffer, sizeof(buffer), save_errno) ++#else ++ strerror (save_errno) ++#endif ++ ); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + } + if (0 != pgm_notify_init (&sock->pending_notify)) + { ++#ifdef _MSC_VER ++ char buffer[1024]; ++#endif + const int save_errno = errno; + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_errno (save_errno), + _("Creating waiting peer notification channel: %s"), +- strerror (save_errno)); ++#ifdef _MSC_VER ++ strerror_s (buffer, sizeof(buffer), save_errno) ++#else ++ strerror (save_errno) ++#endif ++ ); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } + + /* determine IP header size for rate regulation engine & stats */ + sock->iphdr_len = (AF_INET == sock->family) ? sizeof(struct pgm_ip) : sizeof(struct pgm_ip6_hdr); +- pgm_trace (PGM_LOG_ROLE_NETWORK,"Assuming IP header size of %zu bytes", sock->iphdr_len); ++ pgm_trace (PGM_LOG_ROLE_NETWORK,"Assuming IP header size of %lu bytes", sock->iphdr_len); + + if (sock->udp_encap_ucast_port) { + const size_t udphdr_len = sizeof(struct pgm_udphdr); +- pgm_trace (PGM_LOG_ROLE_NETWORK,"Assuming UDP header size of %zu bytes", udphdr_len); ++ pgm_trace (PGM_LOG_ROLE_NETWORK,"Assuming UDP header size of %lu bytes", udphdr_len); + sock->iphdr_len += udphdr_len; + } + ++ { + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + sock->max_tsdu = sock->max_tpdu - sock->iphdr_len - pgm_pkt_offset (FALSE, pgmcc_family); + sock->max_tsdu_fragment = sock->max_tpdu - sock->iphdr_len - pgm_pkt_offset (TRUE, pgmcc_family); ++ { + const unsigned max_fragments = sock->txw_sqns ? MIN( PGM_MAX_FRAGMENTS, sock->txw_sqns ) : PGM_MAX_FRAGMENTS; + sock->max_apdu = MIN( PGM_MAX_APDU, max_fragments * sock->max_tsdu_fragment ); + +@@ -1419,6 +1466,7 @@ + /* Stevens: "SO_REUSEADDR has datatype int." + */ + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Set socket sharing.")); ++ { + const int v = 1; + if (PGM_SOCKET_ERROR == setsockopt (sock->recv_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&v, sizeof(v)) || + PGM_SOCKET_ERROR == setsockopt (sock->send_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&v, sizeof(v)) || +@@ -1433,10 +1481,12 @@ + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } ++ } + + /* request extra packet information to determine destination address on each packet */ + #ifndef CONFIG_TARGET_WINE + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Request socket packet-info.")); ++ { + const sa_family_t recv_family = sock->family; + if (PGM_SOCKET_ERROR == pgm_sockaddr_pktinfo (sock->recv_sock, recv_family, TRUE)) + { +@@ -1449,6 +1499,7 @@ + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; + } ++ } + #endif + } + else +@@ -1501,6 +1552,7 @@ + */ + /* TODO: different ports requires a new bound socket */ + ++ { + union { + struct sockaddr sa; + struct sockaddr_in s4; +@@ -1546,6 +1598,7 @@ + { + char addr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&recv_addr, addr, sizeof(addr)); ++ { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, +@@ -1555,6 +1608,7 @@ + pgm_sock_strerror (save_errno)); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; ++ } + } + + if (PGM_UNLIKELY(pgm_log_mask & PGM_LOG_ROLE_NETWORK)) +@@ -1590,6 +1644,7 @@ + { + char addr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&send_addr, addr, sizeof(addr)); ++ { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, +@@ -1599,6 +1654,7 @@ + pgm_sock_strerror (save_errno)); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; ++ } + } + + /* resolve bound address if 0.0.0.0 */ +@@ -1631,6 +1687,7 @@ + { + char addr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&send_with_router_alert_addr, addr, sizeof(addr)); ++ { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, +@@ -1640,6 +1697,7 @@ + pgm_sock_strerror (save_errno)); + pgm_rwlock_writer_unlock (&sock->lock); + return FALSE; ++ } + } + + if (PGM_UNLIKELY(pgm_log_mask & PGM_LOG_ROLE_NETWORK)) +@@ -1658,7 +1716,7 @@ + /* setup rate control */ + if (sock->txw_max_rte) + { +- pgm_trace (PGM_LOG_ROLE_RATE_CONTROL,_("Setting rate regulation to %zd bytes per second."), ++ pgm_trace (PGM_LOG_ROLE_RATE_CONTROL,_("Setting rate regulation to %ld bytes per second."), + sock->txw_max_rte); + + pgm_rate_create (&sock->rate_control, sock->txw_max_rte, sock->iphdr_len, sock->max_tpdu); +@@ -1684,6 +1742,9 @@ + pgm_rwlock_writer_unlock (&sock->lock); + pgm_debug ("PGM socket successfully bound."); + return TRUE; ++ } ++ } ++ } + } + + bool +@@ -1697,11 +1758,14 @@ + #ifdef CONFIG_TARGET_WINE + pgm_return_val_if_fail (sock->recv_gsr_len == 1, FALSE); + #endif +- for (unsigned i = 0; i < sock->recv_gsr_len; i++) ++ { ++ unsigned i; ++ for (i = 0; i < sock->recv_gsr_len; i++) + { + pgm_return_val_if_fail (sock->recv_gsr[i].gsr_group.ss_family == sock->recv_gsr[0].gsr_group.ss_family, FALSE); + pgm_return_val_if_fail (sock->recv_gsr[i].gsr_group.ss_family == sock->recv_gsr[i].gsr_source.ss_family, FALSE); + } ++ } + pgm_return_val_if_fail (sock->send_gsr.gsr_group.ss_family == sock->recv_gsr[0].gsr_group.ss_family, FALSE); + /* shutdown */ + if (PGM_UNLIKELY(!pgm_rwlock_writer_trylock (&sock->lock))) +@@ -1810,6 +1874,7 @@ + return -1; + } + ++ { + const bool is_congested = (sock->use_pgmcc && sock->tokens < pgm_fp8 (1)) ? TRUE : FALSE; + + if (readfds) +@@ -1826,9 +1891,11 @@ + fds = MAX(fds, ack_fd + 1); + } + } ++ { + const int pending_fd = pgm_notify_get_fd (&sock->pending_notify); + FD_SET(pending_fd, readfds); + fds = MAX(fds, pending_fd + 1); ++ } + } + + if (sock->can_send_data && writefds && !is_congested) +@@ -1838,6 +1905,7 @@ + } + + return *n_fds = MAX(fds, *n_fds); ++ } + } + + #ifdef CONFIG_HAVE_POLL +@@ -1931,6 +1999,7 @@ + return -1; + } + ++ { + struct epoll_event event; + int retval = 0; + +@@ -1988,6 +2057,7 @@ + } + out: + return retval; ++ } + } + #endif + diff --git a/3rdparty/openpgm-svn-r1135/pgm/socket_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/socket_unittest.c new file mode 100644 index 0000000..f4ea446 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/socket_unittest.c @@ -0,0 +1,1293 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for PGM socket. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* mock state */ + +#define TEST_NETWORK "" +#define TEST_PORT 7500 +#define TEST_MAX_TPDU 1500 +#define TEST_TXW_SQNS 32 +#define TEST_RXW_SQNS 32 +#define TEST_HOPS 16 +#define TEST_SPM_AMBIENT ( pgm_secs(30) ) +#define TEST_SPM_HEARTBEAT_INIT { pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(1300), pgm_secs(7), pgm_secs(16), pgm_secs(25), pgm_secs(30) } +#define TEST_PEER_EXPIRY ( pgm_secs(300) ) +#define TEST_SPMR_EXPIRY ( pgm_msecs(250) ) +#define TEST_NAK_BO_IVL ( pgm_msecs(50) ) +#define TEST_NAK_RPT_IVL ( pgm_secs(2) ) +#define TEST_NAK_RDATA_IVL ( pgm_secs(2) ) +#define TEST_NAK_DATA_RETRIES 5 +#define TEST_NAK_NCF_RETRIES 2 + +#define pgm_ipproto_pgm mock_pgm_ipproto_pgm +#define pgm_peer_unref mock_pgm_peer_unref +#define pgm_on_nak_notify mock_pgm_on_nak_notify +#define pgm_send_spm mock_pgm_send_spm +#define pgm_timer_prepare mock_pgm_timer_prepare +#define pgm_timer_check mock_pgm_timer_check +#define pgm_timer_expiration mock_pgm_timer_expiration +#define pgm_timer_dispatch mock_pgm_timer_dispatch +#define pgm_txw_create mock_pgm_txw_create +#define pgm_txw_shutdown mock_pgm_txw_shutdown +#define pgm_rate_create mock_pgm_rate_create +#define pgm_rate_destroy mock_pgm_rate_destroy +#define pgm_rate_remaining mock_pgm_rate_remaining +#define pgm_rs_create mock_pgm_rs_create +#define pgm_rs_destroy mock_pgm_rs_destroy +#define pgm_time_update_now mock_pgm_time_update_now + +#define SOCK_DEBUG +#include "socket.c" + +int mock_pgm_ipproto_pgm = IPPROTO_PGM; + + +static +void +mock_setup (void) +{ + if (!g_thread_supported ()) g_thread_init (NULL); +} + +static +void +mock_teardown (void) +{ +} + +/* stock create pgm sockaddr structure for calls to pgm_bind() + */ + +static +struct pgm_sockaddr_t* +generate_asm_sockaddr (void) +{ + const pgm_gsi_t gsi = { 200, 202, 203, 204, 205, 206 }; + struct pgm_sockaddr_t* pgmsa = g_new0 (struct pgm_sockaddr_t, 1); + pgmsa->sa_port = TEST_PORT; + memcpy (&pgmsa->sa_addr.gsi, &gsi, sizeof(gsi)); + return pgmsa; +} + +/* apply minimum socket options to new socket to pass bind() + */ + +static +void +prebind_socket ( + struct pgm_sock_t* sock + ) +{ + sock->max_tpdu = TEST_MAX_TPDU; + sock->max_tsdu = TEST_MAX_TPDU - sizeof(struct pgm_ip) - pgm_pkt_offset (FALSE, FALSE); + sock->max_tsdu_fragment = TEST_MAX_TPDU - sizeof(struct pgm_ip) - pgm_pkt_offset (TRUE, FALSE); + sock->max_apdu = MIN(TEST_TXW_SQNS, PGM_MAX_FRAGMENTS) * sock->max_tsdu_fragment; + +/* tx */ + sock->can_send_data = TRUE; + sock->spm_ambient_interval = TEST_SPM_AMBIENT; + const guint interval_init[] = TEST_SPM_HEARTBEAT_INIT; + sock->spm_heartbeat_len = sizeof(interval_init) / sizeof(interval_init[0]); + sock->spm_heartbeat_interval = g_new0 (guint, sock->spm_heartbeat_len + 1); + for (guint i = 1; i < sock->spm_heartbeat_len; i++) + sock->spm_heartbeat_interval[i] = interval_init[i]; + sock->txw_sqns = TEST_TXW_SQNS; + +/* rx */ + sock->can_recv_data = TRUE; + sock->rxw_sqns = TEST_RXW_SQNS; + sock->peer_expiry = TEST_PEER_EXPIRY; + sock->spmr_expiry = TEST_SPMR_EXPIRY; + sock->nak_bo_ivl = TEST_NAK_BO_IVL; + sock->nak_rpt_ivl = TEST_NAK_RPT_IVL; + sock->nak_rdata_ivl = TEST_NAK_RDATA_IVL; + sock->nak_data_retries = TEST_NAK_DATA_RETRIES; + sock->nak_ncf_retries = TEST_NAK_NCF_RETRIES; +} + +/* apply minimum sockets options to pass connect() + */ + +static +void +preconnect_socket ( + struct pgm_sock_t* sock + ) +{ +/* tx */ + ((struct sockaddr*)&sock->send_gsr.gsr_group)->sa_family = AF_INET; + ((struct sockaddr_in*)&sock->send_gsr.gsr_group)->sin_addr.s_addr = inet_addr ("239.192.0.1"); + +/* rx */ + sock->recv_gsr_len = 1; + ((struct sockaddr*)&sock->recv_gsr[0].gsr_group)->sa_family = ((struct sockaddr*)&sock->send_gsr.gsr_group)->sa_family; + ((struct sockaddr*)&sock->recv_gsr[0].gsr_source)->sa_family = ((struct sockaddr*)&sock->send_gsr.gsr_group)->sa_family; + ((struct sockaddr_in*)&sock->recv_gsr[0].gsr_group)->sin_addr.s_addr = ((struct sockaddr_in*)&sock->send_gsr.gsr_group)->sin_addr.s_addr; + ((struct sockaddr_in*)&sock->recv_gsr[0].gsr_source)->sin_addr.s_addr = ((struct sockaddr_in*)&sock->send_gsr.gsr_group)->sin_addr.s_addr; +} + +/* stock create unconnected socket for pgm_setsockopt(), etc. + */ + +static +struct pgm_sock_t* +generate_sock (void) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, g_htons(TEST_PORT) }; + struct pgm_sock_t* sock = g_new0 (struct pgm_sock_t, 1); + sock->family = AF_INET; + sock->protocol = IPPROTO_IP; + sock->recv_sock = socket (AF_INET, SOCK_RAW, 113); + sock->send_sock = socket (AF_INET, SOCK_RAW, 113); + sock->send_with_router_alert_sock = socket (AF_INET, SOCK_RAW, 113); + ((struct sockaddr*)&sock->send_addr)->sa_family = AF_INET; + ((struct sockaddr_in*)&sock->send_addr)->sin_addr.s_addr = inet_addr ("127.0.0.2"); + memcpy (&sock->tsi, &tsi, sizeof(pgm_tsi_t)); + sock->dport = g_htons(TEST_PORT); + sock->window = g_new0 (pgm_txw_t, 1); + sock->iphdr_len = sizeof(struct pgm_ip); + pgm_rwlock_init (&sock->lock); + pgm_spinlock_init (&sock->txw_spinlock); + sock->is_bound = FALSE; + sock->is_connected = FALSE; + sock->is_destroyed = FALSE; + sock->is_reset = FALSE; + return sock; +} + +/** receiver module */ +PGM_GNUC_INTERNAL +void +mock_pgm_peer_unref ( + pgm_peer_t* peer + ) +{ +} + +/** source module */ +static +bool +mock_pgm_on_nak_notify ( + GIOChannel* source, + GIOCondition condition, + gpointer data + ) +{ + return TRUE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_send_spm ( + pgm_sock_t* sock, + int flags + ) +{ + return TRUE; +} + +/** timer module */ +PGM_GNUC_INTERNAL +bool +mock_pgm_timer_prepare ( + pgm_sock_t* const sock + ) +{ + return FALSE; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_timer_check ( + pgm_sock_t* const sock + ) +{ + return FALSE; +} + +PGM_GNUC_INTERNAL +pgm_time_t +mock_pgm_timer_expiration ( + pgm_sock_t* const sock + ) +{ + return 100L; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_timer_dispatch ( + pgm_sock_t* const sock + ) +{ + return TRUE; +} + +/** transmit window module */ +pgm_txw_t* +mock_pgm_txw_create ( + const pgm_tsi_t* const tsi, + const uint16_t tpdu_size, + const uint32_t sqns, + const unsigned secs, + const ssize_t max_rte, + const bool use_fec, + const uint8_t rs_n, + const uint8_t rs_k + ) +{ + pgm_txw_t* window = g_new0 (pgm_txw_t, 1); + return window; +} + +void +mock_pgm_txw_shutdown ( + pgm_txw_t* const window + ) +{ + g_free (window); +} + +/** rate control module */ +PGM_GNUC_INTERNAL +void +mock_pgm_rate_create ( + pgm_rate_t* bucket, + ssize_t rate_per_sec, + size_t iphdr_len, + uint16_t max_tpdu + ) +{ +} + +PGM_GNUC_INTERNAL +void +mock_pgm_rate_destroy ( + pgm_rate_t* bucket + ) +{ +} + +PGM_GNUC_INTERNAL +pgm_time_t +mock_pgm_rate_remaining ( + pgm_rate_t* bucket, + gsize packetlen + ) +{ + return 0; +} + +/** reed solomon module */ +void +mock_pgm_rs_create ( + pgm_rs_t* rs, + const uint8_t n, + const uint8_t k + ) +{ +} + +void +mock_pgm_rs_destroy ( + pgm_rs_t* rs + ) +{ +} + +/** time module */ +static pgm_time_t _mock_pgm_time_update_now (void); +pgm_time_update_func mock_pgm_time_update_now = _mock_pgm_time_update_now; + +static +pgm_time_t +_mock_pgm_time_update_now (void) +{ + return 0x1; +} + + +/* mock functions for external references */ + + +/* target: + * bool + * pgm_socket ( + * pgm_sock_t** sock, + * const sa_family_t family, + * const int pgm_sock_type, + * const int protocol, + * pgm_error_t** error + * ) + */ + +START_TEST (test_create_pass_001) +{ + pgm_error_t* err = NULL; + pgm_sock_t* sock; +/* only one type currently implemented */ + const int pgm_sock_type = SOCK_SEQPACKET; +/* PGM/IPv4 */ + sock = NULL; + fail_unless (TRUE == pgm_socket (&sock, AF_INET, pgm_sock_type, IPPROTO_PGM, &err), "create failed"); +/* PGM/UDP over IPv4 */ + sock = NULL; + fail_unless (TRUE == pgm_socket (&sock, AF_INET, pgm_sock_type, IPPROTO_UDP, &err), "create failed"); +/* PGM/IPv6 */ + sock = NULL; + fail_unless (TRUE == pgm_socket (&sock, AF_INET6, pgm_sock_type, IPPROTO_PGM, &err), "create failed"); +/* PGM/UDP over IPv6 */ + sock = NULL; + fail_unless (TRUE == pgm_socket (&sock, AF_INET6, pgm_sock_type, IPPROTO_UDP, &err), "create failed"); + fail_unless (NULL == err, "error raised"); +} +END_TEST + +/* NULL socket */ +START_TEST (test_create_fail_002) +{ + pgm_error_t* err = NULL; + fail_unless (FALSE == pgm_socket (NULL, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); +} +END_TEST + +/* invalid protocol family */ +START_TEST (test_create_fail_003) +{ + pgm_error_t* err = NULL; + pgm_sock_t* sock = NULL; + fail_unless (FALSE == pgm_socket (&sock, AF_UNSPEC, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); +} +END_TEST + +/* invalid socket type */ +START_TEST (test_create_fail_004) +{ + pgm_error_t* err = NULL; + pgm_sock_t* sock = NULL; + fail_unless (FALSE == pgm_socket (&sock, AF_INET, SOCK_STREAM, IPPROTO_PGM, &err), "create failed"); + fail_unless (FALSE == pgm_socket (&sock, AF_INET, SOCK_DGRAM, IPPROTO_PGM, &err), "create failed"); +} +END_TEST + +/* invalid protocol */ +START_TEST (test_create_fail_005) +{ + pgm_error_t* err = NULL; + pgm_sock_t* sock = NULL; + fail_unless (FALSE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_TCP, &err), "create failed"); +} +END_TEST + + +/* target: + * bool + * pgm_bind ( + * pgm_sock_t* sock, + * const struct pgm_sockaddr_t* sockaddr, + * const socklen_t sockaddrlen, + * pgm_error_t** error + * ) + */ + +START_TEST (test_bind_pass_001) +{ + pgm_error_t* err = NULL; + pgm_sock_t* sock = NULL; + struct pgm_sockaddr_t* pgmsa = generate_asm_sockaddr (); + fail_if (NULL == pgmsa, "generate_asm_sockaddr failed"); + fail_unless (TRUE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); + fail_unless (NULL == err, "error raised"); + fail_unless (TRUE == pgm_bind (sock, pgmsa, sizeof(*pgmsa), &err), "bind failed"); +} +END_TEST + +/* fail on unset options */ +START_TEST (test_bind_fail_001) +{ + pgm_error_t* err = NULL; + pgm_sock_t* sock = NULL; + struct pgm_sockaddr_t* pgmsa = generate_asm_sockaddr (); + fail_if (NULL == pgmsa, "generate_asm_sockaddr failed"); + fail_unless (TRUE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); + fail_unless (NULL == err, "error raised"); + fail_unless (FALSE == pgm_bind (sock, pgmsa, sizeof(*pgmsa), &err), "bind failed"); +} +END_TEST + +/* invalid parameters */ +START_TEST (test_bind_fail_002) +{ + pgm_error_t* err = NULL; + fail_unless (FALSE == pgm_bind (NULL, NULL, 0, &err), "bind failed"); +} +END_TEST + +/* target: + * bool + * pgm_bind3 ( + * pgm_sock_t* sock, + * const struct pgm_sockaddr_t* sockaddr, + * const socklen_t sockaddrlen, + * const struct pgm_interface_req_t* send_req, + * const socklen_t send_req_len, + * const struct pgm_interface_req_t* recv_req, + * const socklen_t recv_req_len, + * pgm_error_t** error + * ) + */ + +/* fail on unset options */ +START_TEST (test_bind3_fail_001) +{ + pgm_error_t* err = NULL; + pgm_sock_t* sock = NULL; + struct pgm_sockaddr_t* pgmsa = generate_asm_sockaddr (); + fail_if (NULL == pgmsa, "generate_asm_sockaddr failed"); + fail_unless (TRUE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); + fail_unless (NULL == err, "error raised"); + struct pgm_interface_req_t send_req = { .ir_interface = 0, .ir_scope_id = 0 }, + recv_req = { .ir_interface = 0, .ir_scope_id = 0 }; + fail_unless (FALSE == pgm_bind3 (sock, + pgmsa, sizeof(*pgmsa), + &send_req, sizeof(send_req), + &recv_req, sizeof(recv_req), + &err), "bind failed"); +} +END_TEST + +/* invalid parameters */ +START_TEST (test_bind3_fail_002) +{ + pgm_error_t* err = NULL; + fail_unless (FALSE == pgm_bind3 (NULL, NULL, 0, NULL, 0, NULL, 0, &err), "bind failed"); +} +END_TEST + +/* target: + * bool + * pgm_connect ( + * pgm_sock_t* sock, + * pgm_error_t** error + * ) + */ + +START_TEST (test_connect_pass_001) +{ + pgm_error_t* err = NULL; + pgm_sock_t* sock = NULL; + struct pgm_sockaddr_t* pgmsa = generate_asm_sockaddr (); + fail_if (NULL == pgmsa, "generate_asm_sockaddr failed"); + fail_unless (TRUE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); + fail_unless (NULL == err, "error raised"); + prebind_socket (sock); + fail_unless (TRUE == pgm_bind (sock, pgmsa, sizeof(*pgmsa), &err), "bind failed"); + preconnect_socket (sock); + fail_unless (TRUE == pgm_connect (sock, &err), "connect failed"); +} +END_TEST + +/* invalid parameters */ +START_TEST (test_connect_fail_001) +{ + pgm_error_t* err = NULL; + fail_unless (FALSE == pgm_connect (NULL, &err), "connect failed"); +} +END_TEST + +/* target: + * bool + * pgm_close ( + * pgm_sock_t* sock, + * bool flush + * ) + */ + +/* socket > close */ +START_TEST (test_destroy_pass_001) +{ + pgm_error_t* err = NULL; + pgm_sock_t* sock = NULL; + fail_unless (TRUE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); + fail_unless (TRUE == pgm_close (sock, FALSE), "destroy failed"); +} +END_TEST + +/* socket > bind > close */ +START_TEST (test_destroy_pass_002) +{ + pgm_error_t* err = NULL; + pgm_sock_t* sock = NULL; + struct pgm_sockaddr_t* pgmsa = generate_asm_sockaddr (); + fail_if (NULL == pgmsa, "generate_asm_sockaddr failed"); + fail_unless (TRUE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); + fail_unless (NULL == err, "error raised"); + fail_unless (TRUE == pgm_bind (sock, pgmsa, sizeof(*pgmsa), &err), "bind failed"); + fail_unless (TRUE == pgm_close (sock, FALSE), "destroy failed"); +} +END_TEST + +/* socket > bind > connect > close */ +START_TEST (test_destroy_pass_003) +{ + pgm_error_t* err = NULL; + pgm_sock_t* sock = NULL; + struct pgm_sockaddr_t* pgmsa = generate_asm_sockaddr (); + fail_if (NULL == pgmsa, "generate_asm_sockaddr failed"); + fail_unless (TRUE == pgm_socket (&sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &err), "create failed"); + fail_unless (NULL == err, "error raised"); + fail_unless (TRUE == pgm_bind (sock, pgmsa, sizeof(*pgmsa), &err), "bind failed"); + fail_unless (TRUE == pgm_connect (sock, &err), "connect failed"); + fail_unless (TRUE == pgm_close (sock, FALSE), "destroy failed"); +} +END_TEST + +/* invalid parameters */ +START_TEST (test_destroy_fail_001) +{ + fail_unless (FALSE == pgm_close (NULL, FALSE), "destroy failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_MAX_TPDU, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_max_tpdu_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int level = IPPROTO_PGM; + const int optname = PGM_MTU; + const int max_tpdu = 1500; + const void* optval = &max_tpdu; + const socklen_t optlen = sizeof(max_tpdu); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_max_tpdu failed"); +} +END_TEST + +/* invalid parameters */ +START_TEST (test_set_max_tpdu_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_MTU; + const int max_tpdu = 1500; + const void* optval = &max_tpdu; + const socklen_t optlen = sizeof(max_tpdu); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_max_tpdu failed"); +} +END_TEST + +/* too small */ +START_TEST (test_set_max_tpdu_fail_002) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int level = IPPROTO_PGM; + const int optname = PGM_MTU; + const int max_tpdu = 1; + const void* optval = &max_tpdu; + const socklen_t optlen = sizeof(max_tpdu); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_max_tpdu failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_MULTICAST_LOOP, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_multicast_loop_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int level = IPPROTO_PGM; + const int optname = PGM_MULTICAST_LOOP; + const int loop_enabled = 1; + const void* optval = &loop_enabled; + const socklen_t optlen = sizeof(loop_enabled); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_multicast_loop failed"); +} +END_TEST + +START_TEST (test_set_multicast_loop_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_MULTICAST_LOOP; + const int loop_enabled = 1; + const void* optval = &loop_enabled; + const socklen_t optlen = sizeof(loop_enabled); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_multicast_loop failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_MULTICAST_HOPS, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_hops_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int level = IPPROTO_PGM; + const int optname = PGM_MULTICAST_HOPS; + const int hops = 16; + const void* optval = &hops; + const socklen_t optlen = sizeof(hops); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_hops failed"); +} +END_TEST + +START_TEST (test_set_hops_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_MULTICAST_HOPS; + const int hops = 16; + const void* optval = &hops; + const socklen_t optlen = sizeof(hops); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_hops failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = SOL_SOCKET, + * const int optname = SO_SNDBUF, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_sndbuf_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int level = SOL_SOCKET; + const int optname = SO_SNDBUF; + const int bufsize = 131071; /* 128kB */ + const void* optval = &bufsize; + const socklen_t optlen = sizeof(bufsize); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_sndbuf failed"); +} +END_TEST + +START_TEST (test_set_sndbuf_fail_001) +{ + const int level = SOL_SOCKET; + const int optname = SO_SNDBUF; + const int bufsize = 131071; /* 128kB */ + const void* optval = &bufsize; + const socklen_t optlen = sizeof(bufsize); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_sndbuf failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = SOL_SOCKET, + * const int optname = PGM_RCVBUF, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_rcvbuf_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int level = SOL_SOCKET; + const int optname = SO_RCVBUF; + const int bufsize = 131071; /* 128kB */ + const void* optval = &bufsize; + const socklen_t optlen = sizeof(bufsize); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_rcvbuf failed"); +} +END_TEST + +START_TEST (test_set_rcvbuf_fail_001) +{ + const int level = SOL_SOCKET; + const int optname = SO_RCVBUF; + const int bufsize = 131071; /* 128kB */ + const void* optval = &bufsize; + const socklen_t optlen = sizeof(bufsize); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_rcvbuf failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_USE_FEC, + * const void* optval, + * const socklen_t optlen = sizeof(struct pgm_fecinfo_t) + * ) + */ + +START_TEST (test_set_fec_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int level = IPPROTO_PGM; + const int optname = PGM_USE_FEC; + const struct pgm_fecinfo_t fecinfo = { + .ondemand_parity_enabled = TRUE, + .proactive_packets = 16, + .var_pktlen_enabled = TRUE, + .block_size = 255, + .group_size = 64 + }; + const void* optval = &fecinfo; + const socklen_t optlen = sizeof(fecinfo); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_fec failed"); +} +END_TEST + +START_TEST (test_set_fec_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_USE_FEC; + const struct pgm_fecinfo_t fecinfo = { + .ondemand_parity_enabled = TRUE, + .proactive_packets = 16, + .var_pktlen_enabled = TRUE, + .block_size = 255, + .group_size = 64 + }; + const void* optval = &fecinfo; + const socklen_t optlen = sizeof(fecinfo); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_fec failed"); +} +END_TEST + +/* TODO: invalid Reed-Solomon parameters + */ + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_USE_PGMCC, + * const void* optval, + * const socklen_t optlen = sizeof(struct pgm_pgmccinfo_t) + * ) + */ + +START_TEST (test_set_pgmcc_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int level = IPPROTO_PGM; + const int optname = PGM_USE_PGMCC; + const struct pgm_pgmccinfo_t pgmccinfo = { + .ack_bo_ivl = pgm_msecs(100), + .ack_c = 123, + .ack_c_p = 456 + }; + const void* optval = &pgmccinfo; + const socklen_t optlen = sizeof(pgmccinfo); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_pgmcc failed"); +} +END_TEST + +START_TEST (test_set_pgmcc_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_USE_PGMCC; + const struct pgm_pgmccinfo_t pgmccinfo = { + .ack_bo_ivl = pgm_msecs(100), + .ack_c = 123, + .ack_c_p = 456 + }; + const void* optval = &pgmccinfo; + const socklen_t optlen = sizeof(pgmccinfo); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_pgmcc failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_USE_CR, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_cr_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int level = IPPROTO_PGM; + const int optname = PGM_USE_CR; + const int magic_bunny = 1; + const void* optval = &magic_bunny; + const socklen_t optlen = sizeof(magic_bunny); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_cr failed"); +} +END_TEST + +START_TEST (test_set_cr_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_USE_CR; + const int magic_bunny = 1; + const void* optval = &magic_bunny; + const socklen_t optlen = sizeof(magic_bunny); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_cr failed"); +} +END_TEST + + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_SEND_ONLY, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_send_only_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int level = IPPROTO_PGM; + const int optname = PGM_SEND_ONLY; + const int send_only = 1; + const void* optval = &send_only; + const socklen_t optlen = sizeof(send_only); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_send_only failed"); +} +END_TEST + +START_TEST (test_set_send_only_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_SEND_ONLY; + const int send_only = 1; + const void* optval = &send_only; + const socklen_t optlen = sizeof(send_only); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_send_only failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_RECV_ONLY, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_recv_only_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int level = IPPROTO_PGM; + const int optname = PGM_RECV_ONLY; + const int recv_only = 1; + const void* optval = &recv_only; + const socklen_t optlen = sizeof(recv_only); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_recv_only failed"); +} +END_TEST + +START_TEST (test_set_recv_only_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_RECV_ONLY; + const int recv_only = 1; + const void* optval = &recv_only; + const socklen_t optlen = sizeof(recv_only); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_recv_only failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_PASSIVE, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_recv_only_pass_002) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int level = IPPROTO_PGM; + const int optname = PGM_PASSIVE; + const int passive = 1; + const void* optval = &passive; + const socklen_t optlen = sizeof(passive); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_passive failed"); +} +END_TEST + +START_TEST (test_set_recv_only_fail_002) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_PASSIVE; + const int passive = 1; + const void* optval = &passive; + const socklen_t optlen = sizeof(passive); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_passive failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_ABORT_ON_RESET, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_abort_on_reset_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int level = IPPROTO_PGM; + const int optname = PGM_ABORT_ON_RESET; + const int abort_on_reset= 1; + const void* optval = &abort_on_reset; + const socklen_t optlen = sizeof(abort_on_reset); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_abort_on_reset failed"); +} +END_TEST + +START_TEST (test_set_abort_on_reset_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_ABORT_ON_RESET; + const int abort_on_reset= 1; + const void* optval = &abort_on_reset; + const socklen_t optlen = sizeof(abort_on_reset); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_abort_on_reset failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_NOBLOCK, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_noblock_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int level = IPPROTO_PGM; + const int optname = PGM_NOBLOCK; + const int noblock = 1; + const void* optval = &noblock; + const socklen_t optlen = sizeof(noblock); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_noblock failed"); +} +END_TEST + +START_TEST (test_set_noblock_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_NOBLOCK; + const int noblock = 1; + const void* optval = &noblock; + const socklen_t optlen = sizeof(noblock); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_noblock failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_UDP_ENCAP_UCAST_PORT, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_udp_unicast_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int level = IPPROTO_PGM; + const int optname = PGM_NOBLOCK; + const int unicast_port = 10001; + const void* optval = &unicast_port; + const socklen_t optlen = sizeof(unicast_port); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_udp_unicast failed"); +} +END_TEST + +START_TEST (test_set_udp_unicast_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_NOBLOCK; + const int unicast_port = 10001; + const void* optval = &unicast_port; + const socklen_t optlen = sizeof(unicast_port); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_udp_unicast failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_UDP_ENCAP_MCAST_PORT, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_udp_multicast_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int level = IPPROTO_PGM; + const int optname = PGM_NOBLOCK; + const int multicast_port= 10001; + const void* optval = &multicast_port; + const socklen_t optlen = sizeof(multicast_port); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_udp_multicast failed"); +} +END_TEST + +START_TEST (test_set_udp_multicast_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_NOBLOCK; + const int multicast_port= 10002; + const void* optval = &multicast_port; + const socklen_t optlen = sizeof(multicast_port); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_udp_multicast failed"); +} +END_TEST + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_create = tcase_create ("create"); + suite_add_tcase (s, tc_create); + tcase_add_checked_fixture (tc_create, mock_setup, mock_teardown); + tcase_add_test (tc_create, test_create_pass_001); + tcase_add_test (tc_create, test_create_fail_002); + tcase_add_test (tc_create, test_create_fail_003); + tcase_add_test (tc_create, test_create_fail_004); + tcase_add_test (tc_create, test_create_fail_005); + + TCase* tc_bind = tcase_create ("bind"); + suite_add_tcase (s, tc_bind); + tcase_add_checked_fixture (tc_bind, mock_setup, mock_teardown); + tcase_add_test (tc_bind, test_bind_fail_001); + tcase_add_test (tc_bind, test_bind_fail_002); + + TCase* tc_connect = tcase_create ("connect"); + suite_add_tcase (s, tc_connect); + tcase_add_checked_fixture (tc_connect, mock_setup, mock_teardown); + tcase_add_test (tc_connect, test_connect_pass_001); + tcase_add_test (tc_connect, test_connect_fail_001); + + TCase* tc_destroy = tcase_create ("destroy"); + suite_add_tcase (s, tc_destroy); + tcase_add_checked_fixture (tc_destroy, mock_setup, mock_teardown); + tcase_add_test (tc_destroy, test_destroy_pass_001); + tcase_add_test (tc_destroy, test_destroy_fail_001); + + TCase* tc_set_max_tpdu = tcase_create ("set-max-tpdu"); + suite_add_tcase (s, tc_set_max_tpdu); + tcase_add_checked_fixture (tc_set_max_tpdu, mock_setup, mock_teardown); + tcase_add_test (tc_set_max_tpdu, test_set_max_tpdu_pass_001); + tcase_add_test (tc_set_max_tpdu, test_set_max_tpdu_fail_001); + + TCase* tc_set_multicast_loop = tcase_create ("set-multicast-loop"); + suite_add_tcase (s, tc_set_multicast_loop); + tcase_add_checked_fixture (tc_set_multicast_loop, mock_setup, mock_teardown); + tcase_add_test (tc_set_multicast_loop, test_set_multicast_loop_pass_001); + tcase_add_test (tc_set_multicast_loop, test_set_multicast_loop_fail_001); + + TCase* tc_set_hops = tcase_create ("set-hops"); + suite_add_tcase (s, tc_set_hops); + tcase_add_checked_fixture (tc_set_hops, mock_setup, mock_teardown); + tcase_add_test (tc_set_hops, test_set_hops_pass_001); + tcase_add_test (tc_set_hops, test_set_hops_fail_001); + + TCase* tc_set_sndbuf = tcase_create ("set-sndbuf"); + suite_add_tcase (s, tc_set_sndbuf); + tcase_add_checked_fixture (tc_set_sndbuf, mock_setup, mock_teardown); + tcase_add_test (tc_set_sndbuf, test_set_sndbuf_pass_001); + tcase_add_test (tc_set_sndbuf, test_set_sndbuf_fail_001); + + TCase* tc_set_rcvbuf = tcase_create ("set-rcvbuf"); + suite_add_tcase (s, tc_set_rcvbuf); + tcase_add_checked_fixture (tc_set_rcvbuf, mock_setup, mock_teardown); + tcase_add_test (tc_set_rcvbuf, test_set_rcvbuf_pass_001); + tcase_add_test (tc_set_rcvbuf, test_set_rcvbuf_fail_001); + + TCase* tc_set_fec = tcase_create ("set-fec"); + suite_add_tcase (s, tc_set_fec); + tcase_add_checked_fixture (tc_set_fec, mock_setup, mock_teardown); + tcase_add_test (tc_set_fec, test_set_fec_pass_001); + tcase_add_test (tc_set_fec, test_set_fec_fail_001); + + TCase* tc_set_pgmcc = tcase_create ("set-pgmcc"); + suite_add_tcase (s, tc_set_pgmcc); + tcase_add_checked_fixture (tc_set_pgmcc, mock_setup, mock_teardown); + tcase_add_test (tc_set_pgmcc, test_set_pgmcc_pass_001); + tcase_add_test (tc_set_pgmcc, test_set_pgmcc_fail_001); + + TCase* tc_set_cr = tcase_create ("set-cr"); + suite_add_tcase (s, tc_set_cr); + tcase_add_checked_fixture (tc_set_cr, mock_setup, mock_teardown); + tcase_add_test (tc_set_cr, test_set_cr_pass_001); + tcase_add_test (tc_set_cr, test_set_cr_fail_001); + + TCase* tc_set_send_only = tcase_create ("set-send-only"); + suite_add_tcase (s, tc_set_send_only); + tcase_add_checked_fixture (tc_set_send_only, mock_setup, mock_teardown); + tcase_add_test (tc_set_send_only, test_set_send_only_pass_001); + tcase_add_test (tc_set_send_only, test_set_send_only_fail_001); + + TCase* tc_set_recv_only = tcase_create ("set-recv-only"); + suite_add_tcase (s, tc_set_recv_only); + tcase_add_checked_fixture (tc_set_recv_only, mock_setup, mock_teardown); + tcase_add_test (tc_set_recv_only, test_set_recv_only_pass_001); + tcase_add_test (tc_set_recv_only, test_set_recv_only_pass_002); + tcase_add_test (tc_set_recv_only, test_set_recv_only_fail_001); + tcase_add_test (tc_set_recv_only, test_set_recv_only_fail_002); + + TCase* tc_set_abort_on_reset = tcase_create ("set-abort-on-reset"); + suite_add_tcase (s, tc_set_abort_on_reset); + tcase_add_checked_fixture (tc_set_abort_on_reset, mock_setup, mock_teardown); + tcase_add_test (tc_set_abort_on_reset, test_set_abort_on_reset_pass_001); + tcase_add_test (tc_set_abort_on_reset, test_set_abort_on_reset_fail_001); + + TCase* tc_set_noblock = tcase_create ("set-non-blocking"); + suite_add_tcase (s, tc_set_noblock); + tcase_add_checked_fixture (tc_set_noblock, mock_setup, mock_teardown); + tcase_add_test (tc_set_noblock, test_set_noblock_pass_001); + tcase_add_test (tc_set_noblock, test_set_noblock_fail_001); + + TCase* tc_set_udp_unicast = tcase_create ("set-udp-encap-ucast-port"); + suite_add_tcase (s, tc_set_udp_unicast); + tcase_add_checked_fixture (tc_set_udp_unicast, mock_setup, mock_teardown); + tcase_add_test (tc_set_udp_unicast, test_set_udp_unicast_pass_001); + tcase_add_test (tc_set_udp_unicast, test_set_udp_unicast_fail_001); + + TCase* tc_set_udp_multicast = tcase_create ("set-udp-encap-mcast-port"); + suite_add_tcase (s, tc_set_udp_multicast); + tcase_add_checked_fixture (tc_set_udp_multicast, mock_setup, mock_teardown); + tcase_add_test (tc_set_udp_multicast, test_set_udp_multicast_pass_001); + tcase_add_test (tc_set_udp_multicast, test_set_udp_multicast_fail_001); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + if (0 != getuid()) { + fprintf (stderr, "This test requires super-user privileges to run.\n"); + return EXIT_FAILURE; + } + + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/source.c b/3rdparty/openpgm-svn-r1135/pgm/source.c new file mode 100644 index 0000000..bea0f33 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/source.c @@ -0,0 +1,2345 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM source socket. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __STDC_FORMAT_MACROS +#ifdef _MSC_VER +# include +#else +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + + +//#define SOURCE_DEBUG + +#ifndef SOURCE_DEBUG +# define PGM_DISABLE_ASSERT +#endif + +#if !defined(ENOBUFS) && defined(WSAENOBUFS) +# define ENOBUFS WSAENOBUFS +#endif + + +/* locals */ +static inline bool peer_is_source (const pgm_peer_t*) PGM_GNUC_CONST; +static inline bool peer_is_peer (const pgm_peer_t*) PGM_GNUC_CONST; +static void reset_heartbeat_spm (pgm_sock_t*const, const pgm_time_t); +static bool send_ncf (pgm_sock_t*const restrict, const struct sockaddr*const restrict, const struct sockaddr*const restrict, const uint32_t, const bool); +static bool send_ncf_list (pgm_sock_t*const restrict, const struct sockaddr*const restrict, const struct sockaddr*const restrict, struct pgm_sqn_list_t*const restrict, const bool); +static int send_odata (pgm_sock_t*const restrict, struct pgm_sk_buff_t*const restrict, size_t*restrict); +static int send_odata_copy (pgm_sock_t*const restrict, const void*restrict, const uint16_t, size_t*restrict); +static int send_odatav (pgm_sock_t*const restrict, const struct pgm_iovec*const restrict, const unsigned, size_t*restrict); +static bool send_rdata (pgm_sock_t*restrict, struct pgm_sk_buff_t*restrict); + + +static inline +unsigned +_pgm_popcount ( + uint32_t n + ) +{ +#if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) + return __builtin_popcount (n); +#else +/* MIT HAKMEM 169 */ + const uint32_t t = n - ((n >> 1) & 033333333333) + - ((n >> 2) & 011111111111); + return ((t + (t >> 3) & 030707070707)) % 63; +#endif +} + +static inline +bool +peer_is_source ( + const pgm_peer_t* peer + ) +{ + return (NULL == peer); +} + +static inline +bool +peer_is_peer ( + const pgm_peer_t* peer + ) +{ + return (NULL != peer); +} + +static inline +void +reset_spmr_timer ( + pgm_peer_t* const peer + ) +{ + peer->spmr_expiry = 0; +} + +static inline +size_t +source_max_tsdu ( + const pgm_sock_t* sock, + const bool can_fragment + ) +{ + size_t max_tsdu = can_fragment ? sock->max_tsdu_fragment : sock->max_tsdu; + if (sock->use_var_pktlen /* OPT_VAR_PKT_LEN */) + max_tsdu -= sizeof (uint16_t); + return max_tsdu; +} + +/* prototype of function to send pro-active parity NAKs. + */ +static +bool +pgm_schedule_proactive_nak ( + pgm_sock_t* sock, + uint32_t nak_tg_sqn /* transmission group (shifted) */ + ) +{ + pgm_return_val_if_fail (NULL != sock, FALSE); + const bool status = pgm_txw_retransmit_push (sock->window, + nak_tg_sqn | sock->rs_proactive_h, + TRUE /* is_parity */, + sock->tg_sqn_shift); + return status; +} + +/* a deferred request for RDATA, now processing in the timer thread, we check the transmit + * window to see if the packet exists and forward on, maintaining a lock until the queue is + * empty. + * + * returns TRUE on success, returns FALSE if operation would block. + */ + +bool +pgm_on_deferred_nak ( + pgm_sock_t* const sock + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + +/* We can flush queue and block all odata, or process one set, or process each + * sequence number individually. + */ + +/* parity packets are re-numbered across the transmission group with index h, sharing the space + * with the original packets. beyond the transmission group size (k), the PGM option OPT_PARITY_GRP + * provides the extra offset value. + */ + +/* peek from the retransmit queue so we can eliminate duplicate NAKs up until the repair packet + * has been retransmitted. + */ + pgm_spinlock_lock (&sock->txw_spinlock); + struct pgm_sk_buff_t* skb = pgm_txw_retransmit_try_peek (sock->window); + if (skb) { + skb = pgm_skb_get (skb); + pgm_spinlock_unlock (&sock->txw_spinlock); + if (!send_rdata (sock, skb)) { + pgm_free_skb (skb); + pgm_notify_send (&sock->rdata_notify); + return FALSE; + } + pgm_free_skb (skb); +/* now remove sequence number from retransmit queue, re-enabling NAK processing for this sequence number */ + pgm_txw_retransmit_remove_head (sock->window); + } else + pgm_spinlock_unlock (&sock->txw_spinlock); + return TRUE; +} + +/* SPMR indicates if multicast to cancel own SPMR, or unicast to send SPM. + * + * rate limited to 1/IHB_MIN per TSI (13.4). + * + * if SPMR was valid, returns TRUE, if invalid returns FALSE. + */ + +bool +pgm_on_spmr ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict peer, /* maybe NULL if socket is source */ + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + + pgm_debug ("pgm_on_spmr (sock:%p peer:%p skb:%p)", + (void*)sock, (void*)peer, (void*)skb); + + if (PGM_UNLIKELY(!pgm_verify_spmr (skb))) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed SPMR rejected.")); + return FALSE; + } + + if (peer_is_source (peer)) { + const bool send_status = pgm_send_spm (sock, 0); + if (PGM_UNLIKELY(!send_status)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Failed to send SPM on SPM-Request.")); + } + } else { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Suppressing SPMR due to peer multicast SPMR.")); + reset_spmr_timer (peer); + } + return TRUE; +} + +/* Process opt_pgmcc_feedback PGM option that ships attached to ACK or NAK. + * Contents use to elect best ACKer. + * + * returns TRUE if peer is the elected ACKer. + */ + +static +bool +on_opt_pgmcc_feedback ( + pgm_sock_t* const restrict sock, + const struct pgm_sk_buff_t* const restrict skb, + const struct pgm_opt_pgmcc_feedback* restrict opt_pgmcc_feedback + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + pgm_assert (NULL != opt_pgmcc_feedback); + + const uint32_t opt_tstamp = ntohl (opt_pgmcc_feedback->opt_tstamp); + const uint16_t opt_loss_rate = ntohs (opt_pgmcc_feedback->opt_loss_rate); + + const uint32_t rtt = pgm_to_msecs (skb->tstamp) - opt_tstamp; + const uint64_t peer_loss = rtt * rtt * opt_loss_rate; + + struct sockaddr_storage peer_nla; + pgm_nla_to_sockaddr (&opt_pgmcc_feedback->opt_nla_afi, (struct sockaddr*)&peer_nla); + +/* ACKer elections */ + if (PGM_UNLIKELY(pgm_sockaddr_is_addr_unspecified ((const struct sockaddr*)&sock->acker_nla))) + { + pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Elected first ACKer")); + memcpy (&sock->acker_nla, &peer_nla, pgm_sockaddr_storage_len (&peer_nla)); + } + else if (peer_loss > sock->acker_loss && + 0 != pgm_sockaddr_cmp ((const struct sockaddr*)&peer_nla, (const struct sockaddr*)&sock->acker_nla)) + { + pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Elected new ACKer")); + memcpy (&sock->acker_nla, &peer_nla, pgm_sockaddr_storage_len (&peer_nla)); + } + +/* update ACKer state */ + if (0 == pgm_sockaddr_cmp ((const struct sockaddr*)&peer_nla, (const struct sockaddr*)&sock->acker_nla)) + { + sock->acker_loss = peer_loss; + return TRUE; + } + + return FALSE; +} + +/* NAK requesting RDATA transmission for a sending sock, only valid if + * sequence number(s) still in transmission window. + * + * we can potentially have different IP versions for the NAK packet to the send group. + * + * TODO: fix IPv6 AFIs + * + * take in a NAK and pass off to an asynchronous queue for another thread to process + * + * if NAK is valid, returns TRUE. on error, FALSE is returned. + */ + +bool +pgm_on_nak ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + + pgm_debug ("pgm_on_nak (sock:%p skb:%p)", + (const void*)sock, (const void*)skb); + + const bool is_parity = skb->pgm_header->pgm_options & PGM_OPT_PARITY; + if (is_parity) { + sock->cumulative_stats[PGM_PC_SOURCE_PARITY_NAKS_RECEIVED]++; + if (!sock->use_ondemand_parity) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Parity NAK rejected as on-demand parity is not enabled.")); + sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; + return FALSE; + } + } else + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED]++; + + if (PGM_UNLIKELY(!pgm_verify_nak (skb))) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed NAK rejected.")); + sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; + return FALSE; + } + + const struct pgm_nak* nak = (struct pgm_nak*) skb->data; + const struct pgm_nak6* nak6 = (struct pgm_nak6*)skb->data; + +/* NAK_SRC_NLA contains our sock unicast NLA */ + struct sockaddr_storage nak_src_nla; + pgm_nla_to_sockaddr (&nak->nak_src_nla_afi, (struct sockaddr*)&nak_src_nla); + if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&nak_src_nla, (struct sockaddr*)&sock->send_addr) != 0)) + { + char saddr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&nak_src_nla, saddr, sizeof(saddr)); + pgm_trace (PGM_LOG_ROLE_NETWORK,_("NAK rejected for unmatched NLA: %s"), saddr); + sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; + return FALSE; + } + +/* NAK_GRP_NLA containers our sock multicast group */ + struct sockaddr_storage nak_grp_nla; + pgm_nla_to_sockaddr ((AF_INET6 == nak_src_nla.ss_family) ? &nak6->nak6_grp_nla_afi : &nak->nak_grp_nla_afi, (struct sockaddr*)&nak_grp_nla); + if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&nak_grp_nla, (struct sockaddr*)&sock->send_gsr.gsr_group) != 0)) + { + char sgroup[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&nak_src_nla, sgroup, sizeof(sgroup)); + pgm_trace (PGM_LOG_ROLE_NETWORK,_("NAK rejected as targeted for different multicast group: %s"), sgroup); + sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; + return FALSE; + } + +/* create queue object */ + struct pgm_sqn_list_t sqn_list; + sqn_list.sqn[0] = ntohl (nak->nak_sqn); + sqn_list.len = 1; + + pgm_debug ("nak_sqn %" PRIu32, sqn_list.sqn[0]); + +/* check NAK list */ + const uint32_t* nak_list = NULL; + uint_fast8_t nak_list_len = 0; + if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) + { + const struct pgm_opt_length* opt_len = (AF_INET6 == nak_src_nla.ss_family) ? + (const struct pgm_opt_length*)(nak6 + 1) : + (const struct pgm_opt_length*)(nak + 1); + if (PGM_UNLIKELY(opt_len->opt_type != PGM_OPT_LENGTH)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed NAK rejected.")); + sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; + return FALSE; + } + if (PGM_UNLIKELY(opt_len->opt_length != sizeof(struct pgm_opt_length))) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed NAK rejected.")); + sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; + return FALSE; + } +/* TODO: check for > 16 options & past packet end */ + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; + do { + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); + if ((opt_header->opt_type & PGM_OPT_MASK) == PGM_OPT_NAK_LIST) { + nak_list = ((const struct pgm_opt_nak_list*)(opt_header + 1))->opt_sqn; + nak_list_len = ( opt_header->opt_length - sizeof(struct pgm_opt_header) - sizeof(uint8_t) ) / sizeof(uint32_t); + break; + } + } while (!(opt_header->opt_type & PGM_OPT_END)); + } + +/* nak list numbers */ + if (PGM_UNLIKELY(nak_list_len > 63)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed NAK rejected on too long sequence list.")); + return FALSE; + } + + for (uint_fast8_t i = 0; i < nak_list_len; i++) + { + sqn_list.sqn[sqn_list.len++] = ntohl (*nak_list); + nak_list++; + } + +/* send NAK confirm packet immediately, then defer to timer thread for a.s.a.p + * delivery of the actual RDATA packets. blocking send for NCF is ignored as RDATA + * broadcast will be sent later. + */ + if (nak_list_len) + send_ncf_list (sock, (struct sockaddr*)&nak_src_nla, (struct sockaddr*)&nak_grp_nla, &sqn_list, is_parity); + else + send_ncf (sock, (struct sockaddr*)&nak_src_nla, (struct sockaddr*)&nak_grp_nla, sqn_list.sqn[0], is_parity); + +/* queue retransmit requests */ + for (uint_fast8_t i = 0; i < sqn_list.len; i++) { + const bool push_status = pgm_txw_retransmit_push (sock->window, sqn_list.sqn[i], is_parity, sock->tg_sqn_shift); + if (PGM_UNLIKELY(!push_status)) { + pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Failed to push retransmit request for #%" PRIu32), sqn_list.sqn[i]); + } + } + return TRUE; +} + +/* Null-NAK, or N-NAK propogated by a DLR for hand waving excitement + * + * if NNAK is valid, returns TRUE. on error, FALSE is returned. + */ + +bool +pgm_on_nnak ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + + pgm_debug ("pgm_on_nnak (sock:%p skb:%p)", + (void*)sock, (void*)skb); + + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAK_PACKETS_RECEIVED]++; + + if (PGM_UNLIKELY(!pgm_verify_nnak (skb))) { + sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]++; + return FALSE; + } + + const struct pgm_nak* nnak = (struct pgm_nak*) skb->data; + const struct pgm_nak6* nnak6 = (struct pgm_nak6*)skb->data; + +/* NAK_SRC_NLA contains our sock unicast NLA */ + struct sockaddr_storage nnak_src_nla; + pgm_nla_to_sockaddr (&nnak->nak_src_nla_afi, (struct sockaddr*)&nnak_src_nla); + + if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&nnak_src_nla, (struct sockaddr*)&sock->send_addr) != 0)) + { + sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]++; + return FALSE; + } + +/* NAK_GRP_NLA containers our sock multicast group */ + struct sockaddr_storage nnak_grp_nla; + pgm_nla_to_sockaddr ((AF_INET6 == nnak_src_nla.ss_family) ? &nnak6->nak6_grp_nla_afi : &nnak->nak_grp_nla_afi, (struct sockaddr*)&nnak_grp_nla); + if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&nnak_grp_nla, (struct sockaddr*)&sock->send_gsr.gsr_group) != 0)) + { + sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]++; + return FALSE; + } + +/* check NNAK list */ + uint_fast8_t nnak_list_len = 0; + if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) + { + const struct pgm_opt_length* opt_len = (AF_INET6 == nnak_src_nla.ss_family) ? + (const struct pgm_opt_length*)(nnak6 + 1) : + (const struct pgm_opt_length*)(nnak + 1); + if (PGM_UNLIKELY(opt_len->opt_type != PGM_OPT_LENGTH)) { + sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]++; + return FALSE; + } + if (PGM_UNLIKELY(opt_len->opt_length != sizeof(struct pgm_opt_length))) { + sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]++; + return FALSE; + } +/* TODO: check for > 16 options & past packet end */ + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; + do { + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); + if ((opt_header->opt_type & PGM_OPT_MASK) == PGM_OPT_NAK_LIST) { + nnak_list_len = ( opt_header->opt_length - sizeof(struct pgm_opt_header) - sizeof(uint8_t) ) / sizeof(uint32_t); + break; + } + } while (!(opt_header->opt_type & PGM_OPT_END)); + } + + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAKS_RECEIVED] += 1 + nnak_list_len; + return TRUE; +} + +/* ACK, sent upstream by one selected ACKER for congestion control feedback. + * + * if ACK is valid, returns TRUE. on error, FALSE is returned. + */ + +bool +pgm_on_ack ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + + pgm_debug ("pgm_on_ack (sock:%p skb:%p)", + (const void*)sock, (const void*)skb); + + sock->cumulative_stats[PGM_PC_SOURCE_ACK_PACKETS_RECEIVED]++; + + if (PGM_UNLIKELY(!pgm_verify_ack (skb))) { + sock->cumulative_stats[PGM_PC_SOURCE_ACK_ERRORS]++; + return FALSE; + } + + if (!sock->use_pgmcc) + return FALSE; + + const struct pgm_ack* ack = (struct pgm_ack*)skb->data; + bool is_acker = FALSE; + +/* check PGMCC feedback option for new elections */ + if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) + { + const struct pgm_opt_length* opt_len = (const struct pgm_opt_length*)(ack + 1); + if (PGM_UNLIKELY(opt_len->opt_type != PGM_OPT_LENGTH)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed ACK rejected.")); + return FALSE; + } + if (PGM_UNLIKELY(opt_len->opt_length != sizeof(struct pgm_opt_length))) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed ACK rejected.")); + return FALSE; + } + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; + do { + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); + if ((opt_header->opt_type & PGM_OPT_MASK) == PGM_OPT_PGMCC_FEEDBACK) { + const struct pgm_opt_pgmcc_feedback* opt_pgmcc_feedback = (const struct pgm_opt_pgmcc_feedback*)(opt_header + 1); + is_acker = on_opt_pgmcc_feedback (sock, skb, opt_pgmcc_feedback); + break; /* ignore other options */ + } + } while (!(opt_header->opt_type & PGM_OPT_END)); + } + +/* ignore ACKs from other receivers or sessions */ + if (!is_acker) + return TRUE; + +/* reset ACK expiration */ + sock->next_crqst = 0; + +/* count new ACK sequences */ + const uint32_t ack_rx_max = ntohl (ack->ack_rx_max); + const int32_t delta = ack_rx_max - sock->ack_rx_max; +/* ignore older ACKs when multiple active ACKers */ + if (pgm_uint32_gt (ack_rx_max, sock->ack_rx_max)) + sock->ack_rx_max = ack_rx_max; + uint32_t ack_bitmap = ntohl (ack->ack_bitmap); + if (delta > 32) sock->ack_bitmap = 0; /* sequence jump ahead beyond past bitmap */ + else if (delta > 0) sock->ack_bitmap <<= delta; /* immediate sequence */ + else if (delta > -32) ack_bitmap <<= -delta; /* repair sequence scoped by bitmap */ + else ack_bitmap = 0; /* old sequence */ + unsigned new_acks = _pgm_popcount (ack_bitmap & ~sock->ack_bitmap); + sock->ack_bitmap |= ack_bitmap; + + if (0 == new_acks) + return TRUE; + + const bool is_congestion_limited = (sock->tokens < pgm_fp8 (1)); + +/* after loss detection cancel any further manipulation of the window + * until feedback is received for the next transmitted packet. + */ + if (sock->is_congested) + { + if (pgm_uint32_lte (ack_rx_max, sock->suspended_sqn)) + { + pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("PGMCC window token manipulation suspended due to congestion (T:%u W:%u)"), + pgm_fp8tou (sock->tokens), pgm_fp8tou (sock->cwnd_size)); + const uint_fast32_t token_inc = pgm_fp8mul (pgm_fp8 (new_acks), pgm_fp8 (1) + pgm_fp8div (pgm_fp8 (1), sock->cwnd_size)); + sock->tokens = MIN( sock->tokens + token_inc, sock->cwnd_size ); + goto notify_tx; + } + sock->is_congested = FALSE; + } + +/* count outstanding lost sequences */ + const unsigned total_lost = _pgm_popcount (~sock->ack_bitmap); + +/* no detected data loss at ACKer, increase congestion window size */ + if (0 == total_lost) + { + new_acks += sock->acks_after_loss; + sock->acks_after_loss = 0; + uint_fast32_t n = pgm_fp8 (new_acks); + uint_fast32_t token_inc = 0; + +/* slow-start phase, exponential increase to SSTHRESH */ + if (sock->cwnd_size < sock->ssthresh) { + const uint_fast32_t d = MIN( n, sock->ssthresh - sock->cwnd_size ); + n -= d; + token_inc = d + d; + sock->cwnd_size += d; + } + + const uint_fast32_t iw = pgm_fp8div (pgm_fp8 (1), sock->cwnd_size); + +/* linear window increase */ + token_inc += pgm_fp8mul (n, pgm_fp8 (1) + iw); + sock->cwnd_size += pgm_fp8mul (n, iw); + sock->tokens = MIN( sock->tokens + token_inc, sock->cwnd_size ); +// pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("PGMCC++ (T:%u W:%u)"), +// pgm_fp8tou (sock->tokens), pgm_fp8tou (sock->cwnd_size)); + } + else + { +/* Look for an unacknowledged data packet which is followed by at least three + * acknowledged data packets, then the packet is assumed to be lost and PGMCC + * reacts by halving the window. + * + * Common value will be 0xfffffff7. + */ + sock->acks_after_loss += new_acks; + if (sock->acks_after_loss >= 3) + { + sock->acks_after_loss = 0; + sock->suspended_sqn = ack_rx_max; + sock->is_congested = TRUE; + sock->cwnd_size = pgm_fp8div (sock->cwnd_size, pgm_fp8 (2)); + if (sock->cwnd_size > sock->tokens) + sock->tokens = 0; + else + sock->tokens -= sock->cwnd_size; + sock->ack_bitmap = 0xffffffff; + pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("PGMCC congestion, half window size (T:%u W:%u)"), + pgm_fp8tou (sock->tokens), pgm_fp8tou (sock->cwnd_size)); + } + } + +/* token is now available so notify tx thread that transmission time is available */ +notify_tx: + if (is_congestion_limited && + sock->tokens >= pgm_fp8 (1)) + { + pgm_notify_send (&sock->ack_notify); + } + return TRUE; +} + +/* ambient/heartbeat SPM's + * + * heartbeat: ihb_tmr decaying between ihb_min and ihb_max 2x after last packet + * + * on success, TRUE is returned, if operation would block, FALSE is returned. + */ + +bool +pgm_send_spm ( + pgm_sock_t* const sock, + const int flags + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != sock->window); + + pgm_debug ("pgm_send_spm (sock:%p flags:%d)", + (const void*)sock, flags); + + size_t tpdu_length = sizeof(struct pgm_header); + if (AF_INET == sock->send_gsr.gsr_group.ss_family) + tpdu_length += sizeof(struct pgm_spm); + else + tpdu_length += sizeof(struct pgm_spm6); + if (sock->use_proactive_parity || + sock->use_ondemand_parity || + sock->is_pending_crqst || + PGM_OPT_FIN == flags) + { + tpdu_length += sizeof(struct pgm_opt_length); +/* forward error correction */ + if (sock->use_proactive_parity || + sock->use_ondemand_parity) + tpdu_length += sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_parity_prm); +/* congestion report request */ + if (sock->is_pending_crqst) + tpdu_length += sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_crqst); +/* end of session */ + if (PGM_OPT_FIN == flags) + tpdu_length += sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fin); + } + char buf[ tpdu_length ]; + if (PGM_UNLIKELY(pgm_mem_gc_friendly)) + memset (buf, 0, tpdu_length); + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_spm* spm = (struct pgm_spm *)(header + 1); + struct pgm_spm6* spm6 = (struct pgm_spm6*)(header + 1); + memcpy (header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + header->pgm_sport = sock->tsi.sport; + header->pgm_dport = sock->dport; + header->pgm_type = PGM_SPM; + header->pgm_options = 0; + header->pgm_tsdu_length = 0; + +/* SPM */ + spm->spm_sqn = htonl (sock->spm_sqn); + spm->spm_trail = htonl (pgm_txw_trail_atomic (sock->window)); + spm->spm_lead = htonl (pgm_txw_lead_atomic (sock->window)); + spm->spm_reserved = 0; +/* our nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&sock->send_addr, (char*)&spm->spm_nla_afi); + +/* PGM options */ + if (sock->use_proactive_parity || + sock->use_ondemand_parity || + sock->is_pending_crqst || + PGM_OPT_FIN == flags) + { + struct pgm_opt_length* opt_len; + struct pgm_opt_header *opt_header, *last_opt_header; + uint16_t opt_total_length; + + if (AF_INET == sock->send_gsr.gsr_group.ss_family) + opt_header = (struct pgm_opt_header*)(spm + 1); + else + opt_header = (struct pgm_opt_header*)(spm6 + 1); + header->pgm_options |= PGM_OPT_PRESENT; + opt_len = (struct pgm_opt_length*)opt_header; + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_total_length = sizeof(struct pgm_opt_length); + last_opt_header = opt_header = (struct pgm_opt_header*)(opt_len + 1); + +/* OPT_PARITY_PRM */ + if (sock->use_proactive_parity || + sock->use_ondemand_parity) + { + header->pgm_options |= PGM_OPT_NETWORK; + opt_total_length += sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_parity_prm); + opt_header->opt_type = PGM_OPT_PARITY_PRM; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_parity_prm); + struct pgm_opt_parity_prm* opt_parity_prm = (struct pgm_opt_parity_prm*)(opt_header + 1); + opt_parity_prm->opt_reserved = (sock->use_proactive_parity ? PGM_PARITY_PRM_PRO : 0) | + (sock->use_ondemand_parity ? PGM_PARITY_PRM_OND : 0); + opt_parity_prm->parity_prm_tgs = htonl (sock->rs_k); + last_opt_header = opt_header; + opt_header = (struct pgm_opt_header*)(opt_parity_prm + 1); + } + +/* OPT_CRQST */ + if (sock->is_pending_crqst) + { + header->pgm_options |= PGM_OPT_NETWORK; + opt_total_length += sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_crqst); + opt_header->opt_type = PGM_OPT_CRQST; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_crqst); + struct pgm_opt_crqst* opt_crqst = (struct pgm_opt_crqst*)(opt_header + 1); +/* request receiver worst path report, OPT_CR_RX_WP */ + opt_crqst->opt_reserved = PGM_OPT_CRQST_RXP; + sock->is_pending_crqst = FALSE; + last_opt_header = opt_header; + opt_header = (struct pgm_opt_header*)(opt_crqst + 1); + } + +/* OPT_FIN */ + if (PGM_OPT_FIN == flags) + { + opt_total_length += sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fin); + opt_header->opt_type = PGM_OPT_FIN; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_fin); + struct pgm_opt_fin* opt_fin = (struct pgm_opt_fin*)(opt_header + 1); + opt_fin->opt_reserved = 0; + last_opt_header = opt_header; + opt_header = (struct pgm_opt_header*)(opt_fin + 1); + } + + last_opt_header->opt_type |= PGM_OPT_END; + opt_len->opt_total_length = htons (opt_total_length); + } + +/* checksum optional for SPMs */ + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + + const ssize_t sent = pgm_sendto (sock, + flags != PGM_OPT_SYN && sock->is_controlled_spm, /* rate limited */ + TRUE, /* with router alert */ + buf, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { + sock->blocklen = tpdu_length; + return FALSE; + } +/* advance SPM sequence only on successful transmission */ + sock->spm_sqn++; + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length); + return TRUE; +} + +/* send a NAK confirm (NCF) message with provided sequence number list. + * + * on success, TRUE is returned, returns FALSE if operation would block. + */ + +static +bool +send_ncf ( + pgm_sock_t* const restrict sock, + const struct sockaddr* const restrict nak_src_nla, + const struct sockaddr* const restrict nak_grp_nla, + const uint32_t sequence, + const bool is_parity /* send parity NCF */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != nak_src_nla); + pgm_assert (NULL != nak_grp_nla); + pgm_assert (nak_src_nla->sa_family == nak_grp_nla->sa_family); + +#ifdef SOURCE_DEBUG + char saddr[INET6_ADDRSTRLEN], gaddr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop (nak_src_nla, saddr, sizeof(saddr)); + pgm_sockaddr_ntop (nak_grp_nla, gaddr, sizeof(gaddr)); + pgm_debug ("send_ncf (sock:%p nak-src-nla:%s nak-grp-nla:%s sequence:%" PRIu32" is-parity:%s)", + (void*)sock, + saddr, + gaddr, + sequence, + is_parity ? "TRUE": "FALSE" + ); +#endif + + size_t tpdu_length = sizeof(struct pgm_header); + tpdu_length += (AF_INET == nak_src_nla->sa_family) ? sizeof(struct pgm_nak) : sizeof(struct pgm_nak6); + char buf[ tpdu_length ]; + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_nak* ncf = (struct pgm_nak *)(header + 1); + struct pgm_nak6* ncf6 = (struct pgm_nak6*)(header + 1); + memcpy (header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + header->pgm_sport = sock->tsi.sport; + header->pgm_dport = sock->dport; + header->pgm_type = PGM_NCF; + header->pgm_options = is_parity ? PGM_OPT_PARITY : 0; + header->pgm_tsdu_length = 0; + +/* NCF */ + ncf->nak_sqn = htonl (sequence); + +/* source nla */ + pgm_sockaddr_to_nla (nak_src_nla, (char*)&ncf->nak_src_nla_afi); + +/* group nla */ + pgm_sockaddr_to_nla (nak_grp_nla, (AF_INET6 == nak_src_nla->sa_family) ? (char*)&ncf6->nak6_grp_nla_afi : (char*)&ncf->nak_grp_nla_afi ); + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + + const ssize_t sent = pgm_sendto (sock, + FALSE, /* not rate limited */ + TRUE, /* with router alert */ + buf, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) + return FALSE; + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length); + return TRUE; +} + +/* A NCF packet with a OPT_NAK_LIST option extension + * + * on success, TRUE is returned. on error, FALSE is returned. + */ + +static +bool +send_ncf_list ( + pgm_sock_t* const restrict sock, + const struct sockaddr* const restrict nak_src_nla, + const struct sockaddr* const restrict nak_grp_nla, + struct pgm_sqn_list_t* const restrict sqn_list, /* will change to network-order */ + const bool is_parity /* send parity NCF */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != nak_src_nla); + pgm_assert (NULL != nak_grp_nla); + pgm_assert (sqn_list->len > 1); + pgm_assert (sqn_list->len <= 63); + pgm_assert (nak_src_nla->sa_family == nak_grp_nla->sa_family); + +#ifdef SOURCE_DEBUG + char saddr[INET6_ADDRSTRLEN], gaddr[INET6_ADDRSTRLEN]; + char list[1024]; + pgm_sockaddr_ntop (nak_src_nla, saddr, sizeof(saddr)); + pgm_sockaddr_ntop (nak_grp_nla, gaddr, sizeof(gaddr)); + sprintf (list, "%" PRIu32, sqn_list->sqn[0]); + for (uint_fast8_t i = 1; i < sqn_list->len; i++) { + char sequence[ 2 + strlen("4294967295") ]; + sprintf (sequence, " %" PRIu32, sqn_list->sqn[i]); + strcat (list, sequence); + } + pgm_debug ("send_ncf_list (sock:%p nak-src-nla:%s nak-grp-nla:%s sqn-list:[%s] is-parity:%s)", + (void*)sock, + saddr, + gaddr, + list, + is_parity ? "TRUE": "FALSE" + ); +#endif + + size_t tpdu_length = sizeof(struct pgm_header) + + sizeof(struct pgm_opt_length) + /* includes header */ + sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(uint32_t) ); + tpdu_length += (AF_INET == nak_src_nla->sa_family) ? sizeof(struct pgm_nak) : sizeof(struct pgm_nak6); + char buf[ tpdu_length ]; + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_nak* ncf = (struct pgm_nak *)(header + 1); + struct pgm_nak6* ncf6 = (struct pgm_nak6*)(header + 1); + memcpy (header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + header->pgm_sport = sock->tsi.sport; + header->pgm_dport = sock->dport; + header->pgm_type = PGM_NCF; + header->pgm_options = is_parity ? (PGM_OPT_PRESENT | PGM_OPT_NETWORK | PGM_OPT_PARITY) : (PGM_OPT_PRESENT | PGM_OPT_NETWORK); + header->pgm_tsdu_length = 0; +/* NCF */ + ncf->nak_sqn = htonl (sqn_list->sqn[0]); + +/* source nla */ + pgm_sockaddr_to_nla (nak_src_nla, (char*)&ncf->nak_src_nla_afi); + +/* group nla */ + pgm_sockaddr_to_nla (nak_grp_nla, (AF_INET6 == nak_src_nla->sa_family) ? (char*)&ncf6->nak6_grp_nla_afi : (char*)&ncf->nak_grp_nla_afi ); + +/* OPT_NAK_LIST */ + struct pgm_opt_length* opt_len = (AF_INET6 == nak_src_nla->sa_family) ? (struct pgm_opt_length*)(ncf6 + 1) : (struct pgm_opt_length*)(ncf + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(uint32_t) ) ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_NAK_LIST | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(uint32_t) ); + struct pgm_opt_nak_list* opt_nak_list = (struct pgm_opt_nak_list*)(opt_header + 1); + opt_nak_list->opt_reserved = 0; +/* to network-order */ + for (uint_fast8_t i = 1; i < sqn_list->len; i++) + opt_nak_list->opt_sqn[i-1] = htonl (sqn_list->sqn[i]); + + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + + const ssize_t sent = pgm_sendto (sock, + FALSE, /* not rate limited */ + TRUE, /* with router alert */ + buf, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) + return FALSE; + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length); + return TRUE; +} + +/* cancel any pending heartbeat SPM and schedule a new one + */ + +static +void +reset_heartbeat_spm ( + pgm_sock_t*const sock, + const pgm_time_t now + ) +{ + pgm_mutex_lock (&sock->timer_mutex); + const pgm_time_t next_poll = sock->next_poll; + const pgm_time_t spm_heartbeat_interval = sock->spm_heartbeat_interval[ sock->spm_heartbeat_state = 1 ]; + sock->next_heartbeat_spm = now + spm_heartbeat_interval; + if (pgm_time_after( next_poll, sock->next_heartbeat_spm )) + { + sock->next_poll = sock->next_heartbeat_spm; + if (!sock->is_pending_read) { + pgm_notify_send (&sock->pending_notify); + sock->is_pending_read = TRUE; + } + } + pgm_mutex_unlock (&sock->timer_mutex); +} + +/* state helper for resuming sends + */ +#define STATE(x) (sock->pkt_dontwait_state.x) + +/* send one PGM data packet, transmit window owned memory. + * + * On success, returns PGM_IO_STATUS_NORMAL and the number of data bytes pushed + * into the transmit window and attempted to send to the socket layer is saved + * into bytes_written. On non-blocking sockets, PGM_IO_STATUS_WOULD_BLOCK is + * returned if the send would block. PGM_IO_STATUS_RATE_LIMITED is returned if + * the packet sizes would exceed the current rate limit. + * + * ! always returns successful if data is pushed into the transmit window, even if + * sendto() double fails ¡ we don't want the application to try again as that is the + * reliable socks role. + */ + +static +int +send_odata ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t* const restrict skb, + size_t* restrict bytes_written + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + pgm_assert (skb->len <= sock->max_tsdu); + + pgm_debug ("send_odata (sock:%p skb:%p bytes-written:%p)", + (void*)sock, (void*)skb, (void*)bytes_written); + + const uint16_t tsdu_length = skb->len; + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + const size_t tpdu_length = tsdu_length + pgm_pkt_offset (FALSE, pgmcc_family); + +/* continue if send would block */ + if (sock->is_apdu_eagain) { + STATE(skb)->tstamp = pgm_time_update_now(); + goto retry_send; + } + +/* add PGM header to skbuff */ + STATE(skb) = pgm_skb_get(skb); + STATE(skb)->sock = sock; + STATE(skb)->tstamp = pgm_time_update_now(); + + STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->head; + STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); + memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; + STATE(skb)->pgm_header->pgm_dport = sock->dport; + STATE(skb)->pgm_header->pgm_type = PGM_ODATA; + STATE(skb)->pgm_header->pgm_options = sock->use_pgmcc ? PGM_OPT_PRESENT : 0; + STATE(skb)->pgm_header->pgm_tsdu_length = htons (tsdu_length); + +/* ODATA */ + STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + + STATE(skb)->pgm_header->pgm_checksum = 0; + void* data = STATE(skb)->pgm_data + 1; + if (sock->use_pgmcc) { + struct pgm_opt_length* opt_len = data; + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + ((AF_INET6 == sock->acker_nla.ss_family) ? + sizeof(struct pgm_opt6_pgmcc_data) : + sizeof(struct pgm_opt_pgmcc_data)) ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_PGMCC_DATA | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + ((AF_INET6 == sock->acker_nla.ss_family) ? + sizeof(struct pgm_opt6_pgmcc_data) : + sizeof(struct pgm_opt_pgmcc_data)); + struct pgm_opt_pgmcc_data* pgmcc_data = (struct pgm_opt_pgmcc_data*)(opt_header + 1); + struct pgm_opt6_pgmcc_data* pgmcc_data6 = (struct pgm_opt6_pgmcc_data*)(opt_header + 1); + + pgmcc_data->opt_tstamp = htonl (pgm_to_msecs (STATE(skb)->tstamp)); +/* acker nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&sock->acker_nla, (char*)&pgmcc_data->opt_nla_afi); + if (AF_INET6 == sock->acker_nla.ss_family) + data = (char*)pgmcc_data6 + sizeof(struct pgm_opt6_pgmcc_data); + else + data = (char*)pgmcc_data + sizeof(struct pgm_opt_pgmcc_data); + } + const size_t pgm_header_len = (char*)data - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + STATE(unfolded_odata) = pgm_csum_partial (data, tsdu_length, 0); + STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); + +/* add to transmit window, skb::data set to payload */ + pgm_spinlock_lock (&sock->txw_spinlock); + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); + +/* the transmit window MUST check the user count to ensure it does not + * attempt to send a repair-data packet based on in transit original data. + */ + + ssize_t sent; +retry_send: + +/* congestion control */ + if (sock->use_pgmcc && + sock->tokens < pgm_fp8 (1)) + { +// pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Token limit reached.")); + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + return PGM_IO_STATUS_CONGESTION; /* peer expiration to re-elect ACKer */ + } + + sent = pgm_sendto (sock, + sock->is_controlled_odata, /* rate limited */ + FALSE, /* regular socket */ + STATE(skb)->head, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + if (EAGAIN == errno) { + if (sock->use_pgmcc) + pgm_notify_clear (&sock->ack_notify); + return PGM_IO_STATUS_WOULD_BLOCK; + } + return PGM_IO_STATUS_RATE_LIMITED; + } + +/* save unfolded odata for retransmissions */ + pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); + + sock->is_apdu_eagain = FALSE; + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + if (sock->use_pgmcc) { + sock->tokens -= pgm_fp8 (1); + sock->ack_expiry = STATE(skb)->tstamp + sock->ack_expiry_ivl; + } + + if (PGM_LIKELY((size_t)sent == tpdu_length)) { + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += tsdu_length; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] ++; + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length + sock->iphdr_len); + } + +/* check for end of transmission group */ + if (sock->use_proactive_parity) { + const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); + const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; + if (!((odata_sqn + 1) & ~tg_sqn_mask)) + pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); + } + +/* remove applications reference to skbuff */ + pgm_free_skb (STATE(skb)); + if (bytes_written) + *bytes_written = tsdu_length; + return PGM_IO_STATUS_NORMAL; +} + +/* send one PGM original data packet, callee owned memory. + * + * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets + * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if + * packet size exceeds the current rate limit. + */ + +static +int +send_odata_copy ( + pgm_sock_t* const restrict sock, + const void* restrict tsdu, + const uint16_t tsdu_length, + size_t* restrict bytes_written + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (tsdu_length <= sock->max_tsdu); + if (PGM_LIKELY(tsdu_length)) pgm_assert (NULL != tsdu); + + pgm_debug ("send_odata_copy (sock:%p tsdu:%p tsdu_length:%u bytes-written:%p)", + (void*)sock, tsdu, tsdu_length, (void*)bytes_written); + + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + const size_t tpdu_length = tsdu_length + pgm_pkt_offset (FALSE, pgmcc_family); + +/* continue if blocked mid-apdu, updating timestamp */ + if (sock->is_apdu_eagain) { + STATE(skb)->tstamp = pgm_time_update_now(); + goto retry_send; + } + + STATE(skb) = pgm_alloc_skb (sock->max_tpdu); + STATE(skb)->sock = sock; + STATE(skb)->tstamp = pgm_time_update_now(); + pgm_skb_reserve (STATE(skb), pgm_pkt_offset (FALSE, pgmcc_family)); + pgm_skb_put (STATE(skb), tsdu_length); + + STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->head; + STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); + memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; + STATE(skb)->pgm_header->pgm_dport = sock->dport; + STATE(skb)->pgm_header->pgm_type = PGM_ODATA; + STATE(skb)->pgm_header->pgm_options = sock->use_pgmcc ? PGM_OPT_PRESENT : 0; + STATE(skb)->pgm_header->pgm_tsdu_length = htons (tsdu_length); + +/* ODATA */ + STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + + STATE(skb)->pgm_header->pgm_checksum = 0; + void* data = STATE(skb)->pgm_data + 1; + if (sock->use_pgmcc) { + struct pgm_opt_length* opt_len = data; + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + ((AF_INET6 == sock->acker_nla.ss_family) ? + sizeof(struct pgm_opt6_pgmcc_data) : + sizeof(struct pgm_opt_pgmcc_data)) ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_PGMCC_DATA | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + ((AF_INET6 == sock->acker_nla.ss_family) ? + sizeof(struct pgm_opt6_pgmcc_data) : + sizeof(struct pgm_opt_pgmcc_data)); + struct pgm_opt_pgmcc_data* pgmcc_data = (struct pgm_opt_pgmcc_data*)(opt_header + 1); +/* unused */ +// struct pgm_opt6_pgmcc_data* pgmcc_data6 = (struct pgm_opt6_pgmcc_data*)(opt_header + 1); + + pgmcc_data->opt_reserved = 0; + pgmcc_data->opt_tstamp = htonl (pgm_to_msecs (STATE(skb)->tstamp)); +/* acker nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&sock->acker_nla, (char*)&pgmcc_data->opt_nla_afi); + data = (char*)opt_header + opt_header->opt_length; + } + const size_t pgm_header_len = (char*)data - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + STATE(unfolded_odata) = pgm_csum_partial_copy (tsdu, data, tsdu_length, 0); + STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); + +/* add to transmit window, skb::data set to payload */ + pgm_spinlock_lock (&sock->txw_spinlock); + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); + + ssize_t sent; +retry_send: + +/* congestion control */ + if (sock->use_pgmcc && + sock->tokens < pgm_fp8 (1)) + { +// pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Token limit reached.")); + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + return PGM_IO_STATUS_CONGESTION; + } + + sent = pgm_sendto (sock, + sock->is_controlled_odata, /* rate limited */ + FALSE, /* regular socket */ + STATE(skb)->head, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + if (EAGAIN == errno) { + if (sock->use_pgmcc) + pgm_notify_clear (&sock->ack_notify); + return PGM_IO_STATUS_WOULD_BLOCK; + } + return PGM_IO_STATUS_RATE_LIMITED; + } + + if (sock->use_pgmcc) { + sock->tokens -= pgm_fp8 (1); + pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("PGMCC tokens-- (T:%u W:%u)"), + pgm_fp8tou (sock->tokens), pgm_fp8tou (sock->cwnd_size)); + sock->ack_expiry = STATE(skb)->tstamp + sock->ack_expiry_ivl; + } + +/* save unfolded odata for retransmissions */ + pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); + + sock->is_apdu_eagain = FALSE; + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + + if (PGM_LIKELY((size_t)sent == tpdu_length)) { + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += tsdu_length; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] ++; + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length + sock->iphdr_len); + } + +/* check for end of transmission group */ + if (sock->use_proactive_parity) { + const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); + const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; + if (!((odata_sqn + 1) & ~tg_sqn_mask)) + pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); + } + +/* return data payload length sent */ + if (bytes_written) + *bytes_written = tsdu_length; + return PGM_IO_STATUS_NORMAL; +} + +/* send one PGM original data packet, callee owned scatter/gather io vector + * + * ⎢ DATA₀ ⎢ + * ⎢ DATA₁ ⎢ → send_odatav() → ⎢ TSDU₀ ⎢ → libc + * ⎢ ⋮ ⎢ + * + * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets + * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if + * packet size exceeds the current rate limit. + */ + +static +int +send_odatav ( + pgm_sock_t* const restrict sock, + const struct pgm_iovec* const restrict vector, + const unsigned count, /* number of items in vector */ + size_t* restrict bytes_written + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (count <= PGM_MAX_FRAGMENTS); + if (PGM_LIKELY(count)) pgm_assert (NULL != vector); + + pgm_debug ("send_odatav (sock:%p vector:%p count:%u bytes-written:%p)", + (const void*)sock, (const void*)vector, count, (const void*)bytes_written); + + if (PGM_UNLIKELY(0 == count)) + return send_odata_copy (sock, NULL, 0, bytes_written); + +/* continue if blocked on send */ + if (sock->is_apdu_eagain) + goto retry_send; + + STATE(tsdu_length) = 0; + for (unsigned i = 0; i < count; i++) + { +#ifdef TRANSPORT_DEBUG + if (PGM_LIKELY(vector[i].iov_len)) { + pgm_assert( vector[i].iov_base ); + } +#endif + STATE(tsdu_length) += vector[i].iov_len; + } + pgm_return_val_if_fail (STATE(tsdu_length) <= sock->max_tsdu, PGM_IO_STATUS_ERROR); + + STATE(skb) = pgm_alloc_skb (sock->max_tpdu); + STATE(skb)->sock = sock; + STATE(skb)->tstamp = pgm_time_update_now(); + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + pgm_skb_reserve (STATE(skb), pgm_pkt_offset (FALSE, pgmcc_family)); + pgm_skb_put (STATE(skb), STATE(tsdu_length)); + + STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->data; + STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); + memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; + STATE(skb)->pgm_header->pgm_dport = sock->dport; + STATE(skb)->pgm_header->pgm_type = PGM_ODATA; + STATE(skb)->pgm_header->pgm_options = 0; + STATE(skb)->pgm_header->pgm_tsdu_length = htons (STATE(tsdu_length)); + +/* ODATA */ + STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + + STATE(skb)->pgm_header->pgm_checksum = 0; + const size_t pgm_header_len = (char*)(STATE(skb)->pgm_data + 1) - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + +/* unroll first iteration to make friendly branch prediction */ + char* dst = (char*)(STATE(skb)->pgm_data + 1); + STATE(unfolded_odata) = pgm_csum_partial_copy ((const char*)vector[0].iov_base, dst, vector[0].iov_len, 0); + +/* iterate over one or more vector elements to perform scatter/gather checksum & copy */ + for (unsigned i = 1; i < count; i++) { + dst += vector[i-1].iov_len; + const uint32_t unfolded_element = pgm_csum_partial_copy ((const char*)vector[i].iov_base, dst, vector[i].iov_len, 0); + STATE(unfolded_odata) = pgm_csum_block_add (STATE(unfolded_odata), unfolded_element, vector[i-1].iov_len); + } + + STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); + +/* add to transmit window, skb::data set to payload */ + pgm_spinlock_lock (&sock->txw_spinlock); + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); + + ssize_t sent; + size_t tpdu_length; +retry_send: + pgm_assert ((char*)STATE(skb)->tail > (char*)STATE(skb)->head); + tpdu_length = (char*)STATE(skb)->tail - (char*)STATE(skb)->head; + sent = pgm_sendto (sock, + sock->is_controlled_odata, /* rate limited */ + FALSE, /* regular socket */ + STATE(skb)->head, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + if (EAGAIN == errno) { + if (sock->use_pgmcc) + pgm_notify_clear (&sock->ack_notify); + return PGM_IO_STATUS_WOULD_BLOCK; + } + return PGM_IO_STATUS_RATE_LIMITED; + } + +/* save unfolded odata for retransmissions */ + pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); + + sock->is_apdu_eagain = FALSE; + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + + if (PGM_LIKELY((size_t)sent == STATE(skb)->len)) { + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += STATE(tsdu_length); + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] ++; + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length + sock->iphdr_len); + } + +/* check for end of transmission group */ + if (sock->use_proactive_parity) { + const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); + const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; + if (!((odata_sqn + 1) & ~tg_sqn_mask)) + pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); + } + +/* return data payload length sent */ + if (bytes_written) + *bytes_written = STATE(tsdu_length); + return PGM_IO_STATUS_NORMAL; +} + +/* send PGM original data, callee owned memory. if larger than maximum TPDU + * size will be fragmented. + * + * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets + * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if + * packet size exceeds the current rate limit. + */ + +static +int +send_apdu ( + pgm_sock_t* const restrict sock, + const void* restrict apdu, + const size_t apdu_length, + size_t* restrict bytes_written + ) +{ + size_t bytes_sent = 0; /* counted at IP layer */ + unsigned packets_sent = 0; /* IP packets */ + size_t data_bytes_sent = 0; + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + + pgm_assert (NULL != sock); + pgm_assert (NULL != apdu); + +/* continue if blocked mid-apdu */ + if (sock->is_apdu_eagain) + goto retry_send; + +/* if non-blocking calculate total wire size and check rate limit */ + STATE(is_rate_limited) = FALSE; + if (sock->is_nonblocking && sock->is_controlled_odata) + { + const size_t header_length = pgm_pkt_offset (TRUE, pgmcc_family); + size_t tpdu_length = 0; + size_t offset_ = 0; + do { + const uint16_t tsdu_length = MIN( source_max_tsdu (sock, TRUE), apdu_length - offset_ ); + tpdu_length += sock->iphdr_len + header_length + tsdu_length; + offset_ += tsdu_length; + } while (offset_ < apdu_length); + +/* calculation includes one iphdr length already */ + if (!pgm_rate_check (&sock->rate_control, + tpdu_length - sock->iphdr_len, + sock->is_nonblocking)) + { + sock->blocklen = tpdu_length; + return PGM_IO_STATUS_RATE_LIMITED; + } + STATE(is_rate_limited) = TRUE; + } + + STATE(data_bytes_offset) = 0; + STATE(first_sqn) = pgm_txw_next_lead(sock->window); + + do { +/* retrieve packet storage from transmit window */ + size_t header_length = pgm_pkt_offset (TRUE, pgmcc_family); + STATE(tsdu_length) = MIN( source_max_tsdu (sock, TRUE), apdu_length - STATE(data_bytes_offset) ); + + STATE(skb) = pgm_alloc_skb (sock->max_tpdu); + STATE(skb)->sock = sock; + STATE(skb)->tstamp = pgm_time_update_now(); + pgm_skb_reserve (STATE(skb), header_length); + pgm_skb_put (STATE(skb), STATE(tsdu_length)); + + STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->head; + STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); + memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; + STATE(skb)->pgm_header->pgm_dport = sock->dport; + STATE(skb)->pgm_header->pgm_type = PGM_ODATA; + STATE(skb)->pgm_header->pgm_options = PGM_OPT_PRESENT; + STATE(skb)->pgm_header->pgm_tsdu_length = htons (STATE(tsdu_length)); + +/* ODATA */ + STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + +/* OPT_LENGTH */ + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(STATE(skb)->pgm_data + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment) ); +/* OPT_FRAGMENT */ + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_FRAGMENT | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment); + STATE(skb)->pgm_opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); + STATE(skb)->pgm_opt_fragment->opt_reserved = 0; + STATE(skb)->pgm_opt_fragment->opt_sqn = htonl (STATE(first_sqn)); + STATE(skb)->pgm_opt_fragment->opt_frag_off = htonl (STATE(data_bytes_offset)); + STATE(skb)->pgm_opt_fragment->opt_frag_len = htonl (apdu_length); + +/* TODO: the assembly checksum & copy routine is faster than memcpy & pgm_cksum on >= opteron hardware */ + STATE(skb)->pgm_header->pgm_checksum = 0; + const size_t pgm_header_len = (char*)(STATE(skb)->pgm_opt_fragment + 1) - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + STATE(unfolded_odata) = pgm_csum_partial_copy ((const char*)apdu + STATE(data_bytes_offset), STATE(skb)->pgm_opt_fragment + 1, STATE(tsdu_length), 0); + STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); + +/* add to transmit window, skb::data set to payload */ + pgm_spinlock_lock (&sock->txw_spinlock); + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); + + ssize_t sent; + size_t tpdu_length; +retry_send: + pgm_assert ((char*)STATE(skb)->tail > (char*)STATE(skb)->head); + tpdu_length = (char*)STATE(skb)->tail - (char*)STATE(skb)->head; + sent = pgm_sendto (sock, + !STATE(is_rate_limited), /* rate limit on blocking */ + FALSE, /* regular socket */ + STATE(skb)->head, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + goto blocked; + } + +/* save unfolded odata for retransmissions */ + pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); + + if (PGM_LIKELY((size_t)sent == tpdu_length)) { + bytes_sent += tpdu_length + sock->iphdr_len; /* as counted at IP layer */ + packets_sent++; /* IP packets */ + data_bytes_sent += STATE(tsdu_length); + } + + STATE(data_bytes_offset) += STATE(tsdu_length); + +/* check for end of transmission group */ + if (sock->use_proactive_parity) { + const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); + const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; + if (!((odata_sqn + 1) & ~tg_sqn_mask)) + pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); + } + + } while ( STATE(data_bytes_offset) < apdu_length); + pgm_assert( STATE(data_bytes_offset) == apdu_length ); + + sock->is_apdu_eagain = FALSE; + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; + if (bytes_written) + *bytes_written = apdu_length; + return PGM_IO_STATUS_NORMAL; + +blocked: + if (bytes_sent) { + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; + } + if (EAGAIN == errno) { + if (sock->use_pgmcc) + pgm_notify_clear (&sock->ack_notify); + return PGM_IO_STATUS_WOULD_BLOCK; + } + return PGM_IO_STATUS_RATE_LIMITED; +} + +/* Send one APDU, whether it fits within one TPDU or more. + * + * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets + * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if + * packet size exceeds the current rate limit. + */ +int +pgm_send ( + pgm_sock_t* const restrict sock, + const void* restrict apdu, + const size_t apdu_length, + size_t* restrict bytes_written + ) +{ + pgm_debug ("pgm_send (sock:%p apdu:%p apdu-length:%zu bytes-written:%p)", + (void*)sock, apdu, apdu_length, (void*)bytes_written); + +/* parameters */ + pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); + if (PGM_LIKELY(apdu_length)) pgm_return_val_if_fail (NULL != apdu, PGM_IO_STATUS_ERROR); + +/* shutdown */ + if (PGM_UNLIKELY(!pgm_rwlock_reader_trylock (&sock->lock))) + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + +/* state */ + if (PGM_UNLIKELY(!sock->is_bound || + sock->is_destroyed || + apdu_length > sock->max_apdu)) + { + pgm_rwlock_reader_unlock (&sock->lock); + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + } + +/* source */ + pgm_mutex_lock (&sock->source_mutex); + +/* pass on non-fragment calls */ + if (apdu_length <= sock->max_tsdu) + { + const int status = send_odata_copy (sock, apdu, apdu_length, bytes_written); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + } + + const int status = send_apdu (sock, apdu, apdu_length, bytes_written); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; +} + +/* send PGM original data, callee owned scatter/gather IO vector. if larger than maximum TPDU + * size will be fragmented. + * + * is_one_apdu = true: + * + * ⎢ DATA₀ ⎢ + * ⎢ DATA₁ ⎢ → pgm_sendv() → ⎢ ⋯ TSDU₁ TSDU₀ ⎢ → libc + * ⎢ ⋮ ⎢ + * + * is_one_apdu = false: + * + * ⎢ APDU₀ ⎢ ⎢ ⋯ TSDU₁,₀ TSDU₀,₀ ⎢ + * ⎢ APDU₁ ⎢ → pgm_sendv() → ⎢ ⋯ TSDU₁,₁ TSDU₀,₁ ⎢ → libc + * ⎢ ⋮ ⎢ ⎢ ⋮ ⋮ ⎢ + * + * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets + * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if + * packet size exceeds the current rate limit. + */ + +int +pgm_sendv ( + pgm_sock_t* const restrict sock, + const struct pgm_iovec* const restrict vector, + const unsigned count, /* number of items in vector */ + const bool is_one_apdu, /* true = vector = apdu, false = vector::iov_base = apdu */ + size_t* restrict bytes_written + ) +{ + pgm_debug ("pgm_sendv (sock:%p vector:%p count:%u is-one-apdu:%s bytes-written:%p)", + (const void*)sock, + (const void*)vector, + count, + is_one_apdu ? "TRUE" : "FALSE", + (const void*)bytes_written); + + pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); + pgm_return_val_if_fail (count <= PGM_MAX_FRAGMENTS, PGM_IO_STATUS_ERROR); + if (PGM_LIKELY(count)) pgm_return_val_if_fail (NULL != vector, PGM_IO_STATUS_ERROR); + if (PGM_UNLIKELY(!pgm_rwlock_reader_trylock (&sock->lock))) + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + if (PGM_UNLIKELY(!sock->is_bound || + sock->is_destroyed)) + { + pgm_rwlock_reader_unlock (&sock->lock); + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + } + + pgm_mutex_lock (&sock->source_mutex); + +/* pass on zero length as cannot count vector lengths */ + if (PGM_UNLIKELY(0 == count)) + { + const int status = send_odata_copy (sock, NULL, count, bytes_written); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + } + + size_t bytes_sent = 0; + unsigned packets_sent = 0; + size_t data_bytes_sent = 0; + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + +/* continue if blocked mid-apdu */ + if (sock->is_apdu_eagain) { + if (is_one_apdu) { + if (STATE(apdu_length) <= sock->max_tsdu) + { + const int status = send_odatav (sock, vector, count, bytes_written); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + } + else + goto retry_one_apdu_send; + } else { + goto retry_send; + } + } + +/* calculate (total) APDU length */ + STATE(apdu_length) = 0; + for (unsigned i = 0; i < count; i++) + { +#ifdef TRANSPORT_DEBUG + if (PGM_LIKELY(vector[i].iov_len)) { + pgm_assert( vector[i].iov_base ); + } +#endif + if (!is_one_apdu && + vector[i].iov_len > sock->max_apdu) + { + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + } + STATE(apdu_length) += vector[i].iov_len; + } + +/* pass on non-fragment calls */ + if (is_one_apdu) { + if (STATE(apdu_length) <= sock->max_tsdu) { + const int status = send_odatav (sock, vector, count, bytes_written); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + } else if (STATE(apdu_length) > sock->max_apdu) { + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + } + } + +/* if non-blocking calculate total wire size and check rate limit */ + STATE(is_rate_limited) = FALSE; + if (sock->is_nonblocking && sock->is_controlled_odata) + { + const size_t header_length = pgm_pkt_offset (TRUE, pgmcc_family); + size_t tpdu_length = 0; + size_t offset_ = 0; + do { + const uint16_t tsdu_length = MIN( source_max_tsdu (sock, TRUE), STATE(apdu_length) - offset_ ); + tpdu_length += sock->iphdr_len + header_length + tsdu_length; + offset_ += tsdu_length; + } while (offset_ < STATE(apdu_length)); + +/* calculation includes one iphdr length already */ + if (!pgm_rate_check (&sock->rate_control, + tpdu_length - sock->iphdr_len, + sock->is_nonblocking)) + { + sock->blocklen = tpdu_length; + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_RATE_LIMITED; + } + STATE(is_rate_limited) = TRUE; + } + +/* non-fragmented packets can be forwarded onto basic send() */ + if (!is_one_apdu) + { + for (STATE(data_pkt_offset) = 0; STATE(data_pkt_offset) < count; STATE(data_pkt_offset)++) + { + size_t wrote_bytes; + int status; +retry_send: + status = send_apdu (sock, + vector[STATE(data_pkt_offset)].iov_base, + vector[STATE(data_pkt_offset)].iov_len, + &wrote_bytes); + switch (status) { + case PGM_IO_STATUS_NORMAL: + break; + case PGM_IO_STATUS_WOULD_BLOCK: + case PGM_IO_STATUS_RATE_LIMITED: + sock->is_apdu_eagain = TRUE; + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + case PGM_IO_STATUS_ERROR: + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + default: + pgm_assert_not_reached(); + } + data_bytes_sent += wrote_bytes; + } + + sock->is_apdu_eagain = FALSE; + if (bytes_written) + *bytes_written = data_bytes_sent; + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_NORMAL; + } + + STATE(data_bytes_offset) = 0; + STATE(vector_index) = 0; + STATE(vector_offset) = 0; + + STATE(first_sqn) = pgm_txw_next_lead(sock->window); + + do { +/* retrieve packet storage from transmit window */ + size_t header_length = pgm_pkt_offset (TRUE, pgmcc_family); + STATE(tsdu_length) = MIN( source_max_tsdu (sock, TRUE), STATE(apdu_length) - STATE(data_bytes_offset) ); + STATE(skb) = pgm_alloc_skb (sock->max_tpdu); + STATE(skb)->sock = sock; + STATE(skb)->tstamp = pgm_time_update_now(); + pgm_skb_reserve (STATE(skb), header_length); + pgm_skb_put (STATE(skb), STATE(tsdu_length)); + + STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->head; + STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); + memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; + STATE(skb)->pgm_header->pgm_dport = sock->dport; + STATE(skb)->pgm_header->pgm_type = PGM_ODATA; + STATE(skb)->pgm_header->pgm_options = PGM_OPT_PRESENT; + STATE(skb)->pgm_header->pgm_tsdu_length = htons (STATE(tsdu_length)); + +/* ODATA */ + STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + +/* OPT_LENGTH */ + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(STATE(skb)->pgm_data + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment) ); +/* OPT_FRAGMENT */ + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_FRAGMENT | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment); + STATE(skb)->pgm_opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); + STATE(skb)->pgm_opt_fragment->opt_reserved = 0; + STATE(skb)->pgm_opt_fragment->opt_sqn = htonl (STATE(first_sqn)); + STATE(skb)->pgm_opt_fragment->opt_frag_off = htonl (STATE(data_bytes_offset)); + STATE(skb)->pgm_opt_fragment->opt_frag_len = htonl (STATE(apdu_length)); + +/* checksum & copy */ + STATE(skb)->pgm_header->pgm_checksum = 0; + const size_t pgm_header_len = (char*)(STATE(skb)->pgm_opt_fragment + 1) - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + +/* iterate over one or more vector elements to perform scatter/gather checksum & copy + * + * STATE(vector_index) - index into application scatter/gather vector + * STATE(vector_offset) - current offset into current vector element + * STATE(unfolded_odata)- checksum accumulator + */ + const char* src = (const char*)vector[STATE(vector_index)].iov_base + STATE(vector_offset); + char* dst = (char*)(STATE(skb)->pgm_opt_fragment + 1); + size_t src_length = vector[STATE(vector_index)].iov_len - STATE(vector_offset); + size_t dst_length = 0; + size_t copy_length = MIN( STATE(tsdu_length), src_length ); + STATE(unfolded_odata) = pgm_csum_partial_copy (src, dst, copy_length, 0); + + for(;;) + { + if (copy_length == src_length) { +/* application packet complete */ + STATE(vector_index)++; + STATE(vector_offset) = 0; + } else { +/* data still remaining */ + STATE(vector_offset) += copy_length; + } + + dst_length += copy_length; + +/* sock packet complete */ + if (dst_length == STATE(tsdu_length)) + break; + + src = (const char*)vector[STATE(vector_index)].iov_base + STATE(vector_offset); + dst += copy_length; + src_length = vector[STATE(vector_index)].iov_len - STATE(vector_offset); + copy_length = MIN( STATE(tsdu_length) - dst_length, src_length ); + const uint32_t unfolded_element = pgm_csum_partial_copy (src, dst, copy_length, 0); + STATE(unfolded_odata) = pgm_csum_block_add (STATE(unfolded_odata), unfolded_element, dst_length); + } + + STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); + +/* add to transmit window, skb::data set to payload */ + pgm_spinlock_lock (&sock->txw_spinlock); + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); + + ssize_t sent; + size_t tpdu_length; +retry_one_apdu_send: + tpdu_length = (char*)STATE(skb)->tail - (char*)STATE(skb)->head; + sent = pgm_sendto (sock, + !STATE(is_rate_limited), /* rate limited on blocking */ + FALSE, /* regular socket */ + STATE(skb)->head, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + goto blocked; + } + +/* save unfolded odata for retransmissions */ + pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); + + if (PGM_LIKELY((size_t)sent == tpdu_length)) { + bytes_sent += tpdu_length + sock->iphdr_len; /* as counted at IP layer */ + packets_sent++; /* IP packets */ + data_bytes_sent += STATE(tsdu_length); + } + + STATE(data_bytes_offset) += STATE(tsdu_length); + +/* check for end of transmission group */ + if (sock->use_proactive_parity) { + const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); + const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; + if (!((odata_sqn + 1) & ~tg_sqn_mask)) + pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); + } + + } while ( STATE(data_bytes_offset) < STATE(apdu_length) ); + pgm_assert( STATE(data_bytes_offset) == STATE(apdu_length) ); + + sock->is_apdu_eagain = FALSE; + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; + if (bytes_written) + *bytes_written = STATE(apdu_length); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_NORMAL; + +blocked: + if (bytes_sent) { + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; + } + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + if (EAGAIN == errno) { + if (sock->use_pgmcc) + pgm_notify_clear (&sock->ack_notify); + return PGM_IO_STATUS_WOULD_BLOCK; + } + return PGM_IO_STATUS_RATE_LIMITED; +} + +/* send PGM original data, transmit window owned scatter/gather IO vector. + * + * ⎢ TSDU₀ ⎢ + * ⎢ TSDU₁ ⎢ → pgm_send_skbv() → ⎢ ⋯ TSDU₁ TSDU₀ ⎢ → libc + * ⎢ ⋮ ⎢ + * + * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets + * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if + * packet size exceeds the current rate limit. + */ + +int +pgm_send_skbv ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t** const restrict vector, /* array of skb pointers vs. array of skbs */ + const unsigned count, + const bool is_one_apdu, /* true: vector = apdu, false: vector::iov_base = apdu */ + size_t* restrict bytes_written + ) +{ + pgm_debug ("pgm_send_skbv (sock:%p vector:%p count:%u is-one-apdu:%s bytes-written:%p)", + (const void*)sock, + (const void*)vector, + count, + is_one_apdu ? "TRUE" : "FALSE", + (const void*)bytes_written); + + pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); + pgm_return_val_if_fail (count <= PGM_MAX_FRAGMENTS, PGM_IO_STATUS_ERROR); + if (PGM_LIKELY(count)) pgm_return_val_if_fail (NULL != vector, PGM_IO_STATUS_ERROR); + if (PGM_UNLIKELY(!pgm_rwlock_reader_trylock (&sock->lock))) + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + if (PGM_UNLIKELY(!sock->is_bound || + sock->is_destroyed)) + { + pgm_rwlock_reader_unlock (&sock->lock); + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + } + + pgm_mutex_lock (&sock->source_mutex); + +/* pass on zero length as cannot count vector lengths */ + if (PGM_UNLIKELY(0 == count)) + { + const int status = send_odata_copy (sock, NULL, count, bytes_written); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + } + else if (1 == count) + { + const int status = send_odata (sock, vector[0], bytes_written); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + } + + size_t bytes_sent = 0; + unsigned packets_sent = 0; + size_t data_bytes_sent = 0; + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + +/* continue if blocked mid-apdu */ + if (sock->is_apdu_eagain) + goto retry_send; + + STATE(is_rate_limited) = FALSE; + if (sock->is_nonblocking && sock->is_controlled_odata) + { + size_t total_tpdu_length = 0; + for (unsigned i = 0; i < count; i++) + total_tpdu_length += sock->iphdr_len + pgm_pkt_offset (is_one_apdu, pgmcc_family) + vector[i]->len; + +/* calculation includes one iphdr length already */ + if (!pgm_rate_check (&sock->rate_control, + total_tpdu_length - sock->iphdr_len, + sock->is_nonblocking)) + { + sock->blocklen = total_tpdu_length; + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_RATE_LIMITED; + } + STATE(is_rate_limited) = TRUE; + } + + if (is_one_apdu) + { + STATE(apdu_length) = 0; + STATE(first_sqn) = pgm_txw_next_lead(sock->window); + for (unsigned i = 0; i < count; i++) + { + if (PGM_UNLIKELY(vector[i]->len > sock->max_tsdu_fragment)) { + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_ERROR; + } + STATE(apdu_length) += vector[i]->len; + } + if (PGM_UNLIKELY(STATE(apdu_length) > sock->max_apdu)) { + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_ERROR; + } + } + + for (STATE(vector_index) = 0; STATE(vector_index) < count; STATE(vector_index)++) + { + STATE(tsdu_length) = vector[STATE(vector_index)]->len; + + STATE(skb) = pgm_skb_get(vector[STATE(vector_index)]); + STATE(skb)->sock = sock; + STATE(skb)->tstamp = pgm_time_update_now(); + + STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->head; + STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); + memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; + STATE(skb)->pgm_header->pgm_dport = sock->dport; + STATE(skb)->pgm_header->pgm_type = PGM_ODATA; + STATE(skb)->pgm_header->pgm_options = is_one_apdu ? PGM_OPT_PRESENT : 0; + STATE(skb)->pgm_header->pgm_tsdu_length = htons (STATE(tsdu_length)); + +/* ODATA */ + STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + + if (is_one_apdu) + { +/* OPT_LENGTH */ + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(STATE(skb)->pgm_data + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment) ); +/* OPT_FRAGMENT */ + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_FRAGMENT | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment); + STATE(skb)->pgm_opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); + STATE(skb)->pgm_opt_fragment->opt_reserved = 0; + STATE(skb)->pgm_opt_fragment->opt_sqn = htonl (STATE(first_sqn)); + STATE(skb)->pgm_opt_fragment->opt_frag_off = htonl (STATE(data_bytes_offset)); + STATE(skb)->pgm_opt_fragment->opt_frag_len = htonl (STATE(apdu_length)); + + pgm_assert (STATE(skb)->data == (STATE(skb)->pgm_opt_fragment + 1)); + } + else + { + pgm_assert (STATE(skb)->data == (STATE(skb)->pgm_data + 1)); + } + +/* TODO: the assembly checksum & copy routine is faster than memcpy & pgm_cksum on >= opteron hardware */ + STATE(skb)->pgm_header->pgm_checksum = 0; + pgm_assert ((char*)STATE(skb)->data > (char*)STATE(skb)->pgm_header); + const size_t pgm_header_len = (char*)STATE(skb)->data - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + STATE(unfolded_odata) = pgm_csum_partial ((char*)STATE(skb)->data, STATE(tsdu_length), 0); + STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); + +/* add to transmit window, skb::data set to payload */ + pgm_spinlock_lock (&sock->txw_spinlock); + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); + ssize_t sent; + size_t tpdu_length; +retry_send: + pgm_assert ((char*)STATE(skb)->tail > (char*)STATE(skb)->head); + tpdu_length = (char*)STATE(skb)->tail - (char*)STATE(skb)->head; + sent = pgm_sendto (sock, + !STATE(is_rate_limited), /* rate limited on blocking */ + FALSE, /* regular socket */ + STATE(skb)->head, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + goto blocked; + } + +/* save unfolded odata for retransmissions */ + pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); + + if (PGM_LIKELY((size_t)sent == tpdu_length)) { + bytes_sent += tpdu_length + sock->iphdr_len; /* as counted at IP layer */ + packets_sent++; /* IP packets */ + data_bytes_sent += STATE(tsdu_length); + } + + pgm_free_skb (STATE(skb)); + STATE(data_bytes_offset) += STATE(tsdu_length); + +/* check for end of transmission group */ + if (sock->use_proactive_parity) { + const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); + const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; + if (!((odata_sqn + 1) & ~tg_sqn_mask)) + pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); + } + + } +#ifdef TRANSPORT_DEBUG + if (is_one_apdu) + { + pgm_assert( STATE(data_bytes_offset) == STATE(apdu_length) ); + } +#endif + + sock->is_apdu_eagain = FALSE; + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; + if (bytes_written) + *bytes_written = data_bytes_sent; + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_NORMAL; + +blocked: + if (bytes_sent) { + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; + } + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + if (EAGAIN == errno) { + if (sock->use_pgmcc) + pgm_notify_clear (&sock->ack_notify); + return PGM_IO_STATUS_WOULD_BLOCK; + } + return PGM_IO_STATUS_RATE_LIMITED; +} + +/* cleanup resuming send state helper + */ +#undef STATE + +/* send repair packet. + * + * on success, TRUE is returned. on error, FALSE is returned. + */ + +static +bool +send_rdata ( + pgm_sock_t* restrict sock, + struct pgm_sk_buff_t* restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + pgm_assert ((char*)skb->tail > (char*)skb->head); + + const size_t tpdu_length = (char*)skb->tail - (char*)skb->head; + +/* update previous odata/rdata contents */ + struct pgm_header* header = skb->pgm_header; + struct pgm_data* rdata = skb->pgm_data; + header->pgm_type = PGM_RDATA; +/* RDATA */ + rdata->data_trail = htonl (pgm_txw_trail(sock->window)); + + header->pgm_checksum = 0; + const size_t pgm_header_len = tpdu_length - ntohs(header->pgm_tsdu_length); + uint32_t unfolded_header = pgm_csum_partial (header, pgm_header_len, 0); + uint32_t unfolded_odata = pgm_csum_partial (skb->data, ntohs(header->pgm_tsdu_length), 0); + header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, unfolded_odata, pgm_header_len)); + +/* congestion control */ +// if (sock->use_pgmcc && +// sock->tokens < pgm_fp8 (1)) +// { +// pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Token limit reached.")); +// sock->blocklen = tpdu_length; +// return FALSE; +// } + + const ssize_t sent = pgm_sendto (sock, + sock->is_controlled_rdata, /* rate limited */ + TRUE, /* with router alert */ + header, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + + if (sent < 0 && EAGAIN == errno) { + sock->blocklen = tpdu_length; + return FALSE; + } + + const pgm_time_t now = pgm_time_update_now(); + + if (sock->use_pgmcc) { + if (sock->tokens >= pgm_fp8 (1)) + sock->tokens -= pgm_fp8 (1); + sock->ack_expiry = now + sock->ack_expiry_ivl; + } + +/* re-set spm timer: we are already in the timer thread, no need to prod timers + */ + pgm_mutex_lock (&sock->timer_mutex); + sock->spm_heartbeat_state = 1; + sock->next_heartbeat_spm = now + sock->spm_heartbeat_interval[sock->spm_heartbeat_state++]; + pgm_mutex_unlock (&sock->timer_mutex); + + pgm_txw_inc_retransmit_count (skb); + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_BYTES_RETRANSMITTED] += ntohs(header->pgm_tsdu_length); + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED]++; /* impossible to determine APDU count */ + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length + sock->iphdr_len); + return TRUE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/source.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/source.c.c89.patch new file mode 100644 index 0000000..dce59e9 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/source.c.c89.patch @@ -0,0 +1,1021 @@ +--- source.c 2010-08-05 11:41:13.000000000 +0800 ++++ source.c89 2010-08-05 11:41:04.000000000 +0800 +@@ -124,11 +124,13 @@ + ) + { + pgm_return_val_if_fail (NULL != sock, FALSE); ++ { + const bool status = pgm_txw_retransmit_push (sock->window, + nak_tg_sqn | sock->rs_proactive_h, + TRUE /* is_parity */, + sock->tg_sqn_shift); + return status; ++ } + } + + /* a deferred request for RDATA, now processing in the timer thread, we check the transmit +@@ -159,6 +161,7 @@ + * has been retransmitted. + */ + pgm_spinlock_lock (&sock->txw_spinlock); ++ { + struct pgm_sk_buff_t* skb = pgm_txw_retransmit_try_peek (sock->window); + if (skb) { + skb = pgm_skb_get (skb); +@@ -174,6 +177,7 @@ + } else + pgm_spinlock_unlock (&sock->txw_spinlock); + return TRUE; ++ } + } + + /* SPMR indicates if multicast to cancel own SPMR, or unicast to send SPM. +@@ -233,10 +237,11 @@ + pgm_assert (NULL != skb); + pgm_assert (NULL != opt_pgmcc_feedback); + ++ { + const uint32_t opt_tstamp = ntohl (opt_pgmcc_feedback->opt_tstamp); + const uint16_t opt_loss_rate = ntohs (opt_pgmcc_feedback->opt_loss_rate); + +- const uint32_t rtt = pgm_to_msecs (skb->tstamp) - opt_tstamp; ++ const uint32_t rtt = (uint32_t)(pgm_to_msecs (skb->tstamp) - opt_tstamp); + const uint64_t peer_loss = rtt * rtt * opt_loss_rate; + + struct sockaddr_storage peer_nla; +@@ -263,6 +268,7 @@ + } + + return FALSE; ++ } + } + + /* NAK requesting RDATA transmission for a sending sock, only valid if +@@ -290,6 +296,7 @@ + pgm_debug ("pgm_on_nak (sock:%p skb:%p)", + (const void*)sock, (const void*)skb); + ++ { + const bool is_parity = skb->pgm_header->pgm_options & PGM_OPT_PARITY; + if (is_parity) { + sock->cumulative_stats[PGM_PC_SOURCE_PARITY_NAKS_RECEIVED]++; +@@ -307,6 +314,7 @@ + return FALSE; + } + ++ { + const struct pgm_nak* nak = (struct pgm_nak*) skb->data; + const struct pgm_nak6* nak6 = (struct pgm_nak6*)skb->data; + +@@ -323,6 +331,7 @@ + } + + /* NAK_GRP_NLA containers our sock multicast group */ ++ { + struct sockaddr_storage nak_grp_nla; + pgm_nla_to_sockaddr ((AF_INET6 == nak_src_nla.ss_family) ? &nak6->nak6_grp_nla_afi : &nak->nak_grp_nla_afi, (struct sockaddr*)&nak_grp_nla); + if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&nak_grp_nla, (struct sockaddr*)&sock->send_gsr.gsr_group) != 0)) +@@ -335,6 +344,7 @@ + } + + /* create queue object */ ++ { + struct pgm_sqn_list_t sqn_list; + sqn_list.sqn[0] = ntohl (nak->nak_sqn); + sqn_list.len = 1; +@@ -342,6 +352,7 @@ + pgm_debug ("nak_sqn %" PRIu32, sqn_list.sqn[0]); + + /* check NAK list */ ++ { + const uint32_t* nak_list = NULL; + uint_fast8_t nak_list_len = 0; + if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) +@@ -360,6 +371,7 @@ + return FALSE; + } + /* TODO: check for > 16 options & past packet end */ ++ { + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; + do { + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); +@@ -369,6 +381,7 @@ + break; + } + } while (!(opt_header->opt_type & PGM_OPT_END)); ++ } + } + + /* nak list numbers */ +@@ -376,12 +389,15 @@ + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed NAK rejected on too long sequence list.")); + return FALSE; + } +- +- for (uint_fast8_t i = 0; i < nak_list_len; i++) ++ ++ { ++ uint_fast8_t i; ++ for (i = 0; i < nak_list_len; i++) + { + sqn_list.sqn[sqn_list.len++] = ntohl (*nak_list); + nak_list++; + } ++ } + + /* send NAK confirm packet immediately, then defer to timer thread for a.s.a.p + * delivery of the actual RDATA packets. blocking send for NCF is ignored as RDATA +@@ -393,13 +409,21 @@ + send_ncf (sock, (struct sockaddr*)&nak_src_nla, (struct sockaddr*)&nak_grp_nla, sqn_list.sqn[0], is_parity); + + /* queue retransmit requests */ +- for (uint_fast8_t i = 0; i < sqn_list.len; i++) { ++ { ++ uint_fast8_t i; ++ for (i = 0; i < sqn_list.len; i++) { + const bool push_status = pgm_txw_retransmit_push (sock->window, sqn_list.sqn[i], is_parity, sock->tg_sqn_shift); + if (PGM_UNLIKELY(!push_status)) { + pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Failed to push retransmit request for #%" PRIu32), sqn_list.sqn[i]); + } + } ++ } + return TRUE; ++ } ++ } ++ } ++ } ++ } + } + + /* Null-NAK, or N-NAK propogated by a DLR for hand waving excitement +@@ -427,6 +451,7 @@ + return FALSE; + } + ++ { + const struct pgm_nak* nnak = (struct pgm_nak*) skb->data; + const struct pgm_nak6* nnak6 = (struct pgm_nak6*)skb->data; + +@@ -441,6 +466,7 @@ + } + + /* NAK_GRP_NLA containers our sock multicast group */ ++ { + struct sockaddr_storage nnak_grp_nla; + pgm_nla_to_sockaddr ((AF_INET6 == nnak_src_nla.ss_family) ? &nnak6->nak6_grp_nla_afi : &nnak->nak_grp_nla_afi, (struct sockaddr*)&nnak_grp_nla); + if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&nnak_grp_nla, (struct sockaddr*)&sock->send_gsr.gsr_group) != 0)) +@@ -450,6 +476,7 @@ + } + + /* check NNAK list */ ++ { + uint_fast8_t nnak_list_len = 0; + if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) + { +@@ -465,6 +492,7 @@ + return FALSE; + } + /* TODO: check for > 16 options & past packet end */ ++ { + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; + do { + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); +@@ -473,10 +501,14 @@ + break; + } + } while (!(opt_header->opt_type & PGM_OPT_END)); ++ } + } + + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAKS_RECEIVED] += 1 + nnak_list_len; + return TRUE; ++ } ++ } ++ } + } + + /* ACK, sent upstream by one selected ACKER for congestion control feedback. +@@ -507,6 +539,7 @@ + if (!sock->use_pgmcc) + return FALSE; + ++ { + const struct pgm_ack* ack = (struct pgm_ack*)skb->data; + bool is_acker = FALSE; + +@@ -522,6 +555,7 @@ + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed ACK rejected.")); + return FALSE; + } ++ { + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; + do { + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); +@@ -531,6 +565,7 @@ + break; /* ignore other options */ + } + } while (!(opt_header->opt_type & PGM_OPT_END)); ++ } + } + + /* ignore ACKs from other receivers or sessions */ +@@ -541,22 +576,26 @@ + sock->next_crqst = 0; + + /* count new ACK sequences */ ++ { + const uint32_t ack_rx_max = ntohl (ack->ack_rx_max); + const int32_t delta = ack_rx_max - sock->ack_rx_max; + /* ignore older ACKs when multiple active ACKers */ + if (pgm_uint32_gt (ack_rx_max, sock->ack_rx_max)) + sock->ack_rx_max = ack_rx_max; ++ { + uint32_t ack_bitmap = ntohl (ack->ack_bitmap); + if (delta > 32) sock->ack_bitmap = 0; /* sequence jump ahead beyond past bitmap */ + else if (delta > 0) sock->ack_bitmap <<= delta; /* immediate sequence */ + else if (delta > -32) ack_bitmap <<= -delta; /* repair sequence scoped by bitmap */ + else ack_bitmap = 0; /* old sequence */ ++ { + unsigned new_acks = _pgm_popcount (ack_bitmap & ~sock->ack_bitmap); + sock->ack_bitmap |= ack_bitmap; + + if (0 == new_acks) + return TRUE; + ++ { + const bool is_congestion_limited = (sock->tokens < pgm_fp8 (1)); + + /* after loss detection cancel any further manipulation of the window +@@ -568,14 +607,17 @@ + { + pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("PGMCC window token manipulation suspended due to congestion (T:%u W:%u)"), + pgm_fp8tou (sock->tokens), pgm_fp8tou (sock->cwnd_size)); ++ { + const uint_fast32_t token_inc = pgm_fp8mul (pgm_fp8 (new_acks), pgm_fp8 (1) + pgm_fp8div (pgm_fp8 (1), sock->cwnd_size)); + sock->tokens = MIN( sock->tokens + token_inc, sock->cwnd_size ); ++ } + goto notify_tx; + } + sock->is_congested = FALSE; + } + + /* count outstanding lost sequences */ ++ { + const unsigned total_lost = _pgm_popcount (~sock->ack_bitmap); + + /* no detected data loss at ACKer, increase congestion window size */ +@@ -583,6 +625,7 @@ + { + new_acks += sock->acks_after_loss; + sock->acks_after_loss = 0; ++ { + uint_fast32_t n = pgm_fp8 (new_acks); + uint_fast32_t token_inc = 0; + +@@ -594,6 +637,7 @@ + sock->cwnd_size += d; + } + ++ { + const uint_fast32_t iw = pgm_fp8div (pgm_fp8 (1), sock->cwnd_size); + + /* linear window increase */ +@@ -602,6 +646,8 @@ + sock->tokens = MIN( sock->tokens + token_inc, sock->cwnd_size ); + // pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("PGMCC++ (T:%u W:%u)"), + // pgm_fp8tou (sock->tokens), pgm_fp8tou (sock->cwnd_size)); ++ } ++ } + } + else + { +@@ -636,6 +682,12 @@ + pgm_notify_send (&sock->ack_notify); + } + return TRUE; ++ } ++ } ++ } ++ } ++ } ++ } + } + + /* ambient/heartbeat SPM's +@@ -658,6 +710,7 @@ + pgm_debug ("pgm_send_spm (sock:%p flags:%d)", + (const void*)sock, flags); + ++ { + size_t tpdu_length = sizeof(struct pgm_header); + if (AF_INET == sock->send_gsr.gsr_group.ss_family) + tpdu_length += sizeof(struct pgm_spm); +@@ -683,9 +736,11 @@ + tpdu_length += sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fin); + } +- char buf[ tpdu_length ]; ++ { ++ char* buf = pgm_newa (char, tpdu_length); + if (PGM_UNLIKELY(pgm_mem_gc_friendly)) + memset (buf, 0, tpdu_length); ++ { + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_spm* spm = (struct pgm_spm *)(header + 1); + struct pgm_spm6* spm6 = (struct pgm_spm6*)(header + 1); +@@ -734,12 +789,14 @@ + sizeof(struct pgm_opt_parity_prm); + opt_header->opt_type = PGM_OPT_PARITY_PRM; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_parity_prm); ++ { + struct pgm_opt_parity_prm* opt_parity_prm = (struct pgm_opt_parity_prm*)(opt_header + 1); + opt_parity_prm->opt_reserved = (sock->use_proactive_parity ? PGM_PARITY_PRM_PRO : 0) | + (sock->use_ondemand_parity ? PGM_PARITY_PRM_OND : 0); + opt_parity_prm->parity_prm_tgs = htonl (sock->rs_k); + last_opt_header = opt_header; + opt_header = (struct pgm_opt_header*)(opt_parity_prm + 1); ++ } + } + + /* OPT_CRQST */ +@@ -750,12 +807,14 @@ + sizeof(struct pgm_opt_crqst); + opt_header->opt_type = PGM_OPT_CRQST; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_crqst); ++ { + struct pgm_opt_crqst* opt_crqst = (struct pgm_opt_crqst*)(opt_header + 1); + /* request receiver worst path report, OPT_CR_RX_WP */ + opt_crqst->opt_reserved = PGM_OPT_CRQST_RXP; + sock->is_pending_crqst = FALSE; + last_opt_header = opt_header; + opt_header = (struct pgm_opt_header*)(opt_crqst + 1); ++ } + } + + /* OPT_FIN */ +@@ -765,10 +824,12 @@ + sizeof(struct pgm_opt_fin); + opt_header->opt_type = PGM_OPT_FIN; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_fin); ++ { + struct pgm_opt_fin* opt_fin = (struct pgm_opt_fin*)(opt_header + 1); + opt_fin->opt_reserved = 0; + last_opt_header = opt_header; + opt_header = (struct pgm_opt_header*)(opt_fin + 1); ++ } + } + + last_opt_header->opt_type |= PGM_OPT_END; +@@ -779,6 +840,7 @@ + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + ++ { + const ssize_t sent = pgm_sendto (sock, + flags != PGM_OPT_SYN && sock->is_controlled_spm, /* rate limited */ + TRUE, /* with router alert */ +@@ -794,6 +856,10 @@ + sock->spm_sqn++; + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length); + return TRUE; ++ } ++ } ++ } ++ } + } + + /* send a NAK confirm (NCF) message with provided sequence number list. +@@ -818,6 +884,7 @@ + pgm_assert (nak_src_nla->sa_family == nak_grp_nla->sa_family); + + #ifdef SOURCE_DEBUG ++ { + char saddr[INET6_ADDRSTRLEN], gaddr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop (nak_src_nla, saddr, sizeof(saddr)); + pgm_sockaddr_ntop (nak_grp_nla, gaddr, sizeof(gaddr)); +@@ -828,11 +895,14 @@ + sequence, + is_parity ? "TRUE": "FALSE" + ); ++ } + #endif + ++ { + size_t tpdu_length = sizeof(struct pgm_header); + tpdu_length += (AF_INET == nak_src_nla->sa_family) ? sizeof(struct pgm_nak) : sizeof(struct pgm_nak6); +- char buf[ tpdu_length ]; ++ { ++ char* buf = pgm_newa (char, tpdu_length); + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_nak* ncf = (struct pgm_nak *)(header + 1); + struct pgm_nak6* ncf6 = (struct pgm_nak6*)(header + 1); +@@ -854,6 +924,7 @@ + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + ++ { + const ssize_t sent = pgm_sendto (sock, + FALSE, /* not rate limited */ + TRUE, /* with router alert */ +@@ -865,6 +936,9 @@ + return FALSE; + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length); + return TRUE; ++ } ++ } ++ } + } + + /* A NCF packet with a OPT_NAK_LIST option extension +@@ -891,16 +965,20 @@ + pgm_assert (nak_src_nla->sa_family == nak_grp_nla->sa_family); + + #ifdef SOURCE_DEBUG ++ { + char saddr[INET6_ADDRSTRLEN], gaddr[INET6_ADDRSTRLEN]; + char list[1024]; + pgm_sockaddr_ntop (nak_src_nla, saddr, sizeof(saddr)); + pgm_sockaddr_ntop (nak_grp_nla, gaddr, sizeof(gaddr)); + sprintf (list, "%" PRIu32, sqn_list->sqn[0]); +- for (uint_fast8_t i = 1; i < sqn_list->len; i++) { ++ { ++ uint_fast8_t i; ++ for (i = 1; i < sqn_list->len; i++) { + char sequence[ 2 + strlen("4294967295") ]; + sprintf (sequence, " %" PRIu32, sqn_list->sqn[i]); + strcat (list, sequence); + } ++ } + pgm_debug ("send_ncf_list (sock:%p nak-src-nla:%s nak-grp-nla:%s sqn-list:[%s] is-parity:%s)", + (void*)sock, + saddr, +@@ -908,14 +986,17 @@ + list, + is_parity ? "TRUE": "FALSE" + ); ++ } + #endif + ++ { + size_t tpdu_length = sizeof(struct pgm_header) + + sizeof(struct pgm_opt_length) + /* includes header */ + sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(uint32_t) ); + tpdu_length += (AF_INET == nak_src_nla->sa_family) ? sizeof(struct pgm_nak) : sizeof(struct pgm_nak6); +- char buf[ tpdu_length ]; ++ { ++ char* buf = pgm_newa (char, tpdu_length); + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_nak* ncf = (struct pgm_nak *)(header + 1); + struct pgm_nak6* ncf6 = (struct pgm_nak6*)(header + 1); +@@ -935,6 +1016,7 @@ + pgm_sockaddr_to_nla (nak_grp_nla, (AF_INET6 == nak_src_nla->sa_family) ? (char*)&ncf6->nak6_grp_nla_afi : (char*)&ncf->nak_grp_nla_afi ); + + /* OPT_NAK_LIST */ ++ { + struct pgm_opt_length* opt_len = (AF_INET6 == nak_src_nla->sa_family) ? (struct pgm_opt_length*)(ncf6 + 1) : (struct pgm_opt_length*)(ncf + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); +@@ -942,20 +1024,26 @@ + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(uint32_t) ) ); ++ { + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_NAK_LIST | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(uint32_t) ); ++ { + struct pgm_opt_nak_list* opt_nak_list = (struct pgm_opt_nak_list*)(opt_header + 1); + opt_nak_list->opt_reserved = 0; + /* to network-order */ +- for (uint_fast8_t i = 1; i < sqn_list->len; i++) ++ { ++ uint_fast8_t i; ++ for (i = 1; i < sqn_list->len; i++) + opt_nak_list->opt_sqn[i-1] = htonl (sqn_list->sqn[i]); ++ } + + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + ++ { + const ssize_t sent = pgm_sendto (sock, + FALSE, /* not rate limited */ + TRUE, /* with router alert */ +@@ -967,6 +1055,12 @@ + return FALSE; + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length); + return TRUE; ++ } ++ } ++ } ++ } ++ } ++ } + } + + /* cancel any pending heartbeat SPM and schedule a new one +@@ -980,6 +1074,7 @@ + ) + { + pgm_mutex_lock (&sock->timer_mutex); ++ { + const pgm_time_t next_poll = sock->next_poll; + const pgm_time_t spm_heartbeat_interval = sock->spm_heartbeat_interval[ sock->spm_heartbeat_state = 1 ]; + sock->next_heartbeat_spm = now + spm_heartbeat_interval; +@@ -992,6 +1087,7 @@ + } + } + pgm_mutex_unlock (&sock->timer_mutex); ++ } + } + + /* state helper for resuming sends +@@ -1027,6 +1123,7 @@ + pgm_debug ("send_odata (sock:%p skb:%p bytes-written:%p)", + (void*)sock, (void*)skb, (void*)bytes_written); + ++ { + const uint16_t tsdu_length = skb->len; + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + const size_t tpdu_length = tsdu_length + pgm_pkt_offset (FALSE, pgmcc_family); +@@ -1056,6 +1153,7 @@ + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + + STATE(skb)->pgm_header->pgm_checksum = 0; ++ { + void* data = STATE(skb)->pgm_data + 1; + if (sock->use_pgmcc) { + struct pgm_opt_length* opt_len = data; +@@ -1066,23 +1164,28 @@ + ((AF_INET6 == sock->acker_nla.ss_family) ? + sizeof(struct pgm_opt6_pgmcc_data) : + sizeof(struct pgm_opt_pgmcc_data)) ); ++ { + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_PGMCC_DATA | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + ((AF_INET6 == sock->acker_nla.ss_family) ? + sizeof(struct pgm_opt6_pgmcc_data) : + sizeof(struct pgm_opt_pgmcc_data)); ++ { + struct pgm_opt_pgmcc_data* pgmcc_data = (struct pgm_opt_pgmcc_data*)(opt_header + 1); + struct pgm_opt6_pgmcc_data* pgmcc_data6 = (struct pgm_opt6_pgmcc_data*)(opt_header + 1); + +- pgmcc_data->opt_tstamp = htonl (pgm_to_msecs (STATE(skb)->tstamp)); ++ pgmcc_data->opt_tstamp = htonl ((unsigned long)pgm_to_msecs (STATE(skb)->tstamp)); + /* acker nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&sock->acker_nla, (char*)&pgmcc_data->opt_nla_afi); + if (AF_INET6 == sock->acker_nla.ss_family) + data = (char*)pgmcc_data6 + sizeof(struct pgm_opt6_pgmcc_data); + else + data = (char*)pgmcc_data + sizeof(struct pgm_opt_pgmcc_data); ++ } ++ } + } ++ { + const size_t pgm_header_len = (char*)data - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + STATE(unfolded_odata) = pgm_csum_partial (data, tsdu_length, 0); +@@ -1097,6 +1200,7 @@ + * attempt to send a repair-data packet based on in transit original data. + */ + ++ { + ssize_t sent; + retry_send: + +@@ -1157,6 +1261,10 @@ + if (bytes_written) + *bytes_written = tsdu_length; + return PGM_IO_STATUS_NORMAL; ++ } ++ } ++ } ++ } + } + + /* send one PGM original data packet, callee owned memory. +@@ -1183,6 +1291,7 @@ + pgm_debug ("send_odata_copy (sock:%p tsdu:%p tsdu_length:%u bytes-written:%p)", + (void*)sock, tsdu, tsdu_length, (void*)bytes_written); + ++ { + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + const size_t tpdu_length = tsdu_length + pgm_pkt_offset (FALSE, pgmcc_family); + +@@ -1212,6 +1321,7 @@ + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + + STATE(skb)->pgm_header->pgm_checksum = 0; ++ { + void* data = STATE(skb)->pgm_data + 1; + if (sock->use_pgmcc) { + struct pgm_opt_length* opt_len = data; +@@ -1222,21 +1332,26 @@ + ((AF_INET6 == sock->acker_nla.ss_family) ? + sizeof(struct pgm_opt6_pgmcc_data) : + sizeof(struct pgm_opt_pgmcc_data)) ); ++ { + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_PGMCC_DATA | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + ((AF_INET6 == sock->acker_nla.ss_family) ? + sizeof(struct pgm_opt6_pgmcc_data) : + sizeof(struct pgm_opt_pgmcc_data)); ++ { + struct pgm_opt_pgmcc_data* pgmcc_data = (struct pgm_opt_pgmcc_data*)(opt_header + 1); + struct pgm_opt6_pgmcc_data* pgmcc_data6 = (struct pgm_opt6_pgmcc_data*)(opt_header + 1); + + pgmcc_data->opt_reserved = 0; +- pgmcc_data->opt_tstamp = htonl (pgm_to_msecs (STATE(skb)->tstamp)); ++ pgmcc_data->opt_tstamp = htonl ((unsigned long)pgm_to_msecs (STATE(skb)->tstamp)); + /* acker nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&sock->acker_nla, (char*)&pgmcc_data->opt_nla_afi); + data = (char*)opt_header + opt_header->opt_length; ++ } ++ } + } ++ { + const size_t pgm_header_len = (char*)data - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + STATE(unfolded_odata) = pgm_csum_partial_copy (tsdu, data, tsdu_length, 0); +@@ -1247,6 +1362,7 @@ + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); + ++ { + ssize_t sent; + retry_send: + +@@ -1309,6 +1425,10 @@ + if (bytes_written) + *bytes_written = tsdu_length; + return PGM_IO_STATUS_NORMAL; ++ } ++ } ++ } ++ } + } + + /* send one PGM original data packet, callee owned scatter/gather io vector +@@ -1347,7 +1467,9 @@ + goto retry_send; + + STATE(tsdu_length) = 0; +- for (unsigned i = 0; i < count; i++) ++ { ++ unsigned i; ++ for (i = 0; i < count; i++) + { + #ifdef TRANSPORT_DEBUG + if (PGM_LIKELY(vector[i].iov_len)) { +@@ -1356,11 +1478,13 @@ + #endif + STATE(tsdu_length) += vector[i].iov_len; + } ++ } + pgm_return_val_if_fail (STATE(tsdu_length) <= sock->max_tsdu, PGM_IO_STATUS_ERROR); + + STATE(skb) = pgm_alloc_skb (sock->max_tpdu); + STATE(skb)->sock = sock; + STATE(skb)->tstamp = pgm_time_update_now(); ++ { + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + pgm_skb_reserve (STATE(skb), pgm_pkt_offset (FALSE, pgmcc_family)); + pgm_skb_put (STATE(skb), STATE(tsdu_length)); +@@ -1379,18 +1503,24 @@ + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + + STATE(skb)->pgm_header->pgm_checksum = 0; ++ { + const size_t pgm_header_len = (char*)(STATE(skb)->pgm_data + 1) - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + + /* unroll first iteration to make friendly branch prediction */ + char* dst = (char*)(STATE(skb)->pgm_data + 1); +- STATE(unfolded_odata) = pgm_csum_partial_copy ((const char*)vector[0].iov_base, dst, vector[0].iov_len, 0); ++ STATE(unfolded_odata) = pgm_csum_partial_copy ((const char*)vector[0].iov_base, dst, (uint16_t)vector[0].iov_len, 0); + + /* iterate over one or more vector elements to perform scatter/gather checksum & copy */ +- for (unsigned i = 1; i < count; i++) { ++ { ++ unsigned i; ++ for (i = 1; i < count; i++) { + dst += vector[i-1].iov_len; +- const uint32_t unfolded_element = pgm_csum_partial_copy ((const char*)vector[i].iov_base, dst, vector[i].iov_len, 0); +- STATE(unfolded_odata) = pgm_csum_block_add (STATE(unfolded_odata), unfolded_element, vector[i-1].iov_len); ++ { ++ const uint32_t unfolded_element = pgm_csum_partial_copy ((const char*)vector[i].iov_base, dst, (uint16_t)vector[i].iov_len, 0); ++ STATE(unfolded_odata) = pgm_csum_block_add (STATE(unfolded_odata), unfolded_element, (uint16_t)vector[i-1].iov_len); ++ } ++ } + } + + STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); +@@ -1400,6 +1530,7 @@ + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); + ++ { + ssize_t sent; + size_t tpdu_length; + retry_send: +@@ -1447,6 +1578,9 @@ + if (bytes_written) + *bytes_written = STATE(tsdu_length); + return PGM_IO_STATUS_NORMAL; ++ } ++ } ++ } + } + + /* send PGM original data, callee owned memory. if larger than maximum TPDU +@@ -1530,6 +1664,7 @@ + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + + /* OPT_LENGTH */ ++ { + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(STATE(skb)->pgm_data + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); +@@ -1537,6 +1672,7 @@ + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment) ); + /* OPT_FRAGMENT */ ++ { + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_FRAGMENT | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + +@@ -1549,6 +1685,7 @@ + + /* TODO: the assembly checksum & copy routine is faster than memcpy & pgm_cksum on >= opteron hardware */ + STATE(skb)->pgm_header->pgm_checksum = 0; ++ { + const size_t pgm_header_len = (char*)(STATE(skb)->pgm_opt_fragment + 1) - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + STATE(unfolded_odata) = pgm_csum_partial_copy ((const char*)apdu + STATE(data_bytes_offset), STATE(skb)->pgm_opt_fragment + 1, STATE(tsdu_length), 0); +@@ -1559,6 +1696,7 @@ + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); + ++ { + ssize_t sent; + size_t tpdu_length; + retry_send: +@@ -1596,6 +1734,10 @@ + pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); + } + ++ } ++ } ++ } ++ } + } while ( STATE(data_bytes_offset) < apdu_length); + pgm_assert( STATE(data_bytes_offset) == apdu_length ); + +@@ -1638,7 +1780,7 @@ + size_t* restrict bytes_written + ) + { +- pgm_debug ("pgm_send (sock:%p apdu:%p apdu-length:%zu bytes-written:%p)", ++ pgm_debug ("pgm_send (sock:%p apdu:%p apdu-length:%lu bytes-written:%p)", + (void*)sock, apdu, apdu_length, (void*)bytes_written); + + /* parameters */ +@@ -1670,10 +1812,12 @@ + return status; + } + ++ { + const int status = send_apdu (sock, apdu, apdu_length, bytes_written); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; ++ } + } + + /* send PGM original data, callee owned scatter/gather IO vector. if larger than maximum TPDU +@@ -1735,6 +1879,7 @@ + return status; + } + ++ { + size_t bytes_sent = 0; + unsigned packets_sent = 0; + size_t data_bytes_sent = 0; +@@ -1759,7 +1904,9 @@ + + /* calculate (total) APDU length */ + STATE(apdu_length) = 0; +- for (unsigned i = 0; i < count; i++) ++ { ++ unsigned i; ++ for (i = 0; i < count; i++) + { + #ifdef TRANSPORT_DEBUG + if (PGM_LIKELY(vector[i].iov_len)) { +@@ -1775,6 +1922,7 @@ + } + STATE(apdu_length) += vector[i].iov_len; + } ++ } + + /* pass on non-fragment calls */ + if (is_one_apdu) { +@@ -1885,6 +2033,7 @@ + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + + /* OPT_LENGTH */ ++ { + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(STATE(skb)->pgm_data + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); +@@ -1892,6 +2041,7 @@ + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment) ); + /* OPT_FRAGMENT */ ++ { + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_FRAGMENT | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + +@@ -1904,6 +2054,7 @@ + + /* checksum & copy */ + STATE(skb)->pgm_header->pgm_checksum = 0; ++ { + const size_t pgm_header_len = (char*)(STATE(skb)->pgm_opt_fragment + 1) - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + +@@ -1941,8 +2092,10 @@ + dst += copy_length; + src_length = vector[STATE(vector_index)].iov_len - STATE(vector_offset); + copy_length = MIN( STATE(tsdu_length) - dst_length, src_length ); ++ { + const uint32_t unfolded_element = pgm_csum_partial_copy (src, dst, copy_length, 0); + STATE(unfolded_odata) = pgm_csum_block_add (STATE(unfolded_odata), unfolded_element, dst_length); ++ } + } + + STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); +@@ -1952,6 +2105,7 @@ + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); + ++ { + ssize_t sent; + size_t tpdu_length; + retry_one_apdu_send: +@@ -1988,6 +2142,10 @@ + pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); + } + ++ } ++ } ++ } ++ } + } while ( STATE(data_bytes_offset) < STATE(apdu_length) ); + pgm_assert( STATE(data_bytes_offset) == STATE(apdu_length) ); + +@@ -2018,6 +2176,7 @@ + return PGM_IO_STATUS_WOULD_BLOCK; + } + return PGM_IO_STATUS_RATE_LIMITED; ++ } + } + + /* send PGM original data, transmit window owned scatter/gather IO vector. +@@ -2077,6 +2236,7 @@ + return status; + } + ++ { + size_t bytes_sent = 0; + unsigned packets_sent = 0; + size_t data_bytes_sent = 0; +@@ -2090,8 +2250,11 @@ + if (sock->is_nonblocking && sock->is_controlled_odata) + { + size_t total_tpdu_length = 0; +- for (unsigned i = 0; i < count; i++) ++ { ++ unsigned i; ++ for (i = 0; i < count; i++) + total_tpdu_length += sock->iphdr_len + pgm_pkt_offset (is_one_apdu, pgmcc_family) + vector[i]->len; ++ } + + /* calculation includes one iphdr length already */ + if (!pgm_rate_check (&sock->rate_control, +@@ -2110,7 +2273,9 @@ + { + STATE(apdu_length) = 0; + STATE(first_sqn) = pgm_txw_next_lead(sock->window); +- for (unsigned i = 0; i < count; i++) ++ { ++ unsigned i; ++ for (i = 0; i < count; i++) + { + if (PGM_UNLIKELY(vector[i]->len > sock->max_tsdu_fragment)) { + pgm_mutex_unlock (&sock->source_mutex); +@@ -2119,6 +2284,7 @@ + } + STATE(apdu_length) += vector[i]->len; + } ++ } + if (PGM_UNLIKELY(STATE(apdu_length) > sock->max_apdu)) { + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); +@@ -2157,6 +2323,7 @@ + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment) ); + /* OPT_FRAGMENT */ ++ { + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_FRAGMENT | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + +@@ -2168,6 +2335,7 @@ + STATE(skb)->pgm_opt_fragment->opt_frag_len = htonl (STATE(apdu_length)); + + pgm_assert (STATE(skb)->data == (STATE(skb)->pgm_opt_fragment + 1)); ++ } + } + else + { +@@ -2177,6 +2345,7 @@ + /* TODO: the assembly checksum & copy routine is faster than memcpy & pgm_cksum on >= opteron hardware */ + STATE(skb)->pgm_header->pgm_checksum = 0; + pgm_assert ((char*)STATE(skb)->data > (char*)STATE(skb)->pgm_header); ++ { + const size_t pgm_header_len = (char*)STATE(skb)->data - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + STATE(unfolded_odata) = pgm_csum_partial ((char*)STATE(skb)->data, STATE(tsdu_length), 0); +@@ -2186,6 +2355,7 @@ + pgm_spinlock_lock (&sock->txw_spinlock); + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); ++ { + ssize_t sent; + size_t tpdu_length; + retry_send: +@@ -2223,6 +2393,8 @@ + if (!((odata_sqn + 1) & ~tg_sqn_mask)) + pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); + } ++ } ++ } + + } + #ifdef TRANSPORT_DEBUG +@@ -2259,6 +2431,7 @@ + return PGM_IO_STATUS_WOULD_BLOCK; + } + return PGM_IO_STATUS_RATE_LIMITED; ++ } + } + + /* cleanup resuming send state helper +@@ -2282,6 +2455,7 @@ + pgm_assert (NULL != skb); + pgm_assert ((char*)skb->tail > (char*)skb->head); + ++ { + const size_t tpdu_length = (char*)skb->tail - (char*)skb->head; + + /* update previous odata/rdata contents */ +@@ -2292,6 +2466,7 @@ + rdata->data_trail = htonl (pgm_txw_trail(sock->window)); + + header->pgm_checksum = 0; ++ { + const size_t pgm_header_len = tpdu_length - ntohs(header->pgm_tsdu_length); + uint32_t unfolded_header = pgm_csum_partial (header, pgm_header_len, 0); + uint32_t unfolded_odata = pgm_txw_get_unfolded_checksum (skb); +@@ -2306,6 +2481,7 @@ + return FALSE; + } + ++ { + const ssize_t sent = pgm_sendto (sock, + sock->is_controlled_rdata, /* rate limited */ + TRUE, /* with router alert */ +@@ -2319,6 +2495,7 @@ + return FALSE; + } + ++ { + const pgm_time_t now = pgm_time_update_now(); + + if (sock->use_pgmcc) { +@@ -2338,6 +2515,10 @@ + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED]++; /* impossible to determine APDU count */ + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length + sock->iphdr_len); + return TRUE; ++ } ++ } ++ } ++ } + } + + /* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/source.c.orig b/3rdparty/openpgm-svn-r1135/pgm/source.c.orig new file mode 100644 index 0000000..2c197cd --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/source.c.orig @@ -0,0 +1,2344 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM source socket. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __STDC_FORMAT_MACROS +#ifdef _MSC_VER +# include +#else +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + + +//#define SOURCE_DEBUG + +#ifndef SOURCE_DEBUG +# define PGM_DISABLE_ASSERT +#endif + +#if !defined(ENOBUFS) && defined(WSAENOBUFS) +# define ENOBUFS WSAENOBUFS +#endif + + +/* locals */ +static inline bool peer_is_source (const pgm_peer_t*) PGM_GNUC_CONST; +static inline bool peer_is_peer (const pgm_peer_t*) PGM_GNUC_CONST; +static void reset_heartbeat_spm (pgm_sock_t*const, const pgm_time_t); +static bool send_ncf (pgm_sock_t*const restrict, const struct sockaddr*const restrict, const struct sockaddr*const restrict, const uint32_t, const bool); +static bool send_ncf_list (pgm_sock_t*const restrict, const struct sockaddr*const restrict, const struct sockaddr*const restrict, struct pgm_sqn_list_t*const restrict, const bool); +static int send_odata (pgm_sock_t*const restrict, struct pgm_sk_buff_t*const restrict, size_t*restrict); +static int send_odata_copy (pgm_sock_t*const restrict, const void*restrict, const uint16_t, size_t*restrict); +static int send_odatav (pgm_sock_t*const restrict, const struct pgm_iovec*const restrict, const unsigned, size_t*restrict); +static bool send_rdata (pgm_sock_t*restrict, struct pgm_sk_buff_t*restrict); + + +static inline +unsigned +_pgm_popcount ( + uint32_t n + ) +{ +#if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) + return __builtin_popcount (n); +#else +/* MIT HAKMEM 169 */ + const uint32_t t = n - ((n >> 1) & 033333333333) + - ((n >> 2) & 011111111111); + return ((t + (t >> 3) & 030707070707)) % 63; +#endif +} + +static inline +bool +peer_is_source ( + const pgm_peer_t* peer + ) +{ + return (NULL == peer); +} + +static inline +bool +peer_is_peer ( + const pgm_peer_t* peer + ) +{ + return (NULL != peer); +} + +static inline +void +reset_spmr_timer ( + pgm_peer_t* const peer + ) +{ + peer->spmr_expiry = 0; +} + +static inline +size_t +source_max_tsdu ( + const pgm_sock_t* sock, + const bool can_fragment + ) +{ + size_t max_tsdu = can_fragment ? sock->max_tsdu_fragment : sock->max_tsdu; + if (sock->use_var_pktlen /* OPT_VAR_PKT_LEN */) + max_tsdu -= sizeof (uint16_t); + return max_tsdu; +} + +/* prototype of function to send pro-active parity NAKs. + */ +static +bool +pgm_schedule_proactive_nak ( + pgm_sock_t* sock, + uint32_t nak_tg_sqn /* transmission group (shifted) */ + ) +{ + pgm_return_val_if_fail (NULL != sock, FALSE); + const bool status = pgm_txw_retransmit_push (sock->window, + nak_tg_sqn | sock->rs_proactive_h, + TRUE /* is_parity */, + sock->tg_sqn_shift); + return status; +} + +/* a deferred request for RDATA, now processing in the timer thread, we check the transmit + * window to see if the packet exists and forward on, maintaining a lock until the queue is + * empty. + * + * returns TRUE on success, returns FALSE if operation would block. + */ + +bool +pgm_on_deferred_nak ( + pgm_sock_t* const sock + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + +/* We can flush queue and block all odata, or process one set, or process each + * sequence number individually. + */ + +/* parity packets are re-numbered across the transmission group with index h, sharing the space + * with the original packets. beyond the transmission group size (k), the PGM option OPT_PARITY_GRP + * provides the extra offset value. + */ + +/* peek from the retransmit queue so we can eliminate duplicate NAKs up until the repair packet + * has been retransmitted. + */ + pgm_spinlock_lock (&sock->txw_spinlock); + struct pgm_sk_buff_t* skb = pgm_txw_retransmit_try_peek (sock->window); + if (skb) { + skb = pgm_skb_get (skb); + pgm_spinlock_unlock (&sock->txw_spinlock); + if (!send_rdata (sock, skb)) { + pgm_free_skb (skb); + pgm_notify_send (&sock->rdata_notify); + return FALSE; + } + pgm_free_skb (skb); +/* now remove sequence number from retransmit queue, re-enabling NAK processing for this sequence number */ + pgm_txw_retransmit_remove_head (sock->window); + } else + pgm_spinlock_unlock (&sock->txw_spinlock); + return TRUE; +} + +/* SPMR indicates if multicast to cancel own SPMR, or unicast to send SPM. + * + * rate limited to 1/IHB_MIN per TSI (13.4). + * + * if SPMR was valid, returns TRUE, if invalid returns FALSE. + */ + +bool +pgm_on_spmr ( + pgm_sock_t* const restrict sock, + pgm_peer_t* const restrict peer, /* maybe NULL if socket is source */ + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + + pgm_debug ("pgm_on_spmr (sock:%p peer:%p skb:%p)", + (void*)sock, (void*)peer, (void*)skb); + + if (PGM_UNLIKELY(!pgm_verify_spmr (skb))) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed SPMR rejected.")); + return FALSE; + } + + if (peer_is_source (peer)) { + const bool send_status = pgm_send_spm (sock, 0); + if (PGM_UNLIKELY(!send_status)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Failed to send SPM on SPM-Request.")); + } + } else { + pgm_trace (PGM_LOG_ROLE_RX_WINDOW,_("Suppressing SPMR due to peer multicast SPMR.")); + reset_spmr_timer (peer); + } + return TRUE; +} + +/* Process opt_pgmcc_feedback PGM option that ships attached to ACK or NAK. + * Contents use to elect best ACKer. + * + * returns TRUE if peer is the elected ACKer. + */ + +static +bool +on_opt_pgmcc_feedback ( + pgm_sock_t* const restrict sock, + const struct pgm_sk_buff_t* const restrict skb, + const struct pgm_opt_pgmcc_feedback* restrict opt_pgmcc_feedback + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + pgm_assert (NULL != opt_pgmcc_feedback); + + const uint32_t opt_tstamp = ntohl (opt_pgmcc_feedback->opt_tstamp); + const uint16_t opt_loss_rate = ntohs (opt_pgmcc_feedback->opt_loss_rate); + + const uint32_t rtt = pgm_to_msecs (skb->tstamp) - opt_tstamp; + const uint64_t peer_loss = rtt * rtt * opt_loss_rate; + + struct sockaddr_storage peer_nla; + pgm_nla_to_sockaddr (&opt_pgmcc_feedback->opt_nla_afi, (struct sockaddr*)&peer_nla); + +/* ACKer elections */ + if (PGM_UNLIKELY(pgm_sockaddr_is_addr_unspecified ((const struct sockaddr*)&sock->acker_nla))) + { + pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Elected first ACKer")); + memcpy (&sock->acker_nla, &peer_nla, pgm_sockaddr_storage_len (&peer_nla)); + } + else if (peer_loss > sock->acker_loss && + 0 != pgm_sockaddr_cmp ((const struct sockaddr*)&peer_nla, (const struct sockaddr*)&sock->acker_nla)) + { + pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Elected new ACKer")); + memcpy (&sock->acker_nla, &peer_nla, pgm_sockaddr_storage_len (&peer_nla)); + } + +/* update ACKer state */ + if (0 == pgm_sockaddr_cmp ((const struct sockaddr*)&peer_nla, (const struct sockaddr*)&sock->acker_nla)) + { + sock->acker_loss = peer_loss; + return TRUE; + } + + return FALSE; +} + +/* NAK requesting RDATA transmission for a sending sock, only valid if + * sequence number(s) still in transmission window. + * + * we can potentially have different IP versions for the NAK packet to the send group. + * + * TODO: fix IPv6 AFIs + * + * take in a NAK and pass off to an asynchronous queue for another thread to process + * + * if NAK is valid, returns TRUE. on error, FALSE is returned. + */ + +bool +pgm_on_nak ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + + pgm_debug ("pgm_on_nak (sock:%p skb:%p)", + (const void*)sock, (const void*)skb); + + const bool is_parity = skb->pgm_header->pgm_options & PGM_OPT_PARITY; + if (is_parity) { + sock->cumulative_stats[PGM_PC_SOURCE_PARITY_NAKS_RECEIVED]++; + if (!sock->use_ondemand_parity) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Parity NAK rejected as on-demand parity is not enabled.")); + sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; + return FALSE; + } + } else + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED]++; + + if (PGM_UNLIKELY(!pgm_verify_nak (skb))) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed NAK rejected.")); + sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; + return FALSE; + } + + const struct pgm_nak* nak = (struct pgm_nak*) skb->data; + const struct pgm_nak6* nak6 = (struct pgm_nak6*)skb->data; + +/* NAK_SRC_NLA contains our sock unicast NLA */ + struct sockaddr_storage nak_src_nla; + pgm_nla_to_sockaddr (&nak->nak_src_nla_afi, (struct sockaddr*)&nak_src_nla); + if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&nak_src_nla, (struct sockaddr*)&sock->send_addr) != 0)) + { + char saddr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&nak_src_nla, saddr, sizeof(saddr)); + pgm_trace (PGM_LOG_ROLE_NETWORK,_("NAK rejected for unmatched NLA: %s"), saddr); + sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; + return FALSE; + } + +/* NAK_GRP_NLA containers our sock multicast group */ + struct sockaddr_storage nak_grp_nla; + pgm_nla_to_sockaddr ((AF_INET6 == nak_src_nla.ss_family) ? &nak6->nak6_grp_nla_afi : &nak->nak_grp_nla_afi, (struct sockaddr*)&nak_grp_nla); + if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&nak_grp_nla, (struct sockaddr*)&sock->send_gsr.gsr_group) != 0)) + { + char sgroup[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&nak_src_nla, sgroup, sizeof(sgroup)); + pgm_trace (PGM_LOG_ROLE_NETWORK,_("NAK rejected as targeted for different multicast group: %s"), sgroup); + sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; + return FALSE; + } + +/* create queue object */ + struct pgm_sqn_list_t sqn_list; + sqn_list.sqn[0] = ntohl (nak->nak_sqn); + sqn_list.len = 1; + + pgm_debug ("nak_sqn %" PRIu32, sqn_list.sqn[0]); + +/* check NAK list */ + const uint32_t* nak_list = NULL; + uint_fast8_t nak_list_len = 0; + if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) + { + const struct pgm_opt_length* opt_len = (AF_INET6 == nak_src_nla.ss_family) ? + (const struct pgm_opt_length*)(nak6 + 1) : + (const struct pgm_opt_length*)(nak + 1); + if (PGM_UNLIKELY(opt_len->opt_type != PGM_OPT_LENGTH)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed NAK rejected.")); + sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; + return FALSE; + } + if (PGM_UNLIKELY(opt_len->opt_length != sizeof(struct pgm_opt_length))) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed NAK rejected.")); + sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS]++; + return FALSE; + } +/* TODO: check for > 16 options & past packet end */ + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; + do { + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); + if ((opt_header->opt_type & PGM_OPT_MASK) == PGM_OPT_NAK_LIST) { + nak_list = ((const struct pgm_opt_nak_list*)(opt_header + 1))->opt_sqn; + nak_list_len = ( opt_header->opt_length - sizeof(struct pgm_opt_header) - sizeof(uint8_t) ) / sizeof(uint32_t); + break; + } + } while (!(opt_header->opt_type & PGM_OPT_END)); + } + +/* nak list numbers */ + if (PGM_UNLIKELY(nak_list_len > 63)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed NAK rejected on too long sequence list.")); + return FALSE; + } + + for (uint_fast8_t i = 0; i < nak_list_len; i++) + { + sqn_list.sqn[sqn_list.len++] = ntohl (*nak_list); + nak_list++; + } + +/* send NAK confirm packet immediately, then defer to timer thread for a.s.a.p + * delivery of the actual RDATA packets. blocking send for NCF is ignored as RDATA + * broadcast will be sent later. + */ + if (nak_list_len) + send_ncf_list (sock, (struct sockaddr*)&nak_src_nla, (struct sockaddr*)&nak_grp_nla, &sqn_list, is_parity); + else + send_ncf (sock, (struct sockaddr*)&nak_src_nla, (struct sockaddr*)&nak_grp_nla, sqn_list.sqn[0], is_parity); + +/* queue retransmit requests */ + for (uint_fast8_t i = 0; i < sqn_list.len; i++) { + const bool push_status = pgm_txw_retransmit_push (sock->window, sqn_list.sqn[i], is_parity, sock->tg_sqn_shift); + if (PGM_UNLIKELY(!push_status)) { + pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Failed to push retransmit request for #%" PRIu32), sqn_list.sqn[i]); + } + } + return TRUE; +} + +/* Null-NAK, or N-NAK propogated by a DLR for hand waving excitement + * + * if NNAK is valid, returns TRUE. on error, FALSE is returned. + */ + +bool +pgm_on_nnak ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + + pgm_debug ("pgm_on_nnak (sock:%p skb:%p)", + (void*)sock, (void*)skb); + + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAK_PACKETS_RECEIVED]++; + + if (PGM_UNLIKELY(!pgm_verify_nnak (skb))) { + sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]++; + return FALSE; + } + + const struct pgm_nak* nnak = (struct pgm_nak*) skb->data; + const struct pgm_nak6* nnak6 = (struct pgm_nak6*)skb->data; + +/* NAK_SRC_NLA contains our sock unicast NLA */ + struct sockaddr_storage nnak_src_nla; + pgm_nla_to_sockaddr (&nnak->nak_src_nla_afi, (struct sockaddr*)&nnak_src_nla); + + if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&nnak_src_nla, (struct sockaddr*)&sock->send_addr) != 0)) + { + sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]++; + return FALSE; + } + +/* NAK_GRP_NLA containers our sock multicast group */ + struct sockaddr_storage nnak_grp_nla; + pgm_nla_to_sockaddr ((AF_INET6 == nnak_src_nla.ss_family) ? &nnak6->nak6_grp_nla_afi : &nnak->nak_grp_nla_afi, (struct sockaddr*)&nnak_grp_nla); + if (PGM_UNLIKELY(pgm_sockaddr_cmp ((struct sockaddr*)&nnak_grp_nla, (struct sockaddr*)&sock->send_gsr.gsr_group) != 0)) + { + sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]++; + return FALSE; + } + +/* check NNAK list */ + uint_fast8_t nnak_list_len = 0; + if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) + { + const struct pgm_opt_length* opt_len = (AF_INET6 == nnak_src_nla.ss_family) ? + (const struct pgm_opt_length*)(nnak6 + 1) : + (const struct pgm_opt_length*)(nnak + 1); + if (PGM_UNLIKELY(opt_len->opt_type != PGM_OPT_LENGTH)) { + sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]++; + return FALSE; + } + if (PGM_UNLIKELY(opt_len->opt_length != sizeof(struct pgm_opt_length))) { + sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS]++; + return FALSE; + } +/* TODO: check for > 16 options & past packet end */ + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; + do { + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); + if ((opt_header->opt_type & PGM_OPT_MASK) == PGM_OPT_NAK_LIST) { + nnak_list_len = ( opt_header->opt_length - sizeof(struct pgm_opt_header) - sizeof(uint8_t) ) / sizeof(uint32_t); + break; + } + } while (!(opt_header->opt_type & PGM_OPT_END)); + } + + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAKS_RECEIVED] += 1 + nnak_list_len; + return TRUE; +} + +/* ACK, sent upstream by one selected ACKER for congestion control feedback. + * + * if ACK is valid, returns TRUE. on error, FALSE is returned. + */ + +bool +pgm_on_ack ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t* const restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + + pgm_debug ("pgm_on_ack (sock:%p skb:%p)", + (const void*)sock, (const void*)skb); + + sock->cumulative_stats[PGM_PC_SOURCE_ACK_PACKETS_RECEIVED]++; + + if (PGM_UNLIKELY(!pgm_verify_ack (skb))) { + sock->cumulative_stats[PGM_PC_SOURCE_ACK_ERRORS]++; + return FALSE; + } + + if (!sock->use_pgmcc) + return FALSE; + + const struct pgm_ack* ack = (struct pgm_ack*)skb->data; + bool is_acker = FALSE; + +/* check PGMCC feedback option for new elections */ + if (skb->pgm_header->pgm_options & PGM_OPT_PRESENT) + { + const struct pgm_opt_length* opt_len = (const struct pgm_opt_length*)(ack + 1); + if (PGM_UNLIKELY(opt_len->opt_type != PGM_OPT_LENGTH)) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed ACK rejected.")); + return FALSE; + } + if (PGM_UNLIKELY(opt_len->opt_length != sizeof(struct pgm_opt_length))) { + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Malformed ACK rejected.")); + return FALSE; + } + const struct pgm_opt_header* opt_header = (const struct pgm_opt_header*)opt_len; + do { + opt_header = (const struct pgm_opt_header*)((const char*)opt_header + opt_header->opt_length); + if ((opt_header->opt_type & PGM_OPT_MASK) == PGM_OPT_PGMCC_FEEDBACK) { + const struct pgm_opt_pgmcc_feedback* opt_pgmcc_feedback = (const struct pgm_opt_pgmcc_feedback*)(opt_header + 1); + is_acker = on_opt_pgmcc_feedback (sock, skb, opt_pgmcc_feedback); + break; /* ignore other options */ + } + } while (!(opt_header->opt_type & PGM_OPT_END)); + } + +/* ignore ACKs from other receivers or sessions */ + if (!is_acker) + return TRUE; + +/* reset ACK expiration */ + sock->next_crqst = 0; + +/* count new ACK sequences */ + const uint32_t ack_rx_max = ntohl (ack->ack_rx_max); + const int32_t delta = ack_rx_max - sock->ack_rx_max; +/* ignore older ACKs when multiple active ACKers */ + if (pgm_uint32_gt (ack_rx_max, sock->ack_rx_max)) + sock->ack_rx_max = ack_rx_max; + uint32_t ack_bitmap = ntohl (ack->ack_bitmap); + if (delta > 32) sock->ack_bitmap = 0; /* sequence jump ahead beyond past bitmap */ + else if (delta > 0) sock->ack_bitmap <<= delta; /* immediate sequence */ + else if (delta > -32) ack_bitmap <<= -delta; /* repair sequence scoped by bitmap */ + else ack_bitmap = 0; /* old sequence */ + unsigned new_acks = _pgm_popcount (ack_bitmap & ~sock->ack_bitmap); + sock->ack_bitmap |= ack_bitmap; + + if (0 == new_acks) + return TRUE; + + const bool is_congestion_limited = (sock->tokens < pgm_fp8 (1)); + +/* after loss detection cancel any further manipulation of the window + * until feedback is received for the next transmitted packet. + */ + if (sock->is_congested) + { + if (pgm_uint32_lte (ack_rx_max, sock->suspended_sqn)) + { + pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("PGMCC window token manipulation suspended due to congestion (T:%u W:%u)"), + pgm_fp8tou (sock->tokens), pgm_fp8tou (sock->cwnd_size)); + const uint_fast32_t token_inc = pgm_fp8mul (pgm_fp8 (new_acks), pgm_fp8 (1) + pgm_fp8div (pgm_fp8 (1), sock->cwnd_size)); + sock->tokens = MIN( sock->tokens + token_inc, sock->cwnd_size ); + goto notify_tx; + } + sock->is_congested = FALSE; + } + +/* count outstanding lost sequences */ + const unsigned total_lost = _pgm_popcount (~sock->ack_bitmap); + +/* no detected data loss at ACKer, increase congestion window size */ + if (0 == total_lost) + { + new_acks += sock->acks_after_loss; + sock->acks_after_loss = 0; + uint_fast32_t n = pgm_fp8 (new_acks); + uint_fast32_t token_inc = 0; + +/* slow-start phase, exponential increase to SSTHRESH */ + if (sock->cwnd_size < sock->ssthresh) { + const uint_fast32_t d = MIN( n, sock->ssthresh - sock->cwnd_size ); + n -= d; + token_inc = d + d; + sock->cwnd_size += d; + } + + const uint_fast32_t iw = pgm_fp8div (pgm_fp8 (1), sock->cwnd_size); + +/* linear window increase */ + token_inc += pgm_fp8mul (n, pgm_fp8 (1) + iw); + sock->cwnd_size += pgm_fp8mul (n, iw); + sock->tokens = MIN( sock->tokens + token_inc, sock->cwnd_size ); +// pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("PGMCC++ (T:%u W:%u)"), +// pgm_fp8tou (sock->tokens), pgm_fp8tou (sock->cwnd_size)); + } + else + { +/* Look for an unacknowledged data packet which is followed by at least three + * acknowledged data packets, then the packet is assumed to be lost and PGMCC + * reacts by halving the window. + * + * Common value will be 0xfffffff7. + */ + sock->acks_after_loss += new_acks; + if (sock->acks_after_loss >= 3) + { + sock->acks_after_loss = 0; + sock->suspended_sqn = ack_rx_max; + sock->is_congested = TRUE; + sock->cwnd_size = pgm_fp8div (sock->cwnd_size, pgm_fp8 (2)); + if (sock->cwnd_size > sock->tokens) + sock->tokens = 0; + else + sock->tokens -= sock->cwnd_size; + sock->ack_bitmap = 0xffffffff; + pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("PGMCC congestion, half window size (T:%u W:%u)"), + pgm_fp8tou (sock->tokens), pgm_fp8tou (sock->cwnd_size)); + } + } + +/* token is now available so notify tx thread that transmission time is available */ +notify_tx: + if (is_congestion_limited && + sock->tokens >= pgm_fp8 (1)) + { + pgm_notify_send (&sock->ack_notify); + } + return TRUE; +} + +/* ambient/heartbeat SPM's + * + * heartbeat: ihb_tmr decaying between ihb_min and ihb_max 2x after last packet + * + * on success, TRUE is returned, if operation would block, FALSE is returned. + */ + +bool +pgm_send_spm ( + pgm_sock_t* const sock, + const int flags + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != sock->window); + + pgm_debug ("pgm_send_spm (sock:%p flags:%d)", + (const void*)sock, flags); + + size_t tpdu_length = sizeof(struct pgm_header); + if (AF_INET == sock->send_gsr.gsr_group.ss_family) + tpdu_length += sizeof(struct pgm_spm); + else + tpdu_length += sizeof(struct pgm_spm6); + if (sock->use_proactive_parity || + sock->use_ondemand_parity || + sock->is_pending_crqst || + PGM_OPT_FIN == flags) + { + tpdu_length += sizeof(struct pgm_opt_length); +/* forward error correction */ + if (sock->use_proactive_parity || + sock->use_ondemand_parity) + tpdu_length += sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_parity_prm); +/* congestion report request */ + if (sock->is_pending_crqst) + tpdu_length += sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_crqst); +/* end of session */ + if (PGM_OPT_FIN == flags) + tpdu_length += sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fin); + } + char buf[ tpdu_length ]; + if (PGM_UNLIKELY(pgm_mem_gc_friendly)) + memset (buf, 0, tpdu_length); + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_spm* spm = (struct pgm_spm *)(header + 1); + struct pgm_spm6* spm6 = (struct pgm_spm6*)(header + 1); + memcpy (header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + header->pgm_sport = sock->tsi.sport; + header->pgm_dport = sock->dport; + header->pgm_type = PGM_SPM; + header->pgm_options = 0; + header->pgm_tsdu_length = 0; + +/* SPM */ + spm->spm_sqn = htonl (sock->spm_sqn); + spm->spm_trail = htonl (pgm_txw_trail_atomic (sock->window)); + spm->spm_lead = htonl (pgm_txw_lead_atomic (sock->window)); + spm->spm_reserved = 0; +/* our nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&sock->send_addr, (char*)&spm->spm_nla_afi); + +/* PGM options */ + if (sock->use_proactive_parity || + sock->use_ondemand_parity || + sock->is_pending_crqst || + PGM_OPT_FIN == flags) + { + struct pgm_opt_length* opt_len; + struct pgm_opt_header *opt_header, *last_opt_header; + uint16_t opt_total_length; + + if (AF_INET == sock->send_gsr.gsr_group.ss_family) + opt_header = (struct pgm_opt_header*)(spm + 1); + else + opt_header = (struct pgm_opt_header*)(spm6 + 1); + header->pgm_options |= PGM_OPT_PRESENT; + opt_len = (struct pgm_opt_length*)opt_header; + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_total_length = sizeof(struct pgm_opt_length); + last_opt_header = opt_header = (struct pgm_opt_header*)(opt_len + 1); + +/* OPT_PARITY_PRM */ + if (sock->use_proactive_parity || + sock->use_ondemand_parity) + { + header->pgm_options |= PGM_OPT_NETWORK; + opt_total_length += sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_parity_prm); + opt_header->opt_type = PGM_OPT_PARITY_PRM; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_parity_prm); + struct pgm_opt_parity_prm* opt_parity_prm = (struct pgm_opt_parity_prm*)(opt_header + 1); + opt_parity_prm->opt_reserved = (sock->use_proactive_parity ? PGM_PARITY_PRM_PRO : 0) | + (sock->use_ondemand_parity ? PGM_PARITY_PRM_OND : 0); + opt_parity_prm->parity_prm_tgs = htonl (sock->rs_k); + last_opt_header = opt_header; + opt_header = (struct pgm_opt_header*)(opt_parity_prm + 1); + } + +/* OPT_CRQST */ + if (sock->is_pending_crqst) + { + header->pgm_options |= PGM_OPT_NETWORK; + opt_total_length += sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_crqst); + opt_header->opt_type = PGM_OPT_CRQST; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_crqst); + struct pgm_opt_crqst* opt_crqst = (struct pgm_opt_crqst*)(opt_header + 1); +/* request receiver worst path report, OPT_CR_RX_WP */ + opt_crqst->opt_reserved = PGM_OPT_CRQST_RXP; + sock->is_pending_crqst = FALSE; + last_opt_header = opt_header; + opt_header = (struct pgm_opt_header*)(opt_crqst + 1); + } + +/* OPT_FIN */ + if (PGM_OPT_FIN == flags) + { + opt_total_length += sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fin); + opt_header->opt_type = PGM_OPT_FIN; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_fin); + struct pgm_opt_fin* opt_fin = (struct pgm_opt_fin*)(opt_header + 1); + opt_fin->opt_reserved = 0; + last_opt_header = opt_header; + opt_header = (struct pgm_opt_header*)(opt_fin + 1); + } + + last_opt_header->opt_type |= PGM_OPT_END; + opt_len->opt_total_length = htons (opt_total_length); + } + +/* checksum optional for SPMs */ + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + + const ssize_t sent = pgm_sendto (sock, + flags != PGM_OPT_SYN && sock->is_controlled_spm, /* rate limited */ + TRUE, /* with router alert */ + buf, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { + sock->blocklen = tpdu_length; + return FALSE; + } +/* advance SPM sequence only on successful transmission */ + sock->spm_sqn++; + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length); + return TRUE; +} + +/* send a NAK confirm (NCF) message with provided sequence number list. + * + * on success, TRUE is returned, returns FALSE if operation would block. + */ + +static +bool +send_ncf ( + pgm_sock_t* const restrict sock, + const struct sockaddr* const restrict nak_src_nla, + const struct sockaddr* const restrict nak_grp_nla, + const uint32_t sequence, + const bool is_parity /* send parity NCF */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != nak_src_nla); + pgm_assert (NULL != nak_grp_nla); + pgm_assert (nak_src_nla->sa_family == nak_grp_nla->sa_family); + +#ifdef SOURCE_DEBUG + char saddr[INET6_ADDRSTRLEN], gaddr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop (nak_src_nla, saddr, sizeof(saddr)); + pgm_sockaddr_ntop (nak_grp_nla, gaddr, sizeof(gaddr)); + pgm_debug ("send_ncf (sock:%p nak-src-nla:%s nak-grp-nla:%s sequence:%" PRIu32" is-parity:%s)", + (void*)sock, + saddr, + gaddr, + sequence, + is_parity ? "TRUE": "FALSE" + ); +#endif + + size_t tpdu_length = sizeof(struct pgm_header); + tpdu_length += (AF_INET == nak_src_nla->sa_family) ? sizeof(struct pgm_nak) : sizeof(struct pgm_nak6); + char buf[ tpdu_length ]; + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_nak* ncf = (struct pgm_nak *)(header + 1); + struct pgm_nak6* ncf6 = (struct pgm_nak6*)(header + 1); + memcpy (header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + header->pgm_sport = sock->tsi.sport; + header->pgm_dport = sock->dport; + header->pgm_type = PGM_NCF; + header->pgm_options = is_parity ? PGM_OPT_PARITY : 0; + header->pgm_tsdu_length = 0; + +/* NCF */ + ncf->nak_sqn = htonl (sequence); + +/* source nla */ + pgm_sockaddr_to_nla (nak_src_nla, (char*)&ncf->nak_src_nla_afi); + +/* group nla */ + pgm_sockaddr_to_nla (nak_grp_nla, (AF_INET6 == nak_src_nla->sa_family) ? (char*)&ncf6->nak6_grp_nla_afi : (char*)&ncf->nak_grp_nla_afi ); + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + + const ssize_t sent = pgm_sendto (sock, + FALSE, /* not rate limited */ + TRUE, /* with router alert */ + buf, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) + return FALSE; + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length); + return TRUE; +} + +/* A NCF packet with a OPT_NAK_LIST option extension + * + * on success, TRUE is returned. on error, FALSE is returned. + */ + +static +bool +send_ncf_list ( + pgm_sock_t* const restrict sock, + const struct sockaddr* const restrict nak_src_nla, + const struct sockaddr* const restrict nak_grp_nla, + struct pgm_sqn_list_t* const restrict sqn_list, /* will change to network-order */ + const bool is_parity /* send parity NCF */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != nak_src_nla); + pgm_assert (NULL != nak_grp_nla); + pgm_assert (sqn_list->len > 1); + pgm_assert (sqn_list->len <= 63); + pgm_assert (nak_src_nla->sa_family == nak_grp_nla->sa_family); + +#ifdef SOURCE_DEBUG + char saddr[INET6_ADDRSTRLEN], gaddr[INET6_ADDRSTRLEN]; + char list[1024]; + pgm_sockaddr_ntop (nak_src_nla, saddr, sizeof(saddr)); + pgm_sockaddr_ntop (nak_grp_nla, gaddr, sizeof(gaddr)); + sprintf (list, "%" PRIu32, sqn_list->sqn[0]); + for (uint_fast8_t i = 1; i < sqn_list->len; i++) { + char sequence[ 2 + strlen("4294967295") ]; + sprintf (sequence, " %" PRIu32, sqn_list->sqn[i]); + strcat (list, sequence); + } + pgm_debug ("send_ncf_list (sock:%p nak-src-nla:%s nak-grp-nla:%s sqn-list:[%s] is-parity:%s)", + (void*)sock, + saddr, + gaddr, + list, + is_parity ? "TRUE": "FALSE" + ); +#endif + + size_t tpdu_length = sizeof(struct pgm_header) + + sizeof(struct pgm_opt_length) + /* includes header */ + sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(uint32_t) ); + tpdu_length += (AF_INET == nak_src_nla->sa_family) ? sizeof(struct pgm_nak) : sizeof(struct pgm_nak6); + char buf[ tpdu_length ]; + struct pgm_header* header = (struct pgm_header*)buf; + struct pgm_nak* ncf = (struct pgm_nak *)(header + 1); + struct pgm_nak6* ncf6 = (struct pgm_nak6*)(header + 1); + memcpy (header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + header->pgm_sport = sock->tsi.sport; + header->pgm_dport = sock->dport; + header->pgm_type = PGM_NCF; + header->pgm_options = is_parity ? (PGM_OPT_PRESENT | PGM_OPT_NETWORK | PGM_OPT_PARITY) : (PGM_OPT_PRESENT | PGM_OPT_NETWORK); + header->pgm_tsdu_length = 0; +/* NCF */ + ncf->nak_sqn = htonl (sqn_list->sqn[0]); + +/* source nla */ + pgm_sockaddr_to_nla (nak_src_nla, (char*)&ncf->nak_src_nla_afi); + +/* group nla */ + pgm_sockaddr_to_nla (nak_grp_nla, (AF_INET6 == nak_src_nla->sa_family) ? (char*)&ncf6->nak6_grp_nla_afi : (char*)&ncf->nak_grp_nla_afi ); + +/* OPT_NAK_LIST */ + struct pgm_opt_length* opt_len = (AF_INET6 == nak_src_nla->sa_family) ? (struct pgm_opt_length*)(ncf6 + 1) : (struct pgm_opt_length*)(ncf + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(uint32_t) ) ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_NAK_LIST | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(uint32_t) ); + struct pgm_opt_nak_list* opt_nak_list = (struct pgm_opt_nak_list*)(opt_header + 1); + opt_nak_list->opt_reserved = 0; +/* to network-order */ + for (uint_fast8_t i = 1; i < sqn_list->len; i++) + opt_nak_list->opt_sqn[i-1] = htonl (sqn_list->sqn[i]); + + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial (buf, tpdu_length, 0)); + + const ssize_t sent = pgm_sendto (sock, + FALSE, /* not rate limited */ + TRUE, /* with router alert */ + buf, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) + return FALSE; + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length); + return TRUE; +} + +/* cancel any pending heartbeat SPM and schedule a new one + */ + +static +void +reset_heartbeat_spm ( + pgm_sock_t*const sock, + const pgm_time_t now + ) +{ + pgm_mutex_lock (&sock->timer_mutex); + const pgm_time_t next_poll = sock->next_poll; + const pgm_time_t spm_heartbeat_interval = sock->spm_heartbeat_interval[ sock->spm_heartbeat_state = 1 ]; + sock->next_heartbeat_spm = now + spm_heartbeat_interval; + if (pgm_time_after( next_poll, sock->next_heartbeat_spm )) + { + sock->next_poll = sock->next_heartbeat_spm; + if (!sock->is_pending_read) { + pgm_notify_send (&sock->pending_notify); + sock->is_pending_read = TRUE; + } + } + pgm_mutex_unlock (&sock->timer_mutex); +} + +/* state helper for resuming sends + */ +#define STATE(x) (sock->pkt_dontwait_state.x) + +/* send one PGM data packet, transmit window owned memory. + * + * On success, returns PGM_IO_STATUS_NORMAL and the number of data bytes pushed + * into the transmit window and attempted to send to the socket layer is saved + * into bytes_written. On non-blocking sockets, PGM_IO_STATUS_WOULD_BLOCK is + * returned if the send would block. PGM_IO_STATUS_RATE_LIMITED is returned if + * the packet sizes would exceed the current rate limit. + * + * ! always returns successful if data is pushed into the transmit window, even if + * sendto() double fails ¡ we don't want the application to try again as that is the + * reliable socks role. + */ + +static +int +send_odata ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t* const restrict skb, + size_t* restrict bytes_written + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + pgm_assert (skb->len <= sock->max_tsdu); + + pgm_debug ("send_odata (sock:%p skb:%p bytes-written:%p)", + (void*)sock, (void*)skb, (void*)bytes_written); + + const uint16_t tsdu_length = skb->len; + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + const size_t tpdu_length = tsdu_length + pgm_pkt_offset (FALSE, pgmcc_family); + +/* continue if send would block */ + if (sock->is_apdu_eagain) { + STATE(skb)->tstamp = pgm_time_update_now(); + goto retry_send; + } + +/* add PGM header to skbuff */ + STATE(skb) = pgm_skb_get(skb); + STATE(skb)->sock = sock; + STATE(skb)->tstamp = pgm_time_update_now(); + + STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->head; + STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); + memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; + STATE(skb)->pgm_header->pgm_dport = sock->dport; + STATE(skb)->pgm_header->pgm_type = PGM_ODATA; + STATE(skb)->pgm_header->pgm_options = sock->use_pgmcc ? PGM_OPT_PRESENT : 0; + STATE(skb)->pgm_header->pgm_tsdu_length = htons (tsdu_length); + +/* ODATA */ + STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + + STATE(skb)->pgm_header->pgm_checksum = 0; + void* data = STATE(skb)->pgm_data + 1; + if (sock->use_pgmcc) { + struct pgm_opt_length* opt_len = data; + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + ((AF_INET6 == sock->acker_nla.ss_family) ? + sizeof(struct pgm_opt6_pgmcc_data) : + sizeof(struct pgm_opt_pgmcc_data)) ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_PGMCC_DATA | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + ((AF_INET6 == sock->acker_nla.ss_family) ? + sizeof(struct pgm_opt6_pgmcc_data) : + sizeof(struct pgm_opt_pgmcc_data)); + struct pgm_opt_pgmcc_data* pgmcc_data = (struct pgm_opt_pgmcc_data*)(opt_header + 1); + struct pgm_opt6_pgmcc_data* pgmcc_data6 = (struct pgm_opt6_pgmcc_data*)(opt_header + 1); + + pgmcc_data->opt_tstamp = htonl (pgm_to_msecs (STATE(skb)->tstamp)); +/* acker nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&sock->acker_nla, (char*)&pgmcc_data->opt_nla_afi); + if (AF_INET6 == sock->acker_nla.ss_family) + data = (char*)pgmcc_data6 + sizeof(struct pgm_opt6_pgmcc_data); + else + data = (char*)pgmcc_data + sizeof(struct pgm_opt_pgmcc_data); + } + const size_t pgm_header_len = (char*)data - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + STATE(unfolded_odata) = pgm_csum_partial (data, tsdu_length, 0); + STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); + +/* add to transmit window, skb::data set to payload */ + pgm_spinlock_lock (&sock->txw_spinlock); + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); + +/* the transmit window MUST check the user count to ensure it does not + * attempt to send a repair-data packet based on in transit original data. + */ + + ssize_t sent; +retry_send: + +/* congestion control */ + if (sock->use_pgmcc && + sock->tokens < pgm_fp8 (1)) + { +// pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Token limit reached.")); + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + return PGM_IO_STATUS_CONGESTION; /* peer expiration to re-elect ACKer */ + } + + sent = pgm_sendto (sock, + sock->is_controlled_odata, /* rate limited */ + FALSE, /* regular socket */ + STATE(skb)->head, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + if (EAGAIN == errno) { + if (sock->use_pgmcc) + pgm_notify_clear (&sock->ack_notify); + return PGM_IO_STATUS_WOULD_BLOCK; + } + return PGM_IO_STATUS_RATE_LIMITED; + } + +/* save unfolded odata for retransmissions */ + pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); + + sock->is_apdu_eagain = FALSE; + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + if (sock->use_pgmcc) { + sock->tokens -= pgm_fp8 (1); + sock->ack_expiry = STATE(skb)->tstamp + sock->ack_expiry_ivl; + } + + if (PGM_LIKELY((size_t)sent == tpdu_length)) { + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += tsdu_length; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] ++; + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length + sock->iphdr_len); + } + +/* check for end of transmission group */ + if (sock->use_proactive_parity) { + const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); + const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; + if (!((odata_sqn + 1) & ~tg_sqn_mask)) + pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); + } + +/* remove applications reference to skbuff */ + pgm_free_skb (STATE(skb)); + if (bytes_written) + *bytes_written = tsdu_length; + return PGM_IO_STATUS_NORMAL; +} + +/* send one PGM original data packet, callee owned memory. + * + * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets + * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if + * packet size exceeds the current rate limit. + */ + +static +int +send_odata_copy ( + pgm_sock_t* const restrict sock, + const void* restrict tsdu, + const uint16_t tsdu_length, + size_t* restrict bytes_written + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (tsdu_length <= sock->max_tsdu); + if (PGM_LIKELY(tsdu_length)) pgm_assert (NULL != tsdu); + + pgm_debug ("send_odata_copy (sock:%p tsdu:%p tsdu_length:%u bytes-written:%p)", + (void*)sock, tsdu, tsdu_length, (void*)bytes_written); + + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + const size_t tpdu_length = tsdu_length + pgm_pkt_offset (FALSE, pgmcc_family); + +/* continue if blocked mid-apdu, updating timestamp */ + if (sock->is_apdu_eagain) { + STATE(skb)->tstamp = pgm_time_update_now(); + goto retry_send; + } + + STATE(skb) = pgm_alloc_skb (sock->max_tpdu); + STATE(skb)->sock = sock; + STATE(skb)->tstamp = pgm_time_update_now(); + pgm_skb_reserve (STATE(skb), pgm_pkt_offset (FALSE, pgmcc_family)); + pgm_skb_put (STATE(skb), tsdu_length); + + STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->head; + STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); + memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; + STATE(skb)->pgm_header->pgm_dport = sock->dport; + STATE(skb)->pgm_header->pgm_type = PGM_ODATA; + STATE(skb)->pgm_header->pgm_options = sock->use_pgmcc ? PGM_OPT_PRESENT : 0; + STATE(skb)->pgm_header->pgm_tsdu_length = htons (tsdu_length); + +/* ODATA */ + STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + + STATE(skb)->pgm_header->pgm_checksum = 0; + void* data = STATE(skb)->pgm_data + 1; + if (sock->use_pgmcc) { + struct pgm_opt_length* opt_len = data; + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + ((AF_INET6 == sock->acker_nla.ss_family) ? + sizeof(struct pgm_opt6_pgmcc_data) : + sizeof(struct pgm_opt_pgmcc_data)) ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_PGMCC_DATA | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + ((AF_INET6 == sock->acker_nla.ss_family) ? + sizeof(struct pgm_opt6_pgmcc_data) : + sizeof(struct pgm_opt_pgmcc_data)); + struct pgm_opt_pgmcc_data* pgmcc_data = (struct pgm_opt_pgmcc_data*)(opt_header + 1); +/* unused */ +// struct pgm_opt6_pgmcc_data* pgmcc_data6 = (struct pgm_opt6_pgmcc_data*)(opt_header + 1); + + pgmcc_data->opt_reserved = 0; + pgmcc_data->opt_tstamp = htonl (pgm_to_msecs (STATE(skb)->tstamp)); +/* acker nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&sock->acker_nla, (char*)&pgmcc_data->opt_nla_afi); + data = (char*)opt_header + opt_header->opt_length; + } + const size_t pgm_header_len = (char*)data - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + STATE(unfolded_odata) = pgm_csum_partial_copy (tsdu, data, tsdu_length, 0); + STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); + +/* add to transmit window, skb::data set to payload */ + pgm_spinlock_lock (&sock->txw_spinlock); + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); + + ssize_t sent; +retry_send: + +/* congestion control */ + if (sock->use_pgmcc && + sock->tokens < pgm_fp8 (1)) + { +// pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Token limit reached.")); + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + return PGM_IO_STATUS_CONGESTION; + } + + sent = pgm_sendto (sock, + sock->is_controlled_odata, /* rate limited */ + FALSE, /* regular socket */ + STATE(skb)->head, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + if (EAGAIN == errno) { + if (sock->use_pgmcc) + pgm_notify_clear (&sock->ack_notify); + return PGM_IO_STATUS_WOULD_BLOCK; + } + return PGM_IO_STATUS_RATE_LIMITED; + } + + if (sock->use_pgmcc) { + sock->tokens -= pgm_fp8 (1); + pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("PGMCC tokens-- (T:%u W:%u)"), + pgm_fp8tou (sock->tokens), pgm_fp8tou (sock->cwnd_size)); + sock->ack_expiry = STATE(skb)->tstamp + sock->ack_expiry_ivl; + } + +/* save unfolded odata for retransmissions */ + pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); + + sock->is_apdu_eagain = FALSE; + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + + if (PGM_LIKELY((size_t)sent == tpdu_length)) { + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += tsdu_length; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] ++; + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length + sock->iphdr_len); + } + +/* check for end of transmission group */ + if (sock->use_proactive_parity) { + const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); + const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; + if (!((odata_sqn + 1) & ~tg_sqn_mask)) + pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); + } + +/* return data payload length sent */ + if (bytes_written) + *bytes_written = tsdu_length; + return PGM_IO_STATUS_NORMAL; +} + +/* send one PGM original data packet, callee owned scatter/gather io vector + * + * ⎢ DATA₀ ⎢ + * ⎢ DATA₁ ⎢ → send_odatav() → ⎢ TSDU₀ ⎢ → libc + * ⎢ ⋮ ⎢ + * + * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets + * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if + * packet size exceeds the current rate limit. + */ + +static +int +send_odatav ( + pgm_sock_t* const restrict sock, + const struct pgm_iovec* const restrict vector, + const unsigned count, /* number of items in vector */ + size_t* restrict bytes_written + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (count <= PGM_MAX_FRAGMENTS); + if (PGM_LIKELY(count)) pgm_assert (NULL != vector); + + pgm_debug ("send_odatav (sock:%p vector:%p count:%u bytes-written:%p)", + (const void*)sock, (const void*)vector, count, (const void*)bytes_written); + + if (PGM_UNLIKELY(0 == count)) + return send_odata_copy (sock, NULL, 0, bytes_written); + +/* continue if blocked on send */ + if (sock->is_apdu_eagain) + goto retry_send; + + STATE(tsdu_length) = 0; + for (unsigned i = 0; i < count; i++) + { +#ifdef TRANSPORT_DEBUG + if (PGM_LIKELY(vector[i].iov_len)) { + pgm_assert( vector[i].iov_base ); + } +#endif + STATE(tsdu_length) += vector[i].iov_len; + } + pgm_return_val_if_fail (STATE(tsdu_length) <= sock->max_tsdu, PGM_IO_STATUS_ERROR); + + STATE(skb) = pgm_alloc_skb (sock->max_tpdu); + STATE(skb)->sock = sock; + STATE(skb)->tstamp = pgm_time_update_now(); + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + pgm_skb_reserve (STATE(skb), pgm_pkt_offset (FALSE, pgmcc_family)); + pgm_skb_put (STATE(skb), STATE(tsdu_length)); + + STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->data; + STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); + memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; + STATE(skb)->pgm_header->pgm_dport = sock->dport; + STATE(skb)->pgm_header->pgm_type = PGM_ODATA; + STATE(skb)->pgm_header->pgm_options = 0; + STATE(skb)->pgm_header->pgm_tsdu_length = htons (STATE(tsdu_length)); + +/* ODATA */ + STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + + STATE(skb)->pgm_header->pgm_checksum = 0; + const size_t pgm_header_len = (char*)(STATE(skb)->pgm_data + 1) - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + +/* unroll first iteration to make friendly branch prediction */ + char* dst = (char*)(STATE(skb)->pgm_data + 1); + STATE(unfolded_odata) = pgm_csum_partial_copy ((const char*)vector[0].iov_base, dst, vector[0].iov_len, 0); + +/* iterate over one or more vector elements to perform scatter/gather checksum & copy */ + for (unsigned i = 1; i < count; i++) { + dst += vector[i-1].iov_len; + const uint32_t unfolded_element = pgm_csum_partial_copy ((const char*)vector[i].iov_base, dst, vector[i].iov_len, 0); + STATE(unfolded_odata) = pgm_csum_block_add (STATE(unfolded_odata), unfolded_element, vector[i-1].iov_len); + } + + STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); + +/* add to transmit window, skb::data set to payload */ + pgm_spinlock_lock (&sock->txw_spinlock); + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); + + ssize_t sent; + size_t tpdu_length; +retry_send: + pgm_assert ((char*)STATE(skb)->tail > (char*)STATE(skb)->head); + tpdu_length = (char*)STATE(skb)->tail - (char*)STATE(skb)->head; + sent = pgm_sendto (sock, + sock->is_controlled_odata, /* rate limited */ + FALSE, /* regular socket */ + STATE(skb)->head, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + if (EAGAIN == errno) { + if (sock->use_pgmcc) + pgm_notify_clear (&sock->ack_notify); + return PGM_IO_STATUS_WOULD_BLOCK; + } + return PGM_IO_STATUS_RATE_LIMITED; + } + +/* save unfolded odata for retransmissions */ + pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); + + sock->is_apdu_eagain = FALSE; + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + + if (PGM_LIKELY((size_t)sent == STATE(skb)->len)) { + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += STATE(tsdu_length); + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] ++; + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length + sock->iphdr_len); + } + +/* check for end of transmission group */ + if (sock->use_proactive_parity) { + const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); + const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; + if (!((odata_sqn + 1) & ~tg_sqn_mask)) + pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); + } + +/* return data payload length sent */ + if (bytes_written) + *bytes_written = STATE(tsdu_length); + return PGM_IO_STATUS_NORMAL; +} + +/* send PGM original data, callee owned memory. if larger than maximum TPDU + * size will be fragmented. + * + * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets + * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if + * packet size exceeds the current rate limit. + */ + +static +int +send_apdu ( + pgm_sock_t* const restrict sock, + const void* restrict apdu, + const size_t apdu_length, + size_t* restrict bytes_written + ) +{ + size_t bytes_sent = 0; /* counted at IP layer */ + unsigned packets_sent = 0; /* IP packets */ + size_t data_bytes_sent = 0; + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + + pgm_assert (NULL != sock); + pgm_assert (NULL != apdu); + +/* continue if blocked mid-apdu */ + if (sock->is_apdu_eagain) + goto retry_send; + +/* if non-blocking calculate total wire size and check rate limit */ + STATE(is_rate_limited) = FALSE; + if (sock->is_nonblocking && sock->is_controlled_odata) + { + const size_t header_length = pgm_pkt_offset (TRUE, pgmcc_family); + size_t tpdu_length = 0; + size_t offset_ = 0; + do { + const uint16_t tsdu_length = MIN( source_max_tsdu (sock, TRUE), apdu_length - offset_ ); + tpdu_length += sock->iphdr_len + header_length + tsdu_length; + offset_ += tsdu_length; + } while (offset_ < apdu_length); + +/* calculation includes one iphdr length already */ + if (!pgm_rate_check (&sock->rate_control, + tpdu_length - sock->iphdr_len, + sock->is_nonblocking)) + { + sock->blocklen = tpdu_length; + return PGM_IO_STATUS_RATE_LIMITED; + } + STATE(is_rate_limited) = TRUE; + } + + STATE(data_bytes_offset) = 0; + STATE(first_sqn) = pgm_txw_next_lead(sock->window); + + do { +/* retrieve packet storage from transmit window */ + size_t header_length = pgm_pkt_offset (TRUE, pgmcc_family); + STATE(tsdu_length) = MIN( source_max_tsdu (sock, TRUE), apdu_length - STATE(data_bytes_offset) ); + + STATE(skb) = pgm_alloc_skb (sock->max_tpdu); + STATE(skb)->sock = sock; + STATE(skb)->tstamp = pgm_time_update_now(); + pgm_skb_reserve (STATE(skb), header_length); + pgm_skb_put (STATE(skb), STATE(tsdu_length)); + + STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->head; + STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); + memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; + STATE(skb)->pgm_header->pgm_dport = sock->dport; + STATE(skb)->pgm_header->pgm_type = PGM_ODATA; + STATE(skb)->pgm_header->pgm_options = PGM_OPT_PRESENT; + STATE(skb)->pgm_header->pgm_tsdu_length = htons (STATE(tsdu_length)); + +/* ODATA */ + STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + +/* OPT_LENGTH */ + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(STATE(skb)->pgm_data + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment) ); +/* OPT_FRAGMENT */ + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_FRAGMENT | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment); + STATE(skb)->pgm_opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); + STATE(skb)->pgm_opt_fragment->opt_reserved = 0; + STATE(skb)->pgm_opt_fragment->opt_sqn = htonl (STATE(first_sqn)); + STATE(skb)->pgm_opt_fragment->opt_frag_off = htonl (STATE(data_bytes_offset)); + STATE(skb)->pgm_opt_fragment->opt_frag_len = htonl (apdu_length); + +/* TODO: the assembly checksum & copy routine is faster than memcpy & pgm_cksum on >= opteron hardware */ + STATE(skb)->pgm_header->pgm_checksum = 0; + const size_t pgm_header_len = (char*)(STATE(skb)->pgm_opt_fragment + 1) - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + STATE(unfolded_odata) = pgm_csum_partial_copy ((const char*)apdu + STATE(data_bytes_offset), STATE(skb)->pgm_opt_fragment + 1, STATE(tsdu_length), 0); + STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); + +/* add to transmit window, skb::data set to payload */ + pgm_spinlock_lock (&sock->txw_spinlock); + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); + + ssize_t sent; + size_t tpdu_length; +retry_send: + pgm_assert ((char*)STATE(skb)->tail > (char*)STATE(skb)->head); + tpdu_length = (char*)STATE(skb)->tail - (char*)STATE(skb)->head; + sent = pgm_sendto (sock, + !STATE(is_rate_limited), /* rate limit on blocking */ + FALSE, /* regular socket */ + STATE(skb)->head, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + goto blocked; + } + +/* save unfolded odata for retransmissions */ + pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); + + if (PGM_LIKELY((size_t)sent == tpdu_length)) { + bytes_sent += tpdu_length + sock->iphdr_len; /* as counted at IP layer */ + packets_sent++; /* IP packets */ + data_bytes_sent += STATE(tsdu_length); + } + + STATE(data_bytes_offset) += STATE(tsdu_length); + +/* check for end of transmission group */ + if (sock->use_proactive_parity) { + const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); + const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; + if (!((odata_sqn + 1) & ~tg_sqn_mask)) + pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); + } + + } while ( STATE(data_bytes_offset) < apdu_length); + pgm_assert( STATE(data_bytes_offset) == apdu_length ); + + sock->is_apdu_eagain = FALSE; + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; + if (bytes_written) + *bytes_written = apdu_length; + return PGM_IO_STATUS_NORMAL; + +blocked: + if (bytes_sent) { + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; + } + if (EAGAIN == errno) { + if (sock->use_pgmcc) + pgm_notify_clear (&sock->ack_notify); + return PGM_IO_STATUS_WOULD_BLOCK; + } + return PGM_IO_STATUS_RATE_LIMITED; +} + +/* Send one APDU, whether it fits within one TPDU or more. + * + * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets + * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if + * packet size exceeds the current rate limit. + */ +int +pgm_send ( + pgm_sock_t* const restrict sock, + const void* restrict apdu, + const size_t apdu_length, + size_t* restrict bytes_written + ) +{ + pgm_debug ("pgm_send (sock:%p apdu:%p apdu-length:%zu bytes-written:%p)", + (void*)sock, apdu, apdu_length, (void*)bytes_written); + +/* parameters */ + pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); + if (PGM_LIKELY(apdu_length)) pgm_return_val_if_fail (NULL != apdu, PGM_IO_STATUS_ERROR); + +/* shutdown */ + if (PGM_UNLIKELY(!pgm_rwlock_reader_trylock (&sock->lock))) + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + +/* state */ + if (PGM_UNLIKELY(!sock->is_bound || + sock->is_destroyed || + apdu_length > sock->max_apdu)) + { + pgm_rwlock_reader_unlock (&sock->lock); + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + } + +/* source */ + pgm_mutex_lock (&sock->source_mutex); + +/* pass on non-fragment calls */ + if (apdu_length <= sock->max_tsdu) + { + const int status = send_odata_copy (sock, apdu, apdu_length, bytes_written); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + } + + const int status = send_apdu (sock, apdu, apdu_length, bytes_written); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; +} + +/* send PGM original data, callee owned scatter/gather IO vector. if larger than maximum TPDU + * size will be fragmented. + * + * is_one_apdu = true: + * + * ⎢ DATA₀ ⎢ + * ⎢ DATA₁ ⎢ → pgm_sendv() → ⎢ ⋯ TSDU₁ TSDU₀ ⎢ → libc + * ⎢ ⋮ ⎢ + * + * is_one_apdu = false: + * + * ⎢ APDU₀ ⎢ ⎢ ⋯ TSDU₁,₀ TSDU₀,₀ ⎢ + * ⎢ APDU₁ ⎢ → pgm_sendv() → ⎢ ⋯ TSDU₁,₁ TSDU₀,₁ ⎢ → libc + * ⎢ ⋮ ⎢ ⎢ ⋮ ⋮ ⎢ + * + * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets + * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if + * packet size exceeds the current rate limit. + */ + +int +pgm_sendv ( + pgm_sock_t* const restrict sock, + const struct pgm_iovec* const restrict vector, + const unsigned count, /* number of items in vector */ + const bool is_one_apdu, /* true = vector = apdu, false = vector::iov_base = apdu */ + size_t* restrict bytes_written + ) +{ + pgm_debug ("pgm_sendv (sock:%p vector:%p count:%u is-one-apdu:%s bytes-written:%p)", + (const void*)sock, + (const void*)vector, + count, + is_one_apdu ? "TRUE" : "FALSE", + (const void*)bytes_written); + + pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); + pgm_return_val_if_fail (count <= PGM_MAX_FRAGMENTS, PGM_IO_STATUS_ERROR); + if (PGM_LIKELY(count)) pgm_return_val_if_fail (NULL != vector, PGM_IO_STATUS_ERROR); + if (PGM_UNLIKELY(!pgm_rwlock_reader_trylock (&sock->lock))) + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + if (PGM_UNLIKELY(!sock->is_bound || + sock->is_destroyed)) + { + pgm_rwlock_reader_unlock (&sock->lock); + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + } + + pgm_mutex_lock (&sock->source_mutex); + +/* pass on zero length as cannot count vector lengths */ + if (PGM_UNLIKELY(0 == count)) + { + const int status = send_odata_copy (sock, NULL, count, bytes_written); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + } + + size_t bytes_sent = 0; + unsigned packets_sent = 0; + size_t data_bytes_sent = 0; + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + +/* continue if blocked mid-apdu */ + if (sock->is_apdu_eagain) { + if (is_one_apdu) { + if (STATE(apdu_length) <= sock->max_tsdu) + { + const int status = send_odatav (sock, vector, count, bytes_written); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + } + else + goto retry_one_apdu_send; + } else { + goto retry_send; + } + } + +/* calculate (total) APDU length */ + STATE(apdu_length) = 0; + for (unsigned i = 0; i < count; i++) + { +#ifdef TRANSPORT_DEBUG + if (PGM_LIKELY(vector[i].iov_len)) { + pgm_assert( vector[i].iov_base ); + } +#endif + if (!is_one_apdu && + vector[i].iov_len > sock->max_apdu) + { + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + } + STATE(apdu_length) += vector[i].iov_len; + } + +/* pass on non-fragment calls */ + if (is_one_apdu) { + if (STATE(apdu_length) <= sock->max_tsdu) { + const int status = send_odatav (sock, vector, count, bytes_written); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + } else if (STATE(apdu_length) > sock->max_apdu) { + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + } + } + +/* if non-blocking calculate total wire size and check rate limit */ + STATE(is_rate_limited) = FALSE; + if (sock->is_nonblocking && sock->is_controlled_odata) + { + const size_t header_length = pgm_pkt_offset (TRUE, pgmcc_family); + size_t tpdu_length = 0; + size_t offset_ = 0; + do { + const uint16_t tsdu_length = MIN( source_max_tsdu (sock, TRUE), STATE(apdu_length) - offset_ ); + tpdu_length += sock->iphdr_len + header_length + tsdu_length; + offset_ += tsdu_length; + } while (offset_ < STATE(apdu_length)); + +/* calculation includes one iphdr length already */ + if (!pgm_rate_check (&sock->rate_control, + tpdu_length - sock->iphdr_len, + sock->is_nonblocking)) + { + sock->blocklen = tpdu_length; + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_RATE_LIMITED; + } + STATE(is_rate_limited) = TRUE; + } + +/* non-fragmented packets can be forwarded onto basic send() */ + if (!is_one_apdu) + { + for (STATE(data_pkt_offset) = 0; STATE(data_pkt_offset) < count; STATE(data_pkt_offset)++) + { + size_t wrote_bytes; + int status; +retry_send: + status = send_apdu (sock, + vector[STATE(data_pkt_offset)].iov_base, + vector[STATE(data_pkt_offset)].iov_len, + &wrote_bytes); + switch (status) { + case PGM_IO_STATUS_NORMAL: + break; + case PGM_IO_STATUS_WOULD_BLOCK: + case PGM_IO_STATUS_RATE_LIMITED: + sock->is_apdu_eagain = TRUE; + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + case PGM_IO_STATUS_ERROR: + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + default: + pgm_assert_not_reached(); + } + data_bytes_sent += wrote_bytes; + } + + sock->is_apdu_eagain = FALSE; + if (bytes_written) + *bytes_written = data_bytes_sent; + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_NORMAL; + } + + STATE(data_bytes_offset) = 0; + STATE(vector_index) = 0; + STATE(vector_offset) = 0; + + STATE(first_sqn) = pgm_txw_next_lead(sock->window); + + do { +/* retrieve packet storage from transmit window */ + size_t header_length = pgm_pkt_offset (TRUE, pgmcc_family); + STATE(tsdu_length) = MIN( source_max_tsdu (sock, TRUE), STATE(apdu_length) - STATE(data_bytes_offset) ); + STATE(skb) = pgm_alloc_skb (sock->max_tpdu); + STATE(skb)->sock = sock; + STATE(skb)->tstamp = pgm_time_update_now(); + pgm_skb_reserve (STATE(skb), header_length); + pgm_skb_put (STATE(skb), STATE(tsdu_length)); + + STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->head; + STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); + memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; + STATE(skb)->pgm_header->pgm_dport = sock->dport; + STATE(skb)->pgm_header->pgm_type = PGM_ODATA; + STATE(skb)->pgm_header->pgm_options = PGM_OPT_PRESENT; + STATE(skb)->pgm_header->pgm_tsdu_length = htons (STATE(tsdu_length)); + +/* ODATA */ + STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + +/* OPT_LENGTH */ + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(STATE(skb)->pgm_data + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment) ); +/* OPT_FRAGMENT */ + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_FRAGMENT | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment); + STATE(skb)->pgm_opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); + STATE(skb)->pgm_opt_fragment->opt_reserved = 0; + STATE(skb)->pgm_opt_fragment->opt_sqn = htonl (STATE(first_sqn)); + STATE(skb)->pgm_opt_fragment->opt_frag_off = htonl (STATE(data_bytes_offset)); + STATE(skb)->pgm_opt_fragment->opt_frag_len = htonl (STATE(apdu_length)); + +/* checksum & copy */ + STATE(skb)->pgm_header->pgm_checksum = 0; + const size_t pgm_header_len = (char*)(STATE(skb)->pgm_opt_fragment + 1) - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + +/* iterate over one or more vector elements to perform scatter/gather checksum & copy + * + * STATE(vector_index) - index into application scatter/gather vector + * STATE(vector_offset) - current offset into current vector element + * STATE(unfolded_odata)- checksum accumulator + */ + const char* src = (const char*)vector[STATE(vector_index)].iov_base + STATE(vector_offset); + char* dst = (char*)(STATE(skb)->pgm_opt_fragment + 1); + size_t src_length = vector[STATE(vector_index)].iov_len - STATE(vector_offset); + size_t dst_length = 0; + size_t copy_length = MIN( STATE(tsdu_length), src_length ); + STATE(unfolded_odata) = pgm_csum_partial_copy (src, dst, copy_length, 0); + + for(;;) + { + if (copy_length == src_length) { +/* application packet complete */ + STATE(vector_index)++; + STATE(vector_offset) = 0; + } else { +/* data still remaining */ + STATE(vector_offset) += copy_length; + } + + dst_length += copy_length; + +/* sock packet complete */ + if (dst_length == STATE(tsdu_length)) + break; + + src = (const char*)vector[STATE(vector_index)].iov_base + STATE(vector_offset); + dst += copy_length; + src_length = vector[STATE(vector_index)].iov_len - STATE(vector_offset); + copy_length = MIN( STATE(tsdu_length) - dst_length, src_length ); + const uint32_t unfolded_element = pgm_csum_partial_copy (src, dst, copy_length, 0); + STATE(unfolded_odata) = pgm_csum_block_add (STATE(unfolded_odata), unfolded_element, dst_length); + } + + STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); + +/* add to transmit window, skb::data set to payload */ + pgm_spinlock_lock (&sock->txw_spinlock); + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); + + ssize_t sent; + size_t tpdu_length; +retry_one_apdu_send: + tpdu_length = (char*)STATE(skb)->tail - (char*)STATE(skb)->head; + sent = pgm_sendto (sock, + !STATE(is_rate_limited), /* rate limited on blocking */ + FALSE, /* regular socket */ + STATE(skb)->head, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + goto blocked; + } + +/* save unfolded odata for retransmissions */ + pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); + + if (PGM_LIKELY((size_t)sent == tpdu_length)) { + bytes_sent += tpdu_length + sock->iphdr_len; /* as counted at IP layer */ + packets_sent++; /* IP packets */ + data_bytes_sent += STATE(tsdu_length); + } + + STATE(data_bytes_offset) += STATE(tsdu_length); + +/* check for end of transmission group */ + if (sock->use_proactive_parity) { + const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); + const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; + if (!((odata_sqn + 1) & ~tg_sqn_mask)) + pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); + } + + } while ( STATE(data_bytes_offset) < STATE(apdu_length) ); + pgm_assert( STATE(data_bytes_offset) == STATE(apdu_length) ); + + sock->is_apdu_eagain = FALSE; + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; + if (bytes_written) + *bytes_written = STATE(apdu_length); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_NORMAL; + +blocked: + if (bytes_sent) { + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; + } + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + if (EAGAIN == errno) { + if (sock->use_pgmcc) + pgm_notify_clear (&sock->ack_notify); + return PGM_IO_STATUS_WOULD_BLOCK; + } + return PGM_IO_STATUS_RATE_LIMITED; +} + +/* send PGM original data, transmit window owned scatter/gather IO vector. + * + * ⎢ TSDU₀ ⎢ + * ⎢ TSDU₁ ⎢ → pgm_send_skbv() → ⎢ ⋯ TSDU₁ TSDU₀ ⎢ → libc + * ⎢ ⋮ ⎢ + * + * on success, returns PGM_IO_STATUS_NORMAL, on block for non-blocking sockets + * returns PGM_IO_STATUS_WOULD_BLOCK, returns PGM_IO_STATUS_RATE_LIMITED if + * packet size exceeds the current rate limit. + */ + +int +pgm_send_skbv ( + pgm_sock_t* const restrict sock, + struct pgm_sk_buff_t** const restrict vector, /* array of skb pointers vs. array of skbs */ + const unsigned count, + const bool is_one_apdu, /* true: vector = apdu, false: vector::iov_base = apdu */ + size_t* restrict bytes_written + ) +{ + pgm_debug ("pgm_send_skbv (sock:%p vector:%p count:%u is-one-apdu:%s bytes-written:%p)", + (const void*)sock, + (const void*)vector, + count, + is_one_apdu ? "TRUE" : "FALSE", + (const void*)bytes_written); + + pgm_return_val_if_fail (NULL != sock, PGM_IO_STATUS_ERROR); + pgm_return_val_if_fail (count <= PGM_MAX_FRAGMENTS, PGM_IO_STATUS_ERROR); + if (PGM_LIKELY(count)) pgm_return_val_if_fail (NULL != vector, PGM_IO_STATUS_ERROR); + if (PGM_UNLIKELY(!pgm_rwlock_reader_trylock (&sock->lock))) + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + if (PGM_UNLIKELY(!sock->is_bound || + sock->is_destroyed)) + { + pgm_rwlock_reader_unlock (&sock->lock); + pgm_return_val_if_reached (PGM_IO_STATUS_ERROR); + } + + pgm_mutex_lock (&sock->source_mutex); + +/* pass on zero length as cannot count vector lengths */ + if (PGM_UNLIKELY(0 == count)) + { + const int status = send_odata_copy (sock, NULL, count, bytes_written); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + } + else if (1 == count) + { + const int status = send_odata (sock, vector[0], bytes_written); + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return status; + } + + size_t bytes_sent = 0; + unsigned packets_sent = 0; + size_t data_bytes_sent = 0; + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + +/* continue if blocked mid-apdu */ + if (sock->is_apdu_eagain) + goto retry_send; + + STATE(is_rate_limited) = FALSE; + if (sock->is_nonblocking && sock->is_controlled_odata) + { + size_t total_tpdu_length = 0; + for (unsigned i = 0; i < count; i++) + total_tpdu_length += sock->iphdr_len + pgm_pkt_offset (is_one_apdu, pgmcc_family) + vector[i]->len; + +/* calculation includes one iphdr length already */ + if (!pgm_rate_check (&sock->rate_control, + total_tpdu_length - sock->iphdr_len, + sock->is_nonblocking)) + { + sock->blocklen = total_tpdu_length; + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_RATE_LIMITED; + } + STATE(is_rate_limited) = TRUE; + } + + if (is_one_apdu) + { + STATE(apdu_length) = 0; + STATE(first_sqn) = pgm_txw_next_lead(sock->window); + for (unsigned i = 0; i < count; i++) + { + if (PGM_UNLIKELY(vector[i]->len > sock->max_tsdu_fragment)) { + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_ERROR; + } + STATE(apdu_length) += vector[i]->len; + } + if (PGM_UNLIKELY(STATE(apdu_length) > sock->max_apdu)) { + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_ERROR; + } + } + + for (STATE(vector_index) = 0; STATE(vector_index) < count; STATE(vector_index)++) + { + STATE(tsdu_length) = vector[STATE(vector_index)]->len; + + STATE(skb) = pgm_skb_get(vector[STATE(vector_index)]); + STATE(skb)->sock = sock; + STATE(skb)->tstamp = pgm_time_update_now(); + + STATE(skb)->pgm_header = (struct pgm_header*)STATE(skb)->head; + STATE(skb)->pgm_data = (struct pgm_data*)(STATE(skb)->pgm_header + 1); + memcpy (STATE(skb)->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + STATE(skb)->pgm_header->pgm_sport = sock->tsi.sport; + STATE(skb)->pgm_header->pgm_dport = sock->dport; + STATE(skb)->pgm_header->pgm_type = PGM_ODATA; + STATE(skb)->pgm_header->pgm_options = is_one_apdu ? PGM_OPT_PRESENT : 0; + STATE(skb)->pgm_header->pgm_tsdu_length = htons (STATE(tsdu_length)); + +/* ODATA */ + STATE(skb)->pgm_data->data_sqn = htonl (pgm_txw_next_lead(sock->window)); + STATE(skb)->pgm_data->data_trail = htonl (pgm_txw_trail(sock->window)); + + if (is_one_apdu) + { +/* OPT_LENGTH */ + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(STATE(skb)->pgm_data + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment) ); +/* OPT_FRAGMENT */ + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_FRAGMENT | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment); + STATE(skb)->pgm_opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); + STATE(skb)->pgm_opt_fragment->opt_reserved = 0; + STATE(skb)->pgm_opt_fragment->opt_sqn = htonl (STATE(first_sqn)); + STATE(skb)->pgm_opt_fragment->opt_frag_off = htonl (STATE(data_bytes_offset)); + STATE(skb)->pgm_opt_fragment->opt_frag_len = htonl (STATE(apdu_length)); + + pgm_assert (STATE(skb)->data == (STATE(skb)->pgm_opt_fragment + 1)); + } + else + { + pgm_assert (STATE(skb)->data == (STATE(skb)->pgm_data + 1)); + } + +/* TODO: the assembly checksum & copy routine is faster than memcpy & pgm_cksum on >= opteron hardware */ + STATE(skb)->pgm_header->pgm_checksum = 0; + pgm_assert ((char*)STATE(skb)->data > (char*)STATE(skb)->pgm_header); + const size_t pgm_header_len = (char*)STATE(skb)->data - (char*)STATE(skb)->pgm_header; + const uint32_t unfolded_header = pgm_csum_partial (STATE(skb)->pgm_header, pgm_header_len, 0); + STATE(unfolded_odata) = pgm_csum_partial ((char*)STATE(skb)->data, STATE(tsdu_length), 0); + STATE(skb)->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, STATE(unfolded_odata), pgm_header_len)); + +/* add to transmit window, skb::data set to payload */ + pgm_spinlock_lock (&sock->txw_spinlock); + pgm_txw_add (sock->window, STATE(skb)); + pgm_spinlock_unlock (&sock->txw_spinlock); + ssize_t sent; + size_t tpdu_length; +retry_send: + pgm_assert ((char*)STATE(skb)->tail > (char*)STATE(skb)->head); + tpdu_length = (char*)STATE(skb)->tail - (char*)STATE(skb)->head; + sent = pgm_sendto (sock, + !STATE(is_rate_limited), /* rate limited on blocking */ + FALSE, /* regular socket */ + STATE(skb)->head, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + if (sent < 0 && (EAGAIN == errno || ENOBUFS == errno)) { + sock->is_apdu_eagain = TRUE; + sock->blocklen = tpdu_length; + goto blocked; + } + +/* save unfolded odata for retransmissions */ + pgm_txw_set_unfolded_checksum (STATE(skb), STATE(unfolded_odata)); + + if (PGM_LIKELY((size_t)sent == tpdu_length)) { + bytes_sent += tpdu_length + sock->iphdr_len; /* as counted at IP layer */ + packets_sent++; /* IP packets */ + data_bytes_sent += STATE(tsdu_length); + } + + pgm_free_skb (STATE(skb)); + STATE(data_bytes_offset) += STATE(tsdu_length); + +/* check for end of transmission group */ + if (sock->use_proactive_parity) { + const uint32_t odata_sqn = ntohl (STATE(skb)->pgm_data->data_sqn); + const uint32_t tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; + if (!((odata_sqn + 1) & ~tg_sqn_mask)) + pgm_schedule_proactive_nak (sock, odata_sqn & tg_sqn_mask); + } + + } +#ifdef TRANSPORT_DEBUG + if (is_one_apdu) + { + pgm_assert( STATE(data_bytes_offset) == STATE(apdu_length) ); + } +#endif + + sock->is_apdu_eagain = FALSE; + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; + if (bytes_written) + *bytes_written = data_bytes_sent; + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + return PGM_IO_STATUS_NORMAL; + +blocked: + if (bytes_sent) { + reset_heartbeat_spm (sock, STATE(skb)->tstamp); + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], bytes_sent); + sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT] += packets_sent; + sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT] += data_bytes_sent; + } + pgm_mutex_unlock (&sock->source_mutex); + pgm_rwlock_reader_unlock (&sock->lock); + if (EAGAIN == errno) { + if (sock->use_pgmcc) + pgm_notify_clear (&sock->ack_notify); + return PGM_IO_STATUS_WOULD_BLOCK; + } + return PGM_IO_STATUS_RATE_LIMITED; +} + +/* cleanup resuming send state helper + */ +#undef STATE + +/* send repair packet. + * + * on success, TRUE is returned. on error, FALSE is returned. + */ + +static +bool +send_rdata ( + pgm_sock_t* restrict sock, + struct pgm_sk_buff_t* restrict skb + ) +{ +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (NULL != skb); + pgm_assert ((char*)skb->tail > (char*)skb->head); + + const size_t tpdu_length = (char*)skb->tail - (char*)skb->head; + +/* update previous odata/rdata contents */ + struct pgm_header* header = skb->pgm_header; + struct pgm_data* rdata = skb->pgm_data; + header->pgm_type = PGM_RDATA; +/* RDATA */ + rdata->data_trail = htonl (pgm_txw_trail(sock->window)); + + header->pgm_checksum = 0; + const size_t pgm_header_len = tpdu_length - ntohs(header->pgm_tsdu_length); + uint32_t unfolded_header = pgm_csum_partial (header, pgm_header_len, 0); + uint32_t unfolded_odata = pgm_csum_partial (skb->data, ntohs(header->pgm_tsdu_length), 0); + header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, unfolded_odata, pgm_header_len)); + +/* congestion control */ + if (sock->use_pgmcc && + sock->tokens < pgm_fp8 (1)) + { +// pgm_trace (PGM_LOG_ROLE_CONGESTION_CONTROL,_("Token limit reached.")); + sock->blocklen = tpdu_length; + return FALSE; + } + + const ssize_t sent = pgm_sendto (sock, + sock->is_controlled_rdata, /* rate limited */ + TRUE, /* with router alert */ + header, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + + if (sent < 0 && EAGAIN == errno) { + sock->blocklen = tpdu_length; + return FALSE; + } + + const pgm_time_t now = pgm_time_update_now(); + + if (sock->use_pgmcc) { + sock->tokens -= pgm_fp8 (1); + sock->ack_expiry = now + sock->ack_expiry_ivl; + } + +/* re-set spm timer: we are already in the timer thread, no need to prod timers + */ + pgm_mutex_lock (&sock->timer_mutex); + sock->spm_heartbeat_state = 1; + sock->next_heartbeat_spm = now + sock->spm_heartbeat_interval[sock->spm_heartbeat_state++]; + pgm_mutex_unlock (&sock->timer_mutex); + + pgm_txw_inc_retransmit_count (skb); + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_BYTES_RETRANSMITTED] += ntohs(header->pgm_tsdu_length); + sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED]++; /* impossible to determine APDU count */ + pgm_atomic_add32 (&sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT], tpdu_length + sock->iphdr_len); + return TRUE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/source.c.rej b/3rdparty/openpgm-svn-r1135/pgm/source.c.rej new file mode 100644 index 0000000..515f7ec --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/source.c.rej @@ -0,0 +1,17 @@ +*************** +*** 2295,2301 **** + header->pgm_checksum = 0; + const size_t pgm_header_len = tpdu_length - ntohs(header->pgm_tsdu_length); + uint32_t unfolded_header = pgm_csum_partial (header, pgm_header_len, 0); +- uint32_t unfolded_odata = pgm_txw_get_unfolded_checksum (skb); + header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, unfolded_odata, pgm_header_len)); + + /* congestion control */ +--- 2295,2301 ---- + header->pgm_checksum = 0; + const size_t pgm_header_len = tpdu_length - ntohs(header->pgm_tsdu_length); + uint32_t unfolded_header = pgm_csum_partial (header, pgm_header_len, 0); ++ uint32_t unfolded_odata = pgm_csum_partial (skb->data, ntohs(header->pgm_tsdu_length), 0); + header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, unfolded_odata, pgm_header_len)); + + /* congestion control */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/source_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/source_unittest.c new file mode 100644 index 0000000..d34f165 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/source_unittest.c @@ -0,0 +1,1248 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for PGM source transport. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* mock state */ + +#define TEST_NETWORK "" +#define TEST_PORT 7500 +#define TEST_MAX_TPDU 1500 +#define TEST_TXW_SQNS 32 +#define TEST_RXW_SQNS 32 +#define TEST_HOPS 16 +#define TEST_SPM_AMBIENT ( pgm_secs(30) ) +#define TEST_SPM_HEARTBEAT_INIT { pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(100), pgm_msecs(1300), pgm_secs(7), pgm_secs(16), pgm_secs(25), pgm_secs(30) } +#define TEST_PEER_EXPIRY ( pgm_secs(300) ) +#define TEST_SPMR_EXPIRY ( pgm_msecs(250) ) +#define TEST_NAK_BO_IVL ( pgm_msecs(50) ) +#define TEST_NAK_RPT_IVL ( pgm_secs(2) ) +#define TEST_NAK_RDATA_IVL ( pgm_secs(2) ) +#define TEST_NAK_DATA_RETRIES 5 +#define TEST_NAK_NCF_RETRIES 2 + +static gboolean mock_is_valid_spmr = TRUE; +static gboolean mock_is_valid_ack = TRUE; +static gboolean mock_is_valid_nak = TRUE; +static gboolean mock_is_valid_nnak = TRUE; + + +#define pgm_txw_get_unfolded_checksum mock_pgm_txw_get_unfolded_checksum +#define pgm_txw_set_unfolded_checksum mock_pgm_txw_set_unfolded_checksum +#define pgm_txw_inc_retransmit_count mock_pgm_txw_inc_retransmit_count +#define pgm_txw_add mock_pgm_txw_add +#define pgm_txw_peek mock_pgm_txw_peek +#define pgm_txw_retransmit_push mock_pgm_txw_retransmit_push +#define pgm_txw_retransmit_try_peek mock_pgm_txw_retransmit_try_peek +#define pgm_txw_retransmit_remove_head mock_pgm_txw_retransmit_remove_head +#define pgm_rs_encode mock_pgm_rs_encode +#define pgm_rate_check mock_pgm_rate_check +#define pgm_verify_spmr mock_pgm_verify_spmr +#define pgm_verify_ack mock_pgm_verify_ack +#define pgm_verify_nak mock_pgm_verify_nak +#define pgm_verify_nnak mock_pgm_verify_nnak +#define pgm_compat_csum_partial mock_pgm_compat_csum_partial +#define pgm_compat_csum_partial_copy mock_pgm_compat_csum_partial_copy +#define pgm_csum_block_add mock_pgm_csum_block_add +#define pgm_csum_fold mock_pgm_csum_fold +#define pgm_sendto_hops mock_pgm_sendto_hops +#define pgm_time_update_now mock_pgm_time_update_now +#define pgm_setsockopt mock_pgm_setsockopt + + +#define SOURCE_DEBUG +#include "source.c" + + +static +void +mock_setup (void) +{ + if (!g_thread_supported ()) g_thread_init (NULL); +} + +static +struct pgm_sock_t* +generate_sock (void) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, g_htons(1000) }; + struct pgm_sock_t* sock = g_new0 (struct pgm_sock_t, 1); + memcpy (&sock->tsi, &tsi, sizeof(pgm_tsi_t)); + ((struct sockaddr*)&sock->send_addr)->sa_family = AF_INET; + ((struct sockaddr_in*)&sock->send_addr)->sin_addr.s_addr = inet_addr ("127.0.0.2"); + ((struct sockaddr*)&sock->send_gsr.gsr_group)->sa_family = AF_INET; + ((struct sockaddr_in*)&sock->send_gsr.gsr_group)->sin_addr.s_addr = inet_addr ("239.192.0.1"); + sock->dport = g_htons(TEST_PORT); + sock->window = g_malloc0 (sizeof(pgm_txw_t)); + sock->txw_sqns = TEST_TXW_SQNS; + sock->max_tpdu = TEST_MAX_TPDU; + sock->max_tsdu = TEST_MAX_TPDU - sizeof(struct pgm_ip) - pgm_pkt_offset (FALSE, FALSE); + sock->max_tsdu_fragment = TEST_MAX_TPDU - sizeof(struct pgm_ip) - pgm_pkt_offset (TRUE, FALSE); + sock->max_apdu = MIN(TEST_TXW_SQNS, PGM_MAX_FRAGMENTS) * sock->max_tsdu_fragment; + sock->iphdr_len = sizeof(struct pgm_ip); + sock->spm_heartbeat_interval = g_malloc0 (sizeof(guint) * (2+2)); + sock->spm_heartbeat_interval[0] = pgm_secs(1); + pgm_spinlock_init (&sock->txw_spinlock); + sock->is_bound = FALSE; + sock->is_destroyed = FALSE; + return sock; +} + +static +struct pgm_sk_buff_t* +generate_skb (void) +{ + const char source[] = "i am not a string"; + struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); + pgm_skb_reserve (skb, pgm_pkt_offset (FALSE, FALSE)); + pgm_skb_put (skb, sizeof(source)); + memcpy (skb->data, source, sizeof(source)); + return skb; +} + +static +struct pgm_sk_buff_t* +generate_fragment_skb (void) +{ + const char source[] = "i am not a string"; + struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); + pgm_skb_reserve (skb, pgm_pkt_offset (TRUE, FALSE)); + pgm_skb_put (skb, sizeof(source)); + memcpy (skb->data, source, sizeof(source)); + return skb; +} + +static +struct pgm_sk_buff_t* +generate_odata (void) +{ + const char source[] = "i am not a string"; + const guint16 header_length = sizeof(struct pgm_header) + sizeof(struct pgm_data); + struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); + pgm_skb_reserve (skb, header_length); + memset (skb->head, 0, header_length); + skb->pgm_header = (struct pgm_header*)skb->head; + skb->pgm_data = (struct pgm_data*)(skb->pgm_header + 1); + skb->pgm_header->pgm_type = PGM_ODATA; + skb->pgm_header->pgm_tsdu_length = g_htons (sizeof(source)); + memcpy (skb->data, source, sizeof(source)); + pgm_skb_put (skb, sizeof(source)); +/* reverse pull */ + skb->len += (guint8*)skb->data - (guint8*)skb->head; + skb->data = skb->head; + return skb; +} + +static +pgm_peer_t* +generate_peer (void) +{ + pgm_peer_t* peer = g_malloc0 (sizeof(pgm_peer_t)); + return peer; +} + +static +struct pgm_sk_buff_t* +generate_spmr (void) +{ + struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); + const guint16 header_length = sizeof(struct pgm_header); + pgm_skb_reserve (skb, header_length); + memset (skb->head, 0, header_length); + skb->pgm_header = (struct pgm_header*)skb->head; + skb->pgm_header->pgm_type = PGM_SPMR; + pgm_skb_put (skb, header_length); + return skb; +} + +static +struct pgm_sk_buff_t* +generate_single_nak (void) +{ + struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); + const guint16 header_length = sizeof(struct pgm_header) + sizeof(struct pgm_nak); + pgm_skb_reserve (skb, sizeof(struct pgm_header)); + memset (skb->head, 0, header_length); + skb->pgm_header = (struct pgm_header*)skb->head; + skb->pgm_header->pgm_type = PGM_NAK; + struct pgm_nak* nak = (struct pgm_nak*)(skb->pgm_header + 1); + struct sockaddr_in nla = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr("127.0.0.2") + }; + pgm_sockaddr_to_nla ((struct sockaddr*)&nla, (char*)&nak->nak_src_nla_afi); + struct sockaddr_in group = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr("239.192.0.1") + }; + pgm_sockaddr_to_nla ((struct sockaddr*)&group, (char*)&nak->nak_grp_nla_afi); + pgm_skb_put (skb, header_length); + return skb; +} + +static +struct pgm_sk_buff_t* +generate_single_nnak (void) +{ + struct pgm_sk_buff_t* skb = generate_single_nak (); + skb->pgm_header->pgm_type = PGM_NNAK; + return skb; +} + +static +struct pgm_sk_buff_t* +generate_parity_nak (void) +{ + struct pgm_sk_buff_t* skb = generate_single_nak (); + skb->pgm_header->pgm_options = PGM_OPT_PARITY; + return skb; +} + +static +struct pgm_sk_buff_t* +generate_nak_list (void) +{ + struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU); + const guint16 header_length = sizeof(struct pgm_header) + sizeof(struct pgm_nak) + + sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + + ( 62 * sizeof(guint32) ); + pgm_skb_reserve (skb, sizeof(struct pgm_header)); + memset (skb->head, 0, header_length); + skb->pgm_header = (struct pgm_header*)skb->head; + skb->pgm_header->pgm_type = PGM_NAK; + skb->pgm_header->pgm_options = PGM_OPT_PRESENT | PGM_OPT_NETWORK; + struct pgm_nak *nak = (struct pgm_nak*)(skb->pgm_header + 1); + struct sockaddr_in nla = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr("127.0.0.2") + }; + pgm_sockaddr_to_nla ((struct sockaddr*)&nla, (char*)&nak->nak_src_nla_afi); + struct sockaddr_in group = { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr("239.192.0.1") + }; + pgm_sockaddr_to_nla ((struct sockaddr*)&group, (char*)&nak->nak_grp_nla_afi); + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(nak + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = g_htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_nak_list) + + ( 62 * sizeof(guint32) ) ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_NAK_LIST | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + + ( 62 * sizeof(guint32) ); + struct pgm_opt_nak_list* opt_nak_list = (struct pgm_opt_nak_list*)(opt_header + 1); + for (unsigned i = 1; i < 63; i++) { + opt_nak_list->opt_sqn[i-1] = g_htonl (i); + } + pgm_skb_put (skb, header_length); + return skb; +} + +static +struct pgm_sk_buff_t* +generate_parity_nak_list (void) +{ + struct pgm_sk_buff_t* skb = generate_nak_list (); + skb->pgm_header->pgm_options = PGM_OPT_PARITY | PGM_OPT_PRESENT | PGM_OPT_NETWORK; + return skb; +} + +void +mock_pgm_txw_add ( + pgm_txw_t* const window, + struct pgm_sk_buff_t* const skb + ) +{ + g_debug ("mock_pgm_txw_add (window:%p skb:%p)", + (gpointer)window, (gpointer)skb); +} + +struct pgm_sk_buff_t* +mock_pgm_txw_peek ( + const pgm_txw_t* const window, + const uint32_t sequence + ) +{ + g_debug ("mock_pgm_txw_peek (window:%p sequence:%" G_GUINT32_FORMAT ")", + (gpointer)window, sequence); + return NULL; +} + +bool +mock_pgm_txw_retransmit_push ( + pgm_txw_t* const window, + const uint32_t sequence, + const bool is_parity, + const uint8_t tg_sqn_shift + ) +{ + g_debug ("mock_pgm_txw_retransmit_push (window:%p sequence:%" G_GUINT32_FORMAT " is-parity:%s tg-sqn-shift:%d)", + (gpointer)window, + sequence, + is_parity ? "YES" : "NO", + tg_sqn_shift); + return TRUE; +} + +void +mock_pgm_txw_set_unfolded_checksum ( + struct pgm_sk_buff_t*const skb, + const uint32_t csum + ) +{ +} + +uint32_t +pgm_txw_get_unfolded_checksum ( + const struct pgm_sk_buff_t*const skb + ) +{ + return 0; +} + +void +mock_pgm_txw_inc_retransmit_count ( + struct pgm_sk_buff_t*const skb + ) +{ +} + +struct pgm_sk_buff_t* +mock_pgm_txw_retransmit_try_peek ( + pgm_txw_t* const window + ) +{ + g_debug ("mock_pgm_txw_retransmit_try_peek (window:%p)", + (gpointer)window); + return generate_odata (); +} + +void +mock_pgm_txw_retransmit_remove_head ( + pgm_txw_t* const window + ) +{ + g_debug ("mock_pgm_txw_retransmit_remove_head (window:%p)", + (gpointer)window); +} + +void +mock_pgm_rs_encode ( + pgm_rs_t* rs, + const pgm_gf8_t** src, + uint8_t offset, + pgm_gf8_t* dst, + uint16_t len + ) +{ + g_debug ("mock_pgm_rs_encode (rs:%p src:%p offset:%u dst:%p len:%" G_GSIZE_FORMAT ")", + rs, src, offset, dst, len); +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_rate_check ( + pgm_rate_t* bucket, + const size_t data_size, + const bool is_nonblocking + ) +{ + g_debug ("mock_pgm_rate_check (bucket:%p data-size:%u is-nonblocking:%s)", + bucket, data_size, is_nonblocking ? "TRUE" : "FALSE"); + return TRUE; +} + +bool +mock_pgm_verify_spmr ( + const struct pgm_sk_buff_t* const skb + ) +{ + return mock_is_valid_spmr; +} + +bool +mock_pgm_verify_ack ( + const struct pgm_sk_buff_t* const skb + ) +{ + return mock_is_valid_ack; +} + +bool +mock_pgm_verify_nak ( + const struct pgm_sk_buff_t* const skb + ) +{ + return mock_is_valid_nak; +} + +bool +mock_pgm_verify_nnak ( + const struct pgm_sk_buff_t* const skb + ) +{ + return mock_is_valid_nnak; +} + +uint32_t +mock_pgm_compat_csum_partial ( + const void* addr, + uint16_t len, + uint32_t csum + ) +{ + return 0x0; +} + +uint32_t +mock_pgm_compat_csum_partial_copy ( + const void* src, + void* dst, + uint16_t len, + uint32_t csum + ) +{ + return 0x0; +} + +uint32_t +mock_pgm_csum_block_add ( + uint32_t csum, + uint32_t csum2, + uint16_t offset + ) +{ + return 0x0; +} + +uint16_t +mock_pgm_csum_fold ( + uint32_t csum + ) +{ + return 0x0; +} + +PGM_GNUC_INTERNAL +ssize_t +mock_pgm_sendto_hops ( + pgm_sock_t* sock, + bool use_rate_limit, + bool use_router_alert, + int level, + const void* buf, + size_t len, + const struct sockaddr* to, + socklen_t tolen + ) +{ + char saddr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop (to, saddr, sizeof(saddr)); + g_debug ("mock_pgm_sendto (sock:%p use-rate-limit:%s use-router-alert:%s level:%d buf:%p len:%d to:%s tolen:%d)", + (gpointer)sock, + use_rate_limit ? "YES" : "NO", + use_router_alert ? "YES" : "NO", + level, + buf, + len, + saddr, + tolen); + return len; +} + +/** time module */ +static pgm_time_t _mock_pgm_time_update_now (void); +pgm_time_update_func mock_pgm_time_update_now = _mock_pgm_time_update_now; + +static +pgm_time_t +_mock_pgm_time_update_now (void) +{ + return 0x1; +} + +/** socket module */ +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return can_fragment ? ( sizeof(struct pgm_header) + + sizeof(struct pgm_data) + + sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment) ) + : ( sizeof(struct pgm_header) + sizeof(struct pgm_data) ); +} + +bool +mock_pgm_setsockopt ( + pgm_sock_t* const sock, + const int level, + const int optname, + const void* optval, + const socklen_t optlen + ) +{ + if (NULL == sock) + return FALSE; + return TRUE; +} + + +/* mock functions for external references */ + + +/* target: + * PGMIOStatus + * pgm_send ( + * pgm_sock_t* sock, + * gconstpointer apdu, + * gsize apdu_length, + * gsize* bytes_written + * ) + */ + +START_TEST (test_send_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->is_bound = TRUE; + const gsize apdu_length = 100; + guint8 buffer[ apdu_length ]; + gsize bytes_written; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_send (sock, buffer, apdu_length, &bytes_written), "send not normal"); + fail_unless ((gssize)apdu_length == bytes_written, "send underrun"); +} +END_TEST + +/* large apdu */ +START_TEST (test_send_pass_002) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->is_bound = TRUE; + const gsize apdu_length = 16000; + guint8 buffer[ apdu_length ]; + gsize bytes_written; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_send (sock, buffer, apdu_length, &bytes_written), "send not normal"); + fail_unless ((gssize)apdu_length == bytes_written, "send underrun"); +} +END_TEST + +START_TEST (test_send_fail_001) +{ + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + const gsize apdu_length = 100; + gsize bytes_written; + fail_unless (PGM_IO_STATUS_ERROR == pgm_send (NULL, buffer, apdu_length, &bytes_written), "send not error"); +} +END_TEST + +/* target: + * PGMIOStatus + * pgm_sendv ( + * pgm_sock_t* sock, + * const struct pgmiovec* vector, + * guint count, + * gboolean is_one_apdu, + * gsize* bytes_written + * ) + */ + +START_TEST (test_sendv_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->is_bound = TRUE; + const gsize apdu_length = 100; + guint8 buffer[ apdu_length ]; + struct pgm_iovec vector[] = { { .iov_base = buffer, .iov_len = apdu_length } }; + gsize bytes_written; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_sendv (sock, vector, 1, TRUE, &bytes_written), "send not normal"); + fail_unless ((gssize)apdu_length == bytes_written, "send underrun"); +} +END_TEST + +/* large apdu */ +START_TEST (test_sendv_pass_002) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->is_bound = TRUE; + const gsize apdu_length = 16000; + guint8 buffer[ apdu_length ]; + struct pgm_iovec vector[] = { { .iov_base = buffer, .iov_len = apdu_length } }; + gsize bytes_written; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_sendv (sock, vector, 1, TRUE, &bytes_written), "send not normal"); + fail_unless ((gssize)apdu_length == bytes_written, "send underrun"); +} +END_TEST + +/* multipart apdu */ +START_TEST (test_sendv_pass_003) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->is_bound = TRUE; + const gsize apdu_length = 16000; + guint8 buffer[ apdu_length ]; + struct pgm_iovec vector[ 16 ]; + for (unsigned i = 0; i < G_N_ELEMENTS(vector); i++) { + vector[i].iov_base = &buffer[ (i * apdu_length) / G_N_ELEMENTS(vector) ]; + vector[i].iov_len = apdu_length / G_N_ELEMENTS(vector); + } + gsize bytes_written; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_sendv (sock, vector, G_N_ELEMENTS(vector), TRUE, &bytes_written), "send not normal"); + fail_unless ((gssize)apdu_length == bytes_written, "send underrun"); +} +END_TEST + +/* multiple apdus */ +START_TEST (test_sendv_pass_004) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->is_bound = TRUE; + const gsize apdu_length = 16000; + struct pgm_iovec vector[ 16 ]; + for (unsigned i = 0; i < G_N_ELEMENTS(vector); i++) { + vector[i].iov_base = g_malloc0 (apdu_length); + vector[i].iov_len = apdu_length; + } + gsize bytes_written; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_sendv (sock, vector, G_N_ELEMENTS(vector), FALSE, &bytes_written), "send not normal"); + fail_unless ((gssize)(apdu_length * G_N_ELEMENTS(vector)) == bytes_written, "send underrun"); +} +END_TEST + +START_TEST (test_sendv_fail_001) +{ + guint8 buffer[ TEST_TXW_SQNS * TEST_MAX_TPDU ]; + const gsize tsdu_length = 100; + struct pgm_iovec vector[] = { { .iov_base = buffer, .iov_len = tsdu_length } }; + gsize bytes_written; + fail_unless (PGM_IO_STATUS_ERROR == pgm_sendv (NULL, vector, 1, TRUE, &bytes_written), "send not error"); +} +END_TEST + +/* target: + * PGMIOStatus + * pgm_send_skbv ( + * pgm_sock_t* sock, + * struct pgm_sk_buff_t* vector[], + * guint count, + * gboolean is_one_apdu, + * gsize* bytes_written + * ) + */ + +START_TEST (test_send_skbv_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->is_bound = TRUE; + struct pgm_sk_buff_t* skb = NULL; + skb = generate_skb (); + fail_if (NULL == skb, "generate_skb failed"); + gsize apdu_length = (gsize)skb->len; + gsize bytes_written; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_send_skbv (sock, &skb, 1, TRUE, &bytes_written), "send not normal"); + fail_unless (apdu_length == bytes_written, "send underrun"); +} +END_TEST + +/* multipart apdu */ +START_TEST (test_send_skbv_pass_002) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->is_bound = TRUE; + struct pgm_sk_buff_t* skb[16]; + for (unsigned i = 0; i < G_N_ELEMENTS(skb); i++) { + skb[i] = generate_fragment_skb (); + fail_if (NULL == skb[i], "generate_fragment_skb failed"); + } + gsize apdu_length = (gsize)skb[0]->len * G_N_ELEMENTS(skb); + gsize bytes_written; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_send_skbv (sock, skb, G_N_ELEMENTS(skb), TRUE, &bytes_written), "send not normal"); + fail_unless (apdu_length == bytes_written, "send underrun"); +} +END_TEST + +/* multiple apdus */ +START_TEST (test_send_skbv_pass_003) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->is_bound = TRUE; + struct pgm_sk_buff_t* skb[16]; + for (unsigned i = 0; i < G_N_ELEMENTS(skb); i++) { + skb[i] = generate_skb (); + fail_if (NULL == skb[i], "generate_skb failed"); + } + gsize bytes_written; + fail_unless (PGM_IO_STATUS_NORMAL == pgm_send_skbv (sock, skb, G_N_ELEMENTS(skb), FALSE, &bytes_written), "send not normal"); + fail_unless ((gssize)(skb[0]->len * G_N_ELEMENTS(skb)) == bytes_written, "send underrun"); +} +END_TEST + +START_TEST (test_send_skbv_fail_001) +{ + struct pgm_sk_buff_t* skb = pgm_alloc_skb (TEST_MAX_TPDU), *skbv[] = { skb }; + fail_if (NULL == skb, "alloc_skb failed"); +/* reserve PGM header */ + pgm_skb_put (skb, pgm_pkt_offset (TRUE, FALSE)); + const gsize tsdu_length = 100; + gsize bytes_written; + fail_unless (PGM_IO_STATUS_ERROR == pgm_send_skbv (NULL, skbv, 1, TRUE, &bytes_written), "send not error"); +} +END_TEST + +/* target: + * gboolean + * pgm_send_spm ( + * pgm_sock_t* sock, + * int flags + * ) + */ + +START_TEST (test_send_spm_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + fail_unless (TRUE == pgm_send_spm (sock, 0), "send_spm failed"); +} +END_TEST + +START_TEST (test_send_spm_fail_001) +{ + pgm_send_spm (NULL, 0); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_on_deferred_nak ( + * pgm_sock_t* sock + * ) + */ + +START_TEST (test_on_deferred_nak_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + pgm_on_deferred_nak (sock); +} +END_TEST + +START_TEST (test_on_deferred_nak_fail_001) +{ + pgm_on_deferred_nak (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * gboolean + * pgm_on_spmr ( + * pgm_sock_t* sock, + * pgm_peer_t* peer, + * struct pgm_sk_buff_t* skb + * ) + */ + +/* peer spmr */ +START_TEST (test_on_spmr_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + pgm_peer_t* peer = generate_peer (); + fail_if (NULL == peer, "generate_peer failed"); + struct pgm_sk_buff_t* skb = generate_spmr (); + fail_if (NULL == skb, "generate_spmr failed"); + skb->sock = sock; + fail_unless (TRUE == pgm_on_spmr (sock, peer, skb), "on_spmr failed"); +} +END_TEST + +/* source spmr */ +START_TEST (test_on_spmr_pass_002) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + struct pgm_sk_buff_t* skb = generate_spmr (); + fail_if (NULL == skb, "generate_spmr failed"); + skb->sock = sock; + fail_unless (TRUE == pgm_on_spmr (sock, NULL, skb), "on_spmr failed"); +} +END_TEST + +/* invalid spmr */ +START_TEST (test_on_spmr_fail_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + pgm_peer_t* peer = generate_peer (); + fail_if (NULL == peer, "generate_peer failed"); + struct pgm_sk_buff_t* skb = generate_spmr (); + fail_if (NULL == skb, "generate_spmr failed"); + skb->sock = sock; + mock_is_valid_spmr = FALSE; + fail_unless (FALSE == pgm_on_spmr (sock, peer, skb), "on_spmr failed"); +} +END_TEST + +START_TEST (test_on_spmr_fail_002) +{ + pgm_on_spmr (NULL, NULL, NULL); + fail ("reached"); +} +END_TEST + +/* target: + * gboolean + * pgm_on_nak ( + * pgm_sock_t* sock, + * struct pgm_sk_buff_t* skb + * ) + */ + +/* single nak */ +START_TEST (test_on_nak_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + struct pgm_sk_buff_t* skb = generate_single_nak (); + fail_if (NULL == skb, "generate_single_nak failed"); + skb->sock = sock; + fail_unless (TRUE == pgm_on_nak (sock, skb), "on_nak failed"); +} +END_TEST + +/* nak list */ +START_TEST (test_on_nak_pass_002) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + struct pgm_sk_buff_t* skb = generate_nak_list (); + fail_if (NULL == skb, "generate_nak_list failed"); + skb->sock = sock; + fail_unless (TRUE == pgm_on_nak (sock, skb), "on_nak failed"); +} +END_TEST + +/* single parity nak */ +START_TEST (test_on_nak_pass_003) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->use_ondemand_parity = TRUE; + struct pgm_sk_buff_t* skb = generate_parity_nak (); + fail_if (NULL == skb, "generate_parity_nak failed"); + skb->sock = sock; + fail_unless (TRUE == pgm_on_nak (sock, skb), "on_nak failed"); +} +END_TEST + +/* parity nak list */ +START_TEST (test_on_nak_pass_004) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->use_ondemand_parity = TRUE; + struct pgm_sk_buff_t* skb = generate_parity_nak_list (); + fail_if (NULL == skb, "generate_parity_nak_list failed"); + skb->sock = sock; + fail_unless (TRUE == pgm_on_nak (sock, skb), "on_nak failed"); +} +END_TEST + +START_TEST (test_on_nak_fail_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + struct pgm_sk_buff_t* skb = generate_single_nak (); + fail_if (NULL == skb, "generate_single_nak failed"); + skb->sock = sock; + mock_is_valid_nak = FALSE; + fail_unless (FALSE == pgm_on_nak (sock, skb), "on_nak failed"); +} +END_TEST + +START_TEST (test_on_nak_fail_002) +{ + pgm_on_nak (NULL, NULL); + fail ("reached"); +} +END_TEST + +/* target: + * gboolean + * pgm_on_nnak ( + * pgm_sock_t* sock, + * struct pgm_sk_buff_t* skb + * ) + */ + +START_TEST (test_on_nnak_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + struct pgm_sk_buff_t* skb = generate_single_nnak (); + fail_if (NULL == skb, "generate_single_nnak failed"); + skb->sock = sock; + fail_unless (TRUE == pgm_on_nnak (sock, skb), "on_nnak failed"); +} +END_TEST + +START_TEST (test_on_nnak_fail_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + struct pgm_sk_buff_t* skb = generate_single_nnak (); + fail_if (NULL == skb, "generate_single_nnak failed"); + skb->sock = sock; + mock_is_valid_nnak = FALSE; + fail_unless (FALSE == pgm_on_nnak (sock, skb), "on_nnak failed"); +} +END_TEST + +START_TEST (test_on_nnak_fail_002) +{ + pgm_on_nnak (NULL, NULL); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_AMBIENT_SPM, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_ambient_spm_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int level = IPPROTO_PGM; + const int optname = PGM_AMBIENT_SPM; + const int ambient_spm = pgm_msecs(1000); + const void* optval = &ambient_spm; + const socklen_t optlen = sizeof(ambient_spm); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_ambient_spm failed"); +} +END_TEST + +START_TEST (test_set_ambient_spm_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_AMBIENT_SPM; + const int ambient_spm = pgm_msecs(1000); + const void* optval = &ambient_spm; + const socklen_t optlen = sizeof(ambient_spm); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_ambient_spm failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_HEARTBEAT_SPM, + * const void* optval, + * const socklen_t optlen = sizeof(int) * n + * ) + */ + +START_TEST (test_set_heartbeat_spm_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int level = IPPROTO_PGM; + const int optname = PGM_HEARTBEAT_SPM; + const int intervals[] = { 1, 2, 3, 4, 5 }; + const void* optval = &intervals; + const socklen_t optlen = sizeof(intervals); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_heartbeat_spm failed"); +} +END_TEST + +START_TEST (test_set_heartbeat_spm_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_HEARTBEAT_SPM; + const int intervals[] = { 1, 2, 3, 4, 5 }; + const void* optval = &intervals; + const socklen_t optlen = sizeof(intervals); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_heartbeat_spm failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_TXW_SQNS, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_txw_sqns_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int level = IPPROTO_PGM; + const int optname = PGM_TXW_SQNS; + const int txw_sqns = 100; + const void* optval = &txw_sqns; + const socklen_t optlen = sizeof(txw_sqns); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_txw_sqns failed"); +} +END_TEST + +START_TEST (test_set_txw_sqns_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_TXW_SQNS; + const int txw_sqns = 100; + const void* optval = &txw_sqns; + const socklen_t optlen = sizeof(txw_sqns); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_txw_sqns failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_TXW_SECS, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_txw_secs_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int level = IPPROTO_PGM; + const int optname = PGM_TXW_SECS; + const int txw_secs = pgm_secs(10); + const void* optval = &txw_secs; + const socklen_t optlen = sizeof(txw_secs); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_txw_secs failed"); +} +END_TEST + +START_TEST (test_set_txw_secs_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_TXW_SECS; + const int txw_secs = pgm_secs(10); + const void* optval = &txw_secs; + const socklen_t optlen = sizeof(txw_secs); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_txw_secs failed"); +} +END_TEST + +/* target: + * bool + * pgm_setsockopt ( + * pgm_sock_t* const sock, + * const int level = IPPROTO_PGM, + * const int optname = PGM_TXW_MAX_RTE, + * const void* optval, + * const socklen_t optlen = sizeof(int) + * ) + */ + +START_TEST (test_set_txw_max_rte_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + const int level = IPPROTO_PGM; + const int optname = PGM_TXW_MAX_RTE; + const int txw_max_rte = 100*1000; + const void* optval = &txw_max_rte; + const socklen_t optlen = sizeof(txw_max_rte); + fail_unless (TRUE == pgm_setsockopt (sock, level, optname, optval, optlen), "set_txw_max_rte failed"); +} +END_TEST + +START_TEST (test_set_txw_max_rte_fail_001) +{ + const int level = IPPROTO_PGM; + const int optname = PGM_TXW_MAX_RTE; + const int txw_max_rte = 100*1000; + const void* optval = &txw_max_rte; + const socklen_t optlen = sizeof(txw_max_rte); + fail_unless (FALSE == pgm_setsockopt (NULL, level, optname, optval, optlen), "set_txw_max_rte failed"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_send = tcase_create ("send"); + suite_add_tcase (s, tc_send); + tcase_add_checked_fixture (tc_send, mock_setup, NULL); + tcase_add_test (tc_send, test_send_pass_001); + tcase_add_test (tc_send, test_send_pass_002); + tcase_add_test (tc_send, test_send_fail_001); + + TCase* tc_sendv = tcase_create ("sendv"); + suite_add_tcase (s, tc_sendv); + tcase_add_checked_fixture (tc_sendv, mock_setup, NULL); + tcase_add_test (tc_sendv, test_sendv_pass_001); + tcase_add_test (tc_sendv, test_sendv_pass_002); + tcase_add_test (tc_sendv, test_sendv_pass_003); + tcase_add_test (tc_sendv, test_sendv_pass_004); + tcase_add_test (tc_sendv, test_sendv_fail_001); + + TCase* tc_send_skbv = tcase_create ("send-skbv"); + suite_add_tcase (s, tc_send_skbv); + tcase_add_checked_fixture (tc_send_skbv, mock_setup, NULL); + tcase_add_test (tc_send_skbv, test_send_skbv_pass_001); + tcase_add_test (tc_send_skbv, test_send_skbv_pass_002); + tcase_add_test (tc_send_skbv, test_send_skbv_fail_001); + + TCase* tc_send_spm = tcase_create ("send-spm"); + suite_add_tcase (s, tc_send_spm); + tcase_add_checked_fixture (tc_send_spm, mock_setup, NULL); + tcase_add_test (tc_send_spm, test_send_spm_pass_001); + tcase_add_test_raise_signal (tc_send_spm, test_send_spm_fail_001, SIGABRT); + + TCase* tc_on_deferred_nak = tcase_create ("on-deferred-nak"); + suite_add_tcase (s, tc_on_deferred_nak); + tcase_add_checked_fixture (tc_on_deferred_nak, mock_setup, NULL); + tcase_add_test (tc_on_deferred_nak, test_on_deferred_nak_pass_001); + tcase_add_test_raise_signal (tc_on_deferred_nak, test_on_deferred_nak_fail_001, SIGABRT); + + TCase* tc_on_spmr = tcase_create ("on-spmr"); + suite_add_tcase (s, tc_on_spmr); + tcase_add_checked_fixture (tc_on_spmr, mock_setup, NULL); + tcase_add_test (tc_on_spmr, test_on_spmr_pass_001); + tcase_add_test (tc_on_spmr, test_on_spmr_pass_002); + tcase_add_test (tc_on_spmr, test_on_spmr_fail_001); + tcase_add_test_raise_signal (tc_on_spmr, test_on_spmr_fail_002, SIGABRT); + + TCase* tc_on_nak = tcase_create ("on-nak"); + suite_add_tcase (s, tc_on_nak); + tcase_add_checked_fixture (tc_on_nak, mock_setup, NULL); + tcase_add_test (tc_on_nak, test_on_nak_pass_001); + tcase_add_test (tc_on_nak, test_on_nak_pass_002); + tcase_add_test (tc_on_nak, test_on_nak_pass_003); + tcase_add_test (tc_on_nak, test_on_nak_pass_004); + tcase_add_test (tc_on_nak, test_on_nak_fail_001); + tcase_add_test_raise_signal (tc_on_nak, test_on_nak_fail_002, SIGABRT); + + TCase* tc_on_nnak = tcase_create ("on-nnak"); + suite_add_tcase (s, tc_on_nnak); + tcase_add_checked_fixture (tc_on_nnak, mock_setup, NULL); + tcase_add_test (tc_on_nnak, test_on_nnak_pass_001); + tcase_add_test (tc_on_nnak, test_on_nnak_fail_001); + tcase_add_test_raise_signal (tc_on_nnak, test_on_nnak_fail_002, SIGABRT); + + TCase* tc_set_ambient_spm = tcase_create ("set-ambient-spm"); + suite_add_tcase (s, tc_set_ambient_spm); + tcase_add_checked_fixture (tc_set_ambient_spm, mock_setup, NULL); + tcase_add_test (tc_set_ambient_spm, test_set_ambient_spm_pass_001); + tcase_add_test (tc_set_ambient_spm, test_set_ambient_spm_fail_001); + + TCase* tc_set_heartbeat_spm = tcase_create ("set-heartbeat-spm"); + suite_add_tcase (s, tc_set_heartbeat_spm); + tcase_add_checked_fixture (tc_set_heartbeat_spm, mock_setup, NULL); + tcase_add_test (tc_set_heartbeat_spm, test_set_heartbeat_spm_pass_001); + tcase_add_test (tc_set_heartbeat_spm, test_set_heartbeat_spm_fail_001); + + TCase* tc_set_txw_sqns = tcase_create ("set-txw-sqns"); + suite_add_tcase (s, tc_set_txw_sqns); + tcase_add_checked_fixture (tc_set_txw_sqns, mock_setup, NULL); + tcase_add_test (tc_set_txw_sqns, test_set_txw_sqns_pass_001); + tcase_add_test (tc_set_txw_sqns, test_set_txw_sqns_fail_001); + + TCase* tc_set_txw_secs = tcase_create ("set-txw-secs"); + suite_add_tcase (s, tc_set_txw_secs); + tcase_add_checked_fixture (tc_set_txw_secs, mock_setup, NULL); + tcase_add_test (tc_set_txw_secs, test_set_txw_secs_pass_001); + tcase_add_test (tc_set_txw_secs, test_set_txw_secs_fail_001); + + TCase* tc_set_txw_max_rte = tcase_create ("set-txw-max-rte"); + suite_add_tcase (s, tc_set_txw_max_rte); + tcase_add_checked_fixture (tc_set_txw_max_rte, mock_setup, NULL); + tcase_add_test (tc_set_txw_max_rte, test_set_txw_max_rte_pass_001); + tcase_add_test (tc_set_txw_max_rte, test_set_txw_max_rte_fail_001); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/string.c b/3rdparty/openpgm-svn-r1135/pgm/string.c new file mode 100644 index 0000000..f8f8e33 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/string.c @@ -0,0 +1,486 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable string manipulation functions. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if defined(CONFIG_HAVE_VASPRINTF) && !defined(_GNU_SOURCE) +# define _GNU_SOURCE +#endif +#include +#include +#include /* _GNU_SOURCE for vasprintf */ +#include +#include + + +//#define STRING_DEBUG + +/* Return copy of string, must be freed with pgm_free(). + */ + +char* +pgm_strdup ( + const char* str + ) +{ + char* new_str; + size_t length; + + if (PGM_LIKELY (NULL != str)) + { + length = strlen (str) + 1; + new_str = malloc (length); + memcpy (new_str, str, length); + } + else + new_str = NULL; + + return new_str; +} + +/* Calculates the maximum space needed to store the output of the sprintf() function. + */ + +int +pgm_printf_string_upper_bound ( + const char* format, + va_list args + ) +{ + char c; + return vsnprintf (&c, 1, format, args) + 1; +} + +/* memory must be freed with free() + */ + +int +pgm_vasprintf ( + char** restrict string, + const char* restrict format, + va_list args + ) +{ + pgm_return_val_if_fail (string != NULL, -1); +#ifdef CONFIG_HAVE_VASPRINTF + const int len = vasprintf (string, format, args); + if (len < 0) + *string = NULL; +#else + va_list args2; + va_copy (args2, args); + *string = malloc (pgm_printf_string_upper_bound (format, args)); +/* NB: must be able to handle NULL args, fails on GCC */ + const int len = vsprintf (*string, format, args); + va_end (args2); +#endif + return len; +} + +char* +pgm_strdup_vprintf ( + const char* format, + va_list args + ) +{ + char *string = NULL; + pgm_vasprintf (&string, format, args); + return string; +} + +static +char* +pgm_stpcpy ( + char* restrict dest, + const char* restrict src + ) +{ + pgm_return_val_if_fail (dest != NULL, NULL); + pgm_return_val_if_fail (src != NULL, NULL); +#ifdef CONFIG_HAVE_STPCPY + return stpcpy (dest, src); +#else + char *d = dest; + const char *s = src; + do { + *d++ = *s; + } while (*s++ != '\0'); + return d - 1; +#endif +} + +char* +pgm_strconcat ( + const char* string1, + ... + ) +{ + size_t l; + va_list args; + char* s; + char* concat; + char* ptr; + + if (!string1) + return NULL; + + l = 1 + strlen (string1); + va_start (args, string1); + s = va_arg (args, char*); + while (s) { + l += strlen (s); + s = va_arg (args, char*); + } + va_end (args); + + concat = malloc (l); + ptr = concat; + + ptr = pgm_stpcpy (ptr, string1); + va_start (args, string1); + s = va_arg (args, char*); + while (s) { + ptr = pgm_stpcpy (ptr, s); + s = va_arg (args, char*); + } + va_end (args); + + return concat; +} + +/* Split a string with delimiter, result must be freed with pgm_strfreev(). + */ + +char** +pgm_strsplit ( + const char* restrict string, + const char* restrict delimiter, + int max_tokens + ) +{ + pgm_slist_t *string_list = NULL, *slist; + char **str_array, *s; + unsigned n = 0; + const char *remainder; + + pgm_return_val_if_fail (string != NULL, NULL); + pgm_return_val_if_fail (delimiter != NULL, NULL); + pgm_return_val_if_fail (delimiter[0] != '\0', NULL); + + if (max_tokens < 1) + max_tokens = INT_MAX; + + remainder = string; + s = strstr (remainder, delimiter); + if (s) + { + const size_t delimiter_len = strlen (delimiter); + + while (--max_tokens && s) + { + const size_t len = s - remainder; + char *new_string = malloc (len + 1); + strncpy (new_string, remainder, len); + new_string[len] = 0; + string_list = pgm_slist_prepend (string_list, new_string); + n++; + remainder = s + delimiter_len; + s = strstr (remainder, delimiter); + } + } + if (*string) + { + n++; + string_list = pgm_slist_prepend (string_list, pgm_strdup (remainder)); + } + + str_array = pgm_new (char*, n + 1); + str_array[n--] = NULL; + for (slist = string_list; slist; slist = slist->next) + str_array[n--] = slist->data; + + pgm_slist_free (string_list); + + return str_array; +} + +/* Free a NULL-terminated array of strings, such as created by pgm_strsplit + */ + +void +pgm_strfreev ( + char** str_array + ) +{ + if (PGM_LIKELY (NULL != str_array)) + { + for (unsigned i = 0; str_array[i] != NULL; i++) + free (str_array[i]); + + pgm_free (str_array); + } +} + +/* resize dynamic string + */ + +static +void +pgm_string_maybe_expand ( + pgm_string_t* string, + size_t len + ) +{ + if ((string->len + len) >= string->allocated_len) + { + string->allocated_len = pgm_nearest_power (1, string->len + len + 1); + string->str = pgm_realloc (string->str, string->allocated_len); + } +} + +/* val may not be a part of string + */ + +static +pgm_string_t* +pgm_string_insert_len ( + pgm_string_t* restrict string, + ssize_t pos, + const char* restrict val, + ssize_t len + ) +{ + pgm_return_val_if_fail (NULL != string, NULL); + pgm_return_val_if_fail (NULL != val, string); + + if (len < 0) + len = strlen (val); + + if (pos < 0) + pos = string->len; + else + pgm_return_val_if_fail ((size_t)pos <= string->len, string); + + pgm_string_maybe_expand (string, len); + + if ((size_t)pos < string->len) + memmove (string->str + pos + len, string->str + pos, string->len - pos); + + if (len == 1) + string->str[pos] = *val; + else + memcpy (string->str + pos, val, len); + string->len += len; + string->str[string->len] = 0; + return string; +} + +static +pgm_string_t* +pgm_string_insert_c ( + pgm_string_t* string, + ssize_t pos, + char c + ) +{ + pgm_return_val_if_fail (NULL != string, NULL); + + if (pos < 0) + pos = string->len; + else + pgm_return_val_if_fail ((size_t)pos <= string->len, string); + + pgm_string_maybe_expand (string, 1); + + if ((size_t)pos < string->len) + memmove (string->str + pos + 1, string->str + pos, string->len - pos); + + string->str[pos] = c; + string->len ++; + string->str[string->len] = '\0'; + return string; +} + +static +pgm_string_t* +pgm_string_append_len ( + pgm_string_t* restrict string, + const char* restrict val, + size_t len + ) +{ + pgm_return_val_if_fail (NULL != string, NULL); + pgm_return_val_if_fail (NULL != val, string); + + return pgm_string_insert_len (string, -1, val, len); +} + +/* create new dynamic string + */ + +static +pgm_string_t* +pgm_string_sized_new ( + size_t init_size + ) +{ + pgm_string_t* string = pgm_new (pgm_string_t, 1); + string->allocated_len = 0; + string->len = 0; + string->str = NULL; + pgm_string_maybe_expand (string, MAX(init_size, 2)); + string->str[0] = '\0'; + return string; +} + +pgm_string_t* +pgm_string_new ( + const char* init + ) +{ + pgm_string_t* string; + + if (NULL == init || '\0' == *init) + string = pgm_string_sized_new (2); + else + { + const size_t len = strlen (init); + string = pgm_string_sized_new (len + 2); + pgm_string_append_len (string, init, len); + } + return string; +} + +/* free dynamic string, optionally just the wrapper object + */ + +char* +pgm_string_free ( + pgm_string_t* string, + bool free_segment + ) +{ + char* segment; + + pgm_return_val_if_fail (NULL != string, NULL); + + if (free_segment) { + pgm_free (string->str); + segment = NULL; + } else + segment = string->str; + + pgm_free (string); + return segment; +} + +static +pgm_string_t* +pgm_string_truncate ( + pgm_string_t* restrict string, + size_t len + ) +{ + pgm_return_val_if_fail (NULL != string, NULL); + + string->len = MIN (len, string->len); + string->str[ string->len ] = '\0'; + + return string; +} + +pgm_string_t* +pgm_string_append ( + pgm_string_t* restrict string, + const char* restrict val + ) +{ + pgm_return_val_if_fail (NULL != string, NULL); + pgm_return_val_if_fail (NULL != val, string); + + return pgm_string_insert_len (string, -1, val, -1); +} + +pgm_string_t* +pgm_string_append_c ( + pgm_string_t* string, + char c + ) +{ + pgm_return_val_if_fail (NULL != string, NULL); + + return pgm_string_insert_c (string, -1, c); +} + +static void pgm_string_append_vprintf (pgm_string_t*restrict, const char*restrict, va_list) PGM_GNUC_PRINTF(2, 0); + +static +void +pgm_string_append_vprintf ( + pgm_string_t* restrict string, + const char* restrict format, + va_list args + ) +{ + char *buf; + int len; + + pgm_return_if_fail (NULL != string); + pgm_return_if_fail (NULL != format); + + len = pgm_vasprintf (&buf, format, args); + if (len >= 0) { + pgm_string_maybe_expand (string, len); + memcpy (string->str + string->len, buf, len + 1); + string->len += len; + free (buf); + } +} + +void +pgm_string_printf ( + pgm_string_t* restrict string, + const char* restrict format, + ... + ) +{ + va_list args; + + pgm_string_truncate (string, 0); + + va_start (args, format); + pgm_string_append_vprintf (string, format, args); + va_end (args); +} + +void +pgm_string_append_printf ( + pgm_string_t* restrict string, + const char* restrict format, + ... + ) +{ + va_list args; + + va_start (args, format); + pgm_string_append_vprintf (string, format, args); + va_end (args); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/string.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/string.c.c89.patch new file mode 100644 index 0000000..8dd3d78 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/string.c.c89.patch @@ -0,0 +1,39 @@ +65a66,68 +> #ifdef _MSC_VER +> return _vscprintf (format, args) + 1; +> #else +67a71 +> #endif +80a85 +> { +81a87 +> { +85a92,94 +> # ifdef _MSC_VER +> va_list args2 = args; +> # else +87a97 +> # endif +89a100 +> { +93a105,106 +> } +> } +118a132 +> { +124a139 +> } +198a214,216 +> #ifdef _MSC_VER +> strncpy_s (new_string, len + 1, remainder, len); +> #else +199a218 +> #endif +233c252,254 +< for (unsigned i = 0; str_array[i] != NULL; i++) +--- +> { +> unsigned i; +> for (i = 0; str_array[i] != NULL; i++) +234a256 +> } diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/PGM/Test.pm b/3rdparty/openpgm-svn-r1135/pgm/test/PGM/Test.pm new file mode 100644 index 0000000..cb2ee6d --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/PGM/Test.pm @@ -0,0 +1,394 @@ +package PGM::Test; + +use strict; +our($VERSION); +use Carp; +use IO::File; +use IPC::Open2; +use Net::SSH qw(sshopen2); +use Sys::Hostname; +use POSIX ":sys_wait_h"; +use JSON; + +$VERSION = '1.00'; + +=head1 NAME + +PGM::Test - PGM test module + +=head1 SYNOPSIS + + $test = PGM::Test->new(); + +=cut + +my $json = new JSON; + +sub new { + my $class = shift; + my $self = {}; + my %params = @_; + + $self->{tag} = exists $params{tag} ? $params{tag} : confess "tag parameter is required"; + $self->{host} = exists $params{host} ? $params{host} : confess "host parameter is required"; + $self->{cmd} = exists $params{cmd} ? $params{cmd} : confess "cmd parameter is required"; + + $self->{in} = IO::File->new(); + $self->{out} = IO::File->new(); + $self->{pid} = undef; + + bless $self, $class; + return $self; +} + +sub connect { + my $self = shift; + my $host = hostname; + + if ($self->{host} =~ /^(localhost|127\.1|127\.0\.0\.1|$host)$/) + { + print "$self->{tag}: opening local connection\n"; + $self->{pid} = open2 ($self->{in}, + $self->{out}, + "uname -a && sudo $self->{cmd}") + or croak "open2 failed $!"; + } + else + { + print "$self->{tag}: opening SSH connection to $self->{host} ...\n"; + $self->{pid} = sshopen2 ($self->{host}, + $self->{in}, + $self->{out}, + "uname -a && sudo $self->{cmd}") + or croak "SSH failed: $!"; + } + + print "$self->{tag}: connected.\n"; + $self->wait_for_ready; +} + +sub disconnect { + my($self,$quiet) = @_; + my $out = $self->{out}; + + print "$self->{tag}: sending quit command ...\n"; + eval { + local($SIG{ALRM}) = sub { die "alarm\n"; }; + alarm 10; + print $out "quit\n"; + while (readline($self->{in})) { + chomp; + print "$self->{tag} [$_]\n" if (!$quiet); + } + alarm 0; + }; + if ($@) { + print "$self->{tag}: alarm raised on quit command.\n"; + } else { + print "$self->{tag}: eof.\n"; + } + + print "$self->{tag}: closing SSH connection ...\n"; + close ($self->{in}); + close ($self->{out}); + print "$self->{tag}: closed.\n"; +} + +sub DESTROY { + my $self = shift; + + if ($self->{pid}) { + print "$self->{tag}: waiting child to terminate ...\n"; + eval { + local($SIG{ALRM}) = sub { die "alarm\n"; }; + alarm 10; + waitpid $self->{pid}, 0; + alarm 0; + }; + if ($@) { + die unless $@ eq "alarm\n"; + local($SIG{CHLD}) = 'IGNORE'; + print "$self->{tag}: killing child ...\n"; + kill 'INT' => $self->{pid}; + print "$self->{tag}: killed.\n"; + } else { + print "$self->{tag}: terminated.\n"; + } + } +} + +sub wait_for_ready { + my $self = shift; + + while (readline($self->{in})) { + chomp; + print "$self->{tag} [$_]\n"; + last if /^READY/; + } +} + +sub wait_for_block { + my $self = shift; + my $fh = $self->{in}; + my $b = ''; + my $state = 0; + + while (<$fh>) { + chomp(); + my $l = $_; + if ($state == 0) { + if ($l =~ /^{$/) { + $state = 1; + } else { + print "$self->{tag} [$l]\n"; + } + } + + if ($state == 1) { + $b .= $l; + + if ($l =~ /^}$/) { + $state = 0; + return $b; + } + } + } +} + +sub wait_for_spm { + my $self = shift; + my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; + my $obj = undef; + + eval { + local $SIG{ALRM} = sub { die "alarm\n"; }; + alarm $timeout; + for (;;) { + my $block = $self->wait_for_block; + $obj = $json->jsonToObj($block); + last if ($obj->{PGM}->{type} =~ /SPM$/); + } + alarm 0; + }; + if ($@) { + die unless $@ eq "alarm\n"; + confess "$self->{tag}: alarm raised waiting for spm.\n"; + } + + return $obj; +} + +sub wait_for_spmr { + my $self = shift; + my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; + my $obj = undef; + + eval { + local $SIG{ALRM} = sub { die "alarm\n"; }; + alarm $timeout; + for (;;) { + my $block = $self->wait_for_block; + $obj = $json->jsonToObj($block); + last if ($obj->{PGM}->{type} =~ /SPMR/); + } + alarm 0; + }; + if ($@) { + die unless $@ eq "alarm\n"; + confess "$self->{tag}: alarm raised waiting for spmr.\n"; + } + + return $obj; +} + +sub die_on_spmr { + my $self = shift; + my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; + my $obj = undef; + + eval { + local $SIG{ALRM} = sub { die "alarm\n"; }; + alarm $timeout; + for (;;) { + my $block = $self->wait_for_block; + $obj = $json->jsonToObj($block); + last if ($obj->{PGM}->{type} =~ /SPMR/); + } + alarm 0; + }; + if ($@) { + die unless $@ eq "alarm\n"; + return $obj; + } + + confess "$self->{tag}: spmr received during blackout.\n"; +} + +# data to {app} +sub wait_for_data { + my $self = shift; + my $fh = $self->{in}; + my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; + my $data = undef; + + eval { + local $SIG{ALRM} = sub { die "alarm\n"; }; + alarm $timeout; + while (<$fh>) { + chomp; + if (/^DATA: (.+)$/) { + $data = $1; + last; + } + print "$self->{tag} [$_]\n"; + } + alarm 0; + }; + if ($@) { + die unless $@ eq "alarm\n"; + confess "$self->{tag}: alarm raised waiting for data.\n"; + } + + return $data; +} + +sub wait_for_odata { + my $self = shift; + my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; + my $obj = undef; + + eval { + local $SIG{ALRM} = sub { die "alarm\n"; }; + alarm $timeout; + for (;;) { + my $block = $self->wait_for_block; + $obj = $json->jsonToObj($block); + last if ($obj->{PGM}->{type} =~ /ODATA/); + } + alarm 0; + }; + if ($@) { + die unless $@ eq "alarm\n"; + confess "$self->{tag}: alarm raised waiting for odata.\n"; + } + + return $obj; +} + +sub wait_for_rdata { + my $self = shift; + my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; + my $obj = undef; + + eval { + local $SIG{ALRM} = sub { die "alarm\n"; }; + alarm $timeout; + for (;;) { + my $block = $self->wait_for_block; + $obj = $json->jsonToObj($block); + last if ($obj->{PGM}->{type} =~ /RDATA/); + } + alarm 0; + }; + if ($@) { + die unless $@ eq "alarm\n"; + confess "$self->{tag}: alarm raised waiting for rdata.\n"; + } + + return $obj; +} + +sub die_on_nak { + my $self = shift; + my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; + my $obj = undef; + + eval { + local $SIG{ALRM} = sub { die "alarm\n"; }; + alarm $timeout; + for (;;) { + my $block = $self->wait_for_block; + $obj = $json->jsonToObj($block); + last if ($obj->{PGM}->{type} =~ /NAK/); + } + alarm 0; + }; + if ($@) { + die unless $@ eq "alarm\n"; + return $obj; + } + + confess "$self->{tag}: nak received during blackout.\n"; +} + +sub wait_for_nak { + my $self = shift; + my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; + my $obj = undef; + + eval { + local $SIG{ALRM} = sub { die "alarm\n"; }; + alarm $timeout; + for (;;) { + my $block = $self->wait_for_block; + $obj = $json->jsonToObj($block); + last if ($obj->{PGM}->{type} =~ /NAK/); + } + alarm 0; + }; + if ($@) { + die unless $@ eq "alarm\n"; + confess "$self->{tag}: alarm raised waiting for nak.\n"; + } + + return $obj; +} + +sub wait_for_ncf { + my $self = shift; + my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; + my $obj = undef; + + eval { + local $SIG{ALRM} = sub { die "alarm\n"; }; + alarm $timeout; + for (;;) { + my $block = $self->wait_for_block; + $obj = $json->jsonToObj($block); + last if ($obj->{PGM}->{type} =~ /NCF/); + } + alarm 0; + }; + if ($@) { + die unless $@ eq "alarm\n"; + confess "$self->{tag}: alarm raised waiting for ncf.\n"; + } + + return $obj; +} + +sub print { + my $self = shift; + my $timeout = ref($_[0]) ? $_[0]->{'timeout'} : 10; + my $out = $self->{out}; + + print "$self->{tag}> @_"; + eval { + local($SIG{ALRM}) = sub { die "alarm\n"; }; + alarm $timeout; + print $out "@_"; + $self->wait_for_ready; + alarm 0; + }; + if ($@) { + die unless $@ eq "alarm\n"; + confess "$self->{tag}: alarm raised.\n"; + } +} + +sub say { + my $self = shift; + $self->print ("@_\n"); +} + +1; diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/SConscript b/3rdparty/openpgm-svn-r1135/pgm/test/SConscript new file mode 100644 index 0000000..7ca9926 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/SConscript @@ -0,0 +1,15 @@ +# -*- mode: python -*- +# OpenPGM build script +# $Id$ + +Import('env') +e = env.Clone(); +e.MergeFlags(env['GLIB_FLAGS']); +e.Append(LIBS = ['libpgm', 'libpgmex']); +e.Append(CCFLAGS = '-DGETTEXT_PACKAGE=\'"pgm"\''); + +e.Program(['monitor.c', 'dump-json.c']) +e.Program(['app.c', 'async.c']) +e.Program(['sim.c', 'dump-json.c', 'async.c']) + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/ambient_spm.pl b/3rdparty/openpgm-svn-r1135/pgm/test/ambient_spm.pl new file mode 100755 index 0000000..90de632 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/ambient_spm.pl @@ -0,0 +1,87 @@ +#!/usr/bin/perl +# ambient_spm.pl +# 5.1.4. Ambient SPMs + +use strict; +use PGM::Test; +use IO::Handle; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +pipe(FROM_PARENT, TO_CHILD) or die "pipe: $!"; +FROM_PARENT->autoflush(1); + +$mon->connect; +$app->connect; + +sub close_ssh { + close FROM_PARENT; close TO_CHILD; + $mon = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +if (my $pid = fork) { +# parent + close FROM_PARENT; + + print "mon: wait for odata ...\n"; + $mon->wait_for_odata; + print "mon: odata received.\n"; + print "mon: wait for spm ...\n"; + $mon->wait_for_spm ({ 'timeout' => 45 }); + print "mon: received spm.\n"; + + print TO_CHILD "die\n"; + + close TO_CHILD; + waitpid($pid,0); +} else { +# child + die "cannot fork: $!" unless defined $pid; + close TO_CHILD; + print "app: loop sending data.\n"; + vec(my $rin, fileno(FROM_PARENT), 1) = 1; + my $rout = undef; + +# hide stdout + open(OLDOUT, ">&STDOUT"); + open(STDOUT, ">/dev/null") or die "Can't redirect stdout: $!"; + +# send every ~50ms + while (! select($rout = $rin, undef, undef, 0.05)) + { + $app->say ("send ao ringo"); + } + +# restore stdout + close(STDOUT) or die "Can't close STDOUT: $!"; + open(STDOUT, ">&OLDOUT") or die "Can't restore stdout: $!"; + close(OLDOUT) or die "Can't close OLDOUT: $!"; + + print "app: loop finished.\n"; + close FROM_PARENT; + exit; +} + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/apdu.pl b/3rdparty/openpgm-svn-r1135/pgm/test/apdu.pl new file mode 100755 index 0000000..b26a3f2 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/apdu.pl @@ -0,0 +1,58 @@ +#!/usr/bin/perl +# apdu.pl +# 6.1. Data Reception + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("set network $config{app}{network}"); +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +$sim->say ("set network $config{sim}{network}"); +$sim->say ("create ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); + +print "sim: publish APDU.\n"; +$sim->say ("send ao ringo x 1000"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: received data [$data].\n"; + +my $ref_data = "ringo" x 1000; +die "incoming data corrupt\n" unless ($data == $ref_data); + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/apdu_parity.pl b/3rdparty/openpgm-svn-r1135/pgm/test/apdu_parity.pl new file mode 100755 index 0000000..eb4cf27 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/apdu_parity.pl @@ -0,0 +1,59 @@ +#!/usr/bin/perl +# apdu_parity.pl +# 6.1. Data Reception + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$sim->connect; +$app->connect; + +sub close_ssh { + $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$app->say ("create ao"); +##$app->say ("set ao FEC RS(255,4)"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +$sim->say ("create ao"); +$sim->say ("set ao FEC RS(255,4)"); +$sim->say ("bind ao"); + +print "sim: publish APDU.\n"; +$sim->say ("send brokn ao ringo x 1200"); + +print "sim: insert parity NAK from app.\n"; + +#print "sim: wait for NAK.\n"; +#my $nak = $sim->wait_for_nak; +#die "Selective NAK received, parityPacket=false\n" unless $nak->{PGM}->{options}->{parityPacket}; +#print "Parity NAK received.\n"; + +print "sim: insert parity RDATA from sim.\n"; + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: received data [$data].\n"; + +my $ref_data = "ringo" x 1200; +die "incoming data corrupt\n" unless ($data == $ref_data); + +print "test completed successfully.\n"; + +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/app.c b/3rdparty/openpgm-svn-r1135/pgm/test/app.c new file mode 100644 index 0000000..ea6a2b7 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/app.c @@ -0,0 +1,1068 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM conformance test application. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "async.h" + + +/* typedefs */ + +struct idle_source { + GSource source; + guint64 expiration; +}; + +struct app_session { + char* name; + pgm_sock_t* sock; + pgm_async_t* async; +}; + +/* globals */ +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "app" + +static int g_port = 7500; +static const char* g_network = ";239.192.0.1"; + +static guint g_max_tpdu = 1500; +static guint g_sqns = 100 * 1000; + +static GHashTable* g_sessions = NULL; +static GMainLoop* g_loop = NULL; +static GIOChannel* g_stdin_channel = NULL; + + +static void on_signal (int, gpointer); +static gboolean on_startup (gpointer); +static gboolean on_mark (gpointer); +static void destroy_session (gpointer, gpointer, gpointer); +static int on_data (gpointer, guint, gpointer); +static gboolean on_stdin_data (GIOChannel*, GIOCondition, gpointer); + + +G_GNUC_NORETURN static +void +usage (const char* bin) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + exit (1); +} + +int +main ( + int argc, + char *argv[] + ) +{ + pgm_error_t* err = NULL; + +/* pre-initialise PGM messages module to add hook for GLib logging */ + pgm_messages_init(); + log_init (); + g_message ("app"); + + if (!pgm_init (&err)) { + g_error ("Unable to start PGM engine: %s", (err && err->message) ? err->message : "(null)"); + pgm_error_free (err); + pgm_messages_shutdown(); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:h")) != -1) + { + switch (c) { + case 'n': g_network = optarg; break; + case 's': g_port = atoi (optarg); break; + + case 'h': + case '?': + pgm_messages_shutdown(); + usage (binary_name); + } + } + + g_loop = g_main_loop_new (NULL, FALSE); + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); + signal (SIGHUP, SIG_IGN); + pgm_signal_install (SIGINT, on_signal, g_loop); + pgm_signal_install (SIGTERM, on_signal, g_loop); + +/* delayed startup */ + g_message ("scheduling startup."); + g_timeout_add (0, (GSourceFunc)on_startup, NULL); + +/* dispatch loop */ + g_message ("entering main event loop ... "); + g_main_loop_run (g_loop); + + g_message ("event loop terminated, cleaning up."); + +/* cleanup */ + g_main_loop_unref(g_loop); + g_loop = NULL; + + if (g_sessions) { + g_message ("destroying sessions."); + g_hash_table_foreach_remove (g_sessions, (GHRFunc)destroy_session, NULL); + g_hash_table_unref (g_sessions); + g_sessions = NULL; + } + + if (g_stdin_channel) { + puts ("unbinding stdin."); + g_io_channel_unref (g_stdin_channel); + g_stdin_channel = NULL; + } + + g_message ("PGM engine shutdown."); + pgm_shutdown(); + g_message ("finished."); + pgm_messages_shutdown(); + return EXIT_SUCCESS; +} + +static +void +destroy_session ( + gpointer key, /* session name */ + gpointer value, /* transport_session object */ + G_GNUC_UNUSED gpointer user_data + ) +{ + struct app_session* sess = (struct app_session*)value; + + g_message ("closing socket \"%s\"", (char*)key); + pgm_close (sess->sock, TRUE); + sess->sock = NULL; + + if (sess->async) { + g_message ("destroying asynchronous session on \"%s\"", (char*)key); + pgm_async_destroy (sess->async); + sess->async = NULL; + } + + g_free (sess->name); + sess->name = NULL; + g_free (sess); +} + +static +void +on_signal ( + int signum, + gpointer user_data + ) +{ + GMainLoop* loop = (GMainLoop*)user_data; + g_message ("on_signal (signum:%d user-data:%p)", signum, user_data); + g_main_loop_quit (loop); +} + +static +gboolean +on_startup ( + G_GNUC_UNUSED gpointer data + ) +{ + g_message ("startup."); + + g_sessions = g_hash_table_new (g_str_hash, g_str_equal); + +/* add stdin to event manager */ + g_stdin_channel = g_io_channel_unix_new (fileno(stdin)); + printf ("binding stdin with encoding %s.\n", g_io_channel_get_encoding(g_stdin_channel)); + + g_io_add_watch (g_stdin_channel, G_IO_IN | G_IO_PRI, on_stdin_data, NULL); + +/* period timer to indicate some form of life */ +// TODO: Gnome 2.14: replace with g_timeout_add_seconds() + g_timeout_add(10 * 1000, (GSourceFunc)on_mark, NULL); + + puts ("READY"); + fflush (stdout); + return FALSE; +} + +static +int +on_data ( + gpointer data, + G_GNUC_UNUSED guint len, + G_GNUC_UNUSED gpointer user_data + ) +{ + printf ("DATA: %s\n", (char*)data); + fflush (stdout); + return 0; +} + +static +void +session_create ( + char* session_name + ) +{ + pgm_error_t* pgm_err = NULL; + +/* check for duplicate */ + struct app_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess != NULL) { + printf ("FAILED: duplicate session name '%s'\n", session_name); + return; + } + +/* create new and fill in bits */ + sess = g_new0(struct app_session, 1); + sess->name = g_memdup (session_name, strlen(session_name)+1); + + if (!pgm_socket (&sess->sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) { + printf ("FAILED: pgm_socket(): %s\n", (pgm_err && pgm_err->message) ? pgm_err->message : "(null)"); + pgm_error_free (pgm_err); + goto err_free; + } + +/* success */ + g_hash_table_insert (g_sessions, sess->name, sess); + printf ("created new session \"%s\"\n", sess->name); + puts ("READY"); + + return; + +err_free: + g_free(sess->name); + g_free(sess); +} + +static +void +session_set_nak_bo_ivl ( + char* session_name, + guint milliseconds + ) +{ +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + + if (pgm_msecs (milliseconds) > INT_MAX) { + puts ("FAILED: value out of bounds"); + return; + } + + const int nak_bo_ivl = pgm_msecs (milliseconds); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl))) + printf ("FAILED: set NAK_BO_IVL = %dms\n", milliseconds); + else + puts ("READY"); +} + +static +void +session_set_nak_rpt_ivl ( + char* session_name, + guint milliseconds + ) +{ +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + + if (pgm_msecs (milliseconds) > INT_MAX) { + puts ("FAILED: value out of bounds"); + return; + } + + const int nak_rpt_ivl = pgm_msecs (milliseconds); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl))) + printf ("FAILED: set NAK_RPT_IVL = %dms\n", milliseconds); + else + puts ("READY"); +} + +static +void +session_set_nak_rdata_ivl ( + char* session_name, + guint milliseconds + ) +{ +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + + if (pgm_msecs (milliseconds) > INT_MAX) { + puts ("FAILED: value out of bounds"); + return; + } + + const int nak_rdata_ivl = pgm_msecs (milliseconds); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl))) + printf ("FAILED: set NAK_RDATA_IVL = %dms\n", milliseconds); + else + puts ("READY"); +} + +static +void +session_set_nak_ncf_retries ( + char* session_name, + guint retry_count + ) +{ +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + + if (retry_count > INT_MAX) { + puts ("FAILED: value out of bounds"); + return; + } + + const int nak_ncf_retries = retry_count; + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries))) + printf ("FAILED: set NAK_NCF_RETRIES = %d\n", retry_count); + else + puts ("READY"); +} + +static +void +session_set_nak_data_retries ( + char* session_name, + guint retry_count + ) +{ +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + + if (retry_count > INT_MAX) { + puts ("FAILED: value out of bounds"); + return; + } + + const int nak_data_retries = retry_count; + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries))) + printf ("FAILED: set NAK_DATA_RETRIES = %d\n", retry_count); + else + puts ("READY"); +} + +static +void +session_set_txw_max_rte ( + char* session_name, + guint bitrate + ) +{ +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + + if (bitrate > INT_MAX) { + puts ("FAILED: value out of bounds"); + return; + } + + const int txw_max_rte = bitrate; + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_TXW_MAX_RTE, &txw_max_rte, sizeof(txw_max_rte))) + printf ("FAILED: set TXW_MAX_RTE = %d\n", bitrate); + else + puts ("READY"); +} + +static +void +session_set_fec ( + char* session_name, + guint block_size, + guint group_size + ) +{ +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + + if (block_size > UINT8_MAX || + group_size > UINT8_MAX) + { + puts ("FAILED: value out of bounds"); + return; + } + + const struct pgm_fecinfo_t fecinfo = { + .block_size = block_size, + .proactive_packets = 0, + .group_size = group_size, + .ondemand_parity_enabled = TRUE, + .var_pktlen_enabled = TRUE + }; + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_USE_FEC, &fecinfo, sizeof(fecinfo))) + printf ("FAILED: set FEC = RS(%d, %d)\n", block_size, group_size); + else + puts ("READY"); +} + +static +void +session_bind ( + char* session_name + ) +{ + pgm_error_t* pgm_err = NULL; + +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist))) + puts ("FAILED: disable IP_ROUTER_ALERT"); + +/* set PGM parameters */ + const int send_and_receive = 0, + active = 0, + mtu = g_max_tpdu, + txw_sqns = g_sqns, + rxw_sqns = g_sqns, + ambient_spm = pgm_secs (30), + heartbeat_spm[] = { pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (1300), + pgm_secs (7), + pgm_secs (16), + pgm_secs (25), + pgm_secs (30) }, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_secs (2), + nak_rdata_ivl = pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + g_assert (G_N_ELEMENTS(heartbeat_spm) > 0); + + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_SEND_ONLY, &send_and_receive, sizeof(send_and_receive))) + puts ("FAILED: set bi-directional transport"); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_RECV_ONLY, &send_and_receive, sizeof(send_and_receive))) + puts ("FAILED: set bi-directional transport"); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_PASSIVE, &active, sizeof(active))) + puts ("FAILED: set active transport"); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_MTU, &mtu, sizeof(mtu))) + printf ("FAILED: set MAX_TPDU = %d bytes\n", mtu); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_TXW_SQNS, &txw_sqns, sizeof(txw_sqns))) + printf ("FAILED: set TXW_SQNS = %d\n", txw_sqns); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_RXW_SQNS, &rxw_sqns, sizeof(rxw_sqns))) + printf ("FAILED: set RXW_SQNS = %d\n", rxw_sqns); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_AMBIENT_SPM, &ambient_spm, sizeof(ambient_spm))) + printf ("FAILED: set AMBIENT_SPM = %ds\n", (int)pgm_to_secs (ambient_spm)); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_HEARTBEAT_SPM, &heartbeat_spm, sizeof(heartbeat_spm))) + { + char buffer[1024]; + sprintf (buffer, "%d", heartbeat_spm[0]); + for (unsigned i = 1; i < G_N_ELEMENTS(heartbeat_spm); i++) { + char t[1024]; + sprintf (t, ", %d", heartbeat_spm[i]); + strcat (buffer, t); + } + printf ("FAILED: set HEARTBEAT_SPM = { %s }\n", buffer); + } + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry))) + printf ("FAILED: set PEER_EXPIRY = %ds\n",(int) pgm_to_secs (peer_expiry)); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry))) + printf ("FAILED: set SPMR_EXPIRY = %dms\n", (int)pgm_to_msecs (spmr_expiry)); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl))) + printf ("FAILED: set NAK_BO_IVL = %dms\n", (int)pgm_to_msecs (nak_bo_ivl)); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl))) + printf ("FAILED: set NAK_RPT_IVL = %dms\n", (int)pgm_to_msecs (nak_rpt_ivl)); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl))) + printf ("FAILED: set NAK_RDATA_IVL = %dms\n", (int)pgm_to_msecs (nak_rdata_ivl)); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries))) + printf ("FAILED: set NAK_DATA_RETRIES = %d\n", nak_data_retries); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries))) + printf ("FAILED: set NAK_NCF_RETRIES = %d\n", nak_ncf_retries); + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = g_port; + addr.sa_addr.sport = 0; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + printf ("FAILED: pgm_gsi_create_from_hostname(): %s\n", (pgm_err && pgm_err->message) ? pgm_err->message : "(null)"); + } + +{ + char buffer[1024]; + pgm_tsi_print_r (&addr.sa_addr, buffer, sizeof(buffer)); + printf ("pgm_bind (sock:%p addr:{port:%d tsi:%s} err:%p)\n", + (gpointer)sess->sock, + addr.sa_port, buffer, + (gpointer)&pgm_err); +} + if (!pgm_bind (sess->sock, &addr, sizeof(addr), &pgm_err)) { + printf ("FAILED: pgm_bind(): %s\n", (pgm_err && pgm_err->message) ? pgm_err->message : "(null)"); + pgm_error_free (pgm_err); + } else + puts ("READY"); +} + +static +void +session_connect ( + char* session_name + ) +{ + struct pgm_addrinfo_t hints = { + .ai_family = AF_INET + }, *res = NULL; + pgm_error_t* pgm_err = NULL; + +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + + if (!pgm_getaddrinfo (g_network, &hints, &res, &pgm_err)) { + printf ("FAILED: pgm_getaddrinfo(): %s\n", (pgm_err && pgm_err->message) ? pgm_err->message : "(null)"); + pgm_error_free (pgm_err); + return; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req))) + { + char group[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&res->ai_recv_addrs[i].gsr_group, sizeof(struct sockaddr_in), + group, sizeof(group), + NULL, 0, + NI_NUMERICHOST); + printf ("FAILED: join group (#%u %s)\n", (unsigned)res->ai_recv_addrs[i].gsr_interface, group); + } + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req))) + { + char group[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&res->ai_send_addrs[0].gsr_group, sizeof(struct sockaddr_in), + group, sizeof(group), + NULL, 0, + NI_NUMERICHOST); + printf ("FAILED: send group (#%u %s)\n", (unsigned)res->ai_send_addrs[0].gsr_interface, group); + } + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int non_blocking = 1, + no_multicast_loop = 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_MULTICAST_LOOP, &no_multicast_loop, sizeof(no_multicast_loop))) + puts ("FAILED: disable multicast loop"); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops))) + printf ("FAILED: set TTL = %d\n", multicast_hops); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_TOS, &dscp, sizeof(dscp))) + printf ("FAILED: set TOS = 0x%x\n", dscp); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_NOBLOCK, &non_blocking, sizeof(non_blocking))) + puts ("FAILED: set non-blocking sockets"); + + if (!pgm_connect (sess->sock, &pgm_err)) { + printf ("FAILED: pgm_connect(): %s\n", (pgm_err && pgm_err->message) ? pgm_err->message : "(null)"); + } else + puts ("READY"); +} + +static +void +session_send ( + char* session_name, + char* string + ) +{ +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + +/* send message */ + int status; + gsize stringlen = strlen(string) + 1; + int n_fds = 1; + struct pollfd fds[ n_fds ]; + struct timeval tv; + int timeout; +again: +printf ("pgm_send (sock:%p string:\"%s\" stringlen:%" G_GSIZE_FORMAT " NULL)\n", (gpointer)sess->sock, string, stringlen); + status = pgm_send (sess->sock, string, stringlen, NULL); + switch (status) { + case PGM_IO_STATUS_NORMAL: + puts ("READY"); + break; + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (sess->sock, IPPROTO_PGM, PGM_TIME_REMAIN, &tv, &optlen); + } + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (sess->sock, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &optlen); + } +/* fall through */ + case PGM_IO_STATUS_WOULD_BLOCK: +block: + timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + memset (fds, 0, sizeof(fds)); + pgm_poll_info (sess->sock, fds, &n_fds, POLLOUT); + poll (fds, n_fds, timeout /* ms */); + goto again; + default: + puts ("FAILED: pgm_send()"); + break; + } +} + +static +void +session_listen ( + char* session_name + ) +{ + GError* err = NULL; + +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + +/* listen */ +printf ("pgm_async_create (async:%p sock:%p err:%p)\n", (gpointer)&sess->async, (gpointer)sess->sock, (gpointer)&err); + if (!pgm_async_create (&sess->async, sess->sock, &err)) { + printf ("FAILED: pgm_async_create(): %s", err->message); + g_error_free (err); + return; + } + pgm_async_add_watch (sess->async, on_data, sess); + puts ("READY"); +} + +static +void +session_destroy ( + char* session_name + ) +{ +/* check that session exists */ + struct app_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + +/* remove from hash table */ + g_hash_table_remove (g_sessions, session_name); + +/* stop any async thread */ + if (sess->async) { + pgm_async_destroy (sess->async); + sess->async = NULL; + } + + pgm_close (sess->sock, TRUE); + sess->sock = NULL; + g_free (sess->name); + sess->name = NULL; + g_free (sess); + + puts ("READY"); +} + +/* process input commands from stdin/fd + */ + +static +gboolean +on_stdin_data ( + GIOChannel* source, + G_GNUC_UNUSED GIOCondition condition, + G_GNUC_UNUSED gpointer data + ) +{ + gchar* str = NULL; + gsize len = 0; + gsize term = 0; + GError* err = NULL; + + g_io_channel_read_line (source, &str, &len, &term, &err); + if (len > 0) { + if (term) str[term] = 0; + +/* quit */ + if (strcmp(str, "quit") == 0) + { + g_main_loop_quit(g_loop); + goto out; + } + + regex_t preg; + regmatch_t pmatch[10]; + +/* create socket */ + const char *re = "^create[[:space:]]+([[:alnum:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + session_create (name); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* set NAK_BO_IVL */ + re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+NAK_BO_IVL[[:space:]]+([0-9]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + char *p = str + pmatch[2].rm_so; + guint nak_bo_ivl = strtol (p, &p, 10); + + session_set_nak_bo_ivl (name, nak_bo_ivl); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* set NAK_RPT_IVL */ + re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+NAK_RPT_IVL[[:space:]]+([0-9]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + char *p = str + pmatch[2].rm_so; + guint nak_rpt_ivl = strtol (p, &p, 10); + + session_set_nak_rpt_ivl (name, nak_rpt_ivl); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* set NAK_RDATA_IVL */ + re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+NAK_RDATA_IVL[[:space:]]+([0-9]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + char *p = str + pmatch[2].rm_so; + guint nak_rdata_ivl = strtol (p, &p, 10); + + session_set_nak_rdata_ivl (name, nak_rdata_ivl); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* set NAK_NCF_RETRIES */ + re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+NAK_NCF_RETRIES[[:space:]]+([0-9]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + char *p = str + pmatch[2].rm_so; + guint nak_ncf_retries = strtol (p, &p, 10); + + session_set_nak_ncf_retries (name, nak_ncf_retries); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* set NAK_DATA_RETRIES */ + re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+NAK_DATA_RETRIES[[:space:]]+([0-9]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + char *p = str + pmatch[2].rm_so; + guint nak_data_retries = strtol (p, &p, 10); + + session_set_nak_data_retries (name, nak_data_retries); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* set TXW_MAX_RTE */ + re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+TXW_MAX_RTE[[:space:]]+([0-9]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + char *p = str + pmatch[2].rm_so; + guint txw_max_rte = strtol (p, &p, 10); + + session_set_txw_max_rte (name, txw_max_rte); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* enable Reed-Solomon Forward Error Correction */ + re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+FEC[[:space:]]+RS[[:space:]]*\\([[:space:]]*([0-9]+)[[:space:]]*,[[:space:]]*([0-9]+)[[:space:]]*\\)$"; + regcomp (&preg, re, REG_EXTENDED); + + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + char *p = str + pmatch[2].rm_so; + *(str + pmatch[2].rm_eo) = 0; + guint n = strtol (p, &p, 10); + p = str + pmatch[3].rm_so; + *(str + pmatch[3].rm_eo) = 0; + guint k = strtol (p, &p, 10); + session_set_fec (name, n, k); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* bind socket */ + re = "^bind[[:space:]]+([[:alnum:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + session_bind (name); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* connect socket */ + re = "^connect[[:space:]]+([[:alnum:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + session_connect (name); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* send packet */ + re = "^send[[:space:]]+([[:alnum:]]+)[[:space:]]+([[:alnum:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + char *string = g_memdup (str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so + 1 ); + string[ pmatch[2].rm_eo - pmatch[2].rm_so ] = 0; + + session_send (name, string); + + g_free (name); + g_free (string); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* listen */ + re = "^listen[[:space:]]+([[:alnum:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + session_listen (name); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* destroy transport */ + re = "^destroy[[:space:]]+([[:alnum:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + session_destroy (name); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* set PGM network */ + re = "^set[[:space:]]+network[[:space:]]+([[:print:]]*;[[:print:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char* pgm_network = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + pgm_network[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + g_network = pgm_network; + puts ("READY"); + + regfree (&preg); + goto out; + } + regfree (&preg); + + printf ("unknown command: %s\n", str); + } + +out: + fflush (stdout); + g_free (str); + return TRUE; +} + +/* idle log notification + */ + +static +gboolean +on_mark ( + G_GNUC_UNUSED gpointer data + ) +{ + g_message ("-- MARK --"); + return TRUE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/async.c b/3rdparty/openpgm-svn-r1135/pgm/test/async.c new file mode 100644 index 0000000..3be1458 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/async.c @@ -0,0 +1,579 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Asynchronous queue for receiving packets in a separate managed thread. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include "async.h" + + +//#define ASYNC_DEBUG + +#ifndef ASYNC_DEBUG +# define g_trace(...) while (0) +#else +#include +# define g_trace(...) g_debug(__VA_ARGS__) +#endif + + +/* globals */ + + +/* global locals */ + +typedef struct pgm_event_t pgm_event_t; + +struct pgm_event_t { + gpointer data; + guint len; +}; + + +/* external: Glib event loop GSource of pgm contiguous data */ +struct pgm_watch_t { + GSource source; + GPollFD pollfd; + pgm_async_t* async; +}; + +typedef struct pgm_watch_t pgm_watch_t; + + +static gboolean pgm_src_prepare (GSource*, gint*); +static gboolean pgm_src_check (GSource*); +static gboolean pgm_src_dispatch (GSource*, GSourceFunc, gpointer); + +static GSourceFuncs g_pgm_watch_funcs = { + .prepare = pgm_src_prepare, + .check = pgm_src_check, + .dispatch = pgm_src_dispatch, + .finalize = NULL, + .closure_callback = NULL +}; + + +static inline gpointer pgm_event_alloc (pgm_async_t* const) G_GNUC_MALLOC; +static PGMAsyncError pgm_async_error_from_errno (const gint); + + +static inline +gpointer +pgm_event_alloc ( + pgm_async_t* const async + ) +{ + g_return_val_if_fail (async != NULL, NULL); + return g_slice_alloc (sizeof(pgm_event_t)); +} + +/* release event memory for custom async queue dispatch handlers + */ + +static inline +void +pgm_event_unref ( + pgm_async_t* const async, + pgm_event_t* const event + ) +{ + g_return_if_fail (async != NULL); + g_return_if_fail (event != NULL); + g_slice_free1 (sizeof(pgm_event_t), event); +} + +/* internal receiver thread, sits in a loop processing incoming packets + */ + +static +gpointer +pgm_receiver_thread ( + gpointer data + ) +{ + g_assert (NULL != data); + + pgm_async_t* async = (pgm_async_t*)data; + g_async_queue_ref (async->commit_queue); + +/* incoming message buffer */ + struct pgm_msgv_t msgv; + gsize bytes_read = 0; + struct timeval tv; + + do { +/* blocking read */ + const int status = pgm_recvmsg (async->sock, &msgv, 0, &bytes_read, NULL); + switch (status) { + case PGM_IO_STATUS_NORMAL: + { +/* queue a copy to receiver */ + pgm_event_t* event = pgm_event_alloc (async); + event->data = bytes_read > 0 ? g_malloc (bytes_read) : NULL; + event->len = bytes_read; + gpointer dst = event->data; + guint i = 0; + while (bytes_read) { + const struct pgm_sk_buff_t* skb = msgv.msgv_skb[i++]; + g_assert (NULL != skb); + g_assert (skb->len > 0); + g_assert (skb->len <= bytes_read); + memcpy (dst, skb->data, skb->len); + dst = (char*)dst + skb->len; + bytes_read -= skb->len; + } +/* prod pipe on edge */ + g_async_queue_lock (async->commit_queue); + g_async_queue_push_unlocked (async->commit_queue, event); + if (g_async_queue_length_unlocked (async->commit_queue) == 1) + pgm_notify_send (&async->commit_notify); + g_async_queue_unlock (async->commit_queue); + break; + } + + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (async->sock, IPPROTO_PGM, PGM_TIME_REMAIN, &tv, &optlen); + } + goto block; + + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (async->sock, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &optlen); + } +/* fall through */ + case PGM_IO_STATUS_WOULD_BLOCK: +block: + { +#ifdef CONFIG_HAVE_POLL + const int timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + int n_fds = 3; + struct pollfd fds[1+n_fds]; + memset (fds, 0, sizeof(fds)); + fds[0].fd = pgm_notify_get_fd (&async->destroy_notify); + fds[0].events = POLLIN; + if (-1 == pgm_poll_info (async->sock, &fds[1], &n_fds, POLLIN)) { + g_trace ("poll_info returned errno=%i",errno); + goto cleanup; + } + const int ready = poll (fds, 1 + n_fds, timeout); +#else /* HAVE_SELECT */ + fd_set readfds; + int fd = pgm_notify_get_fd (&async->destroy_notify), n_fds = 1 + fd; + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + if (-1 == pgm_select_info (async->sock, &readfds, NULL, &n_fds)) { + g_trace ("select_info returned errno=%i",errno); + goto cleanup; + } + const int ready = select (n_fds, &readfds, NULL, NULL, PGM_IO_STATUS_RATE_LIMITED == status ? &tv : NULL); +#endif + if (-1 == ready) { + g_trace ("block returned errno=%i",errno); + goto cleanup; + } +#ifdef CONFIG_HAVE_POLL + if (ready > 0 && fds[0].revents) +#else + if (ready > 0 && FD_ISSET(fd, &readfds)) +#endif + goto cleanup; + break; + } + + case PGM_IO_STATUS_ERROR: + case PGM_IO_STATUS_EOF: + goto cleanup; + + case PGM_IO_STATUS_RESET: + { + int is_abort_on_reset; + socklen_t optlen = sizeof (is_abort_on_reset); + pgm_getsockopt (async->sock, IPPROTO_PGM, PGM_ABORT_ON_RESET, &is_abort_on_reset, &optlen); + if (is_abort_on_reset) + goto cleanup; + break; + } + +/* TODO: report to user */ + case PGM_IO_STATUS_FIN: + break; + + default: + g_assert_not_reached(); + } + } while (!async->is_destroyed); + +cleanup: + g_async_queue_unref (async->commit_queue); + return NULL; +} + +/* create asynchronous thread handler + * + * on success, 0 is returned. on error, -1 is returned, and errno set appropriately. + * on invalid parameters, -EINVAL is returned. + */ + +gboolean +pgm_async_create ( + pgm_async_t** async, + pgm_sock_t* const sock, + GError** error + ) +{ + pgm_async_t* new_async; + + g_return_val_if_fail (NULL != async, FALSE); + g_return_val_if_fail (NULL != sock, FALSE); + + g_trace ("create (async:%p sock:%p error:%p)", + (gpointer)async, (gpointer)sock, (gpointer)error); + + if (!g_thread_supported()) + g_thread_init (NULL); + + new_async = g_new0 (pgm_async_t, 1); + new_async->sock = sock; + if (0 != pgm_notify_init (&new_async->commit_notify) || + 0 != pgm_notify_init (&new_async->destroy_notify)) + { + g_set_error (error, + PGM_ASYNC_ERROR, + pgm_async_error_from_errno (errno), + _("Creating async notification channels: %s"), + g_strerror (errno)); + g_free (new_async); + return FALSE; + } + new_async->commit_queue = g_async_queue_new(); +/* setup new thread */ + new_async->thread = g_thread_create_full (pgm_receiver_thread, + new_async, + 0, + TRUE, + TRUE, + G_THREAD_PRIORITY_HIGH, + error); + if (NULL == new_async->thread) { + g_async_queue_unref (new_async->commit_queue); + pgm_notify_destroy (&new_async->commit_notify); + g_free (new_async); + return FALSE; + } + +/* return new object */ + *async = new_async; + return TRUE; +} + +/* tell async thread to stop, wait for it to stop, then cleanup. + * + * on success, 0 is returned. if async is invalid, -EINVAL is returned. + */ + +gboolean +pgm_async_destroy ( + pgm_async_t* const async + ) +{ + g_return_val_if_fail (NULL != async, FALSE); + g_return_val_if_fail (!async->is_destroyed, FALSE); + + async->is_destroyed = TRUE; + pgm_notify_send (&async->destroy_notify); + if (async->thread) + g_thread_join (async->thread); + if (async->commit_queue) { + g_async_queue_unref (async->commit_queue); + async->commit_queue = NULL; + } + pgm_notify_destroy (&async->destroy_notify); + pgm_notify_destroy (&async->commit_notify); + g_free (async); + return TRUE; +} + +/* queue to GSource and GMainLoop */ + +GSource* +pgm_async_create_watch ( + pgm_async_t* async + ) +{ + g_return_val_if_fail (async != NULL, NULL); + + GSource *source = g_source_new (&g_pgm_watch_funcs, sizeof(pgm_watch_t)); + pgm_watch_t *watch = (pgm_watch_t*)source; + + watch->async = async; + watch->pollfd.fd = pgm_async_get_fd (async); + watch->pollfd.events = G_IO_IN; + + g_source_add_poll (source, &watch->pollfd); + + return source; +} + +/* pgm transport attaches to the callees context: the default context instead of + * any internal contexts. + */ + +int +pgm_async_add_watch_full ( + pgm_async_t* async, + gint priority, + pgm_eventfn_t function, + gpointer user_data, + GDestroyNotify notify + ) +{ + g_return_val_if_fail (async != NULL, -EINVAL); + g_return_val_if_fail (function != NULL, -EINVAL); + + GSource* source = pgm_async_create_watch (async); + + if (priority != G_PRIORITY_DEFAULT) + g_source_set_priority (source, priority); + + g_source_set_callback (source, (GSourceFunc)function, user_data, notify); + + guint id = g_source_attach (source, NULL); + g_source_unref (source); + + return id; +} + +int +pgm_async_add_watch ( + pgm_async_t* async, + pgm_eventfn_t function, + gpointer user_data + ) +{ + return pgm_async_add_watch_full (async, G_PRIORITY_HIGH, function, user_data, NULL); +} + +/* returns TRUE if source has data ready, i.e. async queue is not empty + * + * called before event loop poll() + */ + +static +gboolean +pgm_src_prepare ( + GSource* source, + gint* timeout + ) +{ + pgm_watch_t* watch = (pgm_watch_t*)source; + +/* infinite timeout */ + *timeout = -1; + + return ( g_async_queue_length(watch->async->commit_queue) > 0 ); +} + +/* called after event loop poll() + * + * return TRUE if ready to dispatch. + */ + +static +gboolean +pgm_src_check ( + GSource* source + ) +{ + pgm_watch_t* watch = (pgm_watch_t*)source; + + return ( g_async_queue_length(watch->async->commit_queue) > 0 ); +} + +/* called when TRUE returned from prepare or check + */ + +static gboolean +pgm_src_dispatch ( + GSource* source, + GSourceFunc callback, + gpointer user_data + ) +{ + g_trace ("pgm_src_dispatch (source:%p callback:() user-data:%p)", + (gpointer)source, user_data); + + const pgm_eventfn_t function = (pgm_eventfn_t)callback; + pgm_watch_t* watch = (pgm_watch_t*)source; + pgm_async_t* async = watch->async; + +/* empty pipe */ + pgm_notify_read (&async->commit_notify); + +/* purge only one message from the asynchronous queue */ + pgm_event_t* event = g_async_queue_try_pop (async->commit_queue); + if (event) + { +/* important that callback occurs out of lock to allow PGM layer to add more messages */ + (*function) (event->data, event->len, user_data); + +/* return memory to receive window */ + if (event->len) g_free (event->data); + pgm_event_unref (async, event); + } + + return TRUE; +} + +/* synchronous reading from the queue. + * + * returns GIOStatus with success, error, again, or eof. + */ + +GIOStatus +pgm_async_recv ( + pgm_async_t* const async, + gpointer data, + const gsize len, + gsize* const bytes_read, + const int flags, /* MSG_DONTWAIT for non-blocking */ + GError** error + ) +{ + g_return_val_if_fail (NULL != async, G_IO_STATUS_ERROR); + if (len) g_return_val_if_fail (NULL != data, G_IO_STATUS_ERROR); + + g_trace ("pgm_async_recv (async:%p data:%p len:%" G_GSIZE_FORMAT" bytes-read:%p flags:%d error:%p)", + (gpointer)async, data, len, (gpointer)bytes_read, flags, (gpointer)error); + + pgm_event_t* event = NULL; + g_async_queue_lock (async->commit_queue); + if (g_async_queue_length_unlocked (async->commit_queue) == 0) + { + g_async_queue_unlock (async->commit_queue); + if (flags & MSG_DONTWAIT || async->is_nonblocking) + return G_IO_STATUS_AGAIN; +#ifdef CONFIG_HAVE_POLL + struct pollfd fds[1]; + int ready; + do { + memset (fds, 0, sizeof(fds)); + fds[0].fd = pgm_notify_get_fd (&async->commit_notify); + fds[0].events = POLLIN; + ready = poll (fds, G_N_ELEMENTS(fds), -1); + if (-1 == ready || async->is_destroyed) /* errno = EINTR */ + return G_IO_STATUS_ERROR; + } while (ready <= 0); +#else + fd_set readfds; + int n_fds, ready, fd = pgm_notify_get_fd (&async->commit_notify); + do { + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + n_fds = fd + 1; + ready = select (n_fds, &readfds, NULL, NULL, NULL); + if (-1 == ready || async->is_destroyed) /* errno = EINTR */ + return G_IO_STATUS_ERROR; + } while (ready <= 0); +#endif + pgm_notify_read (&async->commit_notify); + g_async_queue_lock (async->commit_queue); + } + event = g_async_queue_pop_unlocked (async->commit_queue); + g_async_queue_unlock (async->commit_queue); + +/* pass data back to callee */ + if (event->len > len) { + *bytes_read = len; + memcpy (data, event->data, *bytes_read); + g_set_error (error, + PGM_ASYNC_ERROR, + PGM_ASYNC_ERROR_OVERFLOW, + _("Message too large to be stored in buffer.")); + pgm_event_unref (async, event); + return G_IO_STATUS_ERROR; + } + + if (bytes_read) + *bytes_read = event->len; + memcpy (data, event->data, event->len); + +/* cleanup */ + if (event->len) g_free (event->data); + pgm_event_unref (async, event); + return G_IO_STATUS_NORMAL; +} + +gboolean +pgm_async_set_nonblocking ( + pgm_async_t* const async, + const gboolean nonblocking + ) +{ + g_return_val_if_fail (NULL != async, FALSE); + async->is_nonblocking = nonblocking; + return TRUE; +} + +GQuark +pgm_async_error_quark (void) +{ + return g_quark_from_static_string ("pgm-async-error-quark"); +} + +static +PGMAsyncError +pgm_async_error_from_errno ( + const gint err_no + ) +{ + switch (err_no) { +#ifdef EFAULT + case EFAULT: + return PGM_ASYNC_ERROR_FAULT; + break; +#endif + +#ifdef EMFILE + case EMFILE: + return PGM_ASYNC_ERROR_MFILE; + break; +#endif + +#ifdef ENFILE + case ENFILE: + return PGM_ASYNC_ERROR_NFILE; + break; +#endif + + default : + return PGM_ASYNC_ERROR_FAILED; + break; + } +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/async.h b/3rdparty/openpgm-svn-r1135/pgm/test/async.h new file mode 100644 index 0000000..d801abd --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/async.h @@ -0,0 +1,76 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * Asynchronous receive thread helper + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_ASYNC_H__ +#define __PGM_ASYNC_H__ + +#include +#include +#include + + +#define PGM_ASYNC_ERROR pgm_async_error_quark () + +typedef enum +{ + /* Derived from errno */ + PGM_ASYNC_ERROR_FAULT, + PGM_ASYNC_ERROR_MFILE, + PGM_ASYNC_ERROR_NFILE, + PGM_ASYNC_ERROR_OVERFLOW, + PGM_ASYNC_ERROR_FAILED +} PGMAsyncError; + +typedef struct pgm_async_t pgm_async_t; + +struct pgm_async_t { + pgm_sock_t* sock; + GThread* thread; + GAsyncQueue* commit_queue; + pgm_notify_t commit_notify; + pgm_notify_t destroy_notify; + gboolean is_destroyed; + gboolean is_nonblocking; +}; + +typedef int (*pgm_eventfn_t)(gpointer, guint, gpointer); + + +G_BEGIN_DECLS + +int pgm_async_create (pgm_async_t**, pgm_sock_t* const, GError**); +int pgm_async_destroy (pgm_async_t* const); +GIOStatus pgm_async_recv (pgm_async_t* const, gpointer, const gsize, gsize* const, const int, GError**); +gboolean pgm_async_set_nonblocking (pgm_async_t* const, const gboolean); +GSource* pgm_async_create_watch (pgm_async_t* const) G_GNUC_WARN_UNUSED_RESULT; +int pgm_async_add_watch_full (pgm_async_t*, gint, pgm_eventfn_t, gpointer, GDestroyNotify); +int pgm_async_add_watch (pgm_async_t*, pgm_eventfn_t, gpointer); +GQuark pgm_async_error_quark (void); + +static inline int pgm_async_get_fd (pgm_async_t* async) +{ + g_return_val_if_fail (async != NULL, -EINVAL); + return pgm_notify_get_fd (&async->commit_notify); +} + +G_END_DECLS + +#endif /* __PGM_ASYNC_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/dump-json.c b/3rdparty/openpgm-svn-r1135/pgm/test/dump-json.c new file mode 100644 index 0000000..adbc217 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/dump-json.c @@ -0,0 +1,1292 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * JSON packet dump. + * + * Copyright (c) 2006-2008 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "dump-json.h" + + +/* globals */ + +#define OPTIONS_TOTAL_LEN(x) *(guint16*)( ((char*)(x)) + sizeof(guint16) ) + + +int verify_ip_header (struct pgm_ip*, guint); +void print_ip_header (struct pgm_ip*); +int verify_pgm_header (struct pgm_header*, guint); +void print_pgm_header (struct pgm_header*); +int verify_spm (struct pgm_header*, char*, guint); +void print_spm (struct pgm_header*, char*); +int verify_poll (struct pgm_header*, char*, guint); +void print_poll (struct pgm_header*, char*); +int verify_polr (struct pgm_header*, char*, guint); +void print_polr (struct pgm_header*, char*); +int verify_odata (struct pgm_header*, char*, guint); +void print_odata (struct pgm_header*, char*); +int verify_rdata (struct pgm_header*, char*, guint); +void print_rdata (struct pgm_header*, char*); +static int generic_verify_nak (const char*, struct pgm_header*, char*, guint); +static void generic_print_nak (const char*, struct pgm_header*, char*); +int verify_nak (struct pgm_header*, char*, guint); +void print_nak (struct pgm_header*, char*); +int verify_nnak (struct pgm_header*, char*, guint); +void print_nnak (struct pgm_header*, char*); +int verify_ncf (struct pgm_header*, char*, guint); +void print_ncf (struct pgm_header*, char*); +int verify_spmr (struct pgm_header*, char*, guint); +void print_spmr (struct pgm_header*, char*); +int verify_options (char*, guint); +void print_options (char*); + + +int +monitor_packet ( + char* data, + guint len + ) +{ + static int count = 0; + + puts ("{"); + printf ("\t\"id\": %i,\n", ++count); + + int retval = 0; + + struct pgm_ip* ip = (struct pgm_ip*)data; + if (verify_ip_header (ip, len) < 0) { + puts ("\t\"valid\": false"); + retval = -1; + goto out; + } + + struct pgm_header* pgm = (struct pgm_header*)(data + (ip->ip_hl * 4)); + guint pgm_len = len - (ip->ip_hl * 4); + if (verify_pgm_header (pgm, pgm_len) < 0) { + puts ("\t\"valid\": false"); + retval = -1; + goto out; + } + + char* pgm_data = (char*)(pgm + 1); + guint pgm_data_len = pgm_len - sizeof(struct pgm_header); + switch (pgm->pgm_type) { + case PGM_SPM: retval = verify_spm (pgm, pgm_data, pgm_data_len); break; + case PGM_POLL: retval = verify_poll (pgm, pgm_data, pgm_data_len); break; + case PGM_POLR: retval = verify_polr (pgm, pgm_data, pgm_data_len); break; + case PGM_ODATA: retval = verify_odata (pgm, pgm_data, pgm_data_len); break; + case PGM_RDATA: retval = verify_rdata (pgm, pgm_data, pgm_data_len); break; + case PGM_NAK: retval = verify_nak (pgm, pgm_data, pgm_data_len); break; + case PGM_NNAK: retval = verify_nnak (pgm, pgm_data, pgm_data_len); break; + case PGM_NCF: retval = verify_ncf (pgm, pgm_data, pgm_data_len); break; + case PGM_SPMR: retval = verify_spmr (pgm, pgm_data, pgm_data_len); break; + } + + if (retval < 0) { + puts ("\t\"valid\": false"); + goto out; + } + +/* packet verified correct */ + puts ("\t\"valid\": true,"); + + print_ip_header (ip); + print_pgm_header (pgm); + + switch (pgm->pgm_type) { + case PGM_SPM: print_spm (pgm, pgm_data); break; + case PGM_POLL: print_poll (pgm, pgm_data); break; + case PGM_POLR: print_polr (pgm, pgm_data); break; + case PGM_ODATA: print_odata (pgm, pgm_data); break; + case PGM_RDATA: print_rdata (pgm, pgm_data); break; + case PGM_NAK: print_nak (pgm, pgm_data); break; + case PGM_NNAK: print_nnak (pgm, pgm_data); break; + case PGM_NCF: print_ncf (pgm, pgm_data); break; + case PGM_SPMR: print_spmr (pgm, pgm_data); break; + } + +out: + puts ("}"); + return retval; +} + + +int +verify_ip_header ( + struct pgm_ip* ip, + guint len + ) +{ +/* minimum size should be IP header plus PGM header */ + if (len < (sizeof(struct pgm_ip) + sizeof(struct pgm_header))) + { + printf ("\t\"message\": \"IP: packet size too small: %i bytes, expecting at least %" G_GSIZE_FORMAT " bytes.\",\n", len, sizeof(struct pgm_header)); + return -1; + } + +/* IP packet header: IPv4 + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |Version| HL | ToS | Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Fragment ID |R|D|M| Fragment Offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | Protocol | Checksum | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Source IP Address | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Destination IP Address | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | IP Options when present ... + * +-+-+-+-+-+-+-+-+-+-+-+-+ ... + * | Data ... + * +-+-+- ... + * + * IPv6: n/a + */ + +/* decode IP header */ + if (ip->ip_v != 4 && ip->ip_v != 6) { /* IP version, 4 or 6 */ + printf ("\t\"message\": \"IP: unknown IP version %i.\",\n", ip->ip_v); + return -1; + } + + guint ip_header_length = ip->ip_hl * 4; /* IP header length in 32bit octets */ + if (ip_header_length < sizeof(struct pgm_ip)) { + printf ("\t\"message\": \"IP: bad IP header length %i, should be at least %" G_GSIZE_FORMAT "lu bytes.\",\n", ip_header_length, sizeof(struct pgm_ip)); + return -1; + } + +/* ip_len can equal packet_length - ip_header_length in FreeBSD/NetBSD + * Stevens/Fenner/Rudolph, Unix Network Programming Vol.1, p.739 + * + * RFC3828 allows partial packets such that len < packet_length with UDP lite + */ + guint packet_length = g_ntohs(ip->ip_len); /* total packet length */ + if (len < packet_length) { /* redundant: often handled in kernel */ + printf ("\t\"message\": \"IP: truncated IP packet: header reports %i actual length %i bytes.\",\n", (int)len, (int)packet_length); + return -1; + } + +/* TCP Segmentation Offload (TSO) might have zero length here */ + if (packet_length < ip_header_length) { + printf ("\t\"message\": \"IP: header reports %i less than IP header length %i.\",\n", (int)packet_length, (int)ip_header_length); + return -1; + } + +/* packets that fail checksum will generally not be passed upstream except with rfc3828 + */ + int sum = pgm_inet_checksum((char*)ip, ip_header_length, 0); + if (sum != 0) { + int ip_sum = g_ntohs(ip->ip_sum); + printf ("\t\"message\": \"IP: IP header checksum incorrect: 0x%x.\",\n", ip_sum); + return -2; + } + + if (ip->ip_p != IPPROTO_PGM) { + printf ("\t\"message\": \"IP: packet IP protocol not PGM: %i.\",\n", ip->ip_p); + return -1; + } + +/* fragmentation offset, bit 0: 0, bit 1: do-not-fragment, bit 2: more-fragments */ + int offset = g_ntohs(ip->ip_off); + if ((offset & 0x1fff) != 0) { + printf ("\t\"message\": \"IP: fragmented IP packet, ignoring.\",\n"); + return -1; + } + + return 0; +} + +void +print_ip_header ( + struct pgm_ip* ip + ) +{ + puts ("\t\"IP\": {"); + printf ("\t\t\"version\": %i,\n", + ip->ip_v + ); + printf ("\t\t\"headerLength\": %i,\n", + ip->ip_hl + ); + printf ("\t\t\"ToS\": %i,\n", + ip->ip_tos & 0x3 + ); + printf ("\t\t\"length\": %i,\n", + g_ntohs(ip->ip_len) + ); + printf ("\t\t\"fragmentId\": %i,\n", + g_ntohs(ip->ip_id) + ); + printf ("\t\t\"DF\": %s,\n", + (g_ntohs(ip->ip_off) & 0x4000) ? "true" : "false" + ); + printf ("\t\t\"MF\": %s,\n", + (g_ntohs(ip->ip_off) & 0x2000) ? "true" : "false" + ); + printf ("\t\t\"fragmentOffset\": %i,\n", + g_ntohs(ip->ip_off) & 0x1fff + ); + printf ("\t\t\"TTL\": %i,\n", + ip->ip_ttl + ); + printf ("\t\t\"protocol\": %i,\n", + ip->ip_p + ); + printf ("\t\t\"sourceIp\": \"%s\",\n", + inet_ntoa(*(struct in_addr*)&ip->ip_src) + ); + printf ("\t\t\"destinationIp\": \"%s\",\n", + inet_ntoa(*(struct in_addr*)&ip->ip_dst) + ); + puts ("\t\t\"IpOptions\": {"); + puts ("\t\t}"); + puts ("\t},"); +} + +int +verify_pgm_header ( + struct pgm_header* pgm, + guint pgm_len + ) +{ + +/* PGM payload, header looks as follows: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Source Port | Destination Port | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Options | Checksum | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Global Source ID ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ... Global Source ID | TSDU Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type specific data ... + * +-+-+-+-+-+-+-+-+-+- ... + */ + if (pgm_len < sizeof(pgm)) { + printf ("\t\"message\": \"PGM: packet size less than PGM header: %i bytes.\",\n", pgm_len); + return -1; + } + + if (pgm->pgm_checksum) + { + int sum = pgm->pgm_checksum; + pgm->pgm_checksum = 0; + int pgm_sum = pgm_csum_fold (pgm_csum_partial ((const char*)pgm, pgm_len, 0)); + pgm->pgm_checksum = sum; + if (pgm_sum != sum) { + printf ("\t\"message\": \"PGM: PGM packet checksum incorrect, packet 0x%x calculated 0x%x.\",\n", sum, pgm_sum); + return -2; + } + } else { + if (pgm->pgm_type != PGM_ODATA && pgm->pgm_type != PGM_RDATA) { + printf ("\t\"message\": \"PGM: No PGM checksum value, mandatory for ODATA/RDATA.\",\n"); + return -1; + } + } + + if ( pgm->pgm_type != PGM_SPM && + pgm->pgm_type != PGM_POLL && + pgm->pgm_type != PGM_POLR && + pgm->pgm_type != PGM_ODATA && + pgm->pgm_type != PGM_RDATA && + pgm->pgm_type != PGM_NAK && + pgm->pgm_type != PGM_NNAK && + pgm->pgm_type != PGM_NCF && + pgm->pgm_type != PGM_SPMR ) + { + printf ("\t\"message\": \"PGM: Not a valid PGM packet type: %i.\",\n", pgm->pgm_type); + return -1; + } + + return 0; +} + +/* note: output trails tsdu length line to allow for comma + */ + +void +print_pgm_header ( + struct pgm_header* pgm + ) +{ + puts ("\t\"PGM\": {"); + printf ("\t\t\"sourcePort\": %i,\n", g_ntohs(pgm->pgm_sport)); + printf ("\t\t\"destinationPort\": %i,\n", g_ntohs(pgm->pgm_dport)); + printf ("\t\t\"type\": \"%s\",\n", pgm_type_string(pgm->pgm_type & 0xf)); + printf ("\t\t\"version\": %i,\n", (pgm->pgm_type & 0xc0) >> 6); + puts ("\t\t\"options\": {"); + printf ("\t\t\t\"networkSignificant\": %s,\n", (pgm->pgm_options & PGM_OPT_NETWORK) ? "true" : "false"); + printf ("\t\t\t\"parityPacket\": %s,\n", (pgm->pgm_options & PGM_OPT_PARITY) ? "true" : "false"); + printf ("\t\t\t\"variableLength\": %s\n", (pgm->pgm_options & PGM_OPT_VAR_PKTLEN) ? "true" : "false"); + puts ("\t\t},"); + printf ("\t\t\"checksum\": %i,\n", pgm->pgm_checksum); + printf ("\t\t\"gsi\": \"%i.%i.%i.%i.%i.%i\",\n", + pgm->pgm_gsi[0], + pgm->pgm_gsi[1], + pgm->pgm_gsi[2], + pgm->pgm_gsi[3], + pgm->pgm_gsi[4], + pgm->pgm_gsi[5]); + printf ("\t\t\"tsduLength\": %i", g_ntohs(pgm->pgm_tsdu_length)); +} + +/* 8.1. Source Path Messages (SPM) + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SPM's Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Trailing Edge Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Leading Edge Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Path NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * NLA = Network Layer Address + * NLA AFI = NLA Address Family Indicator: rfc 1700 (ADDRESS FAMILY NUMBERS) + * => Path NLA = IP address of last network element + */ + +int +verify_spm ( + struct pgm_header* header, + char* data, + guint len + ) +{ + int retval = 0; + +/* truncated packet */ + if (len < sizeof(struct pgm_spm)) { + printf ("\t\"message\": \"SPM: packet length: %i less than minimum SPM length: %" G_GSIZE_FORMAT "lu bytes.\",\n", len, sizeof(struct pgm_spm)); + retval = -1; + goto out; + } + + struct pgm_spm* spm = (struct pgm_spm*)data; + char* opt_offset = (char*)(spm + 1); + guint opt_len = len - sizeof(spm); + + switch (g_ntohs(spm->spm_nla_afi)) { + case AFI_IP6: + if (len < sizeof(struct pgm_spm6)) { + printf ("\t\"message\": \"SPM: packet length: %i less than minimum IPv6 SPM length: %" G_GSIZE_FORMAT "lu bytes.\",\n", len, sizeof(struct pgm_spm6)); + retval = -1; + goto out; + } + opt_offset += sizeof(struct pgm_spm6) - sizeof(struct pgm_spm); + opt_len -= sizeof(struct pgm_spm6) - sizeof(struct pgm_spm); + + case AFI_IP: + break; + + default: + printf ("\t\"message\": \"SPM: invalid AFI of source NLA: %i.\",\n", g_ntohs(spm->spm_nla_afi)); + retval = -1; + goto out; + } + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT) + { + retval = verify_options (opt_offset, opt_len); + } + +out: + return retval; +} + +void +print_spm ( + struct pgm_header* header, + char* data + ) +{ + struct pgm_spm* spm = (struct pgm_spm*)data; + struct pgm_spm6* spm6 = (struct pgm_spm6*)data; + char* opt_offset = (char*)(spm + 1); + + puts (","); + printf ("\t\t\"spmSqn\": %i,\n", g_ntohl(spm->spm_sqn)); + printf ("\t\t\"spmTrail\": %i,\n", g_ntohl(spm->spm_trail)); + printf ("\t\t\"spmLead\": %i,\n", g_ntohl(spm->spm_lead)); + printf ("\t\t\"spmNlaAfi\": %i,\n", g_ntohs (spm->spm_nla_afi)); + + char s[INET6_ADDRSTRLEN]; + switch (g_ntohs(spm->spm_nla_afi)) { + case AFI_IP: + inet_ntop ( AF_INET, &spm->spm_nla, s, sizeof (s) ); + break; + + case AFI_IP6: + inet_ntop ( AF_INET6, &spm6->spm6_nla, s, sizeof (s) ); + opt_offset += sizeof(struct pgm_spm6) - sizeof(struct pgm_spm); + break; + } + + printf ("\t\t\"spmNla\": \"%s\"", s); + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT) + { + puts (","); + print_options (opt_offset); + } + + puts ("\n\t}"); +} + +/* 14.7.1. Poll Request + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLL's Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLL's Round | POLL's Sub-type | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Path NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | POLL's Back-off Interval | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Random String | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Matching Bit-Mask | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Sent to ODATA multicast group with IP Router Alert option. + */ + +#define PGM_MIN_POLL_SIZE ( sizeof(struct pgm_poll) ) + +int +verify_poll ( + G_GNUC_UNUSED struct pgm_header* header, + G_GNUC_UNUSED char* data, + G_GNUC_UNUSED guint len + ) +{ + return -1; +} + +void +print_poll ( + G_GNUC_UNUSED struct pgm_header* header, + G_GNUC_UNUSED char* data + ) +{ +} + +/* 14.7.2. Poll Response + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLR's Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | POLR's Round | reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +int +verify_polr ( + G_GNUC_UNUSED struct pgm_header* header, + G_GNUC_UNUSED char* data, + G_GNUC_UNUSED guint len + ) +{ + return -1; +} + +void +print_polr ( + G_GNUC_UNUSED struct pgm_header* header, + G_GNUC_UNUSED char* data + ) +{ +} + +/* 8.2. Data Packet + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Data Packet Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Trailing Edge Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Data ... + * +-+-+- ... + */ + +int +verify_odata ( + struct pgm_header* header, + char* data, + guint len + ) +{ + int retval = 0; + + if (len < sizeof(struct pgm_data)) { + printf ("\t\"message\": \"ODATA: packet length: %i less than minimum ODATA length: %" G_GSIZE_FORMAT " bytes.\",\n", len, sizeof(struct pgm_data)); + retval = -1; + goto out; + } + + char* tsdu = data + sizeof(struct pgm_data); + guint tsdu_len = len - sizeof(struct pgm_data); + if (header->pgm_options & PGM_OPT_PRESENT) + { + retval = verify_options (tsdu, tsdu_len); + + guint opt_total_len = g_ntohs( OPTIONS_TOTAL_LEN(tsdu) ); + tsdu += opt_total_len; + tsdu_len -= opt_total_len; + } + + if (!retval && g_ntohs(header->pgm_tsdu_length) != tsdu_len) { + printf ("\t\"message\": \"ODATA: TSDU truncated expected %i, found %i bytes.\",\n", g_ntohs(header->pgm_tsdu_length), tsdu_len); + retval = -1; + } +out: + return retval; +} + +void +print_odata ( + struct pgm_header* header, + char* data + ) +{ + struct pgm_data* odata = (struct pgm_data*)data; + char* tsdu = data + sizeof(struct pgm_data); + + puts (","); + printf ("\t\t\"odSqn\": %lu,\n", (gulong)g_ntohl(odata->data_sqn)); + printf ("\t\t\"odTrail\": %lu,\n", (gulong)g_ntohl(odata->data_trail)); + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT) + { + print_options (tsdu); + tsdu += g_ntohs( OPTIONS_TOTAL_LEN(tsdu) ); + puts (","); + } + +/* data */ + printf ("\t\t\"data\": \""); + char* end = tsdu + g_ntohs (header->pgm_tsdu_length); + while (tsdu < end) { + if (isprint(*tsdu)) + putchar(*tsdu); + else + putchar('.'); + tsdu++; + } + + puts ("\""); + puts ("\t}"); +} + +/* 8.2. Repair Data + */ + +int +verify_rdata ( + struct pgm_header* header, + char* data, + guint len + ) +{ + int retval = 0; + + if (len < sizeof(struct pgm_data)) { + printf ("\t\"message\": \"RDATA: packet length: %i less than minimum RDATA length: %" G_GSIZE_FORMAT " bytes.\",\n", len, sizeof(struct pgm_data)); + retval = -1; + goto out; + } + + char* tsdu = data + sizeof(struct pgm_data); + guint tsdu_len = len - sizeof(struct pgm_data); + if (header->pgm_options & PGM_OPT_PRESENT) + { + retval = verify_options (tsdu, tsdu_len); + + guint opt_total_len = g_ntohs( OPTIONS_TOTAL_LEN(tsdu) ); + tsdu += opt_total_len; + tsdu_len -= opt_total_len; + } + + if (!retval && g_ntohs(header->pgm_tsdu_length) != tsdu_len) { + printf ("\t\"message\": \"RDATA: tsdu truncated expected %i, found %i bytes.\",\n", g_ntohs(header->pgm_tsdu_length), tsdu_len); + retval = -1; + } +out: + return retval; +} + +void +print_rdata ( + struct pgm_header* header, + char* data + ) +{ + struct pgm_data* rdata = (struct pgm_data*)data; + char* tsdu = data + sizeof(struct pgm_data); + + puts (","); + printf ("\t\t\"rdSqn\": %lu,\n", (gulong)g_ntohl(rdata->data_sqn)); + printf ("\t\t\"rdTrail\": %lu,\n", (gulong)g_ntohl(rdata->data_trail)); + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT) + { + print_options (tsdu); + tsdu += g_ntohs( OPTIONS_TOTAL_LEN(tsdu) ); + puts (","); + } + +/* data */ + printf ("\t\t\"data\": \""); + char* end = tsdu + g_ntohs (header->pgm_tsdu_length); + while (tsdu < end) { + if (isprint(*tsdu)) + putchar(*tsdu); + else + putchar('.'); + tsdu++; + } + + puts ("\""); + puts ("\t}"); +} + +/* 8.3. NAK + * + * Technically the AFI of the source and multicast group can be different + * but that would be very wibbly wobbly. One example is using a local DLR + * with a IPv4 address to reduce NAK cost for recovery on wide IPv6 + * distribution. + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Requested Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Source NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | NLA AFI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Multicast Group NLA ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+ + * | Option Extensions when present ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... + */ + +int +verify_nak ( + struct pgm_header* header, + char* data, + guint len + ) +{ + return generic_verify_nak ("NAK", header, data, len); +} + +int +verify_ncf ( + struct pgm_header* header, + char* data, + guint len + ) +{ + return generic_verify_nak ("NCF", header, data, len); +} + +int +verify_nnak ( + struct pgm_header* header, + char* data, + guint len + ) +{ + return generic_verify_nak ("NNAK", header, data, len); +} + +static int +generic_verify_nak ( + const char* name, /* upper case */ + G_GNUC_UNUSED struct pgm_header* header, + char* data, + guint len + ) +{ + int retval = 0; + +/* truncated packet */ + if (len < sizeof(struct pgm_nak)) { + printf ("\t\"message\": \"%s: packet length: %i less than minimum %s length: %" G_GSIZE_FORMAT " bytes.\",\n", + name, len, name, sizeof(struct pgm_nak)); + retval = -1; + goto out; + } + + struct pgm_nak* nak = (struct pgm_nak*)data; + int nak_src_nla_afi = g_ntohs (nak->nak_src_nla_afi); + int nak_grp_nla_afi = -1; + +/* check source NLA: unicast address of the ODATA sender */ + switch (nak_src_nla_afi) { + case AFI_IP: + nak_grp_nla_afi = g_ntohs (nak->nak_grp_nla_afi); + break; + + case AFI_IP6: + nak_grp_nla_afi = g_ntohs (((struct pgm_nak6*)nak)->nak6_grp_nla_afi); + break; + + default: + printf ("\t\"message\": \"%s: invalid AFI of source NLA: %i.\",\n", + name, nak_src_nla_afi); + retval = -1; + goto out; + } + +/* check multicast group NLA */ + switch (nak_grp_nla_afi) { + case AFI_IP6: + switch (nak_src_nla_afi) { +/* IPv4 + IPv6 NLA */ + case AFI_IP: + if (len < ( sizeof(struct pgm_nak) + sizeof(struct in6_addr) - sizeof(struct in_addr) )) { + printf ("\t\"message\": \"%s: packet length: %i less than joint IPv4/6 %s length: %" G_GSIZE_FORMAT " bytes.\",\n", + name, len, name, ( sizeof(struct pgm_nak) + sizeof(struct in6_addr) - sizeof(struct in_addr) )); + retval = -1; + } + break; + +/* IPv6 + IPv6 NLA */ + case AFI_IP6: + if (len < sizeof(struct pgm_nak6)) { + printf ("\t\"message\": \"%s: packet length: %i less than IPv6 %s length: %" G_GSIZE_FORMAT " bytes.\",\n", + name, len, name, sizeof(struct pgm_nak6)); + retval = -1; + } + break; + } + break; + + case AFI_IP: + if (nak_src_nla_afi == AFI_IP6) { + if (len < ( sizeof(struct pgm_nak) + sizeof(struct in6_addr) - sizeof(struct in_addr) )) { + printf ("\t\"message\": \"%s: packet length: %i less than joint IPv6/4 %s length: %" G_GSIZE_FORMAT " bytes.\",\n", + name, len, name, ( sizeof(struct pgm_nak) + sizeof(struct in6_addr) - sizeof(struct in_addr) )); + retval = -1; + } + } + break; + + default: + printf ("\t\"message\": \"%s: invalid AFI of group NLA: %i.\",\n", + name, nak_grp_nla_afi); + retval = -1; + break; + } + +out: + return retval; +} + +void +print_nak ( + struct pgm_header* header, + char* data + ) +{ + generic_print_nak ("nak", header, data); +} + +void +print_ncf ( + struct pgm_header* header, + char* data + ) +{ + generic_print_nak ("ncf", header, data); +} + +void +print_nnak ( + struct pgm_header* header, + char* data + ) +{ + generic_print_nak ("nnak", header, data); +} + +static void +generic_print_nak ( + const char* name, /* lower case */ + struct pgm_header* header, + char* data + ) +{ + struct pgm_nak* nak = (struct pgm_nak*)data; + struct pgm_nak6* nak6 = (struct pgm_nak6*)data; + char* opt_offset = (char*)(nak + 1); + + puts (","); + printf ("\t\t\"%sSqn\": %lu,\n", name, (gulong)g_ntohl(nak->nak_sqn)); + + guint16 nak_src_nla_afi = g_ntohs (nak->nak_src_nla_afi); + guint16 nak_grp_nla_afi = 0; + char s[INET6_ADDRSTRLEN]; + + printf ("\t\t\"%sSourceNlaAfi\": %i,\n", name, nak_src_nla_afi); + +/* source nla */ + switch (nak_src_nla_afi) { + case AFI_IP: + inet_ntop ( AF_INET, &nak->nak_src_nla, s, sizeof(s) ); + nak_grp_nla_afi = g_ntohs (nak->nak_grp_nla_afi); + break; + + case AFI_IP6: + inet_ntop ( AF_INET6, &nak6->nak6_src_nla, s, sizeof(s) ); + nak_grp_nla_afi = g_ntohs (nak6->nak6_grp_nla_afi); + opt_offset += sizeof(struct in6_addr) - sizeof(struct in_addr); + break; + } + + printf ("\t\t\"%sSourceNla\": \"%s\",\n", name, s); + printf ("\t\t\"%sGroupNlaAfi\": %i,\n", name, nak_grp_nla_afi); + + switch (nak_grp_nla_afi) { + case AFI_IP6: + switch (nak_src_nla_afi) { +/* IPv4 + IPv6 NLA */ + case AFI_IP: + inet_ntop ( AF_INET6, &nak->nak_grp_nla, s, sizeof(s) ); + break; + +/* IPv6 + IPv6 NLA */ + case AFI_IP6: + inet_ntop ( AF_INET6, &nak6->nak6_grp_nla, s, sizeof(s) ); + break; + } + opt_offset += sizeof(struct in6_addr) - sizeof(struct in_addr); + break; + + case AFI_IP: + switch (nak_src_nla_afi) { +/* IPv4 + IPv4 NLA */ + case AFI_IP: + inet_ntop ( AF_INET, &nak->nak_grp_nla, s, sizeof(s) ); + break; + +/* IPv6 + IPv4 NLA */ + case AFI_IP6: + inet_ntop ( AF_INET, &nak6->nak6_grp_nla, s, sizeof(s) ); + break; + } + break; + } + + printf ("\t\t\"%sGroupNla\": \"%s\"", name, s); + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT) + { + puts (","); + print_options (opt_offset); + } + + puts ("\n\t}"); +} + + +/* 13.6. SPM Request + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Extensions when present ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... + */ + +int +verify_spmr ( + struct pgm_header* header, + char* data, + guint len + ) +{ + int retval = 0; + + char* opt_offset = data; + guint opt_len = len; + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT) + { + retval = verify_options (opt_offset, opt_len); + } + + return retval; +} + +void +print_spmr ( + struct pgm_header* header, + char* data + ) +{ + char* opt_offset = data; + +/* option extensions */ + if (header->pgm_options & PGM_OPT_PRESENT) + { + print_options (opt_offset); + puts (""); + } + + puts ("\t}"); +} + +/* Parse PGM options fields: + * + * assume at least two options, one the mandatory OPT_LENGTH + */ + +#define PGM_MIN_OPT_SIZE ( sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_length) ) + +int +verify_options ( + char* data, + guint len + ) +{ + int retval = 0; + + if (len < PGM_MIN_OPT_SIZE) { + printf ("\t\"message\": \"PGM options: packet size too small for options.\",\n"); + retval = -1; + goto out; + } + +/* OPT_LENGTH first */ + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)data; + if ((opt_len->opt_type & PGM_OPT_MASK) != PGM_OPT_LENGTH) { + printf ("\t\"message\": \"PGM options: first option not OPT_LENGTH.\",\n"); + retval = -1; + goto out; + } + + if (opt_len->opt_length != sizeof(struct pgm_opt_length)) { + printf ("\t\"message\": \"PGM options: OPT_LENGTH incorrect option length: %i, expecting %" G_GSIZE_FORMAT " bytes.\",\n", opt_len->opt_length, sizeof(struct pgm_opt_length)); + retval = -1; + goto out; + } + + if (g_ntohs(opt_len->opt_total_length) < PGM_MIN_OPT_SIZE) { + printf ("\t\"message\": \"PGM options: OPT_LENGTH total length too short: %i bytes.\",\n", g_ntohs(opt_len->opt_total_length)); + retval = -1; + goto out; + } + + if (g_ntohs(opt_len->opt_total_length) > len) { + printf ("\t\"message\": \"PGM options: OPT_LENGTH total length longer than packet allows: %i bytes.\",\n", g_ntohs(opt_len->opt_total_length)); + retval = -1; + goto out; + } + +/* iterate through options (max 16) */ + guint count = 0; + guint total_length = g_ntohs(opt_len->opt_total_length); + + guint opt_counters[256]; + memset (&opt_counters, 0, sizeof(opt_counters)); + + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)data; + for (;;) { + total_length -= opt_header->opt_length; + if (total_length < sizeof(struct pgm_opt_header)) { + printf ("\t\"message\": \"PGM options: option #%i shorter than minimum option size.\",\n", count + 1); + retval = -1; + goto out; + } + opt_header = (struct pgm_opt_header*)( ((char*)opt_header) + opt_header->opt_length ); + if (((int)total_length - (int)opt_header->opt_length) < 0) { + printf ("\t\"message\": \"PGM options: option #%i shorter than embedded size.\",\n", count + 1); + retval = -1; + goto out; + } + + if (opt_counters[opt_header->opt_type]++) { + printf ("\t\"message\": \"PGM options: duplicate option %i.\",\n", opt_header->opt_type); + retval = -1; + goto out; + } + +/* check option types */ + switch (opt_header->opt_type & PGM_OPT_MASK) { + case PGM_OPT_FRAGMENT: + { + if (opt_header->opt_length != sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_fragment)) { + printf ("\t\"message\": \"PGM options: OPT_FRAGMENT incorrect size: %i bytes.\",\n", opt_header->opt_length); + retval = -1; + goto out; + } + + struct pgm_opt_fragment* opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); + if (g_ntohl(opt_fragment->opt_frag_off) > g_ntohl(opt_fragment->opt_frag_len)) { + printf ("\t\"message\": \"PGM options: fragment offset longer than original packet.\",\n"); + retval = -1; + goto out; + } + break; + } + + case PGM_OPT_NAK_LIST: + { + guint list_len = opt_header->opt_length - sizeof(struct pgm_opt_header) - sizeof(guint8); + if (list_len & 1) { + printf ("\t\"message\": \"PGM options: OPT_NAK_LIST invalid odd length: %i bytes.\",\n", opt_header->opt_length); + retval = -1; + goto out; + } + + list_len /= 2; + if (list_len == 0) { + printf ("\t\"message\": \"PGM options: OPT_NAK_LIST empty.\",\n"); + retval = -1; + goto out; + } + + if (list_len > 62) { + printf ("\t\"message\": \"PGM options: OPT_NAK_LIST too long: %i sqns.\",\n", list_len); + retval = -1; + goto out; + } + break; + } + + case PGM_OPT_PARITY_PRM: + { + struct pgm_opt_parity_prm* opt_parity_prm = (struct pgm_opt_parity_prm*)(opt_header + 1); + if ((opt_parity_prm->opt_reserved & PGM_PARITY_PRM_MASK) == 0) { + printf ("\t\"message\": \"PGM options: neither pro-active or on-demand parity set in OPT_PARITY_PRM.\",\n"); + retval = -1; + goto out; + } + guint32 parity_prm_tgs = g_ntohl (opt_parity_prm->parity_prm_tgs); + if (parity_prm_tgs < 2 || parity_prm_tgs > 128) { + printf ("\t\"message\": \"PGM options: transmission group size out of bounds: %i.\",\n", parity_prm_tgs); + retval = -1; + goto out; + } + break; + } + + default: +/* unknown option, skip */ + break; + } +/* end option types */ + + if (opt_header->opt_type & PGM_OPT_END) { + break; + } + + if (count++ == 16) { + printf ("\t\"message\": \"PGM options: more than 16 options found.\",\n"); + retval = -1; + goto out; + } + } + +out: + return retval; +} + +static const char *opx_text[4] = { + "OPX_IGNORE", + "OPX_INVALIDATE", + "OPX_DISCARD", + "OPX_UNKNOWN" +}; + +void +print_options ( + char* data + ) +{ + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)data; + + puts ("\t\t\"pgmOptions\": ["); + puts ("\t\t\t{"); + printf ("\t\t\t\t\"length\": 0x%x,\n", opt_len->opt_length); + puts ("\t\t\t\t\"type\": \"OPT_LENGTH\","); + printf ("\t\t\t\t\"totalLength\": %i\n", g_ntohs (opt_len->opt_total_length)); + printf ("\t\t\t}"); + +/* iterate through options */ + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)data; + do { + opt_header = (struct pgm_opt_header*)( ((char*)opt_header) + opt_header->opt_length ); + + puts (","); + puts ("\t\t\t{"); + printf ("\t\t\t\t\"length\": 0x%x,\n", opt_header->opt_length); + + switch (opt_header->opt_type & PGM_OPT_MASK) { + case PGM_OPT_FRAGMENT: + { + struct pgm_opt_fragment* opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); + printf ("\t\t\t\t\"type\": \"OPT_FRAGMENT%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : ""); + printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false"); + printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]); + printf ("\t\t\t\t\"U-bit\": %s,\n", (opt_fragment->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false"); + printf ("\t\t\t\t\"firstSqn\": %i,\n", g_ntohl(opt_fragment->opt_sqn)); + printf ("\t\t\t\t\"fragmentOffset\": %i,\n", g_ntohl(opt_fragment->opt_frag_off)); + printf ("\t\t\t\t\"originalLength\": %i\n", g_ntohl(opt_fragment->opt_frag_len)); + break; + } + + case PGM_OPT_NAK_LIST: + { + struct pgm_opt_nak_list* opt_nak_list = (struct pgm_opt_nak_list*)(opt_header + 1); + char* end = (char*)opt_header + opt_header->opt_length; + printf ("\t\t\t\t\"type\": \"OPT_NAK_LIST%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : ""); + printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false"); + printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]); + printf ("\t\t\t\t\"U-bit\": %s,\n", (opt_nak_list->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false"); + char sqns[1024] = ""; + guint i = 0; + do { + char sqn[1024]; + sprintf (sqn, "%s%i", (i>0)?", ":"", g_ntohl(opt_nak_list->opt_sqn[i])); + strcat (sqns, sqn); + i++; + } while ((char*)&opt_nak_list->opt_sqn[i] < end); + printf ("\t\t\t\t\"sqn\": [%s]\n", sqns); + break; + } + + case PGM_OPT_PARITY_PRM: + { + struct pgm_opt_parity_prm* opt_parity_prm = (struct pgm_opt_parity_prm*)(opt_header + 1); + printf ("\t\t\t\t\"type\": \"OPT_PARITY_PRM%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : ""); + printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false"); + printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]); + printf ("\t\t\t\t\"U-bit\": %s,\n", (opt_parity_prm->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false"); + printf ("\t\t\t\t\"P-bit\": %s,\n", (opt_parity_prm->opt_reserved & PGM_PARITY_PRM_PRO) ? "true" : "false"); + printf ("\t\t\t\t\"O-bit\": %s,\n", (opt_parity_prm->opt_reserved & PGM_PARITY_PRM_OND) ? "true" : "false"); + printf ("\t\t\t\t\"transmissionGroupSize\": %i\n", g_ntohl(opt_parity_prm->parity_prm_tgs)); + break; + } + + case PGM_OPT_CURR_TGSIZE: + { + struct pgm_opt_curr_tgsize* opt_curr_tgsize = (struct pgm_opt_curr_tgsize*)(opt_header + 1); + printf ("\t\t\t\t\"type\": \"OPT_CURR_TGSIZE%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : ""); + printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false"); + printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]); + printf ("\t\t\t\t\"U-bit\": %s,\n", (opt_curr_tgsize->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false"); + printf ("\t\t\t\t\"actualTransmissionGroupSize\": %i\n", g_ntohl(opt_curr_tgsize->prm_atgsize)); + break; + } + + case PGM_OPT_SYN: + { + struct pgm_opt_syn* opt_syn = (struct pgm_opt_syn*)(opt_header + 1); + printf ("\t\t\t\t\"type\": \"OPT_SYN%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : ""); + printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false"); + printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]); + printf ("\t\t\t\t\"U-bit\": %s\n", (opt_syn->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false"); + break; + } + + case PGM_OPT_FIN: + { + struct pgm_opt_fin* opt_fin = (struct pgm_opt_fin*)(opt_header + 1); + printf ("\t\t\t\t\"type\": \"OPT_FIN%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : ""); + printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false"); + printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]); + printf ("\t\t\t\t\"U-bit\": %s\n", (opt_fin->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false"); + break; + } + + default: + { + guint8 opt_reserved = *(guint8*)(opt_header + 1); + printf ("\t\t\t\t\"type\": \"0x%x%s\",\n", opt_header->opt_type & PGM_OPT_MASK, (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : ""); + printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false"); + printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]); + printf ("\t\t\t\t\"U-bit\": %s\n", (opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false"); + break; + } + } + printf ("\t\t\t}"); + + } while (!(opt_header->opt_type & PGM_OPT_END)); + + printf ("\n\t\t]"); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/dump-json.h b/3rdparty/openpgm-svn-r1135/pgm/test/dump-json.h new file mode 100644 index 0000000..6fa0f9d --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/dump-json.h @@ -0,0 +1,33 @@ +/* vim:ts=8:sts=4:sw=4:noai:noexpandtab + * + * JSON packet dump. + * + * Copyright (c) 2006-2007 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGM_DUMP_JSON_H__ +#define __PGM_DUMP_JSON_H__ + + +G_BEGIN_DECLS + +int monitor_packet (char*, guint); + + +G_END_DECLS + +#endif /* __PGM_DUMP_JSON_H__ */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/heartbeat_spm.pl b/3rdparty/openpgm-svn-r1135/pgm/test/heartbeat_spm.pl new file mode 100755 index 0000000..beb912e --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/heartbeat_spm.pl @@ -0,0 +1,58 @@ +#!/usr/bin/perl +# heartbeat_spm.pl +# 5.1.5. Heartbeat SPMs + +use strict; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$app->connect; + +sub close_ssh { + $mon = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +print "app: publish test data.\n"; +$app->say ("send ao ringo"); + +print "mon: wait for odata ...\n"; +$mon->wait_for_odata; +my $t0 = [gettimeofday]; + +for (1..4) # look for four consecutive heartbeat SPMs less than 5000ms apart +{ + print "mon: wait for spm ...\n"; + $mon->wait_for_spm ({ 'timeout' => 5 }); + my $tn = [gettimeofday]; + my $elapsed = tv_interval ( $t0, $tn ); + $t0 = $tn; + + print "mon: spm received after $elapsed seconds.\n"; +} + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/monitor.c b/3rdparty/openpgm-svn-r1135/pgm/test/monitor.c new file mode 100644 index 0000000..6569a55 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/monitor.c @@ -0,0 +1,349 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM link monitor. + * + * Copyright (c) 2006-2007 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "dump-json.h" + + +/* globals */ + +static const char* g_network = "239.192.0.1"; +static struct in_addr g_filter /* = { 0 } */; + +static GIOChannel* g_io_channel = NULL; +static GIOChannel* g_stdin_channel = NULL; +static GMainLoop* g_loop = NULL; + + +static void on_signal (int, gpointer); +static gboolean on_startup (gpointer); +static gboolean on_mark (gpointer); + +static gboolean on_io_data (GIOChannel*, GIOCondition, gpointer); +static gboolean on_io_error (GIOChannel*, GIOCondition, gpointer); + +static gboolean on_stdin_data (GIOChannel*, GIOCondition, gpointer); + +int +main ( + G_GNUC_UNUSED int argc, + G_GNUC_UNUSED char *argv[] + ) +{ +/* pre-initialise PGM messages module to add hook for GLib logging */ + pgm_messages_init(); + log_init (); + puts ("monitor"); + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); + signal (SIGHUP, SIG_IGN); + pgm_signal_install (SIGINT, on_signal, g_loop); + pgm_signal_install (SIGTERM, on_signal, g_loop); + + g_filter.s_addr = 0; + +/* delayed startup */ + puts ("scheduling startup."); + g_timeout_add(0, (GSourceFunc)on_startup, NULL); + +/* dispatch loop */ + g_loop = g_main_loop_new(NULL, FALSE); + + puts ("entering main event loop ... "); + g_main_loop_run(g_loop); + + puts ("event loop terminated, cleaning up."); + +/* cleanup */ + g_main_loop_unref(g_loop); + g_loop = NULL; + + if (g_io_channel) { + puts ("closing socket."); + + GError *err = NULL; + g_io_channel_shutdown (g_io_channel, FALSE, &err); + g_io_channel = NULL; + } + + if (g_stdin_channel) { + puts ("unbinding stdin."); + g_io_channel_unref (g_stdin_channel); + g_stdin_channel = NULL; + } + + puts ("finished."); + pgm_messages_shutdown(); + return 0; +} + +static void +on_signal ( + int signum, + gpointer user_data + ) +{ + GMainLoop* loop = (GMainLoop*)user_data; + g_message ("on_signal (signum:%d user-data:%p)", signum, user_data); + g_main_loop_quit (loop); +} + +static gboolean +on_startup ( + G_GNUC_UNUSED gpointer data + ) +{ + int e; + + puts ("startup."); + +/* find PGM protocol id */ +// TODO: fix valgrind errors + int ipproto_pgm = IPPROTO_PGM; +#if HAVE_GETPROTOBYNAME_R + char b[1024]; + struct protoent protobuf, *proto; + e = getprotobyname_r("pgm", &protobuf, b, sizeof(b), &proto); + if (e != -1 && proto != NULL) { + if (proto->p_proto != ipproto_pgm) { + printf("Setting PGM protocol number to %i from /etc/protocols.\n"); + ipproto_pgm = proto->p_proto; + } + } +#else + struct protoent *proto = getprotobyname("pgm"); + if (proto != NULL) { + if (proto->p_proto != ipproto_pgm) { + printf("Setting PGM protocol number to %i from /etc/protocols.\n", proto +->p_proto); + ipproto_pgm = proto->p_proto; + } + } +#endif + +/* open socket for snooping */ + puts ("opening raw socket."); + int sock = socket(PF_INET, SOCK_RAW, ipproto_pgm); + if (sock < 0) { + int _e = errno; + perror("on_startup() failed"); + + if (_e == EPERM && 0 != getuid()) { + puts ("PGM protocol requires this program to run as superuser."); + } + g_main_loop_quit(g_loop); + return FALSE; + } + +/* drop out of setuid 0 */ + if (0 == getuid()) { + puts ("dropping superuser privileges."); + setuid((gid_t)65534); + setgid((uid_t)65534); + } + + char _t = 1; + e = setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &_t, sizeof(_t)); + if (e < 0) { + perror("on_startup() failed"); + close(sock); + g_main_loop_quit(g_loop); + return FALSE; + } + +/* buffers */ + int buffer_size = 0; + socklen_t len = 0; + e = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &buffer_size, &len); + if (e == 0) { + printf ("receive buffer set at %i bytes.\n", buffer_size); + } + e = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &buffer_size, &len); + if (e == 0) { + printf ("send buffer set at %i bytes.\n", buffer_size); + } + +/* bind */ + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + e = bind(sock, (struct sockaddr*)&addr, sizeof(addr)); + if (e < 0) { + perror("on_startup() failed"); + close(sock); + g_main_loop_quit(g_loop); + return FALSE; + } + +/* multicast */ + struct ip_mreq mreq; + memset(&mreq, 0, sizeof(mreq)); + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + printf ("listening on interface %s.\n", inet_ntoa(mreq.imr_interface)); + mreq.imr_multiaddr.s_addr = inet_addr(g_network); + printf ("subscription on multicast address %s.\n", inet_ntoa(mreq.imr_multiaddr)); + e = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + if (e < 0) { + perror("on_startup() failed"); + close(sock); + g_main_loop_quit(g_loop); + return FALSE; + } + +/* multicast loopback */ +/* multicast ttl */ + +/* add socket to event manager */ + g_io_channel = g_io_channel_unix_new (sock); + printf ("socket opened with encoding %s.\n", g_io_channel_get_encoding(g_io_channel)); + + /* guint event = */ g_io_add_watch (g_io_channel, G_IO_IN | G_IO_PRI, on_io_data, NULL); + /* guint event = */ g_io_add_watch (g_io_channel, G_IO_ERR | G_IO_HUP | G_IO_NVAL, on_io_error, NULL); + +/* add stdin to event manager */ + g_stdin_channel = g_io_channel_unix_new (fileno(stdin)); + printf ("binding stdin with encoding %s.\n", g_io_channel_get_encoding(g_stdin_channel)); + + g_io_add_watch (g_stdin_channel, G_IO_IN | G_IO_PRI, on_stdin_data, NULL); + +/* period timer to indicate some form of life */ +// TODO: Gnome 2.14: replace with g_timeout_add_seconds() + g_timeout_add(10 * 1000, (GSourceFunc)on_mark, NULL); + + puts ("READY"); + fflush (stdout); + return FALSE; +} + +static gboolean +on_mark ( + G_GNUC_UNUSED gpointer data + ) +{ + g_message ("-- MARK --"); + return TRUE; +} + +static gboolean +on_io_data ( + GIOChannel* source, + G_GNUC_UNUSED GIOCondition condition, + G_GNUC_UNUSED gpointer data + ) +{ + char buffer[4096]; + int fd = g_io_channel_unix_get_fd(source); + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + int len = recvfrom(fd, buffer, sizeof(buffer), MSG_DONTWAIT, (struct sockaddr*)&addr, &addr_len); + + if (g_filter.s_addr && g_filter.s_addr != addr.sin_addr.s_addr) { + return TRUE; + } + + printf ("%i bytes received from %s.\n", len, inet_ntoa(addr.sin_addr)); + + monitor_packet (buffer, len); + fflush (stdout); + + return TRUE; +} + +static gboolean +on_io_error ( + GIOChannel* source, + G_GNUC_UNUSED GIOCondition condition, + G_GNUC_UNUSED gpointer data + ) +{ + puts ("on_error."); + + GError *err; + g_io_channel_shutdown (source, FALSE, &err); + +/* remove event */ + return FALSE; +} + +/* process input commands from stdin/fd + */ + +static gboolean +on_stdin_data ( + GIOChannel* source, + G_GNUC_UNUSED GIOCondition condition, + G_GNUC_UNUSED gpointer data + ) +{ + gchar* str = NULL; + gsize len = 0; + gsize term = 0; + GError* err = NULL; + + g_io_channel_read_line (source, &str, &len, &term, &err); + if (len > 0) { + if (term) str[term] = 0; + + if (strcmp(str, "quit") == 0) { + g_main_loop_quit(g_loop); + } else if (strncmp(str, "filter ", strlen("filter ")) == 0) { + unsigned a, b, c, d; + int retval = sscanf(str, "filter %u.%u.%u.%u", &a, &b, &c, &d); + if (retval == 4) { + g_filter.s_addr = (d << 24) | (c << 16) | (b << 8) | a; + puts ("READY"); + } else { + printf ("invalid syntax for filter command."); + } + } else { + printf ("unknown command: %s\n", str); + } + } + + fflush (stdout); + g_free (str); + return TRUE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/nak.pl b/3rdparty/openpgm-svn-r1135/pgm/test/nak.pl new file mode 100755 index 0000000..bde24c0 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/nak.pl @@ -0,0 +1,68 @@ +#!/usr/bin/perl +# nak.pl +# 5.3. Repairs + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$sim->say ("create ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); +print "sim: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); # to process NAK requests + +print "app: publish test data.\n"; +$app->say ("send ao ringo"); +$app->say ("send ao ichigo"); +$app->say ("send ao momo"); + +my $odata = undef; +my $ocnt = 0; +for (1..3) { + print "mon: wait for odata ...\n"; + $odata = $mon->wait_for_odata; + $ocnt++; + print "mon: received $ocnt x odata.\n"; +} + +print "sim: send nak to app.\n"; +$sim->say ("net send nak ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 2"); + +print "mon: wait for rdata ...\n"; +$mon->wait_for_rdata; +print "mon: rdata received.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/nak_cancellation.pl b/3rdparty/openpgm-svn-r1135/pgm/test/nak_cancellation.pl new file mode 100755 index 0000000..9db12e9 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/nak_cancellation.pl @@ -0,0 +1,163 @@ +#!/usr/bin/perl +# nak_cancellation.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use IO::Handle; +use JSON; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +pipe(FROM_PARENT, TO_CHILD) or die "pipe: $!"; +FROM_PARENT->autoflush(1); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + close FROM_PARENT; close TO_CHILD; + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("set ao NAK_BO_IVL 5000"); # increase to count for test system latency +$app->say ("set ao NAK_RPT_IVL 10000"); +$app->say ("set ao NAK_RDATA_IVL 10000"); +$app->say ("set ao NAK_NCF_RETRIES 15"); +$app->say ("set ao NAK_DATA_RETRIES 10"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +## capture GSI of test spp +$app->say ("send ao nashi"); +print "mon: wait for odata ...\n"; +my $odata = $mon->wait_for_odata; +print "mon: odata received.\n"; + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; +$sim->say ("net send spm ao 3200 90001 90000"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90001 ringo"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,003.\n"; +$sim->say ("net send odata ao 90003 90001 ichigo"); +my $t0 = [gettimeofday]; + +if (my $pid = fork) { +# parent + close FROM_PARENT; + + sleep 10; + + print "app: wait for data ...\n"; + my $data = $app->wait_for_data({ 'timeout' => 0 }); + print "app: data received [$data].\n"; + + print TO_CHILD "die\n"; + + close TO_CHILD; + waitpid($pid,0); +} else { +# child + die "cannot fork: $!" unless defined $pid; + close TO_CHILD; + + print "sim: loop waiting for NAKs ...\n"; + + my $fh = $sim->{in}; + vec(my $rin, fileno(FROM_PARENT), 1) = 1; + vec($rin, fileno($fh), 1) = 1; + my $rout = undef; + + my $b = ''; + my $state = 0; + my $json = new JSON; + my $io = IO::Handle->new_from_fd( fileno($fh), "r" ); + my $cnt = 0; + while (select($rout = $rin, undef, undef, undef)) + { + last if( vec($rout, fileno(FROM_PARENT), 1) ); + while (defined($_ = $io->getline)) + { + chomp; + my $l = $_; + if ($state == 0) { + if ($l =~ /{$/) { + $state = 1; + } else { + print "sim [$l]\n"; + last; + } + } + + if ($state == 1) { + $b .= $l; + + if ($l =~ /^}$/) { + $state = 0; + + my $obj = $json->jsonToObj($b); + if ($obj->{PGM}->{type} =~ /NAK/) { + $cnt++; + my $elapsed = tv_interval ( $t0, [gettimeofday] ); + print "sim: $cnt x NAK received in $elapsed seconds.\n"; + $sim->say ("net send ncf ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 90002"); + } + +# reset + $b = ''; + last; + } + } + } + last if ($io->eof); + } + + print "sim: loop finished.\n"; + close FROM_PARENT; + exit; +} + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/nak_list.pl b/3rdparty/openpgm-svn-r1135/pgm/test/nak_list.pl new file mode 100755 index 0000000..015db45 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/nak_list.pl @@ -0,0 +1,72 @@ +#!/usr/bin/perl +# nak_list.pl +# 9.3. NAK List Option - OPT_NAK_LIST + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$sim->say ("create ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); +print "sim: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +print "app: publish test data.\n"; +$app->say ("send ao ringo"); +$app->say ("send ao ichigo"); +$app->say ("send ao momo"); + +my $odata = undef; +my $ocnt = 0; +for (1..3) { + print "mon: wait for odata ...\n"; + $odata = $mon->wait_for_odata; + $ocnt++; + print "mon: received $ocnt x odata.\n"; +} + +print "sim: send nak to app.\n"; +$sim->say ("net send nak ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 0,1,2"); + +my $rcnt = 0; +for (1..3) { + print "mon: wait for rdata ...\n"; + $mon->wait_for_rdata; + $rcnt++; + print "mon: received $rcnt x rdata.\n"; +} + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/nak_parity.pl b/3rdparty/openpgm-svn-r1135/pgm/test/nak_parity.pl new file mode 100755 index 0000000..50f961b --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/nak_parity.pl @@ -0,0 +1,75 @@ +#!/usr/bin/perl +# nak_parity.pl +# 5.3. Repairs + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$sim->say ("create ao"); +$sim->say ("set ao FEC RS(255,4)"); +$sim->say ("bind ao"); +print "sim: ready.\n"; + +$app->say ("create ao"); +$app->say ("set ao FEC RS(255,4)"); +$app->say ("bind ao"); +$app->say ("listen ao"); # to process NAK requests + +print "app: publish test data.\n"; +$app->say ("send ao ringo"); +$app->say ("send ao ichigo"); +$app->say ("send ao momo"); +$app->say ("send ao budo"); +$app->say ("send ao nashi"); +$app->say ("send ao anzu"); +$app->say ("send ao kaki"); + +my $odata = undef; +my $ocnt = 0; +for (1..7) { + print "mon: wait for odata ...\n"; + $odata = $mon->wait_for_odata; + $ocnt++; + print "mon: received $ocnt x odata.\n"; +} + +print "sim: send nak to app (transmission group = 0, packet count = 1).\n"; +$sim->say ("net send parity nak ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 1"); + +print "mon: wait for rdata ...\n"; +my $rdata = $mon->wait_for_rdata; +print "mon: rdata received.\n"; + +die "Selective RDATA received, parityPacket=false\n" unless $rdata->{PGM}->{options}->{parityPacket}; +print "Parity RDATA received.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/nak_repeat.pl b/3rdparty/openpgm-svn-r1135/pgm/test/nak_repeat.pl new file mode 100755 index 0000000..7f3d80e --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/nak_repeat.pl @@ -0,0 +1,71 @@ +#!/usr/bin/perl +# nak_repeat.pl +# 5.3. Repairs + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$sim->say ("create ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); +print "sim: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); # to process NAK requests + +print "app: publish test data.\n"; +$app->say ("send ao " . ("ringo" x 200)); +$app->say ("send ao " . ("ichigo" x 200)); +$app->say ("send ao " . ("momo" x 200)); + +my $odata = undef; +my $ocnt = 0; +for (1..3) { + print "mon: wait for odata ...\n"; + $odata = $mon->wait_for_odata; + $ocnt++; + print "mon: received $ocnt x odata.\n"; +} + +for (1..1000) { + my $i = $_; + print "sim: $i# send nak to app.\n"; + $sim->say ("net send nak ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 2"); + + print "mon: $i# wait for rdata ...\n"; + $mon->wait_for_rdata; + print "mon: $i# rdata received.\n"; +} + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/ncf.pl b/3rdparty/openpgm-svn-r1135/pgm/test/ncf.pl new file mode 100755 index 0000000..128db2a --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/ncf.pl @@ -0,0 +1,68 @@ +#!/usr/bin/perl +# ncf.pl +# 5.2. Negative Acknowledgment Confirmation + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$sim->say ("create ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); +print "sim: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +print "app: publish test data.\n"; +$app->say ("send ao ringo"); +$app->say ("send ao ichigo"); +$app->say ("send ao momo"); + +my $odata = undef; +my $ocnt = 0; +for (1..3) { + print "mon: wait for odata ...\n"; + $odata = $mon->wait_for_odata; + $ocnt++; + print "mon: received $ocnt x odata.\n"; +} + +print "sim: send nak to app.\n"; +$sim->say ("net send nak ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 2"); + +print "mon: wait for ncf ...\n"; +$mon->wait_for_ncf; +print "mon: ncf received.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/ncf_cancellation.pl b/3rdparty/openpgm-svn-r1135/pgm/test/ncf_cancellation.pl new file mode 100755 index 0000000..fcfbf2b --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/ncf_cancellation.pl @@ -0,0 +1,149 @@ +#!/usr/bin/perl +# ncf_cancellation.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use IO::Handle; +use JSON; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +pipe(FROM_PARENT, TO_CHILD) or die "pipe: $!"; +FROM_PARENT->autoflush(1); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + close FROM_PARENT; close TO_CHILD; + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +## capture GSI of test spp +$app->say ("send ao nashi"); +print "mon: wait for odata ...\n"; +my $odata = $mon->wait_for_odata; +print "mon: odata received.\n"; + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; +$sim->say ("net send spm ao 3200 90001 90000"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90001 ringo"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,003.\n"; +$sim->say ("net send odata ao 90003 90001 ichigo"); +my $t0 = [gettimeofday]; + +if (my $pid = fork) { +# parent + close FROM_PARENT; + + print "app: wait for data ...\n"; + my $data = $app->wait_for_data({ 'timeout' => 0 }); + print "app: data received [$data].\n"; + + print TO_CHILD "die\n"; + + close TO_CHILD; + waitpid($pid,0); +} else { +# child + die "cannot fork: $!" unless defined $pid; + close TO_CHILD; + print "sim: loop waiting for NAKs ...\n"; + + my $fh = $sim->{in}; + vec(my $rin, fileno(FROM_PARENT), 1) = 1; + vec($rin, fileno($fh), 1) = 1; + my $rout = undef; + + my $b = ''; + my $state = 0; + my $json = new JSON; + my $io = IO::Handle->new_from_fd( fileno($fh), "r" ); + my $cnt = 0; + while (select($rout = $rin, undef, undef, undef)) + { + last if( vec($rout, fileno(FROM_PARENT), 1) ); + last unless (defined($_ = $io->getline)); + chomp; + my $l = $_; + if ($state == 0) { + if ($l =~ /{$/) { + $state = 1; + } else { + print "sim [$l]\n"; + } + } + + if ($state == 1) { + $b .= $l; + + if ($l =~ /^}$/) { + $state = 0; + + my $obj = $json->jsonToObj($b); + if ($obj->{PGM}->{type} =~ /NAK/) { + $cnt++; + my $elapsed = tv_interval ( $t0, [gettimeofday] ); + print "sim: $cnt x NAK received in $elapsed seconds.\n"; + } + +# reset + $b = ''; + } + } + } + + print "sim: loop finished.\n"; + close FROM_PARENT; + exit; +} + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/ncf_list.pl b/3rdparty/openpgm-svn-r1135/pgm/test/ncf_list.pl new file mode 100755 index 0000000..d3082e4 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/ncf_list.pl @@ -0,0 +1,74 @@ +#!/usr/bin/perl +# ncf_list.pl +# 9.3. NAK List Option - OPT_NAK_LIST + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$sim->say ("create ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); +print "sim: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +print "app: publish test data.\n"; +$app->say ("send ao ringo"); +$app->say ("send ao ichigo"); +$app->say ("send ao momo"); + +my $odata = undef; +my $ocnt = 0; +for (1..3) { + print "mon: wait for odata ...\n"; + $odata = $mon->wait_for_odata; + $ocnt++; + print "mon: received $ocnt x odata.\n"; +} + +print "sim: send nak to app.\n"; +$sim->say ("net send nak ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 1,2,3"); + +print "mon: wait for ncf ...\n"; +my $ncf = $mon->wait_for_ncf; +print "mon: ncf received.\n"; +die "ncfSqn != 1\n" unless $ncf->{PGM}->{ncfSqn} == 1; +die "NCF list incorrect\n" unless ( + $ncf->{PGM}->{pgmOptions}[1]->{sqn}[0] == 2 + && $ncf->{PGM}->{pgmOptions}[1]->{sqn}[1] == 3 + ); +print "mon: ncf list correct: $ncf->{PGM}->{ncfSqn} + [$ncf->{PGM}->{pgmOptions}[1]->{sqn}[0], $ncf->{PGM}->{pgmOptions}[1]->{sqn}[1]]\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/ncf_suppression.pl b/3rdparty/openpgm-svn-r1135/pgm/test/ncf_suppression.pl new file mode 100755 index 0000000..fbb3de7 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/ncf_suppression.pl @@ -0,0 +1,103 @@ +#!/usr/bin/perl +# ncf_suppression.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +## capture GSI of test spp +$app->say ("send ao nashi"); +print "mon: wait for odata ...\n"; +my $odata = $mon->wait_for_odata; +print "mon: odata received.\n"; + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; +$sim->say ("net send spm ao 3200 90001 90000"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90001 ringo"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +## first run through with regular NAK generation to get regular backoff interval +print "sim: publish ODATA sqn 90,003.\n"; +$sim->say ("net send odata ao 90003 90001 ichigo"); +my $t0 = [gettimeofday]; +print "sim: waiting for valid NAK.\n"; +$sim->wait_for_nak; +my $normal_backoff = tv_interval ( $t0, [gettimeofday] ); +print "sim: NAK received in $normal_backoff seconds.\n"; + +## cleanup by publishing repair data +print "sim: publish RDATA sqn 90,002.\n"; +$sim->say ("net send odata ao 90002 90001 momo"); +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +## second run with NAK suppression +$t0 = [gettimeofday]; +print "sim: publish ODATA sqn 90,005.\n"; +$sim->say ("net send odata ao 90005 90001 anzu"); +print "sim: publish NCF sqn 90,004.\n"; +$sim->say ("net send ncf ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 90004"); + +print "sim: waiting for valid NAK.\n"; +$sim->wait_for_nak; +my $suppressed_backoff = tv_interval ( $t0, [gettimeofday] ); +print "sim: NAK received in $suppressed_backoff seconds.\n"; + +die "NAK suppression failed.\n" unless ($suppressed_backoff > $normal_backoff); + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/odata.pl b/3rdparty/openpgm-svn-r1135/pgm/test/odata.pl new file mode 100755 index 0000000..8dd3580 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/odata.pl @@ -0,0 +1,45 @@ +#!/usr/bin/perl +# odata.pl +# 3.6.2.1. ODATA - Original Data + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$app->connect; + +sub close_ssh { + $mon = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); + +print "app: publish test data.\n"; +$app->say ("send ao ringo"); + +print "mon: wait for odata ...\n"; +$mon->wait_for_odata; +print "mon: received odata.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/odata_completion.pl b/3rdparty/openpgm-svn-r1135/pgm/test/odata_completion.pl new file mode 100755 index 0000000..655aeff --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/odata_completion.pl @@ -0,0 +1,89 @@ +#!/usr/bin/perl +# odata_completion.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +## capture GSI of test spp +$app->say ("send ao nashi"); +print "mon: wait for odata ...\n"; +my $odata = $mon->wait_for_odata; +print "mon: odata received.\n"; + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; +$sim->say ("net send spm ao 3200 90001 90000"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90001 ringo"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,003.\n"; +$sim->say ("net send odata ao 90003 90001 ichigo"); +print "sim: waiting for valid NAK.\n"; +$sim->wait_for_nak; +print "sim: NAK received.\n"; + +print "sim: publish ODATA sqn 90,002.\n"; +$sim->say ("net send odata ao 90002 90001 momo"); + +for (1..2) +{ + print "app: wait for data ...\n"; + my $data = $app->wait_for_data; + print "app: data received [$data].\n"; +} + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/odata_jump.pl b/3rdparty/openpgm-svn-r1135/pgm/test/odata_jump.pl new file mode 100755 index 0000000..748ff23 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/odata_jump.pl @@ -0,0 +1,73 @@ +#!/usr/bin/perl +# odata_jump.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; +$sim->say ("net send spm ao 3200 90001 90000"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90001 ringo"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,003.\n"; +$sim->say ("net send odata ao 90003 90001 ichigo"); + +print "sim: waiting for valid NAK.\n"; +$sim->wait_for_nak; +print "sim: NAK received.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/odata_jump_parity.pl b/3rdparty/openpgm-svn-r1135/pgm/test/odata_jump_parity.pl new file mode 100755 index 0000000..99179cc --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/odata_jump_parity.pl @@ -0,0 +1,83 @@ +#!/usr/bin/perl +# odata_jump_parity.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); + +print "sim: publish SPM txw_trail 32,769 txw_lead 32,768 at spm_sqn 3200, advertise on-demand parity, k=4.\n"; +$sim->say ("net send spm ao 3200 32769 32768 on-demand 4"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 32,769.\n"; +$sim->say ("net send odata ao 32769 32769 ringo"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +# force window into next transmission group +print "sim: publish ODATA sqn 32,771.\n"; +$sim->say ("net send odata ao 32771 32769 ichigo"); +print "sim: publish ODATA sqn 32,772.\n"; +$sim->say ("net send odata ao 32772 32769 momo"); +print "sim: publish ODATA sqn 32,773.\n"; +$sim->say ("net send odata ao 32773 32769 yakitori"); +print "sim: publish ODATA sqn 32,774.\n"; +$sim->say ("net send odata ao 32774 32769 sasami"); +print "sim: publish ODATA sqn 32,775.\n"; +$sim->say ("net send odata ao 32775 32769 tebasaki"); + +print "sim: waiting for valid NAK.\n"; +my $nak = $sim->wait_for_nak; +print "sim: NAK received.\n"; + +die "Selective NAK received, parityPacket=false\n" unless $nak->{PGM}->{options}->{parityPacket}; +print "Parity NAK received.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/odata_number.pl b/3rdparty/openpgm-svn-r1135/pgm/test/odata_number.pl new file mode 100755 index 0000000..e48a7e1 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/odata_number.pl @@ -0,0 +1,60 @@ +#!/usr/bin/perl +# odata_number.pl +# 5.1.1. Maximum Cumulative Transmit Rate + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$app->connect; + +sub close_ssh { + $mon = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +print "app: send 1000 data packets ...\n"; +# hide stdout +open(OLDOUT, ">&STDOUT"); +open(STDOUT, ">/dev/null") or die "Can't redirect stdout: $!"; + +for (0..999) +{ + my $i = $_; + $app->say ("send ao $i"); + my $odata = $mon->wait_for_odata; + + die "out of sequence ODATA, received $odata->{PGM}->{odSqn} expected $i\n" unless $odata->{PGM}->{odSqn} == $i; +} + +# restore stdout +close(STDOUT) or die "Can't close STDOUT: $!"; +open(STDOUT, ">&OLDOUT") or die "Can't restore stdout: $!"; +close(OLDOUT) or die "Can't close OLDOUT: $!"; + +print "mon: received 1000 x odata.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/odata_rate.pl b/3rdparty/openpgm-svn-r1135/pgm/test/odata_rate.pl new file mode 100755 index 0000000..0db4d80 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/odata_rate.pl @@ -0,0 +1,69 @@ +#!/usr/bin/perl +# odata_number.pl +# 5.1.1. Maximum Cumulative Transmit Rate + +use strict; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$app->connect; + +sub close_ssh { + $mon = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("set ao TXW_MAX_RTE 1500"); +$app->say ("bind ao"); +$app->say ("connect ao"); + +print "app: send 50 data packets ...\n"; +my $t0 = [gettimeofday]; + +# hide stdout +open(OLDOUT, ">&STDOUT"); +open(STDOUT, ">/dev/null") or die "Can't redirect stdout: $!"; + +my $payload = "ringo" x 100; +my $bytes = 0; +for (1..50) +{ + $app->say ("send ao $payload"); + my $odata = $mon->wait_for_odata; + $bytes += $odata->{IP}->{length}; +} + +close(STDOUT) or die "Can't close STDOUT: $!"; +open(STDOUT, ">&OLDOUT") or die "Can't restore stdout: $!"; +close(OLDOUT) or die "Can't close OLDOUT: $!"; + +my $elapsed = tv_interval ( $t0, [gettimeofday] ); +print "mon: received 50 x odata, $bytes bytes in $elapsed seconds.\n"; + +my $rate = $bytes / $elapsed; +$rate = $bytes if ($rate > $bytes); +print "mon: incoming data rate $rate bps.\n"; + +die "incoming rate exceeds set TXW_MAX_RTE\n" unless $rate < 1650; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/odata_reception.pl b/3rdparty/openpgm-svn-r1135/pgm/test/odata_reception.pl new file mode 100755 index 0000000..4c47dc0 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/odata_reception.pl @@ -0,0 +1,61 @@ +#!/usr/bin/perl +# odata_reception.pl +# 6.1. Data Reception + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); + +print "sim: publish ODATA sqn 90,000.\n"; +$sim->say ("net send odata ao 90000 90000 ringo"); +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: received data [$data].\n"; + +# no NAKs should be generated. +# TODO: test for silence in {mon} + +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90000 ichigo"); +print "app: wait for data ...\n"; +$data = $app->wait_for_data; +print "app: received data [$data].\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/on-demand_spm.pl b/3rdparty/openpgm-svn-r1135/pgm/test/on-demand_spm.pl new file mode 100755 index 0000000..eb86cf9 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/on-demand_spm.pl @@ -0,0 +1,49 @@ +#!/usr/bin/perl +# on-demand_spm.pl +# 5.1.4. Ambient SPMs with on-demand parity flag + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$app->connect; + +sub close_ssh { + $mon = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("set ao FEC RS(255,64)"); +$app->say ("bind ao"); +print "app: ready.\n"; + +print "mon: wait for spm ...\n"; +my $spm = $mon->wait_for_spm; +print "mon: received spm.\n"; + +die "SPM does not contain any PGM options\n" unless $spm->{PGM}->{pgmOptions}; +die "SPM does not contain a PGM_OPT_PARITY_PRM option\n" unless $spm->{PGM}->{pgmOptions}[1]->{type} =~ /OPT_PARITY_PRM/; +print "pro-active parity " . ($spm->{PGM}->{pgmOptions}[1]->{'P-bit'} ? 'enabled' : 'disabled') . ", P-bit " . ($spm->{PGM}->{pgmOptions}[1]->{'P-bit'} ? 'true' : 'false') . "\n"; +die "on-demand parity disabled, O-bit false\n" unless $spm->{PGM}->{pgmOptions}[1]->{'O-bit'}; +print "on-demand parity enabled, O-bit true\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/outofwindow_ncf.pl b/3rdparty/openpgm-svn-r1135/pgm/test/outofwindow_ncf.pl new file mode 100755 index 0000000..3e1eb88 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/outofwindow_ncf.pl @@ -0,0 +1,105 @@ +#!/usr/bin/perl +# outofwindow_ncf.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +## capture GSI of test spp +$app->say ("send ao nashi"); +print "mon: wait for odata ...\n"; +my $odata = $mon->wait_for_odata; +print "mon: odata received.\n"; + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; +$sim->say ("net send spm ao 3200 90001 90000"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90001 ringo"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +## first run through with regular NAK generation to get regular backoff interval +print "sim: publish ODATA sqn 90,003.\n"; +$sim->say ("net send odata ao 90003 90001 ichigo"); +my $t0 = [gettimeofday]; +print "sim: waiting for valid NAK.\n"; +$sim->wait_for_nak; +my $normal_backoff = tv_interval ( $t0, [gettimeofday] ); +print "sim: NAK received in $normal_backoff seconds.\n"; + +## cleanup by publishing repair data +print "sim: publish RDATA sqn 90,002.\n"; +$sim->say ("net send odata ao 90002 90001 momo"); +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +## second run with NAK suppression +$t0 = [gettimeofday]; +print "sim: publish ODATA sqn 90,005.\n"; +$sim->say ("net send odata ao 90005 90001 anzu"); +print "sim: publish NCF sqn 90,004.\n"; +$sim->say ("net send ncf ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort} 90002"); + +print "sim: waiting for valid NAK.\n"; +$sim->wait_for_nak; +my $outofwindow_backoff = tv_interval ( $t0, [gettimeofday] ); +print "sim: NAK received in $outofwindow_backoff seconds.\n"; + +# allow 100ms tolerance +my $fabs = abs( ($outofwindow_backoff - $normal_backoff) * 1000 ); +die "Out-of-window NCF altered back-off interval by $fabs ms.\n" unless ($fabs < 100); + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/rdata_completion.pl b/3rdparty/openpgm-svn-r1135/pgm/test/rdata_completion.pl new file mode 100755 index 0000000..3b290ee --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/rdata_completion.pl @@ -0,0 +1,89 @@ +#!/usr/bin/perl +# rdata_completion.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +## capture GSI of test spp +$app->say ("send ao nashi"); +print "mon: wait for odata ...\n"; +my $odata = $mon->wait_for_odata; +print "mon: odata received.\n"; + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; +$sim->say ("net send spm ao 3200 90001 90000"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90001 ringo"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,003.\n"; +$sim->say ("net send odata ao 90003 90001 ichigo"); +print "sim: waiting for valid NAK.\n"; +$sim->wait_for_nak; +print "sim: NAK received.\n"; + +print "sim: publish RDATA sqn 90,002.\n"; +$sim->say ("net send rdata ao 90002 90001 momo"); + +for (1..2) +{ + print "app: wait for data ...\n"; + my $data = $app->wait_for_data; + print "app: data received [$data].\n"; +} + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/rdata_completion_parity.pl b/3rdparty/openpgm-svn-r1135/pgm/test/rdata_completion_parity.pl new file mode 100755 index 0000000..95a6f7b --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/rdata_completion_parity.pl @@ -0,0 +1,97 @@ +#!/usr/bin/perl +# rdata_completion_parity.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("set ao FEC RS(255,4)"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +## capture GSI of test spp +$app->say ("send ao nashi"); +print "mon: wait for odata ...\n"; +my $odata = $mon->wait_for_odata; +print "mon: odata received.\n"; + +$sim->say ("create fake ao"); +$sim->say ("set ao FEC RS(255,4)"); +$sim->say ("bind ao"); + +print "sim: publish SPM txw_trail 32,769 txw_lead 32,768 at spm_sqn 3200, advertise on-demand parity, k=4.\n"; +$sim->say ("net send spm ao 3200 32768 32767 on-demand 4"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 32,768.\n"; +$sim->say ("net send odata ao 32768 32768 ringo000"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 32,770.\n"; +$sim->say ("net send odata ao 32770 32768 momo0000"); +print "sim: publish ODATA sqn 32,771.\n"; +$sim->say ("net send odata ao 32771 32768 yakitori"); +print "sim: publish ODATA sqn 32,772.\n"; +$sim->say ("net send odata ao 32772 32768 sasami00"); +print "sim: publish ODATA sqn 32,773.\n"; +$sim->say ("net send odata ao 32773 32768 tebasaki"); + +print "sim: waiting for valid parity NAK.\n"; +my $nak = $sim->wait_for_nak; +die "Selective NAK received, parityPacket=false\n" unless $nak->{PGM}->{options}->{parityPacket}; +print "sim: Parity NAK received.\n"; + +print "sim: publish parity RDATA, tg_sqn 32,768, pkt_cnt 1 (sqn 32,768).\n"; +$sim->say ("net send parity rdata ao 32768 32768 ringo000 ichigo00 momo0000 yakitori"); + +for (1..5) +{ + print "app: wait for data ...\n"; + my $data = $app->wait_for_data; + print "app: data received [$data].\n"; +} + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/rdata_completion_parity_var_pktlen.pl b/3rdparty/openpgm-svn-r1135/pgm/test/rdata_completion_parity_var_pktlen.pl new file mode 100755 index 0000000..9011ea1 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/rdata_completion_parity_var_pktlen.pl @@ -0,0 +1,97 @@ +#!/usr/bin/perl +# rdata_completion_parity_var_pktlen.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("set ao FEC RS(255,4)"); +$app->say ("bind ao"); +$app->say ("listen ao"); + +## capture GSI of test spp +$app->say ("send ao nashi"); +print "mon: wait for odata ...\n"; +my $odata = $mon->wait_for_odata; +print "mon: odata received.\n"; + +$sim->say ("create fake ao"); +$sim->say ("set ao FEC RS(255,4)"); +$sim->say ("bind ao"); + +print "sim: publish SPM txw_trail 32,769 txw_lead 32,768 at spm_sqn 3200, advertise on-demand parity, k=4.\n"; +$sim->say ("net send spm ao 3200 32768 32767 on-demand 4"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 32,768.\n"; +$sim->say ("net send odata ao 32768 32768 ringo"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak ({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 32,770.\n"; +$sim->say ("net send odata ao 32770 32768 momo"); +print "sim: publish ODATA sqn 32,771.\n"; +$sim->say ("net send odata ao 32771 32768 yakitori"); +print "sim: publish ODATA sqn 32,772.\n"; +$sim->say ("net send odata ao 32772 32768 sasami"); +print "sim: publish ODATA sqn 32,773.\n"; +$sim->say ("net send odata ao 32773 32768 tebasaki"); + +print "sim: waiting for valid parity NAK.\n"; +my $nak = $sim->wait_for_nak; +die "Selective NAK received, parityPacket=false\n" unless $nak->{PGM}->{options}->{parityPacket}; +print "sim: Parity NAK received.\n"; + +print "sim: publish parity RDATA, tg_sqn 32,768, pkt_cnt 1 (sqn 32,768).\n"; +$sim->say ("net send parity rdata ao 32768 32768 ringo ichigo momo yakitori"); + +for (1..5) +{ + print "app: wait for data ...\n"; + my $data = $app->wait_for_data; + print "app: data received [$data].\n"; +} + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/rdata_jump.pl b/3rdparty/openpgm-svn-r1135/pgm/test/rdata_jump.pl new file mode 100755 index 0000000..dbb3e02 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/rdata_jump.pl @@ -0,0 +1,73 @@ +#!/usr/bin/perl +# rdata_jump.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; +$sim->say ("net send spm ao 3200 90001 90000"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90001 ringo"); + +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: data received [$data].\n"; + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish RDATA sqn 90,003.\n"; +$sim->say ("net send rdata ao 90003 90001 ichigo"); + +print "sim: waiting for valid NAK.\n"; +$sim->wait_for_nak; +print "sim: NAK received.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/rdata_reception.pl b/3rdparty/openpgm-svn-r1135/pgm/test/rdata_reception.pl new file mode 100755 index 0000000..3592cc7 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/rdata_reception.pl @@ -0,0 +1,61 @@ +#!/usr/bin/perl +# rdata_reception.pl +# 6.1. Data Reception + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); + +print "sim: publish RDATA sqn 90,000.\n"; +$sim->say ("net send rdata ao 90000 90000 ringo"); +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: received data [$data].\n"; + +# no NAKs should be generated. +# TODO: test for silence in {mon} + +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90000 ichigo"); +print "app: wait for data ...\n"; +$data = $app->wait_for_data; +print "app: received data [$data].\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/sim.c b/3rdparty/openpgm-svn-r1135/pgm/test/sim.c new file mode 100644 index 0000000..d600fae --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/sim.c @@ -0,0 +1,2301 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM conformance endpoint simulator. + * + * Copyright (c) 2006-2008 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dump-json.h" +#include "async.h" + + +/* typedefs */ + +struct idle_source { + GSource source; + guint64 expiration; +}; + +struct sim_session { + char* name; + pgm_sock_t* sock; + gboolean is_transport_fake; + GIOChannel* recv_channel; + pgm_async_t* async; +}; + +/* globals */ +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "sim" + +#ifndef SOL_IP +# define SOL_IP IPPROTO_IP +#endif +#ifndef SOL_IPV6 +# define SOL_IPV6 IPPROTO_IPV6 +#endif + + +static int g_port = 7500; +static const char* g_network = ";239.192.0.1"; + +static int g_max_tpdu = 1500; +static int g_sqns = 100 * 1000; + +static GList* g_sessions_list = NULL; +static GHashTable* g_sessions = NULL; +static GMainLoop* g_loop = NULL; +static GIOChannel* g_stdin_channel = NULL; + + +static void on_signal (int, gpointer); +static gboolean on_startup (gpointer); +static gboolean on_mark (gpointer); +static void destroy_session (struct sim_session*); +static int on_data (gpointer, guint, gpointer); +static gboolean on_stdin_data (GIOChannel*, GIOCondition, gpointer); +void generic_net_send_nak (guint8, char*, pgm_tsi_t*, struct pgm_sqn_list_t*); + + +G_GNUC_NORETURN static +void +usage (const char* bin) +{ + fprintf (stderr, "Usage: %s [options]\n", bin); + fprintf (stderr, " -n : Multicast group or unicast IP address\n"); + fprintf (stderr, " -s : IP port\n"); + exit (1); +} + +int +main ( + int argc, + char *argv[] + ) +{ + pgm_error_t* pgm_err = NULL; + +/* pre-initialise PGM messages module to add hook for GLib logging */ + pgm_messages_init(); + log_init (); + g_message ("sim"); + + if (!pgm_init (&pgm_err)) { + g_error ("Unable to start PGM engine: %s", (pgm_err && pgm_err->message) ? pgm_err->message : "(null)"); + pgm_error_free (pgm_err); + pgm_messages_shutdown(); + return EXIT_FAILURE; + } + +/* parse program arguments */ + const char* binary_name = strrchr (argv[0], '/'); + int c; + while ((c = getopt (argc, argv, "s:n:h")) != -1) + { + switch (c) { + case 'n': g_network = optarg; break; + case 's': g_port = atoi (optarg); break; + + case 'h': + case '?': + pgm_messages_shutdown(); + usage (binary_name); + } + } + + g_loop = g_main_loop_new (NULL, FALSE); + +/* setup signal handlers */ + signal (SIGSEGV, on_sigsegv); + signal (SIGHUP, SIG_IGN); + pgm_signal_install (SIGINT, on_signal, g_loop); + pgm_signal_install (SIGTERM, on_signal, g_loop); + +/* delayed startup */ + g_message ("scheduling startup."); + g_timeout_add (0, (GSourceFunc)on_startup, NULL); + +/* dispatch loop */ + g_message ("entering main event loop ... "); + g_main_loop_run (g_loop); + + g_message ("event loop terminated, cleaning up."); + +/* cleanup */ + g_main_loop_unref(g_loop); + g_loop = NULL; + + if (g_sessions) { + g_message ("destroying sessions."); + while (g_sessions_list) { + destroy_session (g_sessions_list->data); + g_sessions_list = g_list_delete_link (g_sessions_list, g_sessions_list); + } + g_hash_table_unref (g_sessions); + g_sessions = NULL; + } + + if (g_stdin_channel) { + puts ("unbinding stdin."); + g_io_channel_unref (g_stdin_channel); + g_stdin_channel = NULL; + } + + g_message ("PGM engine shutdown."); + pgm_shutdown(); + g_message ("finished."); + pgm_messages_shutdown(); + return EXIT_SUCCESS; +} + +static +void +destroy_session ( + struct sim_session* sess + ) +{ + printf ("destroying socket \"%s\"\n", sess->name); + pgm_close (sess->sock, TRUE); + sess->sock = NULL; + g_free (sess->name); + sess->name = NULL; + g_free (sess); +} + +static +void +on_signal ( + int signum, + gpointer user_data + ) +{ + GMainLoop* loop = (GMainLoop*)user_data; + g_message ("on_signal (signum:%d user-data:%p)", signum, user_data); + g_main_loop_quit (loop); +} + +static +gboolean +on_startup ( + G_GNUC_UNUSED gpointer data + ) +{ + g_message ("startup."); + + g_sessions = g_hash_table_new (g_str_hash, g_str_equal); + +/* add stdin to event manager */ + g_stdin_channel = g_io_channel_unix_new (fileno(stdin)); + printf ("binding stdin with encoding %s.\n", g_io_channel_get_encoding(g_stdin_channel)); + + g_io_add_watch (g_stdin_channel, G_IO_IN | G_IO_PRI, on_stdin_data, NULL); + +/* period timer to indicate some form of life */ +// TODO: Gnome 2.14: replace with g_timeout_add_seconds() + g_timeout_add(10 * 1000, (GSourceFunc)on_mark, NULL); + + puts ("READY"); + fflush (stdout); + return FALSE; +} + +static +bool +fake_pgm_socket ( + pgm_sock_t**restrict sock, + const sa_family_t family, + const int pgm_sock_type, + const int protocol, + G_GNUC_UNUSED pgm_error_t**restrict error + ) +{ + pgm_sock_t* new_sock; + + g_return_val_if_fail (NULL != sock, FALSE); + g_return_val_if_fail (AF_INET == family || AF_INET6 == family, FALSE); + g_return_val_if_fail (SOCK_SEQPACKET == pgm_sock_type, FALSE); + g_return_val_if_fail (IPPROTO_UDP == protocol || IPPROTO_PGM == protocol, FALSE); + + new_sock = pgm_new0 (pgm_sock_t, 1); + new_sock->family = family; + new_sock->socket_type = pgm_sock_type; + new_sock->protocol = protocol; + new_sock->can_send_data = TRUE; + new_sock->can_send_nak = TRUE; + new_sock->can_recv_data = TRUE; + new_sock->dport = DEFAULT_DATA_DESTINATION_PORT; + new_sock->tsi.sport = DEFAULT_DATA_SOURCE_PORT; + new_sock->adv_mode = 0; /* advance with time */ + +/* PGMCC */ + new_sock->acker_nla.ss_family = family; + +/* open sockets to implement PGM */ + int socket_type; + if (IPPROTO_UDP == new_sock->protocol) { + puts ("Opening UDP encapsulated sockets."); + socket_type = SOCK_DGRAM; + new_sock->udp_encap_ucast_port = DEFAULT_UDP_ENCAP_UCAST_PORT; + new_sock->udp_encap_mcast_port = DEFAULT_UDP_ENCAP_MCAST_PORT; + } else { + puts ("Opening raw sockets."); + socket_type = SOCK_RAW; + } + + if ((new_sock->recv_sock = socket (new_sock->family, + socket_type, + new_sock->protocol)) == PGM_INVALID_SOCKET) + { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + "Creating receive socket: %s", + pgm_sock_strerror (save_errno)); +#ifndef _WIN32 + if (EPERM == save_errno) { + g_critical ("PGM protocol requires CAP_NET_RAW capability, e.g. sudo execcap 'cap_net_raw=ep'"); + } +#endif + goto err_destroy; + } + + if ((new_sock->send_sock = socket (new_sock->family, + socket_type, + new_sock->protocol)) == PGM_INVALID_SOCKET) + { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + "Creating send socket: %s", + pgm_sock_strerror (save_errno)); + goto err_destroy; + } + + if ((new_sock->send_with_router_alert_sock = socket (new_sock->family, + socket_type, + new_sock->protocol)) == PGM_INVALID_SOCKET) + { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + "Creating IP Router Alert (RFC 2113) send socket: %s", + pgm_sock_strerror (save_errno)); + goto err_destroy; + } + + *sock = new_sock; + + puts ("PGM socket successfully created."); + return TRUE; + +err_destroy: + if (PGM_INVALID_SOCKET != new_sock->recv_sock) { + if (PGM_SOCKET_ERROR == pgm_closesocket (new_sock->recv_sock)) { + const int save_errno = pgm_sock_errno(); + g_warning ("Close on receive socket failed: %s", + pgm_sock_strerror (save_errno)); + } + new_sock->recv_sock = PGM_INVALID_SOCKET; + } + if (PGM_INVALID_SOCKET != new_sock->send_sock) { + if (PGM_SOCKET_ERROR == pgm_closesocket (new_sock->send_sock)) { + const int save_errno = pgm_sock_errno(); + g_warning ("Close on send socket failed: %s", + pgm_sock_strerror (save_errno)); + } + new_sock->send_sock = PGM_INVALID_SOCKET; + } + if (PGM_INVALID_SOCKET != new_sock->send_with_router_alert_sock) { + if (PGM_SOCKET_ERROR == pgm_closesocket (new_sock->send_with_router_alert_sock)) { + const int save_errno = pgm_sock_errno(); + g_warning ("Close on IP Router Alert (RFC 2113) send socket failed: %s", + pgm_sock_strerror (save_errno)); + } + new_sock->send_with_router_alert_sock = PGM_INVALID_SOCKET; + } + pgm_free (new_sock); + return FALSE; +} + +static +gboolean +on_io_data ( + GIOChannel* source, + G_GNUC_UNUSED GIOCondition condition, + gpointer data + ) +{ + pgm_sock_t* sock = data; + + struct pgm_sk_buff_t* skb = pgm_alloc_skb (sock->max_tpdu); + int fd = g_io_channel_unix_get_fd(source); + struct sockaddr_storage src_addr; + socklen_t src_addr_len = sizeof(src_addr); + skb->len = recvfrom(fd, skb->head, sock->max_tpdu, MSG_DONTWAIT, (struct sockaddr*)&src_addr, &src_addr_len); + + printf ("%i bytes received from %s.\n", skb->len, inet_ntoa(((struct sockaddr_in*)&src_addr)->sin_addr)); + + monitor_packet (skb->data, skb->len); + fflush (stdout); + +/* parse packet to maintain peer database */ + if (sock->udp_encap_ucast_port) { + if (!pgm_parse_udp_encap (skb, NULL)) + goto out; + } else { + struct sockaddr_storage addr; + if (!pgm_parse_raw (skb, (struct sockaddr*)&addr, NULL)) + goto out; + } + + if (PGM_IS_UPSTREAM (skb->pgm_header->pgm_type) || + PGM_IS_PEER (skb->pgm_header->pgm_type)) + goto out; /* ignore */ + +/* downstream = source to receivers */ + if (!PGM_IS_DOWNSTREAM (skb->pgm_header->pgm_type)) + goto out; + +/* pgm packet DPORT contains our transport DPORT */ + if (skb->pgm_header->pgm_dport != sock->dport) + goto out; + +/* search for TSI peer context or create a new one */ + pgm_peer_t* sender = pgm_hashtable_lookup (sock->peers_hashtable, &skb->tsi); + if (sender == NULL) + { + printf ("new peer, tsi %s, local nla %s\n", + pgm_tsi_print (&skb->tsi), + inet_ntoa(((struct sockaddr_in*)&src_addr)->sin_addr)); + + pgm_peer_t* peer = g_new0 (pgm_peer_t, 1); + peer->sock = sock; + memcpy (&peer->tsi, &skb->tsi, sizeof(pgm_tsi_t)); + ((struct sockaddr_in*)&peer->nla)->sin_addr.s_addr = INADDR_ANY; + memcpy (&peer->local_nla, &src_addr, src_addr_len); + + pgm_hashtable_insert (sock->peers_hashtable, &peer->tsi, peer); + sender = peer; + } + +/* handle SPMs for advertised NLA */ + if (skb->pgm_header->pgm_type == PGM_SPM) + { + char *pgm_data = (char*)(skb->pgm_header + 1); + struct pgm_spm* spm = (struct pgm_spm*)pgm_data; + guint32 spm_sqn = g_ntohl (spm->spm_sqn); + + if ( pgm_uint32_gte (spm_sqn, sender->spm_sqn) + || ( ((struct sockaddr*)&sender->nla)->sa_family == 0 ) ) + { + pgm_nla_to_sockaddr (&spm->spm_nla_afi, (struct sockaddr*)&sender->nla); + sender->spm_sqn = spm_sqn; + } + } + +out: + return TRUE; +} + +static +bool +fake_pgm_bind3 ( + pgm_sock_t* restrict sock, + const struct pgm_sockaddr_t*const restrict sockaddr, + const socklen_t sockaddrlen, + const struct pgm_interface_req_t*const send_req, /* only use gr_interface and gr_group::sin6_scope */ + const socklen_t send_req_len, + const struct pgm_interface_req_t*const recv_req, + const socklen_t recv_req_len, + pgm_error_t** restrict error /* maybe NULL */ + ) +{ + g_return_val_if_fail (NULL != sock, FALSE); + g_return_val_if_fail (NULL != sockaddr, FALSE); + g_return_val_if_fail (0 != sockaddrlen, FALSE); + if (sockaddr->sa_addr.sport) pgm_return_val_if_fail (sockaddr->sa_addr.sport != sockaddr->sa_port, FALSE); + g_return_val_if_fail (NULL != send_req, FALSE); + g_return_val_if_fail (sizeof(struct pgm_interface_req_t) == send_req_len, FALSE); + g_return_val_if_fail (NULL != recv_req, FALSE); + g_return_val_if_fail (sizeof(struct pgm_interface_req_t) == recv_req_len, FALSE); + + if (sock->is_bound || + sock->is_destroyed) + { + pgm_return_val_if_reached (FALSE); + } + + memcpy (&sock->tsi, &sockaddr->sa_addr, sizeof(pgm_tsi_t)); + sock->dport = htons (sockaddr->sa_port); + if (sock->tsi.sport) { + sock->tsi.sport = htons (sock->tsi.sport); + } else { + do { + sock->tsi.sport = htons (pgm_random_int_range (0, UINT16_MAX)); + } while (sock->tsi.sport == sock->dport); + } + +/* UDP encapsulation port */ + if (sock->udp_encap_mcast_port) { + ((struct sockaddr_in*)&sock->send_gsr.gsr_group)->sin_port = htons (sock->udp_encap_mcast_port); + } + +/* pseudo-random number generator for back-off intervals */ + pgm_rand_create (&sock->rand_); + +/* PGM Children support of POLLs requires 32-bit random node identifier RAND_NODE_ID */ + if (sock->can_recv_data) { + sock->rand_node_id = pgm_rand_int (&sock->rand_); + } + +/* determine IP header size for rate regulation engine & stats */ + sock->iphdr_len = (AF_INET == sock->family) ? sizeof(struct pgm_ip) : sizeof(struct pgm_ip6_hdr); + pgm_trace (PGM_LOG_ROLE_NETWORK,"Assuming IP header size of %zu bytes", sock->iphdr_len); + + if (sock->udp_encap_ucast_port) { + const size_t udphdr_len = sizeof(struct pgm_udphdr); + printf ("Assuming UDP header size of %zu bytes\n", udphdr_len); + sock->iphdr_len += udphdr_len; + } + + const sa_family_t pgmcc_family = sock->use_pgmcc ? sock->family : 0; + sock->max_tsdu = sock->max_tpdu - sock->iphdr_len - pgm_pkt_offset (FALSE, pgmcc_family); + sock->max_tsdu_fragment = sock->max_tpdu - sock->iphdr_len - pgm_pkt_offset (TRUE, pgmcc_family); + const unsigned max_fragments = sock->txw_sqns ? MIN( PGM_MAX_FRAGMENTS, sock->txw_sqns ) : PGM_MAX_FRAGMENTS; + sock->max_apdu = MIN( PGM_MAX_APDU, max_fragments * sock->max_tsdu_fragment ); + +/* create peer list */ + if (sock->can_recv_data) { + sock->peers_hashtable = pgm_hashtable_new (pgm_tsi_hash, pgm_tsi_equal); + pgm_assert (NULL != sock->peers_hashtable); + } + +/* IP/PGM only */ + { + const sa_family_t recv_family = sock->family; + if (AF_INET == recv_family) + { +/* include IP header only for incoming data, only works for IPv4 */ + puts ("Request IP headers."); + if (PGM_SOCKET_ERROR == pgm_sockaddr_hdrincl (sock->recv_sock, recv_family, TRUE)) + { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + "Enabling IP header in front of user data: %s", + pgm_sock_strerror (save_errno)); + return FALSE; + } + } + else + { + pgm_assert (AF_INET6 == recv_family); + puts ("Request socket packet-info."); + if (PGM_SOCKET_ERROR == pgm_sockaddr_pktinfo (sock->recv_sock, recv_family, TRUE)) + { + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + "Enabling receipt of control message per incoming datagram: %s", + pgm_sock_strerror (save_errno)); + return FALSE; + } + } + } + + union { + struct sockaddr sa; + struct sockaddr_in s4; + struct sockaddr_in6 s6; + struct sockaddr_storage ss; + } recv_addr, recv_addr2, send_addr, send_with_router_alert_addr; + +#ifdef CONFIG_BIND_INADDR_ANY +/* force default interface for bind-only, source address is still valid for multicast membership. + * effectively same as running getaddrinfo(hints = {ai_flags = AI_PASSIVE}) + */ + if (AF_INET == sock->family) { + memset (&recv_addr.s4, 0, sizeof(struct sockaddr_in)); + recv_addr.s4.sin_family = AF_INET; + recv_addr.s4.sin_addr.s_addr = INADDR_ANY; + } else { + memset (&recv_addr.s6, 0, sizeof(struct sockaddr_in6)); + recv_addr.s6.sin6_family = AF_INET6; + recv_addr.s6.sin6_addr = in6addr_any; + } + puts ("Binding receive socket to INADDR_ANY."); +#else + if (!pgm_if_indextoaddr (recv_req->ir_interface, + sock->family, + recv_req->ir_scope_id, + &recv_addr.sa, + error)) + { + return FALSE; + } + printf ("Binding receive socket to interface index %u scope %u\n"), + recv_req->ir_interface, + recv_req->ir_scope_id); + +#endif /* CONFIG_BIND_INADDR_ANY */ + + memcpy (&recv_addr2.sa, &recv_addr.sa, pgm_sockaddr_len (&recv_addr.sa)); + ((struct sockaddr_in*)&recv_addr)->sin_port = htons (sock->udp_encap_mcast_port); + if (PGM_SOCKET_ERROR == bind (sock->recv_sock, + &recv_addr.sa, + pgm_sockaddr_len (&recv_addr.sa))) + { + char addr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&recv_addr, addr, sizeof(addr)); + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + "Binding receive socket to address %s: %s", + addr, + pgm_sock_strerror (save_errno)); + return FALSE; + } + + { + char s[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&recv_addr, s, sizeof(s)); + printf ("bind succeeded on recv_gsr[0] interface %s\n", s); + } + +/* keep a copy of the original address source to re-use for router alert bind */ + memset (&send_addr, 0, sizeof(send_addr)); + + if (!pgm_if_indextoaddr (send_req->ir_interface, + sock->family, + send_req->ir_scope_id, + (struct sockaddr*)&send_addr, + error)) + { + return FALSE; + } + else + { + printf ("Binding send socket to interface index %u scope %u\n", + send_req->ir_interface, + send_req->ir_scope_id); + } + + memcpy (&send_with_router_alert_addr, &send_addr, pgm_sockaddr_len ((struct sockaddr*)&send_addr)); + if (PGM_SOCKET_ERROR == bind (sock->send_sock, + (struct sockaddr*)&send_addr, + pgm_sockaddr_len ((struct sockaddr*)&send_addr))) + { + char addr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&send_addr, addr, sizeof(addr)); + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + "Binding send socket to address %s: %s", + addr, + pgm_sock_strerror (save_errno)); + return FALSE; + } + +/* resolve bound address if 0.0.0.0 */ + if (AF_INET == send_addr.ss.ss_family) + { + if ((INADDR_ANY == ((struct sockaddr_in*)&send_addr)->sin_addr.s_addr) && + !pgm_if_getnodeaddr (AF_INET, (struct sockaddr*)&send_addr, sizeof(send_addr), error)) + { + return FALSE; + } + } + else if ((memcmp (&in6addr_any, &((struct sockaddr_in6*)&send_addr)->sin6_addr, sizeof(in6addr_any)) == 0) && + !pgm_if_getnodeaddr (AF_INET6, (struct sockaddr*)&send_addr, sizeof(send_addr), error)) + { + return FALSE; + } + + { + char s[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&send_addr, s, sizeof(s)); + printf ("bind succeeded on send_gsr interface %s\n", s); + } + + if (PGM_SOCKET_ERROR == bind (sock->send_with_router_alert_sock, + (struct sockaddr*)&send_with_router_alert_addr, + pgm_sockaddr_len((struct sockaddr*)&send_with_router_alert_addr))) + { + char addr[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&send_with_router_alert_addr, addr, sizeof(addr)); + const int save_errno = pgm_sock_errno(); + pgm_set_error (error, + PGM_ERROR_DOMAIN_SOCKET, + pgm_error_from_sock_errno (save_errno), + "Binding IP Router Alert (RFC 2113) send socket to address %s: %s", + addr, + pgm_sock_strerror (save_errno)); + return FALSE; + } + + { + char s[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&send_with_router_alert_addr, s, sizeof(s)); + printf ("bind (router alert) succeeded on send_gsr interface %s\n", s); + } + +/* save send side address for broadcasting as source nla */ + memcpy (&sock->send_addr, &send_addr, pgm_sockaddr_len ((struct sockaddr*)&send_addr)); + + sock->is_controlled_spm = FALSE; + sock->is_controlled_odata = FALSE; + sock->is_controlled_rdata = FALSE; + +/* allocate first incoming packet buffer */ + sock->rx_buffer = pgm_alloc_skb (sock->max_tpdu); + +/* bind complete */ + sock->is_bound = TRUE; + +/* cleanup */ + puts ("PGM socket successfully bound."); + return TRUE; +} + +static +bool +fake_pgm_bind ( + pgm_sock_t* restrict sock, + const struct pgm_sockaddr_t*const restrict sockaddr, + const socklen_t sockaddrlen, + pgm_error_t** restrict error + ) +{ + struct pgm_interface_req_t null_req; + memset (&null_req, 0, sizeof(null_req)); + return fake_pgm_bind3 (sock, sockaddr, sockaddrlen, &null_req, sizeof(null_req), &null_req, sizeof(null_req), error); +} + +static +bool +fake_pgm_connect ( + pgm_sock_t* restrict sock, + G_GNUC_UNUSED pgm_error_t** restrict error /* maybe NULL */ + ) +{ + g_return_val_if_fail (sock != NULL, FALSE); + g_return_val_if_fail (sock->recv_gsr_len > 0, FALSE); +#ifdef CONFIG_TARGET_WINE + g_return_val_if_fail (sock->recv_gsr_len == 1, FALSE); +#endif + for (unsigned i = 0; i < sock->recv_gsr_len; i++) + { + g_return_val_if_fail (sock->recv_gsr[i].gsr_group.ss_family == sock->recv_gsr[0].gsr_group.ss_family, FALSE); + g_return_val_if_fail (sock->recv_gsr[i].gsr_group.ss_family == sock->recv_gsr[i].gsr_source.ss_family, FALSE); + } + g_return_val_if_fail (sock->send_gsr.gsr_group.ss_family == sock->recv_gsr[0].gsr_group.ss_family, FALSE); +/* state */ + if (PGM_UNLIKELY(sock->is_connected || !sock->is_bound || sock->is_destroyed)) { + g_return_val_if_reached (FALSE); + } + + sock->next_poll = pgm_time_update_now() + pgm_secs( 30 ); + sock->is_connected = TRUE; + +/* cleanup */ + puts ("PGM socket successfully connected."); + return TRUE; +} + + +static +bool +fake_pgm_close ( + pgm_sock_t* sock, + G_GNUC_UNUSED bool flush + ) +{ + g_return_val_if_fail (sock != NULL, FALSE); + g_return_val_if_fail (!sock->is_destroyed, FALSE); +/* flag existing calls */ + sock->is_destroyed = TRUE; +/* cancel running blocking operations */ + if (PGM_INVALID_SOCKET != sock->recv_sock) { + puts ("Closing receive socket."); + pgm_closesocket (sock->recv_sock); + sock->recv_sock = PGM_INVALID_SOCKET; + } + if (PGM_INVALID_SOCKET != sock->send_sock) { + puts ("Closing send socket."); + pgm_closesocket (sock->send_sock); + sock->send_sock = PGM_INVALID_SOCKET; + } + if (sock->peers_hashtable) { + pgm_hashtable_destroy (sock->peers_hashtable); + sock->peers_hashtable = NULL; + } + if (sock->peers_list) { + do { + pgm_list_t* next = sock->peers_list->next; + pgm_peer_unref ((pgm_peer_t*)sock->peers_list->data); + + sock->peers_list = next; + } while (sock->peers_list); + } + if (PGM_INVALID_SOCKET != sock->send_with_router_alert_sock) { + puts ("Closing send with router alert socket."); + pgm_closesocket (sock->send_with_router_alert_sock); + sock->send_with_router_alert_sock = PGM_INVALID_SOCKET; + } + if (sock->spm_heartbeat_interval) { + puts ("freeing SPM heartbeat interval data."); + g_free (sock->spm_heartbeat_interval); + sock->spm_heartbeat_interval = NULL; + } + if (sock->rx_buffer) { + puts ("freeing receive buffer."); + pgm_free_skb (sock->rx_buffer); + sock->rx_buffer = NULL; + } + + g_free (sock); + return TRUE; +} + +static +void +session_create ( + char* session_name, + gboolean is_fake + ) +{ + pgm_error_t* pgm_err = NULL; + gboolean status; + +/* check for duplicate */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess != NULL) { + printf ("FAILED: duplicate session name '%s'\n", session_name); + return; + } + +/* create new and fill in bits */ + sess = g_new0(struct sim_session, 1); + sess->name = g_memdup (session_name, strlen(session_name)+1); + + if (is_fake) { + sess->is_transport_fake = TRUE; + status = fake_pgm_socket (&sess->sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err); + } else { + status = pgm_socket (&sess->sock, AF_INET, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err); + } + if (!status) { + printf ("FAILED: pgm_socket(): %s\n", (pgm_err && pgm_err->message) ? pgm_err->message : "(null)"); + pgm_error_free (pgm_err); + goto err_free; + } + +/* success */ + g_hash_table_insert (g_sessions, sess->name, sess); + g_sessions_list = g_list_prepend (g_sessions_list, sess); + printf ("created new session \"%s\"\n", sess->name); + puts ("READY"); + return; + +err_free: + g_free(sess->name); + g_free(sess); +} + +static +void +session_set_fec ( + char* session_name, + guint block_size, + guint group_size + ) +{ +/* check that session exists */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + + if (block_size > UINT8_MAX || + group_size > UINT8_MAX) + { + puts ("FAILED: value out of bounds"); + return; + } + + const struct pgm_fecinfo_t fecinfo = { + .block_size = block_size, + .proactive_packets = 0, + .group_size = group_size, + .ondemand_parity_enabled = TRUE, + .var_pktlen_enabled = TRUE + }; + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_USE_FEC, &fecinfo, sizeof(fecinfo))) + printf ("FAILED: set FEC = RS(%d, %d)\n", block_size, group_size); + else + puts ("READY"); +} + +static +void +session_bind ( + char* session_name + ) +{ + pgm_error_t* pgm_err = NULL; + +/* check that session exists */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + +/* Use RFC 2113 tagging for PGM Router Assist */ + const int no_router_assist = 0; + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist))) + puts ("FAILED: disable IP_ROUTER_ALERT"); + +/* set PGM parameters */ + const int send_and_receive = 0, + active = 0, + mtu = g_max_tpdu, + txw_sqns = g_sqns, + rxw_sqns = g_sqns, + ambient_spm = pgm_secs (30), + heartbeat_spm[] = { pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (100), + pgm_msecs (1300), + pgm_secs (7), + pgm_secs (16), + pgm_secs (25), + pgm_secs (30) }, + peer_expiry = pgm_secs (300), + spmr_expiry = pgm_msecs (250), + nak_bo_ivl = pgm_msecs (50), + nak_rpt_ivl = pgm_secs (2), + nak_rdata_ivl = pgm_secs (2), + nak_data_retries = 50, + nak_ncf_retries = 50; + + g_assert (G_N_ELEMENTS(heartbeat_spm) > 0); + + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_SEND_ONLY, &send_and_receive, sizeof(send_and_receive))) + puts ("FAILED: set bi-directional transport"); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_RECV_ONLY, &send_and_receive, sizeof(send_and_receive))) + puts ("FAILED: set bi-directional transport"); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_PASSIVE, &active, sizeof(active))) + puts ("FAILED: set active transport"); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_MTU, &mtu, sizeof(mtu))) + printf ("FAILED: set MAX_TPDU = %d bytes\n", mtu); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_TXW_SQNS, &txw_sqns, sizeof(txw_sqns))) + printf ("FAILED: set TXW_SQNS = %d\n", txw_sqns); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_RXW_SQNS, &rxw_sqns, sizeof(rxw_sqns))) + printf ("FAILED: set RXW_SQNS = %d\n", rxw_sqns); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_AMBIENT_SPM, &ambient_spm, sizeof(ambient_spm))) + printf ("FAILED: set AMBIENT_SPM = %ds\n", (int)pgm_to_secs (ambient_spm)); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_HEARTBEAT_SPM, &heartbeat_spm, sizeof(heartbeat_spm))) + { + char buffer[1024]; + sprintf (buffer, "%d", heartbeat_spm[0]); + for (unsigned i = 1; i < G_N_ELEMENTS(heartbeat_spm); i++) { + char t[1024]; + sprintf (t, ", %d", heartbeat_spm[i]); + strcat (buffer, t); + } + printf ("FAILED: set HEARTBEAT_SPM = { %s }\n", buffer); + } + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry))) + printf ("FAILED: set PEER_EXPIRY = %ds\n",(int) pgm_to_secs (peer_expiry)); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry))) + printf ("FAILED: set SPMR_EXPIRY = %dms\n", (int)pgm_to_msecs (spmr_expiry)); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl))) + printf ("FAILED: set NAK_BO_IVL = %dms\n", (int)pgm_to_msecs (nak_bo_ivl)); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl))) + printf ("FAILED: set NAK_RPT_IVL = %dms\n", (int)pgm_to_msecs (nak_rpt_ivl)); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl))) + printf ("FAILED: set NAK_RDATA_IVL = %dms\n", (int)pgm_to_msecs (nak_rdata_ivl)); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries))) + printf ("FAILED: set NAK_DATA_RETRIES = %d\n", nak_data_retries); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries))) + printf ("FAILED: set NAK_NCF_RETRIES = %d\n", nak_ncf_retries); + +/* create global session identifier */ + struct pgm_sockaddr_t addr; + memset (&addr, 0, sizeof(addr)); + addr.sa_port = g_port; + addr.sa_addr.sport = 0; + if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) { + printf ("FAILED: pgm_gsi_create_from_hostname(): %s\n", (pgm_err && pgm_err->message) ? pgm_err->message : "(null)"); + } + +{ + char buffer[1024]; + pgm_tsi_print_r (&addr.sa_addr, buffer, sizeof(buffer)); + printf ("pgm_bind (sock:%p addr:{port:%d tsi:%s} err:%p)\n", + (gpointer)sess->sock, + addr.sa_port, buffer, + (gpointer)&pgm_err); +} + const bool status = sess->is_transport_fake ? + fake_pgm_bind (sess->sock, &addr, sizeof(addr), &pgm_err) : + pgm_bind (sess->sock, &addr, sizeof(addr), &pgm_err); + if (!status) { + printf ("FAILED: pgm_bind(): %s\n", (pgm_err && pgm_err->message) ? pgm_err->message : "(null)"); + pgm_error_free (pgm_err); + } else + puts ("READY"); +} + +static +void +session_connect ( + char* session_name + ) +{ + struct pgm_addrinfo_t hints = { + .ai_family = AF_INET + }, *res = NULL; + pgm_error_t* pgm_err = NULL; + +/* check that session exists */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + + if (!pgm_getaddrinfo (g_network, &hints, &res, &pgm_err)) { + printf ("FAILED: pgm_getaddrinfo(): %s\n", (pgm_err && pgm_err->message) ? pgm_err->message : "(null)"); + pgm_error_free (pgm_err); + return; + } + +/* join IP multicast groups */ + for (unsigned i = 0; i < res->ai_recv_addrs_len; i++) + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req))) + { + char group[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&res->ai_recv_addrs[i].gsr_group, sizeof(struct sockaddr_in), + group, sizeof(group), + NULL, 0, + NI_NUMERICHOST); + printf ("FAILED: join group (#%u %s)\n", (unsigned)res->ai_recv_addrs[i].gsr_interface, group); + } + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req))) + { + char group[INET6_ADDRSTRLEN]; + getnameinfo ((struct sockaddr*)&res->ai_send_addrs[0].gsr_group, sizeof(struct sockaddr_in), + group, sizeof(group), + NULL, 0, + NI_NUMERICHOST); + printf ("FAILED: send group (#%u %s)\n", (unsigned)res->ai_send_addrs[0].gsr_interface, group); + } + pgm_freeaddrinfo (res); + +/* set IP parameters */ + const int non_blocking = 1, + no_multicast_loop = 0, + multicast_hops = 16, + dscp = 0x2e << 2; /* Expedited Forwarding PHB for network elements, no ECN. */ + + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_MULTICAST_LOOP, &no_multicast_loop, sizeof(no_multicast_loop))) + puts ("FAILED: disable multicast loop"); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops))) + printf ("FAILED: set TTL = %d\n", multicast_hops); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_TOS, &dscp, sizeof(dscp))) + printf ("FAILED: set TOS = 0x%x\n", dscp); + if (!pgm_setsockopt (sess->sock, IPPROTO_PGM, PGM_NOBLOCK, &non_blocking, sizeof(non_blocking))) + puts ("FAILED: set non-blocking sockets"); + + const bool status = sess->is_transport_fake ? + fake_pgm_connect (sess->sock, &pgm_err) : + pgm_connect (sess->sock, &pgm_err); + if (!status) { + printf ("FAILED: pgm_connect(): %s\n", (pgm_err && pgm_err->message) ? pgm_err->message : "(null)"); + return; + } + + if (sess->is_transport_fake) + { +/* add receive socket(s) to event manager */ + sess->recv_channel = g_io_channel_unix_new (sess->sock->recv_sock); + + GSource *source; + source = g_io_create_watch (sess->recv_channel, G_IO_IN); + g_source_set_callback (source, (GSourceFunc)on_io_data, sess->sock, NULL); + g_source_attach (source, NULL); + g_source_unref (source); + } + else + { + pgm_async_create (&sess->async, sess->sock, 0); + pgm_async_add_watch (sess->async, on_data, sess); + } + + puts ("READY"); +} + +static inline +gssize +pgm_sendto_hops ( + pgm_sock_t* sock, + G_GNUC_UNUSED gboolean rl, + gboolean ra, + const int hops, + const void* buf, + gsize len, + const struct sockaddr* to, + socklen_t tolen + ) +{ + const int send_sock = ra ? sock->send_with_router_alert_sock : sock->send_sock; + pgm_mutex_lock (&sock->send_mutex); + const ssize_t sent = sendto (send_sock, buf, len, 0, to, tolen); + pgm_mutex_unlock (&sock->send_mutex); + return sent > 0 ? (gssize)len : (gssize)sent; +} + +static +int +pgm_reset_heartbeat_spm ( + pgm_sock_t* sock + ) +{ + int retval = 0; + + pgm_mutex_lock (&sock->timer_mutex); + +/* re-set spm timer */ + sock->spm_heartbeat_state = 1; + sock->next_heartbeat_spm = pgm_time_update_now() + sock->spm_heartbeat_interval[sock->spm_heartbeat_state++]; + +/* prod timer thread if sleeping */ + if (pgm_time_after( sock->next_poll, sock->next_heartbeat_spm )) + sock->next_poll = sock->next_heartbeat_spm; + + pgm_mutex_unlock (&sock->timer_mutex); + + return retval; +} + +static inline +int +brokn_send_apdu_unlocked ( + pgm_sock_t* sock, + const gchar* buf, + gsize count, + gsize* bytes_written + ) +{ + guint32 opt_sqn = pgm_txw_next_lead(sock->window); + guint packets = 0; + guint bytes_sent = 0; + guint data_bytes_sent = 0; + + pgm_mutex_lock (&sock->source_mutex); + + do { +/* retrieve packet storage from transmit window */ + int header_length = sizeof(struct pgm_header) + sizeof(struct pgm_data) + + sizeof(struct pgm_opt_length) + /* includes header */ + sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_fragment); + int tsdu_length = MIN(sock->max_tpdu - sock->iphdr_len - header_length, count - data_bytes_sent); + int tpdu_length = header_length + tsdu_length; + + struct pgm_sk_buff_t* skb = pgm_alloc_skb (tsdu_length); + pgm_skb_put (skb, tpdu_length); + + skb->pgm_header = (struct pgm_header*)skb->data; + memcpy (skb->pgm_header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + skb->pgm_header->pgm_sport = sock->tsi.sport; + skb->pgm_header->pgm_dport = sock->dport; + skb->pgm_header->pgm_type = PGM_ODATA; + skb->pgm_header->pgm_options = PGM_OPT_PRESENT; + skb->pgm_header->pgm_tsdu_length = g_htons (tsdu_length); + +/* ODATA */ + skb->pgm_data = (struct pgm_data*)(skb->pgm_header + 1); + skb->pgm_data->data_sqn = g_htonl (pgm_txw_next_lead(sock->window)); + skb->pgm_data->data_trail = g_htonl (pgm_txw_trail(sock->window)); + +/* OPT_LENGTH */ + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(skb->pgm_data + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = g_htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment) ); +/* OPT_FRAGMENT */ + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_FRAGMENT | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment); + skb->pgm_opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); + skb->pgm_opt_fragment->opt_reserved = 0; + skb->pgm_opt_fragment->opt_sqn = g_htonl (opt_sqn); + skb->pgm_opt_fragment->opt_frag_off = g_htonl (data_bytes_sent); + skb->pgm_opt_fragment->opt_frag_len = g_htonl (count); + +/* TODO: the assembly checksum & copy routine is faster than memcpy & pgm_cksum on >= opteron hardware */ + skb->pgm_header->pgm_checksum = 0; + + int pgm_header_len = (char*)(skb->pgm_opt_fragment + 1) - (char*)skb->pgm_header; + guint32 unfolded_header = pgm_csum_partial ((const void*)skb->pgm_header, pgm_header_len, 0); + guint32 unfolded_odata = pgm_csum_partial_copy ((const void*)(buf + data_bytes_sent), (void*)(skb->pgm_opt_fragment + 1), tsdu_length, 0); + skb->pgm_header->pgm_checksum = pgm_csum_fold (pgm_csum_block_add (unfolded_header, unfolded_odata, pgm_header_len)); + +/* add to transmit window */ + pgm_spinlock_lock (&sock->txw_spinlock); + pgm_txw_add (sock->window, skb); + pgm_spinlock_unlock (&sock->txw_spinlock); + +/* do not send send packet */ + if (packets != 1) + pgm_sendto_hops (sock, + TRUE, + FALSE, + sock->hops, + skb->data, + tpdu_length, + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + +/* save unfolded odata for retransmissions */ + *(guint32*)&skb->cb = unfolded_odata; + + packets++; + bytes_sent += tpdu_length + sock->iphdr_len; + data_bytes_sent += tsdu_length; + + } while (data_bytes_sent < count); + + if (data_bytes_sent > 0 && bytes_written) + *bytes_written = data_bytes_sent; + +/* release txw lock here in order to allow spms to lock mutex */ + pgm_mutex_unlock (&sock->source_mutex); + pgm_reset_heartbeat_spm (sock); + return PGM_IO_STATUS_NORMAL; +} + +static +int +brokn_send ( + pgm_sock_t* sock, + const gchar* data, + gsize len, + gsize* bytes_written + ) +{ + if ( len <= ( sock->max_tpdu - ( sizeof(struct pgm_header) + + sizeof(struct pgm_data) ) ) ) + { + puts ("FAILED: cannot send brokn single TPDU length APDU"); + return PGM_IO_STATUS_ERROR; + } + + return brokn_send_apdu_unlocked (sock, data, len, bytes_written); +} + +static +void +session_send ( + char* session_name, + char* string, + gboolean is_brokn /* send broken apdu */ + ) +{ +/* check that session exists */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + +/* send message */ + int status; + gsize stringlen = strlen(string) + 1; + int n_fds = 1; + struct pollfd fds[ n_fds ]; + struct timeval tv; + int timeout; +again: + if (is_brokn) + status = brokn_send (sess->sock, string, stringlen, NULL); + else + status = pgm_send (sess->sock, string, stringlen, NULL); + switch (status) { + case PGM_IO_STATUS_NORMAL: + puts ("READY"); + break; + case PGM_IO_STATUS_TIMER_PENDING: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (sess->sock, IPPROTO_PGM, PGM_TIME_REMAIN, &tv, &optlen); + } + goto block; + case PGM_IO_STATUS_RATE_LIMITED: + { + socklen_t optlen = sizeof (tv); + pgm_getsockopt (sess->sock, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &optlen); + } +/* fall through */ + case PGM_IO_STATUS_WOULD_BLOCK: +block: + timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + memset (fds, 0, sizeof(fds)); + pgm_poll_info (sess->sock, fds, &n_fds, POLLOUT); + poll (fds, n_fds, timeout /* ms */); + goto again; + default: + puts ("FAILED: pgm_send()"); + break; + } +} + +static +void +session_destroy ( + char* session_name + ) +{ +/* check that session exists */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + +/* remove from hash table */ + g_hash_table_remove (g_sessions, session_name); + +/* close down receive side first to stop new data incoming */ + if (sess->recv_channel) { + puts ("closing receive channel."); + + GError *err = NULL; + g_io_channel_shutdown (sess->recv_channel, TRUE, &err); + + if (err) { + g_warning ("i/o shutdown error %i %s", err->code, err->message); + } + +/* TODO: flush GLib main loop with context specific to the recv channel */ + + sess->recv_channel = NULL; + } + + if (sess->is_transport_fake) + { + fake_pgm_close (sess->sock, TRUE); + } + else + { + pgm_close (sess->sock, TRUE); + } + sess->sock = NULL; + g_free (sess->name); + sess->name = NULL; + g_free (sess); + + puts ("READY"); +} + +static +void +net_send_data ( + char* session_name, + guint8 pgm_type, /* PGM_ODATA or PGM_RDATA */ + guint32 data_sqn, + guint32 txw_trail, + char* string + ) +{ +/* check that session exists */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + + pgm_sock_t* sock = sess->sock; + +/* payload is string including terminating null. */ + int count = strlen(string) + 1; + +/* send */ + int retval = 0; + int tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_data) + count; + + gchar buf[ tpdu_length ]; + + struct pgm_header *header = (struct pgm_header*)buf; + struct pgm_data *data = (struct pgm_data*)(header + 1); + memcpy (header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + header->pgm_sport = sock->tsi.sport; + header->pgm_dport = sock->dport; + header->pgm_type = pgm_type; + header->pgm_options = 0; + header->pgm_tsdu_length = g_htons (count); + +/* O/RDATA */ + data->data_sqn = g_htonl (data_sqn); + data->data_trail = g_htonl (txw_trail); + + memcpy (data + 1, string, count); + + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial ((char*)header, tpdu_length, 0)); + + pgm_mutex_lock (&sock->send_mutex); + retval = sendto (sock->send_sock, + header, + tpdu_length, + 0, /* not expecting a reply */ + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + pgm_mutex_unlock (&sock->send_mutex); + + puts ("READY"); +} + +/* differs to net_send_data in that the string parameters contains every payload + * for the transmission group. this is required to calculate the correct parity + * as the fake transport does not own a transmission window. + * + * all payloads must be the same length unless variable TSDU support is enabled. + */ +static +void +net_send_parity ( + char* session_name, + guint8 pgm_type, /* PGM_ODATA or PGM_RDATA */ + guint32 data_sqn, + guint32 txw_trail, + char* string + ) +{ +/* check that session exists */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + + pgm_sock_t* sock = sess->sock; + +/* split string into individual payloads */ + guint16 parity_length = 0; + gchar** src; + src = g_strsplit (string, " ", sock->rs_k); + +/* payload is string including terminating null. */ + parity_length = strlen(*src) + 1; + +/* check length of payload array */ + gboolean is_var_pktlen = FALSE; + guint i; + for (i = 0; src[i]; i++) + { + guint tsdu_length = strlen(src[i]) + 1; + if (tsdu_length != parity_length) { + is_var_pktlen = TRUE; + + if (tsdu_length > parity_length) + parity_length = tsdu_length; + } + } + + if ( i != sock->rs_k ) { + printf ("FAILED: payload array length %u, whilst rs_k is %u.\n", i, sock->rs_k); + return; + } + +/* add padding and append TSDU lengths */ + if (is_var_pktlen) + { + for (i = 0; src[i]; i++) + { + guint tsdu_length = strlen(src[i]) + 1; + gchar* new_string = g_new0 (gchar, parity_length + 2); + strncpy (new_string, src[i], parity_length); + *(guint16*)(new_string + parity_length) = tsdu_length; + g_free (src[i]); + src[i] = new_string; + } + parity_length += 2; + } + +/* calculate FEC block offset */ + guint32 tg_sqn_mask = 0xffffffff << sock->tg_sqn_shift; + guint rs_h = data_sqn & ~tg_sqn_mask; + +/* send */ + int retval = 0; + int tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_data) + parity_length; + + gchar buf[ tpdu_length ]; + + struct pgm_header *header = (struct pgm_header*)buf; + struct pgm_data *data = (struct pgm_data*)(header + 1); + memcpy (header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + header->pgm_sport = sock->tsi.sport; + header->pgm_dport = sock->dport; + header->pgm_type = pgm_type; + header->pgm_options = is_var_pktlen ? (PGM_OPT_PARITY | PGM_OPT_VAR_PKTLEN) : PGM_OPT_PARITY; + header->pgm_tsdu_length = g_htons (parity_length); + +/* O/RDATA */ + data->data_sqn = g_htonl (data_sqn); + data->data_trail = g_htonl (txw_trail); + + memset (data + 1, 0, parity_length); + pgm_rs_t rs; + pgm_rs_create (&rs, sock->rs_n, sock->rs_k); + pgm_rs_encode (&rs, (const pgm_gf8_t**)src, sock->rs_k + rs_h, (pgm_gf8_t*)(data + 1), parity_length); + pgm_rs_destroy (&rs); + + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial ((char*)header, tpdu_length, 0)); + + pgm_mutex_lock (&sock->send_mutex); + retval = sendto (sock->send_sock, + header, + tpdu_length, + 0, /* not expecting a reply */ + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + pgm_mutex_unlock (&sock->send_mutex); + + g_strfreev (src); + src = NULL; + + puts ("READY"); +} + +static +void +net_send_spm ( + char* session_name, + guint32 spm_sqn, + guint32 txw_trail, + guint32 txw_lead, + gboolean proactive_parity, + gboolean ondemand_parity, + guint k + ) +{ +/* check that session exists */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + + pgm_sock_t* sock = sess->sock; + +/* send */ + int retval = 0; + int tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_spm); + + if (proactive_parity || ondemand_parity) { + tpdu_length += sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_parity_prm); + } + + gchar buf[ tpdu_length ]; + + struct pgm_header *header = (struct pgm_header*)buf; + struct pgm_spm *spm = (struct pgm_spm*)(header + 1); + memcpy (header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + header->pgm_sport = sock->tsi.sport; + header->pgm_dport = sock->dport; + header->pgm_type = PGM_SPM; + header->pgm_options = (proactive_parity || ondemand_parity) ? (PGM_OPT_PRESENT | PGM_OPT_NETWORK) : 0; + header->pgm_tsdu_length = 0; + +/* SPM */ + spm->spm_sqn = g_htonl (spm_sqn); + spm->spm_trail = g_htonl (txw_trail); + spm->spm_lead = g_htonl (txw_lead); + pgm_sockaddr_to_nla ((struct sockaddr*)&sock->send_addr, (char*)&spm->spm_nla_afi); + + if (proactive_parity || ondemand_parity) { + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(spm + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = g_htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_parity_prm) ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_PARITY_PRM | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_parity_prm); + struct pgm_opt_parity_prm* opt_parity_prm = (struct pgm_opt_parity_prm*)(opt_header + 1); + opt_parity_prm->opt_reserved = (proactive_parity ? PGM_PARITY_PRM_PRO : 0) | + (ondemand_parity ? PGM_PARITY_PRM_OND : 0); + opt_parity_prm->parity_prm_tgs = g_htonl (k); + } + + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial ((char*)header, tpdu_length, 0)); + + retval = sendto (sock->send_sock, + header, + tpdu_length, + 0, /* not expecting a reply */ + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + puts ("READY"); +} + +static +void +net_send_spmr ( + char* session_name, + pgm_tsi_t* tsi + ) +{ +/* check that session exists */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + + pgm_sock_t* sock = sess->sock; + +/* check that the peer exists */ + pgm_peer_t* peer = pgm_hashtable_lookup (sock->peers_hashtable, tsi); + struct sockaddr_storage peer_nla; + pgm_gsi_t* peer_gsi; + guint16 peer_sport; + + if (peer == NULL) { +/* ourself */ + if (pgm_tsi_equal (tsi, &sock->tsi)) + { + peer_gsi = &sock->tsi.gsi; + peer_sport = sock->tsi.sport; + } + else + { + printf ("FAILED: peer \"%s\" not found\n", pgm_tsi_print (tsi)); + return; + } + } + else + { + memcpy (&peer_nla, &peer->local_nla, sizeof(struct sockaddr_storage)); + peer_gsi = &peer->tsi.gsi; + peer_sport = peer->tsi.sport; + } + +/* send */ + int retval = 0; + int tpdu_length = sizeof(struct pgm_header); + gchar buf[ tpdu_length ]; + + struct pgm_header *header = (struct pgm_header*)buf; + memcpy (header->pgm_gsi, peer_gsi, sizeof(pgm_gsi_t)); + header->pgm_sport = sock->dport; + header->pgm_dport = peer_sport; + header->pgm_type = PGM_SPMR; + header->pgm_options = 0; + header->pgm_tsdu_length = 0; + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial ((char*)header, tpdu_length, 0)); + + pgm_mutex_lock (&sock->send_mutex); +/* TTL 1 */ + pgm_sockaddr_multicast_hops (sock->send_sock, sock->send_gsr.gsr_group.ss_family, 1); + retval = sendto (sock->send_sock, + header, + tpdu_length, + 0, /* not expecting a reply */ + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); +/* default TTL */ + pgm_sockaddr_multicast_hops (sock->send_sock, sock->send_gsr.gsr_group.ss_family, sock->hops); + + if (!pgm_tsi_equal (tsi, &sock->tsi)) + { + retval = sendto (sock->send_sock, + header, + tpdu_length, + 0, /* not expecting a reply */ + (struct sockaddr*)&peer_nla, + pgm_sockaddr_len((struct sockaddr*)&peer_nla)); + } + + pgm_mutex_unlock (&sock->send_mutex); + + puts ("READY"); +} + +/* Send a NAK on a valid transport. A fake transport would need to specify the senders NLA, + * we use the peer list to bypass extracting it from the monitor output. + */ + +static +void +net_send_ncf ( + char* session_name, + pgm_tsi_t* tsi, + struct pgm_sqn_list_t* sqn_list /* list of sequence numbers */ + ) +{ +/* check that session exists */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + +/* check that the peer exists */ + pgm_sock_t* sock = sess->sock; + pgm_peer_t* peer = pgm_hashtable_lookup (sock->peers_hashtable, tsi); + if (peer == NULL) { + printf ("FAILED: peer \"%s\" not found\n", pgm_tsi_print (tsi)); + return; + } + +/* check for valid nla */ + if (((struct sockaddr*)&peer->nla)->sa_family == 0 ) { + puts ("FAILED: peer NLA unknown, cannot send NCF."); + return; + } + +/* send */ + int retval = 0; + int tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_nak); + + if (sqn_list->len > 1) { + tpdu_length += sizeof(struct pgm_opt_length) + /* includes header */ + sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(guint32) ); + } + + gchar buf[ tpdu_length ]; + + struct pgm_header *header = (struct pgm_header*)buf; + struct pgm_nak *ncf = (struct pgm_nak*)(header + 1); + memcpy (header->pgm_gsi, &sock->tsi.gsi, sizeof(pgm_gsi_t)); + + struct sockaddr_storage peer_nla; + memcpy (&peer_nla, &peer->nla, sizeof(struct sockaddr_storage)); + +/* dport & sport swap over for a nak */ + header->pgm_sport = sock->tsi.sport; + header->pgm_dport = sock->dport; + header->pgm_type = PGM_NCF; + header->pgm_options = (sqn_list->len > 1) ? (PGM_OPT_PRESENT | PGM_OPT_NETWORK) : 0; + header->pgm_tsdu_length = 0; + +/* NCF */ + ncf->nak_sqn = g_htonl (sqn_list->sqn[0]); + +/* source nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&peer_nla, (char*)&ncf->nak_src_nla_afi); + +/* group nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&sock->recv_gsr[0].gsr_group, (char*)&ncf->nak_grp_nla_afi); + +/* OPT_NAK_LIST */ + if (sqn_list->len > 1) + { + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(ncf + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = g_htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(guint32) ) ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_NAK_LIST | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(guint32) ); + struct pgm_opt_nak_list* opt_nak_list = (struct pgm_opt_nak_list*)(opt_header + 1); + opt_nak_list->opt_reserved = 0; + for (guint i = 1; i < sqn_list->len; i++) { + opt_nak_list->opt_sqn[i-1] = g_htonl (sqn_list->sqn[i]); + } + } + + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial ((char*)header, tpdu_length, 0)); + + retval = sendto (sock->send_with_router_alert_sock, + header, + tpdu_length, + 0, /* not expecting a reply */ + (struct sockaddr*)&sock->send_gsr.gsr_group, + pgm_sockaddr_len((struct sockaddr*)&sock->send_gsr.gsr_group)); + + puts ("READY"); +} + +static +void +net_send_nak ( + char* session_name, + pgm_tsi_t* tsi, + struct pgm_sqn_list_t* sqn_list, /* list of sequence numbers */ + gboolean is_parity /* TRUE = parity, FALSE = selective */ + ) +{ +/* check that session exists */ + struct sim_session* sess = g_hash_table_lookup (g_sessions, session_name); + if (sess == NULL) { + printf ("FAILED: session '%s' not found\n", session_name); + return; + } + +/* check that the peer exists */ + pgm_sock_t* sock = sess->sock; + pgm_peer_t* peer = pgm_hashtable_lookup (sock->peers_hashtable, tsi); + if (peer == NULL) { + printf ("FAILED: peer \"%s\" not found\n", pgm_tsi_print(tsi)); + return; + } + +/* send */ + int retval = 0; + int tpdu_length = sizeof(struct pgm_header) + sizeof(struct pgm_nak); + + if (sqn_list->len > 1) { + tpdu_length += sizeof(struct pgm_opt_length) + /* includes header */ + sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(guint32) ); + } + + gchar buf[ tpdu_length ]; + + struct pgm_header *header = (struct pgm_header*)buf; + struct pgm_nak *nak = (struct pgm_nak*)(header + 1); + memcpy (header->pgm_gsi, &peer->tsi.gsi, sizeof(pgm_gsi_t)); + + guint16 peer_sport = peer->tsi.sport; + struct sockaddr_storage peer_nla; + memcpy (&peer_nla, &peer->nla, sizeof(struct sockaddr_storage)); + +/* dport & sport swap over for a nak */ + header->pgm_sport = sock->dport; + header->pgm_dport = peer_sport; + header->pgm_type = PGM_NAK; + if (is_parity) { + header->pgm_options = (sqn_list->len > 1) ? (PGM_OPT_PRESENT | PGM_OPT_NETWORK | PGM_OPT_PARITY) + : PGM_OPT_PARITY; + } else { + header->pgm_options = (sqn_list->len > 1) ? (PGM_OPT_PRESENT | PGM_OPT_NETWORK) : 0; + } + header->pgm_tsdu_length = 0; + +/* NAK */ + nak->nak_sqn = g_htonl (sqn_list->sqn[0]); + +/* source nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&peer_nla, (char*)&nak->nak_src_nla_afi); + +/* group nla */ + pgm_sockaddr_to_nla ((struct sockaddr*)&sock->recv_gsr[0].gsr_group, (char*)&nak->nak_grp_nla_afi); + +/* OPT_NAK_LIST */ + if (sqn_list->len > 1) + { + struct pgm_opt_length* opt_len = (struct pgm_opt_length*)(nak + 1); + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = g_htons ( sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(guint32) ) ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_NAK_LIST | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_nak_list) + + ( (sqn_list->len-1) * sizeof(guint32) ); + struct pgm_opt_nak_list* opt_nak_list = (struct pgm_opt_nak_list*)(opt_header + 1); + opt_nak_list->opt_reserved = 0; + for (guint i = 1; i < sqn_list->len; i++) { + opt_nak_list->opt_sqn[i-1] = g_htonl (sqn_list->sqn[i]); + } + } + + header->pgm_checksum = 0; + header->pgm_checksum = pgm_csum_fold (pgm_csum_partial ((char*)header, tpdu_length, 0)); + + retval = sendto (sock->send_with_router_alert_sock, + header, + tpdu_length, + 0, /* not expecting a reply */ + (struct sockaddr*)&peer_nla, + pgm_sockaddr_len((struct sockaddr*)&peer_nla)); + + puts ("READY"); +} + +static +int +on_data ( + gpointer data, + G_GNUC_UNUSED guint len, + G_GNUC_UNUSED gpointer user_data + ) +{ + printf ("DATA: %s\n", (char*)data); + fflush (stdout); + + return 0; +} + +/* process input commands from stdin/fd + */ + +static +gboolean +on_stdin_data ( + GIOChannel* source, + G_GNUC_UNUSED GIOCondition condition, + G_GNUC_UNUSED gpointer data + ) +{ + gchar* str = NULL; + gsize len = 0; + gsize term = 0; + GError* err = NULL; + + g_io_channel_read_line (source, &str, &len, &term, &err); + if (len > 0) { + if (term) str[term] = 0; + +/* quit */ + if (strcmp(str, "quit") == 0) + { + g_main_loop_quit(g_loop); + goto out; + } + + regex_t preg; + regmatch_t pmatch[10]; + const char *re; + +/* endpoint simulator specific: */ + +/* send odata or rdata */ + re = "^net[[:space:]]+send[[:space:]]+([or])data[[:space:]]+" + "([[:alnum:]]+)[[:space:]]+" /* transport */ + "([0-9]+)[[:space:]]+" /* sequence number */ + "([0-9]+)[[:space:]]+" /* txw_trail */ + "([[:alnum:]]+)$"; /* payload */ + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + guint8 pgm_type = *(str + pmatch[1].rm_so) == 'o' ? PGM_ODATA : PGM_RDATA; + + char *name = g_memdup (str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so + 1 ); + name[ pmatch[2].rm_eo - pmatch[2].rm_so ] = 0; + + char* p = str + pmatch[3].rm_so; + guint32 data_sqn = strtoul (p, &p, 10); + + p = str + pmatch[4].rm_so; + guint txw_trail = strtoul (p, &p, 10); + + char *string = g_memdup (str + pmatch[5].rm_so, pmatch[5].rm_eo - pmatch[5].rm_so + 1 ); + string[ pmatch[5].rm_eo - pmatch[5].rm_so ] = 0; + + net_send_data (name, pgm_type, data_sqn, txw_trail, string); + + g_free (name); + g_free (string); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* send parity odata or rdata */ + re = "^net[[:space:]]+send[[:space:]]+parity[[:space:]]+([or])data[[:space:]]+" + "([[:alnum:]]+)[[:space:]]+" /* transport */ + "([0-9]+)[[:space:]]+" /* sequence number */ + "([0-9]+)[[:space:]]+" /* txw_trail */ + "([a-z0-9 ]+)$"; /* payloads */ + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + guint8 pgm_type = *(str + pmatch[1].rm_so) == 'o' ? PGM_ODATA : PGM_RDATA; + + char *name = g_memdup (str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so + 1 ); + name[ pmatch[2].rm_eo - pmatch[2].rm_so ] = 0; + + char* p = str + pmatch[3].rm_so; + guint32 data_sqn = strtoul (p, &p, 10); + + p = str + pmatch[4].rm_so; + guint txw_trail = strtoul (p, &p, 10); + +/* ideally confirm number of payloads matches sess->sock::rs_k ... */ + char *string = g_memdup (str + pmatch[5].rm_so, pmatch[5].rm_eo - pmatch[5].rm_so + 1 ); + string[ pmatch[5].rm_eo - pmatch[5].rm_so ] = 0; + + net_send_parity (name, pgm_type, data_sqn, txw_trail, string); + + g_free (name); + g_free (string); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* send spm */ + re = "^net[[:space:]]+send[[:space:]]+spm[[:space:]]+" + "([[:alnum:]]+)[[:space:]]+" /* transport */ + "([0-9]+)[[:space:]]+" /* spm sequence number */ + "([0-9]+)[[:space:]]+" /* txw_trail */ + "([0-9]+)" /* txw_lead */ + "([[:space:]]+pro-active)?" /* pro-active parity */ + "([[:space:]]+on-demand)?" /* on-demand parity */ + "([[:space:]]+[0-9]+)?$"; /* transmission group size */ + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + char* p = str + pmatch[2].rm_so; + guint32 spm_sqn = strtoul (p, &p, 10); + + p = str + pmatch[3].rm_so; + guint txw_trail = strtoul (p, &p, 10); + + p = str + pmatch[4].rm_so; + guint txw_lead = strtoul (p, &p, 10); + + gboolean proactive_parity = pmatch[5].rm_eo > pmatch[5].rm_so; + gboolean ondemand_parity = pmatch[6].rm_eo > pmatch[6].rm_so; + + p = str + pmatch[7].rm_so; + guint k = (pmatch[7].rm_eo > pmatch[7].rm_so) ? strtoul (p, &p, 10) : 0; + + net_send_spm (name, spm_sqn, txw_trail, txw_lead, proactive_parity, ondemand_parity, k); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* send spmr */ + re = "^net[[:space:]]+send[[:space:]]+spmr[[:space:]]+" + "([[:alnum:]]+)[[:space:]]+" /* transport */ + "([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)$"; /* TSI */ + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + pgm_tsi_t tsi; + char *p = str + pmatch[2].rm_so; + tsi.gsi.identifier[0] = strtol (p, &p, 10); + ++p; + tsi.gsi.identifier[1] = strtol (p, &p, 10); + ++p; + tsi.gsi.identifier[2] = strtol (p, &p, 10); + ++p; + tsi.gsi.identifier[3] = strtol (p, &p, 10); + ++p; + tsi.gsi.identifier[4] = strtol (p, &p, 10); + ++p; + tsi.gsi.identifier[5] = strtol (p, &p, 10); + ++p; + tsi.sport = g_htons ( strtol (p, NULL, 10) ); + + net_send_spmr (name, &tsi); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* send nak/ncf */ + re = "^net[[:space:]]+send[[:space:]](parity[[:space:]])?n(ak|cf)[[:space:]]+" + "([[:alnum:]]+)[[:space:]]+" /* transport */ + "([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)[[:space:]]+" /* TSI */ + "([0-9,]+)$"; /* sequence number or list */ + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[3].rm_so, pmatch[3].rm_eo - pmatch[3].rm_so + 1 ); + name[ pmatch[3].rm_eo - pmatch[3].rm_so ] = 0; + + pgm_tsi_t tsi; + char *p = str + pmatch[4].rm_so; + tsi.gsi.identifier[0] = strtol (p, &p, 10); + ++p; + tsi.gsi.identifier[1] = strtol (p, &p, 10); + ++p; + tsi.gsi.identifier[2] = strtol (p, &p, 10); + ++p; + tsi.gsi.identifier[3] = strtol (p, &p, 10); + ++p; + tsi.gsi.identifier[4] = strtol (p, &p, 10); + ++p; + tsi.gsi.identifier[5] = strtol (p, &p, 10); + ++p; + tsi.sport = g_htons ( strtol (p, NULL, 10) ); + +/* parse list of sequence numbers */ + struct pgm_sqn_list_t sqn_list; + sqn_list.len = 0; + { + char* saveptr = NULL; + for (p = str + pmatch[5].rm_so; ; p = NULL) { + char* token = strtok_r (p, ",", &saveptr); + if (!token) break; + sqn_list.sqn[sqn_list.len++] = strtoul (token, NULL, 10); + } + } + + if ( *(str + pmatch[2].rm_so) == 'a' ) + { + net_send_nak (name, &tsi, &sqn_list, (pmatch[1].rm_eo > pmatch[1].rm_so)); + } + else + { + net_send_ncf (name, &tsi, &sqn_list); + } + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/** same as test application: **/ + +/* create transport */ + re = "^create[[:space:]]+(fake[[:space:]]+)?([[:alnum:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so + 1 ); + name[ pmatch[2].rm_eo - pmatch[2].rm_so ] = 0; + + session_create (name, (pmatch[1].rm_eo > pmatch[1].rm_so)); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* enable Reed-Solomon Forward Error Correction */ + re = "^set[[:space:]]+([[:alnum:]]+)[[:space:]]+FEC[[:space:]]+RS[[:space:]]*\\([[:space:]]*([0-9]+)[[:space:]]*,[[:space:]]*([0-9]+)[[:space:]]*\\)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + char *p = str + pmatch[2].rm_so; + *(str + pmatch[2].rm_eo) = 0; + guint n = strtol (p, &p, 10); + p = str + pmatch[3].rm_so; + *(str + pmatch[3].rm_eo) = 0; + guint k = strtol (p, &p, 10); + session_set_fec (name, n, k); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* bind socket */ + re = "^bind[[:space:]]+([[:alnum:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + session_bind (name); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* connect socket */ + re = "^connect[[:space:]]+([[:alnum:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + session_connect (name); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* send packet */ + re = "^send[[:space:]]+([[:alnum:]]+)[[:space:]]+([[:alnum:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + char *string = g_memdup (str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so + 1 ); + string[ pmatch[2].rm_eo - pmatch[2].rm_so ] = 0; + + session_send (name, string, FALSE); + + g_free (name); + g_free (string); + regfree (&preg); + goto out; + } + regfree (&preg); + + re = "^send[[:space:]]+(brokn[[:space:]]+)?([[:alnum:]]+)[[:space:]]+([[:alnum:]]+)[[:space:]]+x[[:space:]]([0-9]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so + 1 ); + name[ pmatch[2].rm_eo - pmatch[2].rm_so ] = 0; + + char* p = str + pmatch[4].rm_so; + int factor = strtol (p, &p, 10); + int src_len = pmatch[3].rm_eo - pmatch[3].rm_so; + char *string = g_malloc ( (factor * src_len) + 1 ); + for (int i = 0; i < factor; i++) + { + memcpy (string + (i * src_len), str + pmatch[3].rm_so, src_len); + } + string[ factor * src_len ] = 0; + + session_send (name, string, (pmatch[1].rm_eo > pmatch[1].rm_so)); + + g_free (name); + g_free (string); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* destroy transport */ + re = "^destroy[[:space:]]+([[:alnum:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *name = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + name[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + + session_destroy (name); + + g_free (name); + regfree (&preg); + goto out; + } + regfree (&preg); + +/* set PGM network */ + re = "^set[[:space:]]+network[[:space:]]+([[:print:]]*;[[:print:]]+)$"; + regcomp (&preg, re, REG_EXTENDED); + if (0 == regexec (&preg, str, G_N_ELEMENTS(pmatch), pmatch, 0)) + { + char *pgm_network = g_memdup (str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1 ); + pgm_network[ pmatch[1].rm_eo - pmatch[1].rm_so ] = 0; + g_network = pgm_network; + puts ("READY"); + + regfree (&preg); + goto out; + } + regfree (&preg); + + printf ("unknown command: %s\n", str); + } + +out: + fflush (stdout); + g_free (str); + return TRUE; +} + +/* idle log notification + */ + +static +gboolean +on_mark ( + G_GNUC_UNUSED gpointer data + ) +{ + g_message ("-- MARK --"); + return TRUE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/spm.pl b/3rdparty/openpgm-svn-r1135/pgm/test/spm.pl new file mode 100755 index 0000000..5da52ac --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/spm.pl @@ -0,0 +1,43 @@ +#!/usr/bin/perl +# spm.pl +# 5.1.4. Ambient SPMs + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$app->connect; + +sub close_ssh { + $mon = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +print "app: ready.\n"; + +print "mon: wait for spm ...\n"; +$mon->wait_for_spm; +print "mon: received spm.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/spm_jump.pl b/3rdparty/openpgm-svn-r1135/pgm/test/spm_jump.pl new file mode 100755 index 0000000..4660a83 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/spm_jump.pl @@ -0,0 +1,62 @@ +#!/usr/bin/perl +# spm_jump.pl +# 6.2. Source Path Messages + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; +$sim->say ("net send spm ao 3200 90001 90000"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,005 at spm_sqn 20.\n"; +$sim->say ("net send spm ao 20 90001 90005"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/spm_jump2.pl b/3rdparty/openpgm-svn-r1135/pgm/test/spm_jump2.pl new file mode 100755 index 0000000..20d279e --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/spm_jump2.pl @@ -0,0 +1,61 @@ +#!/usr/bin/perl +# spm_jump2.pl +# 6.3. Data Recovery by Negative Acknowledgment + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,000 at spm_sqn 3200.\n"; +$sim->say ("net send spm ao 3200 90001 90000"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish SPM txw_trail 90,001 txw_lead 90,001 at spm_sqn 3201.\n"; +$sim->say ("net send spm ao 3201 90001 90001"); + +print "sim: waiting for valid NAK.\n"; +$sim->wait_for_nak; +print "sim: NAK received.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/spm_reception.pl b/3rdparty/openpgm-svn-r1135/pgm/test/spm_reception.pl new file mode 100755 index 0000000..5718305 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/spm_reception.pl @@ -0,0 +1,60 @@ +#!/usr/bin/perl +# spm_reception.pl +# 6.1. Data Reception + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); + +print "sim: publish SPM txw_trail 90,000.\n"; +$sim->say ("net send spm ao 1 90001 90000"); + +# no NAKs should be generated. +print "sim: waiting 2 seconds for erroneous NAKs ...\n"; +$sim->die_on_nak({ 'timeout' => 2 }); +print "sim: no NAKs received.\n"; + +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90000 ichigo"); +print "app: wait for data ...\n"; +my $data = $app->wait_for_data; +print "app: received data [$data].\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/spmr.pl b/3rdparty/openpgm-svn-r1135/pgm/test/spmr.pl new file mode 100755 index 0000000..690e2a5 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/spmr.pl @@ -0,0 +1,75 @@ +#!/usr/bin/perl +# spmr.pl +# 13.3.1. SPM Requests + +use strict; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); +print "sim: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +## capture GSI of test spp +$app->say ("send ao nashi"); +print "mon: wait for odata ...\n"; +my $odata = $mon->wait_for_odata; +print "mon: odata received.\n"; +my $t0 = [gettimeofday]; +my $elapsed; + +$mon->disconnect (1); + +## spm hearbeats are going to clear out the data, lets wait for some quiet +print "sim: wait for SPM interval > 5 seconds ...\n"; +do { + $sim->wait_for_spm; + $elapsed = tv_interval ( $t0, [gettimeofday] ); + print "sim: received SPM after $elapsed seconds.\n"; +} while ($elapsed < 5); + +print "sim: request SPM via SPMR.\n"; +$sim->say ("net send spmr ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort}"); +$t0 = [gettimeofday]; + +print "sim: wait for SPM ...\n"; +$sim->wait_for_spm; +$elapsed = tv_interval ( $t0, [gettimeofday] ); +print "sim: SPM received after $elapsed seconds.\n"; +die "SPM interval too large, indicates heartbeat not SPMR induced.\n" unless ($elapsed < 5.0); + +print "test completed successfully.\n"; + +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/spmr_after_spm.pl b/3rdparty/openpgm-svn-r1135/pgm/test/spmr_after_spm.pl new file mode 100755 index 0000000..c8eb045 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/spmr_after_spm.pl @@ -0,0 +1,80 @@ +#!/usr/bin/perl +# spmr.pl +# 13.3.1. SPM Requests + +use strict; +use Time::HiRes qw( gettimeofday tv_interval ); +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +## capture GSI of test spp +$app->say ("send ao nashi"); +print "mon: wait for odata ...\n"; +my $odata = $mon->wait_for_odata; +print "mon: odata received.\n"; +my $t0 = [gettimeofday]; +my $elapsed; + +## spm hearbeats are going to clear out the data, lets wait for some quiet +print "mon: wait for SPM interval > 5 seconds ...\n"; +do { + $mon->wait_for_spm; + $elapsed = tv_interval ( $t0, [gettimeofday] ); + print "mon: received SPM after $elapsed seconds.\n"; +} while ($elapsed < 5); + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); +print "sim: ready.\n"; + +## app needs to send packet for sim to learn of local NLA +$app->say ("send ao budo"); +print "sim: wait for odata ...\n"; +$odata = $sim->wait_for_odata; +print "sim: odata received.\n"; + +print "sim: request SPM via SPMR.\n"; +$sim->say ("net send spmr ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort}"); +$t0 = [gettimeofday]; + +print "sim: wait for SPM ...\n"; +$sim->wait_for_spm; +$elapsed = tv_interval ( $t0, [gettimeofday] ); +print "sim: SPM received after $elapsed seconds.\n"; +die "SPM interval too large, indicates heartbeat not SPMR induced.\n" unless ($elapsed < 5.0); + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/spmr_from_odata.pl b/3rdparty/openpgm-svn-r1135/pgm/test/spmr_from_odata.pl new file mode 100755 index 0000000..519518b --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/spmr_from_odata.pl @@ -0,0 +1,55 @@ +#!/usr/bin/perl +# spmr_from_odata.pl +# 13.3.1. SPM Requests + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$mon->say ("filter $config{app}{ip}"); +print "mon: ready.\n"; + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); +print "sim: publish ODATA sqn 90,001.\n"; +$sim->say ("net send odata ao 90001 90001 ringo"); + +my $data = $app->wait_for_data; +print "app: received data [$data].\n"; + +print "mon: wait for SPMR ...\n"; +$mon->wait_for_spmr; +print "mon: received SPMR.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/spmr_suppression.pl b/3rdparty/openpgm-svn-r1135/pgm/test/spmr_suppression.pl new file mode 100755 index 0000000..5b73c26 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/spmr_suppression.pl @@ -0,0 +1,60 @@ +#!/usr/bin/perl +# spmr_suppression.pl +# 13.3.1. SPM Requests + +use strict; +use PGM::Test; + +BEGIN { require "test.conf.pl"; } + +$| = 1; + +my $mon = PGM::Test->new(tag => 'mon', host => $config{mon}{host}, cmd => $config{mon}{cmd}); +my $sim = PGM::Test->new(tag => 'sim', host => $config{sim}{host}, cmd => $config{sim}{cmd}); +my $app = PGM::Test->new(tag => 'app', host => $config{app}{host}, cmd => $config{app}{cmd}); + +$mon->connect; +$sim->connect; +$app->connect; + +sub close_ssh { + $mon = $sim = $app = undef; + print "finished.\n"; +} + +$SIG{'INT'} = sub { print "interrupt caught.\n"; close_ssh(); }; + +$sim->say ("create fake ao"); +$sim->say ("bind ao"); +$sim->say ("connect ao"); +print "sim: publish ODATA sqn 90,001 to monitor for GSI.\n"; +$sim->say ("net send odata ao 90001 90001 ringo"); + +## capture GSI of test sim (not app!) +my $odata = $mon->wait_for_odata; +$mon->say ("filter $config{app}{ip}"); + +$app->say ("create ao"); +$app->say ("bind ao"); +$app->say ("connect ao"); +$app->say ("listen ao"); + +print "sim: re-publish ODATA sqn 90,001 to app.\n"; +$sim->say ("net send odata ao 90001 90001 ringo"); +$sim->say ("net send spmr ao $odata->{PGM}->{gsi}.$odata->{PGM}->{sourcePort}"); + +my $data = $app->wait_for_data; +print "app: received data [$data].\n"; + +print "mon: wait for erroneous SPMR ...\n"; +$mon->die_on_spmr({ 'timeout' => 2 }); +print "mon: no SPMR received.\n"; + +print "test completed successfully.\n"; + +$mon->disconnect (1); +$sim->disconnect; +$app->disconnect; +close_ssh; + +# eof diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/sudoers.example b/3rdparty/openpgm-svn-r1135/pgm/test/sudoers.example new file mode 100644 index 0000000..9212139 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/sudoers.example @@ -0,0 +1,26 @@ +# /etc/sudoers +# +# This file MUST be edited with the 'visudo' command as root. +# +# See the man page for details on how to write a sudoers file. +# Host alias specification + +# User alias specification +User_Alias PGM_USER = steve-o + +# Cmnd alias specification +Cmnd_Alias PGM_CONFORMANCE = /miru/projects/openpgm/pgm/ref/debug/test/* + +# Defaults + +Defaults !lecture,tty_tickets,!fqdn + +# User privilege specification +root ALL=(ALL) ALL + +# Members of the admin group may gain root privileges +%admin ALL=(ALL) ALL + + +# PGM testing +PGM_USER ALL = NOPASSWD: PGM_CONFORMANCE diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/test.conf.pl b/3rdparty/openpgm-svn-r1135/pgm/test/test.conf.pl new file mode 100644 index 0000000..66860c6 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/test/test.conf.pl @@ -0,0 +1,27 @@ +# test.conf.pl +use vars qw ( %config ); + +%config = ( + msapp => { + host => 'momo', + ip => '10.6.28.34', + }, + app => { + host => 'ayaka', ip => '10.6.28.31', + cmd => '/miru/projects/openpgm/pgm/ref/release-Linux-x86_64/test/app', + network => 'eth0;239.192.0.1' +# host => 'ryoko', ip => '10.6.28.36', +# cmd => 'LD_LIBRARY_PATH=/opt/glib-sunstudio/lib:$LD_LIBRARY_PATH /miru/projects/openpgm/pgm/ref/release-SunOS-sun4u-sunstudio/test/app', +# network => 'eri0;239.192.0.1' + }, + mon => { + host => 'sora', + cmd => '/miru/projects/openpgm/pgm/ref/release-Linux-x86_64/test/monitor', + network => 'eth0;239.192.0.1' + }, + sim => { + host => 'kiku', + cmd => '/miru/projects/openpgm/pgm/ref/release-Linux-x86_64/test/sim', + network => 'eth0;239.192.0.1' + }, +); diff --git a/3rdparty/openpgm-svn-r1135/pgm/thread.c b/3rdparty/openpgm-svn-r1135/pgm/thread.c new file mode 100644 index 0000000..ad68ca3 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/thread.c @@ -0,0 +1,457 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * mutexes and locks. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +//#define THREAD_DEBUG + + +/* Globals */ + +#if defined(_WIN32) && !defined(CONFIG_HAVE_WIN_COND) +static DWORD cond_event_tls = TLS_OUT_OF_INDEXES; +#endif + +static volatile uint32_t thread_ref_count = 0; + + +#ifndef _WIN32 +# define posix_check_err(err, name) \ + do { \ + const int save_error = (err); \ + if (PGM_UNLIKELY(save_error)) { \ + pgm_error ("file %s: line %d (%s): error '%s' during '%s'", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, \ + strerror (save_error), name); \ + } \ + } while (0) +# define posix_check_cmd(cmd) posix_check_err ((cmd), #cmd) +#else +# define win32_check_err(err, name) \ + do { \ + const bool save_error = (err); \ + if (PGM_UNLIKELY(!save_error)) { \ + pgm_error ("file %s: line %d (%s): error '%s' during '%s'", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, \ + pgm_wsastrerror (GetLastError ()), name); \ + } \ + } while (0) +# define win32_check_cmd(cmd) win32_check_err ((cmd), #cmd) +#endif /* !_WIN32 */ + + +/* only needed for Win32 pre-Vista read-write locks + */ +void +pgm_thread_init (void) +{ + if (pgm_atomic_exchange_and_add32 (&thread_ref_count, 1) > 0) + return; + +#if defined(_WIN32) && !defined(CONFIG_HAVE_WIN_COND) + win32_check_cmd (TLS_OUT_OF_INDEXES != (cond_event_tls = TlsAlloc ())); +#endif +} + +void +pgm_thread_shutdown (void) +{ + pgm_return_if_fail (pgm_atomic_read32 (&thread_ref_count) > 0); + + if (pgm_atomic_exchange_and_add32 (&thread_ref_count, (uint32_t)-1) != 1) + return; + +#if defined(_WIN32) && !defined(CONFIG_HAVE_WIN_COND) + TlsFree (cond_event_tls); +#endif +} + +void +pgm_mutex_init ( + pgm_mutex_t* mutex + ) +{ + pgm_assert (NULL != mutex); +#ifndef _WIN32 + posix_check_cmd (pthread_mutex_init (&mutex->pthread_mutex, NULL)); +#else + HANDLE handle; + win32_check_cmd (handle = CreateMutex (NULL, FALSE, NULL)); + mutex->win32_mutex = handle; +#endif /* !_WIN32 */ +} + +bool +pgm_mutex_trylock ( + pgm_mutex_t* mutex + ) +{ + pgm_assert (NULL != mutex); +#ifndef _WIN32 + const int result = pthread_mutex_trylock (&mutex->pthread_mutex); + if (EBUSY == result) + return FALSE; + posix_check_err (result, "pthread_mutex_trylock"); + return TRUE; +#else + DWORD result; + win32_check_cmd (WAIT_FAILED != (result = WaitForSingleObject (mutex->win32_mutex, 0))); + return WAIT_TIMEOUT != result; +#endif /* !_WIN32 */ +} + +void +pgm_mutex_free ( + pgm_mutex_t* mutex + ) +{ + pgm_assert (NULL != mutex); +#ifndef _WIN32 + posix_check_cmd (pthread_mutex_destroy (&mutex->pthread_mutex)); +#else + win32_check_cmd (CloseHandle (mutex->win32_mutex)); +#endif /* !_WIN32 */ +} + +void +pgm_spinlock_init ( + pgm_spinlock_t* spinlock + ) +{ + pgm_assert (NULL != spinlock); +#ifndef _WIN32 + posix_check_cmd (pthread_spin_init (&spinlock->pthread_spinlock, PTHREAD_PROCESS_PRIVATE)); +#else + InitializeCriticalSection (&spinlock->win32_spinlock); +#endif /* !_WIN32 */ +} + +bool +pgm_spinlock_trylock ( + pgm_spinlock_t* spinlock + ) +{ + pgm_assert (NULL != spinlock); +#ifndef _WIN32 + const int result = pthread_spin_trylock (&spinlock->pthread_spinlock); + if (EBUSY == result) + return FALSE; + posix_check_err (result, "pthread_spinlock_trylock"); + return TRUE; +#else + return TryEnterCriticalSection (&spinlock->win32_spinlock); +#endif /* !_WIN32 */ +} + +void +pgm_spinlock_free ( + pgm_spinlock_t* spinlock + ) +{ + pgm_assert (NULL != spinlock); +#ifndef _WIN32 +/* ignore return value */ + pthread_spin_destroy (&spinlock->pthread_spinlock); +#else + DeleteCriticalSection (&spinlock->win32_spinlock); +#endif /* !_WIN32 */ +} + +void +pgm_cond_init ( + pgm_cond_t* cond + ) +{ + pgm_assert (NULL != cond); +#ifndef _WIN32 + posix_check_cmd (pthread_cond_init (&cond->pthread_cond, NULL)); +#elif defined(CONFIG_HAVE_WIN_COND) + InitializeConditionVariable (&cond->win32_cond); +#else + cond->len = 0; + cond->allocated_len = pgm_nearest_power (1, 2 + 1); + cond->phandle = pgm_new (HANDLE, cond->allocated_len); + InitializeCriticalSection (&cond->win32_spinlock); +#endif /* !_WIN32 */ +} + +void +pgm_cond_signal ( + pgm_cond_t* cond + ) +{ + pgm_assert (NULL != cond); +#ifndef _WIN32 + pthread_cond_signal (&cond->pthread_cond); +#elif defined(CONFIG_HAVE_WIN_COND) + WakeConditionVariable (&cond->win32_cond); +#else + EnterCriticalSection (&cond->win32_spinlock); + if (cond->len > 0) { + SetEvent (cond->phandle[ 0 ]); + memmove (&cond->phandle[ 0 ], &cond->phandle[ 1 ], cond->len - 1); + cond->len--; + } + LeaveCriticalSection (&cond->win32_spinlock); +#endif /* !_WIN32 */ +} + +void +pgm_cond_broadcast ( + pgm_cond_t* cond + ) +{ + pgm_assert (NULL != cond); +#ifndef _WIN32 + pthread_cond_broadcast (&cond->pthread_cond); +#elif defined(CONFIG_HAVE_WIN_COND) + WakeAllConditionVariable (&cond->win32_cond); +#else + EnterCriticalSection (&cond->win32_spinlock); + for (unsigned i = 0; i < cond->len; i++) + SetEvent (cond->phandle[ i ]); + cond->len = 0; + LeaveCriticalSection (&cond->win32_spinlock); +#endif /* !_WIN32 */ +} + +#ifndef _WIN32 +void +pgm_cond_wait ( + pgm_cond_t* cond, + pthread_mutex_t* mutex + ) +{ + pgm_assert (NULL != cond); + pgm_assert (NULL != mutex); + pthread_cond_wait (&cond->pthread_cond, mutex); +} +#else +void +pgm_cond_wait ( + pgm_cond_t* cond, + CRITICAL_SECTION* spinlock + ) +{ + pgm_assert (NULL != cond); + pgm_assert (NULL != spinlock); +# if defined(CONFIG_HAVE_WIN_COND) + SleepConditionVariableCS (&cond->win32_cond, spinlock, INFINITE); +# else + DWORD status; + HANDLE event = TlsGetValue (cond_event_tls); + + if (!event) { + win32_check_cmd (event = CreateEvent (0, FALSE, FALSE, NULL)); + TlsSetValue (cond_event_tls, event); + } + + EnterCriticalSection (&cond->win32_spinlock); + pgm_assert (WAIT_TIMEOUT == WaitForSingleObject (event, 0)); + if ((cond->len + 1) > cond->allocated_len) { + cond->allocated_len = pgm_nearest_power (1, cond->len + 1 + 1); + cond->phandle = pgm_realloc (cond->phandle, cond->allocated_len); + } + cond->phandle[ cond->len++ ] = event; + LeaveCriticalSection (&cond->win32_spinlock); + + EnterCriticalSection (spinlock); + win32_check_cmd (WAIT_FAILED != (status = WaitForSingleObject (event, INFINITE))); + LeaveCriticalSection (spinlock); + + if (WAIT_TIMEOUT == status) { + EnterCriticalSection (&cond->win32_spinlock); + for (unsigned i = 0; i < cond->len; i++) { + if (cond->phandle[ i ] == event) { + if (i != cond->len - 1) + memmove (&cond->phandle[ i ], &cond->phandle[ i + 1 ], sizeof(HANDLE) * (cond->len - i - 1)); + cond->len--; + break; + } + } + win32_check_cmd (WAIT_FAILED != (status = WaitForSingleObject (event, 0))); + LeaveCriticalSection (&cond->win32_spinlock); + } +# endif /* !CONFIG_HAVE_WIN_COND */ +} +#endif /* !_WIN32 */ + +void +pgm_cond_free ( + pgm_cond_t* cond + ) +{ + pgm_assert (NULL != cond); +#ifndef _WIN32 + posix_check_cmd (pthread_cond_destroy (&cond->pthread_cond)); +#elif defined(CONFIG_HAVE_WIN_COND) + /* nop */ +#else + DeleteCriticalSection (&cond->win32_spinlock); + pgm_free (cond->phandle); +#endif /* !_WIN32 */ +} + +void +pgm_rwlock_init ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); +#ifdef CONFIG_HAVE_WIN_SRW_LOCK + InitializeSRWLock (&rwlock->win32_lock); +#elif !defined(_WIN32) + posix_check_cmd (pthread_rwlock_init (&rwlock->pthread_rwlock, NULL)); +#else + InitializeCriticalSection (&rwlock->win32_spinlock); + pgm_cond_init (&rwlock->read_cond); + pgm_cond_init (&rwlock->write_cond); + rwlock->read_counter = 0; + rwlock->have_writer = FALSE; + rwlock->want_to_read = 0; + rwlock->want_to_write = 0; +#endif /* !CONFIG_HAVE_WIN_SRW_LOCK */ +} + +void +pgm_rwlock_free ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); +#ifdef CONFIG_HAVE_WIN_SRW_LOCK + /* nop */ +#elif !defined(_WIN32) + pthread_rwlock_destroy (&rwlock->pthread_rwlock); +#else + pgm_cond_free (&rwlock->read_cond); + pgm_cond_free (&rwlock->write_cond); + DeleteCriticalSection (&rwlock->win32_spinlock); +#endif /* !CONFIG_HAVE_WIN_SRW_LOCK */ +} + +#if !defined(CONFIG_HAVE_WIN_SRW_LOCK) && defined(_WIN32) +static inline +void +_pgm_rwlock_signal ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); + if (rwlock->want_to_write) + pgm_cond_signal (&rwlock->write_cond); + else if (rwlock->want_to_read) + pgm_cond_broadcast (&rwlock->read_cond); +} + +void +pgm_rwlock_reader_lock ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); + EnterCriticalSection (&rwlock->win32_spinlock); + rwlock->want_to_read++; + while (rwlock->have_writer || rwlock->want_to_write) + pgm_cond_wait (&rwlock->read_cond, &rwlock->win32_spinlock); + rwlock->want_to_read--; + rwlock->read_counter++; + LeaveCriticalSection (&rwlock->win32_spinlock); +} + +bool +pgm_rwlock_reader_trylock ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); + bool status; + EnterCriticalSection (&rwlock->win32_spinlock); + if (!rwlock->have_writer && !rwlock->want_to_write) { + rwlock->read_counter++; + status = TRUE; + } else + status = FALSE; + LeaveCriticalSection (&rwlock->win32_spinlock); + return status; +} + +void +pgm_rwlock_reader_unlock( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); + EnterCriticalSection (&rwlock->win32_spinlock); + rwlock->read_counter--; + if (rwlock->read_counter == 0) + _pgm_rwlock_signal (rwlock); + LeaveCriticalSection (&rwlock->win32_spinlock); +} + +void +pgm_rwlock_writer_lock ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); + EnterCriticalSection (&rwlock->win32_spinlock); + rwlock->want_to_write++; + while (rwlock->have_writer || rwlock->read_counter) + pgm_cond_wait (&rwlock->write_cond, &rwlock->win32_spinlock); + rwlock->want_to_write--; + rwlock->have_writer = TRUE; + LeaveCriticalSection (&rwlock->win32_spinlock); +} + +bool +pgm_rwlock_writer_trylock ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); + bool status; + EnterCriticalSection (&rwlock->win32_spinlock); + if (!rwlock->have_writer && !rwlock->read_counter) { + rwlock->have_writer = TRUE; + status = TRUE; + } else + status = FALSE; + LeaveCriticalSection (&rwlock->win32_spinlock); + return status; +} + +void +pgm_rwlock_writer_unlock ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); + EnterCriticalSection (&rwlock->win32_spinlock); + rwlock->have_writer = FALSE; + _pgm_rwlock_signal (rwlock); + LeaveCriticalSection (&rwlock->win32_spinlock); +} +#endif /* !_WIN32 && !CONFIG_HAVE_WIN_SRW_LOCK */ + + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/thread.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/thread.c.c89.patch new file mode 100644 index 0000000..34acaaf --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/thread.c.c89.patch @@ -0,0 +1,137 @@ +--- thread.c 2010-05-21 11:35:21.000000000 +0800 ++++ thread.c89 2010-08-05 11:31:03.000000000 +0800 +@@ -46,7 +46,8 @@ + } while (0) + # define posix_check_cmd(cmd) posix_check_err ((cmd), #cmd) + #else +-# define win32_check_err(err, name) \ ++# ifdef __PRETTY_FUNCTION ++# define win32_check_err(err, name) \ + do { \ + const bool save_error = (err); \ + if (PGM_UNLIKELY(!save_error)) { \ +@@ -55,6 +56,17 @@ + pgm_wsastrerror (GetLastError ()), name); \ + } \ + } while (0) ++# else ++# define win32_check_err(err, name) \ ++ do { \ ++ const bool save_error = (err); \ ++ if (PGM_UNLIKELY(!save_error)) { \ ++ pgm_error ("file %s: line %d: error '%s' during '%s'", \ ++ __FILE__, __LINE__, \ ++ pgm_wsastrerror (GetLastError ()), name); \ ++ } \ ++ } while (0) ++# endif + # define win32_check_cmd(cmd) win32_check_err ((cmd), #cmd) + #endif /* !_WIN32 */ + +@@ -94,9 +106,11 @@ + #ifndef _WIN32 + posix_check_cmd (pthread_mutex_init (&mutex->pthread_mutex, NULL)); + #else ++ { + HANDLE handle; +- win32_check_cmd (handle = CreateMutex (NULL, FALSE, NULL)); ++ win32_check_cmd (NULL != (handle = CreateMutex (NULL, FALSE, NULL))); + mutex->win32_mutex = handle; ++ } + #endif /* !_WIN32 */ + } + +@@ -113,9 +127,11 @@ + posix_check_err (result, "pthread_mutex_trylock"); + return TRUE; + #else ++ { + DWORD result; + win32_check_cmd (WAIT_FAILED != (result = WaitForSingleObject (mutex->win32_mutex, 0))); + return WAIT_TIMEOUT != result; ++ } + #endif /* !_WIN32 */ + } + +@@ -227,8 +243,11 @@ + WakeAllConditionVariable (&cond->win32_cond); + #else + EnterCriticalSection (&cond->win32_spinlock); +- for (unsigned i = 0; i < cond->len; i++) ++ { ++ unsigned i; ++ for (i = 0; i < cond->len; i++) + SetEvent (cond->phandle[ i ]); ++ } + cond->len = 0; + LeaveCriticalSection (&cond->win32_spinlock); + #endif /* !_WIN32 */ +@@ -257,11 +276,12 @@ + # if defined(CONFIG_HAVE_WIN_COND) + SleepConditionVariableCS (&cond->win32_cond, spinlock, INFINITE); + # else ++ { + DWORD status; + HANDLE event = TlsGetValue (cond_event_tls); + + if (!event) { +- win32_check_cmd (event = CreateEvent (0, FALSE, FALSE, NULL)); ++ win32_check_cmd (NULL != (event = CreateEvent (0, FALSE, FALSE, NULL))); + TlsSetValue (cond_event_tls, event); + } + +@@ -280,7 +300,9 @@ + + if (WAIT_TIMEOUT == status) { + EnterCriticalSection (&cond->win32_spinlock); +- for (unsigned i = 0; i < cond->len; i++) { ++ { ++ unsigned i; ++ for (i = 0; i < cond->len; i++) { + if (cond->phandle[ i ] == event) { + if (i != cond->len - 1) + memmove (&cond->phandle[ i ], &cond->phandle[ i + 1 ], sizeof(HANDLE) * (cond->len - i - 1)); +@@ -288,9 +310,11 @@ + break; + } + } ++ } + win32_check_cmd (WAIT_FAILED != (status = WaitForSingleObject (event, 0))); + LeaveCriticalSection (&cond->win32_spinlock); + } ++ } + # endif /* !CONFIG_HAVE_WIN_COND */ + } + #endif /* !_WIN32 */ +@@ -384,6 +408,7 @@ + ) + { + pgm_assert (NULL != rwlock); ++ { + bool status; + EnterCriticalSection (&rwlock->win32_spinlock); + if (!rwlock->have_writer && !rwlock->want_to_write) { +@@ -393,6 +418,7 @@ + status = FALSE; + LeaveCriticalSection (&rwlock->win32_spinlock); + return status; ++ } + } + + void +@@ -429,6 +455,7 @@ + ) + { + pgm_assert (NULL != rwlock); ++ { + bool status; + EnterCriticalSection (&rwlock->win32_spinlock); + if (!rwlock->have_writer && !rwlock->read_counter) { +@@ -438,6 +465,7 @@ + status = FALSE; + LeaveCriticalSection (&rwlock->win32_spinlock); + return status; ++ } + } + + void diff --git a/3rdparty/openpgm-svn-r1135/pgm/time.c b/3rdparty/openpgm-svn-r1135/pgm/time.c new file mode 100644 index 0000000..f012b50 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/time.c @@ -0,0 +1,774 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * high resolution timers. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __STDC_FORMAT_MACROS +#ifdef _MSC_VER +# include +#else +# include +#endif +#include +#include +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include "windows.h" +#endif +#include +#include + +//#define TIME_DEBUG + + +/* globals */ + +pgm_time_update_func pgm_time_update_now PGM_GNUC_READ_MOSTLY; +pgm_time_since_epoch_func pgm_time_since_epoch PGM_GNUC_READ_MOSTLY; + + +/* locals */ + +#define msecs_to_secs(t) ( (t) / 1000 ) +#define usecs_to_secs(t) ( (t) / 1000000UL ) +#define nsecs_to_secs(t) ( (t) / 1000000000UL ) +#define secs_to_msecs(t) ( (pgm_time_t)(t) * 1000 ) +#define secs_to_usecs(t) ( (pgm_time_t)(t) * 1000000UL ) +#define secs_to_nsecs(t) ( (pgm_time_t)(t) * 1000000000UL ) +#define msecs_to_usecs(t) ( (pgm_time_t)(t) * 1000 ) +#define msecs_to_nsecs(t) ( (pgm_time_t)(t) * 1000000UL ) +#define usecs_to_msecs(t) ( (t) / 1000 ) +#define usecs_to_nsecs(t) ( (pgm_time_t)(t) * 1000 ) +#define nsecs_to_msecs(t) ( (t) / 1000000UL ) +#define nsecs_to_usecs(t) ( (t) / 1000 ) +#define fsecs_to_nsecs(t) ( (t) / 1000000UL ) +#define fsecs_to_usecs(t) ( (t) / 1000000000UL ) + +static volatile uint32_t time_ref_count = 0; +static pgm_time_t rel_offset PGM_GNUC_READ_MOSTLY = 0; + +static void pgm_time_conv (const pgm_time_t*const restrict, time_t*restrict); +static void pgm_time_conv_from_reset (const pgm_time_t*const restrict, time_t*restrict); + +#if defined(CONFIG_HAVE_CLOCK_GETTIME) +# include +static pgm_time_t pgm_clock_update (void); +#endif +#ifdef CONFIG_HAVE_FTIME +# include +# ifdef _WIN32 +# define ftime _ftime +# endif +static pgm_time_t pgm_ftime_update (void); +#endif +#ifdef CONFIG_HAVE_GETTIMEOFDAY +# include +static pgm_time_t pgm_gettimeofday_update (void); +#endif +#ifdef CONFIG_HAVE_HPET +# include +# include +# include +# include +# include +# define HPET_MMAP_SIZE 0x400 +# define HPET_GENERAL_CAPS_REGISTER 0x00 +# define HPET_COUNTER_CLK_PERIOD 0x004 +# define HPET_MAIN_COUNTER_REGISTER 0x0f0 +# define HPET_COUNT_SIZE_CAP (1 << 13) +/* HPET counter size maybe 64-bit or 32-bit */ +# if defined(__x86_64__) +typedef uint64_t hpet_counter_t; +# else +typedef uint32_t hpet_counter_t; +# endif +static int hpet_fd PGM_GNUC_READ_MOSTLY = -1; +static char* hpet_ptr PGM_GNUC_READ_MOSTLY; +static uint64_t hpet_offset = 0; +static uint64_t hpet_wrap PGM_GNUC_READ_MOSTLY; +static hpet_counter_t hpet_last = 0; + +# define HPET_NS_SCALE 22 +# define HPET_US_SCALE 34 +static uint_fast32_t hpet_ns_mul PGM_GNUC_READ_MOSTLY = 0; +static uint_fast32_t hpet_us_mul PGM_GNUC_READ_MOSTLY = 0; + +static inline +void +set_hpet_mul ( + const uint32_t hpet_period + ) +{ + hpet_ns_mul = fsecs_to_nsecs((uint64_t)hpet_period << HPET_NS_SCALE); + hpet_us_mul = fsecs_to_usecs((uint64_t)hpet_period << HPET_US_SCALE); +} + +static inline +uint64_t +hpet_to_ns ( + const uint64_t hpet + ) +{ + return (hpet * hpet_ns_mul) >> HPET_NS_SCALE; +} + +static inline +uint64_t +hpet_to_us ( + const uint64_t hpet + ) +{ + return (hpet * hpet_us_mul) >> HPET_US_SCALE; +} + +static bool pgm_hpet_init (pgm_error_t**); +static bool pgm_hpet_shutdown (void); +static pgm_time_t pgm_hpet_update (void); +#endif +#ifdef CONFIG_HAVE_RTC +# include +# include +# include +# include +# include +# include +static int rtc_fd PGM_GNUC_READ_MOSTLY = -1; +static int rtc_frequency PGM_GNUC_READ_MOSTLY = 8192; +static pgm_time_t rtc_count = 0; +static bool pgm_rtc_init (pgm_error_t**); +static bool pgm_rtc_shutdown (void); +static pgm_time_t pgm_rtc_update (void); +#endif +#ifdef CONFIG_HAVE_TSC +# include +# include +# define TSC_NS_SCALE 10 /* 2^10, carefully chosen */ +# define TSC_US_SCALE 20 +static uint_fast32_t tsc_mhz PGM_GNUC_READ_MOSTLY = 0; +static uint_fast32_t tsc_ns_mul PGM_GNUC_READ_MOSTLY = 0; +static uint_fast32_t tsc_us_mul PGM_GNUC_READ_MOSTLY = 0; + +static inline +void +set_tsc_mul ( + const unsigned khz + ) +{ + tsc_ns_mul = (1000000 << TSC_NS_SCALE) / khz; + tsc_us_mul = (1000 << TSC_US_SCALE) / khz; +} + +static inline +uint64_t +tsc_to_ns ( + const uint64_t tsc + ) +{ + return (tsc * tsc_ns_mul) >> TSC_NS_SCALE; +} + +static inline +uint64_t +ns_to_tsc ( + const uint64_t ns + ) +{ + return (ns << TSC_NS_SCALE) / tsc_ns_mul; +} + +static inline +uint64_t +tsc_to_us ( + const uint64_t tsc + ) +{ + return (tsc * tsc_us_mul) >> TSC_US_SCALE; +} + +static inline +uint64_t +us_to_tsc ( + const uint64_t us + ) +{ + return (us << TSC_US_SCALE) / tsc_us_mul; +} + +# ifndef _WIN32 +static bool pgm_tsc_init (pgm_error_t**); +# endif +static pgm_time_t pgm_tsc_update (void); +#endif + + +/* initialize time system. + * + * returns TRUE on success, returns FALSE on error such as being unable to open + * the RTC device, an unstable TSC, or system already initialized. + */ + +bool +pgm_time_init ( + pgm_error_t** error + ) +{ + if (pgm_atomic_exchange_and_add32 (&time_ref_count, 1) > 0) + return TRUE; + +/* current time */ + const char *cfg = getenv ("PGM_TIMER"); + if (cfg == NULL) { +#ifdef CONFIG_HAVE_TSC + cfg = "TSC"; +#else + cfg = "GTOD"; +#endif + } + + pgm_time_since_epoch = pgm_time_conv; + + switch (cfg[0]) { +#ifdef CONFIG_HAVE_FTIME + case 'F': + pgm_minor (_("Using ftime() timer.")); + pgm_time_update_now = pgm_ftime_update; + break; +#endif +#ifdef CONFIG_HAVE_CLOCK_GETTIME + case 'C': + pgm_minor (_("Using clock_gettime() timer.")); + pgm_time_update_now = pgm_clock_update; + break; +#endif +#ifdef CONFIG_HAVE_RTC + case 'R': + pgm_minor (_("Using /dev/rtc timer.")); + pgm_time_update_now = pgm_rtc_update; + pgm_time_since_epoch = pgm_time_conv_from_reset; + break; +#endif +#ifdef CONFIG_HAVE_TSC +# ifdef _WIN32 + default: +# endif + case 'T': + pgm_minor (_("Using TSC timer.")); + pgm_time_update_now = pgm_tsc_update; + pgm_time_since_epoch = pgm_time_conv_from_reset; + break; +#endif +#ifdef CONFIG_HAVE_HPET + case 'H': + pgm_minor (_("Using HPET timer.")); + pgm_time_update_now = pgm_hpet_update; + pgm_time_since_epoch = pgm_time_conv_from_reset; + break; +#endif + +#ifdef CONFIG_HAVE_GETTIMEOFDAY +# ifndef _WIN32 + default: +# endif + case 'G': + pgm_minor (_("Using gettimeofday() timer.")); + pgm_time_update_now = pgm_gettimeofday_update; + break; +#endif + } + +#ifdef CONFIG_HAVE_RTC + if (pgm_time_update_now == pgm_rtc_update) + { + pgm_error_t* sub_error = NULL; + if (!pgm_rtc_init (&sub_error)) { + pgm_propagate_error (error, sub_error); + goto err_cleanup; + } + } +#endif +#ifdef CONFIG_HAVE_TSC + if (pgm_time_update_now == pgm_tsc_update) + { +#ifdef CONFIG_HAVE_PROC +/* attempt to parse clock ticks from kernel + */ + FILE* fp = fopen ("/proc/cpuinfo", "r"); + char buffer[1024]; + if (fp) + { + while (!feof(fp) && fgets (buffer, sizeof(buffer), fp)) + { + if (strstr (buffer, "cpu MHz")) + { + const char *p = strchr (buffer, ':'); + if (p) tsc_mhz = atoi (p + 1); + break; + } + } + fclose (fp); + } +#elif defined(_WIN32) + uint64_t frequency; + if (QueryPerformanceFrequency ((LARGE_INTEGER*)&frequency)) + { + tsc_mhz = frequency / 1000; + } +#endif /* !_WIN32 */ + +/* e.g. export RDTSC_FREQUENCY=3200.000000 + * + * Value can be used to override kernel tick rate as well as internal calibration + */ + const char *env_mhz = getenv ("RDTSC_FREQUENCY"); + if (env_mhz) + tsc_mhz = atoi (env_mhz); + +#ifndef _WIN32 +/* calibrate */ + if (0 >= tsc_mhz) { + pgm_error_t* sub_error = NULL; + if (!pgm_tsc_init (&sub_error)) { + pgm_propagate_error (error, sub_error); + goto err_cleanup; + } + } +#endif + set_tsc_mul (tsc_mhz * 1000); + } +#endif /* CONFIG_HAVE_TSC */ + +#ifdef CONFIG_HAVE_HPET + if (pgm_time_update_now == pgm_hpet_update) + { + pgm_error_t* sub_error = NULL; + if (!pgm_hpet_init (&sub_error)) { + pgm_propagate_error (error, sub_error); + goto err_cleanup; + } + } +#endif + + pgm_time_update_now(); + +/* calculate relative time offset */ +#if defined(CONFIG_HAVE_RTC) || defined(CONFIG_HAVE_TSC) + if ( 0 +# ifdef CONFIG_HAVE_RTC + || pgm_time_update_now == pgm_rtc_update +# endif +# ifdef CONFIG_HAVE_TSC + || pgm_time_update_now == pgm_tsc_update +# endif + ) + { +# if defined( CONFIG_HAVE_GETTIMEOFDAY ) + rel_offset = pgm_gettimeofday_update() - pgm_time_update_now(); +# elif defined( CONFIG_HAVE_FTIME ) + rel_offset = pgm_ftime_update() - pgm_time_update_now(); +# else +# error "gettimeofday() or ftime() required to calculate counter offset" +# endif + } +#else + rel_offset = 0; +#endif + + return TRUE; + +err_cleanup: + pgm_atomic_dec32 (&time_ref_count); + return FALSE; +} + +/* returns TRUE if shutdown succeeded, returns FALSE on error. + */ + +bool +pgm_time_shutdown (void) +{ + pgm_return_val_if_fail (pgm_atomic_read32 (&time_ref_count) > 0, FALSE); + + if (pgm_atomic_exchange_and_add32 (&time_ref_count, (uint32_t)-1) != 1) + return TRUE; + + bool success = TRUE; +#ifdef CONFIG_HAVE_RTC + if (pgm_time_update_now == pgm_rtc_update) + success = pgm_rtc_shutdown (); +#endif +#ifdef CONFIG_HAVE_HPET + if (pgm_time_update_now == pgm_hpet_update) + success = pgm_hpet_shutdown (); +#endif + return success; +} + +#ifdef CONFIG_HAVE_GETTIMEOFDAY +static +pgm_time_t +pgm_gettimeofday_update (void) +{ + struct timeval gettimeofday_now; + static pgm_time_t last = 0; + gettimeofday (&gettimeofday_now, NULL); + const pgm_time_t now = secs_to_usecs (gettimeofday_now.tv_sec) + gettimeofday_now.tv_usec; + if (PGM_UNLIKELY(now < last)) + return last; + else + return last = now; +} +#endif /* CONFIG_HAVE_GETTIMEOFDAY */ + +#ifdef CONFIG_HAVE_CLOCK_GETTIME +static +pgm_time_t +pgm_clock_update (void) +{ + struct timespec clock_now; + static pgm_time_t last = 0; + clock_gettime (CLOCK_MONOTONIC, &clock_now); + const pgm_time_t now = secs_to_usecs (clock_now.tv_sec) + nsecs_to_usecs (clock_now.tv_nsec); + if (PGM_UNLIKELY(now < last)) + return last; + else + return last = now; +} +#endif /* CONFIG_HAVE_CLOCK_GETTIME */ + +#ifdef CONFIG_HAVE_FTIME +static +pgm_time_t +pgm_ftime_update (void) +{ + struct timeb ftime_now; + static pgm_time_t last = 0; + ftime (&ftime_now); + const pgm_time_t now = secs_to_usecs (ftime_now.time) + msecs_to_usecs (ftime_now.millitm); + if (PGM_UNLIKELY(now < last)) + return last; + else + return last = now; +} +#endif /* CONFIG_HAVE_FTIME */ + +#ifdef CONFIG_HAVE_RTC +/* Old PC/AT-Compatible driver: /dev/rtc + * + * Not so speedy 8192 Hz timer, thats 122us resolution. + * + * WARNING: time is relative to start of timer. + * WARNING: only one process is allowed to access the RTC. + */ + +static +bool +pgm_rtc_init ( + pgm_error_t** error + ) +{ + pgm_return_val_if_fail (rtc_fd == -1, FALSE); + + rtc_fd = open ("/dev/rtc", O_RDONLY); + if (-1 == rtc_fd) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_TIME, + PGM_ERROR_FAILED, + _("Cannot open /dev/rtc for reading: %s"), + strerror(errno)); + return FALSE; + } + if (-1 == ioctl (rtc_fd, RTC_IRQP_SET, rtc_frequency)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_TIME, + PGM_ERROR_FAILED, + _("Cannot set RTC frequency to %i Hz: %s"), + rtc_frequency, + strerror(errno)); + return FALSE; + } + if (-1 == ioctl (rtc_fd, RTC_PIE_ON, 0)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_TIME, + PGM_ERROR_FAILED, + _("Cannot enable periodic interrupt (PIE) on RTC: %s"), + strerror(errno)); + return FALSE; + } + return TRUE; +} + +/* returns TRUE on success even if RTC device cannot be closed or had an IO error, + * returns FALSE if the RTC file descriptor is not set. + */ + +static +bool +pgm_rtc_shutdown (void) +{ + pgm_return_val_if_fail (rtc_fd, FALSE); + pgm_warn_if_fail (0 == close (rtc_fd)); + rtc_fd = -1; + return TRUE; +} + +/* RTC only indicates passed ticks therefore is by definition monotonic, we do not + * need to check the difference with respect to the last value. + */ + +static +pgm_time_t +pgm_rtc_update (void) +{ + uint32_t data; + +/* returned value contains interrupt type and count of interrupts since last read */ + pgm_warn_if_fail (sizeof(data) == read (rtc_fd, &data, sizeof(data))); + rtc_count += data >> 8; + return rtc_count * 1000000UL / rtc_frequency; +} +#endif /* CONFIG_HAVE_RTC */ + +#ifdef CONFIG_HAVE_TSC +/* read time stamp counter (TSC), count of ticks from processor reset. + * + * NB: On Windows this will usually be HPET or PIC timer interpolated with TSC. + */ + +static inline +pgm_time_t +rdtsc (void) +{ +# ifndef _WIN32 + uint32_t lo, hi; + +/* We cannot use "=A", since this would use %rax on x86_64 */ + asm volatile ("rdtsc" : "=a" (lo), "=d" (hi)); + + return (pgm_time_t)hi << 32 | lo; +# else + uint64_t counter; + QueryPerformanceCounter ((LARGE_INTEGER*)&counter); + return (pgm_time_t)counter; +# endif +} + +# ifndef _WIN32 +/* determine ratio of ticks to nano-seconds, use /dev/rtc for high accuracy + * millisecond timer and convert. + * + * WARNING: time is relative to start of timer. + */ + +static +bool +pgm_tsc_init ( + PGM_GNUC_UNUSED pgm_error_t** error + ) +{ +# ifdef CONFIG_HAVE_PROC +/* Test for constant TSC from kernel + */ + FILE* fp = fopen ("/proc/cpuinfo", "r"); + char buffer[1024], *flags = NULL; + if (fp) + { + while (!feof(fp) && fgets (buffer, sizeof(buffer), fp)) + { + if (strstr (buffer, "flags")) + { + flags = strchr (buffer, ':'); + break; + } + } + fclose (fp); + } + if (!flags || !strstr (flags, " tsc")) { + pgm_warn (_("Linux kernel reports no Time Stamp Counter (TSC).")); +/* force both to stable clocks even though one might be OK */ + pgm_time_update_now = pgm_gettimeofday_update; + return TRUE; + } + if (!strstr (flags, " constant_tsc")) { + pgm_warn (_("Linux kernel reports non-constant Time Stamp Counter (TSC).")); +/* force both to stable clocks even though one might be OK */ + pgm_time_update_now = pgm_gettimeofday_update; + return TRUE; + } +# endif /* CONFIG_HAVE_PROC */ + pgm_time_t start, stop; + const pgm_time_t calibration_usec = secs_to_usecs (4); + + pgm_info (_("Running a benchmark to measure system clock frequency...")); + + struct timespec req = { + .tv_sec = 4, + .tv_nsec = 0 + }; + start = rdtsc(); + while (-1 == nanosleep (&req, &req) && EINTR == errno); + stop = rdtsc(); + + if (stop < start) + { + pgm_warn (_("Finished RDTSC test. Unstable TSC detected. The benchmark resulted in a " + "non-monotonic time response rendering the TSC unsuitable for high resolution " + "timing. To prevent the start delay from this benchmark and use a stable clock " + "source set the environment variable PGM_TIMER to GTOD.")); +/* force both to stable clocks even though one might be OK */ + pgm_time_update_now = pgm_gettimeofday_update; + return TRUE; + } + +/* TODO: this math needs to be scaled to reduce rounding errors */ + const pgm_time_t tsc_diff = stop - start; + if (tsc_diff > calibration_usec) { +/* cpu > 1 Ghz */ + tsc_mhz = tsc_diff / calibration_usec; + } else { +/* cpu < 1 Ghz */ + tsc_mhz = -( calibration_usec / tsc_diff ); + } + + pgm_info (_("Finished RDTSC test. To prevent the startup delay from this benchmark, " + "set the environment variable RDTSC_FREQUENCY to %" PRIuFAST32 " on this " + "system. This value is dependent upon the CPU clock speed and " + "architecture and should be determined separately for each server."), + tsc_mhz); + return TRUE; +} +# endif + +/* TSC is monotonic on the same core but we do neither force the same core or save the count + * for each core as if the counter is unstable system wide another timing mechanism should be + * used, preferably HPET on x86/AMD64 or gettimeofday() on SPARC. + */ + +static +pgm_time_t +pgm_tsc_update (void) +{ + static pgm_time_t last = 0; + const pgm_time_t now = tsc_to_us (rdtsc()); + if (PGM_UNLIKELY(now < last)) + return last; + else + return last = now; +} +#endif + +#ifdef CONFIG_HAVE_HPET +/* High Precision Event Timer (HPET) created as a system wide stable high resolution timer + * to replace dependency on core specific counters (TSC). + * + * NB: Only available on x86/AMD64 hardware post 2007 + */ + +static +bool +pgm_hpet_init ( + pgm_error_t** error + ) +{ + pgm_return_val_if_fail (hpet_fd == -1, FALSE); + + hpet_fd = open("/dev/hpet", O_RDONLY); + if (hpet_fd < 0) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_TIME, + PGM_ERROR_FAILED, + _("Cannot open /dev/hpet for reading: %s"), + strerror(errno)); + return FALSE; + } + + hpet_ptr = mmap(NULL, HPET_MMAP_SIZE, PROT_READ, MAP_SHARED, hpet_fd, 0); + if (MAP_FAILED == hpet_ptr) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_TIME, + PGM_ERROR_FAILED, + _("Error mapping HPET device: %s"), + strerror(errno)); + close (hpet_fd); + hpet_fd = -1; + return FALSE; + } + +/* HPET counter tick period is in femto-seconds, a value of 0 is not permitted, + * the value must be <= 0x05f5e100 or 100ns. + */ + const uint32_t hpet_period = *((uint32_t*)(hpet_ptr + HPET_COUNTER_CLK_PERIOD)); + set_hpet_mul (hpet_period); +#if defined( __x86_64__ ) || defined( __amd64 ) + const uint32_t hpet_caps = *((uint32_t*)(hpet_ptr + HPET_GENERAL_CAPS_REGISTER)); + hpet_wrap = hpet_caps & HPET_COUNT_SIZE_CAP ? 0 : (1ULL << 32); +#else + hpet_wrap = 1ULL << 32; +#endif + + return TRUE; +} + +static +bool +pgm_hpet_shutdown (void) +{ + pgm_return_val_if_fail (hpet_fd, FALSE); + pgm_warn_if_fail (0 == close (hpet_fd)); + hpet_fd = -1; + return TRUE; +} + +static +pgm_time_t +pgm_hpet_update (void) +{ + const hpet_counter_t hpet_count = *((hpet_counter_t*)(hpet_ptr + HPET_MAIN_COUNTER_REGISTER)); +/* 32-bit HPET counters wrap after ~4 minutes */ + if (PGM_UNLIKELY(hpet_count < hpet_last)) + hpet_offset += hpet_wrap; + hpet_last = hpet_count; + return hpet_to_us (hpet_offset + hpet_count); +} +#endif /* CONFIG_HAVE_HPET */ + +/* convert from pgm_time_t to time_t with pgm_time_t in microseconds since the epoch. + */ +static +void +pgm_time_conv ( + const pgm_time_t* const restrict pgm_time_t_time, + time_t* restrict time_t_time + ) +{ + *time_t_time = pgm_to_secs (*pgm_time_t_time); +} + +/* convert from pgm_time_t to time_t with pgm_time_t in microseconds since the core started. + */ +static +void +pgm_time_conv_from_reset ( + const pgm_time_t* const restrict pgm_time_t_time, + time_t* restrict time_t_time + ) +{ + *time_t_time = pgm_to_secs (*pgm_time_t_time + rel_offset); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/time.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/time.c.c89.patch new file mode 100644 index 0000000..be65fec --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/time.c.c89.patch @@ -0,0 +1,115 @@ +--- time.c 2010-08-04 18:15:50.000000000 +0800 ++++ time.c89 2010-08-05 12:22:53.000000000 +0800 +@@ -232,7 +232,15 @@ + return TRUE; + + /* current time */ ++#ifdef _MSC_VER ++ { ++ char* cfg = NULL; ++ size_t len; ++ errno_t err = _dupenv_s (&cfg, &len, "PGM_TIMER"); ++#else ++ { + const char *cfg = getenv ("PGM_TIMER"); ++#endif + if (cfg == NULL) { + #ifdef CONFIG_HAVE_TSC + cfg = "TSC"; +@@ -292,6 +300,13 @@ + #endif + } + ++#ifdef _MSC_VER ++/* cleanup after _dupenv_s() */ ++ if (!err && len > 0) { ++ free (cfg); ++ } ++#endif ++ + #ifdef CONFIG_HAVE_RTC + if (pgm_time_update_now == pgm_rtc_update) + { +@@ -324,10 +339,12 @@ + fclose (fp); + } + #elif defined(_WIN32) ++ { + uint64_t frequency; + if (QueryPerformanceFrequency ((LARGE_INTEGER*)&frequency)) + { +- tsc_mhz = frequency / 1000; ++ tsc_mhz = (uint_fast32_t)(frequency / 1000); ++ } + } + #endif /* !_WIN32 */ + +@@ -335,10 +352,20 @@ + * + * Value can be used to override kernel tick rate as well as internal calibration + */ ++ { ++#ifdef _MSC_VER ++ char* env_mhz = NULL; ++ size_t len; ++ errno_t err = _dupenv_s (&env_mhz, &len, "RDTSC_FREQUENCY"); ++#else + const char *env_mhz = getenv ("RDTSC_FREQUENCY"); ++#endif + if (env_mhz) + tsc_mhz = atoi (env_mhz); +- ++#ifdef _MSC_VER ++ free (env_mhz); ++#endif ++ } + #ifndef _WIN32 + /* calibrate */ + if (0 >= tsc_mhz) { +@@ -394,6 +421,7 @@ + err_cleanup: + pgm_atomic_dec32 (&time_ref_count); + return FALSE; ++ } + } + + /* returns TRUE if shutdown succeeded, returns FALSE on error. +@@ -407,6 +435,7 @@ + if (pgm_atomic_exchange_and_add32 (&time_ref_count, (uint32_t)-1) != 1) + return TRUE; + ++ { + bool success = TRUE; + #ifdef CONFIG_HAVE_RTC + if (pgm_time_update_now == pgm_rtc_update) +@@ -417,6 +446,7 @@ + success = pgm_hpet_shutdown (); + #endif + return success; ++ } + } + + #ifdef CONFIG_HAVE_GETTIMEOFDAY +@@ -456,14 +486,21 @@ + pgm_time_t + pgm_ftime_update (void) + { +- struct timeb ftime_now; + static pgm_time_t last = 0; ++#ifdef _MSC_VER ++ struct __timeb64 ftime_now; ++ _ftime64_s (&ftime_now); ++#else ++ struct timeb ftime_now; + ftime (&ftime_now); ++#endif ++ { + const pgm_time_t now = secs_to_usecs (ftime_now.time) + msecs_to_usecs (ftime_now.millitm); + if (PGM_UNLIKELY(now < last)) + return last; + else + return last = now; ++ } + } + #endif /* CONFIG_HAVE_FTIME */ + diff --git a/3rdparty/openpgm-svn-r1135/pgm/time_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/time_unittest.c new file mode 100644 index 0000000..fd28572 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/time_unittest.c @@ -0,0 +1,188 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for high resolution timers. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include + + +/* mock state */ + + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + + +#include "time.c" + + +/* target: + * boolean + * pgm_time_init (pgm_error_t** error) + */ + +/* time initialisation uses reference counting */ + +START_TEST (test_init_pass_001) +{ + fail_unless (TRUE == pgm_time_init (NULL), "init #1 failed"); + fail_unless (TRUE == pgm_time_init (NULL), "init #2 failed"); +} +END_TEST + +/* target: + * bool + * pgm_time_shutdown (void) + */ + +START_TEST (test_shutdown_pass_001) +{ + fail_unless (TRUE == pgm_time_init (NULL), "init failed"); + fail_unless (TRUE == pgm_time_shutdown (), "shutdown #1 failed"); + fail_unless (FALSE == pgm_time_shutdown (), "shutdown #2 failed"); +} +END_TEST + +START_TEST (test_shutdown_pass_002) +{ + fail_unless (TRUE == pgm_time_init (NULL), "init #1 failed"); + fail_unless (TRUE == pgm_time_init (NULL), "init #2 failed"); + fail_unless (TRUE == pgm_time_shutdown (), "shutdown #1 failed"); + fail_unless (TRUE == pgm_time_shutdown (), "shutdown #2 failed"); + fail_unless (FALSE == pgm_time_shutdown (), "shutdown #3 failed"); +} +END_TEST + +/* target: + * pgm_time_t + * pgm_time_update_now (void) + */ + +START_TEST (test_update_now_pass_001) +{ + pgm_time_t tstamps[11]; + fail_unless (TRUE == pgm_time_init (NULL), "init failed"); + const pgm_time_t start_time = pgm_time_update_now (); + for (unsigned i = 1; i <= 10; i++) + { + tstamps[i] = pgm_time_update_now(); + } + g_message ("start-time: %" PGM_TIME_FORMAT, start_time); + for (unsigned i = 1; i <= 10; i++) + { + const pgm_time_t check_time = tstamps[i]; + const gint64 elapsed_time = check_time - start_time; + +/* must be monotonic */ + fail_unless (G_LIKELY(check_time >= start_time), "non-monotonic"); + + g_message ("check-point-%2.2u: %" PGM_TIME_FORMAT " (%+" G_GINT64_FORMAT "us)", + i, check_time, pgm_to_usecs(elapsed_time)); + } + fail_unless (TRUE == pgm_time_shutdown (), "shutdown failed"); +} +END_TEST + +/* target: + * void + * pgm_time_since_epoch ( + * pgm_time_t* pgm_time, + * time_t* epoch_time + * ) + */ + +START_TEST (test_since_epoch_pass_001) +{ + char stime[1024]; + time_t t; + struct tm* tmp; + fail_unless (TRUE == pgm_time_init (NULL), "init failed"); + pgm_time_t pgm_now = pgm_time_update_now (); + pgm_time_since_epoch (&pgm_now, &t); + tmp = localtime (&t); + fail_unless (NULL != tmp, "localtime failed"); + fail_unless (0 != strftime (stime, sizeof(stime), "%X", tmp), "strftime failed"); + g_message ("pgm-time:%" PGM_TIME_FORMAT " = %s", + pgm_now, stime); + fail_unless (TRUE == pgm_time_shutdown (), "shutdown failed"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_init = tcase_create ("init"); + suite_add_tcase (s, tc_init); + tcase_add_test (tc_init, test_init_pass_001); + + TCase* tc_shutdown = tcase_create ("shutdown"); + suite_add_tcase (s, tc_shutdown); + tcase_add_test (tc_shutdown, test_shutdown_pass_001); + tcase_add_test (tc_shutdown, test_shutdown_pass_002); + + TCase* tc_update_now = tcase_create ("update-now"); + suite_add_tcase (s, tc_update_now); + tcase_add_test (tc_update_now, test_update_now_pass_001); + + TCase* tc_since_epoch = tcase_create ("since-epoch"); + suite_add_tcase (s, tc_since_epoch); + tcase_add_test (tc_since_epoch, test_since_epoch_pass_001); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/timer.c b/3rdparty/openpgm-svn-r1135/pgm/timer.c new file mode 100644 index 0000000..0df1f66 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/timer.c @@ -0,0 +1,227 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * PGM timer thread. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __STDC_FORMAT_MACROS +#ifdef _MSC_VER +# include +#else +# include +#endif +#include +#include +#include +#include +#include + + +//#define TIMER_DEBUG + + +/* determine which timer fires next: spm (ihb_tmr), nak_rb_ivl, nak_rpt_ivl, or nak_rdata_ivl + * and check whether its already due. + * + * called in sock creation so locks unrequired. + */ + +bool +pgm_timer_prepare ( + pgm_sock_t* const sock + ) +{ + int32_t msec; + +/* pre-conditions */ + pgm_assert (NULL != sock); + pgm_assert (sock->can_send_data || sock->can_recv_data); + + pgm_time_t now = pgm_time_update_now(); + pgm_time_t expiration; + + if (sock->can_send_data) + expiration = sock->next_ambient_spm; + else + expiration = now + sock->peer_expiry; + + sock->next_poll = expiration; + +/* advance time again to adjust for processing time out of the event loop, this + * could cause further timers to expire even before checking for new wire data. + */ + msec = pgm_to_msecs ((int64_t)expiration - (int64_t)now); + if (msec < 0) + msec = 0; + else + msec = MIN (INT32_MAX, msec); + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Next expiration in %" PRIi32 "ms"), msec); + return (msec == 0); +} + +bool +pgm_timer_check ( + pgm_sock_t* const sock + ) +{ + const pgm_time_t now = pgm_time_update_now(); + bool expired; + +/* pre-conditions */ + pgm_assert (NULL != sock); + + pgm_timer_lock (sock); + expired = pgm_time_after_eq (now, sock->next_poll); + pgm_timer_unlock (sock); + return expired; +} + +/* return next timer expiration in microseconds (μs) + */ + +pgm_time_t +pgm_timer_expiration ( + pgm_sock_t* const sock + ) +{ + const pgm_time_t now = pgm_time_update_now(); + pgm_time_t expiration; + +/* pre-conditions */ + pgm_assert (NULL != sock); + + pgm_timer_lock (sock); + expiration = pgm_time_after (sock->next_poll, now) ? pgm_to_usecs (sock->next_poll - now) : 0; + pgm_timer_unlock (sock); + return expiration; +} + +/* call all timers, assume that time_now has been updated by either pgm_timer_prepare + * or pgm_timer_check and no other method calls here. + * + * returns TRUE on success, returns FALSE on blocked send-in-receive operation. + */ + +bool +pgm_timer_dispatch ( + pgm_sock_t* const sock + ) +{ + const pgm_time_t now = pgm_time_update_now(); + pgm_time_t next_expiration = 0; + +/* pre-conditions */ + pgm_assert (NULL != sock); + + pgm_debug ("pgm_timer_dispatch (sock:%p)", (const void*)sock); + +/* find which timers have expired and call each */ + if (sock->can_recv_data) + { + if (!pgm_check_peer_state (sock, now)) + return FALSE; + next_expiration = pgm_min_receiver_expiry (now + sock->peer_expiry, sock); + } + + if (sock->can_send_data) + { +/* reset congestion control on ACK timeout */ + if (sock->use_pgmcc && + sock->tokens < pgm_fp8 (1) && + 0 != sock->ack_expiry) + { + if (pgm_time_after_eq (now, sock->ack_expiry)) + { +#ifdef DEBUG_PGMCC +char nows[1024]; +time_t t = time (NULL); +struct tm* tmp = localtime (&t); +strftime (nows, sizeof(nows), "%Y-%m-%d %H:%M:%S", tmp); +printf ("ACK timeout, T:%u W:%u\n", pgm_fp8tou(sock->tokens), pgm_fp8tou(sock->cwnd_size)); +#endif + sock->tokens = sock->cwnd_size = pgm_fp8 (1); + sock->ack_bitmap = 0xffffffff; + sock->ack_expiry = 0; + +/* notify blocking tx thread that transmission time is now available */ + pgm_notify_send (&sock->ack_notify); + } + next_expiration = next_expiration > 0 ? MIN(next_expiration, sock->ack_expiry) : sock->ack_expiry; + } + +/* SPM broadcast */ + pgm_mutex_lock (&sock->timer_mutex); + const unsigned spm_heartbeat_state = sock->spm_heartbeat_state; + const pgm_time_t next_heartbeat_spm = sock->next_heartbeat_spm; + pgm_mutex_unlock (&sock->timer_mutex); + +/* no lock needed on ambient */ + const pgm_time_t next_ambient_spm = sock->next_ambient_spm; + pgm_time_t next_spm = spm_heartbeat_state ? MIN(next_heartbeat_spm, next_ambient_spm) : next_ambient_spm; + + if (pgm_time_after_eq (now, next_spm) && + !pgm_send_spm (sock, 0)) + return FALSE; + +/* ambient timing not so important so base next event off current time */ + if (pgm_time_after_eq (now, next_ambient_spm)) + { + sock->next_ambient_spm = now + sock->spm_ambient_interval; + next_spm = spm_heartbeat_state ? MIN(next_heartbeat_spm, sock->next_ambient_spm) : sock->next_ambient_spm; + } + +/* heartbeat timing is often high resolution so base times to last event */ + if (spm_heartbeat_state && pgm_time_after_eq (now, next_heartbeat_spm)) + { + unsigned new_heartbeat_state = spm_heartbeat_state; + pgm_time_t new_heartbeat_spm = next_heartbeat_spm; + do { + new_heartbeat_spm += sock->spm_heartbeat_interval[new_heartbeat_state++]; + if (new_heartbeat_state == sock->spm_heartbeat_len) { + new_heartbeat_state = 0; + new_heartbeat_spm = now + sock->spm_ambient_interval; + break; + } + } while (pgm_time_after_eq (now, new_heartbeat_spm)); +/* check for reset heartbeat */ + pgm_mutex_lock (&sock->timer_mutex); + if (next_heartbeat_spm == sock->next_heartbeat_spm) { + sock->spm_heartbeat_state = new_heartbeat_state; + sock->next_heartbeat_spm = new_heartbeat_spm; + next_spm = MIN(sock->next_ambient_spm, new_heartbeat_spm); + } else + next_spm = MIN(sock->next_ambient_spm, sock->next_heartbeat_spm); + sock->next_poll = next_expiration > 0 ? MIN(next_expiration, next_spm) : next_spm; + pgm_mutex_unlock (&sock->timer_mutex); + return TRUE; + } + + next_expiration = next_expiration > 0 ? MIN(next_expiration, next_spm) : next_spm; + +/* check for reset */ + pgm_mutex_lock (&sock->timer_mutex); + sock->next_poll = sock->next_poll > now ? MIN(sock->next_poll, next_expiration) : next_expiration; + pgm_mutex_unlock (&sock->timer_mutex); + } + else + sock->next_poll = next_expiration; + + return TRUE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/timer.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/timer.c.c89.patch new file mode 100644 index 0000000..890f131 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/timer.c.c89.patch @@ -0,0 +1,63 @@ +--- timer.c 2010-08-04 18:16:05.000000000 +0800 ++++ timer.c89 2010-08-05 11:34:01.000000000 +0800 +@@ -52,6 +52,7 @@ + pgm_assert (NULL != sock); + pgm_assert (sock->can_send_data || sock->can_recv_data); + ++ { + pgm_time_t now = pgm_time_update_now(); + pgm_time_t expiration; + +@@ -65,13 +66,14 @@ + /* advance time again to adjust for processing time out of the event loop, this + * could cause further timers to expire even before checking for new wire data. + */ +- msec = pgm_to_msecs ((int64_t)expiration - (int64_t)now); ++ msec = (int32_t)pgm_to_msecs ((int64_t)expiration - (int64_t)now); + if (msec < 0) + msec = 0; + else + msec = MIN (INT32_MAX, msec); + pgm_trace (PGM_LOG_ROLE_NETWORK,_("Next expiration in %" PRIi32 "ms"), msec); + return (msec == 0); ++ } + } + + bool +@@ -148,11 +150,13 @@ + if (pgm_time_after_eq (now, sock->ack_expiry)) + { + #ifdef DEBUG_PGMCC ++{ + char nows[1024]; + time_t t = time (NULL); + struct tm* tmp = localtime (&t); + strftime (nows, sizeof(nows), "%Y-%m-%d %H:%M:%S", tmp); + printf ("ACK timeout, T:%u W:%u\n", pgm_fp8tou(sock->tokens), pgm_fp8tou(sock->cwnd_size)); ++} + #endif + sock->tokens = sock->cwnd_size = pgm_fp8 (1); + sock->ack_bitmap = 0xffffffff; +@@ -166,11 +170,13 @@ + + /* SPM broadcast */ + pgm_mutex_lock (&sock->timer_mutex); ++ { + const unsigned spm_heartbeat_state = sock->spm_heartbeat_state; + const pgm_time_t next_heartbeat_spm = sock->next_heartbeat_spm; + pgm_mutex_unlock (&sock->timer_mutex); + + /* no lock needed on ambient */ ++ { + const pgm_time_t next_ambient_spm = sock->next_ambient_spm; + pgm_time_t next_spm = spm_heartbeat_state ? MIN(next_heartbeat_spm, next_ambient_spm) : next_ambient_spm; + +@@ -217,6 +223,8 @@ + pgm_mutex_lock (&sock->timer_mutex); + sock->next_poll = sock->next_poll > now ? MIN(sock->next_poll, next_expiration) : next_expiration; + pgm_mutex_unlock (&sock->timer_mutex); ++ } ++ } + } + else + sock->next_poll = next_expiration; diff --git a/3rdparty/openpgm-svn-r1135/pgm/timer_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/timer_unittest.c new file mode 100644 index 0000000..2e48802 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/timer_unittest.c @@ -0,0 +1,355 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for PGM timer thread. + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include + + +/* mock state */ + + +#define g_main_context_new mock_g_main_context_new +#define g_main_context_unref mock_g_main_context_unref +#define g_main_loop_new mock_g_main_loop_new +#define g_main_loop_run mock_g_main_loop_run +#define g_main_loop_unref mock_g_main_loop_unref +#define g_source_new mock_g_source_new +#define g_source_set_priority mock_g_source_set_priority +#define g_source_attach mock_g_source_attach +#define g_source_unref mock_g_source_unref +#define pgm_time_now mock_pgm_time_now +#define pgm_time_update_now mock_pgm_time_update_now +#define pgm_min_receiver_expiry mock_pgm_min_receiver_expiry +#define pgm_check_peer_state mock_pgm_check_peer_state +#define pgm_send_spm mock_pgm_send_spm + + +#define TIMER_DEBUG +#include "timer.c" + +static pgm_time_t _mock_pgm_time_update_now(void); +pgm_time_update_func mock_pgm_time_update_now = _mock_pgm_time_update_now; +static pgm_time_t mock_pgm_time_now = 0x1; + + +static +pgm_sock_t* +generate_sock (void) +{ + pgm_sock_t* sock = g_new0 (pgm_sock_t, 1); + return sock; +} + + +/* mock functions for external references */ + +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return 0; +} + +/** GLib */ +static +GMainContext* +mock_g_main_context_new (void) +{ + GMainContext* context = g_malloc0 (sizeof(gpointer)); + return context; +} + +static +GMainLoop* +mock_g_main_loop_new ( + GMainContext* context, + gboolean is_running + ) +{ + g_assert (NULL != context); + GMainLoop* loop = g_malloc0 (sizeof(gpointer)); + return loop; +} + +static +void +mock_g_main_loop_run ( + GMainLoop* loop + ) +{ + g_assert (NULL != loop); +} + +static +void +mock_g_main_loop_unref ( + GMainLoop* loop + ) +{ + g_assert (NULL != loop); + g_free (loop); +} + +static +void +mock_g_main_context_unref ( + GMainContext* context + ) +{ + g_assert (NULL != context); + g_free (context); +} + +static +GSource* +mock_g_source_new ( + GSourceFuncs* source_funcs, + guint struct_size + ) +{ + g_assert (struct_size > 0); + GSource* source = g_malloc0 (struct_size); + return source; +} + +static +void +mock_g_source_set_priority ( + GSource* source, + gint priority + ) +{ + g_assert (NULL != source); +} + +static +guint +mock_g_source_attach ( + GSource* source, + GMainContext* context + ) +{ + g_assert (NULL != source); + return 1; +} + +static +void +mock_g_source_unref ( + GSource* source + ) +{ + g_assert (NULL != source); + g_free (source); +} + +/** time module */ +static +pgm_time_t +_mock_pgm_time_update_now (void) +{ + return mock_pgm_time_now; +} + +/** receiver module */ +PGM_GNUC_INTERNAL +pgm_time_t +mock_pgm_min_receiver_expiry ( + pgm_time_t expiration, + pgm_sock_t* sock + ) +{ + g_assert (NULL != sock); + return 0x1; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_check_peer_state ( + pgm_sock_t* sock, + pgm_time_t now + ) +{ + g_assert (NULL != sock); + return TRUE; +} + +/** source module */ +PGM_GNUC_INTERNAL +bool +mock_pgm_send_spm ( + pgm_sock_t* sock, + int flags + ) +{ + g_assert (NULL != sock); + return TRUE; +} + + +/* target: + * bool + * pgm_timer_prepare ( + * pgm_sock_t* sock + * ) + */ + +START_TEST (test_prepare_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->can_send_data = TRUE; + sock->next_ambient_spm = mock_pgm_time_now + pgm_secs(10); + fail_unless (FALSE == pgm_timer_prepare (sock), "prepare failed"); +} +END_TEST + +START_TEST (test_prepare_fail_001) +{ + gboolean expired = pgm_timer_prepare (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_timer_check ( + * pgm_sock_t* sock + * ) + */ + +START_TEST (test_check_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + fail_unless (TRUE == pgm_timer_check (sock), "check failed"); +} +END_TEST + +START_TEST (test_check_fail_001) +{ + gboolean expired = pgm_timer_check (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * pgm_time_t + * pgm_timer_expiration ( + * pgm_sock_t* sock + * ) + */ + +START_TEST (test_expiration_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + sock->next_poll = mock_pgm_time_now + pgm_secs(300); + fail_unless (pgm_secs(300) == pgm_timer_expiration (sock), "expiration failed"); +} +END_TEST + +START_TEST (test_expiration_fail_001) +{ + long expiration = pgm_timer_expiration (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_timer_dispatch ( + * pgm_sock_t* sock + * ) + */ + +START_TEST (test_dispatch_pass_001) +{ + pgm_sock_t* sock = generate_sock (); + fail_if (NULL == sock, "generate_sock failed"); + pgm_timer_dispatch (sock); +} +END_TEST + +START_TEST (test_dispatch_fail_001) +{ + pgm_timer_dispatch (NULL); + fail ("reached"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_prepare = tcase_create ("prepare"); + suite_add_tcase (s, tc_prepare); + tcase_add_test (tc_prepare, test_prepare_pass_001); + tcase_add_test_raise_signal (tc_prepare, test_prepare_fail_001, SIGABRT); + + TCase* tc_check = tcase_create ("check"); + suite_add_tcase (s, tc_check); + tcase_add_test (tc_check, test_check_pass_001); + tcase_add_test_raise_signal (tc_check, test_check_fail_001, SIGABRT); + + TCase* tc_expiration = tcase_create ("expiration"); + suite_add_tcase (s, tc_expiration); + tcase_add_test (tc_expiration, test_expiration_pass_001); + tcase_add_test_raise_signal (tc_expiration, test_expiration_fail_001, SIGABRT); + + TCase* tc_dispatch = tcase_create ("dispatch"); + suite_add_tcase (s, tc_dispatch); + tcase_add_test (tc_dispatch, test_dispatch_pass_001); + tcase_add_test_raise_signal (tc_dispatch, test_dispatch_fail_001, SIGABRT); + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/token and leaky bucket.txt b/3rdparty/openpgm-svn-r1135/pgm/token and leaky bucket.txt new file mode 100644 index 0000000..3efb9c7 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/token and leaky bucket.txt @@ -0,0 +1,12 @@ +leaky bucket: + + if (BUCKET->rate_limit > BUCKET->rate_per_sec) + BUCKET->rate_limit = BUCKET->rate_per_sec; + + +token bucket: + + guint bucket_limit; + + if (BUCKET->rate_limit > BUCKET->bucket_limit) + BUCKET->rate_limit = BUCKET->bucket_limit; diff --git a/3rdparty/openpgm-svn-r1135/pgm/tsi.c b/3rdparty/openpgm-svn-r1135/pgm/tsi.c new file mode 100644 index 0000000..5f9ae85 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/tsi.c @@ -0,0 +1,119 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * transport session ID helper functions. + * + * Copyright (c) 2006-2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + + +//#define TSI_DEBUG + + +/* locals */ + + +/* re-entrant form of pgm_tsi_print() + * + * returns number of bytes written to buffer on success, returns -1 on + * invalid parameters. + */ + +int +pgm_tsi_print_r ( + const pgm_tsi_t* restrict tsi, + char* restrict buf, + size_t bufsize + ) +{ + pgm_return_val_if_fail (NULL != tsi, -1); + pgm_return_val_if_fail (NULL != buf, -1); + pgm_return_val_if_fail (bufsize > 0, -1); + + const uint8_t* gsi = (const uint8_t*)tsi; + const uint16_t source_port = tsi->sport; + + return snprintf (buf, bufsize, "%i.%i.%i.%i.%i.%i.%i", + gsi[0], gsi[1], gsi[2], gsi[3], gsi[4], gsi[5], ntohs (source_port)); +} + +/* transform TSI to ASCII string form. + * + * on success, returns pointer to ASCII string. on error, returns NULL. + */ + +char* +pgm_tsi_print ( + const pgm_tsi_t* tsi + ) +{ + pgm_return_val_if_fail (tsi != NULL, NULL); + + static char buf[PGM_TSISTRLEN]; + pgm_tsi_print_r (tsi, buf, sizeof(buf)); + return buf; +} + +/* create hash value of TSI for use with GLib hash tables. + * + * on success, returns a hash value corresponding to the TSI. on error, fails + * on assert. + */ + +pgm_hash_t +pgm_tsi_hash ( + const void* p + ) +{ + const union { + pgm_tsi_t tsi; + uint32_t l[2]; + } *u = p; + +/* pre-conditions */ + pgm_assert (NULL != p); + + return u->l[0] ^ u->l[1]; +} + +/* compare two transport session identifier TSI values. + * + * returns TRUE if they are equal, FALSE if they are not. + */ + +bool +pgm_tsi_equal ( + const void* restrict p1, + const void* restrict p2 + ) +{ + const union { + pgm_tsi_t tsi; + uint32_t l[2]; + uint64_t ll; + } *restrict u1 = p1, *restrict u2 = p2; + +/* pre-conditions */ + pgm_assert (NULL != p1); + pgm_assert (NULL != p2); + + return (u1->l[0] == u2->l[0] && u1->l[1] == u2->l[1]); +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/tsi.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/tsi.c.c89.patch new file mode 100644 index 0000000..ff95ba8 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/tsi.c.c89.patch @@ -0,0 +1,33 @@ +--- tsi.c 2010-05-21 11:32:16.000000000 +0800 ++++ tsi.c89 2010-08-05 11:01:48.000000000 +0800 +@@ -46,11 +46,18 @@ + pgm_return_val_if_fail (NULL != buf, -1); + pgm_return_val_if_fail (bufsize > 0, -1); + ++ { + const uint8_t* gsi = (const uint8_t*)tsi; + const uint16_t source_port = tsi->sport; + ++#ifdef _MSC_VER ++ return _snprintf_s (buf, bufsize, _TRUNCATE, "%i.%i.%i.%i.%i.%i.%i", ++ gsi[0], gsi[1], gsi[2], gsi[3], gsi[4], gsi[5], ntohs (source_port)); ++#else + return snprintf (buf, bufsize, "%i.%i.%i.%i.%i.%i.%i", + gsi[0], gsi[1], gsi[2], gsi[3], gsi[4], gsi[5], ntohs (source_port)); ++#endif ++ } + } + + /* transform TSI to ASCII string form. +@@ -65,9 +72,11 @@ + { + pgm_return_val_if_fail (tsi != NULL, NULL); + ++ { + static char buf[PGM_TSISTRLEN]; + pgm_tsi_print_r (tsi, buf, sizeof(buf)); + return buf; ++ } + } + + /* create hash value of TSI for use with GLib hash tables. diff --git a/3rdparty/openpgm-svn-r1135/pgm/tsi_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/tsi_unittest.c new file mode 100644 index 0000000..fddff25 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/tsi_unittest.c @@ -0,0 +1,185 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for transport session ID helper functions. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include + + +/* mock state */ + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + + +#define TSI_DEBUG +#include "tsi.c" + + +/* target: + * gchar* + * pgm_tsi_print ( + * const pgm_tsi_t* tsi + * ) + */ + +START_TEST (test_print_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + fail_if (NULL == pgm_tsi_print (&tsi), "print failed"); +} +END_TEST + +START_TEST (test_print_pass_002) +{ + fail_unless (NULL == pgm_tsi_print (NULL), "print failed"); +} +END_TEST + +/* target: + * int + * pgm_tsi_print_r ( + * const pgm_tsi_t* tsi, + * char* buf, + * gsize bufsize + * ) + */ + +START_TEST (test_print_r_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + char buf[PGM_TSISTRLEN]; + fail_unless (pgm_tsi_print_r (&tsi, buf, sizeof(buf)) > 0, "print_r failed"); +} +END_TEST + +START_TEST (test_print_r_pass_002) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + char buf[PGM_TSISTRLEN]; + fail_unless (pgm_tsi_print_r (NULL, buf, sizeof(buf)) == -1, "print_r failed"); + fail_unless (pgm_tsi_print_r (&tsi, NULL, sizeof(buf)) == -1, "print_r failed"); + fail_unless (pgm_tsi_print_r (&tsi, buf, 0) == -1, "print_r failed"); +} +END_TEST + +/* target: + * gboolean + * pgm_tsi_equal ( + * gconstpointer tsi1, + * gconstpointer tsi2 + * ) + */ + +START_TEST (test_equal_pass_001) +{ + const pgm_tsi_t tsi1 = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const pgm_tsi_t tsi2 = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + fail_unless (pgm_tsi_equal (&tsi1, &tsi2), "equal failed"); +} +END_TEST + +START_TEST (test_equal_pass_002) +{ + const pgm_tsi_t tsi1 = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const pgm_tsi_t tsi2 = { { 9, 8, 7, 6, 5, 4 }, 2000 }; + fail_if (pgm_tsi_equal (&tsi1, &tsi2), "equal failed"); +} +END_TEST + +START_TEST (test_equal_fail_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + gboolean retval = pgm_tsi_equal (NULL, &tsi); + fail ("reached"); +} +END_TEST + +START_TEST (test_equal_fail_002) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + gboolean retval = pgm_tsi_equal (&tsi, NULL); + fail ("reached"); +} +END_TEST + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_print = tcase_create ("print"); + suite_add_tcase (s, tc_print); + tcase_add_test (tc_print, test_print_pass_001); + tcase_add_test (tc_print, test_print_pass_002); + + TCase* tc_print_r = tcase_create ("print-r"); + suite_add_tcase (s, tc_print_r); + tcase_add_test (tc_print_r, test_print_r_pass_001); + tcase_add_test (tc_print_r, test_print_r_pass_002); + + TCase* tc_equal = tcase_create ("equal"); + suite_add_tcase (s, tc_equal); + tcase_add_test (tc_equal, test_equal_pass_001); + tcase_add_test (tc_equal, test_equal_pass_002); + tcase_add_test_raise_signal (tc_equal, test_equal_fail_001, SIGABRT); + tcase_add_test_raise_signal (tc_equal, test_equal_fail_002, SIGABRT); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/txw.c b/3rdparty/openpgm-svn-r1135/pgm/txw.c new file mode 100644 index 0000000..36f4c53 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/txw.c @@ -0,0 +1,767 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * A basic transmit window: pointer array implementation. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define __STDC_FORMAT_MACROS +#ifdef _MSC_VER +# include +#else +# include +#endif +#include +#include +#include + + +//#define TXW_DEBUG + +#ifndef TXW_DEBUG +# define PGM_DISABLE_ASSERT +#endif + + +/* testing function: is TSI null + * + * returns TRUE if null, returns FALSE if not null. + */ + +static inline +bool +pgm_tsi_is_null ( + const void*const tsi + ) +{ + const union { + pgm_tsi_t tsi; + uint32_t l[2]; + } *u = tsi; + +/* pre-conditions */ + pgm_assert (NULL != tsi); + + return (0 == u->l[0] && 0 == u->l[1]); +} + +/* returns the pointer at the given index of the window. responsibility + * is with the caller to verify a single user ownership. + */ + +static inline +struct pgm_sk_buff_t* +_pgm_txw_peek ( + const pgm_txw_t*const window, + const uint32_t sequence + ) +{ + struct pgm_sk_buff_t* skb; + +/* pre-conditions */ + pgm_assert (NULL != window); + + if (pgm_txw_is_empty (window)) + return NULL; + + if (pgm_uint32_gte (sequence, window->trail) && pgm_uint32_lte (sequence, window->lead)) + { + const uint_fast32_t index_ = sequence % pgm_txw_max_length (window); + skb = window->pdata[index_]; + pgm_assert (NULL != skb); + pgm_assert (pgm_skb_is_valid (skb)); + pgm_assert (pgm_tsi_is_null (&skb->tsi)); + } + else + skb = NULL; + + return skb; +} + +/* testing function: can a request be peeked from the retransmit queue. + * + * returns TRUE if request is available, returns FALSE if not available. + */ + +static inline +bool +pgm_txw_retransmit_can_peek ( + pgm_txw_t*const window + ) +{ + pgm_return_val_if_fail (NULL != window, FALSE); + return (NULL != pgm_txw_retransmit_try_peek (window)); +} + +/* sequence state must be smaller than PGM skbuff control buffer */ +PGM_STATIC_ASSERT(sizeof(struct pgm_txw_state_t) <= sizeof(((struct pgm_sk_buff_t*)0)->cb)); + +uint32_t +pgm_txw_get_unfolded_checksum ( + const struct pgm_sk_buff_t*const skb + ) +{ + const pgm_txw_state_t*const state = (const pgm_txw_state_t*const)&skb->cb; + return state->unfolded_checksum; +} + +void +pgm_txw_set_unfolded_checksum ( + struct pgm_sk_buff_t*const skb, + const uint32_t csum + ) +{ + pgm_txw_state_t* state = (pgm_txw_state_t*)&skb->cb; + state->unfolded_checksum = csum; +} + +void +pgm_txw_inc_retransmit_count ( + struct pgm_sk_buff_t*const skb + ) +{ + pgm_txw_state_t*const state = (pgm_txw_state_t*const)&skb->cb; + state->retransmit_count++; +} + +bool +pgm_txw_retransmit_is_empty ( + const pgm_txw_t*const window + ) +{ + pgm_assert (NULL != window); + return pgm_queue_is_empty (&window->retransmit_queue); +} + + +/* globals */ + +static void pgm_txw_remove_tail (pgm_txw_t*const); +static bool pgm_txw_retransmit_push_parity (pgm_txw_t*const, const uint32_t, const uint8_t); +static bool pgm_txw_retransmit_push_selective (pgm_txw_t*const, const uint32_t); + + +/* constructor for transmit window. zero-length windows are not permitted. + * + * returns pointer to window. + */ + +pgm_txw_t* +pgm_txw_create ( + const pgm_tsi_t*const tsi, + const uint16_t tpdu_size, + const uint32_t sqns, /* transmit window size in sequence numbers */ + const unsigned secs, /* size in seconds */ + const ssize_t max_rte, /* max bandwidth */ + const bool use_fec, + const uint8_t rs_n, + const uint8_t rs_k + ) +{ + pgm_txw_t* window; + +/* pre-conditions */ + pgm_assert (NULL != tsi); + if (sqns) { + pgm_assert_cmpuint (tpdu_size, ==, 0); + pgm_assert_cmpuint (sqns, >, 0); + pgm_assert_cmpuint (sqns & PGM_UINT32_SIGN_BIT, ==, 0); + pgm_assert_cmpuint (secs, ==, 0); + pgm_assert_cmpuint (max_rte, ==, 0); + } else { + pgm_assert_cmpuint (tpdu_size, >, 0); + pgm_assert_cmpuint (secs, >, 0); + pgm_assert_cmpuint (max_rte, >, 0); + } + if (use_fec) { + pgm_assert_cmpuint (rs_n, >, 0); + pgm_assert_cmpuint (rs_k, >, 0); + } + + pgm_debug ("create (tsi:%s max-tpdu:%" PRIu16 " sqns:%" PRIu32 " secs %u max-rte %zd use-fec:%s rs(n):%u rs(k):%u)", + pgm_tsi_print (tsi), + tpdu_size, sqns, secs, max_rte, + use_fec ? "YES" : "NO", + rs_n, rs_k); + +/* calculate transmit window parameters */ + pgm_assert (sqns || (tpdu_size && secs && max_rte)); + const unsigned alloc_sqns = sqns ? sqns : ( (secs * max_rte) / tpdu_size ); + window = pgm_malloc0 (sizeof(pgm_txw_t) + ( alloc_sqns * sizeof(struct pgm_sk_buff_t*) )); + window->tsi = tsi; + +/* empty state for transmission group boundaries to align. + * + * trail = 0, lead = -1 + */ + window->lead = -1; + window->trail = window->lead + 1; + +/* reed-solomon forward error correction */ + if (use_fec) { + window->parity_buffer = pgm_alloc_skb (tpdu_size); + window->tg_sqn_shift = pgm_power2_log2 (rs_k); + pgm_rs_create (&window->rs, rs_n, rs_k); + window->is_fec_enabled = 1; + } + +/* pointer array */ + window->alloc = alloc_sqns; + +/* post-conditions */ + pgm_assert_cmpuint (pgm_txw_max_length (window), ==, alloc_sqns); + pgm_assert_cmpuint (pgm_txw_length (window), ==, 0); + pgm_assert_cmpuint (pgm_txw_size (window), ==, 0); + pgm_assert (pgm_txw_is_empty (window)); + pgm_assert (!pgm_txw_is_full (window)); + pgm_assert (!pgm_txw_retransmit_can_peek (window)); + + return window; +} + +/* destructor for transmit window. must not be called more than once for same window. + */ + +void +pgm_txw_shutdown ( + pgm_txw_t*const window + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert_cmpuint (window->alloc, >, 0); + + pgm_debug ("shutdown (window:%p)", (const void*)window); + +/* contents of window */ + while (!pgm_txw_is_empty (window)) { + pgm_txw_remove_tail (window); + } + +/* window must now be empty */ + pgm_assert_cmpuint (pgm_txw_length (window), ==, 0); + pgm_assert_cmpuint (pgm_txw_size (window), ==, 0); + pgm_assert (pgm_txw_is_empty (window)); + pgm_assert (!pgm_txw_is_full (window)); + +/* retransmit queue must be empty */ + pgm_assert (!pgm_txw_retransmit_can_peek (window)); + +/* free reed-solomon state */ + if (window->is_fec_enabled) { + pgm_free_skb (window->parity_buffer); + pgm_rs_destroy (&window->rs); + } + +/* window */ + pgm_free (window); +} + +/* add skb to transmit window, taking ownership. window does not grow. + * PGM skbuff data/tail pointers must point to the PGM payload, and hence skb->len + * is allowed to be zero. + * + * side effects: + * + * 1) sequence number is set in skb. + * 2) window is updated with new skb. + * + * no return value. fatal error raised on invalid parameters. if window is full then + * an entry is dropped to fulfil the request. + * + * it is an error to try to free the skb after adding to the window. + */ + +void +pgm_txw_add ( + pgm_txw_t* const restrict window, + struct pgm_sk_buff_t* const restrict skb /* cannot be NULL */ + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (NULL != skb); + pgm_assert_cmpuint (pgm_txw_max_length (window), >, 0); + pgm_assert (pgm_skb_is_valid (skb)); + pgm_assert (((const pgm_list_t*)skb)->next == NULL); + pgm_assert (((const pgm_list_t*)skb)->prev == NULL); + pgm_assert (pgm_tsi_is_null (&skb->tsi)); + pgm_assert ((char*)skb->data > (char*)skb->head); + pgm_assert ((sizeof(struct pgm_header) + sizeof(struct pgm_data)) <= (size_t)((char*)skb->data - (char*)skb->head)); + + pgm_debug ("add (window:%p skb:%p)", (const char*)window, (const char*)skb); + + if (pgm_txw_is_full (window)) + { +/* transmit window advancement scheme dependent action here */ + pgm_txw_remove_tail (window); + } + +/* generate new sequence number */ + pgm_atomic_inc32 (&window->lead); + skb->sequence = window->lead; + +/* add skb to window */ + const uint_fast32_t index_ = skb->sequence % pgm_txw_max_length (window); + window->pdata[index_] = skb; + +/* statistics */ + window->size += skb->len; + +/* post-conditions */ + pgm_assert_cmpuint (pgm_txw_length (window), >, 0); + pgm_assert_cmpuint (pgm_txw_length (window), <=, pgm_txw_max_length (window)); +} + +/* peek an entry from the window for retransmission. + * + * returns pointer to skbuff on success, returns NULL on invalid parameters. + */ + +struct pgm_sk_buff_t* +pgm_txw_peek ( + const pgm_txw_t*const window, + const uint32_t sequence + ) +{ + pgm_debug ("peek (window:%p sequence:%" PRIu32 ")", + (const void*)window, sequence); + return _pgm_txw_peek (window, sequence); +} + +/* remove an entry from the trailing edge of the transmit window. + */ + +static +void +pgm_txw_remove_tail ( + pgm_txw_t* const window + ) +{ + struct pgm_sk_buff_t* skb; + pgm_txw_state_t* state; + + pgm_debug ("pgm_txw_remove_tail (window:%p)", (const void*)window); + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert (!pgm_txw_is_empty (window)); + + skb = _pgm_txw_peek (window, pgm_txw_trail (window)); + pgm_assert (NULL != skb); + pgm_assert (pgm_skb_is_valid (skb)); + pgm_assert (pgm_tsi_is_null (&skb->tsi)); + + state = (pgm_txw_state_t*)&skb->cb; + if (state->waiting_retransmit) { + pgm_queue_unlink (&window->retransmit_queue, (pgm_list_t*)skb); + state->waiting_retransmit = 0; + } + +/* statistics */ + window->size -= skb->len; + if (state->retransmit_count > 0) { + PGM_HISTOGRAM_COUNTS("Tx.RetransmitCount", state->retransmit_count); + } + if (state->nak_elimination_count > 0) { + PGM_HISTOGRAM_COUNTS("Tx.NakEliminationCount", state->nak_elimination_count); + } + +/* remove reference to skb */ + if (PGM_UNLIKELY(pgm_mem_gc_friendly)) { + const uint_fast32_t index_ = skb->sequence % pgm_txw_max_length (window); + window->pdata[index_] = NULL; + } + pgm_free_skb (skb); + +/* advance trailing pointer */ + pgm_atomic_inc32 (&window->trail); + +/* post-conditions */ + pgm_assert (!pgm_txw_is_full (window)); +} + +/* Try to add a sequence number to the retransmit queue, ignore if + * already there or no longer in the transmit window. + * + * For parity NAKs, we deal on the transmission group sequence number + * rather than the packet sequence number. To simplify managment we + * use the leading window packet to store the details of the entire + * transmisison group. Parity NAKs are ignored if the packet count is + * less than or equal to the count already queued for retransmission. + * + * returns FALSE if request was eliminated, returns TRUE if request was + * added to queue. + */ + +bool +pgm_txw_retransmit_push ( + pgm_txw_t* const window, + const uint32_t sequence, + const bool is_parity, /* parity NAK ⇒ sequence_number = transmission group | packet count */ + const uint8_t tg_sqn_shift + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert_cmpuint (tg_sqn_shift, <, 8 * sizeof(uint32_t)); + + pgm_debug ("retransmit_push (window:%p sequence:%" PRIu32 " is_parity:%s tg_sqn_shift:%u)", + (const void*)window, sequence, is_parity ? "TRUE" : "FALSE", tg_sqn_shift); + +/* early elimination */ + if (pgm_txw_is_empty (window)) + return FALSE; + + if (is_parity) + { + return pgm_txw_retransmit_push_parity (window, sequence, tg_sqn_shift); + } + else + { + return pgm_txw_retransmit_push_selective (window, sequence); + } +} + +static +bool +pgm_txw_retransmit_push_parity ( + pgm_txw_t* const window, + const uint32_t sequence, + const uint8_t tg_sqn_shift + ) +{ + struct pgm_sk_buff_t* skb; + pgm_txw_state_t* state; + +/* pre-conditions */ + pgm_assert (NULL != window); + pgm_assert_cmpuint (tg_sqn_shift, <, 8 * sizeof(uint32_t)); + + const uint32_t tg_sqn_mask = 0xffffffff << tg_sqn_shift; + const uint32_t nak_tg_sqn = sequence & tg_sqn_mask; /* left unshifted */ + const uint32_t nak_pkt_cnt = sequence & ~tg_sqn_mask; + skb = _pgm_txw_peek (window, nak_tg_sqn); + + if (NULL == skb) { + pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Transmission group lead #%" PRIu32 " not in window."), nak_tg_sqn); + return FALSE; + } + + pgm_assert (pgm_skb_is_valid (skb)); + pgm_assert (pgm_tsi_is_null (&skb->tsi)); + state = (pgm_txw_state_t*)&skb->cb; + +/* check if request can be eliminated */ + if (state->waiting_retransmit) + { + pgm_assert (NULL != ((const pgm_list_t*)skb)->next); + pgm_assert (NULL != ((const pgm_list_t*)skb)->prev); + if (state->pkt_cnt_requested < nak_pkt_cnt) { +/* more parity packets requested than currently scheduled, simply bump up the count */ + state->pkt_cnt_requested = nak_pkt_cnt; + } + state->nak_elimination_count++; + return FALSE; + } + else + { + pgm_assert (((const pgm_list_t*)skb)->next == NULL); + pgm_assert (((const pgm_list_t*)skb)->prev == NULL); + } + +/* new request */ + state->pkt_cnt_requested++; + pgm_queue_push_head_link (&window->retransmit_queue, (pgm_list_t*)skb); + pgm_assert (!pgm_queue_is_empty (&window->retransmit_queue)); + state->waiting_retransmit = 1; + return TRUE; +} + +static +bool +pgm_txw_retransmit_push_selective ( + pgm_txw_t* const window, + const uint32_t sequence + ) +{ + struct pgm_sk_buff_t* skb; + pgm_txw_state_t* state; + +/* pre-conditions */ + pgm_assert (NULL != window); + + skb = _pgm_txw_peek (window, sequence); + if (NULL == skb) { + pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Requested packet #%" PRIu32 " not in window."), sequence); + return FALSE; + } + + pgm_assert (pgm_skb_is_valid (skb)); + pgm_assert (pgm_tsi_is_null (&skb->tsi)); + state = (pgm_txw_state_t*)&skb->cb; + +/* check if request can be eliminated */ + if (state->waiting_retransmit) { + pgm_assert (!pgm_queue_is_empty (&window->retransmit_queue)); + state->nak_elimination_count++; + return FALSE; + } + + pgm_assert (((const pgm_list_t*)skb)->next == NULL); + pgm_assert (((const pgm_list_t*)skb)->prev == NULL); + +/* new request */ + pgm_queue_push_head_link (&window->retransmit_queue, (pgm_list_t*)skb); + pgm_assert (!pgm_queue_is_empty (&window->retransmit_queue)); + state->waiting_retransmit = 1; + return TRUE; +} + +/* try to peek a request from the retransmit queue + * + * return pointer of first skb in queue, or return NULL if the queue is empty. + */ + +struct pgm_sk_buff_t* +pgm_txw_retransmit_try_peek ( + pgm_txw_t* const window + ) +{ +/* pre-conditions */ + pgm_assert (NULL != window); + + pgm_debug ("retransmit_try_peek (window:%p)", (const void*)window); + +/* no lock required to detect presence of a request */ + pgm_list_t* tail_link = pgm_queue_peek_tail_link (&window->retransmit_queue); + if (PGM_UNLIKELY(NULL == tail_link)) { + pgm_debug ("retransmit queue empty on peek."); + return NULL; + } + + struct pgm_sk_buff_t* skb = (struct pgm_sk_buff_t*)tail_link; + pgm_assert (pgm_skb_is_valid (skb)); + pgm_txw_state_t* state = (pgm_txw_state_t*)&skb->cb; + + if (!state->waiting_retransmit) { + pgm_assert (((const pgm_list_t*)skb)->next == NULL); + pgm_assert (((const pgm_list_t*)skb)->prev == NULL); + } +/* packet payload still in transit */ + if (PGM_UNLIKELY(1 != pgm_atomic_read32 (&skb->users))) { + pgm_trace (PGM_LOG_ROLE_TX_WINDOW,_("Retransmit sqn #%" PRIu32 " is still in transit in transmit thread."), skb->sequence); + return NULL; + } + if (!state->pkt_cnt_requested) { + return skb; + } + +/* generate parity packet to satisify request */ + const uint8_t rs_h = state->pkt_cnt_sent % (window->rs.n - window->rs.k); + const uint32_t tg_sqn_mask = 0xffffffff << window->tg_sqn_shift; + const uint32_t tg_sqn = skb->sequence & tg_sqn_mask; + bool is_var_pktlen = FALSE; + bool is_op_encoded = FALSE; + uint16_t parity_length = 0; + const pgm_gf8_t* src[ window->rs.k ]; + for (uint_fast8_t i = 0; i < window->rs.k; i++) + { + const struct pgm_sk_buff_t* odata_skb = pgm_txw_peek (window, tg_sqn + i); + const uint16_t odata_tsdu_length = ntohs (odata_skb->pgm_header->pgm_tsdu_length); + if (!parity_length) + { + parity_length = odata_tsdu_length; + } + else if (odata_tsdu_length != parity_length) + { + is_var_pktlen = TRUE; + if (odata_tsdu_length > parity_length) + parity_length = odata_tsdu_length; + } + + src[i] = odata_skb->data; + if (odata_skb->pgm_header->pgm_options & PGM_OPT_PRESENT) { + is_op_encoded = TRUE; + } + } + +/* construct basic PGM header to be completed by send_rdata() */ + skb = window->parity_buffer; + skb->data = skb->tail = skb->head = skb + 1; + +/* space for PGM header */ + pgm_skb_put (skb, sizeof(struct pgm_header)); + + skb->pgm_header = skb->data; + skb->pgm_data = (void*)( skb->pgm_header + 1 ); + memcpy (skb->pgm_header->pgm_gsi, &window->tsi->gsi, sizeof(pgm_gsi_t)); + skb->pgm_header->pgm_options = PGM_OPT_PARITY; + +/* append actual TSDU length if variable length packets, zero pad as necessary. + */ + if (is_var_pktlen) + { + skb->pgm_header->pgm_options |= PGM_OPT_VAR_PKTLEN; + + for (uint_fast8_t i = 0; i < window->rs.k; i++) + { + struct pgm_sk_buff_t* odata_skb = pgm_txw_peek (window, tg_sqn + i); + const uint16_t odata_tsdu_length = ntohs (odata_skb->pgm_header->pgm_tsdu_length); + + pgm_assert (odata_tsdu_length == odata_skb->len); + pgm_assert (parity_length >= odata_tsdu_length); + + if (!odata_skb->zero_padded) { + memset (odata_skb->tail, 0, parity_length - odata_tsdu_length); + *(uint16_t*)((char*)odata_skb->data + parity_length) = odata_tsdu_length; + odata_skb->zero_padded = 1; + } + } + parity_length += 2; + } + + skb->pgm_header->pgm_tsdu_length = htons (parity_length); + +/* space for DATA */ + pgm_skb_put (skb, sizeof(struct pgm_data) + parity_length); + + skb->pgm_data->data_sqn = htonl ( tg_sqn | rs_h ); + + void* data_bytes = skb->pgm_data + 1; + +/* encode every option separately, currently only one applies: opt_fragment + */ + if (is_op_encoded) + { + skb->pgm_header->pgm_options |= PGM_OPT_PRESENT; + + struct pgm_opt_fragment null_opt_fragment; + const pgm_gf8_t* opt_src[ window->rs.k ]; + memset (&null_opt_fragment, 0, sizeof(null_opt_fragment)); + *(uint8_t*)&null_opt_fragment |= PGM_OP_ENCODED_NULL; + for (uint_fast8_t i = 0; i < window->rs.k; i++) + { + const struct pgm_sk_buff_t* odata_skb = pgm_txw_peek (window, tg_sqn + i); + + if (odata_skb->pgm_opt_fragment) + { + pgm_assert (odata_skb->pgm_header->pgm_options & PGM_OPT_PRESENT); +/* skip three bytes of header */ + opt_src[i] = (pgm_gf8_t*)((char*)odata_skb->pgm_opt_fragment + sizeof (struct pgm_opt_header)); + } + else + { + opt_src[i] = (pgm_gf8_t*)&null_opt_fragment; + } + } + +/* add options to this rdata packet */ + const uint16_t opt_total_length = sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment); + +/* add space for PGM options */ + pgm_skb_put (skb, opt_total_length); + + struct pgm_opt_length* opt_len = data_bytes; + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( opt_total_length ); + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_FRAGMENT | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_fragment); + opt_header->opt_reserved = PGM_OP_ENCODED; + struct pgm_opt_fragment* opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); + +/* The cast below is the correct way to handle the problem. + * The (void *) cast is to avoid a GCC warning like: + * + * "warning: dereferencing type-punned pointer will break strict-aliasing rules" + */ + pgm_rs_encode (&window->rs, + opt_src, + window->rs.k + rs_h, + (pgm_gf8_t*)((char*)opt_fragment + sizeof(struct pgm_opt_header)), + sizeof(struct pgm_opt_fragment) - sizeof(struct pgm_opt_header)); + + data_bytes = opt_fragment + 1; + } + +/* encode payload */ + pgm_rs_encode (&window->rs, + src, + window->rs.k + rs_h, + data_bytes, + parity_length); + +/* calculate partial checksum */ + const uint16_t tsdu_length = ntohs (skb->pgm_header->pgm_tsdu_length); + state->unfolded_checksum = pgm_csum_partial ((char*)skb->tail - tsdu_length, tsdu_length, 0); + return skb; +} + +/* remove head entry from retransmit queue, will fail on assertion if queue is empty. + */ + +void +pgm_txw_retransmit_remove_head ( + pgm_txw_t* const window + ) +{ + struct pgm_sk_buff_t* skb; + pgm_txw_state_t* state; + +/* pre-conditions */ + pgm_assert (NULL != window); + + pgm_debug ("retransmit_remove_head (window:%p)", + (const void*)window); + +/* tail link is valid without lock */ + pgm_list_t* tail_link = pgm_queue_peek_tail_link (&window->retransmit_queue); + +/* link must be valid for pop */ + pgm_assert (NULL != tail_link); + + skb = (struct pgm_sk_buff_t*)tail_link; + pgm_assert (pgm_skb_is_valid (skb)); + pgm_assert (pgm_tsi_is_null (&skb->tsi)); + state = (pgm_txw_state_t*)&skb->cb; + if (!state->waiting_retransmit) + { + pgm_assert (((const pgm_list_t*)skb)->next == NULL); + pgm_assert (((const pgm_list_t*)skb)->prev == NULL); + } + if (state->pkt_cnt_requested) + { + state->pkt_cnt_sent++; + +/* remove if all requested parity packets have been sent */ + if (state->pkt_cnt_sent == state->pkt_cnt_requested) { + pgm_queue_pop_tail_link (&window->retransmit_queue); + state->waiting_retransmit = 0; + } + } + else /* selective request */ + { + pgm_queue_pop_tail_link (&window->retransmit_queue); + state->waiting_retransmit = 0; + } +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/txw.c.c89.patch b/3rdparty/openpgm-svn-r1135/pgm/txw.c.c89.patch new file mode 100644 index 0000000..033d337 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/txw.c.c89.patch @@ -0,0 +1,223 @@ +--- txw.c 2010-08-05 11:07:09.000000000 +0800 ++++ txw.c89 2010-08-05 11:11:36.000000000 +0800 +@@ -192,7 +192,7 @@ + pgm_assert_cmpuint (rs_k, >, 0); + } + +- pgm_debug ("create (tsi:%s max-tpdu:%" PRIu16 " sqns:%" PRIu32 " secs %u max-rte %zd use-fec:%s rs(n):%u rs(k):%u)", ++ pgm_debug ("create (tsi:%s max-tpdu:%" PRIu16 " sqns:%" PRIu32 " secs %u max-rte %ld use-fec:%s rs(n):%u rs(k):%u)", + pgm_tsi_print (tsi), + tpdu_size, sqns, secs, max_rte, + use_fec ? "YES" : "NO", +@@ -200,6 +200,7 @@ + + /* calculate transmit window parameters */ + pgm_assert (sqns || (tpdu_size && secs && max_rte)); ++ { + const unsigned alloc_sqns = sqns ? sqns : ( (secs * max_rte) / tpdu_size ); + window = pgm_malloc0 (sizeof(pgm_txw_t) + ( alloc_sqns * sizeof(struct pgm_sk_buff_t*) )); + window->tsi = tsi; +@@ -231,6 +232,7 @@ + pgm_assert (!pgm_txw_retransmit_can_peek (window)); + + return window; ++ } + } + + /* destructor for transmit window. must not be called more than once for same window. +@@ -316,6 +318,7 @@ + skb->sequence = window->lead; + + /* add skb to window */ ++ { + const uint_fast32_t index_ = skb->sequence % pgm_txw_max_length (window); + window->pdata[index_] = skb; + +@@ -325,6 +328,7 @@ + /* post-conditions */ + pgm_assert_cmpuint (pgm_txw_length (window), >, 0); + pgm_assert_cmpuint (pgm_txw_length (window), <=, pgm_txw_max_length (window)); ++ } + } + + /* peek an entry from the window for retransmission. +@@ -452,6 +456,7 @@ + pgm_assert (NULL != window); + pgm_assert_cmpuint (tg_sqn_shift, <, 8 * sizeof(uint32_t)); + ++ { + const uint32_t tg_sqn_mask = 0xffffffff << tg_sqn_shift; + const uint32_t nak_tg_sqn = sequence & tg_sqn_mask; /* left unshifted */ + const uint32_t nak_pkt_cnt = sequence & ~tg_sqn_mask; +@@ -490,6 +495,7 @@ + pgm_assert (!pgm_queue_is_empty (&window->retransmit_queue)); + state->waiting_retransmit = 1; + return TRUE; ++ } + } + + static +@@ -548,14 +554,17 @@ + pgm_debug ("retransmit_try_peek (window:%p)", (const void*)window); + + /* no lock required to detect presence of a request */ ++ { + pgm_list_t* tail_link = pgm_queue_peek_tail_link (&window->retransmit_queue); + if (PGM_UNLIKELY(NULL == tail_link)) { + pgm_debug ("retransmit queue empty on peek."); + return NULL; + } + ++ { + struct pgm_sk_buff_t* skb = (struct pgm_sk_buff_t*)tail_link; + pgm_assert (pgm_skb_is_valid (skb)); ++ { + pgm_txw_state_t* state = (pgm_txw_state_t*)&skb->cb; + + if (!state->waiting_retransmit) { +@@ -572,14 +581,17 @@ + } + + /* generate parity packet to satisify request */ ++ { + const uint8_t rs_h = state->pkt_cnt_sent % (window->rs.n - window->rs.k); + const uint32_t tg_sqn_mask = 0xffffffff << window->tg_sqn_shift; + const uint32_t tg_sqn = skb->sequence & tg_sqn_mask; + bool is_var_pktlen = FALSE; + bool is_op_encoded = FALSE; + uint16_t parity_length = 0; +- const pgm_gf8_t* src[ window->rs.k ]; +- for (uint_fast8_t i = 0; i < window->rs.k; i++) ++ const pgm_gf8_t** src = pgm_newa (pgm_gf8_t*, window->rs.k); ++ { ++ uint_fast8_t i; ++ for (i = 0; i < window->rs.k; i++) + { + const struct pgm_sk_buff_t* odata_skb = pgm_txw_peek (window, tg_sqn + i); + const uint16_t odata_tsdu_length = ntohs (odata_skb->pgm_header->pgm_tsdu_length); +@@ -599,6 +611,7 @@ + is_op_encoded = TRUE; + } + } ++ } + + /* construct basic PGM header to be completed by send_rdata() */ + skb = window->parity_buffer; +@@ -618,7 +631,9 @@ + { + skb->pgm_header->pgm_options |= PGM_OPT_VAR_PKTLEN; + +- for (uint_fast8_t i = 0; i < window->rs.k; i++) ++ { ++ uint_fast8_t i; ++ for (i = 0; i < window->rs.k; i++) + { + struct pgm_sk_buff_t* odata_skb = pgm_txw_peek (window, tg_sqn + i); + const uint16_t odata_tsdu_length = ntohs (odata_skb->pgm_header->pgm_tsdu_length); +@@ -632,6 +647,7 @@ + odata_skb->zero_padded = 1; + } + } ++ } + parity_length += 2; + } + +@@ -642,6 +658,7 @@ + + skb->pgm_data->data_sqn = htonl ( tg_sqn | rs_h ); + ++ { + void* data_bytes = skb->pgm_data + 1; + + /* encode every option separately, currently only one applies: opt_fragment +@@ -650,11 +667,14 @@ + { + skb->pgm_header->pgm_options |= PGM_OPT_PRESENT; + ++ { + struct pgm_opt_fragment null_opt_fragment; +- const pgm_gf8_t* opt_src[ window->rs.k ]; ++ const pgm_gf8_t** opt_src = pgm_newa (pgm_gf8_t*, window->rs.k); + memset (&null_opt_fragment, 0, sizeof(null_opt_fragment)); + *(uint8_t*)&null_opt_fragment |= PGM_OP_ENCODED_NULL; +- for (uint_fast8_t i = 0; i < window->rs.k; i++) ++ { ++ uint_fast8_t i; ++ for (i = 0; i < window->rs.k; i++) + { + const struct pgm_sk_buff_t* odata_skb = pgm_txw_peek (window, tg_sqn + i); + +@@ -669,8 +689,10 @@ + opt_src[i] = (pgm_gf8_t*)&null_opt_fragment; + } + } ++ } + + /* add options to this rdata packet */ ++ { + const uint16_t opt_total_length = sizeof(struct pgm_opt_length) + + sizeof(struct pgm_opt_header) + + sizeof(struct pgm_opt_fragment); +@@ -678,14 +700,17 @@ + /* add space for PGM options */ + pgm_skb_put (skb, opt_total_length); + ++ { + struct pgm_opt_length* opt_len = data_bytes; + opt_len->opt_type = PGM_OPT_LENGTH; + opt_len->opt_length = sizeof(struct pgm_opt_length); + opt_len->opt_total_length = htons ( opt_total_length ); ++ { + struct pgm_opt_header* opt_header = (struct pgm_opt_header*)(opt_len + 1); + opt_header->opt_type = PGM_OPT_FRAGMENT | PGM_OPT_END; + opt_header->opt_length = sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_fragment); + opt_header->opt_reserved = PGM_OP_ENCODED; ++ { + struct pgm_opt_fragment* opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1); + + /* The cast below is the correct way to handle the problem. +@@ -700,6 +725,11 @@ + sizeof(struct pgm_opt_fragment) - sizeof(struct pgm_opt_header)); + + data_bytes = opt_fragment + 1; ++ } ++ } ++ } ++ } ++ } + } + + /* encode payload */ +@@ -710,9 +740,16 @@ + parity_length); + + /* calculate partial checksum */ ++ { + const uint16_t tsdu_length = ntohs (skb->pgm_header->pgm_tsdu_length); + state->unfolded_checksum = pgm_csum_partial ((char*)skb->tail - tsdu_length, tsdu_length, 0); + return skb; ++ } ++ } ++ } ++ } ++ } ++ } + } + + /* remove head entry from retransmit queue, will fail on assertion if queue is empty. +@@ -733,6 +770,7 @@ + (const void*)window); + + /* tail link is valid without lock */ ++ { + pgm_list_t* tail_link = pgm_queue_peek_tail_link (&window->retransmit_queue); + + /* link must be valid for pop */ +@@ -762,6 +800,7 @@ + pgm_queue_pop_tail_link (&window->retransmit_queue); + state->waiting_retransmit = 0; + } ++ } + } + + /* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/txw_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/txw_unittest.c new file mode 100644 index 0000000..bec079b --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/txw_unittest.c @@ -0,0 +1,743 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for transmit window. + * + * Copyright (c) 2009 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + + +/* mock global */ + +#define pgm_histogram_add mock_pgm_histogram_add +#define pgm_rs_create mock_pgm_rs_create +#define pgm_rs_destroy mock_pgm_rs_destroy +#define pgm_rs_encode mock_pgm_rs_encode +#define pgm_compat_csum_partial mock_pgm_compat_csum_partial +#define pgm_histogram_init mock_pgm_histogram_init + +#define TXW_DEBUG +#include "txw.c" + + +/** reed-solomon module */ +void +mock_pgm_rs_create ( + pgm_rs_t* rs, + uint8_t n, + uint8_t k + ) +{ +} + +void +mock_pgm_rs_destroy ( + pgm_rs_t* rs + ) +{ +} + +void +mock_pgm_rs_encode( + pgm_rs_t* rs, + const pgm_gf8_t** src, + const uint8_t offset, + pgm_gf8_t* dst, + const uint16_t len + ) +{ +} + +/** checksum module */ +uint32_t +mock_pgm_compat_csum_partial ( + const void* addr, + uint16_t len, + uint32_t csum + ) +{ + return 0x0; +} + +void +mock_pgm_histogram_init ( + pgm_histogram_t* histogram + ) +{ +} + +void +mock_pgm_histogram_add ( + pgm_histogram_t* histogram, + int value + ) +{ +} + + +/* mock functions for external references */ + +size_t +pgm_pkt_offset ( + const bool can_fragment, + const sa_family_t pgmcc_family /* 0 = disable */ + ) +{ + return 0; +} + + +/* generate valid skb, data pointer pointing to PGM payload + */ +static +struct pgm_sk_buff_t* +generate_valid_skb (void) +{ + const guint16 tsdu_length = 1000; + const guint16 header_length = sizeof(struct pgm_header) + sizeof(struct pgm_data); + struct pgm_sk_buff_t* skb = pgm_alloc_skb (1500); +/* fake but valid transport and timestamp */ + skb->sock = (pgm_sock_t*)0x1; + skb->tstamp = 1; +/* header */ + pgm_skb_reserve (skb, header_length); + memset (skb->head, 0, header_length); + skb->pgm_header = (struct pgm_header*)skb->head; + skb->pgm_data = (struct pgm_data*)(skb->pgm_header + 1); + skb->pgm_header->pgm_type = PGM_ODATA; + skb->pgm_header->pgm_tsdu_length = g_htons (tsdu_length); +/* DATA */ + pgm_skb_put (skb, tsdu_length); + return skb; +} + +/* target: + * pgm_txw_t* + * pgm_txw_create ( + * const pgm_tsi_t* const tsi, + * const guint16 tpdu_size, + * const guint32 sqns, + * const guint secs, + * const guint max_rte, + * const gboolean use_fec, + * const guint rs_n, + * const guint rs_k + * ) + */ + +/* vanilla sequence count window */ +START_TEST (test_create_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + fail_if (NULL == pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0), "create failed"); +} +END_TEST + +/* vanilla time based window */ +START_TEST (test_create_pass_002) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + fail_if (NULL == pgm_txw_create (&tsi, 1500, 0, 60, 800000, FALSE, 0, 0), "create failed"); +} +END_TEST + +/* jumbo frame */ +START_TEST (test_create_pass_003) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + fail_if (NULL == pgm_txw_create (&tsi, 9000, 0, 60, 800000, FALSE, 0, 0), "create failed"); +} +END_TEST + +/* max frame */ +START_TEST (test_create_pass_004) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + fail_if (NULL == pgm_txw_create (&tsi, UINT16_MAX, 0, 60, 800000, FALSE, 0, 0), "create failed"); +} +END_TEST + +/* invalid tpdu size */ +START_TEST (test_create_fail_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const pgm_txw_t* window = pgm_txw_create (&tsi, 0, 0, 60, 800000, FALSE, 0, 0); + fail ("reached"); +} +END_TEST + +/* no specified sequence count or time value */ +START_TEST (test_create_fail_002) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const pgm_txw_t* window = pgm_txw_create (&tsi, 0, 0, 0, 800000, FALSE, 0, 0); + fail ("reached"); +} +END_TEST + +/* no specified rate */ +START_TEST (test_create_fail_003) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const pgm_txw_t* window = pgm_txw_create (&tsi, 0, 0, 60, 0, FALSE, 0, 0); + fail ("reached"); +} +END_TEST + +/* all invalid */ +START_TEST (test_create_fail_004) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + const pgm_txw_t* window = pgm_txw_create (NULL, 0, 0, 0, 0, FALSE, 0, 0); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_txw_shutdown ( + * pgm_txw_t* const window + * ) + */ + +START_TEST (test_shutdown_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + pgm_txw_shutdown (window); +} +END_TEST + +START_TEST (test_shutdown_fail_001) +{ + pgm_txw_shutdown (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_txw_add ( + * pgm_txw_t* const window, + * struct pgm_sk_buff_t* const skb + * ) + * failures raise assert errors and stop process execution. + */ + +START_TEST (test_add_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + pgm_txw_shutdown (window); +} +END_TEST + +/* null skb */ +START_TEST (test_add_fail_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + pgm_txw_add (window, NULL); + fail ("reached"); +} +END_TEST + +/* null window */ +START_TEST (test_add_fail_002) +{ + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (NULL, skb); + fail ("reached"); +} +END_TEST + +/* null skb content */ +START_TEST (test_add_fail_003) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + char buffer[1500]; + memset (buffer, 0, sizeof(buffer)); + pgm_txw_add (window, (struct pgm_sk_buff_t*)buffer); + fail ("reached"); +} +END_TEST + +/* target: + * struct pgm_sk_buff_t* + * pgm_txw_peek ( + * pgm_txw_t* const window, + * const guint32 sequence + * ) + */ + +START_TEST (test_peek_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + fail_unless (skb == pgm_txw_peek (window, window->trail), "peek failed"); + pgm_txw_shutdown (window); +} +END_TEST + +/* null window */ +START_TEST (test_peek_fail_001) +{ + const struct pgm_sk_buff_t* skb = pgm_txw_peek (NULL, 0); + fail ("reached"); +} +END_TEST + +/* empty window */ +START_TEST (test_peek_fail_002) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + fail_unless (NULL == pgm_txw_peek (window, window->trail), "peek failed"); + pgm_txw_shutdown (window); +} +END_TEST + +/** inline function tests **/ +/* pgm_txw_max_length () + */ +START_TEST (test_max_length_pass_001) +{ + const guint window_length = 100; + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, window_length, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + fail_unless (window_length == pgm_txw_max_length (window), "max_length failed"); + pgm_txw_shutdown (window); +} +END_TEST + +START_TEST (test_max_length_fail_001) +{ + const size_t answer = pgm_txw_max_length (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_txw_length () + */ +START_TEST (test_length_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + fail_unless (0 == pgm_txw_length (window), "length failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + fail_unless (1 == pgm_txw_length (window), "length failed"); + pgm_txw_shutdown (window); +} +END_TEST + +START_TEST (test_length_fail_001) +{ + const uint32_t answer = pgm_txw_length (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_txw_size () + */ +START_TEST (test_size_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + fail_unless (0 == pgm_txw_size (window), "size failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + fail_unless (1000 == pgm_txw_size (window), "size failed"); + pgm_txw_shutdown (window); +} +END_TEST + +START_TEST (test_size_fail_001) +{ + const size_t answer = pgm_txw_size (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_txw_is_empty + */ +START_TEST (test_is_empty_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + fail_unless (pgm_txw_is_empty (window), "is_empty failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + fail_if (pgm_txw_is_empty (window), "is_empty failed"); + pgm_txw_shutdown (window); +} +END_TEST + +START_TEST (test_is_empty_fail_001) +{ + const bool answer = pgm_txw_is_empty (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_txw_is_full + */ +START_TEST (test_is_full_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 1, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + fail_if (pgm_txw_is_full (window), "is_full failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + fail_unless (pgm_txw_is_full (window), "is_full failed"); + pgm_txw_shutdown (window); +} +END_TEST + +START_TEST (test_is_full_fail_001) +{ + const bool answer = pgm_txw_is_full (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_txw_lead + */ +START_TEST (test_lead_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + guint32 lead = pgm_txw_lead (window); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + fail_unless (lead + 1 == pgm_txw_lead (window), "lead failed"); + pgm_txw_shutdown (window); +} +END_TEST + +START_TEST (test_lead_fail_001) +{ + const uint32_t answer = pgm_txw_lead (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_txw_next_lead + */ +START_TEST (test_next_lead_pass_001) +{ + const guint window_length = 100; + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, window_length, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + guint32 next_lead = pgm_txw_next_lead (window); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + fail_unless (next_lead == pgm_txw_lead (window), "lead failed"); + pgm_txw_shutdown (window); +} +END_TEST + +START_TEST (test_next_lead_fail_001) +{ + const uint32_t answer = pgm_txw_next_lead (NULL); + fail ("reached"); +} +END_TEST + +/* pgm_txw_trail + */ +START_TEST (test_trail_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 1, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); +/* does not advance with adding skb */ + guint32 trail = pgm_txw_trail (window); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + fail_unless (trail == pgm_txw_trail (window), "trail failed"); +/* does advance when filling up window */ + skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + fail_if (trail == pgm_txw_trail (window), "trail failed"); + pgm_txw_shutdown (window); +} +END_TEST + +START_TEST (test_trail_fail_001) +{ + const uint32_t answer = pgm_txw_trail (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * bool + * pgm_txw_retransmit_push ( + * pgm_txw_t* const window, + * const uint32_t sequence, + * const bool is_parity, + * const uint8_t tg_sqn_shift + * ) + */ + +START_TEST (test_retransmit_push_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); +/* empty window invalidates all requests */ + fail_unless (FALSE == pgm_txw_retransmit_push (window, window->trail, FALSE, 0), "retransmit_push failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); +/* first request */ + fail_unless (TRUE == pgm_txw_retransmit_push (window, window->trail, FALSE, 0), "retransmit_push failed"); +/* second request eliminated */ + fail_unless (FALSE == pgm_txw_retransmit_push (window, window->trail, FALSE, 0), "retransmit_push failed"); + pgm_txw_shutdown (window); +} +END_TEST + +START_TEST (test_retransmit_push_fail_001) +{ + const bool answer = pgm_txw_retransmit_push (NULL, 0, FALSE, 0); + fail ("reached"); +} +END_TEST + +/* target: + * struct pgm_sk_buff_t* + * pgm_txw_retransmit_try_peek ( + * pgm_txw_t* const window + * ) + */ + +START_TEST (test_retransmit_try_peek_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + fail_unless (1 == pgm_txw_retransmit_push (window, window->trail, FALSE, 0), "retransmit_push failed"); + fail_unless (NULL != pgm_txw_retransmit_try_peek (window), "retransmit_try_peek failed"); + pgm_txw_shutdown (window); +} +END_TEST + +/* null window */ +START_TEST (test_retransmit_try_peek_fail_001) +{ + const struct pgm_sk_buff_t* skb = pgm_txw_retransmit_try_peek (NULL); + fail ("reached"); +} +END_TEST + +/* target: + * void + * pgm_txw_retransmit_remove_head ( + * pgm_txw_t* const window + * ) + */ + +START_TEST (test_retransmit_remove_head_pass_001) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + struct pgm_sk_buff_t* skb = generate_valid_skb (); + fail_if (NULL == skb, "generate_valid_skb failed"); + pgm_txw_add (window, skb); + fail_unless (1 == pgm_txw_retransmit_push (window, window->trail, FALSE, 0), "retransmit_push failed"); + fail_unless (NULL != pgm_txw_retransmit_try_peek (window), "retransmit_try_peek failed"); + pgm_txw_retransmit_remove_head (window); + pgm_txw_shutdown (window); +} +END_TEST + +/* null window */ +START_TEST (test_retransmit_remove_head_fail_001) +{ + pgm_txw_retransmit_remove_head (NULL); + fail ("reached"); +} +END_TEST + +/* empty retransmit queue */ +START_TEST (test_retransmit_remove_head_fail_002) +{ + const pgm_tsi_t tsi = { { 1, 2, 3, 4, 5, 6 }, 1000 }; + pgm_txw_t* window = pgm_txw_create (&tsi, 0, 100, 0, 0, FALSE, 0, 0); + fail_if (NULL == window, "create failed"); + pgm_txw_retransmit_remove_head (window); + fail ("reached"); +} +END_TEST + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_create = tcase_create ("create"); + suite_add_tcase (s, tc_create); + tcase_add_test (tc_create, test_create_pass_001); + tcase_add_test (tc_create, test_create_pass_002); + tcase_add_test (tc_create, test_create_pass_003); + tcase_add_test (tc_create, test_create_pass_004); + tcase_add_test_raise_signal (tc_create, test_create_fail_001, SIGABRT); + tcase_add_test_raise_signal (tc_create, test_create_fail_002, SIGABRT); + tcase_add_test_raise_signal (tc_create, test_create_fail_003, SIGABRT); + tcase_add_test_raise_signal (tc_create, test_create_fail_004, SIGABRT); + + TCase* tc_shutdown = tcase_create ("shutdown"); + suite_add_tcase (s, tc_shutdown); + tcase_add_test (tc_shutdown, test_shutdown_pass_001); + tcase_add_test_raise_signal (tc_shutdown, test_shutdown_fail_001, SIGABRT); + + TCase* tc_add = tcase_create ("add"); + suite_add_tcase (s, tc_add); + tcase_add_test (tc_add, test_add_pass_001); + tcase_add_test_raise_signal (tc_add, test_add_fail_001, SIGABRT); + tcase_add_test_raise_signal (tc_add, test_add_fail_002, SIGABRT); + tcase_add_test_raise_signal (tc_add, test_add_fail_003, SIGABRT); + + TCase* tc_peek = tcase_create ("peek"); + suite_add_tcase (s, tc_peek); + tcase_add_test (tc_peek, test_peek_pass_001); + tcase_add_test_raise_signal (tc_peek, test_peek_fail_001, SIGABRT); +/* logical not fatal errors */ + tcase_add_test (tc_peek, test_peek_fail_002); + + TCase* tc_max_length = tcase_create ("max-length"); + suite_add_tcase (s, tc_max_length); + tcase_add_test (tc_max_length, test_max_length_pass_001); + tcase_add_test_raise_signal (tc_max_length, test_max_length_fail_001, SIGABRT); + + TCase* tc_length = tcase_create ("length"); + suite_add_tcase (s, tc_length); + tcase_add_test (tc_length, test_length_pass_001); + tcase_add_test_raise_signal (tc_length, test_length_fail_001, SIGABRT); + + TCase* tc_size = tcase_create ("size"); + suite_add_tcase (s, tc_size); + tcase_add_test (tc_size, test_size_pass_001); + tcase_add_test_raise_signal (tc_size, test_size_fail_001, SIGABRT); + + TCase* tc_is_empty = tcase_create ("is-empty"); + suite_add_tcase (s, tc_is_empty); + tcase_add_test (tc_is_empty, test_is_empty_pass_001); + tcase_add_test_raise_signal (tc_is_empty, test_is_empty_fail_001, SIGABRT); + TCase* tc_is_full = tcase_create ("is-full"); + suite_add_tcase (s, tc_is_full); + tcase_add_test (tc_is_full, test_is_full_pass_001); + tcase_add_test_raise_signal (tc_is_full, test_is_full_fail_001, SIGABRT); + + TCase* tc_lead = tcase_create ("lead"); + suite_add_tcase (s, tc_lead); + tcase_add_test (tc_lead, test_lead_pass_001); + tcase_add_test_raise_signal (tc_lead, test_lead_fail_001, SIGABRT); + + TCase* tc_next_lead = tcase_create ("next-lead"); + suite_add_tcase (s, tc_next_lead); + tcase_add_test (tc_next_lead, test_next_lead_pass_001); + tcase_add_test_raise_signal (tc_next_lead, test_next_lead_fail_001, SIGABRT); + + TCase* tc_trail = tcase_create ("trail"); + suite_add_tcase (s, tc_trail); + tcase_add_test (tc_trail, test_trail_pass_001); + tcase_add_test_raise_signal (tc_trail, test_trail_fail_001, SIGABRT); + + TCase* tc_retransmit_push = tcase_create ("retransmit-push"); + suite_add_tcase (s, tc_retransmit_push); + tcase_add_test (tc_retransmit_push, test_retransmit_push_pass_001); + tcase_add_test_raise_signal (tc_retransmit_push, test_retransmit_push_fail_001, SIGABRT); + + TCase* tc_retransmit_try_peek = tcase_create ("retransmit-try-peek"); + suite_add_tcase (s, tc_retransmit_try_peek); + tcase_add_test (tc_retransmit_try_peek, test_retransmit_try_peek_pass_001); + tcase_add_test_raise_signal (tc_retransmit_try_peek, test_retransmit_try_peek_fail_001, SIGABRT); + + TCase* tc_retransmit_remove_head = tcase_create ("retransmit-remove-head"); + suite_add_tcase (s, tc_retransmit_remove_head); + tcase_add_test (tc_retransmit_remove_head, test_retransmit_remove_head_pass_001); + tcase_add_test_raise_signal (tc_retransmit_remove_head, test_retransmit_remove_head_fail_001, SIGABRT); + tcase_add_test_raise_signal (tc_retransmit_remove_head, test_retransmit_remove_head_fail_002, SIGABRT); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ diff --git a/3rdparty/openpgm-svn-r1135/pgm/valgrind.supp b/3rdparty/openpgm-svn-r1135/pgm/valgrind.supp new file mode 100644 index 0000000..ea74d2d --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/valgrind.supp @@ -0,0 +1,147 @@ +##----------------------------------------------------------------------## +## Suppressions to run OpenPGM + +{ + miru-glib-hack-1 + Memcheck:Leak + fun:memalign + fun:posix_memalign + obj:/usr/lib/libglib-2.0.so* + fun:g_slice_alloc +} + +{ + miru-glib-hack-2 + Memcheck:Leak + fun:calloc + fun:g_malloc0 + obj:/usr/lib/libglib-2.0.so* + fun:g_slice_alloc +} + +{ + miru-glib-hack-2b + Memcheck:Leak + fun:malloc + fun:g_malloc + obj:/usr/lib/libglib-2.0.so* + fun:g_slice_alloc +} + +{ + miru-glib-hack-3 + Memcheck:Leak + fun:malloc + fun:realloc + fun:g_realloc + obj:/usr/lib/libglib-2.0.so* + fun:g_ptr_array_add + fun:g_main_context_check + obj:/usr/lib/libglib-2.0.so* + fun:g_main_loop_run +} + +{ + miru-glib-hack-4 + Memcheck:Leak + fun:realloc + fun:g_realloc + obj:/usr/lib/libglib-2.0.so* + fun:g_array_set_size + fun:g_static_private_set + fun:g_get_language_names +} + +{ + miru-glib-hack-5 + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_slice_alloc + fun:g_array_sized_new + fun:g_static_private_set + fun:g_get_charset + fun:g_log_default_handler + fun:g_logv + fun:g_log +} + +{ + miru-glib-hack-6 + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_log_set_handler +} + +{ + miru-glib-hack-7 + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:g_thread_self + fun:g_thread_init_glib +} + + + +## Annoying libc errors + +{ + miru-libc-hack-1 + Memcheck:Addr8 + obj:/lib/ld-2.*.so + obj:/lib/ld-2.*.so + obj:/lib/libc-2.*.so + obj:/lib/ld-2.*.so + fun:__libc_dlopen_mode + fun:__nss_lookup_function + obj:/lib/libc-2.*.so + fun:getprotobyname_r +} + +{ + miru-libc-hack-1b + Memcheck:Addr8 + obj:/lib/ld-2.*.so + obj:/lib/ld-2.*.so + obj:/lib/ld-2.*.so + obj:/lib/ld-2.*.so + obj:/lib/ld-2.*.so + obj:/lib/ld-2.*.so + obj:/lib/ld-2.*.so + obj:/lib/libc-2.*.so + obj:/lib/ld-2.*.so + fun:__libc_dlopen_mode + fun:__nss_lookup_function + obj:/lib/libc-2.*.so + fun:getprotobyname_r +} + +{ + miru-libc-hack-2 + Memcheck:Cond + obj:/lib/ld-2.*.so + obj:/lib/ld-2.*.so + obj:/lib/libc-2.*.so + obj:/lib/ld-2.*.so + fun:__libc_dlsym + fun:__nss_lookup_function + obj:/lib/libc-2.*.so + fun:getaddrinfo +} + +{ + miru-libc-hack-3 + Memcheck:Addr8 + obj:/lib/ld-2.*.so + obj:/lib/ld-2.*.so + obj:/lib/ld-2.*.so + obj:/lib/ld-2.*.so + obj:/lib/libc-2.*.so + obj:/lib/ld-2.*.so + fun:__libc_dlsym + fun:__nss_lookup_function + obj:/lib/libc-2.*.so + fun:getaddrinfo +} diff --git a/3rdparty/openpgm-svn-r1135/pgm/version_generator.py b/3rdparty/openpgm-svn-r1135/pgm/version_generator.py new file mode 100755 index 0000000..9b48a3f --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/version_generator.py @@ -0,0 +1,52 @@ +#!/usr/bin/python + +import os +import platform +import time + +build_date = time.strftime ("%Y-%m-%d") +build_time = time.strftime ("%H:%M:%S") +build_rev = os.popen('svnversion -n .').read(); + +print """ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * OpenPGM version. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + + +/* globals */ + +const unsigned pgm_major_version = 5; +const unsigned pgm_minor_version = 0; +const unsigned pgm_micro_version = 79; +const char* pgm_build_date = "%s"; +const char* pgm_build_time = "%s"; +const char* pgm_build_system = "%s"; +const char* pgm_build_machine = "%s"; +const char* pgm_build_revision = "%s"; + + +/* eof */ +"""%(build_date, build_time, platform.system(), platform.machine(), build_rev) + +# end of file diff --git a/3rdparty/openpgm-svn-r1135/pgm/win/mingw32-runtime_3.13-1openpgm3.diff b/3rdparty/openpgm-svn-r1135/pgm/win/mingw32-runtime_3.13-1openpgm3.diff new file mode 100644 index 0000000..189c32d --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/win/mingw32-runtime_3.13-1openpgm3.diff @@ -0,0 +1,136 @@ +diff -urN include-original/mswsock.h include/mswsock.h +--- include-original/mswsock.h 2009-08-21 22:41:22.000000000 +0800 ++++ include/mswsock.h 2010-01-21 17:31:14.662159471 +0800 +@@ -83,23 +83,19 @@ + } WSAMSG, *PWSAMSG, *LPWSAMSG; + + +-/* According to MSDN docs, the WSAMSG.Control buffer starts with a +- cmsghdr header of the following form. See also RFC 2292. */ +- +-typedef struct wsacmsghdr { +- UINT cmsg_len; +- INT cmsg_level; +- INT cmsg_type; +- /* followed by UCHAR cmsg_data[]; */ +-} WSACMSGHDR; +- +-/* TODO: Standard Posix.1g macros as per RFC 2292, with WSA_uglification. */ +-#if 0 +-#define WSA_CMSG_FIRSTHDR(mhdr) +-#define WSA_CMSG_NXTHDR(mhdr, cmsg) +-#define WSA_CMSG_SPACE(length) +-#define WSA_CMSG_LEN(length) +-#endif ++ typedef struct _WSACMSGHDR { ++ SIZE_T cmsg_len; ++ INT cmsg_level; ++ INT cmsg_type; ++ } WSACMSGHDR,*PWSACMSGHDR,*LPWSACMSGHDR; ++ ++#define WSA_CMSGHDR_ALIGN(length) (((length) + TYPE_ALIGNMENT(WSACMSGHDR)-1) & (~(TYPE_ALIGNMENT(WSACMSGHDR)-1))) ++#define WSA_CMSGDATA_ALIGN(length) (((length) + MAX_NATURAL_ALIGNMENT-1) & (~(MAX_NATURAL_ALIGNMENT-1))) ++#define WSA_CMSG_FIRSTHDR(msg) (((msg)->Control.len >= sizeof(WSACMSGHDR)) ? (LPWSACMSGHDR)(msg)->Control.buf : (LPWSACMSGHDR)NULL) ++#define WSA_CMSG_NXTHDR(msg,cmsg) ((!(cmsg)) ? WSA_CMSG_FIRSTHDR(msg) : ((((u_char *)(cmsg) + WSA_CMSGHDR_ALIGN((cmsg)->cmsg_len) + sizeof(WSACMSGHDR)) > (u_char *)((msg)->Control.buf) + (msg)->Control.len) ? (LPWSACMSGHDR)NULL : (LPWSACMSGHDR)((u_char *)(cmsg) + WSA_CMSGHDR_ALIGN((cmsg)->cmsg_len)))) ++#define WSA_CMSG_DATA(cmsg) ((u_char *)(cmsg) + WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR))) ++#define WSA_CMSG_SPACE(length) (WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR) + WSA_CMSGHDR_ALIGN(length))) ++#define WSA_CMSG_LEN(length) (WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR)) + length) ++ ++typedef INT (WINAPI * LPFN_WSARECVMSG)(SOCKET, LPWSAMSG, LPDWORD, LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE); ++ + BOOL PASCAL DisconnectEx(SOCKET,LPOVERLAPPED,DWORD,DWORD); + int PASCAL WSARecvMsg(SOCKET,LPWSAMSG,LPDWORD,LPWSAOVERLAPPED,LPWSAOVERLAPPED_COMPLETION_ROUTINE); + +diff -urN include-original/ws2tcpip.h include/ws2tcpip.h +--- include-original/ws2tcpip.h 2009-08-21 22:41:42.000000000 +0800 ++++ include/ws2tcpip.h 2009-08-21 22:42:15.000000000 +0800 +@@ -78,6 +78,18 @@ + + #define UDP_NOCHECKSUM 1 + ++/* RFC 3768 */ ++#define MCAST_JOIN_GROUP 41 ++#define MCAST_LEAVE_GROUP 42 ++#define MCAST_BLOCK_SOURCE 43 ++#define MCAST_UNBLOCK_SOURCE 44 ++#define MCAST_JOIN_SOURCE_GROUP 45 ++#define MCAST_LEAVE_SOURCE_GROUP 46 ++#define MCAST_MSFILTER 47 ++ ++#define MCAST_EXCLUDE 0 ++#define MCAST_INCLUDE 1 ++ + /* INTERFACE_INFO iiFlags */ + #define IFF_UP 1 + #define IFF_BROADCAST 2 +@@ -104,6 +116,7 @@ + #define AI_PASSIVE 1 + #define AI_CANONNAME 2 + #define AI_NUMERICHOST 4 ++#define AI_ADDRCONFIG 0x20 + + /* getaddrinfo error codes */ + #define EAI_AGAIN WSATRY_AGAIN +@@ -132,6 +145,25 @@ + struct in_addr imr_interface; + }; + ++struct group_req { ++ u_long gr_interface; ++ struct sockaddr_storage gr_group; ++}; ++ ++struct group_source_req { ++ u_long gsr_interface; ++ struct sockaddr_storage gsr_group; ++ struct sockaddr_storage gsr_source; ++}; ++ ++struct group_filter { ++ u_long gf_interface; ++ struct sockaddr_storage gf_group; ++ u_long gf_fmode; ++ u_long gf_numsrc; ++ struct sockaddr_storage gf_slist[1]; ++}; ++ + struct ip_msfilter { + struct in_addr imsf_multiaddr; + struct in_addr imsf_interface; +@@ -356,6 +388,13 @@ + sockaddr_gen iiNetmask; + } INTERFACE_INFO, *LPINTERFACE_INFO; + ++typedef struct _INTERFACE_INFO_EX { ++ u_long iiFlags; ++ SOCKET_ADDRESS iiAddress; ++ SOCKET_ADDRESS iiBroadcastAddress; ++ SOCKET_ADDRESS iiNetmask; ++} INTERFACE_INFO_EX, *_LPINTERFACE_INFO_EX; ++ + /* + The definition above can cause problems on NT4,prior to sp4. + To workaround, include the following struct and typedef and +--- include-original/winnt.h 2009-08-21 22:41:42.000000000 +0800 ++++ include/winnt.h 2010-01-21 17:33:56.366162880 +0800 +@@ -43,6 +43,20 @@ + #define UNALIGNED + #endif + ++#ifdef _WIN64 ++#define MAX_NATURAL_ALIGNMENT sizeof(ULONGLONG) ++#define MEMORY_ALLOCATION_ALIGNMENT 16 ++#else ++#define MAX_NATURAL_ALIGNMENT sizeof(DWORD) ++#define MEMORY_ALLOCATION_ALIGNMENT 8 ++#endif ++ ++#ifdef __cplusplus ++#define TYPE_ALIGNMENT(t) __alignof__ (t) ++#else ++#define TYPE_ALIGNMENT(t) FIELD_OFFSET(struct { char x; t test; },test) ++#endif ++ + #ifndef DECLSPEC_ALIGN + #ifdef __GNUC__ + #define DECLSPEC_ALIGN(x) __attribute__((aligned(x))) diff --git a/3rdparty/openpgm-svn-r1135/pgm/win/mingw32-runtime_3.15.2-0openpgm1.diff b/3rdparty/openpgm-svn-r1135/pgm/win/mingw32-runtime_3.15.2-0openpgm1.diff new file mode 100644 index 0000000..b9453eb --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/win/mingw32-runtime_3.15.2-0openpgm1.diff @@ -0,0 +1,135 @@ +diff -urN include-original/mswsock.h include/mswsock.h +--- include-original/mswsock.h 2009-06-30 16:32:31.000000000 +0800 ++++ include/mswsock.h 2010-03-23 20:34:12.000000000 +0800 +@@ -83,23 +83,20 @@ + } WSAMSG, *PWSAMSG, *LPWSAMSG; + + +-/* According to MSDN docs, the WSAMSG.Control buffer starts with a +- cmsghdr header of the following form. See also RFC 2292. */ +- +-typedef struct wsacmsghdr { +- UINT cmsg_len; +- INT cmsg_level; +- INT cmsg_type; +- /* followed by UCHAR cmsg_data[]; */ +-} WSACMSGHDR; +- +-/* TODO: Standard Posix.1g macros as per RFC 2292, with WSA_uglification. */ +-#if 0 +-#define WSA_CMSG_FIRSTHDR(mhdr) +-#define WSA_CMSG_NXTHDR(mhdr, cmsg) +-#define WSA_CMSG_SPACE(length) +-#define WSA_CMSG_LEN(length) +-#endif ++typedef struct _WSACMSGHDR { ++ SIZE_T cmsg_len; ++ INT cmsg_level; ++ INT cmsg_type; ++} WSACMSGHDR,*PWSACMSGHDR,*LPWSACMSGHDR; ++ ++#define WSA_CMSGHDR_ALIGN(length) (((length) + TYPE_ALIGNMENT(WSACMSGHDR)-1) & (~(TYPE_ALIGNMENT(WSACMSGHDR)-1))) ++#define WSA_CMSGDATA_ALIGN(length) (((length) + MAX_NATURAL_ALIGNMENT-1) & (~(MAX_NATURAL_ALIGNMENT-1))) ++#define WSA_CMSG_FIRSTHDR(msg) (((msg)->Control.len >= sizeof(WSACMSGHDR)) ? (LPWSACMSGHDR)(msg)->Control.buf : (LPWSACMSGHDR)NULL) ++#define WSA_CMSG_NXTHDR(msg,cmsg) ((!(cmsg)) ? WSA_CMSG_FIRSTHDR(msg) : ((((u_char *)(cmsg) + WSA_CMSGHDR_ALIGN((cmsg)->cmsg_len) + sizeof(WSACMSGHDR)) > (u_char *)((msg)->Control.buf) + (msg)->Control.len) ? (LPWSACMSGHDR)NULL : (LPWSACMSGHDR)((u_char *)(cmsg) + WSA_CMSGHDR_ALIGN((cmsg)->cmsg_len)))) ++#define WSA_CMSG_DATA(cmsg) ((u_char *)(cmsg) + WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR))) ++#define WSA_CMSG_SPACE(length) (WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR) + WSA_CMSGHDR_ALIGN(length))) ++#define WSA_CMSG_LEN(length) (WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR)) + length) ++typedef INT (WINAPI * LPFN_WSARECVMSG)(SOCKET, LPWSAMSG, LPDWORD, LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE); + + BOOL PASCAL DisconnectEx(SOCKET,LPOVERLAPPED,DWORD,DWORD); + int PASCAL WSARecvMsg(SOCKET,LPWSAMSG,LPDWORD,LPWSAOVERLAPPED,LPWSAOVERLAPPED_COMPLETION_ROUTINE); +diff -urN include-original/winnt.h include/winnt.h +--- include-original/winnt.h 2009-06-30 16:32:32.000000000 +0800 ++++ include/winnt.h 2010-03-23 20:36:29.000000000 +0800 +@@ -43,6 +43,20 @@ + #define UNALIGNED + #endif + ++#ifdef _WIN64 ++#define MAX_NATURAL_ALIGNMENT sizeof(ULONGLONG) ++#define MEMORY_ALLOCATION_ALIGNMENT 16 ++#else ++#define MAX_NATURAL_ALIGNMENT sizeof(DWORD) ++#define MEMORY_ALLOCATION_ALIGNMENT 8 ++#endif ++ ++#ifdef __cplusplus ++#define TYPE_ALIGNMENT(t) __alignof__ (t) ++#else ++#define TYPE_ALIGNMENT(t) FIELD_OFFSET(struct { char x; t test; },test) ++#endif ++ + #ifndef DECLSPEC_ALIGN + #ifdef __GNUC__ + #define DECLSPEC_ALIGN(x) __attribute__((aligned(x))) +diff -urN include-original/ws2tcpip.h include/ws2tcpip.h +--- include-original/ws2tcpip.h 2009-06-30 16:32:32.000000000 +0800 ++++ include/ws2tcpip.h 2010-03-23 20:35:59.000000000 +0800 +@@ -78,6 +78,18 @@ + + #define UDP_NOCHECKSUM 1 + ++/* RFC 3768 */ ++#define MCAST_JOIN_GROUP 41 ++#define MCAST_LEAVE_GROUP 42 ++#define MCAST_BLOCK_SOURCE 43 ++#define MCAST_UNBLOCK_SOURCE 44 ++#define MCAST_JOIN_SOURCE_GROUP 45 ++#define MCAST_LEAVE_SOURCE_GROUP 46 ++#define MCAST_MSFILTER 47 ++ ++#define MCAST_EXCLUDE 0 ++#define MCAST_INCLUDE 1 ++ + /* INTERFACE_INFO iiFlags */ + #define IFF_UP 1 + #define IFF_BROADCAST 2 +@@ -104,6 +116,7 @@ + #define AI_PASSIVE 1 + #define AI_CANONNAME 2 + #define AI_NUMERICHOST 4 ++#define AI_ADDRCONFIG 0x20 + + /* getaddrinfo error codes */ + #define EAI_AGAIN WSATRY_AGAIN +@@ -132,6 +145,25 @@ + struct in_addr imr_interface; + }; + ++struct group_req { ++ u_long gr_interface; ++ struct sockaddr_storage gr_group; ++}; ++ ++struct group_source_req { ++ u_long gsr_interface; ++ struct sockaddr_storage gsr_group; ++ struct sockaddr_storage gsr_source; ++}; ++ ++struct group_filter { ++ u_long gf_interface; ++ struct sockaddr_storage gf_group; ++ u_long gf_fmode; ++ u_long gf_numsrc; ++ struct sockaddr_storage gf_slist[1]; ++}; ++ + struct ip_msfilter { + struct in_addr imsf_multiaddr; + struct in_addr imsf_interface; +@@ -356,6 +388,13 @@ + sockaddr_gen iiNetmask; + } INTERFACE_INFO, *LPINTERFACE_INFO; + ++typedef struct _INTERFACE_INFO_EX { ++ u_long iiFlags; ++ SOCKET_ADDRESS iiAddress; ++ SOCKET_ADDRESS iiBroadcastAddress; ++ SOCKET_ADDRESS iiNetmask; ++} INTERFACE_INFO_EX, *_LPINTERFACE_INFO_EX; ++ + /* + The definition above can cause problems on NT4,prior to sp4. + To workaround, include the following struct and typedef and diff --git a/3rdparty/openpgm-svn-r1135/pgm/win64/mingw-w64-bin_x86-64-linux_4.4.1-1openpgm1.diff b/3rdparty/openpgm-svn-r1135/pgm/win64/mingw-w64-bin_x86-64-linux_4.4.1-1openpgm1.diff new file mode 100644 index 0000000..d873544 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/win64/mingw-w64-bin_x86-64-linux_4.4.1-1openpgm1.diff @@ -0,0 +1,53 @@ +diff -urN include-original/./ws2tcpip.h x86_64-w64-mingw32/include/./ws2tcpip.h +--- include-original/./ws2tcpip.h 2009-09-10 13:36:49.000000000 +0800 ++++ x86_64-w64-mingw32/include/./ws2tcpip.h 2010-01-21 14:59:13.000000000 +0800 +@@ -12,6 +12,25 @@ + + #include + ++struct group_req { ++ u_long gr_interface; ++ struct sockaddr_storage gr_group; ++}; ++ ++struct group_source_req { ++ u_long gsr_interface; ++ struct sockaddr_storage gsr_group; ++ struct sockaddr_storage gsr_source; ++}; ++ ++struct group_filter { ++ u_long gf_interface; ++ struct sockaddr_storage gf_group; ++ u_long gf_fmode; ++ u_long gf_numsrc; ++ struct sockaddr_storage gf_slist[1]; ++}; ++ + struct ip_msfilter { + struct in_addr imsf_multiaddr; + struct in_addr imsf_interface; +@@ -22,6 +41,15 @@ + + #define IP_MSFILTER_SIZE(numsrc) (sizeof(struct ip_msfilter)-sizeof(struct in_addr) + (numsrc)*sizeof(struct in_addr)) + ++/* RFC 3768 */ ++#define MCAST_JOIN_GROUP 41 ++#define MCAST_LEAVE_GROUP 42 ++#define MCAST_BLOCK_SOURCE 43 ++#define MCAST_UNBLOCK_SOURCE 44 ++#define MCAST_JOIN_SOURCE_GROUP 45 ++#define MCAST_LEAVE_SOURCE_GROUP 46 ++#define MCAST_MSFILTER 47 ++ + #define MCAST_INCLUDE 0 + #define MCAST_EXCLUDE 1 + +@@ -277,6 +305,7 @@ + #define AI_PASSIVE 0x1 + #define AI_CANONNAME 0x2 + #define AI_NUMERICHOST 0x4 ++#define AI_ADDRCONFIG 0x20 + + #ifdef __cplusplus + extern "C" { diff --git a/3rdparty/openpgm-svn-r1135/pgm/wsastrerror.c b/3rdparty/openpgm-svn-r1135/pgm/wsastrerror.c new file mode 100644 index 0000000..2e21449 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/wsastrerror.c @@ -0,0 +1,372 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * Winsock Error strings. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#ifdef _WIN32 +# include + + +char* +pgm_wsastrerror ( + const int wsa_errno + ) +{ + switch (wsa_errno) { +#ifdef WSA_INVALID_HANDLE + case WSA_INVALID_HANDLE: return _("Specified event object handle is invalid."); +#endif +#ifdef WSA_NOT_ENOUGH_MEMORY + case WSA_NOT_ENOUGH_MEMORY: return _("Insufficient memory available."); +#endif +#ifdef WSA_INVALID_PARAMETER + case WSA_INVALID_PARAMETER: return _("One or more parameters are invalid."); +#endif +#ifdef WSA_OPERATION_ABORTED + case WSA_OPERATION_ABORTED: return _("Overlapped operation aborted."); +#endif +#ifdef WSA_IO_INCOMPLETE + case WSA_IO_INCOMPLETE: return _("Overlapped I/O event object not in signaled state."); +#endif +#ifdef WSA_IO_PENDING + case WSA_IO_PENDING: return _("Overlapped operations will complete later."); +#endif +#ifdef WSAEINTR + case WSAEINTR: return _("Interrupted function call."); +#endif +#ifdef WSAEBADF + case WSAEBADF: return _("File handle is not valid."); +#endif +#ifdef WSAEACCES + case WSAEACCES: return _("Permission denied."); +#endif +#ifdef WSAEFAULT + case WSAEFAULT: return _("Bad address."); +#endif +#ifdef WSAEINVAL + case WSAEINVAL: return _("Invalid argument."); +#endif +#ifdef WSAEMFILE + case WSAEMFILE: return _("Too many open files."); +#endif +#ifdef WSAEWOULDBLOCK + case WSAEWOULDBLOCK: return _("Resource temporarily unavailable."); +#endif +#ifdef WSAEINPROGRESS + case WSAEINPROGRESS: return _("Operation now in progress."); +#endif +#ifdef WSAEALREADY + case WSAEALREADY: return _("Operation already in progress."); +#endif +#ifdef WSAENOTSOCK + case WSAENOTSOCK: return _("Socket operation on nonsocket."); +#endif +#ifdef WSAEDESTADDRREQ + case WSAEDESTADDRREQ: return _("Destination address required."); +#endif +#ifdef WSAEMSGSIZE + case WSAEMSGSIZE: return _("Message too long."); +#endif +#ifdef WSAEPROTOTYPE + case WSAEPROTOTYPE: return _("Protocol wrong type for socket."); +#endif +#ifdef WSAENOPROTOOPT + case WSAENOPROTOOPT: return _("Bad protocol option."); +#endif +#ifdef WSAEPROTONOSUPPORT + case WSAEPROTONOSUPPORT: return _("Protocol not supported."); +#endif +#ifdef WSAESOCKTNOSUPPORT + case WSAESOCKTNOSUPPORT: return _("Socket type not supported."); +#endif +#ifdef WSAEOPNOTSUPP + case WSAEOPNOTSUPP: return _("Operation not supported."); +#endif +#ifdef WSAEPFNOSUPPORT + case WSAEPFNOSUPPORT: return _("Protocol family not supported."); +#endif +#ifdef WSAEAFNOSUPPORT + case WSAEAFNOSUPPORT: return _("Address family not supported by protocol family."); +#endif +#ifdef WSAEADDRINUSE + case WSAEADDRINUSE: return _("Address already in use."); +#endif +#ifdef WSAEADDRNOTAVAIL + case WSAEADDRNOTAVAIL: return _("Cannot assign requested address."); +#endif +#ifdef WSAENETDOWN + case WSAENETDOWN: return _("Network is down."); +#endif +#ifdef WSAENETUNREACH + case WSAENETUNREACH: return _("Network is unreachable."); +#endif +#ifdef WSAENETRESET + case WSAENETRESET: return _("Network dropped connection on reset."); +#endif +#ifdef WSAECONNABORTED + case WSAECONNABORTED: return _("Software caused connection abort."); +#endif +#ifdef WSAECONNRESET + case WSAECONNRESET: return _("Connection reset by peer."); +#endif +#ifdef WSAENOBUFS + case WSAENOBUFS: return _("No buffer space available."); +#endif +#ifdef WSAEISCONN + case WSAEISCONN: return _("Socket is already connected."); +#endif +#ifdef WSAENOTCONN + case WSAENOTCONN: return _("Socket is not connected."); +#endif +#ifdef WSAESHUTDOWN + case WSAESHUTDOWN: return _("Cannot send after socket shutdown."); +#endif +#ifdef WSAETOOMANYREFS + case WSAETOOMANYREFS: return _("Too many references."); +#endif +#ifdef WSAETIMEDOUT + case WSAETIMEDOUT: return _("Connection timed out."); +#endif +#ifdef WSAECONNREFUSED + case WSAECONNREFUSED: return _("Connection refused."); +#endif +#ifdef WSAELOOP + case WSAELOOP: return _("Cannot translate name."); +#endif +#ifdef WSAENAMETOOLONG + case WSAENAMETOOLONG: return _("Name too long."); +#endif +#ifdef WSAEHOSTDOWN + case WSAEHOSTDOWN: return _("Host is down."); +#endif +#ifdef WSAEHOSTUNREACH + case WSAEHOSTUNREACH: return _("No route to host."); +#endif +#ifdef WSAENOTEMPTY + case WSAENOTEMPTY: return _("Directory not empty."); +#endif +#ifdef WSAEPROCLIM + case WSAEPROCLIM: return _("Too many processes."); +#endif +#ifdef WSAEUSERS + case WSAEUSERS: return _("User quota exceeded."); +#endif +#ifdef WSAEDQUOT + case WSAEDQUOT: return _("Disk quota exceeded."); +#endif +#ifdef WSAESTALE + case WSAESTALE: return _("Stale file handle reference."); +#endif +#ifdef WSAEREMOTE + case WSAEREMOTE: return _("Item is remote."); +#endif +#ifdef WSASYSNOTREADY + case WSASYSNOTREADY: return _("Network subsystem is unavailable."); +#endif +#ifdef WSAVERNOTSUPPORTED + case WSAVERNOTSUPPORTED: return _("Winsock.dll version out of range."); +#endif +#ifdef WSANOTINITIALISED + case WSANOTINITIALISED: return _("Successful WSAStartup not yet performed."); +#endif +#ifdef WSAEDISCON + case WSAEDISCON: return _("Graceful shutdown in progress."); +#endif +#ifdef WSAENOMORE + case WSAENOMORE: return _("No more results."); +#endif +#ifdef WSAECANCELLED + case WSAECANCELLED: return _("Call has been canceled."); +#endif +#ifdef WSAEINVALIDPROCTABLE + case WSAEINVALIDPROCTABLE: return _("Procedure call table is invalid."); +#endif +#ifdef WSAEINVALIDPROVIDER + case WSAEINVALIDPROVIDER: return _("Service provider is invalid."); +#endif +#ifdef WSAEPROVIDERFAILEDINIT + case WSAEPROVIDERFAILEDINIT: return _("Service provider failed to initialize."); +#endif +#ifdef WSASYSCALLFAILURE + case WSASYSCALLFAILURE: return _("System call failure."); +#endif +#ifdef WSASERVICE_NOT_FOUND + case WSASERVICE_NOT_FOUND: return _("Service not found."); +#endif +#ifdef WSATYPE_NOT_FOUND + case WSATYPE_NOT_FOUND: return _("Class type not found."); +#endif +#ifdef WSA_E_NO_MORE + case WSA_E_NO_MORE: return _("No more results."); +#endif +#ifdef WSA_E_CANCELLED + case WSA_E_CANCELLED: return _("Call was canceled."); +#endif +#ifdef WSAEREFUSED + case WSAEREFUSED: return _("Database query was refused."); +#endif +#ifdef WSAHOST_NOT_FOUND + case WSAHOST_NOT_FOUND: return _("Host not found."); +#endif +#ifdef WSATRY_AGAIN + case WSATRY_AGAIN: return _("Nonauthoritative host not found."); +#endif +#ifdef WSANO_RECOVERY + case WSANO_RECOVERY: return _("This is a nonrecoverable error."); +#endif +#ifdef WSANO_DATA + case WSANO_DATA: return _("Valid name, no data record of requested type."); +#endif +#ifdef WSA_QOS_RECEIVERS + case WSA_QOS_RECEIVERS: return _("QOS receivers."); +#endif +#ifdef WSA_QOS_SENDERS + case WSA_QOS_SENDERS: return _("QOS senders."); +#endif +#ifdef WSA_QOS_NO_SENDERS + case WSA_QOS_NO_SENDERS: return _("No QOS senders."); +#endif +#ifdef WSA_QOS_NO_RECEIVERS + case WSA_QOS_NO_RECEIVERS: return _("QOS no receivers."); +#endif +#ifdef WSA_QOS_REQUEST_CONFIRMED + case WSA_QOS_REQUEST_CONFIRMED: return _("QOS request confirmed."); +#endif +#ifdef WSA_QOS_ADMISSION_FAILURE + case WSA_QOS_ADMISSION_FAILURE: return _("QOS admission error."); +#endif +#ifdef WSA_QOS_POLICY_FAILURE + case WSA_QOS_POLICY_FAILURE: return _("QOS policy failure."); +#endif +#ifdef WSA_QOS_BAD_STYLE + case WSA_QOS_BAD_STYLE: return _("QOS bad style."); +#endif +#ifdef WSA_QOS_BAD_OBJECT + case WSA_QOS_BAD_OBJECT: return _("QOS bad object."); +#endif +#ifdef WSA_QOS_TRAFFIC_CTRL_ERROR + case WSA_QOS_TRAFFIC_CTRL_ERROR: return _("QOS traffic control error."); +#endif +#ifdef WSA_QOS_GENERIC_ERROR + case WSA_QOS_GENERIC_ERROR: return _("QOS generic error."); +#endif +#ifdef WSA_QOS_ESERVICETYPE + case WSA_QOS_ESERVICETYPE: return _("QOS service type error."); +#endif +#ifdef WSA_QOS_EFLOWSPEC + case WSA_QOS_EFLOWSPEC: return _("QOS flowspec error."); +#endif +#ifdef WSA_QOS_EPROVSPECBUF + case WSA_QOS_EPROVSPECBUF: return _("Invalid QOS provider buffer."); +#endif +#ifdef WSA_QOS_EFILTERSTYLE + case WSA_QOS_EFILTERSTYLE: return _("Invalid QOS filter style."); +#endif +#ifdef WSA_QOS_EFILTERTYPE + case WSA_QOS_EFILTERTYPE: return _("Invalid QOS filter type."); +#endif +#ifdef WSA_QOS_EFILTERCOUNT + case WSA_QOS_EFILTERCOUNT: return _("Incorrect QOS filter count."); +#endif +#ifdef WSA_QOS_EOBJLENGTH + case WSA_QOS_EOBJLENGTH: return _("Invalid QOS object length."); +#endif +#ifdef WSA_QOS_EFLOWCOUNT + case WSA_QOS_EFLOWCOUNT: return _("Incorrect QOS flow count."); +#endif +#ifdef WSA_QOS_EUNKOWNPSOBJ + case WSA_QOS_EUNKOWNPSOBJ: return _("Unrecognized QOS object."); +#endif +#ifdef WSA_QOS_EPOLICYOBJ + case WSA_QOS_EPOLICYOBJ: return _("Invalid QOS policy object."); +#endif +#ifdef WSA_QOS_EFLOWDESC + case WSA_QOS_EFLOWDESC: return _("Invalid QOS flow descriptor."); +#endif +#ifdef WSA_QOS_EPSFLOWSPEC + case WSA_QOS_EPSFLOWSPEC: return _("Invalid QOS provider-specific flowspec."); +#endif +#ifdef WSA_QOS_EPSFILTERSPEC + case WSA_QOS_EPSFILTERSPEC: return _("Invalid QOS provider-specific filterspec."); +#endif +#ifdef WSA_QOS_ESDMODEOBJ + case WSA_QOS_ESDMODEOBJ: return _("Invalid QOS shape discard mode object."); +#endif +#ifdef WSA_QOS_ESHAPERATEOBJ + case WSA_QOS_ESHAPERATEOBJ: return _("Invalid QOS shaping rate object."); +#endif +#ifdef WSA_QOS_RESERVED_PETYPE + case WSA_QOS_RESERVED_PETYPE: return _("Reserved policy QOS element type."); +#endif + default: return _("Unknown."); + } +} + +char* +pgm_adapter_strerror ( + const int adapter_errno + ) +{ + switch (adapter_errno) { +#ifdef ERROR_ADDRESS_NOT_ASSOCIATED + case ERROR_ADDRESS_NOT_ASSOCIATED: return _("DHCP lease information was available."); +#endif +#ifdef ERROR_BUFFER_OVERFLOW + case ERROR_BUFFER_OVERFLOW: return _("The buffer to receive the adapter information is too small."); +#endif +#ifdef ERROR_INVALID_DATA + case ERROR_INVALID_DATA: return _("Invalid adapter information was retrieved."); +#endif +#ifdef ERROR_INVALID_PARAMETER + case ERROR_INVALID_PARAMETER: return _("One of the parameters is invalid."); +#endif +#ifdef ERROR_NOT_ENOUGH_MEMORY + case ERROR_NOT_ENOUGH_MEMORY: return _("Insufficient memory resources are available to complete the operation."); +#endif +#ifdef ERROR_NO_DATA + case ERROR_NO_DATA: return _("No adapter information exists for the local computer."); +#endif +#ifdef ERROR_NOT_SUPPORTED + case ERROR_NOT_SUPPORTED: return _("The GetAdaptersInfo function is not supported by the operating system running on the local computer.."); +#endif + default: return _("Other."); + } +} + +char* +pgm_win_strerror ( + char* buf, + size_t buflen, + const int win_errno + ) +{ + const DWORD nSize = buflen; + FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, + NULL, /* source */ + win_errno, /* message id */ + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), /* language id */ + (LPTSTR)buf, + buflen, + NULL); /* arguments */ + return buf; +} +#endif /* _WIN32 */ + +/* eof */ diff --git a/OpenPGMConfig.cmake b/OpenPGMConfig.cmake index 2729590..fe719f8 100644 --- a/OpenPGMConfig.cmake +++ b/OpenPGMConfig.cmake @@ -1,6 +1,6 @@ # Set up build SET(pgm_VERSION - svn-r1085 + svn-r1135 ) INCLUDE(${CMAKE_ROOT}/Modules/FindPkgConfig.cmake) @@ -14,8 +14,7 @@ IF(UNIX) IF(CMAKE_COMPILER_IS_GNUCC) # The scripts are fine for Linux/GCC, other platforms may or may # not work. - SET(LIBPGM_CFLAGS - -std=gnu99 + SET(LIBPGM_DEFINITIONS -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_REENTRANT @@ -41,6 +40,14 @@ IF(UNIX) -DCONFIG_BIND_INADDR_ANY -DCONFIG_GALOIS_MUL_LUT -DCONFIG_HAVE_GETOPT + ) + SET(LIBPGM_CFLAGS + -std=gnu99 + ${LIBPGM_DEFINITIONS} + ) + SET(LIBPGM_CXXFLAGS + -std=gnu++98 + ${LIBPGM_DEFINITIONS} ) SET(LIBPGM_LIBRARIES diff --git a/src/gui/clientFileReceiveDialog.cpp b/src/gui/clientFileReceiveDialog.cpp index ff3226a..e457d23 100644 --- a/src/gui/clientFileReceiveDialog.cpp +++ b/src/gui/clientFileReceiveDialog.cpp @@ -219,6 +219,9 @@ void ClientFileReceiveDialog::mcastTransferProgress(qulonglong transferID, qulon progressBar->setRange(0, of); progressBar->setValue(bytes); + + labelA->setText(formatSize(bytes)); + labelB->setText(formatSize(of)); } void ClientFileReceiveDialog::mcastTransferFinished(qulonglong transferID) @@ -233,6 +236,7 @@ void ClientFileReceiveDialog::mcastTransferFinished(qulonglong transferID) QMessageBox::warning(this, tr("Could not rename file"), tr("Failed to rename %1 to %2").arg(_filename).arg(filename)); } accept(); + deleteLater(); } void ClientFileReceiveDialog::mcastTransferFailed(qulonglong transferID, QString reason) @@ -242,6 +246,7 @@ void ClientFileReceiveDialog::mcastTransferFailed(qulonglong transferID, QString QMessageBox::warning(this, tr("File transfer failed"), tr("File transfer failed for the following reason:\n%1").arg(reason)); reject(); + deleteLater(); } void ClientFileReceiveDialog::cancelTransfer() diff --git a/src/net/mcast/CMakeLists.txt b/src/net/mcast/CMakeLists.txt index 914c9d3..e92b090 100644 --- a/src/net/mcast/CMakeLists.txt +++ b/src/net/mcast/CMakeLists.txt @@ -1,15 +1,15 @@ INCLUDE(../../../OpenPGMConfig.cmake) ADD_DEFINITIONS( - ${LIBPGM_CFLAGS} + ${LIBPGM_CXXFLAGS} -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS ) # OpenPGM uses the C99 restrict keyword which g++ does not recognize: -IF(CMAKE_COMPILER_IS_GNUCXX) - ADD_DEFINITIONS(-Drestrict=__restrict__) -ENDIF(CMAKE_COMPILER_IS_GNUCXX) +#IF(CMAKE_COMPILER_IS_GNUCXX) +# ADD_DEFINITIONS(${LIBPGM_CXXFLAGS}) +#ENDIF(CMAKE_COMPILER_IS_GNUCXX) INCLUDE(${QT_USE_FILE}) diff --git a/src/net/mcast/McastPGMSocket.cpp b/src/net/mcast/McastPGMSocket.cpp index 7952f00..bf52bd7 100644 --- a/src/net/mcast/McastPGMSocket.cpp +++ b/src/net/mcast/McastPGMSocket.cpp @@ -148,67 +148,88 @@ bool McastPGMSocket::open(McastConfiguration const* config, Direction direction) return false; } - unsigned const ambient_spm = 4096 * 1000; // every four seconds (approx.) - - // set parameters - if (direction == PSOCK_WRITE) - { - // write-only socket - const int send_only = 1, - spm_heartbeat[] = - { 256 * 1000, - 512 * 1000, - 1024 * 1000, - 2048 * 1000, - 4096 * 1000 }, - max_rate = config->multicastRate(), - max_window = config->multicastWinSize(); - // const int max_window_sqns = 3000; - - pgm_setsockopt(_priv->socket, PGM_SEND_ONLY, &send_only, - sizeof(send_only)); - - // SPM messages - pgm_setsockopt(_priv->socket, PGM_AMBIENT_SPM, &ambient_spm, sizeof(ambient_spm)); - pgm_setsockopt(_priv->socket, PGM_HEARTBEAT_SPM, &spm_heartbeat, sizeof(spm_heartbeat)); - - // Transmit window - pgm_setsockopt(_priv->socket, PGM_TXW_MAX_RTE, &max_rate, sizeof(max_rate)); - pgm_setsockopt(_priv->socket, PGM_TXW_SECS, &max_window, sizeof(max_window)); - // pgm_setsockopt(_priv->socket, PGM_TXW_SQNS, &max_window, sizeof(max_window)); - } - else - { - // readonly - const int recv_only = 1, - passive = 0, - max_window = config->multicastWinSize(), - max_rate = config->multicastRate(), - peer_expiry = ambient_spm * 5, - spmr_expiry = 250 * 1000, - nak_bo_ivl = 100 * 1000, - nak_rpt_ivl = 400 * 1000, - nak_rdata_ivl = 400 * 1000, + unsigned const ambient_spm = 2000 * 1000; // every one hundred milliseconds (approx.) + + /* Options for sending data */ + const int spm_heartbeat[] = + { 512 * 1000, + 1024 * 1000, + 2048 * 1000, + 4096 * 1000 }, + max_rate = 0, + max_window = config->multicastWinSize() * config->multicastRate() / config->multicastMTU(); + // const int max_window_sqns = 3000; + qDebug() << "Computed window size " << max_window << " packets"; + +// pgm_setsockopt(_priv->socket, PGM_SEND_ONLY, &send_only, +// sizeof(send_only)); + + // SPM messages + pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_AMBIENT_SPM, &ambient_spm, sizeof(ambient_spm)); + pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_HEARTBEAT_SPM, &spm_heartbeat, sizeof(spm_heartbeat)); + + // Transmit window + pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_TXW_MAX_RTE, &max_rate, sizeof(max_rate)); +// pgm_setsockopt(_priv->socket, PGM_TXW_SECS, &max_window, sizeof(max_window)); + pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_TXW_SQNS, &max_window, sizeof(max_window)); + + /* Options for receiving data */ + const int passive = 0, + spmr_expiry = 500 * 1000, + nak_bo_ivl = 200 * 1000, + nak_rpt_ivl = 500 * 1000, + nak_rdata_ivl = 500 * 1000, nak_data_retries = 50, - nak_ncf_retries = 50, - no_rxw_sqns = 0; - pgm_setsockopt(_priv->socket, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); - pgm_setsockopt(_priv->socket, PGM_PASSIVE, &passive, sizeof(passive)); - pgm_setsockopt(_priv->socket, PGM_RXW_MAX_RTE, &max_rate, sizeof(max_rate)); - pgm_setsockopt(_priv->socket, PGM_RXW_SECS, &max_window, sizeof(max_window)); - pgm_setsockopt(_priv->socket, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); - pgm_setsockopt(_priv->socket, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); - pgm_setsockopt(_priv->socket, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); - pgm_setsockopt(_priv->socket, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); - pgm_setsockopt(_priv->socket, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); - pgm_setsockopt(_priv->socket, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); - pgm_setsockopt(_priv->socket, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); - pgm_setsockopt(_priv->socket, PGM_RXW_SQNS, &no_rxw_sqns, sizeof(no_rxw_sqns)); + nak_ncf_retries = 50; + qDebug() << "Computed window size " << max_window << " packets"; + +// pgm_setsockopt(_priv->socket, PGM_RECV_ONLY, &recv_only, sizeof(recv_only)); + pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_PASSIVE, &passive, sizeof(passive)); +// pgm_setsockopt(_priv->socket, PGM_RXW_MAX_RTE, &max_rate, sizeof(max_rate)); +// pgm_setsockopt(_priv->socket, PGM_RXW_SECS, &max_window, sizeof(max_window)); + pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry)); + pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl)); + pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl)); + pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl)); + pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries)); + pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries)); + pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_RXW_SQNS, &max_window, sizeof(max_window)); + + /* Try using PGMCC */ + const struct pgm_pgmccinfo_t pgmccinfo = { + 100 /* usecs */ * 1000 /* msecs */, + 75 /* from OpenPGM examples */, + 500 /* from PGMCC internet-draft */ + }; + good = pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_USE_PGMCC, &pgmccinfo, sizeof(pgmccinfo)); + if(!good) + { + qCritical() << "Could not enable PGMCC"; + return false; } +// /* Forward Error Correction */ +// const struct pgm_fecinfo_t pgmfecinfo = { +// 255 /* from OpenPGM examples */, +// 2 /* send two proactive packets */, +// 8 /* from OpenPGM examples */, +// 1 /* enable on-demand parity */, +// 1 /* enable variable packet length */ +// }; +// good = pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_USE_FEC, &pgmfecinfo, sizeof(pgmfecinfo)); +// if(!good) +// { +// qCritical() << "Could not enable FEC"; +// return false; +// } + + // Peer Expiry: We will give 1 minute. + int const peer_expiry = 60 /* seconds */ * 1000000 /* microseconds */; + pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry)); + // MTU int const mtu = config->multicastMTU(); - pgm_setsockopt(_priv->socket, PGM_MTU, &mtu, sizeof(mtu)); + pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_MTU, &mtu, sizeof(mtu)); pgm_sockaddr_t addr; addr.sa_addr.sport = config->multicastSPort(); @@ -235,8 +256,8 @@ bool McastPGMSocket::open(McastConfiguration const* config, Direction direction) const int uport = config->multicastUDPUPort(); const int mport = config->multicastUDPMPort(); - pgm_setsockopt(_priv->socket, PGM_UDP_ENCAP_MCAST_PORT, &mport, sizeof(mport)); - pgm_setsockopt(_priv->socket, PGM_UDP_ENCAP_UCAST_PORT, &uport, sizeof(uport)); + pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_UDP_ENCAP_MCAST_PORT, &mport, sizeof(mport)); + pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_UDP_ENCAP_UCAST_PORT, &uport, sizeof(uport)); } good = pgm_bind3(_priv->socket, &addr, sizeof(addr), &ifreq , sizeof(ifreq), &ifreq, sizeof(ifreq), &err); @@ -255,21 +276,21 @@ bool McastPGMSocket::open(McastConfiguration const* config, Direction direction) // join the group for (unsigned i = 0; i < addrinfo->ai_recv_addrs_len; i++) { - pgm_setsockopt(_priv->socket, PGM_JOIN_GROUP, + pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_JOIN_GROUP, &addrinfo->ai_recv_addrs[i], sizeof(struct group_req)); } // set send address - pgm_setsockopt(_priv->socket, PGM_SEND_GROUP, &addrinfo->ai_send_addrs[0], + pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_SEND_GROUP, &addrinfo->ai_send_addrs[0], sizeof(struct group_req)); // IP parameters const int nonblocking = 1, multicast_loop = 0, multicast_hops = 16; - pgm_setsockopt(_priv->socket, PGM_MULTICAST_LOOP, &multicast_loop, + pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop)); - pgm_setsockopt(_priv->socket, PGM_MULTICAST_HOPS, &multicast_hops, + pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops)); - pgm_setsockopt(_priv->socket, PGM_NOBLOCK, &nonblocking, + pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking)); good = pgm_connect(_priv->socket, &err); @@ -286,6 +307,10 @@ bool McastPGMSocket::open(McastConfiguration const* config, Direction direction) pgm_freeaddrinfo(addrinfo); + /* Prime the generation of SPM packets during the waiting period */ + if(_priv->direction == PSOCK_WRITE) + QTimer::singleShot(0, this, SLOT(handleNak())); + return true; } @@ -337,8 +362,6 @@ void McastPGMSocket::handleNak() if (_finished) return; - qDebug() << "handleNak()"; - // QTimer::singleShot(1000, this, SLOT(handleNakTimeout())); // to handle NAKs in OpenPGM, we need to pgm_recv: @@ -355,9 +378,12 @@ void McastPGMSocket::handleNak() { struct timeval tv; socklen_t size = sizeof(tv); - pgm_getsockopt(_priv->socket, PGM_TIME_REMAIN, &tv, &size); - const int msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); - qDebug() << " timer pending: " << msecs << "ms"; + pgm_getsockopt(_priv->socket, IPPROTO_PGM, PGM_TIME_REMAIN, &tv, &size); + const long usecs = tv.tv_sec * 1000000 + tv.tv_usec; + int msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + if(msecs == 0) + msecs = 1; + qDebug() << " timer pending: " << usecs << "us (rounded to " << msecs << "ms)"; _nakTimeout->start(msecs); break; } @@ -365,9 +391,11 @@ void McastPGMSocket::handleNak() { struct timeval tv; socklen_t size = sizeof(tv); - pgm_getsockopt(_priv->socket, PGM_RATE_REMAIN, &tv, &size); - const int msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); - qDebug() << " rate limited: " << msecs << "ms"; + pgm_getsockopt(_priv->socket, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &size); + int msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + if(msecs == 0) + msecs = 1; + qDebug() << " rate limited: " << msecs << "ms"; _nakTimeout->start(msecs); break; } @@ -410,10 +438,7 @@ void McastPGMSocket::handleData(int fd) void McastPGMSocket::handleData() { - qDebug() << "handleData()"; - if (_finished) { - qDebug() << " finished!"; return; } @@ -445,9 +470,12 @@ void McastPGMSocket::handleData() { struct timeval tv; socklen_t size = sizeof(tv); - pgm_getsockopt(_priv->socket, PGM_TIME_REMAIN, &tv, &size); - const int msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); - qDebug() << " timer pending: " << msecs << "ms"; + pgm_getsockopt(_priv->socket, IPPROTO_PGM, PGM_TIME_REMAIN, &tv, &size); + const long usecs = tv.tv_sec * 1000000 + tv.tv_usec; + int msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + if(msecs == 0) + msecs = 1; + qDebug() << " timer pending: " << usecs << "us (rounded to " << msecs << "ms)"; _dataTimeout->start(msecs); break; } @@ -455,9 +483,11 @@ void McastPGMSocket::handleData() { struct timeval tv; socklen_t size = sizeof(tv); - pgm_getsockopt(_priv->socket, PGM_RATE_REMAIN, &tv, &size); - const int msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); - qDebug() << " rate limit pending: " << msecs << "ms"; + pgm_getsockopt(_priv->socket, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &size); + int msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + if(msecs == 0) + msecs = 1; + qDebug() << " rate limit pending: " << msecs << "ms"; _dataTimeout->start(msecs); break; } @@ -502,7 +532,6 @@ void McastPGMSocket::canSend() if (_finished) return; - // qDebug() << "canSend()"; if (_priv->send_notif) { @@ -536,17 +565,26 @@ void McastPGMSocket::canSend() { _priv->send_notif->setEnabled(true); } + else if(status == PGM_IO_STATUS_CONGESTION) + { + qDebug() << " congested..."; + // wait a short time (10ms?) + _sendTimeout->start(10); + } else if(status == PGM_IO_STATUS_RATE_LIMITED) { struct timeval tv; socklen_t size = sizeof(tv); - pgm_getsockopt(_priv->socket, PGM_RATE_REMAIN, &tv, &size); - int msecs = (tv.tv_sec * 1000) + ((tv.tv_sec + 999) / 1000); + pgm_getsockopt(_priv->socket, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &size); + int msecs = (tv.tv_sec * 1000) + ((tv.tv_usec + 999) / 1000); + if(msecs == 0) + msecs = 1; + qDebug() << " rate_limited, waiting" << msecs << "ms"; _sendTimeout->start(msecs); } else { - qCritical() << "Unhandled status in canSend()"; + qCritical() << "Unhandled status in canSend():" << status; } } diff --git a/src/net/mcast/McastSender.cpp b/src/net/mcast/McastSender.cpp index e25ec86..3fec6a4 100644 --- a/src/net/mcast/McastSender.cpp +++ b/src/net/mcast/McastSender.cpp @@ -18,11 +18,14 @@ #include "McastConstants.h" #include +#include #include // OpenPGM #defines bool. This is bad in C++. #undef bool +#define MCASTFT_START_DEFER_TIME 2000 /* msec */ + McastSender::McastSender(QIODevice* iodev, McastConfiguration const* config, QObject* parent) : QObject(parent), _config(config ? new McastConfiguration(*config) : new McastConfiguration()), @@ -42,16 +45,24 @@ McastSender::~McastSender() void McastSender::start() { _socket = new McastPGMSocket(this); - connect(_socket, SIGNAL(readyToSend()), this, SLOT(readyToSend())); + connect(_socket, SIGNAL(readyToSend()), this, SLOT(deferredStart())); _socket->open(_config, McastPGMSocket::PSOCK_WRITE); } void McastSender::start(McastPGMSocket* socket) { _socket = socket; - connect(_socket, SIGNAL(readyToSend()), this, SLOT(readyToSend())); Q_ASSERT(_socket->isOpen()); - readyToSend(); + deferredStart(); +} + +void McastSender::deferredStart() +{ + // Wait some time, to give the PGM library the chance to generate some + // undisturbed SPM messages: + QTimer::singleShot(MCASTFT_START_DEFER_TIME, this, SLOT(readyToSend())); + disconnect(_socket, SIGNAL(readyToSend()), this, SLOT(deferredStart())); + connect(_socket, SIGNAL(readyToSend()), this, SLOT(readyToSend())); } void McastSender::readyToSend() diff --git a/src/net/mcast/McastSender.h b/src/net/mcast/McastSender.h index dd5154c..0c5e29f 100644 --- a/src/net/mcast/McastSender.h +++ b/src/net/mcast/McastSender.h @@ -57,6 +57,7 @@ public slots: void close(); private slots: + void deferredStart(); void readyToSend(); void socketFinished(); diff --git a/src/net/pvsOutgoingMulticastTransfer.cpp b/src/net/pvsOutgoingMulticastTransfer.cpp index 2f24d49..4df4986 100644 --- a/src/net/pvsOutgoingMulticastTransfer.cpp +++ b/src/net/pvsOutgoingMulticastTransfer.cpp @@ -121,7 +121,7 @@ void PVSOutgoingMulticastTransfer::prepare() _socketInacceptable = false; // announce the transfer: QFileInfo info(*_file); - QString message = QString("%1:%2:%3:%4:%5").arg(_senderName).arg(_id).arg(info.baseName()).arg(info.size()).arg(_config->multicastUDPPortBase()); + QString message = QString("%1:%2:%3:%4:%5").arg(_senderName).arg(_id).arg(info.fileName()).arg(info.size()).arg(_config->multicastUDPPortBase()); PVSMsg msg(PVSCOMMAND, "MCASTFTANNOUNCE", message); emit announce(msg); _prepareTimer->start(5000); diff --git a/src/pvs.cpp b/src/pvs.cpp index c217d52..9bd36d6 100644 --- a/src/pvs.cpp +++ b/src/pvs.cpp @@ -10,6 +10,10 @@ # General information about OpenSLX can be found at http://openslx.org/ */ +#include +#include +#include + #include "pvs.h" #include "src/util/dispatcher.h" #include "src/net/pvsMsg.h" @@ -779,7 +783,11 @@ quint64 PVS::generateMcastTransferID() if (!nodeID) { - QDataStream(QCryptographicHash::hash(getUserName().toLocal8Bit(), QCryptographicHash::Md5)) >> nodeID; + QDateTime t = QDateTime::currentDateTime(); + QCryptographicHash h(QCryptographicHash::Md5); + h.addData(getUserName().toLocal8Bit()); + h.addData(t.toString().toLocal8Bit()); + QDataStream(h.result()) >> nodeID; } return (nodeID & Q_UINT64_C(0xffffffffffff0000)) | (quint64)(++counter); -- cgit v1.2.3-55-g7522 From 266eb5fb14c07e67aa211a5860e9abf3009136e3 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Mon, 4 Oct 2010 00:22:14 +0200 Subject: Implement first version of basic input event support --- CMakeLists.txt | 12 + icons/README | 3 + icons/remote-control-all.png | Bin 0 -> 5365 bytes icons/remote-control.png | Bin 0 -> 3657 bytes pvsmgr.qrc | 2 + src/gui/frame.cpp | 246 ++++++++++- src/gui/frame.h | 27 +- src/input/CMakeLists.txt | 19 + src/input/inputEvent.cpp | 98 +++++ src/input/inputEvent.h | 218 ++++++++++ src/input/inputEventHandler.h | 187 ++++++++ src/input/inputEventNonQt.cpp | 17 + src/input/unprivilegedHandlerChain.h | 34 ++ src/input/x11FakeKeyboardHandler.cpp | 800 +++++++++++++++++++++++++++++++++++ src/input/x11FakeKeyboardHandler.h | 29 ++ src/input/x11FakeMouseHandler.cpp | 59 +++ src/input/x11FakeMouseHandler.h | 34 ++ src/input/x11InputUtils.cpp | 29 ++ src/input/x11InputUtils.h | 27 ++ src/pvs.cpp | 24 ++ src/pvs.h | 4 + src/util/clientGUIUtils.cpp | 13 +- src/util/clientGUIUtils.h | 6 + 23 files changed, 1874 insertions(+), 14 deletions(-) create mode 100644 icons/README create mode 100644 icons/remote-control-all.png create mode 100644 icons/remote-control.png create mode 100644 src/input/CMakeLists.txt create mode 100644 src/input/inputEvent.cpp create mode 100644 src/input/inputEvent.h create mode 100644 src/input/inputEventHandler.h create mode 100644 src/input/inputEventNonQt.cpp create mode 100644 src/input/unprivilegedHandlerChain.h create mode 100644 src/input/x11FakeKeyboardHandler.cpp create mode 100644 src/input/x11FakeKeyboardHandler.h create mode 100644 src/input/x11FakeMouseHandler.cpp create mode 100644 src/input/x11FakeMouseHandler.h create mode 100644 src/input/x11InputUtils.cpp create mode 100644 src/input/x11InputUtils.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2153497..ec688b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,8 +29,16 @@ FIND_PACKAGE( VNC REQUIRED ) INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} + ${X11_INCLUDE_DIR} + ${X11_XTest_INCLUDE_PATH} ) +IF(NOT X11_XTest_FOUND) + MESSAGE(FATAL_ERROR "Could not find X11 extension XTest. It is needed for PVS") +ENDIF() + +ADD_SUBDIRECTORY(src/input) + ################################################################################ # Variables ################################################################################ @@ -299,18 +307,22 @@ TARGET_LINK_LIBRARIES( pvsmgr ${QT_LIBRARIES} ${VNC_LIBRARIES} ${X11_LIBRARIES} + pvsinput ) TARGET_LINK_LIBRARIES( pvsmgrtouch ${QT_LIBRARIES} ${VNC_LIBRARIES} ${X11_LIBRARIES} + pvsinput ) TARGET_LINK_LIBRARIES( pvs ${QT_LIBRARIES} ${VNC_LIBRARIES} ${X11_LIBRARIES} + ${X11_XTest_LIB} + pvsinput ) TARGET_LINK_LIBRARIES( pvsgui diff --git a/icons/README b/icons/README new file mode 100644 index 0000000..823d998 --- /dev/null +++ b/icons/README @@ -0,0 +1,3 @@ +remote-control.png and remote-control-all.png have been taken from the +Nuvola 1.0 Icon Set, generously provided under the LGPL by its designer, +David Vignoni. See http://www.icon-king.com/projects/nuvola/ diff --git a/icons/remote-control-all.png b/icons/remote-control-all.png new file mode 100644 index 0000000..bc7e2ae Binary files /dev/null and b/icons/remote-control-all.png differ diff --git a/icons/remote-control.png b/icons/remote-control.png new file mode 100644 index 0000000..ba460dd Binary files /dev/null and b/icons/remote-control.png differ diff --git a/pvsmgr.qrc b/pvsmgr.qrc index 171412e..cd26abc 100644 --- a/pvsmgr.qrc +++ b/pvsmgr.qrc @@ -19,6 +19,8 @@ icons/dozent.png icons/chat.png icons/cam32.svg + icons/remote-control.png + icons/remote-control-all.png AUTHORS TRANSLATION diff --git a/src/gui/frame.cpp b/src/gui/frame.cpp index cb79643..96ea26c 100644 --- a/src/gui/frame.cpp +++ b/src/gui/frame.cpp @@ -18,11 +18,14 @@ # ----------------------------------------------------------------------------- */ +#include #include "frame.h" #include #include #include +#define MOUSE_MOTION_SEND_INTERVAL 100 /* msecs */ + Frame::Frame(const QString & text, QWidget * parent) : QLabel(parent), _clientVNCThread(0) { @@ -51,10 +54,25 @@ Frame::Frame(const QString & text, QWidget * parent) : button_lock = createToolButton(tr("Lock this client"), QIcon(":/lock"),SLOT(setLock())); //button_unlock = createToolButton(tr("Unlock this client"), QIcon(":/lock"),SLOT(setLock())); button_dozent = createToolButton(tr("Set as Superclient"), QIcon(":/dozent2"),SLOT(setDozent())); + button_control = createToolButton(tr("Enable Remote Control"), QIcon(":/remotecontrol"), SLOT(remoteControlClicked())); + button_control->setCheckable(true); + button_control_all = createToolButton(tr("Remote Control All Clients"), QIcon(":/remotecontrolall"), SLOT(remoteControlAllClicked())); + button_control_all->setCheckable(true); connect(this, SIGNAL(clicked()), this, SLOT(slotClicked())); ip = ""; setToolButtonListVisible(false); + + _remoteControlEnabled = false; + _remoteControlToAll = false; + + _mouseMotionEventTimer = new QTimer(this); + _mouseMotionEventTimer->setInterval(MOUSE_MOTION_SEND_INTERVAL); + _mouseMotionEventTimer->setSingleShot(false); + connect(_mouseMotionEventTimer, SIGNAL(timeout()), this, SLOT(sendMouseMotionEvent())); + + _mousePositionChanged = true; + } Frame::~Frame() @@ -216,23 +234,45 @@ void Frame::slotClicked() void Frame::mousePressEvent(QMouseEvent* event) { - emit clicked(); - if (event->button() == Qt::RightButton) - { - /*if (!_dummy) - DelDummy->setDisabled(true); - menu->exec(QCursor::pos());*/ - } - else - { + if(!_remoteControlEnabled) + { + emit clicked(); + if (event->button() == Qt::RightButton) + { + /*if (!_dummy) + DelDummy->setDisabled(true); + menu->exec(QCursor::pos());*/ + } + else + { - } - QLabel::mousePressEvent(event); + } + QLabel::mousePressEvent(event); + } + else + { + event->accept(); + ConsoleLog writeLine("Captured remote control mousePressEvent"); + + updateMousePosition(event); + sendInputEvent(InputEvent::mousePressRelease(event)); + } } void Frame::mouseReleaseEvent ( QMouseEvent * event ) { - QLabel::mouseReleaseEvent(event); + if(!_remoteControlEnabled) + { + QLabel::mouseReleaseEvent(event); + } + else + { + event->accept(); + ConsoleLog writeLine("Captured remote control mouseReleaseEvent"); + + updateMousePosition(event); + sendInputEvent(InputEvent::mousePressRelease(event)); + } } QToolButton* Frame::createToolButton(const QString &toolTip, const QIcon &icon, const char *member) @@ -330,3 +370,185 @@ void Frame::setDozent() getConFrame()->setDozent(true); } } + +void Frame::remoteControlClicked() +{ + if(_remoteControlEnabled) + { + setMouseTracking(false); + _mouseMotionEventTimer->stop(); + button_control->setToolTip(tr("Enable Remote Control")); + _remoteControlEnabled = false; + button_control->setChecked(false); + releaseKeyboard(); + } + else + { + button_control->setToolTip(tr("Disable Remote Control")); + _remoteControlEnabled = true; + button_control->setChecked(true); + _mouseMotionEventTimer->start(); + setMouseTracking(true); + if(_mouseOver) + grabKeyboard(); + } +} + +void Frame::remoteControlAllClicked() +{ + if(_remoteControlToAll) + { + button_control_all->setToolTip(tr("Remote Control only this Client")); + button_control_all->setChecked(false); + _remoteControlToAll = false; + } + else + { + button_control_all->setToolTip(tr("Remote Control All Clients")); + button_control_all->setChecked(true); + _remoteControlToAll = true; + } +} + + + +void Frame::sendMouseMotionEvent() +{ + InputEvent evt = InputEvent::mouseMotion(_lastRecordedMousePosition.x(), _lastRecordedMousePosition.y()); + + if(!_mousePositionChanged) + return; + + _mousePositionChanged = false; + sendInputEvent(evt); +} + +void Frame::sendInputEvent(InputEvent const& evt) +{ + QString str; + eventToString(evt, str); + std::string evtStr = evt.toString(); + PVSMsg msg(PVSCOMMAND, "INPUTEVENT", str); + + if(_remoteControlEnabled) + { + if(_remoteControlToAll) + { + ConsoleLog writeLine(QString("sendInputEvent(%1) to one").arg(evtStr.c_str())); + PVSConnectionManager::getManager()->getServer()->sendToAll(msg); + } + else + { + ConsoleLog writeLine(QString("sendInputEvent(%1) to all").arg(evtStr.c_str())); + _cFrame->getConnection()->sendMessage(msg); + } + } + else + { + ConsoleLog writeLine("sendMouseMotionEvent() disabled"); + } +} + +void Frame::mouseMoveEvent(QMouseEvent* event) +{ + QPoint newPosition = rescalePosition(event->posF()); + if(newPosition != _lastRecordedMousePosition) { + _lastRecordedMousePosition = newPosition; + _mousePositionChanged = true; + ConsoleLog writeLine(QString("Mouse moved to (%1,%2)").arg(_lastRecordedMousePosition.x()).arg(_lastRecordedMousePosition.y())); + } +} + +QPoint Frame::rescalePosition(QPointF guipos) +{ + QSize s = size(); + QSize t = _clientVNCThread->getSize(); + qreal px, py; + px = guipos.x() * t.width() / (qreal)s.width(); + py = guipos.y() * t.height() / (qreal)s.height(); + return QPoint((int)px, (int)py); +} + +void Frame::updateMousePosition(QMouseEvent* event) +{ + QPoint oldPosition = _lastRecordedMousePosition; + _lastRecordedMousePosition = rescalePosition(event->posF()); + _mousePositionChanged = oldPosition != _lastRecordedMousePosition; + sendMouseMotionEvent(); +} + +void Frame::enterEvent(QEvent* event) +{ + _mouseOver = true; + if(_remoteControlEnabled) + { + grabKeyboard(); + } +} + +void Frame::leaveEvent(QEvent* event) +{ + _mouseOver = false; + if(_remoteControlEnabled) + { + releaseKeyboard(); + } +} + +void Frame::keyPressEvent(QKeyEvent* event) +{ + if(_remoteControlEnabled) + { + // The action of the keyboard may depend on the position of the pointer + sendMouseMotionEvent(); + InputEvent evt = InputEvent::keyboardPress(event); + sendInputEvent(evt); + } + else + { + QLabel::keyPressEvent(event); + } +} + +void Frame::keyReleaseEvent(QKeyEvent* event) +{ + if(_remoteControlEnabled) + { + // The action of the keyboard may depend on the position of the pointer + sendMouseMotionEvent(); + InputEvent evt = InputEvent::keyboardRelease(event); + sendInputEvent(evt); + } + else + { + QLabel::keyReleaseEvent(event); + } +} + +bool Frame::event(QEvent* event) +{ + if(_remoteControlEnabled) + { + bool recognized; + switch(event->type()) + { + case QEvent::ShortcutOverride: + recognized = true; + event->accept(); + break; + case QEvent::KeyPress: + recognized = true; + keyPressEvent(static_cast(event)); + break; + case QEvent::KeyRelease: + recognized = true; + keyReleaseEvent(static_cast(event)); + break; + default: + recognized = false; + } + if(recognized && event->isAccepted()) + return true; + } + return QLabel::event(event); +} diff --git a/src/gui/frame.h b/src/gui/frame.h index 3004e0c..19b330a 100644 --- a/src/gui/frame.h +++ b/src/gui/frame.h @@ -8,6 +8,7 @@ class VNCClientThread; class ConnectionWindow; class ConnectionFrame; class MainWindow; +class InputEvent; class Frame: public QLabel { @@ -48,8 +49,13 @@ public: QToolButton* button_lock; QToolButton* button_unlock; QToolButton* button_dozent; + QToolButton* button_control; + QToolButton* button_control_all; QList toolButtonList; + bool _remoteControlEnabled; + bool _remoteControlToAll; + public Q_SLOTS: void updateImage(int x, int y, int w, int h); void iamDown(); @@ -68,13 +74,23 @@ public Q_SLOTS: void setLock(); //void unlock(); void setDozent(); +private Q_SLOTS: + void remoteControlClicked(); + void remoteControlAllClicked(); + void sendMouseMotionEvent(); signals: - void clicked(); + void clicked(); protected: void paintEvent(QPaintEvent *event); void mousePressEvent ( QMouseEvent * event ); void mouseReleaseEvent ( QMouseEvent * event ); + void mouseMoveEvent ( QMouseEvent * event ); + void enterEvent(QEvent* event); + void leaveEvent(QEvent* event); + void keyPressEvent(QKeyEvent* event); + void keyReleaseEvent(QKeyEvent* event); + bool event(QEvent* event); private: QToolButton* createToolButton(const QString &toolTip, const QIcon &icon, const char *member); @@ -85,6 +101,15 @@ private: bool _isLocked; bool _dozent; int _ux, _uy; + + // for remote control: + QPoint _lastRecordedMousePosition; + bool _mousePositionChanged; + QTimer* _mouseMotionEventTimer; + bool _mouseOver; + QPoint rescalePosition(QPointF guiPosition); + void updateMousePosition(QMouseEvent* event); + void sendInputEvent(InputEvent const&); }; #endif /* FRAME_H_ */ diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt new file mode 100644 index 0000000..aa52ec3 --- /dev/null +++ b/src/input/CMakeLists.txt @@ -0,0 +1,19 @@ +INCLUDE(${QT_USE_FILE}) + +SET(pvsinput_SRCS + inputEvent.cpp + ) + +IF(UNIX) + list(APPEND pvsinput_SRCS + x11InputUtils.cpp + x11FakeKeyboardHandler.cpp + x11FakeMouseHandler.cpp) +ENDIF() + +ADD_LIBRARY( + pvsinput + STATIC + ${pvsinput_HDRS} + ${pvsinput_SRCS} +) diff --git a/src/input/inputEvent.cpp b/src/input/inputEvent.cpp new file mode 100644 index 0000000..04a84e7 --- /dev/null +++ b/src/input/inputEvent.cpp @@ -0,0 +1,98 @@ +/* + * inputEvent.cpp + * + * Created on: 06.09.2010 + * Author: brs + */ + +#include +#include +#include +#include +#include +#include +#include "inputEvent.h" +#include + +// We implement operators to serialize and load an event: +QDataStream& operator <<(QDataStream& ostrm, InputEvent const& evt) +{ + ostrm << evt.type_ << evt.code_ << evt.value_; + return ostrm; +} + +QDataStream& operator >>(QDataStream& istrm, InputEvent& evt) +{ + istrm >> evt.type_ >> evt.code_ >> evt.value_; + return istrm; +} + +void eventToString(InputEvent const& evt, QString& str) +{ + QByteArray ba; + QBuffer buf(&ba); + buf.open(QIODevice::WriteOnly); + QDataStream out(&buf); + out << evt; + str = QString::fromAscii(ba.toBase64()); +} + +bool eventFromString(QString const& str, InputEvent& evt) +{ + // TODO This does not do proper error checking. Only use from trusted sources! + QByteArray ba = QByteArray::fromBase64(str.toAscii()); + QBuffer buf(&ba); + buf.open(QIODevice::ReadOnly); + QDataStream in(&buf); + in >> evt; + return true; +} + +quint16 InputEvent::mouseButtonsFromQt(int b) +{ + quint16 ret = 0; + if(b & Qt::LeftButton) + { + ret |= EB_LEFT; + } + if(b & Qt::RightButton) + { + ret |= EB_RIGHT; + } + if(b & Qt::MidButton) + { + ret |= EB_MIDDLE; + } + return ret; +} + +InputEvent InputEvent::mousePressRelease(QMouseEvent const* evt) +{ + quint16 button = mouseButtonsFromQt(evt->button()); + quint16 buttons = mouseButtonsFromQt(evt->buttons()); + quint16 code; + + if(buttons & button) + { + code = EC_PRESS; + } + else + { + code = EC_RELEASE; + } + + quint32 value = ((quint32)button << 16) | buttons; + return InputEvent(ET_BUTTON, code, value); +} + +InputEvent InputEvent::keyboardPress(QKeyEvent const* evt) +{ + quint32 value = evt->key() | evt->modifiers(); + return InputEvent(ET_KEY, EC_PRESS, value); +} + +InputEvent InputEvent::keyboardRelease(QKeyEvent const* evt) +{ + quint32 value = evt->key() | evt->modifiers(); + return InputEvent(ET_KEY, EC_RELEASE, value); +} diff --git a/src/input/inputEvent.h b/src/input/inputEvent.h new file mode 100644 index 0000000..917cc64 --- /dev/null +++ b/src/input/inputEvent.h @@ -0,0 +1,218 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # inputEvent.h: + # - Definition of an input event + # -------------------------------------------------------------------------- + */ + +#ifndef INPUTEVENT_H_ +#define INPUTEVENT_H_ + +#include +#include +#include +#include + +#ifndef __linux +# error "This will only run on a Linux system. Porting is required for other systems." +#endif + +struct QDataStream; +struct QString; +struct QMouseEvent; +struct QKeyEvent; + +class InputEvent +{ +private: + friend QDataStream& operator<<(QDataStream&, InputEvent const&); + friend QDataStream& operator>>(QDataStream&, InputEvent&); + + friend void eventToString(InputEvent const& evt, QString& str); + friend bool eventFromString(QString const& str, InputEvent& evt); + + uint16_t type_; + uint16_t code_; + uint32_t value_; + + // InputEvents are immutable. Prohibit assignment: + InputEvent& operator=(InputEvent const&); // There intentionally is no implementation. +public: + InputEvent(uint16_t type, uint16_t code, uint32_t value) : type_(type), code_(code), value_(value) + { + } + + InputEvent() + { + } + + static InputEvent mouseMotion(uint16_t x, uint16_t y) + { + return InputEvent(ET_POINTER, 0, ((uint32_t)x << 16) | y); + } + + static uint16_t mouseButtonsFromQt(int b); + + static InputEvent mousePressRelease(QMouseEvent const* event); + static InputEvent keyboardPress(QKeyEvent const* event); + static InputEvent keyboardRelease(QKeyEvent const* event); + + static const uint16_t ET_KEY = 0; + static const uint16_t ET_BUTTON = 1; + static const uint16_t ET_POINTER = 2; + static const uint16_t ET_SPECIAL = 3; + + static const uint16_t EC_PRESS = 0; + static const uint16_t EC_RELEASE = 1; + static const uint16_t EC_REBOOT = 2; + static const uint16_t EC_SYSRQ = 3; + static const uint16_t EC_KILL_X = 4; + + typedef uint32_t event_key; + + typedef uint32_t event_key_modifiers; + + static const uint16_t EB_LEFT = 1; + static const uint16_t EB_MIDDLE = 2; + static const uint16_t EB_RIGHT = 4; + + static const uint32_t MODIFIER_MASK = + 0x7e000000; + + uint16_t type() const + { + return type_; + } + + uint16_t code() const + { + return code_; + } + + uint32_t value() const + { + return value_; + } + + bool isKeyboard() const + { + return type_ == ET_KEY; + } + + bool isButton() const + { + return type_ == ET_BUTTON; + } + + bool isPointer() const + { + return type_ == ET_POINTER; + } + + bool isSpecial() const + { + return type_ == ET_SPECIAL; + } + + bool isPress() const + { + return code_ == EC_PRESS; + } + + bool isRelease() const + { + return code_ == EC_RELEASE; + } + + uint16_t pressedButton() const + { + assert(type_ == ET_BUTTON); + return (value_ >> 16); + } + + uint16_t heldButtons() const + { + assert(type_ == ET_BUTTON); + return (value_ & 0xffff); + } + + uint16_t xCoord() const + { + assert(type_ == ET_POINTER); + return (value_ >> 16); + } + + uint16_t yCoord() const + { + assert(type_ == ET_POINTER); + return (value_ & 0xffff); + } + + static std::string typeToString(uint16_t type) + { + switch(type) + { + case ET_BUTTON: + return "BUTTON"; + case ET_KEY: + return "KEY"; + case ET_POINTER: + return "POINTER"; + case ET_SPECIAL: + return "SPECIAL"; + default: + std::ostringstream s; + s << std::hex << type; + return s.str(); + } + } + + static std::string codeToString(uint16_t code) + { + switch(code) + { + case EC_PRESS: + return "PRESS"; + case EC_RELEASE: + return "RELEASE"; + case EC_SYSRQ: + return "SYSRQ"; + case EC_REBOOT: + return "REBOOT"; + case EC_KILL_X: + return "KILL_X"; + default: + std::ostringstream s; + s << std::hex << code; + return s.str(); + } + } + + std::string toString() const + { + std::ostringstream s; + s << typeToString(type_) << ':' << codeToString(code_) << ':' << std::hex << value_; + return s.str(); + } + + uint32_t qt_keysym() const + { + return value_ & ~MODIFIER_MASK; + } + + uint32_t qt_modifiers() const + { + return value_ & MODIFIER_MASK; + } +}; + +#endif /* INPUTEVENT_H_ */ diff --git a/src/input/inputEventHandler.h b/src/input/inputEventHandler.h new file mode 100644 index 0000000..3910f93 --- /dev/null +++ b/src/input/inputEventHandler.h @@ -0,0 +1,187 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # inputEventHandler.h: + # - Common definitions for input event handlers + # -------------------------------------------------------------------------- + */ + +#ifndef INPUTEVENTHANDLER_H_ +#define INPUTEVENTHANDLER_H_ + +#include +#include +#include +#include +#include + +#define HANDLER_TYPE_DONT_CARE 0xffff +#define HANDLER_CODE_DONT_CARE 0xffff +#define HANDLER_VALUE_DONT_CARE 0xffffffff + +template +class DefaultInputEventHandler { +public: + static bool matches(InputEvent const& evt) { + if(Type != 0xffff) { + if(evt.type() != Type) + return false; + } + if(Code != 0xffff) { + if(evt.code() != Code) + return false; + } + if(Value != 0xffffffff) { + if(evt.value() != Value) + return false; + } + return true; + } + + static void initialize() + { + } +}; + +namespace policy { + +struct NoSecurityCheck { + static bool allow(InputEvent const&) { + return true; + } +}; + +struct PhysicalSeatSecurityCheck { + static bool allow(InputEvent const&) { + return /* TODO implement */ true; + } +}; + +struct AlwaysDenySecurityCheck { + static bool allow(InputEvent const&) { + return false; + } +}; + +struct UnixLike; +struct Linux; +struct Windows; + +#if defined(__linux) +typedef boost::mpl::vector2::type Systems; +#elif defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__) +typedef boost::mpl::vector1::type Systems; +#else +# error "Porting is needed!" +#endif + +struct SystemEnabled; +struct SystemDisabled; + +template +struct RequireSystem +{ + typedef typename boost::mpl::contains::type enabled_type; + static const bool enabled = enabled_type::value; +}; + +struct RequireNoSystem +{ + typedef boost::mpl::bool_::type enabled_type; + static const bool enabled = enabled_type::value; +}; + +} + +template +class HandlerHelper +{ +public: + static bool handle(InputEvent const& evt) { + if(!SecurityPolicy::allow(evt)) + { + return true; + } + if(Delegate::matches(evt)) { + Delegate::handle(evt); + return true; + } else { + return false; + } + } + + static void initialize() + { + Delegate::initialize(); + } +}; + +template +class HandlerHelper +{ +public: + static bool handle(InputEvent const& evt) { + return false; + } + + static void initialize() + { + } +}; + +template +struct Handler : public HandlerHelper +{ +}; + +template +struct InputEventHandlerChainHelper +{ +private: + typedef typename boost::mpl::next::type next_iterator_type; + typedef InputEventHandlerChainHelper next_in_chain; + + typedef typename boost::mpl::deref::type handler_type; + +public: + static void handle(InputEvent const& evt) { + if(!handler_type::handle(evt)) { + next_in_chain::handle(evt); + } + } + + static void initialize() { + handler_type::initialize(); + next_in_chain::initialize(); + } +}; + +template +struct InputEventHandlerChainHelper +{ +public: + static void handle(InputEvent const&) { + // do nothing + } + + static void initialize() { + // do nothing + } +}; + +template +struct InputEventHandlerChain : public InputEventHandlerChainHelper::type, typename boost::mpl::end::type> +{ +}; + +#endif /* INPUTEVENTHANDLER_H_ */ diff --git a/src/input/inputEventNonQt.cpp b/src/input/inputEventNonQt.cpp new file mode 100644 index 0000000..a7455fd --- /dev/null +++ b/src/input/inputEventNonQt.cpp @@ -0,0 +1,17 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # inputEvent.h: + # - Input Event routines that need not be linked against Qt + # -------------------------------------------------------------------------- + */ + + diff --git a/src/input/unprivilegedHandlerChain.h b/src/input/unprivilegedHandlerChain.h new file mode 100644 index 0000000..734720a --- /dev/null +++ b/src/input/unprivilegedHandlerChain.h @@ -0,0 +1,34 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # inputHandlerChain.h: + # - Definition of the input handler chain + # -------------------------------------------------------------------------- + */ + +#ifndef INPUTHANDLERCHAIN_H_ +#define INPUTHANDLERCHAIN_H_ + +#include +#include "inputEventHandler.h" + +#include "x11FakeKeyboardHandler.h" +#include "x11FakeMouseHandler.h" + +typedef boost::mpl::list< + Handler >, + Handler >, + Handler > +>::type unprivileged_handler_list; + +typedef InputEventHandlerChain unprivileged_handler_chain; + +#endif /* INPUTHANDLERCHAIN_H_ */ diff --git a/src/input/x11FakeKeyboardHandler.cpp b/src/input/x11FakeKeyboardHandler.cpp new file mode 100644 index 0000000..bb14b76 --- /dev/null +++ b/src/input/x11FakeKeyboardHandler.cpp @@ -0,0 +1,800 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # x11FakeKeyboardHandler.h: + # - Handle keyboard events on X11 - interface + # -------------------------------------------------------------------------- + */ + +#include +#include +#include +// Qt headers need to be included before X11 headers +#include +#include +// #include +#include +#include +#include +#include +#include +#include +#include +#include "x11InputUtils.h" +#include "x11FakeKeyboardHandler.h" + +//////////////////////// INPUT EVENT TRANSLATION ///////////////////////////////// + +typedef unsigned char xmodifier_type; + +char modifiernames[][8] = { + "SHIFT", + "LOCK", + "CONTROL", + "MOD1", + "MOD2", + "MOD3", + "MOD4", + "MOD5" +}; + +QString modifiers_to_string(xmodifier_type mods) +{ + QString s; + for(int i = 0; i < 8; i++) + { + if(mods & (1< lookup_table_type; +typedef lookup_table_type::const_iterator lookup_table_const_iterator; +typedef lookup_table_type::iterator lookup_table_iterator; +lookup_table_type keysyms; +void initialize_keysyms() { + keysyms[Qt::Key_Escape] = XK_Escape; + keysyms[Qt::Key_Tab] = XK_Tab; +// keysyms[Qt::Key_Backtab] = XK_Backtab; + keysyms[Qt::Key_Backspace] = XK_BackSpace; + keysyms[Qt::Key_Return] = XK_Return; + keysyms[Qt::Key_Enter] = XK_KP_Enter; + keysyms[Qt::Key_Insert] = XK_Insert; + keysyms[Qt::Key_Delete] = XK_Delete; + keysyms[Qt::Key_Pause] = XK_Pause; + keysyms[Qt::Key_Print] = XK_Print; + keysyms[Qt::Key_SysReq] = XK_Sys_Req; + keysyms[Qt::Key_Clear] = XK_Clear; + keysyms[Qt::Key_Home] = XK_Home; + keysyms[Qt::Key_End] = XK_End; + keysyms[Qt::Key_Left] = XK_Left; + keysyms[Qt::Key_Up] = XK_Up; + keysyms[Qt::Key_Right] = XK_Right; + keysyms[Qt::Key_Down] = XK_Down; + keysyms[Qt::Key_PageUp] = XK_Page_Up; + keysyms[Qt::Key_PageDown] = XK_Page_Down; + keysyms[Qt::Key_Shift] = XK_Shift_L; + keysyms[Qt::Key_Control] = XK_Control_L; + keysyms[Qt::Key_Meta] = XK_Meta_L; + keysyms[Qt::Key_Alt] = XK_Alt_L; + keysyms[Qt::Key_CapsLock] = XK_Caps_Lock; + keysyms[Qt::Key_NumLock] = XK_Num_Lock; + keysyms[Qt::Key_ScrollLock] = XK_Scroll_Lock; + keysyms[Qt::Key_F1] = XK_F1; + keysyms[Qt::Key_F2] = XK_F2; + keysyms[Qt::Key_F3] = XK_F3; + keysyms[Qt::Key_F4] = XK_F4; + keysyms[Qt::Key_F5] = XK_F5; + keysyms[Qt::Key_F6] = XK_F6; + keysyms[Qt::Key_F7] = XK_F7; + keysyms[Qt::Key_F8] = XK_F8; + keysyms[Qt::Key_F9] = XK_F9; + keysyms[Qt::Key_F10] = XK_F10; + keysyms[Qt::Key_F11] = XK_F11; + keysyms[Qt::Key_F12] = XK_F12; + keysyms[Qt::Key_F13] = XK_F13; + keysyms[Qt::Key_F14] = XK_F14; + keysyms[Qt::Key_F15] = XK_F15; + keysyms[Qt::Key_F16] = XK_F16; + keysyms[Qt::Key_F17] = XK_F17; + keysyms[Qt::Key_F18] = XK_F18; + keysyms[Qt::Key_F19] = XK_F19; + keysyms[Qt::Key_F20] = XK_F20; + keysyms[Qt::Key_F21] = XK_F21; + keysyms[Qt::Key_F22] = XK_F22; + keysyms[Qt::Key_F23] = XK_F23; + keysyms[Qt::Key_F24] = XK_F24; + keysyms[Qt::Key_F25] = XK_F25; + keysyms[Qt::Key_F26] = XK_F26; + keysyms[Qt::Key_F27] = XK_F27; + keysyms[Qt::Key_F28] = XK_F28; + keysyms[Qt::Key_F29] = XK_F29; + keysyms[Qt::Key_F30] = XK_F30; + keysyms[Qt::Key_F31] = XK_F31; + keysyms[Qt::Key_F32] = XK_F32; + keysyms[Qt::Key_F33] = XK_F33; + keysyms[Qt::Key_F34] = XK_F34; + keysyms[Qt::Key_F35] = XK_F35; + keysyms[Qt::Key_Super_L] = XK_Super_L; + keysyms[Qt::Key_Super_R] = XK_Super_R; + keysyms[Qt::Key_Menu] = XK_Menu; + keysyms[Qt::Key_Hyper_L] = XK_Hyper_L; + keysyms[Qt::Key_Hyper_R] = XK_Hyper_R; + keysyms[Qt::Key_Help] = XK_Help; +// keysyms[Qt::Key_Direction_L] = XK_Direction_L; +// keysyms[Qt::Key_Direction_R] = XK_Direction_R; + keysyms[Qt::Key_Space] = XK_space; + keysyms[Qt::Key_Exclam] = XK_exclam; + keysyms[Qt::Key_QuoteDbl] = XK_quotedbl; + keysyms[Qt::Key_NumberSign] = XK_numbersign; + keysyms[Qt::Key_Dollar] = XK_dollar; + keysyms[Qt::Key_Percent] = XK_percent; + keysyms[Qt::Key_Ampersand] = XK_ampersand; + keysyms[Qt::Key_Apostrophe] = XK_apostrophe; + keysyms[Qt::Key_ParenLeft] = XK_parenleft; + keysyms[Qt::Key_ParenRight] = XK_parenright; + keysyms[Qt::Key_Asterisk] = XK_asterisk; + keysyms[Qt::Key_Plus] = XK_plus; + keysyms[Qt::Key_Comma] = XK_comma; + keysyms[Qt::Key_Minus] = XK_minus; + keysyms[Qt::Key_Period] = XK_period; + keysyms[Qt::Key_Slash] = XK_slash; + keysyms[Qt::Key_0] = XK_0; + keysyms[Qt::Key_1] = XK_1; + keysyms[Qt::Key_2] = XK_2; + keysyms[Qt::Key_3] = XK_3; + keysyms[Qt::Key_4] = XK_4; + keysyms[Qt::Key_5] = XK_5; + keysyms[Qt::Key_6] = XK_6; + keysyms[Qt::Key_7] = XK_7; + keysyms[Qt::Key_8] = XK_8; + keysyms[Qt::Key_9] = XK_9; + keysyms[Qt::Key_Colon] = XK_colon; + keysyms[Qt::Key_Semicolon] = XK_semicolon; + keysyms[Qt::Key_Less] = XK_less; + keysyms[Qt::Key_Equal] = XK_equal; + keysyms[Qt::Key_Greater] = XK_greater; + keysyms[Qt::Key_Question] = XK_question; + keysyms[Qt::Key_At] = XK_at; + keysyms[Qt::Key_A] = XK_A; + keysyms[Qt::Key_B] = XK_B; + keysyms[Qt::Key_C] = XK_C; + keysyms[Qt::Key_D] = XK_D; + keysyms[Qt::Key_E] = XK_E; + keysyms[Qt::Key_F] = XK_F; + keysyms[Qt::Key_G] = XK_G; + keysyms[Qt::Key_H] = XK_H; + keysyms[Qt::Key_I] = XK_I; + keysyms[Qt::Key_J] = XK_J; + keysyms[Qt::Key_K] = XK_K; + keysyms[Qt::Key_L] = XK_L; + keysyms[Qt::Key_M] = XK_M; + keysyms[Qt::Key_N] = XK_N; + keysyms[Qt::Key_O] = XK_O; + keysyms[Qt::Key_P] = XK_P; + keysyms[Qt::Key_Q] = XK_Q; + keysyms[Qt::Key_R] = XK_R; + keysyms[Qt::Key_S] = XK_S; + keysyms[Qt::Key_T] = XK_T; + keysyms[Qt::Key_U] = XK_U; + keysyms[Qt::Key_V] = XK_V; + keysyms[Qt::Key_W] = XK_W; + keysyms[Qt::Key_X] = XK_X; + keysyms[Qt::Key_Y] = XK_Y; + keysyms[Qt::Key_Z] = XK_Z; + keysyms[Qt::Key_BracketLeft] = XK_bracketleft; + keysyms[Qt::Key_Backslash] = XK_backslash; + keysyms[Qt::Key_BracketRight] = XK_bracketright; + keysyms[Qt::Key_AsciiCircum] = XK_asciicircum; + keysyms[Qt::Key_Underscore] = XK_underscore; + keysyms[Qt::Key_QuoteLeft] = XK_quoteleft; + keysyms[Qt::Key_BraceLeft] = XK_braceleft; + keysyms[Qt::Key_Bar] = XK_bar; + keysyms[Qt::Key_BraceRight] = XK_braceright; + keysyms[Qt::Key_AsciiTilde] = XK_asciitilde; + keysyms[Qt::Key_nobreakspace] = XK_nobreakspace; + keysyms[Qt::Key_exclamdown] = XK_exclamdown; + keysyms[Qt::Key_cent] = XK_cent; + keysyms[Qt::Key_sterling] = XK_sterling; + keysyms[Qt::Key_currency] = XK_currency; + keysyms[Qt::Key_yen] = XK_yen; + keysyms[Qt::Key_brokenbar] = XK_brokenbar; + keysyms[Qt::Key_section] = XK_section; + keysyms[Qt::Key_diaeresis] = XK_diaeresis; + keysyms[Qt::Key_copyright] = XK_copyright; + keysyms[Qt::Key_ordfeminine] = XK_ordfeminine; + keysyms[Qt::Key_guillemotleft] = XK_guillemotleft; + keysyms[Qt::Key_notsign] = XK_notsign; + keysyms[Qt::Key_hyphen] = XK_hyphen; + keysyms[Qt::Key_registered] = XK_registered; + keysyms[Qt::Key_macron] = XK_macron; + keysyms[Qt::Key_degree] = XK_degree; + keysyms[Qt::Key_plusminus] = XK_plusminus; + keysyms[Qt::Key_twosuperior] = XK_twosuperior; + keysyms[Qt::Key_threesuperior] = XK_threesuperior; + keysyms[Qt::Key_acute] = XK_acute; + keysyms[Qt::Key_mu] = XK_mu; + keysyms[Qt::Key_paragraph] = XK_paragraph; + keysyms[Qt::Key_periodcentered] = XK_periodcentered; + keysyms[Qt::Key_cedilla] = XK_cedilla; + keysyms[Qt::Key_onesuperior] = XK_onesuperior; + keysyms[Qt::Key_masculine] = XK_masculine; + keysyms[Qt::Key_guillemotright] = XK_guillemotright; + keysyms[Qt::Key_onequarter] = XK_onequarter; + keysyms[Qt::Key_onehalf] = XK_onehalf; + keysyms[Qt::Key_threequarters] = XK_threequarters; + keysyms[Qt::Key_questiondown] = XK_questiondown; + keysyms[Qt::Key_Agrave] = XK_Agrave; + keysyms[Qt::Key_Aacute] = XK_Aacute; + keysyms[Qt::Key_Acircumflex] = XK_Acircumflex; + keysyms[Qt::Key_Atilde] = XK_Atilde; + keysyms[Qt::Key_Adiaeresis] = XK_Adiaeresis; + keysyms[Qt::Key_Aring] = XK_Aring; + keysyms[Qt::Key_AE] = XK_AE; + keysyms[Qt::Key_Ccedilla] = XK_Ccedilla; + keysyms[Qt::Key_Egrave] = XK_Egrave; + keysyms[Qt::Key_Eacute] = XK_Eacute; + keysyms[Qt::Key_Ecircumflex] = XK_Ecircumflex; + keysyms[Qt::Key_Ediaeresis] = XK_Ediaeresis; + keysyms[Qt::Key_Igrave] = XK_Igrave; + keysyms[Qt::Key_Iacute] = XK_Iacute; + keysyms[Qt::Key_Icircumflex] = XK_Icircumflex; + keysyms[Qt::Key_Idiaeresis] = XK_Idiaeresis; + keysyms[Qt::Key_ETH] = XK_ETH; + keysyms[Qt::Key_Ntilde] = XK_Ntilde; + keysyms[Qt::Key_Ograve] = XK_Ograve; + keysyms[Qt::Key_Oacute] = XK_Oacute; + keysyms[Qt::Key_Ocircumflex] = XK_Ocircumflex; + keysyms[Qt::Key_Otilde] = XK_Otilde; + keysyms[Qt::Key_Odiaeresis] = XK_Odiaeresis; + keysyms[Qt::Key_multiply] = XK_multiply; + keysyms[Qt::Key_Ooblique] = XK_Ooblique; + keysyms[Qt::Key_Ugrave] = XK_Ugrave; + keysyms[Qt::Key_Uacute] = XK_Uacute; + keysyms[Qt::Key_Ucircumflex] = XK_Ucircumflex; + keysyms[Qt::Key_Udiaeresis] = XK_Udiaeresis; + keysyms[Qt::Key_Yacute] = XK_Yacute; + keysyms[Qt::Key_THORN] = XK_THORN; + keysyms[Qt::Key_ssharp] = XK_ssharp; + keysyms[Qt::Key_Agrave] = XK_Agrave; + keysyms[Qt::Key_Aacute] = XK_Aacute; + keysyms[Qt::Key_Acircumflex] = XK_Acircumflex; + keysyms[Qt::Key_Atilde] = XK_Atilde; + keysyms[Qt::Key_Adiaeresis] = XK_Adiaeresis; + keysyms[Qt::Key_Aring] = XK_Aring; + keysyms[Qt::Key_AE] = XK_AE; + keysyms[Qt::Key_Ccedilla] = XK_Ccedilla; + keysyms[Qt::Key_Egrave] = XK_Egrave; + keysyms[Qt::Key_Eacute] = XK_Eacute; + keysyms[Qt::Key_Ecircumflex] = XK_Ecircumflex; + keysyms[Qt::Key_Ediaeresis] = XK_Ediaeresis; + keysyms[Qt::Key_Igrave] = XK_Igrave; + keysyms[Qt::Key_Iacute] = XK_Iacute; + keysyms[Qt::Key_Icircumflex] = XK_Icircumflex; + keysyms[Qt::Key_Idiaeresis] = XK_Idiaeresis; + keysyms[Qt::Key_ETH] = XK_ETH; + keysyms[Qt::Key_Ntilde] = XK_Ntilde; + keysyms[Qt::Key_Ograve] = XK_Ograve; + keysyms[Qt::Key_Oacute] = XK_Oacute; + keysyms[Qt::Key_Ocircumflex] = XK_Ocircumflex; + keysyms[Qt::Key_Otilde] = XK_Otilde; + keysyms[Qt::Key_Odiaeresis] = XK_Odiaeresis; + keysyms[Qt::Key_division] = XK_division; + keysyms[Qt::Key_Ooblique] = XK_Ooblique; + keysyms[Qt::Key_Ugrave] = XK_Ugrave; + keysyms[Qt::Key_Uacute] = XK_Uacute; + keysyms[Qt::Key_Ucircumflex] = XK_Ucircumflex; + keysyms[Qt::Key_Udiaeresis] = XK_Udiaeresis; + keysyms[Qt::Key_Yacute] = XK_Yacute; + keysyms[Qt::Key_THORN] = XK_THORN; + keysyms[Qt::Key_ydiaeresis] = XK_ydiaeresis; + keysyms[Qt::Key_AltGr] = XK_ISO_Level3_Shift; + keysyms[Qt::Key_Multi_key] = XK_Multi_key; + keysyms[Qt::Key_Codeinput] = XK_Codeinput; + keysyms[Qt::Key_SingleCandidate] = XK_SingleCandidate; + keysyms[Qt::Key_MultipleCandidate] = XK_MultipleCandidate; + keysyms[Qt::Key_PreviousCandidate] = XK_PreviousCandidate; + keysyms[Qt::Key_Mode_switch] = XK_Mode_switch; +// keysyms[Qt::Key_script_switch] = XK_script_switch; + keysyms[Qt::Key_Kanji] = XK_Kanji; + keysyms[Qt::Key_Muhenkan] = XK_Muhenkan; +// keysyms[Qt::Key_Henkan_Mode] = XK_Henkan_Mode; + keysyms[Qt::Key_Henkan] = XK_Henkan; + keysyms[Qt::Key_Romaji] = XK_Romaji; + keysyms[Qt::Key_Hiragana] = XK_Hiragana; + keysyms[Qt::Key_Katakana] = XK_Katakana; + keysyms[Qt::Key_Hiragana_Katakana] = XK_Hiragana_Katakana; + keysyms[Qt::Key_Zenkaku] = XK_Zenkaku; + keysyms[Qt::Key_Hankaku] = XK_Hankaku; + keysyms[Qt::Key_Zenkaku_Hankaku] = XK_Zenkaku_Hankaku; + keysyms[Qt::Key_Touroku] = XK_Touroku; + keysyms[Qt::Key_Massyo] = XK_Massyo; + keysyms[Qt::Key_Kana_Lock] = XK_Kana_Lock; + keysyms[Qt::Key_Kana_Shift] = XK_Kana_Shift; + keysyms[Qt::Key_Eisu_Shift] = XK_Eisu_Shift; + keysyms[Qt::Key_Eisu_toggle] = XK_Eisu_toggle; +// keysyms[Qt::Key_Kanji_Bangou] = XK_Kanji_Bangou; +// keysyms[Qt::Key_Zen_Koho] = XK_Zen_Koho; +// keysyms[Qt::Key_Mae_Koho] = XK_Mae_Koho; + keysyms[Qt::Key_Hangul] = XK_Hangul; + keysyms[Qt::Key_Hangul_Hanja] = XK_Hangul_Hanja; + keysyms[Qt::Key_Hangul] = XK_Hangul; + keysyms[Qt::Key_Hangul_Start] = XK_Hangul_Start; + keysyms[Qt::Key_Hangul_End] = XK_Hangul_End; + keysyms[Qt::Key_Hangul_Hanja] = XK_Hangul_Hanja; + keysyms[Qt::Key_Hangul_Jamo] = XK_Hangul_Jamo; + keysyms[Qt::Key_Hangul_Romaja] = XK_Hangul_Romaja; +// keysyms[Qt::Key_Hangul_Codeinput] = XK_Hangul_Codeinput; + keysyms[Qt::Key_Hangul_Jeonja] = XK_Hangul_Jeonja; + keysyms[Qt::Key_Hangul_Banja] = XK_Hangul_Banja; + keysyms[Qt::Key_Hangul_PreHanja] = XK_Hangul_PreHanja; + keysyms[Qt::Key_Hangul_PostHanja] = XK_Hangul_PostHanja; +// keysyms[Qt::Key_Hangul_SingleCandidate] = XK_Hangul_SingleCandidate; +// keysyms[Qt::Key_Hangul_MultipleCandidate] = XK_Hangul_MultipleCandidate; +// keysyms[Qt::Key_Hangul_PreviousCandidate] = XK_Hangul_PreviousCandidate; + keysyms[Qt::Key_Hangul_Special] = XK_Hangul_Special; +// keysyms[Qt::Key_Hangul_switch] = XK_Hangul_switch; + keysyms[Qt::Key_Dead_Grave] = XK_dead_grave; + keysyms[Qt::Key_Dead_Acute] = XK_dead_acute; + keysyms[Qt::Key_Dead_Circumflex] = XK_dead_circumflex; + keysyms[Qt::Key_Dead_Tilde] = XK_dead_tilde; + keysyms[Qt::Key_Dead_Macron] = XK_dead_macron; + keysyms[Qt::Key_Dead_Breve] = XK_dead_breve; + keysyms[Qt::Key_Dead_Abovedot] = XK_dead_abovedot; + keysyms[Qt::Key_Dead_Diaeresis] = XK_dead_diaeresis; + keysyms[Qt::Key_Dead_Abovering] = XK_dead_abovering; + keysyms[Qt::Key_Dead_Doubleacute] = XK_dead_doubleacute; + keysyms[Qt::Key_Dead_Caron] = XK_dead_caron; + keysyms[Qt::Key_Dead_Cedilla] = XK_dead_cedilla; + keysyms[Qt::Key_Dead_Ogonek] = XK_dead_ogonek; + keysyms[Qt::Key_Dead_Iota] = XK_dead_iota; + keysyms[Qt::Key_Dead_Voiced_Sound] = XK_dead_voiced_sound; + keysyms[Qt::Key_Dead_Semivoiced_Sound] = XK_dead_semivoiced_sound; + keysyms[Qt::Key_Dead_Belowdot] = XK_dead_belowdot; + keysyms[Qt::Key_Dead_Hook] = XK_dead_hook; + keysyms[Qt::Key_Dead_Horn] = XK_dead_horn; +// keysyms[Qt::Key_Back] = XK_Back; +// keysyms[Qt::Key_Forward] = XK_Forward; +// keysyms[Qt::Key_Stop] = XK_Stop; +// keysyms[Qt::Key_Refresh] = XK_Refresh; +// keysyms[Qt::Key_VolumeDown] = XK_VolumeDown; +// keysyms[Qt::Key_VolumeMute] = XK_VolumeMute; +// keysyms[Qt::Key_VolumeUp] = XK_VolumeUp; +// keysyms[Qt::Key_BassBoost] = XK_BassBoost; +// keysyms[Qt::Key_BassUp] = XK_BassUp; +// keysyms[Qt::Key_BassDown] = XK_BassDown; +// keysyms[Qt::Key_TrebleUp] = XK_TrebleUp; +// keysyms[Qt::Key_TrebleDown] = XK_TrebleDown; +// keysyms[Qt::Key_MediaPlay] = XK_MediaPlay; +// keysyms[Qt::Key_MediaStop] = XK_MediaStop; +// keysyms[Qt::Key_MediaPrevious] = XK_MediaPrevious; +// keysyms[Qt::Key_MediaNext] = XK_MediaNext; +// keysyms[Qt::Key_MediaRecord] = XK_MediaRecord; +// keysyms[Qt::Key_HomePage] = XK_HomePage; +// keysyms[Qt::Key_Favorites] = XK_Favorites; +// keysyms[Qt::Key_Search] = XK_Search; +// keysyms[Qt::Key_Standby] = XK_Standby; +// keysyms[Qt::Key_OpenUrl] = XK_OpenUrl; +// keysyms[Qt::Key_LaunchMail] = XK_LaunchMail; +// keysyms[Qt::Key_LaunchMedia] = XK_LaunchMedia; +// keysyms[Qt::Key_Launch0] = XK_Launch0; +// keysyms[Qt::Key_Launch1] = XK_Launch1; +// keysyms[Qt::Key_Launch2] = XK_Launch2; +// keysyms[Qt::Key_Launch3] = XK_Launch3; +// keysyms[Qt::Key_Launch4] = XK_Launch4; +// keysyms[Qt::Key_Launch5] = XK_Launch5; +// keysyms[Qt::Key_Launch6] = XK_Launch6; +// keysyms[Qt::Key_Launch7] = XK_Launch7; +// keysyms[Qt::Key_Launch8] = XK_Launch8; +// keysyms[Qt::Key_Launch9] = XK_Launch9; +// keysyms[Qt::Key_LaunchA] = XK_LaunchA; +// keysyms[Qt::Key_LaunchB] = XK_LaunchB; +// keysyms[Qt::Key_LaunchC] = XK_LaunchC; +// keysyms[Qt::Key_LaunchD] = XK_LaunchD; +// keysyms[Qt::Key_LaunchE] = XK_LaunchE; +// keysyms[Qt::Key_LaunchF] = XK_LaunchF; +// keysyms[Qt::Key_Display] = XK_Display; +// keysyms[Qt::Key_MediaLast] = XK_MediaLast; + keysyms[Qt::Key_Select] = XK_Select; +// keysyms[Qt::Key_Yes] = XK_Yes; +// keysyms[Qt::Key_No] = XK_No; + keysyms[Qt::Key_Cancel] = XK_Cancel; +// keysyms[Qt::Key_Printer] = XK_Printer; + keysyms[Qt::Key_Execute] = XK_Execute; +// keysyms[Qt::Key_Sleep] = XK_Sleep; +// keysyms[Qt::Key_MediaPlay] = XK_MediaPlay; +// keysyms[Qt::Key_Zoom] = XK_Zoom; +// keysyms[Qt::Key_Jisho] = XK_Jisho; +// keysyms[Qt::Key_Oyayubi_Left] = XK_Oyayubi_Left; +// keysyms[Qt::Key_Oyayubi_Right] = XK_Oyayubi_Right; +// keysyms[Qt::Key_Context1] = XK_Context1; +// keysyms[Qt::Key_Context2] = XK_Context2; +// keysyms[Qt::Key_Context3] = XK_Context3; +// keysyms[Qt::Key_Context4] = XK_Context4; +// keysyms[Qt::Key_Call] = XK_Call; +// keysyms[Qt::Key_Hangup] = XK_Hangup; +// keysyms[Qt::Key_Flip] = XK_Flip; + keysyms[Qt::Key_unknown] = XK_VoidSymbol; +} + +/* Store Keycodes for "normal" Latin1 keys: */ +int basic_keycodes[0x100]; +int basic_modifiers[0x100]; + +int modifier_keycodes[8]; +Qt::KeyboardModifier modifier_meaning[8]; + +int xmodifier_from_qtmodifier(Qt::KeyboardModifier m) { + for(int i = 0; i < 8; i++) { + if(m == modifier_meaning[i]) + return i; + } + return -1; +} + +typedef std::map qt_to_xmodifier_type; +typedef std::map x_to_qtmodifier_type; +qt_to_xmodifier_type qt_to_xmodifier; +x_to_qtmodifier_type x_to_qtmodifier; + +typedef std::multimap modifier_to_keycode_map; +modifier_to_keycode_map modifier_to_keycode; +typedef std::map keycode_to_modifier_map; +keycode_to_modifier_map keycode_to_modifier; + +void initialize_basic_keycodes() +{ + for(int i = 0; i < 8; i++) { + modifier_keycodes[i] = -1; + } + for(int i = 0; i < 0x100; i++) { + basic_keycodes[i] = -1; + } + + Display* dpy = X11InputUtils::display(); + int min_keycode, max_keycode; + XDisplayKeycodes(dpy, &min_keycode, &max_keycode); + + int xkb_opcode, xkb_event, xkb_error, xkb_major, xkb_minor; + bool xkb_present = XkbQueryExtension(dpy, &xkb_opcode, &xkb_event, &xkb_error, &xkb_major, &xkb_minor); + if(xkb_present) { + int keysyms_per_code; + const int count = max_keycode - min_keycode + 1; + KeySym* mapping = XGetKeyboardMapping(dpy, min_keycode, count, &keysyms_per_code); + for(int i = 0; i < count; i++) + { + for(int j = 0; j < keysyms_per_code; j++) + { + const int idx = i * keysyms_per_code + j; + const KeySym ks = mapping[idx]; + const int keycode = min_keycode + i; + + if(ks >= ' ' && ks < 0x100) + { + if(ks == XK_at) { + ConsoleLog writeLine(QString("Keycode %1 (%2) gives `@' with modifiers `%3', but it is really `%4'").arg(keycode).arg(XKeysymToString(XKeycodeToKeysym(dpy, keycode, 0))).arg(modifiers_to_string(j)).arg(XKeysymToString(XKeycodeToKeysym(dpy, keycode, j)))); + + } + + if(basic_keycodes[ks] != -1) + continue; // already found + + basic_keycodes[ks] = keycode; + basic_modifiers[ks] = j; + } + } + } + XFree(mapping); + } + else + { + for(int i = min_keycode; i <= max_keycode; i++) + { + for(int j = 0; j <= 0xff; j++) + { + KeySym ks = 0; + unsigned int unconsumed; + if(!XkbLookupKeySym(dpy, i, j, &unconsumed, &ks) || ks == NoSymbol) + continue; + + if(ks && (ks < 0x100)) + { +// ConsoleLog writeLine(QString("Keycode %1 (%2) seems to give `%3' with modifiers `%4', of which `%5' are unconsumed") +// .arg(i) +// .arg(XKeysymToString(XKeycodeToKeysym(dpy, i, 0))) +// .arg(XKeysymToString(ks)) +// .arg(modifiers_to_string(j)) +// .arg(modifiers_to_string(unconsumed))); + if(basic_keycodes[ks] != -1) + continue; + + basic_keycodes[ks] = i; + basic_modifiers[ks] = unconsumed & j; + } + } + } + } + + // find out which keycodes cause the modifier state bits: + XModifierKeymap* modkm; + modkm = XGetModifierMapping(dpy); + + const int shift_kc = XKeysymToKeycode(dpy, XK_Shift_L); + const int control_kc = XKeysymToKeycode(dpy, XK_Control_L); + const int alt_kc = XKeysymToKeycode(dpy, XK_Alt_L); + const int meta_kc = XKeysymToKeycode(dpy, XK_Meta_L); + const int switch_kc = XKeysymToKeycode(dpy, XK_Mode_switch); + + for(int i = 0; i < 8; i++) { + for(int j = 0; j < modkm->max_keypermod; j++) { + const int idx = i * modkm->max_keypermod + j; + const int kc = modkm->modifiermap[idx]; + + modifier_to_keycode.insert(std::make_pair((1< int_set_type; +int_set_type pressed_modifier_keys; +xmodifier_type current_modifiers; + +void trackModifiers(int keycode, bool down) +{ + // is this a modifier key? + const bool is_modifier = keycode_to_modifier.find(keycode) != keycode_to_modifier.end(); + + if(!is_modifier) + { + return; + } + + if(down) { + pressed_modifier_keys.insert(keycode); + } else { + pressed_modifier_keys.erase(keycode); + } + + int_set_type::iterator i, end = pressed_modifier_keys.end(); + xmodifier_type modifs = 0; + for(i = pressed_modifier_keys.begin(); i != end; i++) + { + keycode_to_modifier_map::iterator found_kc = keycode_to_modifier.find(*i); + if(found_kc != keycode_to_modifier.end()) + { + modifs |= (*found_kc).second; + } + } + current_modifiers = modifs; + + ConsoleLog writeLine(QString("[trackModifiers] current modifiers: %1").arg(modifiers_to_string(modifs))); +} + +typedef std::pair modifier_tweak_type; +typedef std::vector tweak_sequence_type; + +void tweakModifiers(Display* dpy, xmodifier_type neededState, xmodifier_type actualState, tweak_sequence_type& tracker) +{ + ConsoleLog writeLine(QString("tweakModifiers: Trying to get to `%1' from `%2'").arg(modifiers_to_string(neededState)).arg(modifiers_to_string(actualState))); + for(int j = 0; j < 8; j++) + { + xmodifier_type i = 1<second == i) { + kc = *iter; + + // release this key: + ConsoleLog writeLine(QString(" releasing key %1").arg(kc)); + XTestFakeKeyEvent(dpy, kc, 0, CurrentTime); + tracker.push_back(std::make_pair(kc, false)); + } + } + } + + if(kc == -1) { + // strange, but we need to release some other key: + // we don't know which one, so we abort this and hope for the best + continue; + } + } + } +} + +void untweakModifiers(Display* dpy, tweak_sequence_type& tracker) +{ + tweak_sequence_type::iterator i, end = tracker.end(); + for(i = tracker.begin(); i != end; i++) { + modifier_tweak_type& t = *i; + XTestFakeKeyEvent(dpy, t.first, !t.second, CurrentTime); + } +} + +void X11FakeKeyboardHandler::initialize() +{ + initialize_keysyms(); + initialize_basic_keycodes(); + pressed_modifier_keys.clear(); + current_modifiers = 0; +} + +void X11FakeKeyboardHandler::handle(InputEvent const& evt) +{ + Display* dpy = X11InputUtils::display(); + + // find out which keysym caused this event: + lookup_table_const_iterator i = keysyms.find(evt.qt_keysym()); + if(i == keysyms.end()) { + // Special cases. We don't know how to directly translate those, so we will try to emulate them. + switch(evt.qt_keysym()) + { + case Qt::Key_Backtab: + handle(InputEvent(evt.type(), evt.code(), evt.qt_modifiers() | Qt::ShiftModifier | Qt::Key_Tab)); + break; + default: + ConsoleLog writeLine(QString("Unknown keysym received: %1").arg(evt.qt_keysym(), 8, 16)); + } + } else { + KeySym ks = (*i).second; + + // is it a "normal" key? + if(ks >= ' ' && ks < 0x100) + { + if(basic_keycodes[ks] != -1) + { + if(evt.isPress()) + { + // Try the simple way first: + // Does the keycode with current modifiers yield the requested symbol? + KeySym unmod_ks = XKeycodeToKeysym(dpy, basic_keycodes[ks], current_modifiers); + ConsoleLog writeLine(QString("Pressing the key for %1 would yield %2 right now.").arg(XKeysymToString(ks)).arg(XKeysymToString(unmod_ks))); + + if(ks == XKeycodeToKeysym(dpy, basic_keycodes[ks], current_modifiers)) + { + XTestFakeKeyEvent(dpy, basic_keycodes[ks], 1, CurrentTime); + } + else + { + // what modifier keys do we need to press? + xmodifier_type mods = translate_modifiers(evt.qt_modifiers()); + + // we may need to press additional modifiers to generate this keysym: + // but, since we do not differentiate upper and lower case, + // and instead let the sender handle those modifiers, + // we need to AND ShiftModifier and LockModifier out. + mods |= basic_modifiers[ks] & ~(1< +{ +public: + static void handle(InputEvent const&); + static void initialize(); +}; + +#endif /* X11FAKEKEYBOARDHANDLER_H_ */ diff --git a/src/input/x11FakeMouseHandler.cpp b/src/input/x11FakeMouseHandler.cpp new file mode 100644 index 0000000..432e19f --- /dev/null +++ b/src/input/x11FakeMouseHandler.cpp @@ -0,0 +1,59 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # x11FakeMouseHandler.h: + # - Handle mouse events on X11 - implementation + # -------------------------------------------------------------------------- + */ + +#include +#include +#include "x11InputUtils.h" +#include "x11FakeMouseHandler.h" + +void X11FakeMouseButtonHandler::handle(InputEvent const& evt) +{ + quint16 pressedButton = evt.pressedButton(); + + Display* dpy = X11InputUtils::display(); + + XTestGrabControl(dpy, 1); + for(int i = 0; i < 16; i++) { + if((1< +{ +public: + static void handle(InputEvent const&); +}; + +class X11FakeMouseMovementHandler : public DefaultInputEventHandler +{ +public: + static void handle(InputEvent const&); +}; + +#endif /* X11FAKEMOUSEHANDLER_H_ */ diff --git a/src/input/x11InputUtils.cpp b/src/input/x11InputUtils.cpp new file mode 100644 index 0000000..b589bd6 --- /dev/null +++ b/src/input/x11InputUtils.cpp @@ -0,0 +1,29 @@ +/* + # Copyright (c) 2009, 2010 - OpenSLX Project, Computer Center University of + # Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # ------------------------------------------------------------------------ + # src/input/x11InputUtils.h: + # Utilities for input handling under X11 - implementation + */ + +#include "x11InputUtils.h" + +static Display* _dpy = 0; + +void X11InputUtils::setDisplay(Display* dpy) +{ + _dpy = dpy; +} + +Display* X11InputUtils::display() +{ + return _dpy; +} diff --git a/src/input/x11InputUtils.h b/src/input/x11InputUtils.h new file mode 100644 index 0000000..9c85d09 --- /dev/null +++ b/src/input/x11InputUtils.h @@ -0,0 +1,27 @@ +/* + # Copyright (c) 2009, 2010 - OpenSLX Project, Computer Center University of + # Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # ------------------------------------------------------------------------ + # src/input/x11InputUtils.h: + # Utilities for input handling under X11 - interface + */ + +#ifndef X11INPUTUTILS_H_ +#define X11INPUTUTILS_H_ + +#include + +struct X11InputUtils { + static void setDisplay(Display*); + static Display* display(); +}; + +#endif /* X11INPUTUTILS_H_ */ diff --git a/src/pvs.cpp b/src/pvs.cpp index 2069e36..e44d04d 100644 --- a/src/pvs.cpp +++ b/src/pvs.cpp @@ -15,6 +15,9 @@ #include "src/net/pvsMsg.h" #include "src/net/pvsServiceDiscovery.h" #include "src/net/pvsDiscoveredServer.h" +#include "src/input/inputEvent.h" +#include "src/input/unprivilegedHandlerChain.h" +#include "src/input/x11InputUtils.h" // D-Bus #include "pvsadaptor.h" @@ -76,6 +79,7 @@ PVS::PVS() : sigaction(SIGINT, &act, 0); sigaction(SIGQUIT, &act, 0); + initializeInputEventHandling(); } PVS::~PVS() @@ -176,6 +180,12 @@ void PVS::onCommand(PVSMsg cmdMessage) unlock(); return; } + if (ident.compare("INPUTEVENT") == 0) + { + InputEvent evt; + eventFromString(message, evt); + handleInputEvent(evt); + } #ifdef never // prototype @@ -627,3 +637,17 @@ void PVS::signalHandler(int signal) } +// Input handling + +void PVS::handleInputEvent(InputEvent const& evt) +{ + std::string s = evt.toString(); + ConsoleLog writeLine(QString("Received input event: %1").arg(s.c_str())); + unprivileged_handler_chain::handle(evt); +} + +void PVS::initializeInputEventHandling() +{ + X11InputUtils::setDisplay(X11Info::display()); + unprivileged_handler_chain::initialize(); +} diff --git a/src/pvs.h b/src/pvs.h index 4b1e29e..8e94169 100644 --- a/src/pvs.h +++ b/src/pvs.h @@ -28,6 +28,7 @@ class PVSServiceDiscovery; class PVSDiscoveredServer; +class InputEvent; /** * PVSClient @@ -142,5 +143,8 @@ private: int _timerLockTest; int _timerLockDelay; + // input event handling: + void handleInputEvent(InputEvent const& evt); + void initializeInputEventHandling(); }; #endif /* PVSCLIENT_H_ */ diff --git a/src/util/clientGUIUtils.cpp b/src/util/clientGUIUtils.cpp index 4d4cc0d..a8a9487 100644 --- a/src/util/clientGUIUtils.cpp +++ b/src/util/clientGUIUtils.cpp @@ -2,7 +2,7 @@ BlankScreen::BlankScreen() { - dpy = XOpenDisplay(NULL); + dpy = X11Info::display(); scr = DefaultScreen(dpy); assert(dpy); blackColor = BlackPixel(dpy, DefaultScreen(dpy)); @@ -142,3 +142,14 @@ bool BlankScreen::unlock() lockMsg.clear(); return !(locked = false); } + +static Display* _dpy = 0; + +Display* X11Info::display() +{ + if(!_dpy) + { + _dpy = XOpenDisplay(0); + } + return _dpy; +} diff --git a/src/util/clientGUIUtils.h b/src/util/clientGUIUtils.h index 28b05cc..4da0a99 100644 --- a/src/util/clientGUIUtils.h +++ b/src/util/clientGUIUtils.h @@ -36,4 +36,10 @@ private: int offX, offY; }; +class X11Info +{ +public: + static Display* display(); +}; + #endif -- cgit v1.2.3-55-g7522 From ea719ff8b67916ac0080295bd0698e4ca2a134c8 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Mon, 4 Oct 2010 00:25:02 +0200 Subject: Fix a bug in closeup frame handling. If, while a frame was in closeup mode, the corresponding client went away, the ConnectionFrame would get deleted, but still referenced in ConnectionWindow::_closeupFrame. Naturally, this led to disaster when the same (but really new) frame would be selected again. QPointer guards against QObject's going AWOL. --- src/gui/connectionWindow.h | 3 ++- src/gui/mainWindow.cpp | 23 +++++++++++++---------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/gui/connectionWindow.h b/src/gui/connectionWindow.h index 81b5033..85e2881 100644 --- a/src/gui/connectionWindow.h +++ b/src/gui/connectionWindow.h @@ -9,6 +9,7 @@ #include #include #include +#include #define FRAME_DELAY 1000 // to comply with the standard value in the gui @@ -109,7 +110,7 @@ protected: private: ConnectionFrame* newConFrame(PVSClient* newConnection); // returns a new frame for the given connection ConnectionFrame* currentSingleFrame; // pointer to the frame thats currently in FullScreen - ConnectionFrame* _closeupFrame; + QPointer _closeupFrame; QPoint currentPosition (ConnectionFrame* cF); QMenu *menu; QAction *newDummy; diff --git a/src/gui/mainWindow.cpp b/src/gui/mainWindow.cpp index eb15e82..8a2b512 100644 --- a/src/gui/mainWindow.cpp +++ b/src/gui/mainWindow.cpp @@ -742,19 +742,22 @@ void MainWindow::closeUp() QMessageBox::information(this, "PVS", message); } } - else if (conWin->getCloseupFrame()) + else { - /*PVSClient* pvsClient = - PVSConnectionManager::getManager()->getClientFromIp( - selectedClients->front().toStdString().c_str());*/ - conWin->getCloseupFrame()->setWindowFlags(Qt::Widget); - conWin->getCloseupFrame()->paintCloseUp(conWin->getCloseupFrame()->getPrevWidth(), conWin->getCloseupFrame()->getPrevHeight()); - conWin->getCloseupFrame()->move(_framePosOnCloseUp);//back to the position before the closeup - if (conWin->getCloseupFrame()->getConnection()->getVNCConnection()) - conWin->getCloseupFrame()->getFrame()->getVNCClientThread()->setUpdatefreq(_updatefreq); + if (conWin->getCloseupFrame()) + { + /*PVSClient* pvsClient = + PVSConnectionManager::getManager()->getClientFromIp( + selectedClients->front().toStdString().c_str());*/ + conWin->getCloseupFrame()->setWindowFlags(Qt::Widget); + conWin->getCloseupFrame()->paintCloseUp(conWin->getCloseupFrame()->getPrevWidth(), conWin->getCloseupFrame()->getPrevHeight()); + conWin->getCloseupFrame()->move(_framePosOnCloseUp);//back to the position before the closeup + if (conWin->getCloseupFrame()->getConnection()->getVNCConnection()) + conWin->getCloseupFrame()->getFrame()->getVNCClientThread()->setUpdatefreq(_updatefreq); + conWin->setCloseupFrame(NULL); + } is_closeup = false; - conWin->setCloseupFrame(NULL); } } -- cgit v1.2.3-55-g7522 From 2eaaecd06784d3f0aa5542b69272bc1199ef128d Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 14:05:57 +0200 Subject: Fix forgotten NOT on test for Xkb presence --- src/input/x11FakeKeyboardHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input/x11FakeKeyboardHandler.cpp b/src/input/x11FakeKeyboardHandler.cpp index bb14b76..95e83ab 100644 --- a/src/input/x11FakeKeyboardHandler.cpp +++ b/src/input/x11FakeKeyboardHandler.cpp @@ -471,7 +471,7 @@ void initialize_basic_keycodes() int xkb_opcode, xkb_event, xkb_error, xkb_major, xkb_minor; bool xkb_present = XkbQueryExtension(dpy, &xkb_opcode, &xkb_event, &xkb_error, &xkb_major, &xkb_minor); - if(xkb_present) { + if(!xkb_present) { int keysyms_per_code; const int count = max_keycode - min_keycode + 1; KeySym* mapping = XGetKeyboardMapping(dpy, min_keycode, count, &keysyms_per_code); -- cgit v1.2.3-55-g7522 From 7fb94c8d48d70c47bb6c292cb5b58bc76b3ee376 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 14:09:40 +0200 Subject: Fix Xkb handling on certain systems. On some systems XTEST events are delivered via a special input device that may be configured with a different keymap than the physical keyboard. This patch fixes the keycode lookup to take the XTEST device into account. --- src/input/x11FakeKeyboardHandler.cpp | 77 +++++++++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 6 deletions(-) diff --git a/src/input/x11FakeKeyboardHandler.cpp b/src/input/x11FakeKeyboardHandler.cpp index 95e83ab..82cc437 100644 --- a/src/input/x11FakeKeyboardHandler.cpp +++ b/src/input/x11FakeKeyboardHandler.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include "x11InputUtils.h" @@ -502,14 +503,64 @@ void initialize_basic_keycodes() } else { + // Try to find out what device XTest will send events on: + int xkbDeviceId = XkbUseCoreKbd; + Atom xtestDeviceProp = XInternAtom(dpy, "XTEST Device", false); + int ndevinfos; + XIDeviceInfo* devinfos = XIQueryDevice(dpy, XIAllDevices, &ndevinfos); + if(devinfos) + { + for(int i = 0; i < ndevinfos && xkbDeviceId == XkbUseCoreKbd; i++) + { + XIDeviceInfo* devinfo = devinfos + i; + qDebug("Found device of type %d with name %s", devinfo->use, devinfo->name); + + // We want it to be a keyboard. + if(devinfo->use != XIMasterKeyboard && devinfo->use != XISlaveKeyboard) + continue; + + int nprops; + Atom* props = XIListProperties(dpy, devinfo->deviceid, &nprops); + if(props) + { + for(int j = 0; j < nprops && xkbDeviceId == XkbUseCoreKbd; j++) + { + Atom prop = props[j]; + if(prop == xtestDeviceProp) + { + // The device is the XTest Keyboard: + xkbDeviceId = devinfo->deviceid; + } + } + XFree(props); + } + } + XIFreeDeviceInfo(devinfos); + } + + // Okay, we know which device to query. Now get its keymap: + XkbDescPtr keybDesc = XkbGetKeyboard(dpy, XkbAllComponentsMask, xkbDeviceId); + if(!keybDesc) + { + qWarning("Unable to retrieve keyboard description for device %d. Falling back to unreliable global mapping", xkbDeviceId); + } + for(int i = min_keycode; i <= max_keycode; i++) { for(int j = 0; j <= 0xff; j++) { KeySym ks = 0; unsigned int unconsumed; - if(!XkbLookupKeySym(dpy, i, j, &unconsumed, &ks) || ks == NoSymbol) - continue; + if(keybDesc) + { + if(!XkbTranslateKeyCode(keybDesc, i, j, &unconsumed, &ks) || ks == NoSymbol) + continue; + } + else + { + if(!XkbLookupKeySym(dpy, i, j, &unconsumed, &ks) || ks == NoSymbol) + continue; + } if(ks && (ks < 0x100)) { @@ -527,6 +578,12 @@ void initialize_basic_keycodes() } } } + + // Free the keyboard description: + if(keybDesc) + { + XkbFreeKeyboard(keybDesc, XkbAllComponentsMask, true); + } } // find out which keycodes cause the modifier state bits: @@ -750,10 +807,18 @@ void X11FakeKeyboardHandler::handle(InputEvent const& evt) xmodifier_type mods = translate_modifiers(evt.qt_modifiers()); // we may need to press additional modifiers to generate this keysym: - // but, since we do not differentiate upper and lower case, - // and instead let the sender handle those modifiers, - // we need to AND ShiftModifier and LockModifier out. - mods |= basic_modifiers[ks] & ~(1<button(), event->buttons())); } } @@ -271,7 +271,7 @@ void Frame::mouseReleaseEvent ( QMouseEvent * event ) ConsoleLog writeLine("Captured remote control mouseReleaseEvent"); updateMousePosition(event); - sendInputEvent(InputEvent::mousePressRelease(event)); + sendInputEvent(InputEvent::mousePressRelease(event->button(), event->buttons())); } } @@ -501,8 +501,7 @@ void Frame::keyPressEvent(QKeyEvent* event) { // The action of the keyboard may depend on the position of the pointer sendMouseMotionEvent(); - InputEvent evt = InputEvent::keyboardPress(event); - sendInputEvent(evt); + sendInputEvent(InputEvent::keyboardPress(event->key(), event->modifiers())); } else { @@ -516,8 +515,7 @@ void Frame::keyReleaseEvent(QKeyEvent* event) { // The action of the keyboard may depend on the position of the pointer sendMouseMotionEvent(); - InputEvent evt = InputEvent::keyboardRelease(event); - sendInputEvent(evt); + sendInputEvent(InputEvent::keyboardRelease(event->key(), event->modifiers())); } else { diff --git a/src/input/inputEvent.cpp b/src/input/inputEvent.cpp index 04a84e7..9b69a3a 100644 --- a/src/input/inputEvent.cpp +++ b/src/input/inputEvent.cpp @@ -66,10 +66,10 @@ quint16 InputEvent::mouseButtonsFromQt(int b) return ret; } -InputEvent InputEvent::mousePressRelease(QMouseEvent const* evt) +InputEvent InputEvent::mousePressRelease(int qt_button, int qt_buttons) { - quint16 button = mouseButtonsFromQt(evt->button()); - quint16 buttons = mouseButtonsFromQt(evt->buttons()); + quint16 button = mouseButtonsFromQt(qt_button); + quint16 buttons = mouseButtonsFromQt(qt_buttons); quint16 code; if(buttons & button) @@ -85,14 +85,15 @@ InputEvent InputEvent::mousePressRelease(QMouseEvent const* evt) return InputEvent(ET_BUTTON, code, value); } -InputEvent InputEvent::keyboardPress(QKeyEvent const* evt) +InputEvent InputEvent::keyboardPress(int key, int mods) { - quint32 value = evt->key() | evt->modifiers(); + quint32 value = key | mods; return InputEvent(ET_KEY, EC_PRESS, value); } -InputEvent InputEvent::keyboardRelease(QKeyEvent const* evt) +InputEvent InputEvent::keyboardRelease(int key, int mods) { - quint32 value = evt->key() | evt->modifiers(); + quint32 value = key | mods; return InputEvent(ET_KEY, EC_RELEASE, value); } + diff --git a/src/input/inputEvent.h b/src/input/inputEvent.h index 917cc64..7a64bfc 100644 --- a/src/input/inputEvent.h +++ b/src/input/inputEvent.h @@ -62,9 +62,9 @@ public: static uint16_t mouseButtonsFromQt(int b); - static InputEvent mousePressRelease(QMouseEvent const* event); - static InputEvent keyboardPress(QKeyEvent const* event); - static InputEvent keyboardRelease(QKeyEvent const* event); + static InputEvent mousePressRelease(int button, int buttons); + static InputEvent keyboardPress(int key, int mods); + static InputEvent keyboardRelease(int key, int mods); static const uint16_t ET_KEY = 0; static const uint16_t ET_BUTTON = 1; @@ -76,6 +76,7 @@ public: static const uint16_t EC_REBOOT = 2; static const uint16_t EC_SYSRQ = 3; static const uint16_t EC_KILL_X = 4; + static const uint16_t EC_SAY_HELLO = 5; //< for debugging purposes typedef uint32_t event_key; -- cgit v1.2.3-55-g7522 From c5c46660130456afea285e460be44e1c723e4a49 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 15:07:43 +0200 Subject: Refactor InputEvent handler code. - Make static methods virtual and store instances in the chains. - Propagate security context information. - Saner security policy implementation. --- src/input/inputEventHandler.h | 108 +++++++++++++++++++++++------------ src/input/inputHandlerChain.h | 34 +++++++++++ src/input/unprivilegedHandlerChain.h | 34 ----------- src/input/x11FakeKeyboardHandler.cpp | 4 +- src/input/x11FakeKeyboardHandler.h | 4 +- src/input/x11FakeMouseHandler.cpp | 6 +- src/input/x11FakeMouseHandler.h | 4 +- src/pvs.cpp | 6 +- src/pvs.h | 2 + 9 files changed, 121 insertions(+), 81 deletions(-) create mode 100644 src/input/inputHandlerChain.h delete mode 100644 src/input/unprivilegedHandlerChain.h diff --git a/src/input/inputEventHandler.h b/src/input/inputEventHandler.h index 3910f93..330f5a7 100644 --- a/src/input/inputEventHandler.h +++ b/src/input/inputEventHandler.h @@ -27,49 +27,80 @@ #define HANDLER_CODE_DONT_CARE 0xffff #define HANDLER_VALUE_DONT_CARE 0xffffffff +class InputEventContext +{ +public: + virtual pid_t getSenderPid() const = 0; + virtual uid_t getSenderUid() const = 0; + virtual gid_t getSenderGid() const = 0; +}; + +struct SpecialInputEventDescription +{ + SpecialInputEventDescription(QString const& d, quint16 t, quint16 c, quint32 v = 0) + : descriptionString(d), evtType(t), evtCode(c), evtValue(v) + { + } + + QString descriptionString; + quint16 evtType; + quint16 evtCode; + quint32 evtValue; + + InputEvent toEvent() const + { + return InputEvent(evtType, evtCode, evtValue); + } +}; + template class DefaultInputEventHandler { public: - static bool matches(InputEvent const& evt) { - if(Type != 0xffff) { + virtual bool matches(InputEvent const& evt, InputEventContext const*) { + if(Type != HANDLER_TYPE_DONT_CARE) { if(evt.type() != Type) return false; } - if(Code != 0xffff) { + if(Code != HANDLER_CODE_DONT_CARE) { if(evt.code() != Code) return false; } - if(Value != 0xffffffff) { + if(Value != HANDLER_VALUE_DONT_CARE) { if(evt.value() != Value) return false; } return true; } - static void initialize() + virtual void initialize() { } -}; -namespace policy { + virtual void handle(InputEvent const& evt, InputEventContext const*) = 0; -struct NoSecurityCheck { - static bool allow(InputEvent const&) { - return true; + static void describeInto(QList& description) + { } }; -struct PhysicalSeatSecurityCheck { - static bool allow(InputEvent const&) { - return /* TODO implement */ true; - } +namespace policy { + +enum SecurityFlags { + SEC_PHYSICAL_SEAT = 1, + SEC_PRIVILEGED_USER = 2 }; -struct AlwaysDenySecurityCheck { - static bool allow(InputEvent const&) { - return false; +bool allowPhysicalSeat(InputEvent const& evt, InputEventContext const* ctx); +bool allowPrivilegedUser(InputEvent const& evt, InputEventContext const* ctx); + +template +struct Security +{ + bool allow(InputEvent const& evt, InputEventContext const* ctx) + { + return true; } }; @@ -107,39 +138,43 @@ template class HandlerHelper { public: - static bool handle(InputEvent const& evt) { - if(!SecurityPolicy::allow(evt)) + bool handle(InputEvent const& evt, InputEventContext const* context = 0) { + if(!securityPolicy.allow(evt, context)) { return true; } - if(Delegate::matches(evt)) { - Delegate::handle(evt); + if(delegate.matches(evt, context)) { + delegate.handle(evt, context); return true; } else { return false; } } - static void initialize() + void initialize() { - Delegate::initialize(); + delegate.initialize(); } + +private: + Delegate delegate; + SecurityPolicy securityPolicy; }; template class HandlerHelper { public: - static bool handle(InputEvent const& evt) { + bool handle(InputEvent const& evt, InputEventContext const* context = 0) { return false; } - static void initialize() + void initialize() { } }; -template +template > struct Handler : public HandlerHelper { }; @@ -153,28 +188,31 @@ private: typedef typename boost::mpl::deref::type handler_type; + handler_type _handler; + next_in_chain _next; + public: - static void handle(InputEvent const& evt) { - if(!handler_type::handle(evt)) { - next_in_chain::handle(evt); + void handle(InputEvent const& evt, InputEventContext const* context = 0) { + if(!_handler.handle(evt, context)) { + _next.handle(evt, context); } } - static void initialize() { - handler_type::initialize(); - next_in_chain::initialize(); + void initialize() { + _handler.initialize(); + _next.initialize(); + } } }; template struct InputEventHandlerChainHelper { -public: - static void handle(InputEvent const&) { + void handle(InputEvent const&, InputEventContext const* context = 0) { // do nothing } - static void initialize() { + void initialize() { // do nothing } }; diff --git a/src/input/inputHandlerChain.h b/src/input/inputHandlerChain.h new file mode 100644 index 0000000..4bb9fe5 --- /dev/null +++ b/src/input/inputHandlerChain.h @@ -0,0 +1,34 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # inputHandlerChain.h: + # - Definition of the input handler chain + # -------------------------------------------------------------------------- + */ + +#ifndef INPUTHANDLERCHAIN_H_ +#define INPUTHANDLERCHAIN_H_ + +#include +#include "inputEventHandler.h" + +#include "x11FakeKeyboardHandler.h" +#include "x11FakeMouseHandler.h" + +typedef boost::mpl::list< + Handler >, + Handler >, + Handler > +>::type unprivileged_handler_list; + +typedef InputEventHandlerChain unprivileged_handler_chain; + +#endif /* INPUTHANDLERCHAIN_H_ */ diff --git a/src/input/unprivilegedHandlerChain.h b/src/input/unprivilegedHandlerChain.h deleted file mode 100644 index 734720a..0000000 --- a/src/input/unprivilegedHandlerChain.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg - # - # This program is free software distributed under the GPL version 2. - # See http://openslx.org/COPYING - # - # If you have any feedback please consult http://openslx.org/feedback and - # send your suggestions, praise, or complaints to feedback@openslx.org - # - # General information about OpenSLX can be found at http://openslx.org/ - # -------------------------------------------------------------------------- - # inputHandlerChain.h: - # - Definition of the input handler chain - # -------------------------------------------------------------------------- - */ - -#ifndef INPUTHANDLERCHAIN_H_ -#define INPUTHANDLERCHAIN_H_ - -#include -#include "inputEventHandler.h" - -#include "x11FakeKeyboardHandler.h" -#include "x11FakeMouseHandler.h" - -typedef boost::mpl::list< - Handler >, - Handler >, - Handler > ->::type unprivileged_handler_list; - -typedef InputEventHandlerChain unprivileged_handler_chain; - -#endif /* INPUTHANDLERCHAIN_H_ */ diff --git a/src/input/x11FakeKeyboardHandler.cpp b/src/input/x11FakeKeyboardHandler.cpp index 82cc437..3a0b864 100644 --- a/src/input/x11FakeKeyboardHandler.cpp +++ b/src/input/x11FakeKeyboardHandler.cpp @@ -20,6 +20,7 @@ // Qt headers need to be included before X11 headers #include #include +#include "x11FakeKeyboardHandler.h" // #include #include #include @@ -30,7 +31,6 @@ #include #include #include "x11InputUtils.h" -#include "x11FakeKeyboardHandler.h" //////////////////////// INPUT EVENT TRANSLATION ///////////////////////////////// @@ -766,7 +766,7 @@ void X11FakeKeyboardHandler::initialize() current_modifiers = 0; } -void X11FakeKeyboardHandler::handle(InputEvent const& evt) +void X11FakeKeyboardHandler::handle(InputEvent const& evt, InputEventContext const*) { Display* dpy = X11InputUtils::display(); diff --git a/src/input/x11FakeKeyboardHandler.h b/src/input/x11FakeKeyboardHandler.h index c888202..3dde7cc 100644 --- a/src/input/x11FakeKeyboardHandler.h +++ b/src/input/x11FakeKeyboardHandler.h @@ -22,8 +22,8 @@ class X11FakeKeyboardHandler : public DefaultInputEventHandler { public: - static void handle(InputEvent const&); - static void initialize(); + void handle(InputEvent const&, InputEventContext const* = 0); + void initialize(); }; #endif /* X11FAKEKEYBOARDHANDLER_H_ */ diff --git a/src/input/x11FakeMouseHandler.cpp b/src/input/x11FakeMouseHandler.cpp index 432e19f..58415d5 100644 --- a/src/input/x11FakeMouseHandler.cpp +++ b/src/input/x11FakeMouseHandler.cpp @@ -14,12 +14,12 @@ # -------------------------------------------------------------------------- */ +#include "x11FakeMouseHandler.h" // need to include before X headers #include #include #include "x11InputUtils.h" -#include "x11FakeMouseHandler.h" -void X11FakeMouseButtonHandler::handle(InputEvent const& evt) +void X11FakeMouseButtonHandler::handle(InputEvent const& evt, InputEventContext const*) { quint16 pressedButton = evt.pressedButton(); @@ -42,7 +42,7 @@ void X11FakeMouseButtonHandler::handle(InputEvent const& evt) XFlush(dpy); } -void X11FakeMouseMovementHandler::handle(InputEvent const& evt) +void X11FakeMouseMovementHandler::handle(InputEvent const& evt, InputEventContext const*) { ConsoleLog writeLine(QString("Received mouse motion event (%1,%2)").arg(evt.xCoord()).arg(evt.yCoord())); Display* dpy = X11InputUtils::display(); diff --git a/src/input/x11FakeMouseHandler.h b/src/input/x11FakeMouseHandler.h index 19ed29e..0e32256 100644 --- a/src/input/x11FakeMouseHandler.h +++ b/src/input/x11FakeMouseHandler.h @@ -22,13 +22,13 @@ class X11FakeMouseButtonHandler : public DefaultInputEventHandler { public: - static void handle(InputEvent const&); + void handle(InputEvent const&, InputEventContext const* = 0); }; class X11FakeMouseMovementHandler : public DefaultInputEventHandler { public: - static void handle(InputEvent const&); + void handle(InputEvent const&, InputEventContext const* = 0); }; #endif /* X11FAKEMOUSEHANDLER_H_ */ diff --git a/src/pvs.cpp b/src/pvs.cpp index e44d04d..911ed0d 100644 --- a/src/pvs.cpp +++ b/src/pvs.cpp @@ -16,7 +16,7 @@ #include "src/net/pvsServiceDiscovery.h" #include "src/net/pvsDiscoveredServer.h" #include "src/input/inputEvent.h" -#include "src/input/unprivilegedHandlerChain.h" +#include "src/input/inputHandlerChain.h" #include "src/input/x11InputUtils.h" // D-Bus @@ -643,11 +643,11 @@ void PVS::handleInputEvent(InputEvent const& evt) { std::string s = evt.toString(); ConsoleLog writeLine(QString("Received input event: %1").arg(s.c_str())); - unprivileged_handler_chain::handle(evt); + _inputEventHandlers.handle(evt); } void PVS::initializeInputEventHandling() { X11InputUtils::setDisplay(X11Info::display()); - unprivileged_handler_chain::initialize(); + _inputEventHandlers.initialize(); } diff --git a/src/pvs.h b/src/pvs.h index 8e94169..f4e53c0 100644 --- a/src/pvs.h +++ b/src/pvs.h @@ -24,6 +24,7 @@ #include "src/version.h" #include "src/util/consoleLogger.h" #include "src/util/clientGUIUtils.h" +#include "src/input/inputHandlerChain.h" class PVSServiceDiscovery; @@ -144,6 +145,7 @@ private: int _timerLockDelay; // input event handling: + unprivileged_handler_chain _inputEventHandlers; void handleInputEvent(InputEvent const& evt); void initializeInputEventHandling(); }; -- cgit v1.2.3-55-g7522 From ee159229bcfb6f2dbd19ef16e37fa2c65cd3846d Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 15:15:36 +0200 Subject: Add Permission checking and session information code. --- src/input/inputEventHandler.cpp | 36 +++++ src/input/inputEventHandler.h | 4 + src/input/pvsCheckPrivileges.cpp | 314 +++++++++++++++++++++++++++++++++++++++ src/input/pvsCheckPrivileges.h | 139 +++++++++++++++++ 4 files changed, 493 insertions(+) create mode 100644 src/input/inputEventHandler.cpp create mode 100644 src/input/pvsCheckPrivileges.cpp create mode 100644 src/input/pvsCheckPrivileges.h diff --git a/src/input/inputEventHandler.cpp b/src/input/inputEventHandler.cpp new file mode 100644 index 0000000..c16c358 --- /dev/null +++ b/src/input/inputEventHandler.cpp @@ -0,0 +1,36 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # inputEventHandler.h: + # - Common definitions for input event handlers - implementation + # -------------------------------------------------------------------------- + */ + +#include "inputEventHandler.h" +#include "pvsCheckPrivileges.h" + +bool policy::allowPrivilegedUser(InputEvent const& evt, InputEventContext const* ctx) +{ + if(ctx) + return PVSCheckPrivileges::instance()->require(PVSCheckPrivileges::SESSION_UNKNOWN, PVSCheckPrivileges::USER_PRIVILEGED, + ctx); + else + return false; +} + +bool policy::allowPhysicalSeat(InputEvent const& evt, InputEventContext const* ctx) +{ + if(ctx) + return PVSCheckPrivileges::instance()->require(PVSCheckPrivileges::SESSION_LOCAL, PVSCheckPrivileges::USER_UNKNOWN, + ctx); + else + return false; +} diff --git a/src/input/inputEventHandler.h b/src/input/inputEventHandler.h index 330f5a7..a0ada2e 100644 --- a/src/input/inputEventHandler.h +++ b/src/input/inputEventHandler.h @@ -100,6 +100,10 @@ struct Security { bool allow(InputEvent const& evt, InputEventContext const* ctx) { + if((flags & SEC_PHYSICAL_SEAT) && !allowPhysicalSeat(evt, ctx)) + return false; + if((flags & SEC_PRIVILEGED_USER) && !allowPrivilegedUser(evt, ctx)) + return false; return true; } }; diff --git a/src/input/pvsCheckPrivileges.cpp b/src/input/pvsCheckPrivileges.cpp new file mode 100644 index 0000000..56073bc --- /dev/null +++ b/src/input/pvsCheckPrivileges.cpp @@ -0,0 +1,314 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # pvsCheckPrivileges.cpp: + # - A small program that checks whether or not it is called from + # a physical seat and conditionally executes the pvs input daemon. + # Additional security-relevant conditions should be checked here. + # + # The program is called with exactly one parameter, specifying the + # number of the file descriptor which is to be passed to its child. + # -------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pvsCheckPrivileges.h" + +using namespace std; + +#define TIMEOUT_VALUE 10000 /* Wait for max. 10 seconds */ + +// We need these classes for PolicyKit access: +struct PolKitSubject { + QString subject_kind; + QMap subject_details; +}; +Q_DECLARE_METATYPE(PolKitSubject); + +QDBusArgument& operator<<(QDBusArgument& arg, PolKitSubject const& subj) +{ + arg.beginStructure(); + arg << subj.subject_kind << subj.subject_details; + arg.endStructure(); + return arg; +} + +QDBusArgument const& operator>>(QDBusArgument const& arg, PolKitSubject& subj) +{ + arg.beginStructure(); + arg >> subj.subject_kind >> subj.subject_details; + arg.endStructure(); + return arg; +} + +struct PolKitAuthReply { + bool isAuthorized; + bool isChallenge; + QMap details; +}; +Q_DECLARE_METATYPE(PolKitAuthReply); + +QDBusArgument& operator<<(QDBusArgument& arg, PolKitAuthReply const& reply) +{ + arg.beginStructure(); + arg << reply.isAuthorized << reply.isChallenge << reply.details; + arg.endStructure(); + return arg; +} + +QDBusArgument const& operator>>(QDBusArgument const& arg, PolKitAuthReply& reply) +{ + arg.beginStructure(); + arg >> reply.isAuthorized >> reply.isChallenge >> reply.details; + arg.endStructure(); + return arg; +} + +// We need to pass QMap to QVariant: +typedef QMap QStringStringMap; +Q_DECLARE_METATYPE(QStringStringMap) + +PVSCheckPrivileges* PVSCheckPrivileges::instance() +{ + static PVSCheckPrivileges static_instance; + return &static_instance; +} + +PVSCheckPrivileges::PVSCheckPrivileges() +{ +} + +PVSCheckPrivileges::~PVSCheckPrivileges() +{ +} + +QString PVSCheckPrivileges::getSessionReference(CachedInputContext const& sender) +{ + if(!sender.isValid()) + { + return QString(); + } + + QString sessionReference = _savedConsoleKitSession.value(sender, QString()); + if(sessionReference.isNull()) + { + QDBusConnection conn = QDBusConnection::systemBus(); + // Find the name of the current session: + QDBusInterface manager("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager", conn); + QDBusReply replyGetSession = manager.call(QDBus::Block, "GetSessionForUnixProcess", (quint32)sender.pid); + if(!replyGetSession.isValid()) + { + qWarning("Reply to GetSessionForUnixProcess is invalid: %s: %s", replyGetSession.error().name().toLocal8Bit().constData(), replyGetSession.error().message().toLocal8Bit().constData()); + return QString(); + } + _savedConsoleKitSession[sender] = sessionReference = replyGetSession.value().path(); + } + return sessionReference; +} + +PVSCheckPrivileges::SessionKind PVSCheckPrivileges::getSessionKind(CachedInputContext const& sender) +{ + if(!sender.isValid()) + { + return SESSION_UNKNOWN; + } + + // if the sender is root, we always suppose he works locally. + if(sender.uid == 0) + { + return SESSION_LOCAL; + } + + QString sessionReference = getSessionReference(sender); + if(sessionReference.isNull()) + { + return SESSION_LOOKUP_FAILURE; + } + + QDBusConnection conn = QDBusConnection::systemBus(); + QDBusInterface session("org.freedesktop.ConsoleKit", sessionReference, "org.freedesktop.ConsoleKit.Session", conn); + QDBusReply replyIsLocal = session.call(QDBus::Block, "IsLocal"); + + if(!replyIsLocal.isValid()) + { + qWarning("Unable to find out whether the current session is local: %s: %s", replyIsLocal.error().name().toLocal8Bit().constData(), replyIsLocal.error().message().toLocal8Bit().constData()); + return SESSION_LOOKUP_FAILURE; + } + return replyIsLocal.value() ? SESSION_LOCAL : SESSION_NONLOCAL; +} + +PVSCheckPrivileges::UserPrivilege PVSCheckPrivileges::getUserPrivilege(CachedInputContext const& sender) +{ + // Always allow root: + if(sender.uid == 0) + return USER_PRIVILEGED; + + // For PolKit, we need the start-time of the process. + // On Linux, we can get it from /proc: + QString procStat = QString("/proc/%1/stat").arg(sender.pid); + QFile procStatFile(procStat); + if(!procStatFile.exists()) + { + qWarning("Could not look up any info on process %d, its %s file does not exist", sender.pid, procStat.toLocal8Bit().constData()); + return USER_LOOKUP_FAILURE; + } + procStatFile.open(QIODevice::ReadOnly); + QByteArray procStatBytes = procStatFile.readAll(); + qDebug() << "Read stat file: " << procStatBytes; + QString procStatLine = QString::fromLocal8Bit(procStatBytes.constData(), procStatBytes.length()); + QStringList procStatFields = procStatLine.split(QRegExp("\\s+")); + qDebug() << "Found stat fields: " << procStatFields; + bool ok; + quint64 startTime = procStatFields[21].toULongLong(&ok); + if(!ok) + { + qWarning("Could not find out start time for process %d", (int)sender.pid); + return USER_LOOKUP_FAILURE; + } + + // Okay, we got the startTime. Now ask PolicyKit: + + QDBusConnection conn = QDBusConnection::systemBus(); + QDBusInterface intf("org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority", "org.freedesktop.PolicyKit1.Authority", conn); + PolKitSubject subj; + subj.subject_kind = "unix-process"; + subj.subject_details["pid"] = (quint32)sender.pid; + subj.subject_details["start-time"] = startTime; + QDBusReply reply = intf.call(QDBus::Block, + QLatin1String("CheckAuthorization"), + QVariant::fromValue(subj), + "org.openslx.pvs.privilegedinput", + QVariant::fromValue(QMap()) /* No details */, + (quint32)1 /* Allow Interaction */, + QUuid::createUuid().toString() /* Cancellation ID */); + if(!reply.isValid()) + { + QDBusError err = reply.error(); + + qWarning("Reply to CheckAuthorization is invalid: %s: %s", err.name().toLocal8Bit().constData(), err.message().toLocal8Bit().constData()); + return USER_LOOKUP_FAILURE; + } + return reply.value().isAuthorized ? USER_PRIVILEGED : USER_UNPRIVILEGED; +} + +QString PVSCheckPrivileges::getX11SessionName(CachedInputContext const& sender) +{ + QString sessionReference = getSessionReference(sender); + if(sessionReference.isNull()) + { + return QString(); + } + + QDBusConnection conn = QDBusConnection::systemBus(); + QDBusInterface intf("org.freedesktop.ConsoleKit", sessionReference, "org.freedesktop.ConsoleKit.Session", conn); + QDBusReply reply = intf.call(QDBus::Block, QLatin1String("GetX11Display")); + if(!reply.isValid()) + { + QDBusError err = reply.error(); + + qWarning("Reply to GetX11Display is invalid: %s: %s", err.name().toLocal8Bit().constData(), err.message().toLocal8Bit().constData()); + return QString(); + } + + return reply.value(); +} + +QString PVSCheckPrivileges::getX11DisplayDevice(CachedInputContext const& sender) +{ + QString sessionReference = getSessionReference(sender); + if(sessionReference.isNull()) + { + return QString(); + } + + QDBusConnection conn = QDBusConnection::systemBus(); + QDBusInterface intf("org.freedesktop.ConsoleKit", sessionReference, "org.freedesktop.ConsoleKit.Session", conn); + QDBusReply reply = intf.call(QDBus::Block, QLatin1String("GetX11DisplayDevice")); + if(!reply.isValid()) + { + QDBusError err = reply.error(); + + qWarning("Reply to GetX11DisplayDevice is invalid: %s: %s", err.name().toLocal8Bit().constData(), err.message().toLocal8Bit().constData()); + return QString(); + } + + return reply.value(); +} + +bool PVSCheckPrivileges::require(SessionKind sessionKind, CachedInputContext const& sender) +{ + SessionKind cachedSessionKind; + + if(sessionKind < SESSION_NONLOCAL) + { + if((cachedSessionKind = _savedSessionKind.value(sender, SESSION_UNKNOWN)) == SESSION_UNKNOWN) + { + cachedSessionKind = getSessionKind(sender); + if(cachedSessionKind != SESSION_LOOKUP_FAILURE) + _savedSessionKind[sender] = cachedSessionKind; + qDebug("Got session kind: %s", toString(cachedSessionKind).toLocal8Bit().constData()); + } + if(cachedSessionKind > sessionKind) + return false; + } + + return true; +} + +bool PVSCheckPrivileges::require(UserPrivilege userPrivilege, CachedInputContext const& sender) +{ + UserPrivilege cachedUserPrivilege; + + if(userPrivilege < USER_UNPRIVILEGED) + { + if((cachedUserPrivilege = _savedUserPrivilege.value(sender, USER_UNKNOWN)) == USER_UNKNOWN) + { + cachedUserPrivilege = getUserPrivilege(sender); + if(cachedUserPrivilege != USER_LOOKUP_FAILURE) + _savedUserPrivilege[sender] = cachedUserPrivilege; + qDebug("Got user privilege: %s", toString(cachedUserPrivilege).toLocal8Bit().constData()); + } + if(cachedUserPrivilege > userPrivilege) + return false; + } + return true; +} + +bool PVSCheckPrivileges::require(SessionKind sessionKind, + UserPrivilege userPrivilege, + CachedInputContext const& sender) +{ + if(!require(sessionKind, sender)) + return false; + if(!require(userPrivilege, sender)) + return false; + return true; +} + +uint qHash(CachedInputContext const& p) +{ + return qHash(qMakePair(p.pid, qMakePair(p.uid, p.gid))); +} diff --git a/src/input/pvsCheckPrivileges.h b/src/input/pvsCheckPrivileges.h new file mode 100644 index 0000000..e5cc9a4 --- /dev/null +++ b/src/input/pvsCheckPrivileges.h @@ -0,0 +1,139 @@ +/* + # Copyright (c) 2009,2010 - OpenSLX Project, Computer Center University of + # Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # ----------------------------------------------------------------------------- + # src/net/pvsCheckPrivileges_linux.h + # - Linux implementation of privilege checking + # ----------------------------------------------------------------------------- + */ + +#ifndef PVSCHECKPRIVILEGES_H_ +#define PVSCHECKPRIVILEGES_H_ + +#include +#include +#include +#include "inputEventHandler.h" + +struct CachedInputContext +{ + CachedInputContext(InputEventContext const* source) + { + if(source) + { + pid = source->getSenderPid(); + uid = source->getSenderUid(); + gid = source->getSenderGid(); + } + else + { + pid = (pid_t)-1; + uid = (uid_t)-1; + gid = (gid_t)-1; + } + } + + CachedInputContext() + { + pid = (pid_t)-1; + uid = (uid_t)-1; + gid = (gid_t)-1; + } + + pid_t pid; + uid_t uid; + gid_t gid; + + bool isValid() const + { + return (pid != (pid_t)-1) && (uid != (uid_t)-1) && (gid != (gid_t)-1); + } + + bool operator==(CachedInputContext const& other) const + { + return (other.pid == pid) && (other.uid == uid) && (other.gid == gid); + } +}; +uint qHash(CachedInputContext const& p); + +class PVSCheckPrivileges +{ +public: + typedef enum { + SESSION_LOOKUP_FAILURE, // Comes first because we default to assume + // the session is local if we cannot look it + // up. + SESSION_LOCAL, + SESSION_NONLOCAL, + SESSION_UNKNOWN + } SessionKind; + static QString toString(SessionKind k) + { + switch(k) + { + case SESSION_LOOKUP_FAILURE: return "SESSION_LOOKUP_FAILURE"; + case SESSION_LOCAL: return "SESSION_LOCAL"; + case SESSION_NONLOCAL: return "SESSION_NONLOCAL"; + case SESSION_UNKNOWN: return "SESSION_UNKNOWN"; + default: return QString("unknown value (%1)").arg(k); + } + } + + typedef enum { + USER_PRIVILEGED, + USER_UNPRIVILEGED, + USER_LOOKUP_FAILURE, // Comes last because we default to assume + // the user is unprivileged if we cannot get + // permission from PolicyKit. + USER_UNKNOWN + } UserPrivilege; + static QString toString(UserPrivilege k) + { + switch(k) + { + case USER_PRIVILEGED: return "USER_PRIVILEGED"; + case USER_UNPRIVILEGED: return "USER_UNPRIVILEGED"; + case USER_LOOKUP_FAILURE: return "USER_LOOKUP_FAILURE"; + case USER_UNKNOWN: return "USER_UNKNOWN"; + default: return QString("unknown value (%1)").arg(k); + } + } + + static PVSCheckPrivileges* instance(); + + bool require(SessionKind sessionKind, CachedInputContext const& sender); + bool require(UserPrivilege userPrivilege, CachedInputContext const& sender); + bool require(SessionKind sessionKind, UserPrivilege userPrivilege, CachedInputContext const& sender); + QString getX11SessionName(CachedInputContext const& sender); + QString getX11DisplayDevice(CachedInputContext const& sender); + +private: + PVSCheckPrivileges(); + virtual ~PVSCheckPrivileges(); + + typedef QPair > piduidgid; + piduidgid makePidUidGid(pid_t pid, uid_t uid, gid_t gid) + { + return qMakePair(pid, qMakePair(uid, gid)); + } + + QString getSessionReference(CachedInputContext const& sender); + SessionKind getSessionKind(CachedInputContext const& sender); + UserPrivilege getUserPrivilege(CachedInputContext const& sender); + + static PVSCheckPrivileges* _instance; + + QHash _savedUserPrivilege; + QHash _savedSessionKind; + QHash _savedConsoleKitSession; +}; + +#endif /* PVSCHECKPRIVILEGES_H_ */ -- cgit v1.2.3-55-g7522 From e3dcd9bc390bab4e650ec5bcbc1720cdcdea0247 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 15:26:13 +0200 Subject: Add description to input event handlers so they can --- src/input/inputEventHandler.h | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/input/inputEventHandler.h b/src/input/inputEventHandler.h index a0ada2e..44713c2 100644 --- a/src/input/inputEventHandler.h +++ b/src/input/inputEventHandler.h @@ -18,6 +18,9 @@ #define INPUTEVENTHANDLER_H_ #include +#include +#include +#include #include #include #include @@ -57,6 +60,12 @@ template class DefaultInputEventHandler { +protected: + static QString tr(char const* string) + { + return QCoreApplication::translate("InputEventHandler", string); + } + public: virtual bool matches(InputEvent const& evt, InputEventContext const*) { if(Type != HANDLER_TYPE_DONT_CARE) { @@ -160,6 +169,11 @@ public: delegate.initialize(); } + static void describeInto(QList& list) + { + Delegate::describeInto(list); + } + private: Delegate delegate; SecurityPolicy securityPolicy; @@ -176,6 +190,10 @@ public: void initialize() { } + + static void describeInto(QList&) + { + } }; template > @@ -206,6 +224,18 @@ public: _handler.initialize(); _next.initialize(); } + + static void describeInto(QList& list) + { + handler_type::describeInto(list); + next_in_chain::describeInto(list); + } + + static QList describe() + { + QList list; + describeInto(list); + return list; } }; @@ -219,6 +249,16 @@ struct InputEventHandlerChainHelper void initialize() { // do nothing } + + static void describeInto(QList&) + { + // do nothing + } + + static QList describe() + { + return QList(); + } }; template -- cgit v1.2.3-55-g7522 From ac05f7d367ae9983e7996738871efbdbf3ec66e8 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 15:30:04 +0200 Subject: Implement privileged input daemon, first version without handlers. --- src/input/CMakeLists.txt | 45 ++++++- src/input/inputHandlerChain.h | 8 +- src/input/privilegedHandlerForwarder.cpp | 82 +++++++++++++ src/input/privilegedHandlerForwarder.h | 32 +++++ src/input/pvsPrivInputHandler.cpp | 108 +++++++++++++++++ src/input/pvsPrivInputHandler.h | 45 +++++++ src/input/pvsPrivInputSocket.cpp | 196 +++++++++++++++++++++++++++++++ src/input/pvsPrivInputSocket.h | 31 +++++ src/input/pvsprivinputd.cpp | 147 +++++++++++++++++++++++ 9 files changed, 687 insertions(+), 7 deletions(-) create mode 100644 src/input/privilegedHandlerForwarder.cpp create mode 100644 src/input/privilegedHandlerForwarder.h create mode 100644 src/input/pvsPrivInputHandler.cpp create mode 100644 src/input/pvsPrivInputHandler.h create mode 100644 src/input/pvsPrivInputSocket.cpp create mode 100644 src/input/pvsPrivInputSocket.h create mode 100644 src/input/pvsprivinputd.cpp diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index aa52ec3..5f009c5 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -2,18 +2,51 @@ INCLUDE(${QT_USE_FILE}) SET(pvsinput_SRCS inputEvent.cpp + inputEventHandler.cpp ) + +if(UNIX) + set(pvsprivinputd_SRCS + pvsprivinputd.cpp + pvsPrivInputHandler.cpp + pvsCheckPrivileges.cpp + ) + + set(pvsprivinputd_MOC_HDRS + pvsPrivInputHandler.h + ) -IF(UNIX) - list(APPEND pvsinput_SRCS - x11InputUtils.cpp + qt4_wrap_cpp(pvsprivinputd_MOC_SRCS + ${pvsprivinputd_MOC_HDRS} + ) + + add_executable(pvsprivinputd + ${pvsprivinputd_SRCS} + ${pvsprivinputd_MOC_SRCS} + ) + + set_property(SOURCE ${pvsprivinputd_SRCS} ${pvsprivinputd_MOC_SRCS} + APPEND + PROPERTY COMPILE_FLAGS " -I${QT_QTCORE_INCLUDE_DIR} -I${QT_QTDBUS_INCLUDE_DIR} -I${QT_QTNETWORK_INCLUDE_DIR}") + + target_link_libraries(pvsprivinputd + pvsinput + ${QT_QTCORE_LIBRARY} + ${QT_QTDBUS_LIBRARY} + ${QT_QTNETWORK_LIBRARY} + ) + + list(APPEND pvsinput_SRCS + pvsPrivInputSocket.cpp + x11InputUtils.cpp x11FakeKeyboardHandler.cpp - x11FakeMouseHandler.cpp) -ENDIF() + x11FakeMouseHandler.cpp + privilegedHandlerForwarder.cpp + ) +endif() ADD_LIBRARY( pvsinput STATIC - ${pvsinput_HDRS} ${pvsinput_SRCS} ) diff --git a/src/input/inputHandlerChain.h b/src/input/inputHandlerChain.h index 4bb9fe5..61c3d62 100644 --- a/src/input/inputHandlerChain.h +++ b/src/input/inputHandlerChain.h @@ -22,13 +22,19 @@ #include "x11FakeKeyboardHandler.h" #include "x11FakeMouseHandler.h" +#include "privilegedHandlerForwarder.h" typedef boost::mpl::list< Handler >, Handler >, - Handler > + Handler >, + Handler >::type unprivileged_handler_list; typedef InputEventHandlerChain unprivileged_handler_chain; +typedef boost::mpl::list< +>::type privileged_handler_list; + +typedef InputEventHandlerChain privileged_handler_chain; #endif /* INPUTHANDLERCHAIN_H_ */ diff --git a/src/input/privilegedHandlerForwarder.cpp b/src/input/privilegedHandlerForwarder.cpp new file mode 100644 index 0000000..3c0b3bd --- /dev/null +++ b/src/input/privilegedHandlerForwarder.cpp @@ -0,0 +1,82 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # inputHandlerChain.cpp: + # - Forward privileged input events to a special handler process - implementation + # -------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pvsPrivInputSocket.h" +#include "privilegedHandlerForwarder.h" + +using namespace std; + +#ifndef UNIX_PATH_MAX +# define UNIX_PATH_MAX 108 /* according to unix(7) */ +#endif + +void PrivilegedHandlerForwarder::initialize() +{ + QSettings settings(QSettings::NativeFormat, QSettings::SystemScope, "openslx", "pvsinputd"); + + QString defaultPath = "/tmp/pvsprivinputd.sock"; + QByteArray socketPath = settings.value("socketpath", defaultPath).toString().toLocal8Bit(); + + _socket = pvsPrivInputMakeClientSocket(); + if(_socket < 0) + { + return; + } +} + +void PrivilegedHandlerForwarder::handle(InputEvent const& evt, InputEventContext const*) +{ + qDebug("Trying to handle %s in PrivilegedHandlerForwarder", evt.toString().c_str()); + if(_socket < 0) + { + initialize(); + } + + QByteArray data; + QDataStream strm(&data, QIODevice::WriteOnly); + strm.setByteOrder(QDataStream::BigEndian); + strm << evt; + + assert(data.size() == 8); + + int delta = 0; + int count = 0; + do { + delta = write(_socket, data.constData(), data.size()); + if(delta < 0) + { + qWarning("Error while communicating with pvsprivinputd: %s", strerror(errno)); + close(_socket); + + // Try again: + initialize(); + } + else if(delta != 8) + { + // This should normally not happen. + qWarning("Could not send a complete packet. Only %d bytes sent", delta); + } + count++; + } while(delta != 8 && count < 3); +} diff --git a/src/input/privilegedHandlerForwarder.h b/src/input/privilegedHandlerForwarder.h new file mode 100644 index 0000000..37e8f24 --- /dev/null +++ b/src/input/privilegedHandlerForwarder.h @@ -0,0 +1,32 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # inputHandlerChain.h: + # - Forward privileged input events to a special handler process. + # -------------------------------------------------------------------------- + */ + +#ifndef PRIVILEGEDHANDLERFORWARDER_H_ +#define PRIVILEGEDHANDLERFORWARDER_H_ + +#include "inputEventHandler.h" + +class PrivilegedHandlerForwarder : public DefaultInputEventHandler +{ +public: + void handle(InputEvent const& evt, InputEventContext const* = 0); + void initialize(); + +private: + int _socket; +}; + +#endif /* PRIVILEGEDHANDLERFORWARDER_H_ */ diff --git a/src/input/pvsPrivInputHandler.cpp b/src/input/pvsPrivInputHandler.cpp new file mode 100644 index 0000000..70ed1bc --- /dev/null +++ b/src/input/pvsPrivInputHandler.cpp @@ -0,0 +1,108 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # pvsPrivInputHandler.h + # - Handle privileged input connection requests - implementation + # -------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include "inputEvent.h" +#include "inputEventHandler.h" +#include "pvsPrivInputSocket.h" +#include "pvsPrivInputHandler.h" + +using namespace std; + +class PrivInputContext : public InputEventContext +{ +public: + PrivInputContext(pid_t pid, uid_t uid, gid_t gid) + : _pid(pid), _uid(uid), _gid(gid) + { + } + + pid_t getSenderPid() const + { + return _pid; + } + + uid_t getSenderUid() const + { + return _uid; + } + + gid_t getSenderGid() const + { + return _gid; + } + +private: + pid_t _pid; + uid_t _uid; + gid_t _gid; +}; + +PVSPrivInputHandler::PVSPrivInputHandler(int fd, QObject* parent) : + QObject(parent) +{ + _fd = fd; + _notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this); + _notifier->setEnabled(true); + connect(_notifier, SIGNAL(activated(int)), this, SLOT(canRead())); +} + +PVSPrivInputHandler::~PVSPrivInputHandler() +{ + delete _notifier; +} + +void PVSPrivInputHandler::canRead() +{ + _notifier->setEnabled(false); + + // We need to retrieve the credentials of the process: + size_t siz = 8; + _messageAssembly.clear(); + _messageAssembly.resize(8); + pid_t pid; + uid_t uid; + gid_t gid; + int err; + + if(!pvsPrivInputRecvMessage(_fd, _messageAssembly.data(), siz, pid, uid, gid, &err)) + { + close(_fd); + deleteLater(); + return; + } + else + { + if(siz != 8) + { + qWarning("Malformed PVS Input Event packet"); + return; + } + QDataStream strm(&_messageAssembly, QIODevice::ReadOnly); + InputEvent evt; + strm.setByteOrder(QDataStream::BigEndian); + strm >> evt; + PrivInputContext ctx(pid, uid, gid); + _handlerChain.handle(evt, &ctx); + } + + _notifier->setEnabled(true); +} diff --git a/src/input/pvsPrivInputHandler.h b/src/input/pvsPrivInputHandler.h new file mode 100644 index 0000000..9980cdf --- /dev/null +++ b/src/input/pvsPrivInputHandler.h @@ -0,0 +1,45 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # pvsPrivInputHandler.h + # - Handle privileged input connection requests - interface + # -------------------------------------------------------------------------- + */ + +#ifndef PVSPRIVINPUTHANDLER_H_ +#define PVSPRIVINPUTHANDLER_H_ + +#include +#include +#include +#include "inputHandlerChain.h" + +class QSocketNotifier; + +class PVSPrivInputHandler : public QObject +{ + Q_OBJECT +public: + PVSPrivInputHandler(int fd, QObject* parent = 0); + virtual ~PVSPrivInputHandler(); + +private slots: + void canRead(); + +private: + QSocketNotifier* _notifier; + QByteArray _messageAssembly; + int _bytes; + int _fd; + privileged_handler_chain _handlerChain; +}; + +#endif /* PVSPRIVINPUTHANDLER_H_ */ diff --git a/src/input/pvsPrivInputSocket.cpp b/src/input/pvsPrivInputSocket.cpp new file mode 100644 index 0000000..2428582 --- /dev/null +++ b/src/input/pvsPrivInputSocket.cpp @@ -0,0 +1,196 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # pvsPrivInputSocket.h: + # - Centralize knowledge of socket address and connection options + # for pvsprivinputd - implementation + # -------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include "pvsPrivInputSocket.h" + +using namespace std; + +#ifndef UNIX_PATH_MAX +# define UNIX_PATH_MAX 108 /* according to unix(7) */ +#endif + +QString pvsPrivInputGetSocketAddress() +{ + QSettings settings(QSettings::NativeFormat, QSettings::SystemScope, "openslx", "pvsprivinputd"); + return settings.value("socketpath", "/tmp/pvsprivinputd.sock").toString(); +} + +int pvsPrivInputMakeClientSocket() +{ + int sock = socket(AF_UNIX, SOCK_DGRAM, 0); + if(sock < 0) + { + qWarning("Could not create a socket: %s", strerror(errno)); + return -1; + } + + QByteArray socketPath = pvsPrivInputGetSocketAddress().toLocal8Bit(); + struct sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, socketPath.constData(), UNIX_PATH_MAX - 1); + if(connect(sock, reinterpret_cast(&addr), + sizeof(addr)) < 0) + { + qWarning("Could not connect to pvsprivinputd at %s: %s", socketPath.constData(), strerror(errno)); + close(sock); + return -1; + } + + return sock; +} + +int pvsPrivInputMakeServerSocket() +{ + int sock = socket(AF_UNIX, SOCK_DGRAM, 0); + if(sock < 0) + { + qCritical("Could not create a socket: %s", strerror(errno)); + return -1; + } + + // Bind to the address: + QByteArray socketPath = pvsPrivInputGetSocketAddress().toLocal8Bit(); + struct sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, socketPath.constData(), UNIX_PATH_MAX - 1); + if(bind(sock, reinterpret_cast(&addr), sizeof(addr)) < 0) + { + qCritical("Could not bind socket to %s", strerror(errno)); + close(sock); + return -1; + } + + // Announce that credentials are requested: + int passcred = 1; + if(setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &passcred, sizeof(passcred)) < 0) + { + // We will not operate without credentials. + qCritical("Could not request peer credentials: %s", strerror(errno)); + close(sock); + return -1; + } + +#if 0 /* Only for SOCK_STREAM: */ + // Listen for connections: + if(listen(sock, 1) < 0) + { + qCritical("Could not listen for connections to %s: %s", socketPath.constData(), strerror(errno)); + close(sock); + return -1; + } +#endif + + return sock; +} + +bool pvsPrivInputSendMessage(int sock, void* buf, size_t _len, int* err) +{ + /* + * Portability note: All UNIX-like systems can transmit credentials over UNIX + * sockets, but only Linux does it automagically. + */ + + long len = (long)_len; + + // send(2) does not split messages on a SOCK_DGRAM socket. + int e = send(sock, buf, len, 0); + if(e < 0) + { + qWarning("Failed to send message of length %d over socket %d: %s", (unsigned)len, e, strerror(errno)); + if(err) + *err = errno; + return false; + } + else if(e < len) + { + qWarning("Failed to send a complete message of length %d over socket %d, only %d bytes were sent", (unsigned)len, sock, e); + if(err) + *err = errno; + return false; + } + + return true; +} + +bool pvsPrivInputRecvMessage(int sock, void* buf, size_t& len, + pid_t& pid, uid_t& uid, gid_t& gid, int* err) +{ + struct iovec iov; + struct msghdr msg; + char ctlbuf[1024]; + iov.iov_base = buf; + iov.iov_len = len; + msg.msg_name = 0; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = &ctlbuf; + msg.msg_controllen = sizeof(ctlbuf); + msg.msg_flags = 0; + int bytes_read = recvmsg(sock, &msg, 0); + if(bytes_read < 0) + { + qWarning("Could not read from socket: %s", strerror(errno)); + if(err) + *err = errno; + return false; + } + + pid = -1; + uid = -1; + gid = -1; + + struct cmsghdr* cmsg; + for(cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + if(cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) + { + struct ucred* creds = reinterpret_cast(CMSG_DATA(cmsg)); + pid = creds->pid; + uid = creds->uid; + gid = creds->gid; + break; + } + else if(cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) + { + // We need to close passed file descriptors. If we don't, we + // have a denial-of-service vulnerability. + int* fds = reinterpret_cast(CMSG_DATA(cmsg)); + unsigned num_fds = cmsg->cmsg_len / sizeof(int); + for(unsigned i = 0; i < num_fds; i++) + { + close(fds[i]); + } + } + } + + if(pid == (pid_t)-1 || uid == (uid_t)-1 || gid == (gid_t)-1) + { + *err = 0; + return false; + } + + return true; +} diff --git a/src/input/pvsPrivInputSocket.h b/src/input/pvsPrivInputSocket.h new file mode 100644 index 0000000..e6fb0c0 --- /dev/null +++ b/src/input/pvsPrivInputSocket.h @@ -0,0 +1,31 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # pvsPrivInputSocket.h: + # - Centralize knowledge of socket address and connection options + # for pvsprivinputd - interface + # -------------------------------------------------------------------------- + */ + +#ifndef PVSPRIVINPUTSOCKET_H_ +#define PVSPRIVINPUTSOCKET_H_ + +#include +#include +#include + +QString pvsPrivInputGetSocketAddress(); +int pvsPrivInputMakeClientSocket(); +int pvsPrivInputMakeServerSocket(); +bool pvsPrivInputSendMessage(int sock, void* buf, size_t len, int* err = 0); +bool pvsPrivInputRecvMessage(int sock, void* buf, size_t& len, pid_t& pid, uid_t& uid, gid_t& gid, int* err = 0); + +#endif /* PVSPRIVINPUTSOCKET_H_ */ diff --git a/src/input/pvsprivinputd.cpp b/src/input/pvsprivinputd.cpp new file mode 100644 index 0000000..3a5e1a8 --- /dev/null +++ b/src/input/pvsprivinputd.cpp @@ -0,0 +1,147 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # pvsprivinputd.cpp: + # - Handle privileged input events - daemon + # -------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pvsPrivInputSocket.h" +#include "pvsPrivInputHandler.h" + +using namespace std; + +#ifndef UNIX_PATH_MAX +# define UNIX_PATH_MAX 108 /* according to unix(7) */ +#endif + +QByteArray socketPath; +int sigintFds[2]; + +static void unlinkSocket() +{ + if(!socketPath.isNull()) + { + unlink(socketPath.constData()); + } +} + +static void onInterrupt(int) +{ + char buf[] = { '!' }; + char msg[] = "SIGINT caught. Quitting.\n"; + write(sigintFds[0], buf, 1); + write(1, msg, sizeof(msg)-1); +} + +int main(int argc, char** argv) +{ + QCoreApplication app(argc, argv); + app.setApplicationName("pvsprivinputd"); + app.setOrganizationName("openslx"); + + bool no_fork = app.arguments().contains("--no-fork", Qt::CaseInsensitive); + if(!no_fork) + { + pid_t pid; + pid_t sid; + + pid = fork(); + if(pid < 0) + { + qCritical("Could not fork: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + if(pid > 0) + { + exit(EXIT_SUCCESS); + } + + // We are now inside a child process. + // Detach from parent: + sid = setsid(); + if(sid < 0) + { + qCritical("Could not detach from parent process: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + + // Change to some benign directory: + if(chdir("/") < 0) + { + qCritical("Could not change directory: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + } + + // Okay, we are running as a daemon. Defer reopening standard file descriptors + // so we can report errors. + + // Set the file creation mask to allow all processes to connect to + // the socket (access control is handled differently): + umask(0); + + // Ignore SIGPIPE. Connection errors are handled internally. + // According to signal(2), the only error is that the signal number + // is invalid. This cannot happen. + signal(SIGPIPE, SIG_IGN); + + // set up signal handling: + if(socketpair(AF_UNIX, SOCK_STREAM, 0, sigintFds) < 0) + { + qCritical("Could not set up signal handling. Giving up."); + exit(EXIT_FAILURE); + } + socketPath = pvsPrivInputGetSocketAddress().toLocal8Bit(); + QSocketNotifier sigintWatcher(sigintFds[1], QSocketNotifier::Read); + QObject::connect(&sigintWatcher, SIGNAL(activated(int)), &app, SLOT(quit())); + signal(SIGINT, onInterrupt); + + // Create our socket: + int sock = pvsPrivInputMakeServerSocket(); + if(sock < 0) + { + exit(EXIT_FAILURE); + } + + // Our setup is complete. + if(!no_fork) + { + // Reopen standard file descriptors: + freopen("/dev/null", "r", stdin); + freopen("/dev/null", "w", stderr); + freopen("/dev/null", "w", stdout); + } + + // Install our main object + PVSPrivInputHandler handler(sock); + + atexit(unlinkSocket); + + // and run the main loop. + int ret = app.exec(); + + return ret; +} -- cgit v1.2.3-55-g7522 From 9b8657b04122838e3149208789a004baaa3ba635 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 15:31:11 +0200 Subject: Implement example privileged SayHelloHandler. --- src/input/CMakeLists.txt | 1 + src/input/inputHandlerChain.h | 2 ++ src/input/sayHelloHandler.cpp | 25 +++++++++++++++++++++++++ src/input/sayHelloHandler.h | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+) create mode 100644 src/input/sayHelloHandler.cpp create mode 100644 src/input/sayHelloHandler.h diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 5f009c5..4aaa845 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -10,6 +10,7 @@ if(UNIX) pvsprivinputd.cpp pvsPrivInputHandler.cpp pvsCheckPrivileges.cpp + sayHelloHandler.cpp ) set(pvsprivinputd_MOC_HDRS diff --git a/src/input/inputHandlerChain.h b/src/input/inputHandlerChain.h index 61c3d62..57168c2 100644 --- a/src/input/inputHandlerChain.h +++ b/src/input/inputHandlerChain.h @@ -23,6 +23,7 @@ #include "x11FakeKeyboardHandler.h" #include "x11FakeMouseHandler.h" #include "privilegedHandlerForwarder.h" +#include "sayHelloHandler.h" typedef boost::mpl::list< Handler >, @@ -34,6 +35,7 @@ typedef boost::mpl::list< typedef InputEventHandlerChain unprivileged_handler_chain; typedef boost::mpl::list< + Handler >::type privileged_handler_list; typedef InputEventHandlerChain privileged_handler_chain; diff --git a/src/input/sayHelloHandler.cpp b/src/input/sayHelloHandler.cpp new file mode 100644 index 0000000..fc6f668 --- /dev/null +++ b/src/input/sayHelloHandler.cpp @@ -0,0 +1,25 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # rebootSystemHandler.h + # - Handle "say hello" requests - interface + # -------------------------------------------------------------------------- + */ + +#include "sayHelloHandler.h" +#include + +using namespace std; + +void SayHelloHandler::handle(InputEvent const&, InputEventContext const* ctx) +{ + cerr << "I'm right here! You sent this message from pid " << ctx->getSenderPid() << " as user " << ctx->getSenderUid() << " with gid " << ctx->getSenderGid() << endl; +} diff --git a/src/input/sayHelloHandler.h b/src/input/sayHelloHandler.h new file mode 100644 index 0000000..b0d4c7e --- /dev/null +++ b/src/input/sayHelloHandler.h @@ -0,0 +1,34 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # rebootSystemHandler.h + # - Handle "say hello" requests - interface + # -------------------------------------------------------------------------- + */ + +#ifndef SAYHELLOHANDLER_CPP_ +#define SAYHELLOHANDLER_CPP_ + +#include +#include "inputEventHandler.h" + +class SayHelloHandler : public DefaultInputEventHandler +{ +public: + void handle(InputEvent const&, InputEventContext const*); + + static void describeInto(QList& list) + { + list << SpecialInputEventDescription(tr(QT_TRANSLATE_NOOP("InputEventHandler", "Say Hello")), InputEvent::ET_SPECIAL, InputEvent::EC_SAY_HELLO); + } +}; + +#endif /* SAYHELLOHANDLER_CPP_ */ -- cgit v1.2.3-55-g7522 From 21e7bf3b7579c4d9a5cf747ddd64f2fcfa2bf6bb Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 15:32:11 +0200 Subject: Implement context menu in pvsmgr[touch] by pressing the Menu key for 5 seconds. --- src/gui/frame.cpp | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++----- src/gui/frame.h | 2 ++ 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/src/gui/frame.cpp b/src/gui/frame.cpp index 32257ec..ccc49cb 100644 --- a/src/gui/frame.cpp +++ b/src/gui/frame.cpp @@ -19,12 +19,14 @@ */ #include +#include #include "frame.h" #include #include #include #define MOUSE_MOTION_SEND_INTERVAL 100 /* msecs */ +#define SPECIAL_EVENT_WAIT_TIME 5000 /* msecs */ Frame::Frame(const QString & text, QWidget * parent) : QLabel(parent), _clientVNCThread(0) @@ -73,6 +75,11 @@ Frame::Frame(const QString & text, QWidget * parent) : _mousePositionChanged = true; + _specialEventTimer = new QTimer(this); + _specialEventTimer->setInterval(SPECIAL_EVENT_WAIT_TIME); + _specialEventTimer->setSingleShot(true); + connect(_specialEventTimer, SIGNAL(timeout()), this, SLOT(showSpecialEventMenu())); + } Frame::~Frame() @@ -499,9 +506,18 @@ void Frame::keyPressEvent(QKeyEvent* event) { if(_remoteControlEnabled) { - // The action of the keyboard may depend on the position of the pointer - sendMouseMotionEvent(); - sendInputEvent(InputEvent::keyboardPress(event->key(), event->modifiers())); + if(event->key() == Qt::Key_Menu) + { + qDebug("Menu has been pressed"); + if(!event->isAutoRepeat()) + _specialEventTimer->start(); + } + else + { + // The action of the keyboard may depend on the position of the pointer + sendMouseMotionEvent(); + sendInputEvent(InputEvent::keyboardPress(event->key(), event->modifiers())); + } } else { @@ -513,9 +529,27 @@ void Frame::keyReleaseEvent(QKeyEvent* event) { if(_remoteControlEnabled) { - // The action of the keyboard may depend on the position of the pointer sendMouseMotionEvent(); - sendInputEvent(InputEvent::keyboardRelease(event->key(), event->modifiers())); + if(event->key() == Qt::Key_Menu) + { + if(!event->isAutoRepeat()) + { + qDebug("Menu has been released"); + if(_specialEventTimer->isActive()) + { + qDebug("Pressing key on client"); + // Pressing the key has been deferred, so do it now: + sendInputEvent(InputEvent::keyboardPress(event->key(), event->modifiers())); + } + sendInputEvent(InputEvent::keyboardRelease(event->key(), event->modifiers())); + _specialEventTimer->stop(); + } + } + else + { + // The action of the keyboard may depend on the position of the pointer + sendInputEvent(InputEvent::keyboardRelease(event->key(), event->modifiers())); + } } else { @@ -550,3 +584,26 @@ bool Frame::event(QEvent* event) } return QLabel::event(event); } + +void Frame::showSpecialEventMenu() +{ + qDebug("Trying to show menu..."); + QMenu* menu = new QMenu(this); + QList specialEvents = privileged_handler_chain::describe(); + QList::iterator iter; + int i; + for(i = 0, iter = specialEvents.begin(); + iter != specialEvents.end(); + iter++, i++) + { + QAction* act = menu->addAction((*iter).descriptionString); + act->setData(i); + } + QAction* selected = menu->exec(QCursor::pos()); + if(selected) + { + int index = selected->data().toInt(); + sendInputEvent(specialEvents.at(index).toEvent()); + } + delete menu; +} diff --git a/src/gui/frame.h b/src/gui/frame.h index 19b330a..8271670 100644 --- a/src/gui/frame.h +++ b/src/gui/frame.h @@ -78,6 +78,7 @@ private Q_SLOTS: void remoteControlClicked(); void remoteControlAllClicked(); void sendMouseMotionEvent(); + void showSpecialEventMenu(); signals: void clicked(); @@ -107,6 +108,7 @@ private: bool _mousePositionChanged; QTimer* _mouseMotionEventTimer; bool _mouseOver; + QTimer* _specialEventTimer; QPoint rescalePosition(QPointF guiPosition); void updateMousePosition(QMouseEvent* event); void sendInputEvent(InputEvent const&); -- cgit v1.2.3-55-g7522 From 898c669b978fcebec5a6882c456d302d481f9513 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 15:32:40 +0200 Subject: Bug fix: If there is no VNC thread, do not attempt to rescale mouse position. --- src/gui/frame.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gui/frame.cpp b/src/gui/frame.cpp index ccc49cb..1b80c3d 100644 --- a/src/gui/frame.cpp +++ b/src/gui/frame.cpp @@ -468,6 +468,9 @@ void Frame::mouseMoveEvent(QMouseEvent* event) QPoint Frame::rescalePosition(QPointF guipos) { + if(!_clientVNCThread) + return QPoint(); + QSize s = size(); QSize t = _clientVNCThread->getSize(); qreal px, py; -- cgit v1.2.3-55-g7522 From 52585c14017d11c439d4962c1ec12f5b87cc93d6 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 15:34:12 +0200 Subject: Implement RebootSystem and KillX11 handlers. --- src/input/CMakeLists.txt | 2 + src/input/inputHandlerChain.h | 7 ++- src/input/killX11Handler.cpp | 89 +++++++++++++++++++++++++++++++++++++++ src/input/killX11Handler.h | 34 +++++++++++++++ src/input/rebootSystemHandler.cpp | 32 ++++++++++++++ src/input/rebootSystemHandler.h | 34 +++++++++++++++ 6 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 src/input/killX11Handler.cpp create mode 100644 src/input/killX11Handler.h create mode 100644 src/input/rebootSystemHandler.cpp create mode 100644 src/input/rebootSystemHandler.h diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 4aaa845..311d8ce 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -10,6 +10,8 @@ if(UNIX) pvsprivinputd.cpp pvsPrivInputHandler.cpp pvsCheckPrivileges.cpp + rebootSystemHandler.cpp + killX11Handler.cpp sayHelloHandler.cpp ) diff --git a/src/input/inputHandlerChain.h b/src/input/inputHandlerChain.h index 57168c2..8bcb1d8 100644 --- a/src/input/inputHandlerChain.h +++ b/src/input/inputHandlerChain.h @@ -23,7 +23,9 @@ #include "x11FakeKeyboardHandler.h" #include "x11FakeMouseHandler.h" #include "privilegedHandlerForwarder.h" +#include "rebootSystemHandler.h" #include "sayHelloHandler.h" +#include "killX11Handler.h" typedef boost::mpl::list< Handler >, @@ -35,8 +37,11 @@ typedef boost::mpl::list< typedef InputEventHandlerChain unprivileged_handler_chain; typedef boost::mpl::list< - Handler + Handler, + Handler, policy::Security >, + Handler, policy::Security > >::type privileged_handler_list; typedef InputEventHandlerChain privileged_handler_chain; + #endif /* INPUTHANDLERCHAIN_H_ */ diff --git a/src/input/killX11Handler.cpp b/src/input/killX11Handler.cpp new file mode 100644 index 0000000..7ac75a1 --- /dev/null +++ b/src/input/killX11Handler.cpp @@ -0,0 +1,89 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # killX11Handler.h + # - Kill the X11 Server - implementation + # -------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include "pvsCheckPrivileges.h" +#include "killX11Handler.h" + +using namespace std; + +void KillX11Handler::handle(InputEvent const&, InputEventContext const* ctx) +{ + // Find out which display device is used: + QString displayDevice = PVSCheckPrivileges::instance()->getX11DisplayDevice(ctx); + QString displayDeviceAbs = QFileInfo(displayDevice).canonicalFilePath(); + + if(displayDevice.isNull()) + { + qWarning("Can not kill X server for %d,%d,%d: No Display Device", ctx->getSenderPid(), ctx->getSenderUid(), ctx->getSenderGid()); + return; + } + + QList pids; + + // Find all processes that have it opened: + QDir proc("/proc"); + QFileInfoList entries = proc.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + foreach(QFileInfo fi, entries) + { + // We are only interested in numerical ids: + bool ok; + pid_t pid = fi.fileName().toUInt(&ok); + if(!ok) + continue; + + // We have a pid. Now check open files: + QDir fds(QString("/proc/%1/fd").arg(pid)); + qDebug("Searching for X server in %s", fds.absolutePath().toLocal8Bit().constData()); + QFileInfoList fdfiles = fds.entryInfoList(QDir::Files); + foreach(QFileInfo fdfile, fdfiles) + { + qDebug(" Looking for terminal in %s", fdfile.absoluteFilePath().toLocal8Bit().constData()); + QFileInfo fdTarget(fdfile.readLink()); + if(fdTarget.canonicalFilePath() == displayDeviceAbs) + { + qDebug(" ... found"); + pids << pid; + } + } + } + + // If everything is well, we have exactly one pid: + if(pids.size() != 1) + { + QStringList pidStrs; + foreach(pid_t pid, pids) + { + pidStrs << QString::number(pid); + } + + qWarning("Display device %s is open by multiple or zero processes (%s). We don't know which is X. Aborting.", + displayDeviceAbs.toLocal8Bit().constData(), + pidStrs.join(", ").toLocal8Bit().constData()); + return; + } + + // We found the PID for the X server. Now kill it. + QString exe = QFileInfo(QString("/proc/%1/exe").arg(pids[0])).readLink(); + qDebug("Killing X server, PID %d, exe name %s with SIGTERM", pids[0], exe.toLocal8Bit().constData()); + +// kill(pids[0], SIGTERM); +} diff --git a/src/input/killX11Handler.h b/src/input/killX11Handler.h new file mode 100644 index 0000000..2f3ef44 --- /dev/null +++ b/src/input/killX11Handler.h @@ -0,0 +1,34 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # killX11Handler.h + # - Kill the X11 Server - interface + # -------------------------------------------------------------------------- + */ + +#ifndef KILLX11HANDLER_H_ +#define KILLX11HANDLER_H_ + +#include +#include "inputEventHandler.h" + +class KillX11Handler : public DefaultInputEventHandler +{ +public: + void handle(InputEvent const&, InputEventContext const*); + + static void describeInto(QList& list) + { + list << SpecialInputEventDescription(tr(QT_TRANSLATE_NOOP("InputEventHandler", "Kill X Server")), InputEvent::ET_SPECIAL, InputEvent::EC_KILL_X); + } +}; + +#endif /* KILLX11HANDLER_H_ */ diff --git a/src/input/rebootSystemHandler.cpp b/src/input/rebootSystemHandler.cpp new file mode 100644 index 0000000..b5b8f8a --- /dev/null +++ b/src/input/rebootSystemHandler.cpp @@ -0,0 +1,32 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # rebootSystemHandler.cpp + # - Handle system reboot requests - implementation + # -------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include "rebootSystemHandler.h" + +using namespace std; + +void RebootLinuxSystemHandler::handle(InputEvent const& evt, InputEventContext const* ctx) +{ + // Rebooting a linux system is particulary easy: + if(kill(1, SIGINT) < 0) + { + qWarning("Could not kill /sbin/init: %s", strerror(errno)); + } +} diff --git a/src/input/rebootSystemHandler.h b/src/input/rebootSystemHandler.h new file mode 100644 index 0000000..34fa8ae --- /dev/null +++ b/src/input/rebootSystemHandler.h @@ -0,0 +1,34 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # rebootSystemHandler.h + # - Handle system reboot requests - interface + # -------------------------------------------------------------------------- + */ + +#ifndef REBOOTSYSTEMHANDLER_H_ +#define REBOOTSYSTEMHANDLER_H_ + +#include +#include "inputEventHandler.h" + +class RebootLinuxSystemHandler : public DefaultInputEventHandler +{ +public: + void handle(InputEvent const&, InputEventContext const*); + + static void describeInto(QList& list) + { + list << SpecialInputEventDescription(tr(QT_TRANSLATE_NOOP("InputEventHandler", "Reboot")), InputEvent::ET_SPECIAL, InputEvent::EC_REBOOT); + } +}; + +#endif /* REBOOTSYSTEMHANDLER_H_ */ -- cgit v1.2.3-55-g7522 From ab5d1fefe041c4083f8e26a436a480abdaa6dae0 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 16:26:57 +0200 Subject: Unify keyword case in src/input/CMakeLists.txt --- src/input/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 311d8ce..29374e2 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -1,6 +1,6 @@ -INCLUDE(${QT_USE_FILE}) +include(${QT_USE_FILE}) -SET(pvsinput_SRCS +set(pvsinput_SRCS inputEvent.cpp inputEventHandler.cpp ) @@ -48,7 +48,7 @@ if(UNIX) ) endif() -ADD_LIBRARY( +add_library( pvsinput STATIC ${pvsinput_SRCS} -- cgit v1.2.3-55-g7522 From 9527024db774c3103a92f321f5a6ada6352af42e Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 18:15:33 +0200 Subject: Update translation files (1) --- i18n/pvs_ar_JO.ts | 8 ++++---- i18n/pvs_de_DE.ts | 8 ++++---- i18n/pvs_es_MX.ts | 8 ++++---- i18n/pvs_fr_FR.ts | 8 ++++---- i18n/pvs_pl_PL.ts | 8 ++++---- i18n/pvsmgr_ar_JO.ts | 48 +++++++++++++++++++++++++++++++++++------------- i18n/pvsmgr_de_DE.ts | 48 +++++++++++++++++++++++++++++++++++------------- i18n/pvsmgr_es_MX.ts | 48 +++++++++++++++++++++++++++++++++++------------- i18n/pvsmgr_fr_FR.ts | 48 +++++++++++++++++++++++++++++++++++------------- i18n/pvsmgr_pl_PL.ts | 48 +++++++++++++++++++++++++++++++++++------------- 10 files changed, 195 insertions(+), 85 deletions(-) diff --git a/i18n/pvs_ar_JO.ts b/i18n/pvs_ar_JO.ts index 0b4c72f..b604bb0 100644 --- a/i18n/pvs_ar_JO.ts +++ b/i18n/pvs_ar_JO.ts @@ -4,22 +4,22 @@ PVS - + Message - + VNC connection - + The host - + requested your screen! diff --git a/i18n/pvs_de_DE.ts b/i18n/pvs_de_DE.ts index 0b4c72f..b604bb0 100644 --- a/i18n/pvs_de_DE.ts +++ b/i18n/pvs_de_DE.ts @@ -4,22 +4,22 @@ PVS - + Message - + VNC connection - + The host - + requested your screen! diff --git a/i18n/pvs_es_MX.ts b/i18n/pvs_es_MX.ts index 0b4c72f..b604bb0 100644 --- a/i18n/pvs_es_MX.ts +++ b/i18n/pvs_es_MX.ts @@ -4,22 +4,22 @@ PVS - + Message - + VNC connection - + The host - + requested your screen! diff --git a/i18n/pvs_fr_FR.ts b/i18n/pvs_fr_FR.ts index 0b4c72f..b604bb0 100644 --- a/i18n/pvs_fr_FR.ts +++ b/i18n/pvs_fr_FR.ts @@ -4,22 +4,22 @@ PVS - + Message - + VNC connection - + The host - + requested your screen! diff --git a/i18n/pvs_pl_PL.ts b/i18n/pvs_pl_PL.ts index 0b4c72f..b604bb0 100644 --- a/i18n/pvs_pl_PL.ts +++ b/i18n/pvs_pl_PL.ts @@ -4,22 +4,22 @@ PVS - + Message - + VNC connection - + The host - + requested your screen! diff --git a/i18n/pvsmgr_ar_JO.ts b/i18n/pvsmgr_ar_JO.ts index bc5c69c..758c16b 100644 --- a/i18n/pvsmgr_ar_JO.ts +++ b/i18n/pvsmgr_ar_JO.ts @@ -270,46 +270,68 @@ Perform an unprojection or remove remote help to get a target. Frame - + View - + Foto - - + + Lock this client - + Set as Superclient - + + + Enable Remote Control + + + + + + Remote Control All Clients + + + + Unlock this client - + You can't lock a Superclient-machine. - + Set client as Superclient - + Unset client as Superclient + + + Disable Remote Control + + + + + Remote Control only this Client + + MainWindow @@ -351,22 +373,22 @@ Perform an unprojection or remove remote help to get a target. - + This operation can only be performed for at least one selected Client! - + You have to set a Superclient-machine before performing this action. - + Open Image - + Image Files (*.png *.jpg *.svg) diff --git a/i18n/pvsmgr_de_DE.ts b/i18n/pvsmgr_de_DE.ts index fd6d56b..ab5fd7d 100644 --- a/i18n/pvsmgr_de_DE.ts +++ b/i18n/pvsmgr_de_DE.ts @@ -270,46 +270,68 @@ Perform an unprojection or remove remote help to get a target. Frame - + View - + Foto - - + + Lock this client - + Set as Superclient - + + + Enable Remote Control + + + + + + Remote Control All Clients + + + + Unlock this client - + You can't lock a Superclient-machine. - + Set client as Superclient - + Unset client as Superclient + + + Disable Remote Control + + + + + Remote Control only this Client + + MainWindow @@ -351,22 +373,22 @@ Perform an unprojection or remove remote help to get a target. - + This operation can only be performed for at least one selected Client! - + You have to set a Superclient-machine before performing this action. - + Open Image - + Image Files (*.png *.jpg *.svg) diff --git a/i18n/pvsmgr_es_MX.ts b/i18n/pvsmgr_es_MX.ts index fc53e6d..96a545e 100644 --- a/i18n/pvsmgr_es_MX.ts +++ b/i18n/pvsmgr_es_MX.ts @@ -270,46 +270,68 @@ Perform an unprojection or remove remote help to get a target. Frame - + View Ver - + Foto Imagen - - + + Lock this client Bloquear este cliente - + Set as Superclient Asignar como super cliente - + + + Enable Remote Control + + + + + + Remote Control All Clients + + + + Unlock this client Desbloquear este cliente - + You can't lock a Superclient-machine. Super clientes no pueden ser bloqueados. - + Set client as Superclient Asignar cliente como super cliente - + Unset client as Superclient Desactivar como super cliente + + + Disable Remote Control + + + + + Remote Control only this Client + + MainWindow @@ -351,22 +373,22 @@ Perform an unprojection or remove remote help to get a target. Esta operación solamente puede ser realizada para un cliente! - + This operation can only be performed for at least one selected Client! Esta operación solamente puede ser realizada para el ultimo cliente seleccionado! - + You have to set a Superclient-machine before performing this action. Usted debe asignar un super cliente antes de realizar esta acción. - + Open Image Abrir imagen - + Image Files (*.png *.jpg *.svg) Extensión de imagenes (*.png *.jpg *.svg) diff --git a/i18n/pvsmgr_fr_FR.ts b/i18n/pvsmgr_fr_FR.ts index bc5c69c..758c16b 100644 --- a/i18n/pvsmgr_fr_FR.ts +++ b/i18n/pvsmgr_fr_FR.ts @@ -270,46 +270,68 @@ Perform an unprojection or remove remote help to get a target. Frame - + View - + Foto - - + + Lock this client - + Set as Superclient - + + + Enable Remote Control + + + + + + Remote Control All Clients + + + + Unlock this client - + You can't lock a Superclient-machine. - + Set client as Superclient - + Unset client as Superclient + + + Disable Remote Control + + + + + Remote Control only this Client + + MainWindow @@ -351,22 +373,22 @@ Perform an unprojection or remove remote help to get a target. - + This operation can only be performed for at least one selected Client! - + You have to set a Superclient-machine before performing this action. - + Open Image - + Image Files (*.png *.jpg *.svg) diff --git a/i18n/pvsmgr_pl_PL.ts b/i18n/pvsmgr_pl_PL.ts index bc5c69c..758c16b 100644 --- a/i18n/pvsmgr_pl_PL.ts +++ b/i18n/pvsmgr_pl_PL.ts @@ -270,46 +270,68 @@ Perform an unprojection or remove remote help to get a target. Frame - + View - + Foto - - + + Lock this client - + Set as Superclient - + + + Enable Remote Control + + + + + + Remote Control All Clients + + + + Unlock this client - + You can't lock a Superclient-machine. - + Set client as Superclient - + Unset client as Superclient + + + Disable Remote Control + + + + + Remote Control only this Client + + MainWindow @@ -351,22 +373,22 @@ Perform an unprojection or remove remote help to get a target. - + This operation can only be performed for at least one selected Client! - + You have to set a Superclient-machine before performing this action. - + Open Image - + Image Files (*.png *.jpg *.svg) -- cgit v1.2.3-55-g7522 From 43038c98f8cc48cfded692ea44e02e93597da305 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 19:04:36 +0200 Subject: Add hard requirement for XInput library. XInput2 will be preferred if its presence is detected. --- CMakeLists.txt | 7 +++++- src/input/CMakeLists.txt | 8 ++++++ src/input/x11FakeKeyboardHandler.cpp | 49 +++++++++++++++++++++++++++++++++--- 3 files changed, 59 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ec688b5..dab8b1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,10 +31,14 @@ INCLUDE_DIRECTORIES( ${CMAKE_BINARY_DIR} ${X11_INCLUDE_DIR} ${X11_XTest_INCLUDE_PATH} + ${X11_Xinput_INCLUDE_PATH} ) IF(NOT X11_XTest_FOUND) - MESSAGE(FATAL_ERROR "Could not find X11 extension XTest. It is needed for PVS") + MESSAGE(FATAL_ERROR "Could not find X11 extension XTest or its developer files.") +ENDIF() +IF(NOT X11_Xinput_FOUND) + MESSAGE(FATAL_ERROR "Could not find X11 extension Xinput or its developer files.") ENDIF() ADD_SUBDIRECTORY(src/input) @@ -322,6 +326,7 @@ TARGET_LINK_LIBRARIES( pvs ${VNC_LIBRARIES} ${X11_LIBRARIES} ${X11_XTest_LIB} + ${X11_Xinput_LIB} pvsinput ) diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 29374e2..1a3b154 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -6,6 +6,14 @@ set(pvsinput_SRCS ) if(UNIX) + find_file(XINPUT2_HDR X11/extensions/XInput2.h) + if(XINPUT2_HDR) + set_property(SOURCE x11FakeKeyboardHandler.cpp + APPEND + PROPERTY COMPILE_DEFINITIONS HAVE_XINPUT2_H + ) + endif() + set(pvsprivinputd_SRCS pvsprivinputd.cpp pvsPrivInputHandler.cpp diff --git a/src/input/x11FakeKeyboardHandler.cpp b/src/input/x11FakeKeyboardHandler.cpp index 3a0b864..e7a5826 100644 --- a/src/input/x11FakeKeyboardHandler.cpp +++ b/src/input/x11FakeKeyboardHandler.cpp @@ -27,7 +27,11 @@ #include #include #include -#include +#ifdef HAVE_XINPUT2_H +# include +#else +# include +#endif #include #include #include "x11InputUtils.h" @@ -457,6 +461,37 @@ modifier_to_keycode_map modifier_to_keycode; typedef std::map keycode_to_modifier_map; keycode_to_modifier_map keycode_to_modifier; +// We need to query the input devices, preferrable through XInput2, but +// if that is not available we will contend ourselves with XInput1: +#ifndef HAVE_XINPUT2_H +# define XIAllDevices 1 /* does not matter */ +# define XIDeviceInfo XDeviceInfo +# define XIQueryDevice(dpy, which, ninfos) XListInputDevices(dpy, ninfos) +# define XIFreeDeviceInfo(infos) XFreeDeviceList(infos) +# define XIMasterKeyboard IsXKeyboard +# define XISlaveKeyboard IsXExtensionKeyboard + static inline Atom* getDeviceProperties(Display* dpy, XIDeviceInfo* devinfo, int* nprops) + { + if(devinfo->use == IsXKeyboard) + { + // According to XOpenDevice(3X11) you cannot open the Core Keyboard. + *nprops = 0; + return 0; + } + + XDevice* dev = XOpenDevice(dpy, devinfo->id); + Atom* props = XListDeviceProperties(dpy, dev, nprops); + XCloseDevice(dpy, dev); + return props; + } +#else + static inline Atom* getDeviceProperties(Display* dpy, XIDeviceInfo* devinfo, int* nprops) + { + return XIListProperties(dpy, devinfo->deviceid, nprops); + } +#endif + + void initialize_basic_keycodes() { for(int i = 0; i < 8; i++) { @@ -504,23 +539,26 @@ void initialize_basic_keycodes() else { // Try to find out what device XTest will send events on: - int xkbDeviceId = XkbUseCoreKbd; + unsigned int xkbDeviceId = XkbUseCoreKbd; Atom xtestDeviceProp = XInternAtom(dpy, "XTEST Device", false); int ndevinfos; XIDeviceInfo* devinfos = XIQueryDevice(dpy, XIAllDevices, &ndevinfos); if(devinfos) { +#ifndef HAVE_INPUT2_H +# define deviceid id +#endif for(int i = 0; i < ndevinfos && xkbDeviceId == XkbUseCoreKbd; i++) { XIDeviceInfo* devinfo = devinfos + i; - qDebug("Found device of type %d with name %s", devinfo->use, devinfo->name); + qDebug("Found device %lu of type %d with name %s", (unsigned long)devinfo->deviceid, devinfo->use, devinfo->name); // We want it to be a keyboard. if(devinfo->use != XIMasterKeyboard && devinfo->use != XISlaveKeyboard) continue; int nprops; - Atom* props = XIListProperties(dpy, devinfo->deviceid, &nprops); + Atom* props = getDeviceProperties(dpy, devinfo, &nprops); if(props) { for(int j = 0; j < nprops && xkbDeviceId == XkbUseCoreKbd; j++) @@ -536,6 +574,9 @@ void initialize_basic_keycodes() } } XIFreeDeviceInfo(devinfos); +#ifdef deviceid /* XInput1 */ +# undef deviceid +#endif } // Okay, we know which device to query. Now get its keymap: -- cgit v1.2.3-55-g7522 From 681a40332ac5c1023500d753e0c444b59933eecb Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Sat, 17 Jul 2010 16:43:49 +0200 Subject: Implement --no-fork/-F command line argument for pvs --- src/pvsDaemon.cpp | 97 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 38 deletions(-) diff --git a/src/pvsDaemon.cpp b/src/pvsDaemon.cpp index e87bfe7..591d43b 100644 --- a/src/pvsDaemon.cpp +++ b/src/pvsDaemon.cpp @@ -66,6 +66,9 @@ int main(int argc, char** argv) { int frequency = 5; int port = -1; +#ifdef as_daemon + bool no_fork = false; +#endif QFileInfo script; script.setFile("/usr/bin/pvs-vncsrv"); @@ -107,11 +110,14 @@ int main(int argc, char** argv) { "freq", required_argument, 0, 'f' }, { "client", required_argument, 0, 'e' }, { "script", required_argument, 0, 's' }, +#ifdef as_daemon + { "no-fork", no_argument, 0, 'F' }, +#endif { 0, 0, 0, 0 }, }; /* getopt_long stores the option index here. */ - int c = getopt_long(argc, argv, "hvoc:f:e:s:p:", long_options, + int c = getopt_long(argc, argv, "hvoFc:f:e:s:p:", long_options, &option_index); option_index++; if (c == -1) @@ -267,6 +273,13 @@ int main(int argc, char** argv) } break; } +#ifdef as_daemon + case 'F': + { + no_fork = true; + break; + } +#endif case '?': { ConsoleLog writeError( @@ -293,51 +306,59 @@ int main(int argc, char** argv) #ifdef as_daemon - /* Our process ID and Session ID */ - pid_t pid, sid; - - /* Fork off the parent process */ - pid = fork(); - if (pid < 0) - { - exit(-1); - } - /* If we got a good PID, then - we can exit the parent process. */ - if (pid > 0) + if (!no_fork) { - exit(0); - } + /* Our process ID and Session ID */ + pid_t pid, sid; - /* Change the file mode mask */ - umask(0); + /* Fork off the parent process */ + pid = fork(); + if (pid < 0) + { + exit(-1); + } + /* If we got a good PID, then + we can exit the parent process. */ + if (pid > 0) + { + exit(0); + } - /* Open any logs here */ + /* Change the file mode mask */ + umask(0); - /* Create a new SID for the child process */ - sid = setsid(); - if (sid < 0) - { - /* Log the failure */ - exit(-1); - } + /* Open any logs here */ - /* Change the current working directory */ - if ((chdir("/")) < 0) - { - /* Log the failure */ - exit(-1); - } + /* Create a new SID for the child process */ + sid = setsid(); + if (sid < 0) + { + /* Log the failure */ + exit(-1); + } + + /* Change the current working directory */ + if ((chdir("/")) < 0) + { + /* Log the failure */ + exit(-1); + } - /* Close out the standard file descriptors */ - close(STDIN_FILENO); - freopen ((QString("/home/").append(getUserName().append(QString("/.pvs/dump")))).toUtf8().data(),"w",stdout); - //close(STDOUT_FILENO); - close(STDERR_FILENO); + /* Close out the standard file descriptors */ + close(STDIN_FILENO); + freopen ((QString("/home/").append(getUserName().append(QString("/.pvs/dump")))).toUtf8().data(),"w",stdout); + //close(STDOUT_FILENO); + close(STDERR_FILENO); - /* Daemon-specific initialization goes here */ + /* Daemon-specific initialization goes here */ - /* The Big Loop */ + /* The Big Loop */ + } + else + { + /* just the umask(), please */ + umask(0); + } #endif -- cgit v1.2.3-55-g7522 From 5bcf831e1734be78d9468631926e875a5b05e11f Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Sun, 3 Oct 2010 23:27:39 +0200 Subject: Implement Network-wide Configuration Interface for Multicast File Transfer --- CMakeLists.txt | 6 ++ pvsmgr.qrc | 1 + src/gui/mainWindow.cpp | 17 +++- src/gui/mainWindow.h | 1 + src/gui/multicastConfigDialog.cpp | 170 ++++++++++++++++++++++++++++++++++++ src/gui/multicastConfigDialog.h | 38 ++++++++ src/gui/ui/mainwindow.ui | 15 ++++ src/gui/ui/mainwindowtouch.ui | 11 +++ src/gui/ui/multicastConfigDialog.ui | 156 +++++++++++++++++++++++++++++++++ src/net/mcast/McastConfiguration.h | 17 ++++ src/net/pvsListenServer.cpp | 60 +++++++++++++ src/net/pvsListenServer.h | 10 +++ src/pvs.cpp | 58 +++++++++++- src/pvs.h | 1 + 14 files changed, 559 insertions(+), 2 deletions(-) create mode 100644 src/gui/multicastConfigDialog.cpp create mode 100644 src/gui/multicastConfigDialog.h create mode 100644 src/gui/ui/multicastConfigDialog.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f2089a..0899bdf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,7 @@ SET( PVSMGR_SRCS src/util/TextFile.cpp src/util/serviceDiscoveryUtil.cpp src/gui/aboutDialog.cpp + src/gui/multicastConfigDialog.cpp ) # pvs @@ -131,6 +132,7 @@ SET( PVSMGR_UIS src/gui/ui/aboutDialog.ui src/gui/ui/serverChatDialog.ui src/gui/ui/clientFileSendDialog.ui + src/gui/ui/multicastConfigDialog.ui ) SET( PVSMGRTOUCH_UIS @@ -140,6 +142,7 @@ SET( PVSMGRTOUCH_UIS src/gui/ui/projectionDialog.ui src/gui/ui/aboutDialog.ui src/gui/ui/serverChatDialog.ui + src/gui/ui/multicastConfigDialog.ui ) SET( PVSGUI_UIS @@ -186,6 +189,7 @@ SET( PVSMGR_MOC_HDRS src/net/pvsServiceBroadcast.h src/net/SslServer.h src/gui/aboutDialog.h + src/gui/multicastConfigDialog.h ) SET( PVS_MOC_HDRS @@ -317,12 +321,14 @@ TARGET_LINK_LIBRARIES( pvsmgr ${QT_LIBRARIES} ${VNC_LIBRARIES} ${X11_LIBRARIES} + pvsmcast ) TARGET_LINK_LIBRARIES( pvsmgrtouch ${QT_LIBRARIES} ${VNC_LIBRARIES} ${X11_LIBRARIES} + pvsmcast ) TARGET_LINK_LIBRARIES( pvs diff --git a/pvsmgr.qrc b/pvsmgr.qrc index 171412e..e0b6b51 100644 --- a/pvsmgr.qrc +++ b/pvsmgr.qrc @@ -19,6 +19,7 @@ icons/dozent.png icons/chat.png icons/cam32.svg + icons/network_configure.png AUTHORS TRANSLATION diff --git a/src/gui/mainWindow.cpp b/src/gui/mainWindow.cpp index eb15e82..c911721 100644 --- a/src/gui/mainWindow.cpp +++ b/src/gui/mainWindow.cpp @@ -16,7 +16,10 @@ #include #include +#include +#include #include +#include using namespace std; // setting the IF-DEF Block for the touchgui and the normal gui, for later use @@ -34,6 +37,7 @@ using namespace std; #include //#include #include +#include #include MainWindow::MainWindow(QWidget *parent) : @@ -129,6 +133,7 @@ MainWindow::MainWindow(QWidget *parent) : ui->actionFoto->setStatusTip(tr("Make a screenshot for the selected client(s)")); ui->actionLock->setStatusTip(tr("Lock or Unlock all Clients")); connect(ui->actionCreate_profile, SIGNAL(triggered()), this, SLOT(createProfile())); + connect(ui->actionConfigure_Network, SIGNAL(triggered()), this, SLOT(configureNetwork())); connect(ui->actionShow_Username, SIGNAL(triggered()), this, SLOT(showusername())); connect(ui->actionShow_Hostname_IP, SIGNAL(triggered()), this, SLOT(showip())); @@ -1197,7 +1202,17 @@ void MainWindow::startChatDialog() sChatDialog.raise();//show the chat dialog on top level } - +void MainWindow::configureNetwork() +{ + PVSServer* server = PVSConnectionManager::getManager()->getServer(); + McastConfiguration mc(*(server->getMulticastConfiguration())); + MulticastConfigDialog* mcd = new MulticastConfigDialog(&mc, this); + int result = mcd->exec(); + if(result == QDialog::Accepted) + { + server->multicastReconfigure(&mc); + } +} MainWindow* MainWindow::myself = NULL; ConnectionList* MainWindow::conList = NULL; diff --git a/src/gui/mainWindow.h b/src/gui/mainWindow.h index 00bd927..41d9cfa 100644 --- a/src/gui/mainWindow.h +++ b/src/gui/mainWindow.h @@ -194,6 +194,7 @@ private slots: void setPasswordForConnection(int enabled); void combobox1(int menuindex1); // Funktion um index der combobox auszulesen und weiterzuverarbeiten s. Ticker 671 //void combobox2(int menuindex2); // Funktion um index der combobox auszulesen und weiterzuverarbeiten + void configureNetwork(); }; diff --git a/src/gui/multicastConfigDialog.cpp b/src/gui/multicastConfigDialog.cpp new file mode 100644 index 0000000..ff370c7 --- /dev/null +++ b/src/gui/multicastConfigDialog.cpp @@ -0,0 +1,170 @@ +#include +#include +#include +#include +#include "multicastConfigDialog.h" +#include +// #include "multicastValidators.h" + +MulticastConfigDialog::MulticastConfigDialog(QWidget* parent) : + QDialog(parent) +{ + setupUi(); +} + +MulticastConfigDialog::MulticastConfigDialog(McastConfiguration* config, + QWidget *parent) : + QDialog(parent) +{ + setupUi(); + _config = config; + + _ui.groupAddressEdit->setText(config->multicastAddress()); + _ui.dataPortEdit->setText(QString::number(config->multicastUDPPortBase())); + + connect(_ui.buttonBox, SIGNAL(accepted()), this, SLOT(dialogAccepted())); + connect(_ui.buttonBox, SIGNAL(rejected()), this, SLOT(reject())); +} + +void MulticastConfigDialog::setupUi() +{ + _ui.setupUi(this); + + QIntValidator* portValidator = new QIntValidator(1024, 65535, this); + _ui.dataPortEdit->setValidator(portValidator); + + connect(_ui.groupAddressEdit, SIGNAL(textChanged(QString const&)), this, + SLOT(validateGroupAddress(QString const&))); + connect(_ui.dataPortEdit, SIGNAL(textChanged(QString const&)), this, + SLOT(validateDataPort(QString const&))); + + connect(_ui.buttonBox, SIGNAL(accepted()), this, SLOT(dialogAccepted())); + connect(_ui.buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + validateGroupAddress(_ui.groupAddressEdit->text()); + validateDataPort(_ui.dataPortEdit->text()); +} + +MulticastConfigDialog::~MulticastConfigDialog() +{ +} + +void MulticastConfigDialog::dialogAccepted() +{ + QHostAddress addr; + bool addressParses = addr.setAddress(_ui.groupAddressEdit->text()); + _config->multicastAddress(_ui.groupAddressEdit->text()); + quint16 port = _ui.dataPortEdit->text().toInt(); + _config->multicastUDPPortBase(port); + _config->multicastDPort(port + 1); + _config->multicastSPort(port + 2); + _config->multicastRate(_ui.rateSpinbox->value() * 1024); + accept(); +} + +void MulticastConfigDialog::setError(QWidget* widget, + QLabel* errorMessageLabel, QString text) +{ + if (errorMessageLabel) + errorMessageLabel->setText(QString( + "") + text + + ""); + if (widget) + widget->setStyleSheet("background-color: #ffcccc;"); +} + +void MulticastConfigDialog::setOK(QWidget* widget, QLabel* errorMessageLabel) +{ + if (errorMessageLabel) + errorMessageLabel->setText(QString( + "") + + tr("OK") + ""); + if (widget) + widget->setStyleSheet("background-color: #ccffcc;"); +} + +void MulticastConfigDialog::validateGroupAddress(QString const& input) +{ + QHostAddress a; + + _isAddressValid = false; + + if (!a.setAddress(input)) + { + setError(_ui.groupAddressEdit, _ui.groupAddressMessage, tr( + "Not a valid IP Address")); + revalidateButtons(); + return; + } + + // check if it is IPv4 + if (a.protocol() != QAbstractSocket::IPv4Protocol) + { + setError(_ui.groupAddressEdit, _ui.groupAddressMessage, tr( + "Not a valid IPv4 Address")); + revalidateButtons(); + return; + } + + // check if it is a valid multicast address + quint32 addr = a.toIPv4Address(); + if ((addr & 0xf0000000) != 0xe0000000) + { + setError(_ui.groupAddressEdit, _ui.groupAddressMessage, tr( + "Not an IPv4 multicast address")); + revalidateButtons(); + return; + } + + _isAddressValid = true; + setOK(_ui.groupAddressEdit, _ui.groupAddressMessage); + revalidateButtons(); +} + +void MulticastConfigDialog::validateDataPort(QString const& input) +{ + bool ok; + int p = input.toInt(&ok, 0); + + _isPortValid = false; + + if (!ok) + { + setError(_ui.dataPortEdit, _ui.dataPortMessage, tr("Not a number")); + revalidateButtons(); + return; + } + + if (p < 0) + { + setError(_ui.dataPortEdit, _ui.dataPortMessage, tr("Must be positive")); + revalidateButtons(); + return; + } + + if (p < 1024) + { + setError(_ui.dataPortEdit, _ui.dataPortMessage, tr( + "Must not be a privileged port")); + revalidateButtons(); + return; + } + + if (p > 65535) + { + setError(_ui.dataPortEdit, _ui.dataPortMessage, tr( + "Port number too large")); + revalidateButtons(); + return; + } + + _isPortValid = true; + setOK(_ui.dataPortEdit, _ui.dataPortMessage); + revalidateButtons(); +} + +void MulticastConfigDialog::revalidateButtons() +{ + _ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(_isAddressValid + && _isPortValid); +} diff --git a/src/gui/multicastConfigDialog.h b/src/gui/multicastConfigDialog.h new file mode 100644 index 0000000..6421813 --- /dev/null +++ b/src/gui/multicastConfigDialog.h @@ -0,0 +1,38 @@ +#ifndef MULTICASTCONFIGDIALOG_H +#define MULTICASTCONFIGDIALOG_H + +#include +#include +#include "ui_multicastConfigDialog.h" +#include + +class McastConfiguration; + +class MulticastConfigDialog : public QDialog +{ + Q_OBJECT + +public: + MulticastConfigDialog(QWidget* parent = 0); + MulticastConfigDialog(McastConfiguration* dbusIface, QWidget *parent = 0); + ~MulticastConfigDialog(); + +private: + Ui::MulticastConfigDialogClass _ui; + McastConfiguration* _config; + bool _isAddressValid; + bool _isPortValid; + + void setupUi(); + + void setError(QWidget* input, QLabel* messageLabel, QString text); + void setOK(QWidget* input, QLabel* messageLabel); + void revalidateButtons(); + +private slots: + void dialogAccepted(); + void validateGroupAddress(QString const&); + void validateDataPort(QString const&); +}; + +#endif // MULTICASTCONFIGDIALOG_H diff --git a/src/gui/ui/mainwindow.ui b/src/gui/ui/mainwindow.ui index eb49d1b..7b8f2b4 100644 --- a/src/gui/ui/mainwindow.ui +++ b/src/gui/ui/mainwindow.ui @@ -243,9 +243,16 @@ + + + Network + + + + @@ -531,6 +538,14 @@ - + + + &Configure... + + + Configure Network Parameters + + diff --git a/src/gui/ui/mainwindowtouch.ui b/src/gui/ui/mainwindowtouch.ui index 9030b17..cf7e411 100644 --- a/src/gui/ui/mainwindowtouch.ui +++ b/src/gui/ui/mainwindowtouch.ui @@ -365,6 +365,8 @@ + + @@ -636,6 +638,15 @@ Ctrl+D + + + + :/netconf:/netconf + + + Configure Network... + + diff --git a/src/gui/ui/multicastConfigDialog.ui b/src/gui/ui/multicastConfigDialog.ui new file mode 100644 index 0000000..1ddf02c --- /dev/null +++ b/src/gui/ui/multicastConfigDialog.ui @@ -0,0 +1,156 @@ + + + MulticastConfigDialogClass + + + + 0 + 0 + 331 + 314 + + + + + 0 + 0 + + + + PVS - Multicast Configuration + + + + + + + 0 + 0 + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> +<table style="-qt-table-type: root; margin-top:4px; margin-bottom:4px; margin-left:4px; margin-right:4px;"> +<tr> +<td style="border: none;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">You need to specify connection parameters for multicast messaging on your network. These parameters will automatically be distributed to client computers, so you need to assign them only once.</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">You will probably want to assign an address from the <span style=" text-decoration: underline;">239.0.0.0/8</span> &quot;Administratively Scoped&quot; range.</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Do not assign arbitrary numbers without checking with your network administrator!</span></p></td></tr></table></body></html> + + + true + + + + + + + + + Multicast Group Address + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 009.009.009.009; + + + ... + + + + + + + <span style=" font-weight:600; color:#008800;">OK</span> + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Data Port (1024-65535) + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 00009; + + + 5 + + + + + + + <span style=" font-weight:600; color:#008800;">OK</span> + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Transmission Rate + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + KiB/s + + + 10240 + + + 10 + + + 100 + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + diff --git a/src/net/mcast/McastConfiguration.h b/src/net/mcast/McastConfiguration.h index 6884036..53f7a54 100644 --- a/src/net/mcast/McastConfiguration.h +++ b/src/net/mcast/McastConfiguration.h @@ -166,6 +166,23 @@ public: emit changed(); } + McastConfiguration& operator=(McastConfiguration const& source) + { + if(this != &source) + { + _multicastInterface = source._multicastInterface; + _multicastAddress = source._multicastAddress; + _multicastRate = source._multicastRate; + _multicastSPort = source._multicastSPort; + _multicastDPort = source._multicastDPort; + _multicastWinSize = source._multicastWinSize; + _multicastMTU = source._multicastMTU; + _multicastUDPPortBase = source._multicastUDPPortBase; + _multicastUseUDP = source._multicastUseUDP; + } + return *this; + } + void loadFrom(QSettings* settings, char const* group = 0); void writeTo(QSettings* settings, char const* group = 0) const; diff --git a/src/net/pvsListenServer.cpp b/src/net/pvsListenServer.cpp index f21303e..1c1387b 100644 --- a/src/net/pvsListenServer.cpp +++ b/src/net/pvsListenServer.cpp @@ -21,9 +21,13 @@ #include "pvsClientConnection.h" #include "src/util/consoleLogger.h" #include +#include +#include +#include #include "SslServer.h" #include //#define verbose +#include "mcast/McastConfiguration.h" // Create listener PVSListenServer::PVSListenServer(int port, int clients) @@ -36,6 +40,7 @@ PVSListenServer::PVSListenServer(int port, int clients) else _clientsMax = clients; _port = port; + _mcastConfig = 0; init(); } @@ -178,6 +183,7 @@ void PVSListenServer::onClientConnected(PVSClientConnection* connected) { connected->setServerID(_id); connected->setID(generateID()); + connected->push_back_send(mcastConfigMessage()); } void PVSListenServer::onClientDisconnected(PVSClientConnection* disconnected) @@ -227,6 +233,11 @@ PVSClientConnection* PVSListenServer::getConnectionFromID(int id) // Initialize listening socket bool PVSListenServer::init() { + if (_mcastConfig) + delete _mcastConfig; + _mcastConfig = new McastConfiguration(this); + loadMcastConfig(); + if (_listenSocket != NULL) shutdown(); @@ -306,3 +317,52 @@ bool PVSListenServer::isListening() { return _listenSocket != NULL && _listenSocket->isListening(); } + +void PVSListenServer::loadMcastConfig() +{ + QSettings settings; + _mcastConfig->loadFrom(&settings, "multicast-filetransfer"); +} + +void PVSListenServer::saveMcastConfig() +{ + QSettings settings; + _mcastConfig->writeTo(&settings, "multicast-filetransfer"); + settings.sync(); +} + +PVSMsg PVSListenServer::mcastConfigMessage() +{ + // If anything is changed here, do not forget to + // 1. assign a new version number + // 2. adapt PVS::onCommand(PVSMsg) in pvs.cpp + QByteArray ba; + QDataStream strm(&ba, QIODevice::WriteOnly); + strm << (quint16)1 // version + << _mcastConfig->multicastAddress() + << _mcastConfig->multicastUDPPortBase() + << _mcastConfig->multicastDPort() + << _mcastConfig->multicastSPort() + << _mcastConfig->multicastMTU() + << _mcastConfig->multicastWinSize() + << _mcastConfig->multicastRate() + << _mcastConfig->multicastUseUDP(); + + QByteArray b64 = ba.toBase64(); + QString message = QString::fromAscii(b64.constData(), b64.length()); + PVSMsg msg(PVSCOMMAND, "MCASTFTCONFIG", message); + return msg; +} + +void PVSListenServer::multicastReconfigure(McastConfiguration const* source) +{ + _mcastConfig->multicastAddress(source->multicastAddress()); + *_mcastConfig = *source; + saveMcastConfig(); + sendToAll(mcastConfigMessage()); +} + +McastConfiguration const* PVSListenServer::getMulticastConfiguration() +{ + return _mcastConfig; +} diff --git a/src/net/pvsListenServer.h b/src/net/pvsListenServer.h index ab021c7..90d2a77 100644 --- a/src/net/pvsListenServer.h +++ b/src/net/pvsListenServer.h @@ -10,6 +10,7 @@ class SslServer; class PVSClientConnection; class PVSMsg; +class McastConfiguration; class PVSListenServer : public QObject { @@ -40,6 +41,12 @@ private: bool init(); unsigned int generateID(); + McastConfiguration* _mcastConfig; + + void loadMcastConfig(); + void saveMcastConfig(); + PVSMsg mcastConfigMessage(); + protected: void timerEvent(QTimerEvent *event); @@ -60,6 +67,9 @@ public: bool disconnectClient(PVSClientConnection* delinquent); void onConnectionRemoved(PVSClientConnection* delinquent); + void multicastReconfigure(McastConfiguration const* source); + McastConfiguration const* getMulticastConfiguration(); + std::list* getClientListPtr() { return &_clients; diff --git a/src/pvs.cpp b/src/pvs.cpp index 9bd36d6..df21468 100644 --- a/src/pvs.cpp +++ b/src/pvs.cpp @@ -45,7 +45,10 @@ PVS::PVS() : loadCommands(); _blankScreen = NULL; _vncPort = -1; + + QSettings settings; _masterMcastConfig = new McastConfiguration(this); + _masterMcastConfig->loadFrom(&settings); // add a notify to the allow file, so we get informed when the file is changed QString watchPath("/home/"); @@ -234,6 +237,11 @@ void PVS::onCommand(PVSMsg cmdMessage) qDebug() << "Ignoring malformed MCASTFTANNOUNCE command: " << message; return; } + if (ident.compare("MCASTFTCONFIG") == 0) + { + loadMcastConfig(message); + return; + } #ifdef never // prototype @@ -735,7 +743,8 @@ void PVS::cancelIncomingMulticastTransfer(qulonglong transferID) void PVS::setMulticastInterface(QString const& interfaceName) { QSettings settings; - settings.setValue("multicast/interface", interfaceName); + _masterMcastConfig->multicastInterface(interfaceName); + _masterMcastConfig->writeTo(&settings, "multicast"); settings.sync(); } @@ -814,3 +823,50 @@ void PVS::incomingMulticastTransferDelete(qulonglong transferID) _incomingTransfers.remove(transferID); transfer->deleteLater(); } + +void PVS::loadMcastConfig(QString const& message) +{ + QByteArray ba = QByteArray::fromBase64(message.toAscii()); + QDataStream d(&ba, QIODevice::ReadOnly); + quint16 ver, udpp, dp, sp, mtu, wsz; + quint32 rate; + QString addr; + bool useudp; + + d >> ver; + if(ver != 1) + { + ConsoleLog writeLine(QString("Unable to decode multicast configuration message: Unknown version %1").arg(ver)); + return; + } + + d >> addr + >> udpp + >> dp + >> sp + >> mtu + >> wsz + >> rate + >> useudp; + if(d.status() != QDataStream::Ok) + { + ConsoleLog writeLine(QString("Unable to decode multicast configuration message: There was an error reading")); + return; + } + + _masterMcastConfig->multicastUDPPortBase(udpp); + _masterMcastConfig->multicastDPort(dp); + _masterMcastConfig->multicastSPort(sp); + _masterMcastConfig->multicastMTU(mtu); + _masterMcastConfig->multicastWinSize(wsz); + _masterMcastConfig->multicastRate(rate); + _masterMcastConfig->multicastAddress(addr); + _masterMcastConfig->multicastUseUDP(useudp); + + QSettings settings; + _masterMcastConfig->writeTo(&settings, "multicast"); + settings.sync(); + + ConsoleLog writeLine(QString("Reconfigured multicast filetransfer to IP %1, UDP port base %2, destination port %3, source port %4, MTU %5, Window Size %6, rate %7, %8using UDP") + .arg(addr).arg(udpp).arg(dp).arg(sp).arg(mtu).arg(wsz).arg(rate).arg(useudp ? "" : "not ")); +} diff --git a/src/pvs.h b/src/pvs.h index 4c61ffd..3b3365c 100644 --- a/src/pvs.h +++ b/src/pvs.h @@ -88,6 +88,7 @@ public Q_SLOTS: void cancelOutgoingMulticastTransfer(quint64 transferID); void cancelIncomingMulticastTransfer(qulonglong transferID); void setMulticastInterface(QString const& interfaceName); + void loadMcastConfig(QString const& encoded); Q_SIGNALS: void project(QString host, int port, QString passwd, bool fullscreen, -- cgit v1.2.3-55-g7522 From b003c313227e330c3ba92714655a4fc0f408f9fa Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Mon, 4 Oct 2010 00:11:13 +0200 Subject: Remove system-dependent interface enumeration code and introduce proper Model-View portable code in its stead. --- src/gui/clientConfigDialog.cpp | 52 ++------------------ src/gui/clientConfigDialog.h | 5 +- src/net/pvsNetworkInterfaceListModel.cpp | 81 ++++++++++++++++++++++++++++++++ src/net/pvsNetworkInterfaceListModel.h | 35 ++++++++++++++ 4 files changed, 124 insertions(+), 49 deletions(-) create mode 100644 src/net/pvsNetworkInterfaceListModel.cpp create mode 100644 src/net/pvsNetworkInterfaceListModel.h diff --git a/src/gui/clientConfigDialog.cpp b/src/gui/clientConfigDialog.cpp index 3867118..70d38fc 100644 --- a/src/gui/clientConfigDialog.cpp +++ b/src/gui/clientConfigDialog.cpp @@ -17,16 +17,13 @@ */ #include +#include +#include #include "clientConfigDialog.h" #include #include #include - -// For getting the network interface list: -#ifdef __linux -# include -# include -#endif +#include using namespace std; @@ -38,11 +35,10 @@ ClientConfigDialog::ClientConfigDialog(QWidget *parent) : connect(this, SIGNAL(accepted()), this, SLOT(writeSettings())); connect(radioButtonOtherRO, SIGNAL(clicked()), this, SLOT(checkPermissions())); - connect(reloadInterfaceListButton, SIGNAL(clicked()), this, SLOT(reloadNetworkInterfaceList())); - reloadNetworkInterfaceList(); + _interfaceListModel = new PVSNetworkInterfaceListModel(this); interfaceList->setModel(_interfaceListModel); interfaceList->setModelColumn(0); - + connect(reloadInterfaceListButton, SIGNAL(clicked()), _interfaceListModel, SLOT(reloadInterfaceList())); } ClientConfigDialog::~ClientConfigDialog() @@ -124,41 +120,3 @@ void ClientConfigDialog::checkPermissions() if (radioButtonLecturerNO->isChecked() && radioButtonOtherRO->isChecked()) radioButtonLecturerRO->setChecked(true); } - -void ClientConfigDialog::reloadNetworkInterfaceList() -{ -#ifdef __linux - static struct ifreq ifreqs[20]; - ifconf ifconfigs; - memset(&ifconfigs, 0, sizeof(ifconfigs)); - ifconfigs.ifc_len = sizeof(ifreqs); - ifconfigs.ifc_buf = (char*)&ifreqs; - - int nosock = socket(AF_INET, SOCK_STREAM, 0); - if (nosock < 0) - { - qWarning() << "Could not get a socket descriptor:" << strerror(errno); - } - int retval; - if ((retval = ioctl(nosock, SIOCGIFCONF, (char*)(&ifconfigs))) < 0) - { - qWarning() << "Could not get the list of interfaces:" << strerror(errno); - return; - } - - QStringList interfaces; - for(int i = 0; i < ifconfigs.ifc_len/sizeof(struct ifreq); i++) - { - char ifname[IFNAMSIZ + 1]; - strncpy(ifname, ifreqs[i].ifr_name, IFNAMSIZ); - ifname[IFNAMSIZ] = '\0'; - interfaces << QString::fromLocal8Bit(ifname); - } - if(!_interfaceListModel) - _interfaceListModel = new QStringListModel(interfaces, this); - else - _interfaceListModel->setStringList(interfaces); -#else -# warning "We have no way to get your system's network interface list. Some porting may be required." -#endif -} diff --git a/src/gui/clientConfigDialog.h b/src/gui/clientConfigDialog.h index 803f2c8..5582ec6 100644 --- a/src/gui/clientConfigDialog.h +++ b/src/gui/clientConfigDialog.h @@ -17,6 +17,8 @@ #include #include "ui_clientConfigDialog.h" +class QAbstractItemModel; + class ClientConfigDialog: public QDialog, private Ui::ClientConfigDialogClass { Q_OBJECT @@ -35,11 +37,10 @@ Q_SIGNALS: private Q_SLOTS: void checkPermissions(); - void reloadNetworkInterfaceList(); private: QSettings _settings; - QStringListModel* _interfaceListModel; + QAbstractItemModel* _interfaceListModel; }; diff --git a/src/net/pvsNetworkInterfaceListModel.cpp b/src/net/pvsNetworkInterfaceListModel.cpp new file mode 100644 index 0000000..67d0c0a --- /dev/null +++ b/src/net/pvsNetworkInterfaceListModel.cpp @@ -0,0 +1,81 @@ +/* + * pvsNetworkInterfaceListModel.cpp + * + * Created on: 04.08.2010 + * Author: brs + */ + +#include "pvsNetworkInterfaceListModel.h" +#include + +PVSNetworkInterfaceListModel::PVSNetworkInterfaceListModel(QObject* parent) : + QAbstractListModel(parent) +{ + reloadInterfaceList(); +} + +PVSNetworkInterfaceListModel::~PVSNetworkInterfaceListModel() +{ +} + +void PVSNetworkInterfaceListModel::reloadInterfaceList() +{ + _interfaces = QNetworkInterface::allInterfaces(); + reset(); +} + +QVariant PVSNetworkInterfaceListModel::data(QModelIndex const& index, int role) const +{ + int i = index.row(); + if(0 > i || i >= _interfaces.size()) + { + return QVariant(); + } + QNetworkInterface intf = _interfaces.at(i); + + switch(role) + { + case Qt::DisplayRole: + { + QString name = intf.humanReadableName(); + QList addresses = intf.addressEntries(); + QStringList l; + + foreach(QNetworkAddressEntry addr, addresses) + { + l.append(addr.ip().toString()); + } + + return QString("%1 (%2)").arg(name).arg(l.join(", ")); + } + case Qt::EditRole: + case Qt::UserRole: + return intf.name(); + default: + return QVariant(); + } +} + +QVariant PVSNetworkInterfaceListModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(section == 0 && orientation == Qt::Vertical && role == Qt::DisplayRole) + { + return tr("Interface"); + } + else + { + return QVariant(); + } +} + +int PVSNetworkInterfaceListModel::rowCount(QModelIndex const& parent) const +{ + if(parent.isValid()) + { + return 0; + } + else + { + return _interfaces.size(); + } +} diff --git a/src/net/pvsNetworkInterfaceListModel.h b/src/net/pvsNetworkInterfaceListModel.h new file mode 100644 index 0000000..3a9b95d --- /dev/null +++ b/src/net/pvsNetworkInterfaceListModel.h @@ -0,0 +1,35 @@ +/* + * pvsNetworkInterfaceListModel.h + * + * Created on: 04.08.2010 + * Author: brs + */ + +#ifndef PVSNETWORKINTERFACELISTMODEL_H_ +#define PVSNETWORKINTERFACELISTMODEL_H_ + +#include +#include +#include +#include + +class PVSNetworkInterfaceListModel : public QAbstractListModel +{ + Q_OBJECT + +public: + PVSNetworkInterfaceListModel(QObject* parent = 0); + virtual ~PVSNetworkInterfaceListModel(); + + QVariant data(QModelIndex const& index, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + int rowCount(QModelIndex const&) const; + +private: + QList _interfaces; + +public slots: + void reloadInterfaceList(); +}; + +#endif /* PVSNETWORKINTERFACELISTMODEL_H_ */ -- cgit v1.2.3-55-g7522 From de40178d470008c990711569c2bd5e7f4d4d429d Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Mon, 4 Oct 2010 00:11:25 +0200 Subject: Add Network Configuration icon --- icons/README | 3 +++ icons/network_configure.png | Bin 0 -> 4447 bytes 2 files changed, 3 insertions(+) create mode 100644 icons/README create mode 100644 icons/network_configure.png diff --git a/icons/README b/icons/README new file mode 100644 index 0000000..eccf713 --- /dev/null +++ b/icons/README @@ -0,0 +1,3 @@ +network-configure.png has been taken from the +Nuvola 1.0 Icon Set, generously provided under the LGPL by its designer, +David Vignoni. See http://www.icon-king.com/projects/nuvola/ diff --git a/icons/network_configure.png b/icons/network_configure.png new file mode 100644 index 0000000..4a2de8c Binary files /dev/null and b/icons/network_configure.png differ -- cgit v1.2.3-55-g7522 From ccf4d3cff3dde8976c9a29fc5cd25c38229c75e3 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Mon, 4 Oct 2010 00:12:21 +0200 Subject: Fix OpenPGM error handling bug resulting in spurious warnings and/or memory leaks. --- src/net/mcast/McastPGMSocket.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/net/mcast/McastPGMSocket.cpp b/src/net/mcast/McastPGMSocket.cpp index bf52bd7..fa86d3b 100644 --- a/src/net/mcast/McastPGMSocket.cpp +++ b/src/net/mcast/McastPGMSocket.cpp @@ -447,7 +447,7 @@ void McastPGMSocket::handleData() { char buf[4096]; size_t size; - pgm_error_t* err; + pgm_error_t* err = 0; status = pgm_recv(_priv->socket, buf, sizeof(buf), MSG_DONTWAIT, &size, &err); @@ -496,6 +496,7 @@ void McastPGMSocket::handleData() qDebug() << " connection reset"; emit connectionReset(); qCritical() << "Connection Reset: PGM Error: " << (err ? err->message : "(null)"); + pgm_error_free(err); break; } else if (status == PGM_IO_STATUS_FIN) -- cgit v1.2.3-55-g7522 From 9a545d82ada962bfeec4238ec6f4231759dab93e Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 15:39:09 +0200 Subject: Remove unnecessary check for glib headers/libraries --- OpenPGMConfig.cmake | 7 ------- 1 file changed, 7 deletions(-) diff --git a/OpenPGMConfig.cmake b/OpenPGMConfig.cmake index fe719f8..1b7adbc 100644 --- a/OpenPGMConfig.cmake +++ b/OpenPGMConfig.cmake @@ -3,13 +3,6 @@ SET(pgm_VERSION svn-r1135 ) -INCLUDE(${CMAKE_ROOT}/Modules/FindPkgConfig.cmake) - -PKG_CHECK_MODULES(GLIB glib-2.0>=2.10) -IF(NOT GLIB_FOUND) - MESSAGE(FATAL_ERROR "You don't seem to have GLib2 installed.") -ENDIF(NOT GLIB_FOUND) - IF(UNIX) IF(CMAKE_COMPILER_IS_GNUCC) # The scripts are fine for Linux/GCC, other platforms may or may -- cgit v1.2.3-55-g7522 From dd39677f10aaba9196b3119b9b38cd77e34b1df3 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 15:42:45 +0200 Subject: Whitespace fix --- src/gui/mainWindow.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/gui/mainWindow.cpp b/src/gui/mainWindow.cpp index c911721..1ed3ef2 100644 --- a/src/gui/mainWindow.cpp +++ b/src/gui/mainWindow.cpp @@ -160,8 +160,6 @@ MainWindow::MainWindow(QWidget *parent) : _serverSocket = new QTcpServer(); _serverSocket->listen(QHostAddress::Any, 29481); connect(_serverSocket, SIGNAL(newConnection()), this, SLOT(incomingFile())); - - } MainWindow::~MainWindow() -- cgit v1.2.3-55-g7522 From c73f21c8a607b99c855d5cce545a305f16ac9354 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 15:43:16 +0200 Subject: Forgot to add pvsNetworkInterfaceListModel.cpp to CMakeLists.txt --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0899bdf..8afb407 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,6 +116,7 @@ SET( PVSGUI_SRCS src/util/vncClientThread.cpp src/util/TextFile.cpp src/gui/aboutDialog.cpp + src/net/pvsNetworkInterfaceListModel.cpp ) @@ -212,6 +213,7 @@ SET( PVSGUI_MOC_HDRS src/gui/clientInfoDialog.h src/util/vncClientThread.h src/gui/aboutDialog.h + src/net/pvsNetworkInterfaceListModel.h ) # i18n (Qt) -- cgit v1.2.3-55-g7522 From c88247ec4d10d37b8387dc532eed0f8aba5c41f6 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 20:40:41 +0200 Subject: Fix SIGSEGV when address info cannot be parsed. --- src/net/mcast/McastPGMSocket.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/net/mcast/McastPGMSocket.cpp b/src/net/mcast/McastPGMSocket.cpp index fa86d3b..731fc13 100644 --- a/src/net/mcast/McastPGMSocket.cpp +++ b/src/net/mcast/McastPGMSocket.cpp @@ -128,6 +128,7 @@ bool McastPGMSocket::open(McastConfiguration const* config, Direction direction) { qCritical() << "Could not parse address info: PGM Error: " << err->message; + return false; } sa_family_t family = addrinfo->ai_send_addrs[0].gsr_group.ss_family; -- cgit v1.2.3-55-g7522 From c1a95b58a2045bf4fdbe0ae7d8186e5d04309622 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 20:41:14 +0200 Subject: Delete received file when user cancels rename. --- src/gui/clientFileReceiveDialog.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/gui/clientFileReceiveDialog.cpp b/src/gui/clientFileReceiveDialog.cpp index e457d23..fc6a1a3 100644 --- a/src/gui/clientFileReceiveDialog.cpp +++ b/src/gui/clientFileReceiveDialog.cpp @@ -231,9 +231,16 @@ void ClientFileReceiveDialog::mcastTransferFinished(qulonglong transferID) QString filename = QFileDialog::getSaveFileName(this, tr("Where should I save %1?").arg(_filename), _filename); QFile* file = new QFile(_filename); - if(!file->rename(filename)) + if(filename.isNull() || filename.isEmpty()) { - QMessageBox::warning(this, tr("Could not rename file"), tr("Failed to rename %1 to %2").arg(_filename).arg(filename)); + file->remove(); + } + else + { + if(!file->rename(filename)) + { + QMessageBox::warning(this, tr("Could not rename file"), tr("Failed to rename %1 to %2").arg(_filename).arg(filename)); + } } accept(); deleteLater(); -- cgit v1.2.3-55-g7522 From 1be828c0fa17b61103099caa7c84c7588b7b66a5 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 22:05:38 +0200 Subject: Fix SIGSEGV-generating lookup bug when incoming multicast transfers are retried --- src/pvs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pvs.cpp b/src/pvs.cpp index df21468..1c389df 100644 --- a/src/pvs.cpp +++ b/src/pvs.cpp @@ -755,7 +755,7 @@ void PVS::onIncomingMulticastTransfer(QString const& sender, qulonglong transfer return; PVSIncomingMulticastTransfer* transfer; - if (_incomingTransfers.value(transferID, 0)) + if (transfer = _incomingTransfers.value(transferID, 0)) { transfer->updatePort(port); QTimer::singleShot(0, transfer, SLOT(start())); -- cgit v1.2.3-55-g7522 From e753c26e49e47e24ab2417b775815bf035bd5baf Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 23:00:14 +0200 Subject: Fix recognition of letters in keyboard handler --- src/input/x11FakeKeyboardHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input/x11FakeKeyboardHandler.cpp b/src/input/x11FakeKeyboardHandler.cpp index e7a5826..b1492e7 100644 --- a/src/input/x11FakeKeyboardHandler.cpp +++ b/src/input/x11FakeKeyboardHandler.cpp @@ -848,7 +848,7 @@ void X11FakeKeyboardHandler::handle(InputEvent const& evt, InputEventContext con xmodifier_type mods = translate_modifiers(evt.qt_modifiers()); // we may need to press additional modifiers to generate this keysym: - if(isalpha(ks)) + if(QChar(ks, 0).isLetter()) { // but, since we do not differentiate upper and lower case, // and instead let the sender handle those modifiers, -- cgit v1.2.3-55-g7522 From 76756513af78ff0c96cdbcad91727f26f48cc922 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 00:05:54 +0200 Subject: Fix typo that prevents correct compilation when XInput2.h is present --- src/input/x11FakeKeyboardHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input/x11FakeKeyboardHandler.cpp b/src/input/x11FakeKeyboardHandler.cpp index b1492e7..0c32b66 100644 --- a/src/input/x11FakeKeyboardHandler.cpp +++ b/src/input/x11FakeKeyboardHandler.cpp @@ -545,7 +545,7 @@ void initialize_basic_keycodes() XIDeviceInfo* devinfos = XIQueryDevice(dpy, XIAllDevices, &ndevinfos); if(devinfos) { -#ifndef HAVE_INPUT2_H +#ifndef HAVE_XINPUT2_H # define deviceid id #endif for(int i = 0; i < ndevinfos && xkbDeviceId == XkbUseCoreKbd; i++) -- cgit v1.2.3-55-g7522 From d783dd243478921031d0f7bca80840429a8b9996 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 14:55:04 +0200 Subject: Change location of pvsprivinputd.conf to /etc/ Also centralize the knowledge of that location so that it lives in one place only. --- src/input/pvsPrivInputSocket.cpp | 31 +++++++++++++++++++++++++++++-- src/input/pvsPrivInputSocket.h | 5 +++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/input/pvsPrivInputSocket.cpp b/src/input/pvsPrivInputSocket.cpp index 2428582..c491dd9 100644 --- a/src/input/pvsPrivInputSocket.cpp +++ b/src/input/pvsPrivInputSocket.cpp @@ -29,10 +29,37 @@ using namespace std; # define UNIX_PATH_MAX 108 /* according to unix(7) */ #endif +static QSettings* pvsPrivInputSettings = 0; + +QString pvsPrivInputGetSettingsPath() +{ + return "/etc/pvsprivinputd.conf"; +} + +QSettings* pvsPrivInputGetSettings() +{ + if(!pvsPrivInputSettings) + { + pvsPrivInputSettings = new QSettings(pvsPrivInputGetSettingsPath(), QSettings::IniFormat); + } + return pvsPrivInputSettings; +} + +QSettings* pvsPrivInputReopenSettings() +{ + if(pvsPrivInputSettings) + { + delete pvsPrivInputSettings; + pvsPrivInputSettings = 0; + } + return pvsPrivInputGetSettings(); +} + QString pvsPrivInputGetSocketAddress() { - QSettings settings(QSettings::NativeFormat, QSettings::SystemScope, "openslx", "pvsprivinputd"); - return settings.value("socketpath", "/tmp/pvsprivinputd.sock").toString(); + return pvsPrivInputGetSettings()->value("socketpath", "/tmp/pvsprivinputd.sock").toString(); +} + } int pvsPrivInputMakeClientSocket() diff --git a/src/input/pvsPrivInputSocket.h b/src/input/pvsPrivInputSocket.h index e6fb0c0..879ca99 100644 --- a/src/input/pvsPrivInputSocket.h +++ b/src/input/pvsPrivInputSocket.h @@ -22,6 +22,11 @@ #include #include +class QSettings; + +QSettings* pvsPrivInputGetSettings(); +QSettings* pvsPrivInputReopenSettings(); +QString pvsPrivInputGetSettingsPath(); QString pvsPrivInputGetSocketAddress(); int pvsPrivInputMakeClientSocket(); int pvsPrivInputMakeServerSocket(); -- cgit v1.2.3-55-g7522 From a450fca8bd1964be1a4eb846ace7e0d4849d8d48 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 14:55:32 +0200 Subject: Add headers containing translated strings to sources for pvsmgr[touch] --- CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4214fd3..85fb968 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,6 +103,12 @@ SET( PVSMGR_SRCS src/util/serviceDiscoveryUtil.cpp src/gui/aboutDialog.cpp src/gui/multicastConfigDialog.cpp + + # We need the following headers for translations. + # They will not be compiled. + src/input/killX11Handler.h + src/input/rebootSystemHandler.h + src/input/sayHelloHandler.h ) # pvs -- cgit v1.2.3-55-g7522 From 9602e1063f5459d9ca05ba8f90304b7d34072561 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 15:01:33 +0200 Subject: Implement administratively configured user privileges The administrator can set a list of users and a list of groups to see as privileged. This list is reloaded whenever the configuration file changes, or changes to the user/group database are detected. --- src/input/CMakeLists.txt | 1 + src/input/pvsCheckPrivileges.cpp | 161 ++++++++++++++++++++++++++++++++++++++- src/input/pvsCheckPrivileges.h | 19 ++++- 3 files changed, 178 insertions(+), 3 deletions(-) diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 1a3b154..29c463a 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -25,6 +25,7 @@ if(UNIX) set(pvsprivinputd_MOC_HDRS pvsPrivInputHandler.h + pvsCheckPrivileges.h ) qt4_wrap_cpp(pvsprivinputd_MOC_SRCS diff --git a/src/input/pvsCheckPrivileges.cpp b/src/input/pvsCheckPrivileges.cpp index 56073bc..71c5861 100644 --- a/src/input/pvsCheckPrivileges.cpp +++ b/src/input/pvsCheckPrivileges.cpp @@ -19,13 +19,19 @@ # -------------------------------------------------------------------------- */ +#include +#include +#include #include #include #include #include +#include #include #include #include +#include +#include #include #include #include @@ -35,6 +41,7 @@ #include #include #include +#include "pvsPrivInputSocket.h" #include "pvsCheckPrivileges.h" using namespace std; @@ -97,8 +104,19 @@ PVSCheckPrivileges* PVSCheckPrivileges::instance() return &static_instance; } -PVSCheckPrivileges::PVSCheckPrivileges() +PVSCheckPrivileges::PVSCheckPrivileges(QObject* parent) + : QObject(parent) { + // initialise our cache: + updateCachedUserDatabase(); + rereadConfiguration(); + + // and make it update itself: + QStringList paths; + paths << "/etc/passwd" << "/etc/group" << pvsPrivInputGetSettingsPath(); + _watcher = new QFileSystemWatcher(paths, this); + connect(_watcher, SIGNAL(fileChanged(QString const&)), this, SLOT(updateCachedUserDatabase())); + connect(_watcher, SIGNAL(fileChanged(QString const&)), this, SLOT(rereadConfiguration())); } PVSCheckPrivileges::~PVSCheckPrivileges() @@ -164,8 +182,26 @@ PVSCheckPrivileges::UserPrivilege PVSCheckPrivileges::getUserPrivilege(CachedInp { // Always allow root: if(sender.uid == 0) + { + return USER_PRIVILEGED; + } + + // Or if the user is one of those enumerated in the privileged-users configuration value: + if(_privilegedUsers.contains(sender.uid)) + { return USER_PRIVILEGED; + } + + // Or if the user is a member of one of the groups enumerated in the privileged-groups configuration value: + foreach(gid_t gid, _privilegedGroups) + { + if(_userGroupMap.contains(sender.uid, gid)) + { + return USER_PRIVILEGED; + } + } + // User is not trivially privileged, so try to check with PolicyKit. // For PolKit, we need the start-time of the process. // On Linux, we can get it from /proc: QString procStat = QString("/proc/%1/stat").arg(sender.pid); @@ -312,3 +348,126 @@ uint qHash(CachedInputContext const& p) { return qHash(qMakePair(p.pid, qMakePair(p.uid, p.gid))); } + +void PVSCheckPrivileges::updateCachedUserDatabase() +{ + QHash userNames; + + _userGroupMap.clear(); + + // assemble a list of known users and their primary groups: + setpwent(); // open the user database + struct passwd* userrec; + while((userrec = getpwent())) + { + userNames.insert(userrec->pw_name, userrec->pw_uid); + _userGroupMap.insert(userrec->pw_uid, userrec->pw_gid); + } + endpwent(); // close the database + + // augment with secondary groups: + setgrent(); // open the group database + struct group* grouprec; + while((grouprec = getgrent())) + { + char** membername = grouprec->gr_mem; + while(*membername) + { + uid_t uid = userNames.value(*membername, (uid_t)-1); + if(uid != (uid_t)-1) + { + _userGroupMap.insert(uid, grouprec->gr_gid); + } + membername++; + } + } + endgrent(); + + // decisions may have changed, so clear the caches: + _savedUserPrivilege.clear(); +} + +void PVSCheckPrivileges::rereadConfiguration() +{ + QSettings* settings = pvsPrivInputReopenSettings(); + + _privilegedGroups.clear(); + QVariant groupList = settings->value("privileged-groups"); + if(groupList.isValid()) + { + if(!groupList.canConvert(QVariant::StringList)) + { + qWarning("There is a privileged-groups setting, but it cannot be converted to a list of strings."); + goto endGroupListScan; + } + + QStringList groups = groupList.toStringList(); + foreach(QString groupName, groups) + { + bool ok; + gid_t gid = groupName.toUInt(&ok); + if(ok) + { + _privilegedGroups.append(gid); + } + else + { + // lookup the name: + QByteArray groupNameBytes = groupName.toLocal8Bit(); + struct group* group = getgrnam(groupNameBytes.constData()); + if(group) + { + _privilegedGroups.append(group->gr_gid); + } + else + { + qWarning("privileged-groups setting contains %s which is neither a numeric GID " + "nor a valid group name. Skipping.", + groupNameBytes.constData()); + } + } + } + } + +endGroupListScan: + _privilegedUsers.clear(); + QVariant userList = settings->value("privileged-users"); + if(userList.isValid()) + { + if(!userList.canConvert(QVariant::StringList)) + { + qWarning("There is a privileged-users setting, but it cannot be converted to a list of strings."); + goto endUserListScan; + } + + QStringList users = userList.toStringList(); + foreach(QString userName, users) + { + bool ok; + uid_t uid = userName.toUInt(&ok); + if(ok) + { + _privilegedUsers.append(uid); + } + else + { + // lookup the name: + QByteArray userNameBytes = userName.toLocal8Bit(); + struct passwd* user = getpwnam(userNameBytes.constData()); + if(user) + { + _privilegedUsers.append(user->pw_uid); + } + else + { + qWarning("privileged-users setting contains %s which is neither a numeric UID " + "nor a valid user name. Skipping.", + userNameBytes.constData()); + } + } + } + } + +endUserListScan: + return; +} diff --git a/src/input/pvsCheckPrivileges.h b/src/input/pvsCheckPrivileges.h index e5cc9a4..fc82e4c 100644 --- a/src/input/pvsCheckPrivileges.h +++ b/src/input/pvsCheckPrivileges.h @@ -19,7 +19,10 @@ #define PVSCHECKPRIVILEGES_H_ #include +#include #include +#include +#include #include #include "inputEventHandler.h" @@ -64,8 +67,11 @@ struct CachedInputContext }; uint qHash(CachedInputContext const& p); -class PVSCheckPrivileges +class QFileSystemWatcher; + +class PVSCheckPrivileges : public QObject { + Q_OBJECT public: typedef enum { SESSION_LOOKUP_FAILURE, // Comes first because we default to assume @@ -115,8 +121,12 @@ public: QString getX11SessionName(CachedInputContext const& sender); QString getX11DisplayDevice(CachedInputContext const& sender); +public slots: + void updateCachedUserDatabase(); + void rereadConfiguration(); + private: - PVSCheckPrivileges(); + PVSCheckPrivileges(QObject* parent = 0); virtual ~PVSCheckPrivileges(); typedef QPair > piduidgid; @@ -134,6 +144,11 @@ private: QHash _savedUserPrivilege; QHash _savedSessionKind; QHash _savedConsoleKitSession; + + QList _privilegedUsers; + QList _privilegedGroups; + QMultiMap _userGroupMap; + QFileSystemWatcher* _watcher; }; #endif /* PVSCHECKPRIVILEGES_H_ */ -- cgit v1.2.3-55-g7522 From 6afd810d1954018791456a7cebca0d275c50ed95 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 15:08:03 +0200 Subject: Refactor signal handling in pvsprivinputd Only use one socketpair and delegate the actual decision of what to do when a specific signal is received to a special object. --- src/input/CMakeLists.txt | 2 + src/input/pvsPrivInputSignalHandler.cpp | 69 +++++++++++++++++++++++++++++++++ src/input/pvsPrivInputSignalHandler.h | 47 ++++++++++++++++++++++ src/input/pvsPrivInputSocket.cpp | 14 ++++++- src/input/pvsPrivInputSocket.h | 1 + src/input/pvsprivinputd.cpp | 40 +++++++++++-------- 6 files changed, 155 insertions(+), 18 deletions(-) create mode 100644 src/input/pvsPrivInputSignalHandler.cpp create mode 100644 src/input/pvsPrivInputSignalHandler.h diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 29c463a..fcb5ff5 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -18,6 +18,7 @@ if(UNIX) pvsprivinputd.cpp pvsPrivInputHandler.cpp pvsCheckPrivileges.cpp + pvsPrivInputSignalHandler.cpp rebootSystemHandler.cpp killX11Handler.cpp sayHelloHandler.cpp @@ -26,6 +27,7 @@ if(UNIX) set(pvsprivinputd_MOC_HDRS pvsPrivInputHandler.h pvsCheckPrivileges.h + pvsPrivInputSignalHandler.h ) qt4_wrap_cpp(pvsprivinputd_MOC_SRCS diff --git a/src/input/pvsPrivInputSignalHandler.cpp b/src/input/pvsPrivInputSignalHandler.cpp new file mode 100644 index 0000000..b09c335 --- /dev/null +++ b/src/input/pvsPrivInputSignalHandler.cpp @@ -0,0 +1,69 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # pvsPrivInputSignalHandler.cpp: + # - Handle signals forwarded over a file descriptor - implementation + # -------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include "pvsPrivInputSocket.h" +#include "pvsPrivInputSignalHandler.h" + +void PVSPrivInputSignalHandler::signalReceived(int sigfd) +{ + int signum; + + pid_t pid; + uid_t uid; + gid_t gid; + int err; + size_t siz = sizeof(signum); + + if(!pvsPrivInputRecvMessage(sigfd, &signum, siz, pid, uid, gid, &err)) + { + qWarning("Should have received a signal but could not read message: %s", strerror(err)); + return; + } + + if(siz != sizeof(signum)) + { + qWarning("Sould have received a message of size %d bytes but got %d bytes instead. Discarding message.", (int)sizeof(signum), (int)siz); + return; + } + + if(!_allowUnauthenticatedKilling && pid != getpid()) + { + qCritical("SOMETHING STRANGE IS GOING ON!\nReceived signal %d from PID %d\nPlease kill me instead of sending me signal notifications.", signum, (int)pid); + return; + } + + switch(signum) + { + case SIGINT: + qDebug("Caught SIGINT. Quitting."); + emit terminate(); + break; + case SIGTERM: + qDebug("Caught SIGTERM. Quitting."); + emit terminate(); + break; + case SIGHUP: + qDebug("Caught SIGHUP. Reloading configuration."); + emit reloadConfiguration(); + break; + default: + qWarning("Received signal %d, but don't know how to handle it.", signum); + } +} diff --git a/src/input/pvsPrivInputSignalHandler.h b/src/input/pvsPrivInputSignalHandler.h new file mode 100644 index 0000000..cb75c86 --- /dev/null +++ b/src/input/pvsPrivInputSignalHandler.h @@ -0,0 +1,47 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # pvsPrivInputSignalHandler.h: + # - Handle signals forwarded over a file descriptor - interface + # -------------------------------------------------------------------------- + */ + +#ifndef PVSPRIVINPUTSIGNALHANDLER_H_ +#define PVSPRIVINPUTSIGNALHANDLER_H_ + +#include + +class PVSPrivInputSignalHandler : public QObject +{ + Q_OBJECT +public: + PVSPrivInputSignalHandler(QObject* parent = 0) + : QObject(parent) + { + } + + void allowUnauthenticatedKilling(bool value) + { + _allowUnauthenticatedKilling = value; + } + +public slots: + void signalReceived(int sigfd); + +signals: + void terminate(); + void reloadConfiguration(); + +private: + bool _allowUnauthenticatedKilling; +}; + +#endif /* PVSPRIVINPUTSIGNALHANDLER_H_ */ diff --git a/src/input/pvsPrivInputSocket.cpp b/src/input/pvsPrivInputSocket.cpp index c491dd9..df5dff5 100644 --- a/src/input/pvsPrivInputSocket.cpp +++ b/src/input/pvsPrivInputSocket.cpp @@ -60,6 +60,17 @@ QString pvsPrivInputGetSocketAddress() return pvsPrivInputGetSettings()->value("socketpath", "/tmp/pvsprivinputd.sock").toString(); } +bool pvsPrivInputEnableReceiveCredentials(int sock) +{ + int passcred = 1; + if(setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &passcred, sizeof(passcred)) < 0) + { + return false; + } + else + { + return true; + } } int pvsPrivInputMakeClientSocket() @@ -110,8 +121,7 @@ int pvsPrivInputMakeServerSocket() } // Announce that credentials are requested: - int passcred = 1; - if(setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &passcred, sizeof(passcred)) < 0) + if(!pvsPrivInputEnableReceiveCredentials(sock)) { // We will not operate without credentials. qCritical("Could not request peer credentials: %s", strerror(errno)); diff --git a/src/input/pvsPrivInputSocket.h b/src/input/pvsPrivInputSocket.h index 879ca99..447360b 100644 --- a/src/input/pvsPrivInputSocket.h +++ b/src/input/pvsPrivInputSocket.h @@ -28,6 +28,7 @@ QSettings* pvsPrivInputGetSettings(); QSettings* pvsPrivInputReopenSettings(); QString pvsPrivInputGetSettingsPath(); QString pvsPrivInputGetSocketAddress(); +bool pvsPrivInputEnableReceiveCredentials(int sock); int pvsPrivInputMakeClientSocket(); int pvsPrivInputMakeServerSocket(); bool pvsPrivInputSendMessage(int sock, void* buf, size_t len, int* err = 0); diff --git a/src/input/pvsprivinputd.cpp b/src/input/pvsprivinputd.cpp index 3a5e1a8..361bf9f 100644 --- a/src/input/pvsprivinputd.cpp +++ b/src/input/pvsprivinputd.cpp @@ -30,6 +30,7 @@ #include #include "pvsPrivInputSocket.h" #include "pvsPrivInputHandler.h" +#include "pvsPrivInputSignalHandler.h" using namespace std; @@ -38,7 +39,7 @@ using namespace std; #endif QByteArray socketPath; -int sigintFds[2]; +int signalFds[2]; static void unlinkSocket() { @@ -48,12 +49,9 @@ static void unlinkSocket() } } -static void onInterrupt(int) +static void onCaughtSignal(int signum) { - char buf[] = { '!' }; - char msg[] = "SIGINT caught. Quitting.\n"; - write(sigintFds[0], buf, 1); - write(1, msg, sizeof(msg)-1); + pvsPrivInputSendMessage(signalFds[0], &signum, sizeof(signum), 0); } int main(int argc, char** argv) @@ -103,28 +101,38 @@ int main(int argc, char** argv) // the socket (access control is handled differently): umask(0); + // Store the socket path before the signal handler is installed so it does not risk segfaulting + // due to bad timing. + socketPath = pvsPrivInputGetSocketAddress().toLocal8Bit(); + // Ignore SIGPIPE. Connection errors are handled internally. // According to signal(2), the only error is that the signal number // is invalid. This cannot happen. signal(SIGPIPE, SIG_IGN); - // set up signal handling: - if(socketpair(AF_UNIX, SOCK_STREAM, 0, sigintFds) < 0) + // Create our socket: + int sock = pvsPrivInputMakeServerSocket(); + if(sock < 0) { - qCritical("Could not set up signal handling. Giving up."); exit(EXIT_FAILURE); } - socketPath = pvsPrivInputGetSocketAddress().toLocal8Bit(); - QSocketNotifier sigintWatcher(sigintFds[1], QSocketNotifier::Read); - QObject::connect(&sigintWatcher, SIGNAL(activated(int)), &app, SLOT(quit())); - signal(SIGINT, onInterrupt); - // Create our socket: - int sock = pvsPrivInputMakeServerSocket(); - if(sock < 0) + // set up signal handling: + if(socketpair(AF_UNIX, SOCK_DGRAM, 0, signalFds) < 0) { + qCritical("Could not set up signal handling. Giving up."); exit(EXIT_FAILURE); } + PVSPrivInputSignalHandler sigHandler; + QSocketNotifier sigintWatcher(signalFds[1], QSocketNotifier::Read); + sigHandler.allowUnauthenticatedKilling(!pvsPrivInputEnableReceiveCredentials(signalFds[1])); + QObject::connect(&sigintWatcher, SIGNAL(activated(int)), &sigHandler, SLOT(signalReceived(int))); + QObject::connect(&sigHandler, SIGNAL(terminate()), &app, SLOT(quit())); + QObject::connect(&sigHandler, SIGNAL(reloadConfiguration()), PVSCheckPrivileges::instance(), SLOT(updateCachedUserDatabase())); + QObject::connect(&sigHandler, SIGNAL(reloadConfiguration()), PVSCheckPrivileges::instance(), SLOT(rereadConfiguration())); + signal(SIGINT, onCaughtSignal); + signal(SIGTERM, onCaughtSignal); + signal(SIGHUP, onCaughtSignal); // Our setup is complete. if(!no_fork) -- cgit v1.2.3-55-g7522 From c5c91f40c02d7b8611fb5e479f340fe19254592f Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 15:09:43 +0200 Subject: Fix deletion order bug PVSCheckPrivileges::instance() is statically allocated. When it is deleted, the QCoreApplication is already gone (since it is stack- allocated), and the destructor of QFileSystemWatcher waits forever. --- src/input/pvsCheckPrivileges.cpp | 13 +++++++++++-- src/input/pvsCheckPrivileges.h | 1 + src/input/pvsprivinputd.cpp | 3 +++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/input/pvsCheckPrivileges.cpp b/src/input/pvsCheckPrivileges.cpp index 71c5861..57b5d8b 100644 --- a/src/input/pvsCheckPrivileges.cpp +++ b/src/input/pvsCheckPrivileges.cpp @@ -100,8 +100,17 @@ Q_DECLARE_METATYPE(QStringStringMap) PVSCheckPrivileges* PVSCheckPrivileges::instance() { - static PVSCheckPrivileges static_instance; - return &static_instance; + static PVSCheckPrivileges* static_instance = 0; + if(!static_instance) + { + static_instance = new PVSCheckPrivileges(); + } + return static_instance; +} + +void PVSCheckPrivileges::deleteInstance() +{ + delete instance(); } PVSCheckPrivileges::PVSCheckPrivileges(QObject* parent) diff --git a/src/input/pvsCheckPrivileges.h b/src/input/pvsCheckPrivileges.h index fc82e4c..ec6c591 100644 --- a/src/input/pvsCheckPrivileges.h +++ b/src/input/pvsCheckPrivileges.h @@ -114,6 +114,7 @@ public: } static PVSCheckPrivileges* instance(); + static void deleteInstance(); bool require(SessionKind sessionKind, CachedInputContext const& sender); bool require(UserPrivilege userPrivilege, CachedInputContext const& sender); diff --git a/src/input/pvsprivinputd.cpp b/src/input/pvsprivinputd.cpp index 361bf9f..2f19d90 100644 --- a/src/input/pvsprivinputd.cpp +++ b/src/input/pvsprivinputd.cpp @@ -28,6 +28,7 @@ #include #include #include +#include "pvsCheckPrivileges.h" #include "pvsPrivInputSocket.h" #include "pvsPrivInputHandler.h" #include "pvsPrivInputSignalHandler.h" @@ -151,5 +152,7 @@ int main(int argc, char** argv) // and run the main loop. int ret = app.exec(); + + PVSCheckPrivileges::deleteInstance(); return ret; } -- cgit v1.2.3-55-g7522 From c8579de3b79b64bcb72adfd97d2ebf424e8df4b7 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 15:13:31 +0200 Subject: Detect the presence of PolKit on configuration When no PolKit is installed, the program gets built without support for it. This can be overriden by specifying -DENABLE_POLKIT=ON on the cmake command line. --- src/input/CMakeLists.txt | 21 +++++++++++++++++++++ src/input/pvsCheckPrivileges.cpp | 4 ++++ 2 files changed, 25 insertions(+) diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index fcb5ff5..44cf6b6 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -57,6 +57,27 @@ if(UNIX) x11FakeMouseHandler.cpp privilegedHandlerForwarder.cpp ) + + # we need pkg-config to find out where to install the action file: + find_package(PkgConfig) + if(NOT PKG_CONFIG_FOUND) + # we will try to make a best effort and put our policy file into + # ${PREFIX}/share/polkit-1, but only if we can find + # the pkexec executable. + find_program(PKEXEC_LOCATION pkexec) + if(PKEXEC_LOCATION OR ENABLE_POLKIT) + set(POLKIT_FOUND ON) + endif() + else() + pkg_check_modules(POLKIT "polkit-gobject-1") + endif() + + # now, arrange for policykit integration: + if(POLKIT_FOUND OR ENABLE_POLKIT) + set_property(SOURCE ${pvsprivinputd_SRCS} + APPEND + PROPERTY COMPILE_DEFINITIONS HAVE_POLKIT) + endif() endif() add_library( diff --git a/src/input/pvsCheckPrivileges.cpp b/src/input/pvsCheckPrivileges.cpp index 57b5d8b..f9a8851 100644 --- a/src/input/pvsCheckPrivileges.cpp +++ b/src/input/pvsCheckPrivileges.cpp @@ -211,6 +211,7 @@ PVSCheckPrivileges::UserPrivilege PVSCheckPrivileges::getUserPrivilege(CachedInp } // User is not trivially privileged, so try to check with PolicyKit. +#ifdef HAVE_POLKIT // but only if it is present // For PolKit, we need the start-time of the process. // On Linux, we can get it from /proc: QString procStat = QString("/proc/%1/stat").arg(sender.pid); @@ -257,6 +258,9 @@ PVSCheckPrivileges::UserPrivilege PVSCheckPrivileges::getUserPrivilege(CachedInp return USER_LOOKUP_FAILURE; } return reply.value().isAuthorized ? USER_PRIVILEGED : USER_UNPRIVILEGED; +#else + return USER_UNPRIVILEGED; +#endif } QString PVSCheckPrivileges::getX11SessionName(CachedInputContext const& sender) -- cgit v1.2.3-55-g7522 From dce35518d9b91b1bac0394792f83450fbe4aa3bc Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 15:17:40 +0200 Subject: Install PolKit policy file and try to detect the policy directory. --- src/input/CMakeLists.txt | 19 +++++++++++++++++++ src/input/org.openslx.pvs.input.policy | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 src/input/org.openslx.pvs.input.policy diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 44cf6b6..0cf5177 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -67,16 +67,35 @@ if(UNIX) find_program(PKEXEC_LOCATION pkexec) if(PKEXEC_LOCATION OR ENABLE_POLKIT) set(POLKIT_FOUND ON) + set(POLKIT_PREFIX ${CMAKE_INSTALL_PREFIX}) + set(POLKIT_POLICY_DIR ${POLKIT_PREFIX}/share/polkit-1/actions) endif() else() pkg_check_modules(POLKIT "polkit-gobject-1") + if(POLKIT_FOUND) + execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} polkit-gobject-1 --variable=policydir + OUTPUT_VARIABLE POLKIT_POLICY_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT POLKIT_POLICY_DIR) + set(POLKIT_FOUND OFF) + endif() + endif() endif() # now, arrange for policykit integration: if(POLKIT_FOUND OR ENABLE_POLKIT) + if(NOT POLKIT_POLICY_DIR) + message(SEND_ERROR "PolicyKit integration is enabled, but cannot find PolicyKit's actions directory. Please set POLKIT_POLICY_DIR to the right value") + endif() + + install(FILES org.openslx.pvs.input.policy + DESTINATION ${POLKIT_POLICY_DIR}) set_property(SOURCE ${pvsprivinputd_SRCS} APPEND PROPERTY COMPILE_DEFINITIONS HAVE_POLKIT) + message(STATUS "PolicyKit integration: enabled") + else() + message(STATUS "PolicyKit integration: disabled") endif() endif() diff --git a/src/input/org.openslx.pvs.input.policy b/src/input/org.openslx.pvs.input.policy new file mode 100644 index 0000000..f0de9f2 --- /dev/null +++ b/src/input/org.openslx.pvs.input.policy @@ -0,0 +1,18 @@ + + + + The OpenSLX project + http://lab.openslx.org/pvs + + + Use privileged input actions in PVS + Authentication is required to let PVS use privileged input actions + + auth_self_keep + auth_self_keep + auth_admin_keep + + + -- cgit v1.2.3-55-g7522 From aeeb2b034f50ad2938b1c32507f20070a26c1849 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 15:19:44 +0200 Subject: Implement log targets for pvsprivinputd Also: - add command line options and usage message. - change --no-fork to --daemon to bring it in line with the pvs daemon. --- src/input/CMakeLists.txt | 2 + src/input/pvsSyslog.cpp | 133 ++++++++++++++++++++++++++++++++++++++++++++ src/input/pvsSyslog.h | 63 +++++++++++++++++++++ src/input/pvsprivinputd.cpp | 116 ++++++++++++++++++++++++++++++++++++-- 4 files changed, 308 insertions(+), 6 deletions(-) create mode 100644 src/input/pvsSyslog.cpp create mode 100644 src/input/pvsSyslog.h diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 0cf5177..a2e6fda 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -18,6 +18,7 @@ if(UNIX) pvsprivinputd.cpp pvsPrivInputHandler.cpp pvsCheckPrivileges.cpp + pvsSyslog.cpp pvsPrivInputSignalHandler.cpp rebootSystemHandler.cpp killX11Handler.cpp @@ -27,6 +28,7 @@ if(UNIX) set(pvsprivinputd_MOC_HDRS pvsPrivInputHandler.h pvsCheckPrivileges.h + pvsSyslog.h pvsPrivInputSignalHandler.h ) diff --git a/src/input/pvsSyslog.cpp b/src/input/pvsSyslog.cpp new file mode 100644 index 0000000..6a9be31 --- /dev/null +++ b/src/input/pvsSyslog.cpp @@ -0,0 +1,133 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # pvsSyslog.cpp: + # - Send output to syslog or to a file - implementation + # -------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include +#include "pvsSyslog.h" + +using namespace std; + +#define PVS_LOG_BUFSIZ 8192 + +void PVSLogRedirector::inputAvailableOn(int fd) +{ + QByteArray readbuf; + readbuf.resize(PVS_LOG_BUFSIZ); + ssize_t siz; + bool cont = true; + + do + { + siz = recv(fd, readbuf.data(), readbuf.size(), MSG_DONTWAIT); + if(siz > 0) + { + _buf.append(readbuf.constData(), siz); + + // Send lines: + int oldnpos = -1; + int npos = -1; + while((npos = _buf.indexOf('\n', oldnpos + 1)) >= 0) + { + if((oldnpos + 1) == npos) + { + oldnpos = npos; + continue; + } + QString line = QString::fromLocal8Bit(_buf.constData() + oldnpos + 1, npos - oldnpos - 1); + doRedirectInput(line); + oldnpos = npos; + } + + // All complete lines have been sent. Remove sent bytes from buffer. + _buf.remove(0, oldnpos + 1); + + // If line length is too large, send it now. + if(_buf.size() >= BUFSIZ) + { + QString longLine = QString::fromLocal8Bit(_buf.constData(), _buf.size()); + doRedirectInput(longLine); + _buf.clear(); + } + } + else if(siz == 0) + { + // Socket closed on other end. + break; + } + else if(siz < 0) + { + int err = errno; + switch(err) + { + case EAGAIN: +#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) + case EWOULDBLOCK: +#endif + // We read all there is to read. + cont = false; + break; + default: + // Something happened. + // We can't inform the user via qDebug() and friends as that would loop right back to us again, + // so we emit a log line. + doRedirectInput(QString("Error in logging code: Could not read log input: %s").arg(strerror(err))); + cont = false; + } + } + } + while(cont); +} + +PVSSyslogRedirector::PVSSyslogRedirector() +{ + openlog("pvsprivinputd", LOG_PID, LOG_USER); +} + +PVSSyslogRedirector::~PVSSyslogRedirector() +{ + closelog(); +} + +void PVSSyslogRedirector::doRedirectInput(QString const& line) +{ + QByteArray bytes = line.toLocal8Bit(); + syslog(LOG_NOTICE, "%s", bytes.data()); +} + +PVSLogfileRedirector::PVSLogfileRedirector(QString const& filename) +{ + _file = new QFile(filename, this); + _file->open(QIODevice::Append); + _stream = new QTextStream(_file); +} + +PVSLogfileRedirector::~PVSLogfileRedirector() +{ + _stream->flush(); + _file->close(); + delete _stream; + delete _file; +} + +void PVSLogfileRedirector::doRedirectInput(QString const& line) +{ + *_stream << line << flush; +} diff --git a/src/input/pvsSyslog.h b/src/input/pvsSyslog.h new file mode 100644 index 0000000..8c9591a --- /dev/null +++ b/src/input/pvsSyslog.h @@ -0,0 +1,63 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # pvsSyslog.h: + # - Send output to syslog or to a file - interface + # -------------------------------------------------------------------------- + */ + +#ifndef PVSSYSLOG_H_ +#define PVSSYSLOG_H_ + +#include +#include + +class PVSLogRedirector : public QObject +{ + Q_OBJECT +public slots: + void inputAvailableOn(int fd); + +protected: + virtual void doRedirectInput(QString const& line) = 0; + +private: + QByteArray _buf; +}; + +class PVSSyslogRedirector : public PVSLogRedirector +{ +public: + PVSSyslogRedirector(); + virtual ~PVSSyslogRedirector(); + +protected: + void doRedirectInput(QString const& line); +}; + +class QFile; +class QTextStream; + +class PVSLogfileRedirector : public PVSLogRedirector +{ +public: + PVSLogfileRedirector(QString const& filename); + virtual ~PVSLogfileRedirector(); + +protected: + void doRedirectInput(QString const& line); + +private: + QFile* _file; + QTextStream* _stream; +}; + +#endif /* PVSSYSLOG_H_ */ diff --git a/src/input/pvsprivinputd.cpp b/src/input/pvsprivinputd.cpp index 2f19d90..e6ae155 100644 --- a/src/input/pvsprivinputd.cpp +++ b/src/input/pvsprivinputd.cpp @@ -22,8 +22,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -32,6 +34,7 @@ #include "pvsPrivInputSocket.h" #include "pvsPrivInputHandler.h" #include "pvsPrivInputSignalHandler.h" +#include "pvsSyslog.h" using namespace std; @@ -42,6 +45,9 @@ using namespace std; QByteArray socketPath; int signalFds[2]; +QTextStream qout(stdout, QIODevice::WriteOnly); +QTextStream qerr(stderr, QIODevice::WriteOnly); + static void unlinkSocket() { if(!socketPath.isNull()) @@ -55,13 +61,70 @@ static void onCaughtSignal(int signum) pvsPrivInputSendMessage(signalFds[0], &signum, sizeof(signum), 0); } +static void usage() +{ + qout << QObject::tr( + "Usage: %1 [--help|-h] [--daemon|-d] [--log=|-l]\n" + "\n" + "Options:\n" + " --help, -h Show this message\n" + " --daemon, -d Run in background\n" + " --log=,\n" + " -l Redirect all output to \n" + " valid values are:\n" + " - any file name\n" + " - `syslog' to redirect output to the system log\n" + " - `null' to discard output (default)\n" + " (without quotes)\n" + "\n" + "Signals:\n" + " SIGINT, SIGTERM (or press Ctrl+C when run in foreground)\n" + " Quit\n" + " SIGHUP Reload configuration and cached user data\n").arg(qApp->arguments().at(0)) << flush; +} + int main(int argc, char** argv) { QCoreApplication app(argc, argv); app.setApplicationName("pvsprivinputd"); app.setOrganizationName("openslx"); - bool no_fork = app.arguments().contains("--no-fork", Qt::CaseInsensitive); + bool no_fork = true; + QString logTarget = "null"; + + foreach(QString arg, app.arguments().mid(1)) + { + if(arg == "--daemon" || arg == "-d") + { + no_fork = false; + } + else if(arg.startsWith("-l")) + { + logTarget = arg.mid(2); + } + else if(arg.startsWith("--log=")) + { + logTarget = arg.mid(6); + } + else if(arg == "--help" || arg == "-h") + { + usage(); + return EXIT_SUCCESS; + } + else + { + qout << "Unexpected argument: " << arg << endl; + usage(); + return EXIT_FAILURE; + } + } + + if(logTarget != "null" && logTarget != "syslog") + { + logTarget = QFileInfo(logTarget).absoluteFilePath(); + qout << "Writing log to " << logTarget << endl; + } + if(!no_fork) { pid_t pid; @@ -118,6 +181,9 @@ int main(int argc, char** argv) exit(EXIT_FAILURE); } + // Install our main object + PVSPrivInputHandler handler(sock); + // set up signal handling: if(socketpair(AF_UNIX, SOCK_DGRAM, 0, signalFds) < 0) { @@ -135,24 +201,62 @@ int main(int argc, char** argv) signal(SIGTERM, onCaughtSignal); signal(SIGHUP, onCaughtSignal); + PVSLogRedirector* logRedir = 0; + QSocketNotifier* logNotif = 0; // Our setup is complete. if(!no_fork) { // Reopen standard file descriptors: freopen("/dev/null", "r", stdin); - freopen("/dev/null", "w", stderr); - freopen("/dev/null", "w", stdout); - } - // Install our main object - PVSPrivInputHandler handler(sock); + if(logTarget == "null") + { + freopen("/dev/null", "w", stdout); + freopen("/dev/null", "w", stderr); + } + else if(logTarget == "syslog") + { + logRedir = new PVSSyslogRedirector(); + } + else + { + logRedir = new PVSLogfileRedirector(logTarget); + } + + if(logRedir) + { + int logFds[2]; + if(socketpair(AF_UNIX, SOCK_STREAM, 0, logFds) < 0) + { + qWarning("Could not open a socket pair: %s", strerror(errno)); + return EXIT_FAILURE; + } + + logNotif = new QSocketNotifier(logFds[1], QSocketNotifier::Read); + QObject::connect(logNotif, SIGNAL(activated(int)), logRedir, SLOT(inputAvailableOn(int))); + + // redirect stdout and stderr: + dup2(logFds[0], 1); + dup2(logFds[0], 2); + } + } atexit(unlinkSocket); + qout << "PVS Privileged Input Daemon initialization complete. Entering main loop." << endl; + // and run the main loop. int ret = app.exec(); + if(logRedir) + { + delete logNotif; + delete logRedir; + } PVSCheckPrivileges::deleteInstance(); + + qout << "PVS Privileged Input Daemon deinitialization complete. Exiting." << endl; + return ret; } -- cgit v1.2.3-55-g7522 From ea97a3d4459e856c62db281a0f12e20452aebefc Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 15:21:49 +0200 Subject: Install a pvsprivinputd.conf template into /etc --- src/input/CMakeLists.txt | 5 +++++ src/input/pvsprivinputd.conf | 14 ++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 src/input/pvsprivinputd.conf diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index a2e6fda..398ca55 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -99,6 +99,11 @@ if(UNIX) else() message(STATUS "PolicyKit integration: disabled") endif() + + # Install a pvsprivinputd.conf template + install(FILES pvsprivinputd.conf + DESTINATION /etc + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) endif() add_library( diff --git a/src/input/pvsprivinputd.conf b/src/input/pvsprivinputd.conf new file mode 100644 index 0000000..52df206 --- /dev/null +++ b/src/input/pvsprivinputd.conf @@ -0,0 +1,14 @@ +;; Lines starting with `;' are comments. +;; +;; socketpath: +;; Where pvsprivinputd should put its listening socket. +socketpath = /tmp/pvsprivinputd.sock + +;; privileged-users: +;; Comma-separated list of users that are allowed to run privileged actions +privileged-users = root + +;; privileged-groups: +;; Comma-separated list of user groups that are allowed to run privileged actions +; privileged-groups = wheel + \ No newline at end of file -- cgit v1.2.3-55-g7522 From c5a99933202c91630edc2ddd97e0e964b27540d6 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 17:56:59 +0200 Subject: Sanitize security model yet again The flags model was not satisfactory since it made it unnecessarily difficult to express the standard policy of "allow all to users that are physically sitting in front of the machine and to privileged users". The new model expressly knows different policies (two at the moment) and refrains from decomposing them. Additional policies are not difficult to add. --- src/input/CMakeLists.txt | 2 +- src/input/inputEventHandler.h | 58 +++++++++++++++++++++++++++++++------------ src/input/inputHandlerChain.h | 10 ++++---- 3 files changed, 48 insertions(+), 22 deletions(-) diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 398ca55..0e72c4c 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -2,7 +2,6 @@ include(${QT_USE_FILE}) set(pvsinput_SRCS inputEvent.cpp - inputEventHandler.cpp ) if(UNIX) @@ -23,6 +22,7 @@ if(UNIX) rebootSystemHandler.cpp killX11Handler.cpp sayHelloHandler.cpp + inputEventHandler.cpp ) set(pvsprivinputd_MOC_HDRS diff --git a/src/input/inputEventHandler.h b/src/input/inputEventHandler.h index 44713c2..52e3338 100644 --- a/src/input/inputEventHandler.h +++ b/src/input/inputEventHandler.h @@ -18,6 +18,7 @@ #define INPUTEVENTHANDLER_H_ #include +#include #include #include #include @@ -97,26 +98,33 @@ public: namespace policy { enum SecurityFlags { - SEC_PHYSICAL_SEAT = 1, - SEC_PRIVILEGED_USER = 2 + SEC_FREE_FOR_ALL, + SEC_PHYSICAL_OR_PRIVILEGED }; bool allowPhysicalSeat(InputEvent const& evt, InputEventContext const* ctx); bool allowPrivilegedUser(InputEvent const& evt, InputEventContext const* ctx); -template -struct Security +struct SecurityAllowAny { bool allow(InputEvent const& evt, InputEventContext const* ctx) { - if((flags & SEC_PHYSICAL_SEAT) && !allowPhysicalSeat(evt, ctx)) - return false; - if((flags & SEC_PRIVILEGED_USER) && !allowPrivilegedUser(evt, ctx)) - return false; return true; } }; +struct SecurityAllowPhysicalOrPrivileged +{ + bool allow(InputEvent const& evt, InputEventContext const* ctx) + { + if(allowPhysicalSeat(evt, ctx)) + return true; + else if(allowPrivilegedUser(evt, ctx)) + return true; + return false; + } +}; + struct UnixLike; struct Linux; struct Windows; @@ -154,6 +162,8 @@ public: bool handle(InputEvent const& evt, InputEventContext const* context = 0) { if(!securityPolicy.allow(evt, context)) { + std::string evtStr = evt.toString(); + qWarning("Input Event %s has been denied by security policy", evtStr.c_str()); return true; } if(delegate.matches(evt, context)) { @@ -196,19 +206,32 @@ public: } }; -template > +template struct Handler : public HandlerHelper { }; -template +template +struct ApplyDefaultSecurityPolicy +{ + typedef HandlerType type; +}; + +template +struct ApplyDefaultSecurityPolicy > +{ + typedef Handler type; +}; + +template struct InputEventHandlerChainHelper { private: typedef typename boost::mpl::next::type next_iterator_type; - typedef InputEventHandlerChainHelper next_in_chain; + typedef InputEventHandlerChainHelper next_in_chain; - typedef typename boost::mpl::deref::type handler_type; + typedef typename boost::mpl::deref::type handler_entry_type; + typedef typename ApplyDefaultSecurityPolicy::type handler_type; handler_type _handler; next_in_chain _next; @@ -239,8 +262,8 @@ public: } }; -template -struct InputEventHandlerChainHelper +template +struct InputEventHandlerChainHelper { void handle(InputEvent const&, InputEventContext const* context = 0) { // do nothing @@ -261,8 +284,11 @@ struct InputEventHandlerChainHelper } }; -template -struct InputEventHandlerChain : public InputEventHandlerChainHelper::type, typename boost::mpl::end::type> +template +struct InputEventHandlerChain : + public InputEventHandlerChainHelper::type, + typename boost::mpl::end::type> { }; diff --git a/src/input/inputHandlerChain.h b/src/input/inputHandlerChain.h index 8bcb1d8..b012aa6 100644 --- a/src/input/inputHandlerChain.h +++ b/src/input/inputHandlerChain.h @@ -34,14 +34,14 @@ typedef boost::mpl::list< Handler >::type unprivileged_handler_list; -typedef InputEventHandlerChain unprivileged_handler_chain; +typedef InputEventHandlerChain unprivileged_handler_chain; typedef boost::mpl::list< - Handler, - Handler, policy::Security >, - Handler, policy::Security > + Handler, + Handler >, + Handler > >::type privileged_handler_list; -typedef InputEventHandlerChain privileged_handler_chain; +typedef InputEventHandlerChain privileged_handler_chain; #endif /* INPUTHANDLERCHAIN_H_ */ -- cgit v1.2.3-55-g7522 From 32672a51fe8b8ccf63e1cfeb89ac5e020fde787a Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 18:00:08 +0200 Subject: Implement magic SysRq handler. --- CMakeLists.txt | 1 + src/input/CMakeLists.txt | 1 + src/input/inputHandlerChain.h | 4 ++- src/input/magicSysRqHandler.cpp | 28 ++++++++++++++++ src/input/magicSysRqHandler.h | 72 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 src/input/magicSysRqHandler.cpp create mode 100644 src/input/magicSysRqHandler.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 85fb968..cc695ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,6 +109,7 @@ SET( PVSMGR_SRCS src/input/killX11Handler.h src/input/rebootSystemHandler.h src/input/sayHelloHandler.h + src/input/magicSysRqHandler.h ) # pvs diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 0e72c4c..20fd531 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -21,6 +21,7 @@ if(UNIX) pvsPrivInputSignalHandler.cpp rebootSystemHandler.cpp killX11Handler.cpp + magicSysRqHandler.cpp sayHelloHandler.cpp inputEventHandler.cpp ) diff --git a/src/input/inputHandlerChain.h b/src/input/inputHandlerChain.h index b012aa6..3c9446c 100644 --- a/src/input/inputHandlerChain.h +++ b/src/input/inputHandlerChain.h @@ -26,6 +26,7 @@ #include "rebootSystemHandler.h" #include "sayHelloHandler.h" #include "killX11Handler.h" +#include "magicSysRqHandler.h" typedef boost::mpl::list< Handler >, @@ -39,7 +40,8 @@ typedef InputEventHandlerChain, Handler >, - Handler > + Handler >, + Handler > >::type privileged_handler_list; typedef InputEventHandlerChain privileged_handler_chain; diff --git a/src/input/magicSysRqHandler.cpp b/src/input/magicSysRqHandler.cpp new file mode 100644 index 0000000..108bfca --- /dev/null +++ b/src/input/magicSysRqHandler.cpp @@ -0,0 +1,28 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # inputEventHandler.h: + # - Common definitions for input event handlers + # -------------------------------------------------------------------------- + */ + +#include +#include "magicSysRqHandler.h" + +void MagicSysRqHandler::handle(InputEvent const& evt, InputEventContext const*) +{ + QFile trigger("/proc/sysrq-trigger"); + trigger.open(QIODevice::WriteOnly); + char c = (char)(evt.value() & 0xff); + trigger.write(&c, 1); + trigger.flush(); + trigger.close(); +} diff --git a/src/input/magicSysRqHandler.h b/src/input/magicSysRqHandler.h new file mode 100644 index 0000000..563d091 --- /dev/null +++ b/src/input/magicSysRqHandler.h @@ -0,0 +1,72 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # magicSysRqHandler.h + # - Trigger Magic-SysRq functions - interface + # -------------------------------------------------------------------------- + */ + +#ifndef MAGICSYSRQHANDLER_H_ +#define MAGICSYSRQHANDLER_H_ + +#include "inputEventHandler.h" + +class MagicSysRqHandler : public DefaultInputEventHandler +{ +public: + void handle(InputEvent const& evt, InputEventContext const* ctx); + + static void describeInto(QList& list) + { + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Reboot immediately"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'b'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Crash system"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'c'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Show all held logs"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'd'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Send SIGTERM to all"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'e'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Activate OOM killer"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'f'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Send SIGKILL to all"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'i'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Force thaw filesystems"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'j'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Kill all on terminal"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'k'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Show stack traces"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'l'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump memory info"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'm'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Make real-time tasks niceable"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'n'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Power off immediately"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'o'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump registers and flags"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'p'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump timers and clockevents"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'q'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Turn off raw keyboard mode"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'r'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Sync all mounted filesystems"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 's'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump task list"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 't'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Remount all read-only"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'u'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump uninterruptible tasks"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'w'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump ftrace buffer"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'z'); + } +}; + +#endif /* MAGICSYSRQHANDLER_H_ */ -- cgit v1.2.3-55-g7522 From e2ef3a0cf2916173154df3eb632230cf1fdaf7c6 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Thu, 7 Oct 2010 08:55:52 +0200 Subject: Update strings for special input events --- i18n/pvsmgr_ar_JO.ts | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++ i18n/pvsmgr_de_DE.ts | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++ i18n/pvsmgr_es_MX.ts | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++ i18n/pvsmgr_fr_FR.ts | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++ i18n/pvsmgr_pl_PL.ts | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 590 insertions(+) diff --git a/i18n/pvsmgr_ar_JO.ts b/i18n/pvsmgr_ar_JO.ts index d434341..e4ee3cb 100644 --- a/i18n/pvsmgr_ar_JO.ts +++ b/i18n/pvsmgr_ar_JO.ts @@ -333,6 +333,124 @@ Perform an unprojection or remove remote help to get a target. + + InputEventHandler + + + Kill X Server + + + + + Reboot immediately + + + + + Crash system + + + + + Show all held logs + + + + + Send SIGTERM to all + + + + + Activate OOM killer + + + + + Send SIGKILL to all + + + + + Force thaw filesystems + + + + + Kill all on terminal + + + + + Show stack traces + + + + + Dump memory info + + + + + Make real-time tasks niceable + + + + + Power off immediately + + + + + Dump registers and flags + + + + + Dump timers and clockevents + + + + + Turn off raw keyboard mode + + + + + Sync all mounted filesystems + + + + + Dump task list + + + + + Remount all read-only + + + + + Dump uninterruptible tasks + + + + + Dump ftrace buffer + + + + + Reboot + + + + + Say Hello + + + MainWindow diff --git a/i18n/pvsmgr_de_DE.ts b/i18n/pvsmgr_de_DE.ts index 921b5ba..39e79cd 100644 --- a/i18n/pvsmgr_de_DE.ts +++ b/i18n/pvsmgr_de_DE.ts @@ -333,6 +333,124 @@ Perform an unprojection or remove remote help to get a target. + + InputEventHandler + + + Kill X Server + + + + + Reboot immediately + + + + + Crash system + + + + + Show all held logs + + + + + Send SIGTERM to all + + + + + Activate OOM killer + + + + + Send SIGKILL to all + + + + + Force thaw filesystems + + + + + Kill all on terminal + + + + + Show stack traces + + + + + Dump memory info + + + + + Make real-time tasks niceable + + + + + Power off immediately + + + + + Dump registers and flags + + + + + Dump timers and clockevents + + + + + Turn off raw keyboard mode + + + + + Sync all mounted filesystems + + + + + Dump task list + + + + + Remount all read-only + + + + + Dump uninterruptible tasks + + + + + Dump ftrace buffer + + + + + Reboot + + + + + Say Hello + + + MainWindow diff --git a/i18n/pvsmgr_es_MX.ts b/i18n/pvsmgr_es_MX.ts index 86a21b8..1470448 100644 --- a/i18n/pvsmgr_es_MX.ts +++ b/i18n/pvsmgr_es_MX.ts @@ -333,6 +333,124 @@ Perform an unprojection or remove remote help to get a target. + + InputEventHandler + + + Kill X Server + + + + + Reboot immediately + + + + + Crash system + + + + + Show all held logs + + + + + Send SIGTERM to all + + + + + Activate OOM killer + + + + + Send SIGKILL to all + + + + + Force thaw filesystems + + + + + Kill all on terminal + + + + + Show stack traces + + + + + Dump memory info + + + + + Make real-time tasks niceable + + + + + Power off immediately + + + + + Dump registers and flags + + + + + Dump timers and clockevents + + + + + Turn off raw keyboard mode + + + + + Sync all mounted filesystems + + + + + Dump task list + + + + + Remount all read-only + + + + + Dump uninterruptible tasks + + + + + Dump ftrace buffer + + + + + Reboot + + + + + Say Hello + + + MainWindow diff --git a/i18n/pvsmgr_fr_FR.ts b/i18n/pvsmgr_fr_FR.ts index d434341..e4ee3cb 100644 --- a/i18n/pvsmgr_fr_FR.ts +++ b/i18n/pvsmgr_fr_FR.ts @@ -333,6 +333,124 @@ Perform an unprojection or remove remote help to get a target. + + InputEventHandler + + + Kill X Server + + + + + Reboot immediately + + + + + Crash system + + + + + Show all held logs + + + + + Send SIGTERM to all + + + + + Activate OOM killer + + + + + Send SIGKILL to all + + + + + Force thaw filesystems + + + + + Kill all on terminal + + + + + Show stack traces + + + + + Dump memory info + + + + + Make real-time tasks niceable + + + + + Power off immediately + + + + + Dump registers and flags + + + + + Dump timers and clockevents + + + + + Turn off raw keyboard mode + + + + + Sync all mounted filesystems + + + + + Dump task list + + + + + Remount all read-only + + + + + Dump uninterruptible tasks + + + + + Dump ftrace buffer + + + + + Reboot + + + + + Say Hello + + + MainWindow diff --git a/i18n/pvsmgr_pl_PL.ts b/i18n/pvsmgr_pl_PL.ts index d434341..e4ee3cb 100644 --- a/i18n/pvsmgr_pl_PL.ts +++ b/i18n/pvsmgr_pl_PL.ts @@ -333,6 +333,124 @@ Perform an unprojection or remove remote help to get a target. + + InputEventHandler + + + Kill X Server + + + + + Reboot immediately + + + + + Crash system + + + + + Show all held logs + + + + + Send SIGTERM to all + + + + + Activate OOM killer + + + + + Send SIGKILL to all + + + + + Force thaw filesystems + + + + + Kill all on terminal + + + + + Show stack traces + + + + + Dump memory info + + + + + Make real-time tasks niceable + + + + + Power off immediately + + + + + Dump registers and flags + + + + + Dump timers and clockevents + + + + + Turn off raw keyboard mode + + + + + Sync all mounted filesystems + + + + + Dump task list + + + + + Remount all read-only + + + + + Dump uninterruptible tasks + + + + + Dump ftrace buffer + + + + + Reboot + + + + + Say Hello + + + MainWindow -- cgit v1.2.3-55-g7522 From 958d63d244a3f4a4542c529761ccdd9cd8244fcf Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Thu, 7 Oct 2010 09:29:06 +0200 Subject: Make behaviour on lookup failures configurable --- src/input/pvsCheckPrivileges.cpp | 78 ++++++++++++++++++++++++++++++++++++---- src/input/pvsCheckPrivileges.h | 8 ++--- src/input/pvsprivinputd.conf | 16 ++++++++- 3 files changed, 88 insertions(+), 14 deletions(-) diff --git a/src/input/pvsCheckPrivileges.cpp b/src/input/pvsCheckPrivileges.cpp index f9a8851..2026c0a 100644 --- a/src/input/pvsCheckPrivileges.cpp +++ b/src/input/pvsCheckPrivileges.cpp @@ -311,7 +311,12 @@ bool PVSCheckPrivileges::require(SessionKind sessionKind, CachedInputContext con { SessionKind cachedSessionKind; - if(sessionKind < SESSION_NONLOCAL) + if(sessionKind == SESSION_NONLOCAL) + { + // All sessions are at least non-local + return true; + } + else if(sessionKind == SESSION_LOCAL) { if((cachedSessionKind = _savedSessionKind.value(sender, SESSION_UNKNOWN)) == SESSION_UNKNOWN) { @@ -320,18 +325,49 @@ bool PVSCheckPrivileges::require(SessionKind sessionKind, CachedInputContext con _savedSessionKind[sender] = cachedSessionKind; qDebug("Got session kind: %s", toString(cachedSessionKind).toLocal8Bit().constData()); } - if(cachedSessionKind > sessionKind) + + switch(cachedSessionKind) + { + case SESSION_LOOKUP_FAILURE: + case SESSION_UNKNOWN: + { + // If we cannot find out the correct session kind, look up what we should do in + // the configuration: + QSettings* config = pvsPrivInputGetSettings(); + QVariant assumeLocal = config->value("assume-session-local", false); + if(!assumeLocal.canConvert(QVariant::Bool)) + { + qWarning("There is an assume-session-local setting, but cannot convert it to boolean"); + return false; + } + return assumeLocal.toBool(); + } + case SESSION_LOCAL: + return true; + case SESSION_NONLOCAL: return false; + default: + qWarning("Internal error: Undefined session kind %d", (int)cachedSessionKind); + return false; + } + } + else + { + qWarning("Internal error: It does not make sense to require an unknown session or undefined session kind %d", (int)sessionKind); + return false; } - - return true; } bool PVSCheckPrivileges::require(UserPrivilege userPrivilege, CachedInputContext const& sender) { UserPrivilege cachedUserPrivilege; - if(userPrivilege < USER_UNPRIVILEGED) + if(userPrivilege == USER_UNPRIVILEGED) + { + // All users are unprivileged + return true; + } + else if(userPrivilege == USER_PRIVILEGED) { if((cachedUserPrivilege = _savedUserPrivilege.value(sender, USER_UNKNOWN)) == USER_UNKNOWN) { @@ -340,10 +376,38 @@ bool PVSCheckPrivileges::require(UserPrivilege userPrivilege, CachedInputContext _savedUserPrivilege[sender] = cachedUserPrivilege; qDebug("Got user privilege: %s", toString(cachedUserPrivilege).toLocal8Bit().constData()); } - if(cachedUserPrivilege > userPrivilege) + + switch(cachedUserPrivilege) + { + case USER_LOOKUP_FAILURE: + case USER_UNKNOWN: + { + // If we cannot find out the correct user privilege level, look up what we should do in + // the configuration: + QSettings* config = pvsPrivInputGetSettings(); + QVariant assumePrivileged = config->value("assume-user-privileged", false); + if(!assumePrivileged.canConvert(QVariant::Bool)) + { + qWarning("There is an assume-session-local setting, but cannot convert it to boolean"); + return false; + } + return assumePrivileged.toBool(); + } + case USER_PRIVILEGED: + return true; + case USER_UNPRIVILEGED: + return false; + default: + qWarning("Internal error: Found undefined user privilege level %d", (int)cachedUserPrivilege); + _savedUserPrivilege.remove(sender); return false; + } + } + else + { + qWarning("Internal error: It does not make sense to require an unknown or undefined user privilege level %d", (int)userPrivilege); + return false; } - return true; } bool PVSCheckPrivileges::require(SessionKind sessionKind, diff --git a/src/input/pvsCheckPrivileges.h b/src/input/pvsCheckPrivileges.h index ec6c591..62b463c 100644 --- a/src/input/pvsCheckPrivileges.h +++ b/src/input/pvsCheckPrivileges.h @@ -74,11 +74,9 @@ class PVSCheckPrivileges : public QObject Q_OBJECT public: typedef enum { - SESSION_LOOKUP_FAILURE, // Comes first because we default to assume - // the session is local if we cannot look it - // up. SESSION_LOCAL, SESSION_NONLOCAL, + SESSION_LOOKUP_FAILURE, SESSION_UNKNOWN } SessionKind; static QString toString(SessionKind k) @@ -96,9 +94,7 @@ public: typedef enum { USER_PRIVILEGED, USER_UNPRIVILEGED, - USER_LOOKUP_FAILURE, // Comes last because we default to assume - // the user is unprivileged if we cannot get - // permission from PolicyKit. + USER_LOOKUP_FAILURE, USER_UNKNOWN } UserPrivilege; static QString toString(UserPrivilege k) diff --git a/src/input/pvsprivinputd.conf b/src/input/pvsprivinputd.conf index 52df206..a62a922 100644 --- a/src/input/pvsprivinputd.conf +++ b/src/input/pvsprivinputd.conf @@ -11,4 +11,18 @@ privileged-users = root ;; privileged-groups: ;; Comma-separated list of user groups that are allowed to run privileged actions ; privileged-groups = wheel - \ No newline at end of file + +;; assume-session-local: +;; Assume that a session is local if it can not be looked up in ConsoleKit, +;; for example, if you are not running ConsoleKit. +;; +;; WARNING: Setting this to true may be a security risk. Running ConsoleKit is +;; really recommended. +; assume-session-local = false + +;; assume-user-privileged: +;; Assume that a user is privileged if he/she can not be looked up in +;; the user database or PolicyKit fails to deliver an answer. +;; +;; WARNING: Setting this to true is most definitely a security risk. +; assume-user-privileged = false \ No newline at end of file -- cgit v1.2.3-55-g7522 From 3be768a6b55af9356d45aac1fb0533b3dc8ddcd8 Mon Sep 17 00:00:00 2001 From: jjl Date: Thu, 14 Oct 2010 17:39:51 +0200 Subject: [PVSMGR] dummies removed --- src/gui/mainWindow.cpp | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/gui/mainWindow.cpp b/src/gui/mainWindow.cpp index 0f2fd0e..79ae004 100644 --- a/src/gui/mainWindow.cpp +++ b/src/gui/mainWindow.cpp @@ -270,24 +270,24 @@ void MainWindow::loadSettings() if (current.compare("default") == 0) { setWindowTitle("PVSmgr - Default"); - QPoint pos1 = settings.value("default/1", QPoint(0, 0)).toPoint(); - MainWindow::getConnectionWindow()->addFrameBySettings("1", pos1); - QPoint pos2 = settings.value("default/2", QPoint(194, 0)).toPoint(); - MainWindow::getConnectionWindow()->addFrameBySettings("2", pos2); - QPoint pos3 = settings.value("default/3", QPoint(388, 0)).toPoint(); - MainWindow::getConnectionWindow()->addFrameBySettings("3", pos3); - QPoint pos4 = settings.value("default/4", QPoint(582, 0)).toPoint(); - MainWindow::getConnectionWindow()->addFrameBySettings("4", pos4); - QPoint pos5 = settings.value("default/5", QPoint(0, 173)).toPoint(); - MainWindow::getConnectionWindow()->addFrameBySettings("5", pos5); - QPoint pos6 = settings.value("default/6", QPoint(194, 173)).toPoint(); - MainWindow::getConnectionWindow()->addFrameBySettings("6", pos6); - QPoint pos7 = settings.value("default/7", QPoint(388, 173)).toPoint(); - MainWindow::getConnectionWindow()->addFrameBySettings("7", pos7); - QPoint pos8 = settings.value("default/8", QPoint(582, 173)).toPoint(); - MainWindow::getConnectionWindow()->addFrameBySettings("8", pos8); - QPoint pos9 = settings.value("default/9", QPoint(293, 346)).toPoint(); - MainWindow::getConnectionWindow()->addFrameBySettings("9", pos9); +// QPoint pos1 = settings.value("default/1", QPoint(0, 0)).toPoint(); +// MainWindow::getConnectionWindow()->addFrameBySettings("1", pos1); +// QPoint pos2 = settings.value("default/2", QPoint(194, 0)).toPoint(); +// MainWindow::getConnectionWindow()->addFrameBySettings("2", pos2); +// QPoint pos3 = settings.value("default/3", QPoint(388, 0)).toPoint(); +// MainWindow::getConnectionWindow()->addFrameBySettings("3", pos3); +// QPoint pos4 = settings.value("default/4", QPoint(582, 0)).toPoint(); +// MainWindow::getConnectionWindow()->addFrameBySettings("4", pos4); +// QPoint pos5 = settings.value("default/5", QPoint(0, 173)).toPoint(); +// MainWindow::getConnectionWindow()->addFrameBySettings("5", pos5); +// QPoint pos6 = settings.value("default/6", QPoint(194, 173)).toPoint(); +// MainWindow::getConnectionWindow()->addFrameBySettings("6", pos6); +// QPoint pos7 = settings.value("default/7", QPoint(388, 173)).toPoint(); +// MainWindow::getConnectionWindow()->addFrameBySettings("7", pos7); +// QPoint pos8 = settings.value("default/8", QPoint(582, 173)).toPoint(); +// MainWindow::getConnectionWindow()->addFrameBySettings("8", pos8); +// QPoint pos9 = settings.value("default/9", QPoint(293, 346)).toPoint(); +// MainWindow::getConnectionWindow()->addFrameBySettings("9", pos9); QString title = "PVSmgr - "; title.append(_profilName); -- cgit v1.2.3-55-g7522 From a498546d6200bdce6027110122d518b15747b3c1 Mon Sep 17 00:00:00 2001 From: jjl Date: Thu, 14 Oct 2010 17:57:55 +0200 Subject: [PVSGUI] No X required for --help and --version --- AUTHORS | 11 ++++++----- CMakeLists.txt | 4 ++-- TRANSLATION | 4 +--- src/pvsgui.cpp | 37 +++++++++++++++++++------------------ src/version.h | 4 ++-- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/AUTHORS b/AUTHORS index daf9a81..a081ceb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -2,19 +2,20 @@ Initial design and first versions: Simon Wittenberg Involved team members at the moment: +Fabian Schillinger +Johann Latocha +Sébastien Braun + +Credits of left team members: Johann Betz Javier Castillo -Johann Latocha Achille Nana Simon Rettberg Fadi Salameh Alexander Hoppe Benjamin Lieberwirt -Credits of left team members: -- - Project management, supervision: Dirk von Suchodoletz Sebastian Schmelzer -Simon Wittenberg \ No newline at end of file +Johann Latocha \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index cc695ce..a78e1ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -445,8 +445,8 @@ SET( CPACK_GENERATOR "DEB;RPM" ) SET( CPACK_SET_DESTDIR "ON" ) SET( CPACK_PACKAGE_NAME "pvs" ) SET( CPACK_PACKAGE_VERSION_MAJOR "2" ) -SET( CPACK_PACKAGE_VERSION_MINOR "0" ) -SET( CPACK_PACKAGE_VERSION_PATCH "3" ) +SET( CPACK_PACKAGE_VERSION_MINOR "8" ) +SET( CPACK_PACKAGE_VERSION_PATCH "0" ) SET( CPACK_PACKAGE_DESCRIPTION_SUMMARY "Pool Video Switch" ) SET( CPACK_PACKAGE_DESCRIPTION "") SET( CPACK_PACKAGE_CONTACT "Simon Wittenberg " ) diff --git a/TRANSLATION b/TRANSLATION index 74a10cb..1d04f06 100644 --- a/TRANSLATION +++ b/TRANSLATION @@ -1,4 +1,2 @@ Javier Castillo -Johann Latocha -Achille Nana -Fadi Salameh \ No newline at end of file +Johann Latocha \ No newline at end of file diff --git a/src/pvsgui.cpp b/src/pvsgui.cpp index eb1383b..1520c81 100644 --- a/src/pvsgui.cpp +++ b/src/pvsgui.cpp @@ -450,24 +450,6 @@ void printVersion() int main(int argc, char *argv[]) { - QtSingleApplication app(argc, argv); - app.setQuitOnLastWindowClosed(false); - app.setOrganizationName("openslx"); - app.setOrganizationDomain("openslx.org"); - app.setApplicationName("pvsgui"); - - // only one instance should be allowed - if (app.sendMessage("")) - { - qDebug("[PVSGUI] ERROR: Already running. Exiting"); - return 0; - } - - // use system locale as language to translate gui - QTranslator translator; - translator.load(":pvsgui"); - app.installTranslator(&translator); - bool visible = false; int position = -1; @@ -506,6 +488,25 @@ int main(int argc, char *argv[]) opt = getopt_long( argc, argv, optString, longOpts, &longIndex ); } + + QtSingleApplication app(argc, argv); + app.setQuitOnLastWindowClosed(false); + app.setOrganizationName("openslx"); + app.setOrganizationDomain("openslx.org"); + app.setApplicationName("pvsgui"); + + // only one instance should be allowed + if (app.sendMessage("")) + { + qDebug("[PVSGUI] ERROR: Already running. Exiting"); + return 0; + } + + // use system locale as language to translate gui + QTranslator translator; + translator.load(":pvsgui"); + app.installTranslator(&translator); + PVSGUI pvsgui; pvsgui.setPosition(position); pvsgui.setVisible(visible); diff --git a/src/version.h b/src/version.h index de5c6aa..a08c819 100644 --- a/src/version.h +++ b/src/version.h @@ -1,2 +1,2 @@ -#define VERSION_STRING "2.0.5" -#define VERSION_NUMBER 205 +#define VERSION_STRING "2.8.0" +#define VERSION_NUMBER 280 -- cgit v1.2.3-55-g7522